diff --git a/.changeset/smart-files-flow.md b/.changeset/smart-files-flow.md new file mode 100644 index 000000000000..e0f9020ab0da --- /dev/null +++ b/.changeset/smart-files-flow.md @@ -0,0 +1,6 @@ +--- +'astro': patch +'@astrojs/mdx': patch +--- + +Prevent body head content injection in MDX when using layout diff --git a/packages/astro/src/runtime/server/render/page.ts b/packages/astro/src/runtime/server/render/page.ts index ac05abb889f1..c8e07a4e7cc2 100644 --- a/packages/astro/src/runtime/server/render/page.ts +++ b/packages/astro/src/runtime/server/render/page.ts @@ -76,6 +76,8 @@ export async function renderPage( route?: RouteData | undefined ): Promise { if (!isAstroComponentFactory(componentFactory)) { + result._metadata.headInTree = + result.componentMetadata.get((componentFactory as any).moduleId)?.containsHead ?? false; const pageProps: Record = { ...(props ?? {}), 'server:root': true }; let output: ComponentIterable; diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index 195b678b8440..e284dd4c36bc 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -160,6 +160,7 @@ export default function mdx(partialMdxOptions: Partial = {}): AstroI // Ensures styles and scripts are injected into a `` // When a layout is not applied code += `\nContent[Symbol.for('astro.needsHeadRendering')] = !Boolean(frontmatter.layout);`; + code += `\nContent.moduleId = ${JSON.stringify(id)};` if (command === 'dev') { // TODO: decline HMR updates until we have a stable approach diff --git a/packages/integrations/mdx/test/css-head-mdx.test.js b/packages/integrations/mdx/test/css-head-mdx.test.js index 781e4d62d51d..f4ece38d9710 100644 --- a/packages/integrations/mdx/test/css-head-mdx.test.js +++ b/packages/integrations/mdx/test/css-head-mdx.test.js @@ -81,5 +81,18 @@ describe('Head injection w/ MDX', () => { const bodyLinks = $('body link[rel=stylesheet]'); expect(bodyLinks).to.have.a.lengthOf(0); }); + + it('Injection caused by delayed slots', async () => { + const html = await fixture.readFile('/componentwithtext/index.html'); + + // Using cheerio here because linkedom doesn't support head tag injection + const $ = cheerio.load(html); + + const headLinks = $('head link[rel=stylesheet]'); + expect(headLinks).to.have.a.lengthOf(1); + + const bodyLinks = $('body link[rel=stylesheet]'); + expect(bodyLinks).to.have.a.lengthOf(0); + }); }); }); diff --git a/packages/integrations/mdx/test/fixtures/css-head-mdx/src/components/BasicBlock.astro b/packages/integrations/mdx/test/fixtures/css-head-mdx/src/components/BasicBlock.astro new file mode 100644 index 000000000000..95660b6b90ff --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/css-head-mdx/src/components/BasicBlock.astro @@ -0,0 +1,14 @@ +--- +const { inlineStyle, title, display = 'horizontal' } = Astro.props; +const lineEnding = display === 'horizontal' ? ', ' : '
'; +--- + +{title &&

} +
+ some name + line 1 + line 2 + line 3 + line 4 + line 5 +
diff --git a/packages/integrations/mdx/test/fixtures/css-head-mdx/src/layouts/DocumentLayout.astro b/packages/integrations/mdx/test/fixtures/css-head-mdx/src/layouts/DocumentLayout.astro new file mode 100644 index 000000000000..a09a1c218a4e --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/css-head-mdx/src/layouts/DocumentLayout.astro @@ -0,0 +1,15 @@ +--- +// Extend the BaseLayout, adding space for a banner at the top of the page +// after the main heading, then the detail for the actual page +import ContentLayout from './ContentLayout.astro'; +const { frontmatter } = Astro.props; +--- + + +
+
+

+ +

+
+
diff --git a/packages/integrations/mdx/test/fixtures/css-head-mdx/src/pages/componentwithtext.mdx b/packages/integrations/mdx/test/fixtures/css-head-mdx/src/pages/componentwithtext.mdx new file mode 100644 index 000000000000..2c6f3032ff9c --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/css-head-mdx/src/pages/componentwithtext.mdx @@ -0,0 +1,12 @@ +--- +layout: ../layouts/DocumentLayout.astro +title: blah blah +--- + +import BasicBlock from '../components/BasicBlock.astro'; + +Some text for a paragraph. + + + +Some other text.