From 30578015919e019cd8dd354288a45c1fc63bd01f Mon Sep 17 00:00:00 2001 From: hippotastic <6137925+hippotastic@users.noreply.github.com> Date: Fri, 3 Jun 2022 15:38:45 +0200 Subject: [PATCH] Fix: Allow self-closing tags in Markdown (#3516) --- .changeset/late-swans-impress.md | 5 +++ packages/markdown/remark/src/remark-mdxish.ts | 37 ++++++++++++++++++- .../markdown/remark/test/strictness.test.js | 16 ++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 .changeset/late-swans-impress.md create mode 100644 packages/markdown/remark/test/strictness.test.js diff --git a/.changeset/late-swans-impress.md b/.changeset/late-swans-impress.md new file mode 100644 index 000000000..ace91bef8 --- /dev/null +++ b/.changeset/late-swans-impress.md @@ -0,0 +1,5 @@ +--- +'@astrojs/markdown-remark': patch +--- + +Fix: Allow self-closing tags in Markdown diff --git a/packages/markdown/remark/src/remark-mdxish.ts b/packages/markdown/remark/src/remark-mdxish.ts index 0ad8cbf4f..e94a1146f 100644 --- a/packages/markdown/remark/src/remark-mdxish.ts +++ b/packages/markdown/remark/src/remark-mdxish.ts @@ -1,11 +1,13 @@ import { mdxjs } from 'micromark-extension-mdxjs'; import { mdxFromMarkdown, mdxToMarkdown } from './mdast-util-mdxish.js'; +import type * as fromMarkdown from 'mdast-util-from-markdown'; +import type { Tag } from 'mdast-util-mdx-jsx'; export default function remarkMdxish(this: any, options = {}) { const data = this.data(); add('micromarkExtensions', mdxjs(options)); - add('fromMarkdownExtensions', mdxFromMarkdown()); + add('fromMarkdownExtensions', makeFromMarkdownLessStrict(mdxFromMarkdown())); add('toMarkdownExtensions', mdxToMarkdown()); function add(field: string, value: unknown) { @@ -13,3 +15,36 @@ export default function remarkMdxish(this: any, options = {}) { list.push(value); } } + +function makeFromMarkdownLessStrict(extensions: fromMarkdown.Extension[]) { + extensions.forEach(extension => { + // Fix exit handlers that are too strict + ['mdxJsxFlowTag', 'mdxJsxTextTag'].forEach(exitHandler => { + if (!extension.exit || !extension.exit[exitHandler]) + return; + extension.exit[exitHandler] = chainHandlers( + fixSelfClosing, + extension.exit[exitHandler] + ); + }); + }); + + return extensions; +} + +const selfClosingTags = new Set([ + 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', + 'track', 'wbr' +]); + +function fixSelfClosing(this: fromMarkdown.CompileContext) { + const tag = this.getData('mdxJsxTag') as Tag; + if (tag.name && selfClosingTags.has(tag.name)) + tag.selfClosing = true; +} + +function chainHandlers(...handlers: fromMarkdown.Handle[]) { + return function handlerChain (this: fromMarkdown.CompileContext, token: fromMarkdown.Token) { + handlers.forEach(handler => handler.call(this, token)); + }; +} diff --git a/packages/markdown/remark/test/strictness.test.js b/packages/markdown/remark/test/strictness.test.js new file mode 100644 index 000000000..c18cc41dd --- /dev/null +++ b/packages/markdown/remark/test/strictness.test.js @@ -0,0 +1,16 @@ +import { renderMarkdown } from '../dist/index.js'; +import chai from 'chai'; + +describe('strictness', () => { + it('should allow self-closing HTML tags (void elements)', async () => { + const { code } = await renderMarkdown( + `Use self-closing void elements
like wordbreak and images: `, + {} + ); + + chai.expect(code).to.equal( + `

Use self-closing void elements
like wordbreak and images: ` + + `

` + ); + }); +});