Skip to content

Commit 4116128

Browse files
authored
[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
1 parent 5afb5ef commit 4116128

File tree

7 files changed

+79
-10
lines changed

7 files changed

+79
-10
lines changed

.changeset/tasty-masks-draw.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/mdx': patch
3+
---
4+
5+
Pass injected frontmatter from remark and rehype plugins to layouts

packages/integrations/mdx/src/astro-data-utils.ts

+36
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,42 @@ export function rehypeApplyFrontmatterExport(pageFrontmatter: Record<string, any
1919
const exportNodes = [
2020
jsToTreeNode(`export const ${EXPORT_NAME} = ${JSON.stringify(frontmatter)};`),
2121
];
22+
if (frontmatter.layout) {
23+
exportNodes.unshift(
24+
jsToTreeNode(
25+
/** @see 'vite-plugin-markdown' for layout props reference */
26+
`import { jsx as layoutJsx } from 'astro/jsx-runtime';
27+
import Layout from ${JSON.stringify(frontmatter.layout)};
28+
29+
export default function ({ children }) {
30+
const { layout, ...content } = frontmatter;
31+
content.astro = {};
32+
Object.defineProperty(content.astro, 'headings', {
33+
get() {
34+
throw new Error('The "astro" property is no longer supported! To access "headings" from your layout, try using "Astro.props.headings."')
35+
}
36+
});
37+
Object.defineProperty(content.astro, 'html', {
38+
get() {
39+
throw new Error('The "astro" property is no longer supported! To access "html" from your layout, try using "Astro.props.compiledContent()."')
40+
}
41+
});
42+
Object.defineProperty(content.astro, 'source', {
43+
get() {
44+
throw new Error('The "astro" property is no longer supported! To access "source" from your layout, try using "Astro.props.rawContent()."')
45+
}
46+
});
47+
return layoutJsx(Layout, {
48+
content,
49+
frontmatter: content,
50+
headings: getHeadings(),
51+
'server:root': true,
52+
children,
53+
});
54+
};`
55+
)
56+
);
57+
}
2258
tree.children = exportNodes.concat(tree.children);
2359
};
2460
}

packages/integrations/mdx/src/index.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,7 @@ export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration {
9898
async transform(code, id) {
9999
if (!id.endsWith('mdx')) return;
100100

101-
let { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
102-
if (frontmatter.layout) {
103-
const { layout, ...contentProp } = frontmatter;
104-
pageContent += `\n\nexport default async function({ children }) {\nconst Layout = (await import(${JSON.stringify(
105-
frontmatter.layout
106-
)})).default;\nconst frontmatter=${JSON.stringify(
107-
contentProp
108-
)};\nreturn <Layout frontmatter={frontmatter} content={frontmatter} headings={getHeadings()}>{children}</Layout> }`;
109-
}
110-
101+
const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id);
111102
const compiled = await mdxCompile(new VFile({ value: pageContent, path: id }), {
112103
...mdxPluginOpts,
113104
rehypePlugins: [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
const defaults = { title: 'Frontmatter not passed to layout!' }
3+
const { frontmatter = defaults, content = defaults } = Astro.props;
4+
---
5+
6+
<!DOCTYPE html>
7+
<html lang="en">
8+
<head>
9+
<meta charset="UTF-8">
10+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
11+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
12+
<title>{frontmatter.title}</title>
13+
</head>
14+
<body>
15+
<slot />
16+
</body>
17+
</html>
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
---
2+
layout: '../layouts/Base.astro'
3+
---
4+
15
# Page 1
26

37
Look at that!

packages/integrations/mdx/test/fixtures/mdx-frontmatter-injection/src/pages/page-2.mdx

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
---
2+
layout: '../layouts/Base.astro'
3+
---
4+
15
# Page 2
26

37
## Table of contents

packages/integrations/mdx/test/mdx-frontmatter-injection.test.js

+12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { expect } from 'chai';
2+
import { parseHTML } from 'linkedom';
23
import { loadFixture } from '../../../astro/test/test-utils.js';
34

45
const FIXTURE_ROOT = new URL('./fixtures/mdx-frontmatter-injection/', import.meta.url);
@@ -41,4 +42,15 @@ describe('MDX frontmatter injection', () => {
4142
expect(titles).to.contain('Overridden title');
4243
expect(readingTimes).to.contain('1000 min read');
4344
});
45+
46+
it('passes injected frontmatter to layouts', async () => {
47+
const html1 = await fixture.readFile('/page-1/index.html');
48+
const html2 = await fixture.readFile('/page-2/index.html');
49+
50+
const title1 = parseHTML(html1).document.querySelector('title');
51+
const title2 = parseHTML(html2).document.querySelector('title');
52+
53+
expect(title1.innerHTML).to.equal('Page 1');
54+
expect(title2.innerHTML).to.equal('Page 2');
55+
});
4456
});

0 commit comments

Comments
 (0)