[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 { prependForwardSlash } from '../core/path.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -120,21 +121,32 @@ async function render({
|
||||||
id: string;
|
id: string;
|
||||||
collectionToRenderEntryMap: CollectionToEntryMap;
|
collectionToRenderEntryMap: CollectionToEntryMap;
|
||||||
}) {
|
}) {
|
||||||
const lazyImport = collectionToRenderEntryMap[collection]?.[id];
|
const UnexpectedRenderError = new AstroError({
|
||||||
if (!lazyImport) throw new Error(`${String(collection)} → ${String(id)} does not exist.`);
|
...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({
|
const Content = createComponent({
|
||||||
factory(result, props, slots) {
|
factory(result, baseProps, slots) {
|
||||||
let styles = '',
|
let styles = '',
|
||||||
links = '',
|
links = '',
|
||||||
scripts = '';
|
scripts = '';
|
||||||
if (Array.isArray(mod?.collectedStyles)) {
|
if (Array.isArray(collectedStyles)) {
|
||||||
styles = mod.collectedStyles.map((style: any) => renderStyleElement(style)).join('');
|
styles = collectedStyles.map((style: any) => renderStyleElement(style)).join('');
|
||||||
}
|
}
|
||||||
if (Array.isArray(mod?.collectedLinks)) {
|
if (Array.isArray(collectedLinks)) {
|
||||||
links = mod.collectedLinks
|
links = collectedLinks
|
||||||
.map((link: any) => {
|
.map((link: any) => {
|
||||||
return renderUniqueStylesheet(result, {
|
return renderUniqueStylesheet(result, {
|
||||||
href: prependForwardSlash(link),
|
href: prependForwardSlash(link),
|
||||||
|
@ -142,8 +154,17 @@ async function render({
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
}
|
}
|
||||||
if (Array.isArray(mod?.collectedScripts)) {
|
if (Array.isArray(collectedScripts)) {
|
||||||
scripts = mod.collectedScripts.map((script: any) => renderScriptElement(script)).join('');
|
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(
|
return createHeadAndContent(
|
||||||
|
|
|
@ -36,7 +36,9 @@ export function astroContentAssetPropagationPlugin({ mode }: { mode: string }):
|
||||||
if (isPropagatedAsset(id)) {
|
if (isPropagatedAsset(id)) {
|
||||||
const basePath = id.split('?')[0];
|
const basePath = id.split('?')[0];
|
||||||
const code = `
|
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 collectedLinks = ${JSON.stringify(LINKS_PLACEHOLDER)};
|
||||||
export const collectedStyles = ${JSON.stringify(STYLES_PLACEHOLDER)};
|
export const collectedStyles = ${JSON.stringify(STYLES_PLACEHOLDER)};
|
||||||
export const collectedScripts = ${JSON.stringify(SCRIPTS_PLACEHOLDER)};
|
export const collectedScripts = ${JSON.stringify(SCRIPTS_PLACEHOLDER)};
|
||||||
|
|
|
@ -69,6 +69,15 @@ describe('Content Collections - render()', () => {
|
||||||
'`WithScripts.astro` hoisted script included unexpectedly.'
|
'`WithScripts.astro` hoisted script included unexpectedly.'
|
||||||
).to.be.undefined;
|
).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', () => {
|
describe('Build - SSR', () => {
|
||||||
|
@ -108,6 +117,18 @@ describe('Content Collections - render()', () => {
|
||||||
// Includes styles
|
// Includes styles
|
||||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(0);
|
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', () => {
|
describe('Dev - SSG', () => {
|
||||||
|
@ -160,5 +181,17 @@ describe('Content Collections - render()', () => {
|
||||||
// Includes inline script
|
// Includes inline script
|
||||||
expect($('script[data-is-inline]')).to.have.a.lengthOf(1);
|
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