import type { MarkedToken, Token } from 'marked';
import { marked } from 'marked';
import { dedent } from 'ts-dedent';
import type { MarkdownLine, MarkdownWordType } from './types.js';
import type { MermaidConfig } from '../config.type.js';
/**
* @param markdown - markdown to process
* @returns processed markdown
*/
function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfig): string {
//Replace
with \n
const withoutBR = markdown.replace(/
/g, '\n');
// Replace multiple newlines with a single newline
const withoutMultipleNewlines = withoutBR.replace(/\n{2,}/g, '\n');
// Remove extra spaces at the beginning of each line
const withoutExtraSpaces = dedent(withoutMultipleNewlines);
if (markdownAutoWrap === false) {
return withoutExtraSpaces.replace(/ /g, ' ');
}
return withoutExtraSpaces;
}
/**
* @param markdown - markdown to split into lines
*/
export function markdownToLines(markdown: string, config: MermaidConfig = {}): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown, config);
const nodes = marked.lexer(preprocessedMarkdown);
const lines: MarkdownLine[] = [[]];
let currentLine = 0;
function processNode(node: MarkedToken, parentType: MarkdownWordType = 'normal') {
if (node.type === 'text') {
const textLines = node.text.split('\n');
textLines.forEach((textLine, index) => {
if (index !== 0) {
currentLine++;
lines.push([]);
}
textLine.split(' ').forEach((word) => {
if (word) {
lines[currentLine].push({ content: word, type: parentType });
}
});
});
} else if (node.type === 'strong' || node.type === 'em') {
node.tokens.forEach((contentNode) => {
processNode(contentNode as MarkedToken, node.type);
});
} else if (node.type === 'html') {
lines[currentLine].push({ content: node.text, type: 'normal' });
}
}
nodes.forEach((treeNode) => {
if (treeNode.type === 'paragraph') {
treeNode.tokens?.forEach((contentNode) => {
processNode(contentNode as MarkedToken);
});
} else if (treeNode.type === 'html') {
lines[currentLine].push({ content: treeNode.text, type: 'normal' });
}
});
return lines;
}
export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidConfig = {}) {
const nodes = marked.lexer(markdown);
function output(node: Token): string {
if (node.type === 'text') {
if (markdownAutoWrap === false) {
return node.text.replace(/\n */g, '
').replace(/ /g, ' ');
}
return node.text.replace(/\n */g, '
');
} else if (node.type === 'strong') {
return `${node.tokens?.map(output).join('')}`;
} else if (node.type === 'em') {
return `${node.tokens?.map(output).join('')}`;
} else if (node.type === 'paragraph') {
return `
${node.tokens?.map(output).join('')}
`; } else if (node.type === 'space') { return ''; } else if (node.type === 'html') { return `${node.text}`; } return `Unsupported markdown: ${node.type}`; } return nodes.map(output).join(''); }