Fix: use set:html when markdown mode is md (#4008)

* refactor: add legacy.jsxInMarkdown flag to config

* fix: use `set:html` when `markdown.mode` is 'md'

* Revert "refactor: add legacy.jsxInMarkdown flag to config"

This reverts commit 5572e8d9b3.

* fix: move `remarkUnwrap, remarkEscape` to MDX only

* fix: only apply scary HTML passthroughs on MDX

* fix: move all JSX-specific rehype plugins under `isMDX`

* fix: "allowDangerousHtml" for md (required for Shiki)

* fix: apply `set:html` for non-layouts too

* test: JSX expressions, components, syntax highlighting

* chore: changeset

* fix: ignore "setup" and "components" in plain MD mode

* refactor: create new fixture to avoid weird caching error

* fix: dup package name

* chore: update lock

* fix: apply rehypeCollectHeaders to md
This commit is contained in:
Ben Holmes 2022-07-21 18:01:26 -04:00 committed by GitHub
parent c2968b0542
commit 399d7e2698
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 143 additions and 13 deletions

View file

@ -0,0 +1,6 @@
---
'astro': patch
'@astrojs/markdown-remark': patch
---
Avoid parsing JSX, components, and Astro islands when using "plain" md mode. This brings `markdown.mode: 'md'` in-line with our docs description.

View file

@ -137,6 +137,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
const filename = normalizeFilename(id); const filename = normalizeFilename(id);
const source = await fs.promises.readFile(filename, 'utf8'); const source = await fs.promises.readFile(filename, 'utf8');
const renderOpts = config.markdown; const renderOpts = config.markdown;
const isMDX = renderOpts.mode === 'mdx';
const fileUrl = new URL(`file://${filename}`); const fileUrl = new URL(`file://${filename}`);
const isPage = fileUrl.pathname.startsWith(resolvePages(config).pathname); const isPage = fileUrl.pathname.startsWith(resolvePages(config).pathname);
@ -148,7 +149,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
// Turn HTML comments into JS comments while preventing nested `*/` sequences // Turn HTML comments into JS comments while preventing nested `*/` sequences
// from ending the JS comment by injecting a zero-width space // from ending the JS comment by injecting a zero-width space
// Inside code blocks, this is removed during renderMarkdown by the remark-escape plugin. // Inside code blocks, this is removed during renderMarkdown by the remark-escape plugin.
if (renderOpts.mode === 'mdx') { if (isMDX) {
markdownContent = markdownContent.replace( markdownContent = markdownContent.replace(
/<\s*!--([^-->]*)(.*?)-->/gs, /<\s*!--([^-->]*)(.*?)-->/gs,
(whole) => `{/*${whole.replace(/\*\//g, '*\u200b/')}*/}` (whole) => `{/*${whole.replace(/\*\//g, '*\u200b/')}*/}`
@ -167,19 +168,30 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
const prelude = `--- const prelude = `---
import Slugger from 'github-slugger'; import Slugger from 'github-slugger';
${layout ? `import Layout from '${layout}';` : ''} ${layout ? `import Layout from '${layout}';` : ''}
${components ? `import * from '${components}';` : ''} ${isMDX && components ? `import * from '${components}';` : ''}
${hasInjectedScript ? `import '${PAGE_SSR_SCRIPT_ID}';` : ''} ${hasInjectedScript ? `import '${PAGE_SSR_SCRIPT_ID}';` : ''}
${setup} ${isMDX ? setup : ''}
const slugger = new Slugger(); const slugger = new Slugger();
function $$slug(value) { function $$slug(value) {
return slugger.slug(value); return slugger.slug(value);
} }
const $$content = ${JSON.stringify(content)} const $$content = ${JSON.stringify(isMDX
? content
// Avoid stripping "setup" and "components"
// in plain MD mode
: { ...content, setup, components })}
---`; ---`;
const imports = `${layout ? `import Layout from '${layout}';` : ''} const imports = `${layout ? `import Layout from '${layout}';` : ''}
${setup}`.trim(); ${isMDX ? setup : ''}`.trim();
// Wrap with set:html fragment to skip
// JSX expressions and components in "plain" md mode
if (!isMDX) {
astroResult = `<Fragment set:html={${JSON.stringify(astroResult)}} />`
}
// If the user imported "Layout", wrap the content in a Layout // If the user imported "Layout", wrap the content in a Layout
if (/\bLayout\b/.test(imports)) { if (/\bLayout\b/.test(imports)) {
astroResult = `${prelude}\n<Layout content={$$content}>\n\n${astroResult}\n\n</Layout>`; astroResult = `${prelude}\n<Layout content={$$content}>\n\n${astroResult}\n\n</Layout>`;

View file

@ -0,0 +1,52 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Astro Markdown - plain MD mode', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-markdown-md-mode/',
});
await fixture.build();
});
it('Leaves JSX expressions unprocessed', async () => {
const html = await fixture.readFile('/jsx-expressions/index.html');
const $ = cheerio.load(html);
expect($('h2').html()).to.equal('{frontmatter.title}');
});
it('Leaves JSX components un-transformed', async () => {
const html = await fixture.readFile('/components/index.html');
expect(html).to.include('<counter client:load="" count="{0}">');
});
describe('syntax highlighting', async () => {
it('handles Shiki', async () => {
const html = await fixture.readFile('/code-in-md/index.html');
const $ = cheerio.load(html);
expect($('pre.astro-code').length).to.not.equal(0);
});
it('handles Prism', async () => {
fixture = await loadFixture({
root: './fixtures/astro-markdown-md-mode/',
markdown: {
syntaxHighlight: 'prism',
mode: 'md',
},
});
await fixture.build();
const html = await fixture.readFile('/code-in-md/index.html');
const $ = cheerio.load(html);
expect($('pre.language-html').length).to.not.equal(0);
});
});
});

View file

@ -0,0 +1,11 @@
import { defineConfig } from 'astro/config';
import svelte from "@astrojs/svelte";
// https://astro.build/config
export default defineConfig({
markdown: {
mode: 'md',
},
integrations: [svelte()],
site: 'https://astro.build/',
});

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-markdown-md-mode",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/svelte": "workspace:*",
"astro": "workspace:*"
}
}

View file

@ -0,0 +1,5 @@
<script>
export let count = 0;
</script>
<button onClick={() => count += 1}>{count}</button>

View file

@ -0,0 +1,7 @@
# Fenced code blocks
```html
<body>
<div>This should also work without any problems.</div>
</body>
```

View file

@ -0,0 +1,5 @@
---
setup: import Counter from '../components/Counter.svelte'
---
<Counter client:load count={0}></Counter>

View file

@ -0,0 +1,13 @@
---
title: Blog Post with JSX expressions
paragraph: JSX at the start of the line!
list: ['test-1', 'test-2', 'test-3']
---
## {frontmatter.title}
{frontmatter.paragraph}
<ul>
{frontmatter.list.map(item => <li id={item}>{item}</li>)}
</ul>

View file

@ -46,8 +46,7 @@ export async function renderMarkdown(
let parser = unified() let parser = unified()
.use(markdown) .use(markdown)
.use(isMDX ? [remarkMdxish, remarkMarkAndUnravel] : []) .use(isMDX ? [remarkMdxish, remarkMarkAndUnravel, remarkUnwrap, remarkEscape] : [])
.use([remarkUnwrap, remarkEscape]);
if (remarkPlugins.length === 0 && rehypePlugins.length === 0) { if (remarkPlugins.length === 0 && rehypePlugins.length === 0) {
remarkPlugins = [...DEFAULT_REMARK_PLUGINS]; remarkPlugins = [...DEFAULT_REMARK_PLUGINS];
@ -76,13 +75,13 @@ export async function renderMarkdown(
markdownToHtml as any, markdownToHtml as any,
{ {
allowDangerousHtml: true, allowDangerousHtml: true,
passThrough: [ passThrough: isMDX ? [
'raw', 'raw',
'mdxFlowExpression', 'mdxFlowExpression',
'mdxJsxFlowElement', 'mdxJsxFlowElement',
'mdxJsxTextElement', 'mdxJsxTextElement',
'mdxTextExpression', 'mdxTextExpression',
], ] : [],
}, },
], ],
]); ]);
@ -92,10 +91,13 @@ export async function renderMarkdown(
}); });
parser parser
.use(isMDX ? [rehypeJsx, rehypeExpressions] : [rehypeRaw]) .use(isMDX ? [
.use(rehypeEscape) rehypeJsx,
.use(rehypeIslands) rehypeExpressions,
.use([rehypeCollectHeaders]) rehypeEscape,
rehypeIslands,
rehypeCollectHeaders,
] : [rehypeCollectHeaders, rehypeRaw])
.use(rehypeStringify, { allowDangerousHtml: true }); .use(rehypeStringify, { allowDangerousHtml: true });
let result: string; let result: string;

View file

@ -1280,6 +1280,14 @@ importers:
dependencies: dependencies:
astro: link:../../.. astro: link:../../..
packages/astro/test/fixtures/astro-markdown-md-mode:
specifiers:
'@astrojs/svelte': workspace:*
astro: workspace:*
dependencies:
'@astrojs/svelte': link:../../../../integrations/svelte
astro: link:../../..
packages/astro/test/fixtures/astro-markdown-plugins: packages/astro/test/fixtures/astro-markdown-plugins:
specifiers: specifiers:
'@astrojs/preact': workspace:* '@astrojs/preact': workspace:*