Skip to content

Commit

Permalink
[Content collections] Apply MDX components on render (#6064)
Browse files Browse the repository at this point in the history
* fix: apply MDX components during render()

* test: MDX components export in SSG and SSR

* chore: changeset
  • Loading branch information
bholmesdev committed Feb 1, 2023
1 parent 01ef1e5 commit 2fb72c8
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-tomatoes-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Apply MDX `components` export when rendering as a content collection entry
39 changes: 30 additions & 9 deletions packages/astro/src/content/internal.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { prependForwardSlash } from '../core/path.js';

import {
Expand Down Expand Up @@ -120,30 +121,50 @@ async function render({
id: string;
collectionToRenderEntryMap: CollectionToEntryMap;
}) {
const UnexpectedRenderError = new AstroError({
...AstroErrorData.UnknownContentCollectionError,
message: `Unexpected error while rendering ${String(collection)}${String(id)}.`,
});

const lazyImport = collectionToRenderEntryMap[collection]?.[id];
if (!lazyImport) throw new Error(`${String(collection)}${String(id)} does not exist.`);
if (typeof lazyImport !== 'function') throw UnexpectedRenderError;

const baseMod = await lazyImport();
if (baseMod == null || typeof baseMod !== 'object') throw UnexpectedRenderError;

const mod = await lazyImport();
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),
});
})
.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(
Expand Down
4 changes: 3 additions & 1 deletion packages/astro/src/content/vite-plugin-content-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)};
Expand Down
33 changes: 33 additions & 0 deletions packages/astro/test/content-collections-render.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,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', () => {
Expand Down Expand Up @@ -110,6 +119,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', () => {
Expand Down Expand Up @@ -162,5 +183,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 changes: 4 additions & 0 deletions packages/astro/test/fixtures/content/src/components/H2.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

<h2 data-components-export-applied="true"><slot /></h2>
Original file line number Diff line number Diff line change
@@ -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**
Original file line number Diff line number Diff line change
@@ -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>

0 comments on commit 2fb72c8

Please sign in to comment.