diff --git a/mod.ts b/mod.ts
index 93eb18d..545f590 100644
--- a/mod.ts
+++ b/mod.ts
@@ -31,6 +31,7 @@ export class Renderer extends Marked.Renderer {
allowMath: boolean;
baseUrl: string | undefined;
#slugger: GitHubSlugger;
+ mermaidImport: boolean = false;
constructor(options: Marked.MarkedOptions & RenderOptions = {}) {
super(options);
@@ -63,23 +64,41 @@ export class Renderer extends Marked.Renderer {
// a language of `ts, ignore` should really be `ts`
// and it should be lowercase to ensure it has parity with regular github markdown
language = language?.split(",")?.[0].toLocaleLowerCase();
+ const isMermaid = language === "mermaid";
// transform math code blocks into HTML+MathML
// https://github.blog/changelog/2022-06-28-fenced-block-syntax-for-mathematical-expressions/
if (language === "math" && this.allowMath) {
return katex.renderToString(code, { displayMode: true });
}
+ if (isMermaid) {
+ this.mermaidImport = true;
+ }
const grammar =
language && Object.hasOwnProperty.call(Prism.languages, language)
? Prism.languages[language]
: undefined;
if (grammar === undefined) {
+ if (isMermaid) {
+ return minify(`
`);
+ }
return `${he.encode(code)}
`;
}
const html = Prism.highlight(code, grammar, language!);
const titleHtml = title
? `${title}
`
: ``;
+ if (isMermaid) {
+ return minify(`
+
+ `);
+ }
return ``;
}
@@ -99,6 +118,10 @@ export class Renderer extends Marked.Renderer {
}
}
+function minify(str: string): string {
+ return str.replace(/^\s+|\s+$|\n/gm, "");
+}
+
const BLOCK_MATH_REGEXP = /\$\$\s(.+?)\s\$\$/g;
const INLINE_MATH_REGEXP = /\s\$((?=\S).*?(?=\S))\$/g;
@@ -169,8 +192,33 @@ export function render(markdown: string, opts: RenderOptions = {}): string {
: Marked.marked.parse(markdown, marked_opts)
) as string;
+ let additionalCode = "";
+ if (marked_opts.renderer.mermaidImport) {
+ additionalCode = minify(`
+
+
+ `);
+ }
+
if (opts.disableHtmlSanitization) {
- return html;
+ return additionalCode + html;
}
let defaultAllowedTags = sanitizeHtml.defaults.allowedTags.concat([
@@ -243,6 +291,8 @@ export function render(markdown: string, opts: RenderOptions = {}): string {
"markdown-alert",
"markdown-alert-*",
"markdown-code-title",
+ "mermaid-code",
+ "mermaid-container",
],
span: [
"token",
@@ -334,22 +384,25 @@ export function render(markdown: string, opts: RenderOptions = {}): string {
],
};
- return sanitizeHtml(html, {
- transformTags: {
- img: transformMedia,
- video: transformMedia,
- },
- allowedTags: [...defaultAllowedTags, ...(opts.allowedTags ?? [])],
- allowedAttributes: mergeAttributes(
- defaultAllowedAttributes,
- opts.allowedAttributes ?? {},
- ),
- allowedClasses: { ...defaultAllowedClasses, ...opts.allowedClasses },
- allowProtocolRelative: false,
- parser: {
- lowerCaseAttributeNames: false,
- },
- });
+ return (
+ additionalCode +
+ sanitizeHtml(html, {
+ transformTags: {
+ img: transformMedia,
+ video: transformMedia,
+ },
+ allowedTags: [...defaultAllowedTags, ...(opts.allowedTags ?? [])],
+ allowedAttributes: mergeAttributes(
+ defaultAllowedAttributes,
+ opts.allowedAttributes ?? {},
+ ),
+ allowedClasses: { ...defaultAllowedClasses, ...opts.allowedClasses },
+ allowProtocolRelative: false,
+ parser: {
+ lowerCaseAttributeNames: false,
+ },
+ })
+ );
}
function mergeAttributes(
diff --git a/test/test.ts b/test/test.ts
index 1310ac0..581222f 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -268,6 +268,40 @@ Deno.test("code fence with a title", () => {
assertEquals(html, expected);
});
+Deno.test("code containing mermaid", () => {
+ // test with two code blocks to see if the script and styles are not replicated
+ const markdown =
+ "```mermaid\ngraph TD;A-->B;A-->C;B-->D;C-->D;\n```\n\n```mermaid\ngraph TD;A-->B;A-->C;B-->D;C-->D;\n```";
+ const expected = `
+
+ graph TD;A-->B;A-->C;B-->D;C-->D;
graph TD;A-->B;A-->C;B-->D;C-->D;
+ graph TD;A-->B;A-->C;B-->D;C-->D;
graph TD;A-->B;A-->C;B-->D;C-->D;
`;
+
+ const html = render(markdown);
+ assertEquals(
+ html,
+ expected
+ .split("\n")
+ .map((line) => line.trim())
+ .join(""),
+ );
+});
+
Deno.test("link with title", () => {
const markdown = `[link](https://example.com "asdf")`;
const expected =