diff --git a/.changeset/empty-bats-grow.md b/.changeset/empty-bats-grow.md new file mode 100644 index 000000000..4ad7871c9 --- /dev/null +++ b/.changeset/empty-bats-grow.md @@ -0,0 +1,5 @@ +--- +'@astrojs/markdown-remark': patch +--- + +Fix: HTML comments in markdown code blocks should not be wrapped in JS comments diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index ba749e249..2bcc0b241 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -141,6 +141,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin { // Turn HTML comments into JS comments while preventing nested `*/` sequences // from ending the JS comment by injecting a zero-width space + // Inside code blocks, this is removed during renderMarkdown by the remark-escape plugin. markdownContent = markdownContent.replace( /<\s*!--([^-->]*)(.*?)-->/gs, (whole) => `{/*${whole.replace(/\*\//g, '*\u200b/')}*/}` diff --git a/packages/astro/test/astro-markdown.test.js b/packages/astro/test/astro-markdown.test.js index dd40bc071..0afbfdf36 100644 --- a/packages/astro/test/astro-markdown.test.js +++ b/packages/astro/test/astro-markdown.test.js @@ -79,6 +79,20 @@ describe('Astro Markdown', () => { expect($('h1').text()).to.equal('It still works!'); }); + it.only('Can handle HTML comments in inline code', async () => { + const html = await fixture.readFile('/comment-with-js/index.html'); + const $ = cheerio.load(html); + + expect($('p code').text()).to.equal(''); + }); + + it('Can handle HTML comments in code fences', async () => { + const html = await fixture.readFile('/comment-with-js/index.html'); + const $ = cheerio.load(html); + + expect($('body > code').text()).to.equal('') + }); + // https://github.com/withastro/astro/issues/3254 it('Can handle scripts in markdown pages', async () => { const html = await fixture.readFile('/script/index.html'); diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/comment-with-js.md b/packages/astro/test/fixtures/astro-markdown/src/pages/comment-with-js.md index 387b8380e..374463d2d 100644 --- a/packages/astro/test/fixtures/astro-markdown/src/pages/comment-with-js.md +++ b/packages/astro/test/fixtures/astro-markdown/src/pages/comment-with-js.md @@ -14,4 +14,10 @@ function test() { ``` --> +``` + +``` + +`` + # It still works! diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index 9dfc0e38c..7034564c3 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -6,6 +6,7 @@ import rehypeEscape from './rehype-escape.js'; import rehypeExpressions from './rehype-expressions.js'; import rehypeIslands from './rehype-islands.js'; import rehypeJsx from './rehype-jsx.js'; +import remarkEscape from './remark-escape.js'; import remarkMarkAndUnravel from './remark-mark-and-unravel.js'; import remarkMdxish from './remark-mdxish.js'; import remarkPrism from './remark-prism.js'; @@ -46,7 +47,7 @@ export async function renderMarkdown( let parser = unified() .use(markdown) .use(isMDX ? [remarkMdxish, remarkMarkAndUnravel] : []) - .use([remarkUnwrap]); + .use([remarkUnwrap, remarkEscape]); if (remarkPlugins.length === 0 && rehypePlugins.length === 0) { remarkPlugins = [...DEFAULT_REMARK_PLUGINS]; diff --git a/packages/markdown/remark/src/remark-escape.ts b/packages/markdown/remark/src/remark-escape.ts new file mode 100644 index 000000000..312bbe2cd --- /dev/null +++ b/packages/markdown/remark/src/remark-escape.ts @@ -0,0 +1,17 @@ +import { visit } from 'unist-util-visit'; +import type { Literal } from 'unist'; + +// In code blocks, this removes the JS comment wrapper added to +// HTML comments by vite-plugin-markdown. +export default function remarkEscape() { + return (tree: any) => { + visit(tree, 'code', removeCommentWrapper); + visit(tree, 'inlineCode', removeCommentWrapper); + }; + + function removeCommentWrapper(node: Literal) { + node.value = node.value + .replace(/{\/\*\*\/}/gs, '-->'); + } +} diff --git a/packages/markdown/remark/test/expressions.test.js b/packages/markdown/remark/test/expressions.test.js index 2963ff7f1..12ddc1f48 100644 --- a/packages/markdown/remark/test/expressions.test.js +++ b/packages/markdown/remark/test/expressions.test.js @@ -79,4 +79,24 @@ describe('expressions', () => { chai.expect(code).to.equal(`{frontmatter.list.map(item =>

{item}

)}`); }); + + it('should unwrap HTML comments in inline code blocks', async () => { + const { code } = await renderMarkdown( + `\`{/**/}\`` + ); + + chai.expect(code).to.equal('

<!-- HTML comment -->

'); + }); + + it('should unwrap HTML comments in code fences', async () => { + const { code } = await renderMarkdown( + ` + \`\`\` + + \`\`\` + ` + ); + + chai.expect(code).to.match(/(?