From 00fab4ce135eb799cac69140403d7724686733d6 Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Fri, 22 Jul 2022 17:45:16 -0500 Subject: [PATCH] Feat: new `legacy.astroFlavoredMarkdown` flag (#4016) * 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 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 Co-authored-by: Nate Moore --- .changeset/two-hounds-sort.md | 20 ++ .../preact-compat-component/astro.config.mjs | 3 + .../preact-component/astro.config.mjs | 3 + .../fixtures/react-component/astro.config.mjs | 3 + .../fixtures/solid-component/astro.config.mjs | 3 + .../svelte-component/astro.config.mjs | 3 + .../tailwindcss/src/pages/markdown-page.md | 11 -- .../fixtures/vue-component/astro.config.mjs | 3 + packages/astro/src/@types/astro.ts | 31 +-- packages/astro/src/core/build/generate.ts | 5 +- .../astro/src/core/build/vite-plugin-ssr.ts | 5 +- packages/astro/src/core/config.ts | 12 +- packages/astro/src/core/render/dev/index.ts | 5 +- packages/astro/src/jsx-runtime/index.ts | 35 ++-- packages/astro/src/runtime/server/jsx.ts | 31 ++- .../astro/src/vite-plugin-markdown/index.ts | 15 +- .../astro/test/astro-markdown-md-mode.test.js | 1 - .../astro-markdown-css/astro.config.mjs | 3 + .../astro-markdown-md-mode/astro.config.mjs | 3 - .../fixtures/astro-markdown/astro.config.mjs | 3 + .../import-ts-with-js/astro.config.mjs | 9 + .../fixtures/slots-preact/astro.config.mjs | 8 +- .../test/fixtures/slots-preact/package.json | 1 + .../fixtures/slots-preact/src/pages/mdx.mdx | 7 + .../fixtures/slots-react/astro.config.mjs | 8 +- .../test/fixtures/slots-react/package.json | 1 + .../fixtures/slots-react/src/pages/mdx.mdx | 7 + .../fixtures/slots-solid/astro.config.mjs | 8 +- .../test/fixtures/slots-solid/package.json | 1 + .../fixtures/slots-solid/src/pages/mdx.mdx | 7 + .../fixtures/slots-svelte/astro.config.mjs | 8 +- .../test/fixtures/slots-svelte/package.json | 1 + .../fixtures/slots-svelte/src/pages/mdx.mdx | 7 + .../test/fixtures/slots-vue/astro.config.mjs | 8 +- .../test/fixtures/slots-vue/package.json | 1 + .../test/fixtures/slots-vue/src/pages/mdx.mdx | 7 + .../fixtures/tailwindcss/astro.config.mjs | 5 +- packages/astro/test/slots-preact.test.js | 20 ++ packages/astro/test/slots-react.test.js | 20 ++ packages/astro/test/slots-solid.test.js | 20 ++ packages/astro/test/slots-svelte.test.js | 20 ++ packages/astro/test/slots-vue.test.js | 20 ++ packages/markdown/component/Markdown.astro | 1 - .../src/pages/astro.astro | 2 +- .../langs/src/pages/astro.astro | 2 +- .../normal/src/pages/astro.astro | 2 +- .../themes-custom/src/pages/astro.astro | 2 +- .../themes-integrated/src/pages/astro.astro | 2 +- .../wrap-false/src/pages/astro.astro | 2 +- .../wrap-null/src/pages/astro.astro | 2 +- .../wrap-true/src/pages/astro.astro | 2 +- .../fixtures/astro-markdown/astro.config.mjs | 3 + .../astro-markdown/src/pages/code.astro | 2 +- .../astro-markdown/src/pages/complex.astro | 2 - .../astro-markdown/src/pages/deep.astro | 6 +- .../src/pages/nested-list.astro | 2 +- .../src/pages/no-elements.astro | 2 +- packages/markdown/remark/src/index.ts | 11 +- packages/markdown/remark/src/types.ts | 1 + .../markdown/remark/test/autolinking.test.js | 176 ++++++++++-------- .../markdown/remark/test/components.test.js | 22 ++- .../markdown/remark/test/expressions.test.js | 30 +-- .../markdown/remark/test/strictness.test.js | 28 +-- pnpm-lock.yaml | 10 + 64 files changed, 481 insertions(+), 223 deletions(-) create mode 100644 .changeset/two-hounds-sort.md delete mode 100644 packages/astro/e2e/fixtures/tailwindcss/src/pages/markdown-page.md create mode 100644 packages/astro/test/fixtures/import-ts-with-js/astro.config.mjs create mode 100644 packages/astro/test/fixtures/slots-preact/src/pages/mdx.mdx create mode 100644 packages/astro/test/fixtures/slots-react/src/pages/mdx.mdx create mode 100644 packages/astro/test/fixtures/slots-solid/src/pages/mdx.mdx create mode 100644 packages/astro/test/fixtures/slots-svelte/src/pages/mdx.mdx create mode 100644 packages/astro/test/fixtures/slots-vue/src/pages/mdx.mdx diff --git a/.changeset/two-hounds-sort.md b/.changeset/two-hounds-sort.md new file mode 100644 index 000000000..b6be5ea04 --- /dev/null +++ b/.changeset/two-hounds-sort.md @@ -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, + } +}); +``` diff --git a/packages/astro/e2e/fixtures/preact-compat-component/astro.config.mjs b/packages/astro/e2e/fixtures/preact-compat-component/astro.config.mjs index 7d2c8a855..2cd377763 100644 --- a/packages/astro/e2e/fixtures/preact-compat-component/astro.config.mjs +++ b/packages/astro/e2e/fixtures/preact-compat-component/astro.config.mjs @@ -3,5 +3,8 @@ import preact from '@astrojs/preact'; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [preact({ compat: true })], }); diff --git a/packages/astro/e2e/fixtures/preact-component/astro.config.mjs b/packages/astro/e2e/fixtures/preact-component/astro.config.mjs index 7a8aef521..bcaa451eb 100644 --- a/packages/astro/e2e/fixtures/preact-component/astro.config.mjs +++ b/packages/astro/e2e/fixtures/preact-component/astro.config.mjs @@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx'; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [preact(), mdx()], }); diff --git a/packages/astro/e2e/fixtures/react-component/astro.config.mjs b/packages/astro/e2e/fixtures/react-component/astro.config.mjs index 5c044b69d..badddf1d3 100644 --- a/packages/astro/e2e/fixtures/react-component/astro.config.mjs +++ b/packages/astro/e2e/fixtures/react-component/astro.config.mjs @@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx'; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [react(), mdx()], }); diff --git a/packages/astro/e2e/fixtures/solid-component/astro.config.mjs b/packages/astro/e2e/fixtures/solid-component/astro.config.mjs index f527c69b4..35d38c8f1 100644 --- a/packages/astro/e2e/fixtures/solid-component/astro.config.mjs +++ b/packages/astro/e2e/fixtures/solid-component/astro.config.mjs @@ -4,5 +4,8 @@ import solid from '@astrojs/solid-js'; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [solid(), mdx()], }); diff --git a/packages/astro/e2e/fixtures/svelte-component/astro.config.mjs b/packages/astro/e2e/fixtures/svelte-component/astro.config.mjs index bc5c6c9bb..99f557d43 100644 --- a/packages/astro/e2e/fixtures/svelte-component/astro.config.mjs +++ b/packages/astro/e2e/fixtures/svelte-component/astro.config.mjs @@ -4,5 +4,8 @@ import mdx from '@astrojs/mdx'; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [svelte(), mdx()], }); diff --git a/packages/astro/e2e/fixtures/tailwindcss/src/pages/markdown-page.md b/packages/astro/e2e/fixtures/tailwindcss/src/pages/markdown-page.md deleted file mode 100644 index e4c6b6bc9..000000000 --- a/packages/astro/e2e/fixtures/tailwindcss/src/pages/markdown-page.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "Markdown + Tailwind" -setup: | - import Button from '../components/Button.astro'; - import Complex from '../components/Complex.astro'; ---- - -
- - -
\ No newline at end of file diff --git a/packages/astro/e2e/fixtures/vue-component/astro.config.mjs b/packages/astro/e2e/fixtures/vue-component/astro.config.mjs index 9a3f12727..84c024e68 100644 --- a/packages/astro/e2e/fixtures/vue-component/astro.config.mjs +++ b/packages/astro/e2e/fixtures/vue-component/astro.config.mjs @@ -4,6 +4,9 @@ import mdx from '@astrojs/mdx'; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [ mdx(), vue({ diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 2e6b167c0..3c73edbad 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -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 @@ -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 diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 2a181fe7f..55cd4e8c1 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -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, diff --git a/packages/astro/src/core/build/vite-plugin-ssr.ts b/packages/astro/src/core/build/vite-plugin-ssr.ts index 2c3b88a11..d8e6ff728 100644 --- a/packages/astro/src/core/build/vite-plugin-ssr.ts +++ b/packages/astro/src/core/build/vite-plugin-ssr.ts @@ -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, diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts index bbfad0439..42e44c62a 100644 --- a/packages/astro/src/core/config.ts +++ b/packages/astro/src/core/config.ts @@ -50,6 +50,9 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = { rehypePlugins: [], }, vite: {}, + legacy: { + astroFlavoredMarkdown: false, + } }; async function resolvePostcssConfig(inlineOptions: any, root: URL): Promise { @@ -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)]) @@ -212,6 +212,12 @@ export const AstroConfigSchema = z.object({ vite: z .custom((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 */ diff --git a/packages/astro/src/core/render/dev/index.ts b/packages/astro/src/core/render/dev/index.ts index 428c30edf..d4af8c1ed 100644 --- a/packages/astro/src/core/render/dev/index.ts +++ b/packages/astro/src/core/render/dev/index.ts @@ -173,7 +173,10 @@ export async function render( links, styles, logging, - markdown: astroConfig.markdown, + markdown: { + ...astroConfig.markdown, + isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown + }, mod, mode, origin, diff --git a/packages/astro/src/jsx-runtime/index.ts b/packages/astro/src/jsx-runtime/index.ts index fdabff324..20630a82a 100644 --- a/packages/astro/src/jsx-runtime/index.ts +++ b/packages/astro/src/jsx-runtime/index.ts @@ -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); } diff --git a/packages/astro/src/runtime/server/jsx.ts b/packages/astro/src/runtime/server/jsx.ts index 179fb172f..44d69e702 100644 --- a/packages/astro/src/runtime/server/jsx.ts +++ b/packages/astro/src/runtime/server/jsx.ts @@ -19,6 +19,9 @@ let consoleFilterRefs = 0; export async function renderJSX(result: SSRResult, vnode: any): Promise { switch (true) { case vnode instanceof HTMLString: + if (vnode.toString().trim() === '') { + return ''; + } return vnode; case typeof vnode === 'string': return markHTMLString(escapeHTML(vnode)); @@ -55,6 +58,9 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise { } 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); @@ -76,7 +82,7 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise { } const { children = null, ...props } = vnode.props ?? {}; - const slots: Record = { + const _slots: Record = { default: [], }; function extractSlots(child: any): any { @@ -84,19 +90,32 @@ export async function renderJSX(result: SSRResult, vnode: any): Promise { 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 = {}; + 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; if (vnode.type === ClientOnlyPlaceholder && vnode.props['client:only']) { diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts index 027407d34..fdd009f8a 100644 --- a/packages/astro/src/vite-plugin-markdown/index.ts +++ b/packages/astro/src/vite-plugin-markdown/index.ts @@ -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); @@ -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/')}*/}` @@ -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; @@ -168,9 +169,9 @@ 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) { @@ -178,7 +179,7 @@ function $$slug(value) { } const $$content = ${JSON.stringify( - isMDX + isAstroFlavoredMd ? content : // Avoid stripping "setup" and "components" // in plain MD mode @@ -186,11 +187,11 @@ const $$content = ${JSON.stringify( )} ---`; 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 = ``; } diff --git a/packages/astro/test/astro-markdown-md-mode.test.js b/packages/astro/test/astro-markdown-md-mode.test.js index 46056605f..89f2a26ab 100644 --- a/packages/astro/test/astro-markdown-md-mode.test.js +++ b/packages/astro/test/astro-markdown-md-mode.test.js @@ -38,7 +38,6 @@ describe('Astro Markdown - plain MD mode', () => { root: './fixtures/astro-markdown-md-mode/', markdown: { syntaxHighlight: 'prism', - mode: 'md', }, }); await fixture.build(); diff --git a/packages/astro/test/fixtures/astro-markdown-css/astro.config.mjs b/packages/astro/test/fixtures/astro-markdown-css/astro.config.mjs index 50eaa792c..410c20408 100644 --- a/packages/astro/test/fixtures/astro-markdown-css/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-markdown-css/astro.config.mjs @@ -2,5 +2,8 @@ import { defineConfig } from 'astro/config'; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [] }); diff --git a/packages/astro/test/fixtures/astro-markdown-md-mode/astro.config.mjs b/packages/astro/test/fixtures/astro-markdown-md-mode/astro.config.mjs index 3fab631f3..908e3442f 100644 --- a/packages/astro/test/fixtures/astro-markdown-md-mode/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-markdown-md-mode/astro.config.mjs @@ -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/', }); diff --git a/packages/astro/test/fixtures/astro-markdown/astro.config.mjs b/packages/astro/test/fixtures/astro-markdown/astro.config.mjs index be33a26cc..baefed8cc 100644 --- a/packages/astro/test/fixtures/astro-markdown/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-markdown/astro.config.mjs @@ -6,4 +6,7 @@ import svelte from "@astrojs/svelte"; export default defineConfig({ integrations: [preact(), svelte()], site: 'https://astro.build/', + legacy: { + astroFlavoredMarkdown: true, + } }); diff --git a/packages/astro/test/fixtures/import-ts-with-js/astro.config.mjs b/packages/astro/test/fixtures/import-ts-with-js/astro.config.mjs new file mode 100644 index 000000000..410c20408 --- /dev/null +++ b/packages/astro/test/fixtures/import-ts-with-js/astro.config.mjs @@ -0,0 +1,9 @@ +import { defineConfig } from 'astro/config'; + +// https://astro.build/config +export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, + integrations: [] +}); diff --git a/packages/astro/test/fixtures/slots-preact/astro.config.mjs b/packages/astro/test/fixtures/slots-preact/astro.config.mjs index cd324a40f..01ce725cc 100644 --- a/packages/astro/test/fixtures/slots-preact/astro.config.mjs +++ b/packages/astro/test/fixtures/slots-preact/astro.config.mjs @@ -1,7 +1,11 @@ import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; import preact from '@astrojs/preact'; // https://astro.build/config export default defineConfig({ - integrations: [preact()], -}); \ No newline at end of file + legacy: { + astroFlavoredMarkdown: true, + }, + integrations: [preact(), mdx()], +}); diff --git a/packages/astro/test/fixtures/slots-preact/package.json b/packages/astro/test/fixtures/slots-preact/package.json index 95f33ee50..240071578 100644 --- a/packages/astro/test/fixtures/slots-preact/package.json +++ b/packages/astro/test/fixtures/slots-preact/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "dependencies": { + "@astrojs/mdx": "workspace:*", "@astrojs/preact": "workspace:*", "astro": "workspace:*" } diff --git a/packages/astro/test/fixtures/slots-preact/src/pages/mdx.mdx b/packages/astro/test/fixtures/slots-preact/src/pages/mdx.mdx new file mode 100644 index 000000000..6b7bcea55 --- /dev/null +++ b/packages/astro/test/fixtures/slots-preact/src/pages/mdx.mdx @@ -0,0 +1,7 @@ +import Counter from '../components/Counter.jsx' + +# Slots: Preact + +

Hello world!

+

/ Named

+

/ Dash Case

diff --git a/packages/astro/test/fixtures/slots-react/astro.config.mjs b/packages/astro/test/fixtures/slots-react/astro.config.mjs index f03a45258..20fa1428e 100644 --- a/packages/astro/test/fixtures/slots-react/astro.config.mjs +++ b/packages/astro/test/fixtures/slots-react/astro.config.mjs @@ -1,7 +1,11 @@ import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; import react from '@astrojs/react'; // https://astro.build/config export default defineConfig({ - integrations: [react()], -}); \ No newline at end of file + legacy: { + astroFlavoredMarkdown: true, + }, + integrations: [react(), mdx()], +}); diff --git a/packages/astro/test/fixtures/slots-react/package.json b/packages/astro/test/fixtures/slots-react/package.json index 8159a5624..bea72fe3d 100644 --- a/packages/astro/test/fixtures/slots-react/package.json +++ b/packages/astro/test/fixtures/slots-react/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "dependencies": { + "@astrojs/mdx": "workspace:*", "@astrojs/react": "workspace:*", "astro": "workspace:*", "react": "^18.1.0", diff --git a/packages/astro/test/fixtures/slots-react/src/pages/mdx.mdx b/packages/astro/test/fixtures/slots-react/src/pages/mdx.mdx new file mode 100644 index 000000000..f50196171 --- /dev/null +++ b/packages/astro/test/fixtures/slots-react/src/pages/mdx.mdx @@ -0,0 +1,7 @@ +import Counter from '../components/Counter.jsx' + +# Slots: React + +

Hello world!

+

/ Named

+

/ Dash Case

diff --git a/packages/astro/test/fixtures/slots-solid/astro.config.mjs b/packages/astro/test/fixtures/slots-solid/astro.config.mjs index 6bc082cce..35d38c8f1 100644 --- a/packages/astro/test/fixtures/slots-solid/astro.config.mjs +++ b/packages/astro/test/fixtures/slots-solid/astro.config.mjs @@ -1,7 +1,11 @@ import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; import solid from '@astrojs/solid-js'; // https://astro.build/config export default defineConfig({ - integrations: [solid()], -}); \ No newline at end of file + legacy: { + astroFlavoredMarkdown: true, + }, + integrations: [solid(), mdx()], +}); diff --git a/packages/astro/test/fixtures/slots-solid/package.json b/packages/astro/test/fixtures/slots-solid/package.json index e378bd772..be9555ace 100644 --- a/packages/astro/test/fixtures/slots-solid/package.json +++ b/packages/astro/test/fixtures/slots-solid/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "dependencies": { + "@astrojs/mdx": "workspace:*", "@astrojs/solid-js": "workspace:*", "astro": "workspace:*" } diff --git a/packages/astro/test/fixtures/slots-solid/src/pages/mdx.mdx b/packages/astro/test/fixtures/slots-solid/src/pages/mdx.mdx new file mode 100644 index 000000000..679f42ab9 --- /dev/null +++ b/packages/astro/test/fixtures/slots-solid/src/pages/mdx.mdx @@ -0,0 +1,7 @@ +import Counter from '../components/Counter.jsx' + +# Slots: Solid + +

Hello world!

+

/ Named

+

/ Dash Case

diff --git a/packages/astro/test/fixtures/slots-svelte/astro.config.mjs b/packages/astro/test/fixtures/slots-svelte/astro.config.mjs index dbf6d6b8f..afd7dd326 100644 --- a/packages/astro/test/fixtures/slots-svelte/astro.config.mjs +++ b/packages/astro/test/fixtures/slots-svelte/astro.config.mjs @@ -1,7 +1,11 @@ import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; import svelte from '@astrojs/svelte'; // https://astro.build/config export default defineConfig({ - integrations: [svelte()], -}); \ No newline at end of file + legacy: { + astroFlavoredMarkdown: true, + }, + integrations: [svelte(), mdx()], +}); diff --git a/packages/astro/test/fixtures/slots-svelte/package.json b/packages/astro/test/fixtures/slots-svelte/package.json index 53af8ea93..95dbd239d 100644 --- a/packages/astro/test/fixtures/slots-svelte/package.json +++ b/packages/astro/test/fixtures/slots-svelte/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "dependencies": { + "@astrojs/mdx": "workspace:*", "@astrojs/svelte": "workspace:*", "astro": "workspace:*" } diff --git a/packages/astro/test/fixtures/slots-svelte/src/pages/mdx.mdx b/packages/astro/test/fixtures/slots-svelte/src/pages/mdx.mdx new file mode 100644 index 000000000..f93df5a10 --- /dev/null +++ b/packages/astro/test/fixtures/slots-svelte/src/pages/mdx.mdx @@ -0,0 +1,7 @@ +import Counter from '../components/Counter.svelte' + +# Slots: Svelte + +

Hello world!

+

/ Named

+

/ Dash Case

diff --git a/packages/astro/test/fixtures/slots-vue/astro.config.mjs b/packages/astro/test/fixtures/slots-vue/astro.config.mjs index 8a3a38574..1fbe9ba8e 100644 --- a/packages/astro/test/fixtures/slots-vue/astro.config.mjs +++ b/packages/astro/test/fixtures/slots-vue/astro.config.mjs @@ -1,7 +1,11 @@ import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; import vue from '@astrojs/vue'; // https://astro.build/config export default defineConfig({ - integrations: [vue()], -}); \ No newline at end of file + legacy: { + astroFlavoredMarkdown: true, + }, + integrations: [vue(), mdx()], +}); diff --git a/packages/astro/test/fixtures/slots-vue/package.json b/packages/astro/test/fixtures/slots-vue/package.json index f6d90b40d..4e2e7dd06 100644 --- a/packages/astro/test/fixtures/slots-vue/package.json +++ b/packages/astro/test/fixtures/slots-vue/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "dependencies": { + "@astrojs/mdx": "workspace:*", "@astrojs/vue": "workspace:*", "astro": "workspace:*" } diff --git a/packages/astro/test/fixtures/slots-vue/src/pages/mdx.mdx b/packages/astro/test/fixtures/slots-vue/src/pages/mdx.mdx new file mode 100644 index 000000000..65c835311 --- /dev/null +++ b/packages/astro/test/fixtures/slots-vue/src/pages/mdx.mdx @@ -0,0 +1,7 @@ +import Counter from '../components/Counter.vue' + +# Slots: Vue + +

Hello world!

+

/ Named

+

/ Dash Case

diff --git a/packages/astro/test/fixtures/tailwindcss/astro.config.mjs b/packages/astro/test/fixtures/tailwindcss/astro.config.mjs index c3f6aad6f..34c98eaf8 100644 --- a/packages/astro/test/fixtures/tailwindcss/astro.config.mjs +++ b/packages/astro/test/fixtures/tailwindcss/astro.config.mjs @@ -3,10 +3,13 @@ import tailwind from '@astrojs/tailwind'; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [tailwind()], vite: { build: { assetsInlineLimit: 0, }, }, -}); \ No newline at end of file +}); diff --git a/packages/astro/test/slots-preact.test.js b/packages/astro/test/slots-preact.test.js index 4cfb7218f..b7330a182 100644 --- a/packages/astro/test/slots-preact.test.js +++ b/packages/astro/test/slots-preact.test.js @@ -53,4 +53,24 @@ describe('Slots: Preact', () => { expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); }); }); + + describe('For MDX Pages', () => { + it('Renders default slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#content').text().trim()).to.equal('Hello world!'); + }); + + it('Renders named slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#named').text().trim()).to.equal('Fallback / Named'); + }); + + it('Converts dash-case slot to camelCase', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); + }); + }); }); diff --git a/packages/astro/test/slots-react.test.js b/packages/astro/test/slots-react.test.js index f3356dd58..8e61d41ec 100644 --- a/packages/astro/test/slots-react.test.js +++ b/packages/astro/test/slots-react.test.js @@ -53,4 +53,24 @@ describe('Slots: React', () => { expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); }); }); + + describe('For MDX Pages', () => { + it('Renders default slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#content').text().trim()).to.equal('Hello world!'); + }); + + it('Renders named slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#named').text().trim()).to.equal('Fallback / Named'); + }); + + it('Converts dash-case slot to camelCase', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); + }); + }); }); diff --git a/packages/astro/test/slots-solid.test.js b/packages/astro/test/slots-solid.test.js index ecef1839e..60e3231c9 100644 --- a/packages/astro/test/slots-solid.test.js +++ b/packages/astro/test/slots-solid.test.js @@ -53,4 +53,24 @@ describe('Slots: Solid', () => { expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); }); }); + + describe('For MDX Pages', () => { + it('Renders default slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#content').text().trim()).to.equal('Hello world!'); + }); + + it('Renders named slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#named').text().trim()).to.equal('Fallback / Named'); + }); + + it('Converts dash-case slot to camelCase', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); + }); + }); }); diff --git a/packages/astro/test/slots-svelte.test.js b/packages/astro/test/slots-svelte.test.js index 917677587..a96a397e3 100644 --- a/packages/astro/test/slots-svelte.test.js +++ b/packages/astro/test/slots-svelte.test.js @@ -53,4 +53,24 @@ describe('Slots: Svelte', () => { expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); }); }); + + describe('For MDX Pages', () => { + it('Renders default slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#content').text().trim()).to.equal('Hello world!'); + }); + + it('Renders named slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#named').text().trim()).to.equal('Fallback / Named'); + }); + + it('Preserves dash-case slot', async () => { + const html = await fixture.readFile('/index.html'); + const $ = cheerio.load(html); + expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); + }); + }); }); diff --git a/packages/astro/test/slots-vue.test.js b/packages/astro/test/slots-vue.test.js index 64ec656e3..2999904b7 100644 --- a/packages/astro/test/slots-vue.test.js +++ b/packages/astro/test/slots-vue.test.js @@ -53,4 +53,24 @@ describe('Slots: Vue', () => { expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); }); }); + + describe('For MDX Pages', () => { + it('Renders default slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#content').text().trim()).to.equal('Hello world!'); + }); + + it('Renders named slot', async () => { + const html = await fixture.readFile('/mdx/index.html'); + const $ = cheerio.load(html); + expect($('#named').text().trim()).to.equal('Fallback / Named'); + }); + + it('Converts dash-case slot to camelCase', async () => { + const html = await fixture.readFile('/markdown/index.html'); + const $ = cheerio.load(html); + expect($('#dash-case').text().trim()).to.equal('Fallback / Dash Case'); + }); + }); }); diff --git a/packages/markdown/component/Markdown.astro b/packages/markdown/component/Markdown.astro index a88fdbf34..7e346b4cc 100644 --- a/packages/markdown/component/Markdown.astro +++ b/packages/markdown/component/Markdown.astro @@ -41,7 +41,6 @@ if (!content) { if (content) { htmlContent = await (Astro as any).__renderMarkdown(content, { - mode: 'md', $: { scopedClassName: className, }, diff --git a/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/pages/astro.astro index f3a019f58..f039638e4 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/pages/astro.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown-plugins/src/pages/astro.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro'; --- - + # Hello world diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/pages/astro.astro index d6740987d..68b22b7e8 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/pages/astro.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/langs/src/pages/astro.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro'; --- - + # Hello world ```rinfo diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/pages/astro.astro index 890471468..473b21dc1 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/pages/astro.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/normal/src/pages/astro.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro'; --- - + # Hello world ``` diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/pages/astro.astro index ec99141e1..aae5ddbba 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/pages/astro.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-custom/src/pages/astro.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro'; --- - + # Hello world ```js diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/astro.astro index ec99141e1..aae5ddbba 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/astro.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/themes-integrated/src/pages/astro.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro'; --- - + # Hello world ```js diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/pages/astro.astro index 890471468..473b21dc1 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/pages/astro.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-false/src/pages/astro.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro'; --- - + # Hello world ``` diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/pages/astro.astro index 890471468..473b21dc1 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/pages/astro.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-null/src/pages/astro.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro'; --- - + # Hello world ``` diff --git a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/pages/astro.astro b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/pages/astro.astro index 890471468..473b21dc1 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/pages/astro.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown-shiki/wrap-true/src/pages/astro.astro @@ -4,7 +4,7 @@ import Layout from '../layouts/content.astro'; --- - + # Hello world ``` diff --git a/packages/markdown/component/test/fixtures/astro-markdown/astro.config.mjs b/packages/markdown/component/test/fixtures/astro-markdown/astro.config.mjs index be33a26cc..405776c2b 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown/astro.config.mjs +++ b/packages/markdown/component/test/fixtures/astro-markdown/astro.config.mjs @@ -4,6 +4,9 @@ import svelte from "@astrojs/svelte"; // https://astro.build/config export default defineConfig({ + legacy: { + astroFlavoredMarkdown: true, + }, integrations: [preact(), svelte()], site: 'https://astro.build/', }); diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code.astro index c3f9af2d3..7e2b08187 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/code.astro @@ -4,7 +4,7 @@ const title = 'My Blog Post'; const description = 'This is a post about some stuff.'; --- - + ## Interesting Topic ```js diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/complex.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/complex.astro index 37d3807e4..caef4e8bf 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/complex.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/complex.astro @@ -2,7 +2,6 @@ import Markdown from '@astrojs/markdown-component'; import Layout from '../layouts/content.astro'; import Hello from '../components/Hello.jsx'; -import Counter from '../components/Counter.jsx'; const title = 'My Blog Post'; const description = 'This is a post about some stuff.'; @@ -13,6 +12,5 @@ const description = 'This is a post about some stuff.'; ## Interesting Topic - diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/deep.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/deep.astro index 32117a505..7de9ec598 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/deep.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/deep.astro @@ -10,19 +10,19 @@ const description = 'This is a post about some stuff.';
- + ## A
- + ## B
- + ## C
diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/nested-list.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/nested-list.astro index 3795f78f7..771850bc8 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/nested-list.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/nested-list.astro @@ -20,7 +20,7 @@ const content = `

Welcome to Astro

- + - list - nested list diff --git a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/no-elements.astro b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/no-elements.astro index aca4e31d5..5671ffaa4 100644 --- a/packages/markdown/component/test/fixtures/astro-markdown/src/pages/no-elements.astro +++ b/packages/markdown/component/test/fixtures/astro-markdown/src/pages/no-elements.astro @@ -2,4 +2,4 @@ import Markdown from '@astrojs/markdown-component'; --- - + diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index 1d0d1f946..706fc7458 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -29,24 +29,23 @@ export const DEFAULT_REHYPE_PLUGINS = []; /** Shared utility for rendering markdown */ export async function renderMarkdown( content: string, - opts: MarkdownRenderingOptions = {} + opts: MarkdownRenderingOptions, ): Promise { let { fileURL, - mode = 'mdx', syntaxHighlight = 'shiki', shikiConfig = {}, remarkPlugins = [], rehypePlugins = [], + isAstroFlavoredMd = false, } = opts; const input = new VFile({ value: content, path: fileURL }); const scopedClassName = opts.$?.scopedClassName; - const isMDX = mode === 'mdx'; const { headers, rehypeCollectHeaders } = createCollectHeaders(); let parser = unified() .use(markdown) - .use(isMDX ? [remarkMdxish, remarkMarkAndUnravel, remarkUnwrap, remarkEscape] : []); + .use(isAstroFlavoredMd ? [remarkMdxish, remarkMarkAndUnravel, remarkUnwrap, remarkEscape] : []); if (remarkPlugins.length === 0 && rehypePlugins.length === 0) { remarkPlugins = [...DEFAULT_REMARK_PLUGINS]; @@ -75,7 +74,7 @@ export async function renderMarkdown( markdownToHtml as any, { allowDangerousHtml: true, - passThrough: isMDX + passThrough: isAstroFlavoredMd ? [ 'raw', 'mdxFlowExpression', @@ -94,7 +93,7 @@ export async function renderMarkdown( parser .use( - isMDX + isAstroFlavoredMd ? [rehypeJsx, rehypeExpressions, rehypeEscape, rehypeIslands, rehypeCollectHeaders] : [rehypeCollectHeaders, rehypeRaw] ) diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index ddd96b3d1..0a72edf0c 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -41,6 +41,7 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions { $?: { scopedClassName: string | null; }; + isAstroFlavoredMd?: boolean; } export interface MarkdownHeader { diff --git a/packages/markdown/remark/test/autolinking.test.js b/packages/markdown/remark/test/autolinking.test.js index 513d12ac6..61117bddc 100644 --- a/packages/markdown/remark/test/autolinking.test.js +++ b/packages/markdown/remark/test/autolinking.test.js @@ -2,91 +2,107 @@ import { renderMarkdown } from '../dist/index.js'; import chai from 'chai'; describe('autolinking', () => { - it('autolinks URLs starting with a protocol in plain text', async () => { - const { code } = await renderMarkdown(`See https://example.com for more.`, {}); - - chai - .expect(code.replace(/\n/g, '')) - .to.equal(`

See https://example.com for more.

`); - }); - - it('autolinks URLs starting with "www." in plain text', async () => { - const { code } = await renderMarkdown(`See www.example.com for more.`, {}); - - chai - .expect(code.trim()) - .to.equal(`

See www.example.com for more.

`); - }); - - it('does not autolink URLs in code blocks', async () => { - const { code } = await renderMarkdown( - 'See `https://example.com` or `www.example.com` for more.', - {} - ); - - chai - .expect(code.trim()) - .to.equal( - `

See https://example.com or ` + - `www.example.com for more.

` + describe('plain md', () => { + it('autolinks URLs starting with a protocol in plain text', async () => { + const { code } = await renderMarkdown(`See https://example.com for more.`, {}); + + chai + .expect(code.replace(/\n/g, '')) + .to.equal(`

See https://example.com for more.

`); + }); + + it('autolinks URLs starting with "www." in plain text', async () => { + const { code } = await renderMarkdown(`See www.example.com for more.`, {}); + + chai + .expect(code.trim()) + .to.equal(`

See www.example.com for more.

`); + }); + + it('does not autolink URLs in code blocks', async () => { + const { code } = await renderMarkdown( + 'See `https://example.com` or `www.example.com` for more.', + {} ); + + chai + .expect(code.trim()) + .to.equal( + `

See https://example.com or ` + + `www.example.com for more.

` + ); + }); }); - it('does not autolink URLs in fenced code blocks', async () => { - const { code } = await renderMarkdown( - 'Example:\n```\nGo to https://example.com or www.example.com now.\n```', - {} - ); + describe('astro-flavored md', () => { + const renderAstroMd = text => renderMarkdown(text, { isAstroFlavoredMd: true }); - chai - .expect(code) - .to.contain(`
 {
-		const { code } = await renderMarkdown(
-			`See [http://example.com](http://example.com) or ` +
-				`https://example.com`,
-			{}
-		);
-
-		chai
-			.expect(code.replace(/\n/g, ''))
-			.to.equal(
-				`

See http://example.com or ` + - `https://example.com

` + it('does not autolink URLs in code blocks', async () => { + const { code } = await renderAstroMd( + 'See `https://example.com` or `www.example.com` for more.', + {} ); - }); - - it('does not autolink URLs starting with "www." when nested inside links', async () => { - const { code } = await renderMarkdown( - `See [www.example.com](https://www.example.com) or ` + - `www.example.com`, - {} - ); - - chai - .expect(code.replace(/\n/g, '')) - .to.equal( - `

See www.example.com or ` + - `www.example.com

` + + chai + .expect(code.trim()) + .to.equal( + `

See https://example.com or ` + + `www.example.com for more.

` + ); + }); + + it('does not autolink URLs in fenced code blocks', async () => { + const { code } = await renderAstroMd( + 'Example:\n```\nGo to https://example.com or www.example.com now.\n```' ); - }); - - it('does not autolink URLs when nested several layers deep inside links', async () => { - const { code } = await renderMarkdown( - `**Visit _our www.example.com or ` + - `http://localhost pages_ for more!**`, - {} - ); - - chai - .expect(code.replace(/\n/g, '')) - .to.equal( - `` + - `Visit our www.example.com or http://localhost pages for more!` + - `` + + chai + .expect(code) + .to.contain(`
 {
+			const { code } = await renderAstroMd(
+				`See [http://example.com](http://example.com) or ` +
+					`https://example.com`
 			);
-	});
+	
+			chai
+				.expect(code.replace(/\n/g, ''))
+				.to.equal(
+					`

See http://example.com or ` + + `https://example.com

` + ); + }); + + it('does not autolink URLs starting with "www." when nested inside links', async () => { + const { code } = await renderAstroMd( + `See [www.example.com](https://www.example.com) or ` + + `www.example.com` + ); + + chai + .expect(code.replace(/\n/g, '')) + .to.equal( + `

See www.example.com or ` + + `www.example.com

` + ); + }); + + it('does not autolink URLs when nested several layers deep inside links', async () => { + const { code } = await renderAstroMd( + `**Visit _our www.example.com or ` + + `http://localhost pages_ for more!**` + ); + + chai + .expect(code.replace(/\n/g, '')) + .to.equal( + `` + + `Visit our www.example.com or http://localhost pages for more!` + + `` + ); + }); + }) }); diff --git a/packages/markdown/remark/test/components.test.js b/packages/markdown/remark/test/components.test.js index 4e06d7301..e60469426 100644 --- a/packages/markdown/remark/test/components.test.js +++ b/packages/markdown/remark/test/components.test.js @@ -2,32 +2,34 @@ import { renderMarkdown } from '../dist/index.js'; import chai from 'chai'; describe('components', () => { + const renderAstroMd = (text) => renderMarkdown(text, { isAstroFlavoredMd: true }); + it('should be able to serialize string', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``); chai.expect(code).to.equal(``); }); it('should be able to serialize boolean attribute', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``); chai.expect(code).to.equal(``); }); it('should be able to serialize array', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``); chai.expect(code).to.equal(``); }); it('should be able to serialize object', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``); chai.expect(code).to.equal(``); }); it('should be able to serialize empty attribute', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``); chai.expect(code).to.equal(``); }); @@ -35,25 +37,25 @@ describe('components', () => { // Notable omission: shorthand attribute it('should be able to serialize spread attribute', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``); chai.expect(code).to.equal(``); }); it('should allow client:* directives', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``); chai.expect(code).to.equal(``); }); it('should normalize children', async () => { - const { code } = await renderMarkdown(`Hello world!`, {}); + const { code } = await renderAstroMd(`Hello world!`); chai.expect(code).to.equal(`Hello world!`); }); it('should be able to nest components', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( `Hello world!`, {} ); @@ -64,7 +66,7 @@ describe('components', () => { }); it('should allow markdown without many spaces', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( ` # Hello world! `, diff --git a/packages/markdown/remark/test/expressions.test.js b/packages/markdown/remark/test/expressions.test.js index 828f70561..b2cb1a73d 100644 --- a/packages/markdown/remark/test/expressions.test.js +++ b/packages/markdown/remark/test/expressions.test.js @@ -1,21 +1,23 @@ import { renderMarkdown } from '../dist/index.js'; -import chai, { expect } from 'chai'; +import chai from 'chai'; describe('expressions', () => { + const renderAstroMd = (text, opts) => renderMarkdown(text, { isAstroFlavoredMd: true, ...opts }); + it('should be able to serialize bare expression', async () => { - const { code } = await renderMarkdown(`{a}`, {}); + const { code } = await renderAstroMd(`{a}`, {}); chai.expect(code).to.equal(`{a}`); }); it('should be able to serialize expression inside component', async () => { - const { code } = await renderMarkdown(`{a}`, {}); + const { code } = await renderAstroMd(`{a}`, {}); chai.expect(code).to.equal(`{a}`); }); it('should be able to serialize expression inside markdown', async () => { - const { code } = await renderMarkdown(`# {frontmatter.title}`, {}); + const { code } = await renderAstroMd(`# {frontmatter.title}`, {}); chai .expect(code) @@ -23,7 +25,7 @@ describe('expressions', () => { }); it('should be able to serialize complex expression inside markdown', async () => { - const { code } = await renderMarkdown(`# Hello {frontmatter.name}`, {}); + const { code } = await renderAstroMd(`# Hello {frontmatter.name}`, {}); chai .expect(code) @@ -31,7 +33,7 @@ describe('expressions', () => { }); it('should be able to serialize complex expression with markup inside markdown', async () => { - const { code } = await renderMarkdown(`# Hello {frontmatter.name}`, {}); + const { code } = await renderAstroMd(`# Hello {frontmatter.name}`, {}); chai .expect(code) @@ -41,7 +43,7 @@ describe('expressions', () => { }); it('should be able to avoid evaluating JSX-like expressions in an inline code & generate a slug for id', async () => { - const { code } = await renderMarkdown(`# \`{frontmatter.title}\``, {}); + const { code } = await renderAstroMd(`# \`{frontmatter.title}\``, {}); chai .expect(code) @@ -49,7 +51,7 @@ describe('expressions', () => { }); it('should be able to avoid evaluating JSX-like expressions in inline codes', async () => { - const { code } = await renderMarkdown(`# \`{ foo }\` is a shorthand for \`{ foo: foo }\``, {}); + const { code } = await renderAstroMd(`# \`{ foo }\` is a shorthand for \`{ foo: foo }\``, {}); chai .expect(code) @@ -59,7 +61,7 @@ describe('expressions', () => { }); it('should be able to avoid evaluating JSX-like expressions & escape HTML tag characters in inline codes', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( `###### \`{}\` is equivalent to \`Record\` (at TypeScript v{frontmatter.version})`, {} ); @@ -72,7 +74,7 @@ describe('expressions', () => { }); it('should be able to encode ampersand characters in code blocks', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( 'The ampersand in ` ` must be encoded in code blocks.', {} ); @@ -85,7 +87,7 @@ describe('expressions', () => { }); it('should be able to encode ampersand characters in fenced code blocks', async () => { - const { code } = await renderMarkdown(` + const { code } = await renderAstroMd(` \`\`\`md The ampersand in \` \` must be encoded in code blocks. \`\`\` @@ -95,7 +97,7 @@ describe('expressions', () => { }); it('should be able to serialize function expression', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( `{frontmatter.list.map(item =>

{item}

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

<!-- HTML comment -->

'); }); it('should unwrap HTML comments in code fences', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( ` \`\`\` diff --git a/packages/markdown/remark/test/strictness.test.js b/packages/markdown/remark/test/strictness.test.js index 5ba9c215a..c70872701 100644 --- a/packages/markdown/remark/test/strictness.test.js +++ b/packages/markdown/remark/test/strictness.test.js @@ -1,9 +1,11 @@ import { renderMarkdown } from '../dist/index.js'; import chai from 'chai'; -describe('strictness', () => { +describe('strictness in Astro-flavored markdown', () => { + const renderAstroMd = (text, opts) => renderMarkdown(text, { isAstroFlavoredMd: true, ...opts }); + it('should allow self-closing HTML tags (void elements)', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( `Use self-closing void elements
like wordbreak and images: `, {} ); @@ -17,25 +19,25 @@ describe('strictness', () => { }); it('should allow attribute names starting with ":" after element names', async () => { - const { code } = await renderMarkdown(`
Test
`, {}); + const { code } = await renderAstroMd(`
Test
`, {}); chai.expect(code.trim()).to.equal(`
Test
`); }); it('should allow attribute names starting with ":" after local element names', async () => { - const { code } = await renderMarkdown(`x`, {}); + const { code } = await renderAstroMd(`x`, {}); chai.expect(code.trim()).to.equal(`x`); }); it('should allow attribute names starting with ":" after attribute names', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``, {}); chai.expect(code.trim()).to.equal(``); }); it('should allow attribute names starting with ":" after local attribute names', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( ``, {} ); @@ -44,19 +46,19 @@ describe('strictness', () => { }); it('should allow attribute names starting with ":" after attribute values', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``, {}); chai.expect(code.trim()).to.equal(``); }); it('should allow attribute names starting with "@" after element names', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``, {}); chai.expect(code.trim()).to.equal(``); }); it('should allow attribute names starting with "@" after local element names', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( `Test`, {} ); @@ -65,7 +67,7 @@ describe('strictness', () => { }); it('should allow attribute names starting with "@" after attribute names', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( ``, {} ); @@ -74,7 +76,7 @@ describe('strictness', () => { }); it('should allow attribute names starting with "@" after local attribute names', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( ``, {} ); @@ -83,7 +85,7 @@ describe('strictness', () => { }); it('should allow attribute names starting with "@" after attribute values', async () => { - const { code } = await renderMarkdown( + const { code } = await renderAstroMd( ``, {} ); @@ -92,7 +94,7 @@ describe('strictness', () => { }); it('should allow attribute names containing dots', async () => { - const { code } = await renderMarkdown(``, {}); + const { code } = await renderAstroMd(``, {}); chai.expect(code.trim()).to.equal(``); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6611b3bf..f2c67e34b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1714,19 +1714,23 @@ importers: packages/astro/test/fixtures/slots-preact: specifiers: + '@astrojs/mdx': workspace:* '@astrojs/preact': workspace:* astro: workspace:* dependencies: + '@astrojs/mdx': link:../../../../integrations/mdx '@astrojs/preact': link:../../../../integrations/preact astro: link:../../.. packages/astro/test/fixtures/slots-react: specifiers: + '@astrojs/mdx': workspace:* '@astrojs/react': workspace:* astro: workspace:* react: ^18.1.0 react-dom: ^18.1.0 dependencies: + '@astrojs/mdx': link:../../../../integrations/mdx '@astrojs/react': link:../../../../integrations/react astro: link:../../.. react: 18.2.0 @@ -1734,25 +1738,31 @@ importers: packages/astro/test/fixtures/slots-solid: specifiers: + '@astrojs/mdx': workspace:* '@astrojs/solid-js': workspace:* astro: workspace:* dependencies: + '@astrojs/mdx': link:../../../../integrations/mdx '@astrojs/solid-js': link:../../../../integrations/solid astro: link:../../.. packages/astro/test/fixtures/slots-svelte: specifiers: + '@astrojs/mdx': workspace:* '@astrojs/svelte': workspace:* astro: workspace:* dependencies: + '@astrojs/mdx': link:../../../../integrations/mdx '@astrojs/svelte': link:../../../../integrations/svelte astro: link:../../.. packages/astro/test/fixtures/slots-vue: specifiers: + '@astrojs/mdx': workspace:* '@astrojs/vue': workspace:* astro: workspace:* dependencies: + '@astrojs/mdx': link:../../../../integrations/mdx '@astrojs/vue': link:../../../../integrations/vue astro: link:../../..