Skip to content

Commit

Permalink
Feat: new legacy.astroFlavoredMarkdown flag (#4016)
Browse files Browse the repository at this point in the history
* refactor: add legacy.jsxInMarkdown flag to config

* refactor: jsxInMarkdown -> astroFlavoredMarkdown

* refactor: remove `markdown.mode`

* feat: wire up legacy.astroFlavoredMarkdown

* test: add legacy to astro-markdown fixture

* test: remark autolinking

* test: remark components

* test: remark expressions

* test: remark strictness

* chore: remove "mode" from md component

* chore: remove "mode: md" from tests

* Fixing legacy MD tests, adding named slots tests for MDX pages

* chore: update lock file

* WIP: debugging named slots in MDX

* fix: handle named slots in MDX properly

* chore: re-enabling slots tests for MDX pages

* fixing test validation for svelte & vue

* removing unused Tailwind test

* legacy flag for Markdown component tests

* adding is:raw to Markdown component test

* adding is:raw to all Markdown component test fixtures

* can't use is:raw when nesting markdown components

* another nested test can't use is:raw

* one more <Markdown> test fix

* fixing another JSX markdown component test

* chore: add changeset

* e2e tests were missing the legacy flag

* removing the broken tailwind E2E markdown page

Co-authored-by: Tony Sullivan <tony.f.sullivan@outlook.com>
Co-authored-by: Nate Moore <nate@astro.build>
  • Loading branch information
3 people authored Jul 22, 2022
1 parent c17efc1 commit 00fab4c
Show file tree
Hide file tree
Showing 64 changed files with 481 additions and 223 deletions.
20 changes: 20 additions & 0 deletions .changeset/two-hounds-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'astro': minor
'@astrojs/markdown-component': minor
'@astrojs/markdown-remark': minor
---

The use of components and JSX expressions in Markdown are no longer supported by default.

For long term support, migrate to the `@astrojs/mdx` integration for MDX support (including `.mdx` pages!).

Not ready to migrate to MDX? Add the legacy flag to your Astro config to re-enable the previous Markdown support.

```js
// https://astro.build/config
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
}
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ import preact from '@astrojs/preact';

// https://astro.build/config
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
},
integrations: [preact({ compat: true })],
});
3 changes: 3 additions & 0 deletions packages/astro/e2e/fixtures/preact-component/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
},
integrations: [preact(), mdx()],
});
3 changes: 3 additions & 0 deletions packages/astro/e2e/fixtures/react-component/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
},
integrations: [react(), mdx()],
});
3 changes: 3 additions & 0 deletions packages/astro/e2e/fixtures/solid-component/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ import solid from '@astrojs/solid-js';

// https://astro.build/config
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
},
integrations: [solid(), mdx()],
});
3 changes: 3 additions & 0 deletions packages/astro/e2e/fixtures/svelte-component/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
},
integrations: [svelte(), mdx()],
});
11 changes: 0 additions & 11 deletions packages/astro/e2e/fixtures/tailwindcss/src/pages/markdown-page.md

This file was deleted.

3 changes: 3 additions & 0 deletions packages/astro/e2e/fixtures/vue-component/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
},
integrations: [
mdx(),
vue({
Expand Down
31 changes: 10 additions & 21 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,27 +527,6 @@ export interface AstroUserConfig {
*/
drafts?: boolean;

/**
* @docs
* @name markdown.mode
* @type {'md' | 'mdx'}
* @default `mdx`
* @description
* Control whether Markdown processing is done using MDX or not.
*
* MDX processing enables you to use JSX inside your Markdown files. However, there may be instances where you don't want this behavior, and would rather use a "vanilla" Markdown processor. This field allows you to control that behavior.
*
* ```js
* {
* markdown: {
* // Example: Use non-MDX processor for Markdown files
* mode: 'md',
* }
* }
* ```
*/
mode?: 'md' | 'mdx';

/**
* @docs
* @name markdown.shikiConfig
Expand Down Expand Up @@ -716,6 +695,16 @@ export interface AstroUserConfig {
buildOptions?: never;
/** @deprecated `devOptions` has been renamed to `server` */
devOptions?: never;

legacy?: {
/**
* Enable components and JSX expressions in markdown
* Consider our MDX integration before applying this flag!
* @see https://docs.astro.build/en/guides/integrations-guide/mdx/
* Default: false
*/
astroFlavoredMarkdown?: boolean;
};
}

// NOTE(fks): We choose to keep our hand-generated AstroUserConfig interface so that
Expand Down
5 changes: 4 additions & 1 deletion packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ async function generatePath(
adapterName: undefined,
links,
logging,
markdown: astroConfig.markdown,
markdown: {
...astroConfig.markdown,
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
},
mod,
mode: opts.mode,
origin,
Expand Down
5 changes: 4 additions & 1 deletion packages/astro/src/core/build/vite-plugin-ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ function buildManifest(
routes,
site: astroConfig.site,
base: astroConfig.base,
markdown: astroConfig.markdown,
markdown: {
...astroConfig.markdown,
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
},
pageMap: null as any,
renderers: [],
entryModules,
Expand Down
12 changes: 9 additions & 3 deletions packages/astro/src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
rehypePlugins: [],
},
vite: {},
legacy: {
astroFlavoredMarkdown: false,
}
};

async function resolvePostcssConfig(inlineOptions: any, root: URL): Promise<PostCSSConfigResult> {
Expand Down Expand Up @@ -172,9 +175,6 @@ export const AstroConfigSchema = z.object({
.default({}),
markdown: z
.object({
// NOTE: "mdx" allows us to parse/compile Astro components in markdown.
// TODO: This should probably be updated to something more like "md" | "astro"
mode: z.enum(['md', 'mdx']).default('mdx'),
drafts: z.boolean().default(false),
syntaxHighlight: z
.union([z.literal('shiki'), z.literal('prism'), z.literal(false)])
Expand Down Expand Up @@ -212,6 +212,12 @@ export const AstroConfigSchema = z.object({
vite: z
.custom<ViteUserConfig>((data) => data instanceof Object && !Array.isArray(data))
.default(ASTRO_CONFIG_DEFAULTS.vite),
legacy: z
.object({
astroFlavoredMarkdown: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.legacy.astroFlavoredMarkdown),
})
.optional()
.default({}),
});

/** Turn raw config values into normalized values */
Expand Down
5 changes: 4 additions & 1 deletion packages/astro/src/core/render/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,10 @@ export async function render(
links,
styles,
logging,
markdown: astroConfig.markdown,
markdown: {
...astroConfig.markdown,
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown
},
mod,
mode,
origin,
Expand Down
35 changes: 18 additions & 17 deletions packages/astro/src/jsx-runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,24 @@ export function transformSlots(vnode: AstroVNode) {
delete child.props.slot;
delete vnode.props.children;
}
if (!Array.isArray(vnode.props.children)) return;
// Handle many children with slot attributes
vnode.props.children = vnode.props.children
.map((child) => {
if (!isVNode(child)) return child;
if (!('slot' in child.props)) return child;
const name = toSlotName(child.props.slot);
if (Array.isArray(slots[name])) {
slots[name].push(child);
} else {
slots[name] = [child];
slots[name]['$$slot'] = true;
}
delete child.props.slot;
return Empty;
})
.filter((v) => v !== Empty);
if (Array.isArray(vnode.props.children)) {
// Handle many children with slot attributes
vnode.props.children = vnode.props.children
.map((child) => {
if (!isVNode(child)) return child;
if (!('slot' in child.props)) return child;
const name = toSlotName(child.props.slot);
if (Array.isArray(slots[name])) {
slots[name].push(child);
} else {
slots[name] = [child];
slots[name]['$$slot'] = true;
}
delete child.props.slot;
return Empty;
})
.filter((v) => v !== Empty);
}
Object.assign(vnode.props, slots);
}

Expand Down
31 changes: 25 additions & 6 deletions packages/astro/src/runtime/server/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ let consoleFilterRefs = 0;
export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
switch (true) {
case vnode instanceof HTMLString:
if (vnode.toString().trim() === '') {
return '';
}
return vnode;
case typeof vnode === 'string':
return markHTMLString(escapeHTML(vnode));
Expand Down Expand Up @@ -55,6 +58,9 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
}

if (vnode.type) {
if (typeof vnode.type === 'function' && (vnode.type as any)['astro:renderer']) {
skipAstroJSXCheck.add(vnode.type)
}
if (typeof vnode.type === 'function' && vnode.props['server:root']) {
const output = await vnode.type(vnode.props ?? {});
return await renderJSX(result, output);
Expand All @@ -76,27 +82,40 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise<any> {
}

const { children = null, ...props } = vnode.props ?? {};
const slots: Record<string, any> = {
const _slots: Record<string, any> = {
default: [],
};
function extractSlots(child: any): any {
if (Array.isArray(child)) {
return child.map((c) => extractSlots(c));
}
if (!isVNode(child)) {
return slots.default.push(child);
_slots.default.push(child);
return
}
if ('slot' in child.props) {
slots[child.props.slot] = [...(slots[child.props.slot] ?? []), child];
_slots[child.props.slot] = [...(_slots[child.props.slot] ?? []), child];
delete child.props.slot;
return;
}
slots.default.push(child);
_slots.default.push(child);
}
extractSlots(children);
for (const [key, value] of Object.entries(slots)) {
slots[key] = () => renderJSX(result, value);
for (const [key, value] of Object.entries(props)) {
if (value['$$slot']) {
_slots[key] = value;
delete props[key];
}
}
const slotPromises = [];
const slots: Record<string, any> = {};
for (const [key, value] of Object.entries(_slots)) {
slotPromises.push(renderJSX(result, value).then(output => {
if (output.toString().trim().length === 0) return;
slots[key] = () => output;
}))
}
await Promise.all(slotPromises);

let output: string | AsyncIterable<string>;
if (vnode.type === ClientOnlyPlaceholder && vnode.props['client:only']) {
Expand Down
15 changes: 8 additions & 7 deletions packages/astro/src/vite-plugin-markdown/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
const filename = normalizeFilename(id);
const source = await fs.promises.readFile(filename, 'utf8');
const renderOpts = config.markdown;
const isMDX = renderOpts.mode === 'mdx';
const isAstroFlavoredMd = config.legacy.astroFlavoredMarkdown;

const fileUrl = new URL(`file://${filename}`);
const isPage = fileUrl.pathname.startsWith(resolvePages(config).pathname);
Expand All @@ -149,7 +149,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.
if (isMDX) {
if (isAstroFlavoredMd) {
markdownContent = markdownContent.replace(
/<\s*!--([^-->]*)(.*?)-->/gs,
(whole) => `{/*${whole.replace(/\*\//g, '*\u200b/')}*/}`
Expand All @@ -159,6 +159,7 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
let renderResult = await renderMarkdown(markdownContent, {
...renderOpts,
fileURL: fileUrl,
isAstroFlavoredMd,
} as any);
let { code: astroResult, metadata } = renderResult;
const { layout = '', components = '', setup = '', ...content } = frontmatter;
Expand All @@ -168,29 +169,29 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
const prelude = `---
import Slugger from 'github-slugger';
${layout ? `import Layout from '${layout}';` : ''}
${isMDX && components ? `import * from '${components}';` : ''}
${isAstroFlavoredMd && components ? `import * from '${components}';` : ''}
${hasInjectedScript ? `import '${PAGE_SSR_SCRIPT_ID}';` : ''}
${isMDX ? setup : ''}
${isAstroFlavoredMd ? setup : ''}
const slugger = new Slugger();
function $$slug(value) {
return slugger.slug(value);
}
const $$content = ${JSON.stringify(
isMDX
isAstroFlavoredMd
? content
: // Avoid stripping "setup" and "components"
// in plain MD mode
{ ...content, setup, components }
)}
---`;
const imports = `${layout ? `import Layout from '${layout}';` : ''}
${isMDX ? setup : ''}`.trim();
${isAstroFlavoredMd ? setup : ''}`.trim();

// Wrap with set:html fragment to skip
// JSX expressions and components in "plain" md mode
if (!isMDX) {
if (!isAstroFlavoredMd) {
astroResult = `<Fragment set:html={${JSON.stringify(astroResult)}} />`;
}

Expand Down
1 change: 0 additions & 1 deletion packages/astro/test/astro-markdown-md-mode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ describe('Astro Markdown - plain MD mode', () => {
root: './fixtures/astro-markdown-md-mode/',
markdown: {
syntaxHighlight: 'prism',
mode: 'md',
},
});
await fixture.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ import { defineConfig } from 'astro/config';

// https://astro.build/config
export default defineConfig({
legacy: {
astroFlavoredMarkdown: true,
},
integrations: []
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import svelte from "@astrojs/svelte";

// https://astro.build/config
export default defineConfig({
markdown: {
mode: 'md',
},
integrations: [svelte()],
site: 'https://astro.build/',
});
Loading

0 comments on commit 00fab4c

Please sign in to comment.