Skip to content

Commit

Permalink
Ensure astro-island scripts render when using Astro.slots.render (#5326)
Browse files Browse the repository at this point in the history
* Ensure astro-island scripts render when using Astro.slots.render

* Adding a changeset
  • Loading branch information
matthewp authored Nov 8, 2022
1 parent b211ead commit 88c1bbe
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-ladybugs-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fix omitted island hydration scripts in slots
12 changes: 6 additions & 6 deletions packages/astro/src/core/render/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
SSRLoadedRenderer,
SSRResult,
} from '../../@types/astro';
import { renderSlot } from '../../runtime/server/index.js';
import { renderSlot, stringifyChunk } from '../../runtime/server/index.js';
import { renderJSX } from '../../runtime/server/jsx.js';
import { AstroCookies } from '../cookies/index.js';
import { LogOptions, warn } from '../logger/core.js';
Expand Down Expand Up @@ -118,11 +118,11 @@ class Slots {
}
}
}
const content = await renderSlot(this.#result, this.#slots[name]).then((res) =>
res != null ? String(res) : res
);
if (cacheable) this.#cache.set(name, content);
return content;
const content = await renderSlot(this.#result, this.#slots[name]);
const outHTML = stringifyChunk(this.#result, content);

if (cacheable) this.#cache.set(name, outHTML);
return outHTML;
}
}

Expand Down
15 changes: 14 additions & 1 deletion packages/astro/src/runtime/server/render/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getPrescripts,
PrescriptType,
} from '../scripts.js';
import { isSlotString, type SlotString } from './slot.js';

export const Fragment = Symbol.for('astro:fragment');
export const Renderer = Symbol.for('astro:renderer');
Expand All @@ -18,7 +19,7 @@ export const decoder = new TextDecoder();
// Rendering produces either marked strings of HTML or instructions for hydration.
// These directive instructions bubble all the way up to renderPage so that we
// can ensure they are added only once, and as soon as possible.
export function stringifyChunk(result: SSRResult, chunk: string | RenderInstruction) {
export function stringifyChunk(result: SSRResult, chunk: string | SlotString | RenderInstruction) {
switch ((chunk as any).type) {
case 'directive': {
const { hydration } = chunk as RenderInstruction;
Expand All @@ -39,6 +40,18 @@ export function stringifyChunk(result: SSRResult, chunk: string | RenderInstruct
}
}
default: {
if(isSlotString(chunk as string)) {
let out = '';
const c = (chunk as SlotString);
if(c.instructions) {
for(const instr of c.instructions) {
out += stringifyChunk(result, instr);
}
}
out += chunk.toString();
return out;
}

return chunk.toString();
}
}
Expand Down
8 changes: 8 additions & 0 deletions packages/astro/src/runtime/server/render/slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@ import type { RenderInstruction } from './types.js';
import { HTMLString, markHTMLString } from '../escape.js';
import { renderChild } from './any.js';

const slotString = Symbol.for('astro:slot-string');

export class SlotString extends HTMLString {
public instructions: null | RenderInstruction[];
public [slotString]: boolean;
constructor(content: string, instructions: null | RenderInstruction[]) {
super(content);
this.instructions = instructions;
this[slotString] = true;
}
}

export function isSlotString(str: string): str is any {
return !!(str as any)[slotString];
}

export async function renderSlot(_result: any, slotted: string, fallback?: any): Promise<string> {
if (slotted) {
let iterator = renderChild(slotted);
Expand Down
6 changes: 6 additions & 0 deletions packages/astro/test/astro-slots-nested.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,10 @@ describe('Nested Slots', () => {
const scriptInTemplate = $($('template')[0].children[0]).find('script');
expect(scriptInTemplate).to.have.a.lengthOf(0, 'script defined outside of the inner template');
});

it('Slots rendered via Astro.slots.render have the hydration script', async () => {
const html = await fixture.readFile('/component-slot/index.html');
const $ = cheerio.load(html);
expect($('script')).to.have.a.lengthOf(1, 'script rendered');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
const content = await Astro.slots.render('default');
---
<Fragment set:html={content} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
import SlotRender from '../components/SlotRender.astro'
import Inner from '../components/Inner'
---

<html lang="en">
<head>
<title>Testing</title>
</head>
<body>
<main>
<SlotRender>
<Inner client:load />
</SlotRender>
</main>
</body>
</html>

0 comments on commit 88c1bbe

Please sign in to comment.