summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@skypack.dev> 2023-02-09 11:50:04 -0500
committerGravatar GitHub <noreply@github.com> 2023-02-09 11:50:04 -0500
commit3390cb84443a43eb997f3efeb5ca298a8477aaf0 (patch)
treec54683aa2e3354eff5454b3c31a4ce9ffdbc22e9
parent0c3485ab0703e38205df232eda75a163a2c2a47a (diff)
downloadastro-3390cb84443a43eb997f3efeb5ca298a8477aaf0.tar.gz
astro-3390cb84443a43eb997f3efeb5ca298a8477aaf0.tar.zst
astro-3390cb84443a43eb997f3efeb5ca298a8477aaf0.zip
Fix head injection misplacement with Astro.slots.render() (#6196)
* Fix head injection misplacement with Astro.slots.render() * Adding a changeset * Fix case of JSX with no layout * missing break
-rw-r--r--.changeset/thirty-bugs-film.md5
-rw-r--r--packages/astro/src/runtime/server/render/common.ts20
-rw-r--r--packages/astro/src/runtime/server/render/scope.ts4
-rw-r--r--packages/astro/src/runtime/server/render/slot.ts2
-rw-r--r--packages/astro/test/fixtures/head-injection/package.json (renamed from packages/astro/test/fixtures/head-injection-md/package.json)2
-rw-r--r--packages/astro/test/fixtures/head-injection/src/components/Layout.astro (renamed from packages/astro/test/fixtures/head-injection-md/src/components/Layout.astro)0
-rw-r--r--packages/astro/test/fixtures/head-injection/src/components/RegularSlot.astro8
-rw-r--r--packages/astro/test/fixtures/head-injection/src/components/SlotRenderComponent.astro12
-rw-r--r--packages/astro/test/fixtures/head-injection/src/components/SlotRenderLayout.astro7
-rw-r--r--packages/astro/test/fixtures/head-injection/src/components/SlotsRender.astro25
-rw-r--r--packages/astro/test/fixtures/head-injection/src/pages/index.md (renamed from packages/astro/test/fixtures/head-injection-md/src/pages/index.md)0
-rw-r--r--packages/astro/test/fixtures/head-injection/src/pages/with-slot-in-render-slot.astro24
-rw-r--r--packages/astro/test/fixtures/head-injection/src/pages/with-slot-in-slot.astro11
-rw-r--r--packages/astro/test/fixtures/head-injection/src/pages/with-slot-render.astro9
-rw-r--r--packages/astro/test/head-injection-md.test.js27
-rw-r--r--packages/astro/test/head-injection.test.js55
-rw-r--r--packages/integrations/mdx/test/css-head-mdx.test.js13
-rw-r--r--packages/integrations/mdx/test/fixtures/css-head-mdx/src/pages/noLayoutWithComponent.mdx22
-rw-r--r--pnpm-lock.yaml2
19 files changed, 216 insertions, 32 deletions
diff --git a/.changeset/thirty-bugs-film.md b/.changeset/thirty-bugs-film.md
new file mode 100644
index 000000000..9a8ba2cc9
--- /dev/null
+++ b/.changeset/thirty-bugs-film.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix head injection misplacement with Astro.slots.render()
diff --git a/packages/astro/src/runtime/server/render/common.ts b/packages/astro/src/runtime/server/render/common.ts
index 2750c886f..484f574c6 100644
--- a/packages/astro/src/runtime/server/render/common.ts
+++ b/packages/astro/src/runtime/server/render/common.ts
@@ -9,7 +9,7 @@ import {
PrescriptType,
} from '../scripts.js';
import { renderAllHeadContent } from './head.js';
-import { ScopeFlags } from './scope.js';
+import { hasScopeFlag, ScopeFlags } from './scope.js';
import { isSlotString, type SlotString } from './slot.js';
export const Fragment = Symbol.for('astro:fragment');
@@ -63,7 +63,23 @@ export function stringifyChunk(result: SSRResult, chunk: string | SlotString | R
return '';
}
- // Astro.slots.render('default') should never render head content.
+ // Astro rendered within JSX, head will be injected by the page itself.
+ case ScopeFlags.JSX | ScopeFlags.Astro: {
+ if(hasScopeFlag(result, ScopeFlags.JSX)) {
+ return '';
+ }
+ break;
+ }
+
+ // If the current scope is with Astro.slots.render()
+ case ScopeFlags.Slot: {
+ if(hasScopeFlag(result, ScopeFlags.RenderSlot)) {
+ return '';
+ }
+ break;
+ }
+
+ // Astro.slots.render() should never render head content.
case ScopeFlags.RenderSlot | ScopeFlags.Astro:
case ScopeFlags.RenderSlot | ScopeFlags.Astro | ScopeFlags.JSX:
case ScopeFlags.RenderSlot | ScopeFlags.Astro | ScopeFlags.JSX | ScopeFlags.HeadBuffer: {
diff --git a/packages/astro/src/runtime/server/render/scope.ts b/packages/astro/src/runtime/server/render/scope.ts
index 43cf80b51..fce96c0e7 100644
--- a/packages/astro/src/runtime/server/render/scope.ts
+++ b/packages/astro/src/runtime/server/render/scope.ts
@@ -18,6 +18,10 @@ export function removeScopeFlag(result: SSRResult, flag: ScopeFlagValues) {
result.scope &= ~flag;
}
+export function hasScopeFlag(result: SSRResult, flag: ScopeFlagValues) {
+ return (result.scope & flag) === flag;
+}
+
export function createScopedResult(result: SSRResult, flag?: ScopeFlagValues): SSRResult {
const scopedResult = Object.create(result, {
scope: {
diff --git a/packages/astro/src/runtime/server/render/slot.ts b/packages/astro/src/runtime/server/render/slot.ts
index 5e21d1e07..b0e8a0fa7 100644
--- a/packages/astro/src/runtime/server/render/slot.ts
+++ b/packages/astro/src/runtime/server/render/slot.ts
@@ -4,7 +4,7 @@ import type { RenderInstruction } from './types.js';
import { HTMLString, markHTMLString } from '../escape.js';
import { renderChild } from './any.js';
-import { createScopedResult, ScopeFlags } from './scope.js';
+import { createScopedResult, hasScopeFlag, ScopeFlags } from './scope.js';
type RenderTemplateResult = ReturnType<typeof renderTemplate>;
export type ComponentSlots = Record<string, ComponentSlotValue>;
diff --git a/packages/astro/test/fixtures/head-injection-md/package.json b/packages/astro/test/fixtures/head-injection/package.json
index d2f2c6778..82455011a 100644
--- a/packages/astro/test/fixtures/head-injection-md/package.json
+++ b/packages/astro/test/fixtures/head-injection/package.json
@@ -1,5 +1,5 @@
{
- "name": "@test/head-injection-md",
+ "name": "@test/head-injection",
"version": "0.0.0",
"private": true,
"dependencies": {
diff --git a/packages/astro/test/fixtures/head-injection-md/src/components/Layout.astro b/packages/astro/test/fixtures/head-injection/src/components/Layout.astro
index 225a16a12..225a16a12 100644
--- a/packages/astro/test/fixtures/head-injection-md/src/components/Layout.astro
+++ b/packages/astro/test/fixtures/head-injection/src/components/Layout.astro
diff --git a/packages/astro/test/fixtures/head-injection/src/components/RegularSlot.astro b/packages/astro/test/fixtures/head-injection/src/components/RegularSlot.astro
new file mode 100644
index 000000000..cec06fe2f
--- /dev/null
+++ b/packages/astro/test/fixtures/head-injection/src/components/RegularSlot.astro
@@ -0,0 +1,8 @@
+<style>
+ div {
+ font-weight: bolder;
+ }
+</style>
+<div>
+ <slot />
+</div>
diff --git a/packages/astro/test/fixtures/head-injection/src/components/SlotRenderComponent.astro b/packages/astro/test/fixtures/head-injection/src/components/SlotRenderComponent.astro
new file mode 100644
index 000000000..d8756fff5
--- /dev/null
+++ b/packages/astro/test/fixtures/head-injection/src/components/SlotRenderComponent.astro
@@ -0,0 +1,12 @@
+---
+const html = await Astro.slots.render('slot-name');
+---
+<div class="p-sample">
+<Fragment set:html={html} />
+</div>
+
+<style>
+.p-sample {
+ color: red;
+}
+</style>
diff --git a/packages/astro/test/fixtures/head-injection/src/components/SlotRenderLayout.astro b/packages/astro/test/fixtures/head-injection/src/components/SlotRenderLayout.astro
new file mode 100644
index 000000000..efef491a0
--- /dev/null
+++ b/packages/astro/test/fixtures/head-injection/src/components/SlotRenderLayout.astro
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <head> </head>
+ <body>
+ <slot />
+ </body>
+</html>
diff --git a/packages/astro/test/fixtures/head-injection/src/components/SlotsRender.astro b/packages/astro/test/fixtures/head-injection/src/components/SlotsRender.astro
new file mode 100644
index 000000000..85a57916e
--- /dev/null
+++ b/packages/astro/test/fixtures/head-injection/src/components/SlotsRender.astro
@@ -0,0 +1,25 @@
+---
+export interface Props {
+ title: string;
+ subtitle: string;
+ content?: string;
+}
+const {
+ title,
+ subtitle = await Astro.slots.render("subtitle"),
+ content = await Astro.slots.render("content"),
+} = Astro.props;
+---
+
+<style>
+ section {
+ background: slategrey;
+ }
+</style>
+<section>
+ <div>
+ {title && <h1>{title}</h1>}
+ {subtitle && <p set:html={subtitle} />}
+ {content && <div set:html={content} />}
+ </div>
+</section>
diff --git a/packages/astro/test/fixtures/head-injection-md/src/pages/index.md b/packages/astro/test/fixtures/head-injection/src/pages/index.md
index f32c4c3d6..f32c4c3d6 100644
--- a/packages/astro/test/fixtures/head-injection-md/src/pages/index.md
+++ b/packages/astro/test/fixtures/head-injection/src/pages/index.md
diff --git a/packages/astro/test/fixtures/head-injection/src/pages/with-slot-in-render-slot.astro b/packages/astro/test/fixtures/head-injection/src/pages/with-slot-in-render-slot.astro
new file mode 100644
index 000000000..e3c2975e2
--- /dev/null
+++ b/packages/astro/test/fixtures/head-injection/src/pages/with-slot-in-render-slot.astro
@@ -0,0 +1,24 @@
+---
+import Layout from '../components/Layout.astro';
+import SlotsRender from '../components/SlotsRender.astro';
+---
+
+<Layout>
+ <SlotsRender
+ title="Lorem ipsum lorem"
+ subtitle="At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti"
+ >
+ <Fragment slot="content">
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat.
+ </p>
+
+ <p class="mt-4">
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
+ sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ </p>
+ </Fragment>
+ </SlotsRender>
+</Layout>
diff --git a/packages/astro/test/fixtures/head-injection/src/pages/with-slot-in-slot.astro b/packages/astro/test/fixtures/head-injection/src/pages/with-slot-in-slot.astro
new file mode 100644
index 000000000..85b228c43
--- /dev/null
+++ b/packages/astro/test/fixtures/head-injection/src/pages/with-slot-in-slot.astro
@@ -0,0 +1,11 @@
+---
+import Layout from "../components/SlotRenderLayout.astro";
+import RegularSlot from "../components/RegularSlot.astro"
+---
+<Layout>
+ <RegularSlot>
+ <RegularSlot>
+ <p slot="slot-name">Paragraph.</p>
+ </RegularSlot>
+ </RegularSlot>
+</Layout>
diff --git a/packages/astro/test/fixtures/head-injection/src/pages/with-slot-render.astro b/packages/astro/test/fixtures/head-injection/src/pages/with-slot-render.astro
new file mode 100644
index 000000000..337b4a95c
--- /dev/null
+++ b/packages/astro/test/fixtures/head-injection/src/pages/with-slot-render.astro
@@ -0,0 +1,9 @@
+---
+import Layout from "../components/SlotRenderLayout.astro";
+import Component from "../components/SlotRenderComponent.astro"
+---
+<Layout>
+ <Component>
+ <p slot="slot-name">Paragraph.</p>
+ </Component>
+</Layout>
diff --git a/packages/astro/test/head-injection-md.test.js b/packages/astro/test/head-injection-md.test.js
deleted file mode 100644
index 1acd5e83b..000000000
--- a/packages/astro/test/head-injection-md.test.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { expect } from 'chai';
-import * as cheerio from 'cheerio';
-import { loadFixture } from './test-utils.js';
-
-describe('Head injection with markdown', () => {
- /** @type {import('./test-utils').Fixture} */
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- root: './fixtures/head-injection-md/',
- });
- });
-
- describe('build', () => {
- before(async () => {
- await fixture.build();
- });
-
- it('only injects head content once', async () => {
- const html = await fixture.readFile(`/index.html`);
- const $ = cheerio.load(html);
-
- expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1);
- });
- });
-});
diff --git a/packages/astro/test/head-injection.test.js b/packages/astro/test/head-injection.test.js
new file mode 100644
index 000000000..6f6010773
--- /dev/null
+++ b/packages/astro/test/head-injection.test.js
@@ -0,0 +1,55 @@
+import { expect } from 'chai';
+import * as cheerio from 'cheerio';
+import { loadFixture } from './test-utils.js';
+
+describe('Head injection', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/head-injection/',
+ });
+ });
+
+ describe('build', () => {
+ before(async () => {
+ await fixture.build();
+ });
+
+ describe('Markdown', () => {
+ it('only injects head content once', async () => {
+ const html = await fixture.readFile(`/index.html`);
+ const $ = cheerio.load(html);
+
+ expect($('head link[rel=stylesheet]')).to.have.a.lengthOf(1);
+ });
+ });
+
+ describe('Astro components', () => {
+ it('Using slots within slots', async () => {
+ const html = await fixture.readFile('/with-slot-in-slot/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('head link[rel=stylesheet]')).to.have.a.lengthOf(1);
+ expect($('body link[rel=stylesheet]')).to.have.a.lengthOf(0);
+ });
+
+ it('Using slots with Astro.slots.render()', async () => {
+ const html = await fixture.readFile('/with-slot-render/index.html');
+ const $ = cheerio.load(html);
+
+ expect($('head link[rel=stylesheet]')).to.have.a.lengthOf(1);
+ expect($('body link[rel=stylesheet]')).to.have.a.lengthOf(0);
+ });
+
+ it('Using slots within slots using Astro.slots.render()', async () => {
+ const html = await fixture.readFile('/with-slot-in-render-slot/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);
+ });
+ });
+ });
+});
diff --git a/packages/integrations/mdx/test/css-head-mdx.test.js b/packages/integrations/mdx/test/css-head-mdx.test.js
index c38f23701..82a86e5d2 100644
--- a/packages/integrations/mdx/test/css-head-mdx.test.js
+++ b/packages/integrations/mdx/test/css-head-mdx.test.js
@@ -3,6 +3,7 @@ import mdx from '@astrojs/mdx';
import { expect } from 'chai';
import { parseHTML } from 'linkedom';
import { loadFixture } from '../../../astro/test/test-utils.js';
+import * as cheerio from 'cheerio';
describe('Head injection w/ MDX', () => {
let fixture;
@@ -56,5 +57,17 @@ describe('Head injection w/ MDX', () => {
const links = document.querySelectorAll('head link[rel=stylesheet]');
expect(links).to.have.a.lengthOf(1);
});
+
+ it('Using component but no layout', async () => {
+ const html = await fixture.readFile('/noLayoutWithComponent/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/pages/noLayoutWithComponent.mdx b/packages/integrations/mdx/test/fixtures/css-head-mdx/src/pages/noLayoutWithComponent.mdx
new file mode 100644
index 000000000..9d799d4db
--- /dev/null
+++ b/packages/integrations/mdx/test/fixtures/css-head-mdx/src/pages/noLayoutWithComponent.mdx
@@ -0,0 +1,22 @@
+---
+title: 'Lorem'
+description: 'Lorem ipsum dolor sit amet'
+pubDate: 'Jul 02 2022'
+---
+
+import MyComponent from '../components/HelloWorld.astro';
+
+
+## Lorem
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+## Lorem 2
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+<MyComponent />
+
+## Lorem 3
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7c6538959..daf8f0ecf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1849,7 +1849,7 @@ importers:
dependencies:
astro: link:../../..
- packages/astro/test/fixtures/head-injection-md:
+ packages/astro/test/fixtures/head-injection:
specifiers:
astro: workspace:*
dependencies: