diff --git a/packages/astro/e2e/fixtures/server-islands/src/components/Island.astro b/packages/astro/e2e/fixtures/server-islands/src/components/Island.astro index a6d5fa3dc7..b7c376f517 100644 --- a/packages/astro/e2e/fixtures/server-islands/src/components/Island.astro +++ b/packages/astro/e2e/fixtures/server-islands/src/components/Island.astro @@ -1,3 +1,4 @@ --- ---

I am an island

+ diff --git a/packages/astro/e2e/fixtures/server-islands/src/pages/index.astro b/packages/astro/e2e/fixtures/server-islands/src/pages/index.astro index a620e7169c..71b7d0e266 100644 --- a/packages/astro/e2e/fixtures/server-islands/src/pages/index.astro +++ b/packages/astro/e2e/fixtures/server-islands/src/pages/index.astro @@ -7,6 +7,8 @@ import Island from '../components/Island.astro'; - + +

children

+
diff --git a/packages/astro/e2e/server-islands.test.js b/packages/astro/e2e/server-islands.test.js index 0255b9f056..1479b807b1 100644 --- a/packages/astro/e2e/server-islands.test.js +++ b/packages/astro/e2e/server-islands.test.js @@ -30,6 +30,13 @@ test.describe('Server islands', () => { await expect(el, 'element rendered').toBeVisible(); await expect(el, 'should have content').toHaveText('I am an island'); }); + + test('Slots are provided back to the server islands', async ({ page, astro }) => { + await page.goto(astro.resolveUrl('/')); + let el = page.locator('#children'); + + await expect(el, 'element rendered').toBeVisible(); + }); }); test.describe('Production', () => { diff --git a/packages/astro/src/core/server-islands/endpoint.ts b/packages/astro/src/core/server-islands/endpoint.ts index 9a6d0bde82..31eae7f7d3 100644 --- a/packages/astro/src/core/server-islands/endpoint.ts +++ b/packages/astro/src/core/server-islands/endpoint.ts @@ -1,6 +1,6 @@ -import { renderComponent, renderTemplate, type AstroComponentFactory } from '../../runtime/server/index.js'; -import type { APIRoute, ComponentInstance, ManifestData, RouteData, SSRManifest } from '../../@types/astro.js'; -import type { ModuleLoader } from '../module-loader/loader.js'; +import { renderComponent, renderTemplate, type AstroComponentFactory, type ComponentSlots } from '../../runtime/server/index.js'; +import type { ComponentInstance, ManifestData, RouteData, SSRManifest } from '../../@types/astro.js'; +import { createSlotValueFromString } from '../../runtime/server/render/slot.js'; export const SERVER_ISLAND_ROUTE = '/_server-islands/[name]'; export const SERVER_ISLAND_COMPONENT = '_server-islands.astro'; @@ -33,7 +33,7 @@ export function ensureServerIslandRoute(manifest: ManifestData) { type RenderOptions = { componentExport: string; props: Record; - slots: Record; + slots: Record; } export function createEndpoint(manifest: SSRManifest) { @@ -59,10 +59,14 @@ export function createEndpoint(manifest: SSRManifest) { } const props = data.props; - const slots = data.slots; const componentModule = await imp(); const Component = (componentModule as any)[data.componentExport]; + const slots: ComponentSlots = {}; + for(const prop in data.slots) { + slots[prop] = createSlotValueFromString(data.slots[prop]); + } + return renderTemplate`${renderComponent(result, 'Component', Component, props, slots)}`; } diff --git a/packages/astro/src/runtime/server/render/component.ts b/packages/astro/src/runtime/server/render/component.ts index e9811df635..001668d6d2 100644 --- a/packages/astro/src/runtime/server/render/component.ts +++ b/packages/astro/src/runtime/server/render/component.ts @@ -494,7 +494,7 @@ export async function renderComponent( displayName: string, Component: unknown, props: Record, - slots: any = {} + slots: ComponentSlots = {} ): Promise { if (isPromise(Component)) { Component = await Component.catch(handleCancellation); diff --git a/packages/astro/src/runtime/server/render/server-islands.ts b/packages/astro/src/runtime/server/render/server-islands.ts index b2a6f7d922..5a1e45c59c 100644 --- a/packages/astro/src/runtime/server/render/server-islands.ts +++ b/packages/astro/src/runtime/server/render/server-islands.ts @@ -3,7 +3,7 @@ import type { } from '../../../@types/astro.js'; import { renderChild } from "./any.js"; import type { RenderInstance } from "./common.js"; -import { renderSlot, type ComponentSlotValue } from "./slot.js"; +import { renderSlotToString, type ComponentSlots } from "./slot.js"; const internalProps = new Set([ 'server:component-path', @@ -18,9 +18,9 @@ export function containsServerDirective(props: Record,) { export function renderServerIsland( result: SSRResult, - displayName: string, + _displayName: string, props: Record, - slots: Record, + slots: ComponentSlots, ): RenderInstance { return { async render(destination) { @@ -42,8 +42,14 @@ export function renderServerIsland( destination.write('') // Render the slots - if(slots.fallback) { - await renderChild(destination, slots.fallback(result)); + const renderedSlots: Record = {}; + for(const name in slots) { + if(name !== 'fallback') { + const content = await renderSlotToString(result, slots[name]); + renderedSlots[name] = content.toString(); + } else { + await renderChild(destination, slots.fallback(result)); + } } const hostId = crypto.randomUUID(); @@ -55,7 +61,7 @@ let script = document.querySelector('script[data-island-id="${hostId}"]'); let data = { componentExport, props: ${JSON.stringify(props)}, - slot: ${JSON.stringify(slots)}, + slots: ${JSON.stringify(renderedSlots)}, }; let response = await fetch('/_server-islands/${componentId}', { diff --git a/packages/astro/src/runtime/server/render/slot.ts b/packages/astro/src/runtime/server/render/slot.ts index 95f9008250..fae7edc9eb 100644 --- a/packages/astro/src/runtime/server/render/slot.ts +++ b/packages/astro/src/runtime/server/render/slot.ts @@ -1,8 +1,8 @@ import type { SSRResult } from '../../../@types/astro.js'; -import type { renderTemplate } from './astro/render-template.js'; +import { renderTemplate } from './astro/render-template.js'; import type { RenderInstruction } from './instruction.js'; -import { HTMLString, markHTMLString } from '../escape.js'; +import { HTMLString, markHTMLString, unescapeHTML } from '../escape.js'; import { renderChild } from './any.js'; import { type RenderDestination, type RenderInstance, chunkToString } from './common.js'; @@ -103,3 +103,9 @@ export async function renderSlots( } return { slotInstructions, children }; } + +export function createSlotValueFromString(content: string): ComponentSlotValue { + return function() { + return renderTemplate`${unescapeHTML(content)}`; + }; +}