diff options
author | 2022-08-11 12:36:34 -0500 | |
---|---|---|
committer | 2022-08-11 12:36:34 -0500 | |
commit | 4116128082121ee276d51cb245bf8095be4728a1 (patch) | |
tree | cf5b82544feb3597443c2fb3bc4e164ed5a1c932 | |
parent | 5afb5ef7af95f9245c1010d35aaf508d4989cb4a (diff) | |
download | astro-4116128082121ee276d51cb245bf8095be4728a1.tar.gz astro-4116128082121ee276d51cb245bf8095be4728a1.tar.zst astro-4116128082121ee276d51cb245bf8095be4728a1.zip |
[MDX] Pass injected frontmatter to layouts (#4255)
* fix: move layout generation to remark plugin
* test: frontmatter injection in layout
* chore: changeset
* fix: remove content fallback
7 files changed, 79 insertions, 10 deletions
diff --git a/.changeset/tasty-masks-draw.md b/.changeset/tasty-masks-draw.md new file mode 100644 index 000000000..790cac151 --- /dev/null +++ b/.changeset/tasty-masks-draw.md @@ -0,0 +1,5 @@ +--- +'@astrojs/mdx': patch +--- + +Pass injected frontmatter from remark and rehype plugins to layouts diff --git a/packages/integrations/mdx/src/astro-data-utils.ts b/packages/integrations/mdx/src/astro-data-utils.ts index fdad15b8f..0bc375c27 100644 --- a/packages/integrations/mdx/src/astro-data-utils.ts +++ b/packages/integrations/mdx/src/astro-data-utils.ts @@ -19,6 +19,42 @@ export function rehypeApplyFrontmatterExport(pageFrontmatter: Record<string, any const exportNodes = [ jsToTreeNode(`export const ${EXPORT_NAME} = ${JSON.stringify(frontmatter)};`), ]; + if (frontmatter.layout) { + exportNodes.unshift( + jsToTreeNode( + /** @see 'vite-plugin-markdown' for layout props reference */ + `import { jsx as layoutJsx } from 'astro/jsx-runtime'; + import Layout from ${JSON.stringify(frontmatter.layout)}; + + export default function ({ children }) { + const { layout, ...content } = frontmatter; + content.astro = {}; + Object.defineProperty(content.astro, 'headings', { + get() { + throw new Error('The "astro" property is no longer supported! To access "headings" from your layout, try using "Astro.props.headings."') + } + }); + Object.defineProperty(content.astro, 'html', { + get() { + throw new Error('The "astro" property is no longer supported! To access "html" from your layout, try using "Astro.props.compiledContent()."') + } + }); + Object.defineProperty(content.astro, 'source', { + get() { + throw new Error('The "astro" property is no longer supported! To access "source" from your layout, try using "Astro.props.rawContent()."') + } + }); + return layoutJsx(Layout, { + content, + frontmatter: content, + headings: getHeadings(), + 'server:root': true, + children, + }); + };` + ) + ); + } tree.children = exportNodes.concat(tree.children); }; } diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index 05b803705..f92a10a53 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -98,16 +98,7 @@ export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration { async transform(code, id) { if (!id.endsWith('mdx')) return; - let { data: frontmatter, content: pageContent } = parseFrontmatter(code, id); - if (frontmatter.layout) { - const { layout, ...contentProp } = frontmatter; - pageContent += `\n\nexport default async function({ children }) {\nconst Layout = (await import(${JSON.stringify( - frontmatter.layout - )})).default;\nconst frontmatter=${JSON.stringify( - contentProp - )};\nreturn <Layout frontmatter={frontmatter} content={frontmatter} headings={getHeadings()}>{children}</Layout> }`; - } - + const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id); const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), { ...mdxPluginOpts, rehypePlugins: [ diff --git a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/layouts/Base.astro b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/layouts/Base.astro new file mode 100644 index 000000000..b3d55f0a8 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/layouts/Base.astro @@ -0,0 +1,17 @@ +--- +const defaults = { title: 'Frontmatter not passed to layout!' } +const { frontmatter = defaults, content = defaults } = Astro.props; +--- + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>{frontmatter.title}</title> +</head> +<body> + <slot /> +</body> +</html> diff --git a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx index 2fcd655ec..1092099f0 100644 --- a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx +++ b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-1.mdx @@ -1,3 +1,7 @@ +--- +layout: '../layouts/Base.astro' +--- + # Page 1 Look at that! diff --git a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx index 4a6b9addd..c82eb97c2 100644 --- a/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx +++ b/packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx @@ -1,3 +1,7 @@ +--- +layout: '../layouts/Base.astro' +--- + # Page 2 ## Table of contents diff --git a/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js b/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js index 420e4716e..780f7252c 100644 --- a/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js +++ b/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { parseHTML } from 'linkedom'; import { loadFixture } from '../../../astro/test/test-utils.js'; const FIXTURE_ROOT = new URL('./fixtures/mdx-frontmatter-injection/', import.meta.url); @@ -41,4 +42,15 @@ describe('MDX frontmatter injection', () => { expect(titles).to.contain('Overridden title'); expect(readingTimes).to.contain('1000 min read'); }); + + it('passes injected frontmatter to layouts', async () => { + const html1 = await fixture.readFile('/page-1/index.html'); + const html2 = await fixture.readFile('/page-2/index.html'); + + const title1 = parseHTML(html1).document.querySelector('title'); + const title2 = parseHTML(html2).document.querySelector('title'); + + expect(title1.innerHTML).to.equal('Page 1'); + expect(title2.innerHTML).to.equal('Page 2'); + }); }); |