diff --git a/src/compiler/GardenPageCompiler.ts b/src/compiler/GardenPageCompiler.ts index 1dc54ac..ec08fe4 100644 --- a/src/compiler/GardenPageCompiler.ts +++ b/src/compiler/GardenPageCompiler.ts @@ -30,6 +30,7 @@ import { import Logger from "js-logger"; import { DataviewCompiler } from "./DataviewCompiler"; import { PublishFile } from "../publishFile/PublishFile"; +import { replaceBlockIDs } from "./replaceBlockIDs"; export interface Asset { path: string; @@ -144,21 +145,7 @@ export class GardenPageCompiler { }; createBlockIDs: TCompilerStep = () => (text: string) => { - const block_pattern = / \^([\w\d-]+)/g; - const complex_block_pattern = /\n\^([\w\d-]+)\n/g; - - text = text.replace( - complex_block_pattern, - (_match: string, $1: string) => { - return `{ #${$1}}\n\n`; - }, - ); - - text = text.replace(block_pattern, (match: string, $1: string) => { - return `\n{ #${$1}}\n`; - }); - - return text; + return replaceBlockIDs(text); }; removeObsidianComments: TCompilerStep = () => (text) => { diff --git a/src/compiler/createBlockIDs.test.ts b/src/compiler/createBlockIDs.test.ts new file mode 100644 index 0000000..9d4e196 --- /dev/null +++ b/src/compiler/createBlockIDs.test.ts @@ -0,0 +1,69 @@ +import { replaceBlockIDs } from "./replaceBlockIDs"; + +describe("replaceBlockIDs", () => { + it("should replace block IDs in markdown", () => { + const EXPECTED_MARKDOWN = ` + # header + + foo ^block-id-1234 + + bar ^block-id-5678 + + below + ^block-id-9999 + `; + + const result = replaceBlockIDs(EXPECTED_MARKDOWN); + + expect(result).toBe(` + # header + + foo + { #block-id-1234} + + bar + { #block-id-5678} + + below + { #block-id-9999} + `); + }); + + it("should not replace block IDs in code blocks", () => { + const CODEBLOCK_WITH_BLOCKIDS = ` +\`\`\` +foobar. +this is a code block. +but it contains a block ID to try to fool the compiler +and, consequently, wreck your garden. +here it goes: ^block-id-1234 +and for fun, here's another: ^block-id-5678 +\`\`\` + +additionally, block IDs outside of code blocks should be replaced +for example, ^block-id-9999 +and ^block-id-0000 + `; + + const result = replaceBlockIDs(CODEBLOCK_WITH_BLOCKIDS); + + expect(result).toBe(` +\`\`\` +foobar. +this is a code block. +but it contains a block ID to try to fool the compiler +and, consequently, wreck your garden. +here it goes: ^block-id-1234 +and for fun, here's another: ^block-id-5678 +\`\`\` + +additionally, block IDs outside of code blocks should be replaced +for example, +{ #block-id-9999} + +and +{ #block-id-0000} + + `); + }); +}); diff --git a/src/compiler/replaceBlockIDs.ts b/src/compiler/replaceBlockIDs.ts new file mode 100644 index 0000000..50eb1a6 --- /dev/null +++ b/src/compiler/replaceBlockIDs.ts @@ -0,0 +1,35 @@ +export function replaceBlockIDs(markdown: string) { + const block_pattern = / \^([\w\d-]+)/g; + const complex_block_pattern = /\n\^([\w\d-]+)\n/g; + + // To ensure code blocks are not modified... + const codeBlockPattern = /```[\s\S]*?```/g; + + // Extract code blocks and replace them with placeholders + const codeBlocks: string[] = []; + + markdown = markdown.replace(codeBlockPattern, (match) => { + codeBlocks.push(match); + + return `{{CODE_BLOCK_${codeBlocks.length - 1}}}`; + }); + + // Replace patterns outside code blocks + markdown = markdown.replace( + complex_block_pattern, + (_match: string, $1: string) => { + return `{ #${$1}}\n\n`; + }, + ); + + markdown = markdown.replace(block_pattern, (_match: string, $1: string) => { + return `\n{ #${$1}}\n`; + }); + + // Reinsert code blocks + codeBlocks.forEach((block, index) => { + markdown = markdown.replace(`{{CODE_BLOCK_${index}}}`, block); + }); + + return markdown; +}