Fix head injection in body with slots.render() and head buffering (#6216)

* Fix head injection in body with slots.render() and head buffering

* Adding a changeset

* An MDX test too
This commit is contained in:
Matthew Phillips 2023-02-13 07:49:10 -05:00 committed by GitHub
parent 79f49acbe1
commit 79783fc018
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 85 additions and 9 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fix head injection in body with slots.render() and head buffering

View file

@ -72,13 +72,22 @@ export function stringifyChunk(result: SSRResult, chunk: string | SlotString | R
}
// If the current scope is with Astro.slots.render()
case ScopeFlags.Slot: {
case ScopeFlags.Slot:
case ScopeFlags.Slot | ScopeFlags.HeadBuffer: {
if (hasScopeFlag(result, ScopeFlags.RenderSlot)) {
return '';
}
break;
}
// Nested element inside of JSX during head buffering phase
case ScopeFlags.HeadBuffer: {
if(hasScopeFlag(result, ScopeFlags.JSX | ScopeFlags.HeadBuffer)) {
return "";
}
break;
}
// Astro.slots.render() should never render head content.
case ScopeFlags.RenderSlot | ScopeFlags.Astro:
case ScopeFlags.RenderSlot | ScopeFlags.Astro | ScopeFlags.JSX:

View file

@ -0,0 +1,7 @@
---
import SlotRenderComponent from "./SlotRenderComponent.astro";
---
<SlotRenderComponent>
<p slot="slot-name">Paragraph.</p>
</SlotRenderComponent>

View file

@ -0,0 +1,7 @@
---
import Layout from "../components/Layout.astro";
import UsesSlotRender from "../components/UsesSlotRender.astro"
---
<Layout>
<UsesSlotRender />
</Layout>

View file

@ -50,6 +50,14 @@ describe('Head injection', () => {
expect($('head link[rel=stylesheet]')).to.have.a.lengthOf(2);
expect($('body link[rel=stylesheet]')).to.have.a.lengthOf(0);
});
it('Using slots in Astro.slots.render() inside head buffering', async () => {
const html = await fixture.readFile('/with-render-slot-in-head-buffer/index.html');
const $ = cheerio.load(html);
expect($('head link[rel=stylesheet]')).to.have.a.lengthOf(2);
expect($('body link[rel=stylesheet]')).to.have.a.lengthOf(0);
});
});
});
});

View file

@ -69,5 +69,17 @@ describe('Head injection w/ MDX', () => {
const bodyLinks = $('body link[rel=stylesheet]');
expect(bodyLinks).to.have.a.lengthOf(0);
});
it('JSX component rendering Astro children within head buffering phase', async () => {
const html = await fixture.readFile('/posts/using-component/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);
});
});
});

View file

@ -0,0 +1,11 @@
---
const { title } = Astro.props;
---
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<style is:global>
@import "../styles/global.css";
</style>

View file

@ -0,0 +1 @@
<span>just a generic component</span>

View file

@ -0,0 +1,9 @@
---
import Component from "./GenericComponent.astro";
---
<div>
<slot name="title" />
<slot name="intro" class="inline" />
<Component />
</div>

View file

@ -0,0 +1,13 @@
---
title: testing
---
import MDXWrapper from "../../components/MDXWrapper.astro";
<MDXWrapper>
<h1 slot="title">
testing
</h1>
<div slot="intro">
Intro
</div>
</MDXWrapper>

View file

@ -1,4 +1,5 @@
---
import BaseHead from "../components/BaseHead.astro";
export interface Props {
title: string;
}
@ -9,14 +10,7 @@ const { title } = Astro.props;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<style is:global>
@import "../styles/global.css";
</style>
<BaseHead title={title} />
</head>
<body>
<slot />