Ensure astro-island scripts render when using Astro.slots.render (#5326)
* Ensure astro-island scripts render when using Astro.slots.render * Adding a changeset
This commit is contained in:
parent
b211eadeff
commit
88c1bbe3a7
7 changed files with 60 additions and 7 deletions
5
.changeset/chilly-ladybugs-leave.md
Normal file
5
.changeset/chilly-ladybugs-leave.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fix omitted island hydration scripts in slots
|
|
@ -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';
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
4
packages/astro/test/fixtures/astro-slots-nested/src/components/SlotRender.astro
vendored
Normal file
4
packages/astro/test/fixtures/astro-slots-nested/src/components/SlotRender.astro
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
const content = await Astro.slots.render('default');
|
||||
---
|
||||
<Fragment set:html={content} />
|
17
packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro
vendored
Normal file
17
packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro
vendored
Normal file
|
@ -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>
|
Loading…
Reference in a new issue