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:
parent
c2968b0542
commit
399d7e2698
11 changed files with 143 additions and 13 deletions
6
.changeset/moody-crabs-occur.md
Normal file
6
.changeset/moody-crabs-occur.md
Normal 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.
|
|
@ -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>`;
|
||||||
|
|
52
packages/astro/test/astro-markdown-md-mode.test.js
Normal file
52
packages/astro/test/astro-markdown-md-mode.test.js
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
11
packages/astro/test/fixtures/astro-markdown-md-mode/astro.config.mjs
vendored
Normal file
11
packages/astro/test/fixtures/astro-markdown-md-mode/astro.config.mjs
vendored
Normal 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/',
|
||||||
|
});
|
9
packages/astro/test/fixtures/astro-markdown-md-mode/package.json
vendored
Normal file
9
packages/astro/test/fixtures/astro-markdown-md-mode/package.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "@test/astro-markdown-md-mode",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/svelte": "workspace:*",
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
5
packages/astro/test/fixtures/astro-markdown-md-mode/src/components/Counter.svelte
vendored
Normal file
5
packages/astro/test/fixtures/astro-markdown-md-mode/src/components/Counter.svelte
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<script>
|
||||||
|
export let count = 0;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button onClick={() => count += 1}>{count}</button>
|
7
packages/astro/test/fixtures/astro-markdown-md-mode/src/pages/code-in-md.md
vendored
Normal file
7
packages/astro/test/fixtures/astro-markdown-md-mode/src/pages/code-in-md.md
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Fenced code blocks
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body>
|
||||||
|
<div>This should also work without any problems.</div>
|
||||||
|
</body>
|
||||||
|
```
|
5
packages/astro/test/fixtures/astro-markdown-md-mode/src/pages/components.md
vendored
Normal file
5
packages/astro/test/fixtures/astro-markdown-md-mode/src/pages/components.md
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
setup: import Counter from '../components/Counter.svelte'
|
||||||
|
---
|
||||||
|
|
||||||
|
<Counter client:load count={0}></Counter>
|
13
packages/astro/test/fixtures/astro-markdown-md-mode/src/pages/jsx-expressions.md
vendored
Normal file
13
packages/astro/test/fixtures/astro-markdown-md-mode/src/pages/jsx-expressions.md
vendored
Normal 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>
|
|
@ -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;
|
||||||
|
|
|
@ -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:*
|
||||||
|
|
Loading…
Reference in a new issue