[Content collections] Apply MDX components on render (#6064)
* fix: apply MDX components during render() * test: MDX components export in SSG and SSR * chore: changeset
This commit is contained in:
parent
a784e603a9
commit
80dc1a5cbd
7 changed files with 108 additions and 11 deletions
5
.changeset/heavy-tomatoes-know.md
Normal file
5
.changeset/heavy-tomatoes-know.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Apply MDX `components` export when rendering as a content collection entry
|
|
@ -1,3 +1,4 @@
|
|||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||
import { prependForwardSlash } from '../core/path.js';
|
||||
|
||||
import {
|
||||
|
@ -120,21 +121,32 @@ async function render({
|
|||
id: string;
|
||||
collectionToRenderEntryMap: CollectionToEntryMap;
|
||||
}) {
|
||||
const lazyImport = collectionToRenderEntryMap[collection]?.[id];
|
||||
if (!lazyImport) throw new Error(`${String(collection)} → ${String(id)} does not exist.`);
|
||||
const UnexpectedRenderError = new AstroError({
|
||||
...AstroErrorData.UnknownContentCollectionError,
|
||||
message: `Unexpected error while rendering ${String(collection)} → ${String(id)}.`,
|
||||
});
|
||||
|
||||
const mod = await lazyImport();
|
||||
const lazyImport = collectionToRenderEntryMap[collection]?.[id];
|
||||
if (typeof lazyImport !== 'function') throw UnexpectedRenderError;
|
||||
|
||||
const baseMod = await lazyImport();
|
||||
if (baseMod == null || typeof baseMod !== 'object') throw UnexpectedRenderError;
|
||||
|
||||
const { collectedStyles, collectedLinks, collectedScripts, getMod } = baseMod;
|
||||
if (typeof getMod !== 'function') throw UnexpectedRenderError;
|
||||
const mod = await getMod();
|
||||
if (mod == null || typeof mod !== 'object') throw UnexpectedRenderError;
|
||||
|
||||
const Content = createComponent({
|
||||
factory(result, props, slots) {
|
||||
factory(result, baseProps, slots) {
|
||||
let styles = '',
|
||||
links = '',
|
||||
scripts = '';
|
||||
if (Array.isArray(mod?.collectedStyles)) {
|
||||
styles = mod.collectedStyles.map((style: any) => renderStyleElement(style)).join('');
|
||||
if (Array.isArray(collectedStyles)) {
|
||||
styles = collectedStyles.map((style: any) => renderStyleElement(style)).join('');
|
||||
}
|
||||
if (Array.isArray(mod?.collectedLinks)) {
|
||||
links = mod.collectedLinks
|
||||
if (Array.isArray(collectedLinks)) {
|
||||
links = collectedLinks
|
||||
.map((link: any) => {
|
||||
return renderUniqueStylesheet(result, {
|
||||
href: prependForwardSlash(link),
|
||||
|
@ -142,8 +154,17 @@ async function render({
|
|||
})
|
||||
.join('');
|
||||
}
|
||||
if (Array.isArray(mod?.collectedScripts)) {
|
||||
scripts = mod.collectedScripts.map((script: any) => renderScriptElement(script)).join('');
|
||||
if (Array.isArray(collectedScripts)) {
|
||||
scripts = collectedScripts.map((script: any) => renderScriptElement(script)).join('');
|
||||
}
|
||||
|
||||
let props = baseProps;
|
||||
// Auto-apply MDX components export
|
||||
if (id.endsWith('mdx')) {
|
||||
props = {
|
||||
components: mod.components ?? {},
|
||||
...baseProps,
|
||||
};
|
||||
}
|
||||
|
||||
return createHeadAndContent(
|
||||
|
|
|
@ -36,7 +36,9 @@ export function astroContentAssetPropagationPlugin({ mode }: { mode: string }):
|
|||
if (isPropagatedAsset(id)) {
|
||||
const basePath = id.split('?')[0];
|
||||
const code = `
|
||||
export { Content, getHeadings, frontmatter } from ${JSON.stringify(basePath)};
|
||||
export async function getMod() {
|
||||
return import(${JSON.stringify(basePath)});
|
||||
}
|
||||
export const collectedLinks = ${JSON.stringify(LINKS_PLACEHOLDER)};
|
||||
export const collectedStyles = ${JSON.stringify(STYLES_PLACEHOLDER)};
|
||||
export const collectedScripts = ${JSON.stringify(SCRIPTS_PLACEHOLDER)};
|
||||
|
|
|
@ -69,6 +69,15 @@ describe('Content Collections - render()', () => {
|
|||
'`WithScripts.astro` hoisted script included unexpectedly.'
|
||||
).to.be.undefined;
|
||||
});
|
||||
|
||||
it('Applies MDX components export', async () => {
|
||||
const html = await fixture.readFile('/launch-week-components-export/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const h2 = $('h2');
|
||||
expect(h2).to.have.a.lengthOf(1);
|
||||
expect(h2.attr('data-components-export-applied')).to.equal('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Build - SSR', () => {
|
||||
|
@ -108,6 +117,18 @@ describe('Content Collections - render()', () => {
|
|||
// Includes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(0);
|
||||
});
|
||||
|
||||
it('Applies MDX components export', async () => {
|
||||
const app = await fixture.loadTestAdapterApp();
|
||||
const request = new Request('http://example.com/launch-week-components-export');
|
||||
const response = await app.render(request);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const h2 = $('h2');
|
||||
expect(h2).to.have.a.lengthOf(1);
|
||||
expect(h2.attr('data-components-export-applied')).to.equal('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dev - SSG', () => {
|
||||
|
@ -160,5 +181,17 @@ describe('Content Collections - render()', () => {
|
|||
// Includes inline script
|
||||
expect($('script[data-is-inline]')).to.have.a.lengthOf(1);
|
||||
});
|
||||
|
||||
it('Applies MDX components export', async () => {
|
||||
const response = await fixture.fetch('/launch-week-components-export', { method: 'GET' });
|
||||
expect(response.status).to.equal(200);
|
||||
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const h2 = $('h2');
|
||||
expect(h2).to.have.a.lengthOf(1);
|
||||
expect(h2.attr('data-components-export-applied')).to.equal('true');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
4
packages/astro/test/fixtures/content/src/components/H2.astro
vendored
Normal file
4
packages/astro/test/fixtures/content/src/components/H2.astro
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
---
|
||||
|
||||
<h2 data-components-export-applied="true"><slot /></h2>
|
18
packages/astro/test/fixtures/content/src/content/blog/promo/launch-week-components-export.mdx
vendored
Normal file
18
packages/astro/test/fixtures/content/src/content/blog/promo/launch-week-components-export.mdx
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: 'Launch week!'
|
||||
description: 'Join us for the exciting launch of SPACE BLOG'
|
||||
publishedDate: 'Sat May 21 2022 00:00:00 GMT-0400 (Eastern Daylight Time)'
|
||||
tags: ['announcement']
|
||||
---
|
||||
|
||||
import H2 from '../../../components/H2.astro';
|
||||
|
||||
export const components = { h2: H2 };
|
||||
|
||||
Join us for the space blog launch!
|
||||
|
||||
## Details
|
||||
|
||||
- THIS THURSDAY
|
||||
- Houston, TX
|
||||
- Dress code: **interstellar casual** ✨
|
14
packages/astro/test/fixtures/content/src/pages/launch-week-components-export.astro
vendored
Normal file
14
packages/astro/test/fixtures/content/src/pages/launch-week-components-export.astro
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
import { getEntryBySlug } from 'astro:content';
|
||||
|
||||
const entry = await getEntryBySlug('blog', 'promo/launch-week-components-export');
|
||||
const { Content } = await entry.render();
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
<title>Launch Week</title>
|
||||
</head>
|
||||
<body>
|
||||
<Content />
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue