mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-06-06 09:31:35 -04:00
110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
import {
|
|
convertRawFrontmatterToNoteFrontmatter,
|
|
defaultNoteFrontmatter,
|
|
extractFirstHeading,
|
|
extractFrontmatter,
|
|
generateNoteTitle,
|
|
NoteFrontmatter,
|
|
NoteType,
|
|
parseRawFrontmatterFromYaml,
|
|
} from '@hedgedoc/commons';
|
|
import { parseDocument } from 'htmlparser2';
|
|
import MarkdownIt from 'markdown-it';
|
|
|
|
interface FrontmatterExtractionResult {
|
|
title: string;
|
|
description: string;
|
|
tags: string[];
|
|
noteType: NoteType;
|
|
}
|
|
|
|
interface FrontmatterParserResult {
|
|
frontmatter: NoteFrontmatter;
|
|
firstLineOfContentIndex: number;
|
|
}
|
|
|
|
/**
|
|
* Parses the frontmatter of the given content and extracts the metadata that are necessary to create a new revision
|
|
*
|
|
* @param content the revision content that contains the frontmatter
|
|
* @returns the extracted metadata, including the title, description, tags, and note type
|
|
*/
|
|
export function extractRevisionMetadataFromContent(
|
|
content: string,
|
|
): FrontmatterExtractionResult {
|
|
const parserResult = parseFrontmatter(content);
|
|
const frontmatter = parserResult?.frontmatter;
|
|
const firstLineOfContentIndex = parserResult?.firstLineOfContentIndex;
|
|
|
|
const title = generateNoteTitle(frontmatter, () =>
|
|
extractFirstHeadingFromContent(
|
|
generateContentWithoutFrontmatter(firstLineOfContentIndex, content),
|
|
),
|
|
);
|
|
const description = frontmatter?.description ?? '';
|
|
const tags = frontmatter?.tags ?? [];
|
|
const noteType = frontmatter?.type ?? NoteType.DOCUMENT;
|
|
|
|
return { title, description, tags, noteType };
|
|
}
|
|
|
|
/**
|
|
* Generates the content of a revision without the frontmatter.
|
|
*
|
|
* @param firstLineOfContentIndex the index of the first line of content after the frontmatter
|
|
* @param content the full content including frontmatter
|
|
* @returns the content without frontmatter
|
|
*/
|
|
function generateContentWithoutFrontmatter(
|
|
firstLineOfContentIndex: number | undefined,
|
|
content: string,
|
|
): string {
|
|
return firstLineOfContentIndex === undefined
|
|
? content
|
|
: content.split('\n').slice(firstLineOfContentIndex).join('\n');
|
|
}
|
|
|
|
/**
|
|
* Parses the frontmatter from the given content and returns the parsed frontmatter and the index of the first line of content.
|
|
*
|
|
* @param content the content to parse
|
|
* @returns an object containing the parsed frontmatter and the index of the first line of content, or undefined if no frontmatter was found
|
|
*/
|
|
function parseFrontmatter(
|
|
content: string,
|
|
): FrontmatterParserResult | undefined {
|
|
const extractionResult = extractFrontmatter(content.split('\n'));
|
|
const rawText = extractionResult?.rawText;
|
|
if (!rawText) {
|
|
return undefined;
|
|
}
|
|
|
|
const firstLineOfContentIndex = extractionResult.lineOffset + 1;
|
|
const rawDataValidation = parseRawFrontmatterFromYaml(rawText);
|
|
const noteFrontmatter =
|
|
rawDataValidation.error !== undefined
|
|
? defaultNoteFrontmatter
|
|
: convertRawFrontmatterToNoteFrontmatter(rawDataValidation.value);
|
|
return {
|
|
frontmatter: noteFrontmatter,
|
|
firstLineOfContentIndex: firstLineOfContentIndex,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Extracts the first heading from the given markdown content
|
|
*
|
|
* @param content the content to extract the first heading from
|
|
* @returns the first heading or undefined if no heading was found
|
|
*/
|
|
function extractFirstHeadingFromContent(content: string): string | undefined {
|
|
const markdownIt = new MarkdownIt('default');
|
|
const html = markdownIt.render(content);
|
|
const document = parseDocument(html);
|
|
return extractFirstHeading(document);
|
|
}
|