diff --git a/spec/services/export/md.spec.ts b/spec/services/export/md.spec.ts new file mode 100644 index 000000000..612b43513 --- /dev/null +++ b/spec/services/export/md.spec.ts @@ -0,0 +1,24 @@ +import markdownExportService from "../../../src/services/export/md.js"; +import { trimIndentation } from "../../support/utils.js"; + +describe("Markdown export", () => { + it("trims language tag for code blocks", () => { + const html = trimIndentation`\ +

A diff:

+
Hello
+            -world
+            +worldy
+            
`; + const expected = trimIndentation`\ + A diff: + + \`\`\`diff + Hello + -world + +worldy + + \`\`\``; + + expect(markdownExportService.toMarkdown(html)).toBe(expected); + }); +}); \ No newline at end of file diff --git a/src/services/export/md.ts b/src/services/export/md.ts index 84af6f56f..8f7213f84 100644 --- a/src/services/export/md.ts +++ b/src/services/export/md.ts @@ -8,12 +8,44 @@ let instance: TurndownService | null = null; function toMarkdown(content: string) { if (instance === null) { instance = new TurndownService({ codeBlockStyle: 'fenced' }); + instance.addRule('fencedCodeBlock', { + filter: function (node, options) { + return ( + options.codeBlockStyle === 'fenced' && + node.nodeName === 'PRE' && + node.firstChild !== null && + node.firstChild.nodeName === 'CODE' + ) + }, + + replacement: function (content, node, options) { + if (!node.firstChild || !("getAttribute" in node.firstChild) || typeof node.firstChild.getAttribute !== "function") { + return content; + } + + var className = node.firstChild.getAttribute('class') || '' + var language = (className.match(/language-(\S+)/) || [null, ''])[1]; + language = rewriteLanguageTag(language); + + return ( + '\n\n' + options.fence + language + '\n' + + node.firstChild.textContent + + '\n' + options.fence + '\n\n' + ) + } + }) instance.use(turndownPluginGfm.gfm); } return instance.turndown(content); } +function rewriteLanguageTag(source: string) { + return source + .split("-") + .at(-1); +} + export default { toMarkdown };