From 05bc7f017b516c1ff9f14e16d1e4898c4576cc1b Mon Sep 17 00:00:00 2001
From: Matthew Phillips <matthew@skypack.dev>
Date: Tue, 8 Nov 2022 11:07:54 -0500
Subject: [PATCH 1/2] Ensure astro-island scripts render when using
 Astro.slots.render

---
 packages/astro/src/core/render/result.ts        | 12 ++++++------
 .../astro/src/runtime/server/render/common.ts   | 15 ++++++++++++++-
 .../astro/src/runtime/server/render/slot.ts     |  8 ++++++++
 packages/astro/test/astro-slots-nested.test.js  |  6 ++++++
 .../src/components/SlotRender.astro             |  4 ++++
 .../src/pages/component-slot.astro              | 17 +++++++++++++++++
 6 files changed, 55 insertions(+), 7 deletions(-)
 create mode 100644 packages/astro/test/fixtures/astro-slots-nested/src/components/SlotRender.astro
 create mode 100644 packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro

diff --git a/packages/astro/src/core/render/result.ts b/packages/astro/src/core/render/result.ts
index 2d7c07d84eef..615f47bfafdf 100644
--- a/packages/astro/src/core/render/result.ts
+++ b/packages/astro/src/core/render/result.ts
@@ -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;
 	}
 }
 
diff --git a/packages/astro/src/runtime/server/render/common.ts b/packages/astro/src/runtime/server/render/common.ts
index 3aac428cf747..ef33ae3eeac0 100644
--- a/packages/astro/src/runtime/server/render/common.ts
+++ b/packages/astro/src/runtime/server/render/common.ts
@@ -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();
 		}
 	}
diff --git a/packages/astro/src/runtime/server/render/slot.ts b/packages/astro/src/runtime/server/render/slot.ts
index 5aee6dfa4101..52a1d59a259a 100644
--- a/packages/astro/src/runtime/server/render/slot.ts
+++ b/packages/astro/src/runtime/server/render/slot.ts
@@ -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);
diff --git a/packages/astro/test/astro-slots-nested.test.js b/packages/astro/test/astro-slots-nested.test.js
index 58f7153b04d4..9e02388cea11 100644
--- a/packages/astro/test/astro-slots-nested.test.js
+++ b/packages/astro/test/astro-slots-nested.test.js
@@ -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');
+	});
 });
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/components/SlotRender.astro b/packages/astro/test/fixtures/astro-slots-nested/src/components/SlotRender.astro
new file mode 100644
index 000000000000..3da3fbd54218
--- /dev/null
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/components/SlotRender.astro
@@ -0,0 +1,4 @@
+---
+const content = await Astro.slots.render('default');
+---
+<Fragment set:html={content} />
diff --git a/packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro b/packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro
new file mode 100644
index 000000000000..b9a03f887eb4
--- /dev/null
+++ b/packages/astro/test/fixtures/astro-slots-nested/src/pages/component-slot.astro
@@ -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>

From 5408711f4408fe8f1756b533222158409a62f872 Mon Sep 17 00:00:00 2001
From: Matthew Phillips <matthew@skypack.dev>
Date: Tue, 8 Nov 2022 11:09:18 -0500
Subject: [PATCH 2/2] Adding a changeset

---
 .changeset/chilly-ladybugs-leave.md | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .changeset/chilly-ladybugs-leave.md

diff --git a/.changeset/chilly-ladybugs-leave.md b/.changeset/chilly-ladybugs-leave.md
new file mode 100644
index 000000000000..52a016ff7409
--- /dev/null
+++ b/.changeset/chilly-ladybugs-leave.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix omitted island hydration scripts in slots