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,
|
SSRLoadedRenderer,
|
||||||
SSRResult,
|
SSRResult,
|
||||||
} from '../../@types/astro';
|
} 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 { renderJSX } from '../../runtime/server/jsx.js';
|
||||||
import { AstroCookies } from '../cookies/index.js';
|
import { AstroCookies } from '../cookies/index.js';
|
||||||
import { LogOptions, warn } from '../logger/core.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) =>
|
const content = await renderSlot(this.#result, this.#slots[name]);
|
||||||
res != null ? String(res) : res
|
const outHTML = stringifyChunk(this.#result, content);
|
||||||
);
|
|
||||||
if (cacheable) this.#cache.set(name, content);
|
if (cacheable) this.#cache.set(name, outHTML);
|
||||||
return content;
|
return outHTML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
getPrescripts,
|
getPrescripts,
|
||||||
PrescriptType,
|
PrescriptType,
|
||||||
} from '../scripts.js';
|
} from '../scripts.js';
|
||||||
|
import { isSlotString, type SlotString } from './slot.js';
|
||||||
|
|
||||||
export const Fragment = Symbol.for('astro:fragment');
|
export const Fragment = Symbol.for('astro:fragment');
|
||||||
export const Renderer = Symbol.for('astro:renderer');
|
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.
|
// Rendering produces either marked strings of HTML or instructions for hydration.
|
||||||
// These directive instructions bubble all the way up to renderPage so that we
|
// 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.
|
// 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) {
|
switch ((chunk as any).type) {
|
||||||
case 'directive': {
|
case 'directive': {
|
||||||
const { hydration } = chunk as RenderInstruction;
|
const { hydration } = chunk as RenderInstruction;
|
||||||
|
@ -39,6 +40,18 @@ export function stringifyChunk(result: SSRResult, chunk: string | RenderInstruct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default: {
|
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();
|
return chunk.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,22 @@ import type { RenderInstruction } from './types.js';
|
||||||
import { HTMLString, markHTMLString } from '../escape.js';
|
import { HTMLString, markHTMLString } from '../escape.js';
|
||||||
import { renderChild } from './any.js';
|
import { renderChild } from './any.js';
|
||||||
|
|
||||||
|
const slotString = Symbol.for('astro:slot-string');
|
||||||
|
|
||||||
export class SlotString extends HTMLString {
|
export class SlotString extends HTMLString {
|
||||||
public instructions: null | RenderInstruction[];
|
public instructions: null | RenderInstruction[];
|
||||||
|
public [slotString]: boolean;
|
||||||
constructor(content: string, instructions: null | RenderInstruction[]) {
|
constructor(content: string, instructions: null | RenderInstruction[]) {
|
||||||
super(content);
|
super(content);
|
||||||
this.instructions = instructions;
|
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> {
|
export async function renderSlot(_result: any, slotted: string, fallback?: any): Promise<string> {
|
||||||
if (slotted) {
|
if (slotted) {
|
||||||
let iterator = renderChild(slotted);
|
let iterator = renderChild(slotted);
|
||||||
|
|
|
@ -17,4 +17,10 @@ describe('Nested Slots', () => {
|
||||||
const scriptInTemplate = $($('template')[0].children[0]).find('script');
|
const scriptInTemplate = $($('template')[0].children[0]).find('script');
|
||||||
expect(scriptInTemplate).to.have.a.lengthOf(0, 'script defined outside of the inner template');
|
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