diff --git a/shared/editor/lib/markdown/rules.test.ts b/shared/editor/lib/markdown/rules.test.ts new file mode 100644 index 0000000000..6d90e159a5 --- /dev/null +++ b/shared/editor/lib/markdown/rules.test.ts @@ -0,0 +1,37 @@ +import type { Schema } from "prosemirror-model"; +import makeRules from "./rules"; + +const tableMarkdown = "| a | b |\n| --- | --- |\n| 1 | 2 |"; + +const schemaWith = (nodes: string[]) => + ({ + nodes: Object.fromEntries(nodes.map((name) => [name, {}])), + }) as unknown as Schema; + +describe("makeRules", () => { + it("parses tables when the schema supports them", () => { + const md = makeRules({ schema: schemaWith(["table"]) }); + const types = md.parse(tableMarkdown, {}).map((token) => token.type); + expect(types).toContain("table_open"); + }); + + it("does not emit table tokens when the schema lacks table support", () => { + const md = makeRules({ schema: schemaWith([]) }); + const types = md.parse(tableMarkdown, {}).map((token) => token.type); + expect(types).not.toContain("table_open"); + }); + + it("does not emit heading tokens for setext headings when the schema lacks heading support", () => { + const md = makeRules({ schema: schemaWith([]) }); + const types = md.parse("Title\n=====\n", {}).map((token) => token.type); + expect(types).not.toContain("heading_open"); + }); + + it("does not emit code_block tokens when the schema lacks code block support", () => { + const md = makeRules({ schema: schemaWith([]) }); + const indented = md.parse(" foo\n", {}).map((token) => token.type); + const fenced = md.parse("```\nfoo\n```\n", {}).map((token) => token.type); + expect(indented).not.toContain("code_block"); + expect(fenced).not.toContain("fence"); + }); +}); diff --git a/shared/editor/lib/markdown/rules.ts b/shared/editor/lib/markdown/rules.ts index b6ec30936a..e37f56b5b9 100644 --- a/shared/editor/lib/markdown/rules.ts +++ b/shared/editor/lib/markdown/rules.ts @@ -36,7 +36,15 @@ export default function makeRules({ markdownIt.disable("hr"); } if (!schema?.nodes.heading) { - markdownIt.disable("heading"); + // "heading" is ATX (# Title), "lheading" is setext (Title\n=====). + markdownIt.disable(["heading", "lheading"]); + } + if (!schema?.nodes.table) { + markdownIt.disable("table"); + } + if (!schema?.nodes.code_block) { + // "code" is indented code blocks, "fence" is ``` delimited blocks. + markdownIt.disable(["code", "fence"]); } plugins.forEach((plugin) => markdownIt.use(plugin));