0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-04-07 23:41:43 -05:00

fix: renderToString function not render properly nested slots when they are components (#13432)

* Fix/render to string slots (#1)

* add force HTMLString with local tests

* remove local test & logs

* remove test counter in basic exemple

* re-add test & create SlotString instead of HTMLString

* add test of a nested rendered component inside renderToString slots function

* add changeset on astro package

* Apply suggestions from code review

---------

Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>

Co-authored-by: ematipico <602478+ematipico@users.noreply.github.com>
This commit is contained in:
Edward Brunetiere 2025-03-18 10:32:07 +01:00 committed by GitHub
parent 7783dbf811
commit defad33140
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 41 additions and 0 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fix an issue in the Container API, where the `renderToString` function doesn't render adequately nested slots when they are components.

View file

@ -0,0 +1,9 @@
---
interface Props {
count?: number;
}
let { count = 0 } = Astro.props;
---
<p id="counter">{count}</p>

View file

@ -1,6 +1,7 @@
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { expect, test } from 'vitest';
import Card from '../src/components/Card.astro';
import CounterLight from '../src/components/CounterLight.astro';
test('Card with slots', async () => {
const container = await AstroContainer.create();
@ -13,3 +14,16 @@ test('Card with slots', async () => {
expect(result).toContain('This is a card');
expect(result).toContain('Card content');
});
test('Card with nested CounterLight', async () => {
const container = await AstroContainer.create();
const counterLight = await container.renderToString(CounterLight, { props: { count: 1 } });
const result = await container.renderToString(Card, {
slots: {
default: counterLight,
},
});
expect(result).toContain('This is a card');
expect(result).toContain(counterLight);
});

View file

@ -26,6 +26,7 @@ import type {
SSRResult,
} from '../types/public/internal.js';
import { ContainerPipeline } from './pipeline.js';
import { SlotString } from '../runtime/server/render/slot.js';
/** Public type, used for integrations to define a renderer for the container API */
export type ContainerRenderer = {
@ -474,6 +475,10 @@ export class experimental_AstroContainer {
component: AstroComponentFactory,
options: ContainerRenderOptions = {},
): Promise<string> {
if (options.slots) {
options.slots = markAllSlotsAsSlotString(options.slots);
}
const response = await this.renderToResponse(component, options);
return await response.text();
}
@ -588,3 +593,11 @@ export class experimental_AstroContainer {
function isNamedRenderer(renderer: any): renderer is NamedSSRLoadedRendererValue {
return !!renderer?.name;
}
function markAllSlotsAsSlotString(slots: Record<string, any>): Record<string, any> {
const markedSlots: Record<string, any> = {};
for (const slotName in slots) {
markedSlots[slotName] = new SlotString(slots[slotName], null);
}
return markedSlots;
}