diff --git a/.changeset/wicked-books-sip.md b/.changeset/wicked-books-sip.md new file mode 100644 index 000000000000..d6e4741a56e7 --- /dev/null +++ b/.changeset/wicked-books-sip.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix server islands with trailingSlash: always diff --git a/packages/astro/e2e/fixtures/server-islands/astro.config.mjs b/packages/astro/e2e/fixtures/server-islands/astro.config.mjs index f03c53335d30..4bec97b9e1ad 100644 --- a/packages/astro/e2e/fixtures/server-islands/astro.config.mjs +++ b/packages/astro/e2e/fixtures/server-islands/astro.config.mjs @@ -5,9 +5,11 @@ import nodejs from '@astrojs/node'; // https://astro.build/config export default defineConfig({ + base: '/base', output: 'hybrid', adapter: nodejs({ mode: 'standalone' }), integrations: [react(), mdx()], + trailingSlash: 'always', experimental: { serverIslands: true, } diff --git a/packages/astro/e2e/server-islands.test.js b/packages/astro/e2e/server-islands.test.js index 1479b807b121..5f29acf60954 100644 --- a/packages/astro/e2e/server-islands.test.js +++ b/packages/astro/e2e/server-islands.test.js @@ -16,7 +16,7 @@ test.describe('Server islands', () => { }); test('Load content from the server', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/')); + await page.goto(astro.resolveUrl('/base/')); let el = page.locator('#island'); await expect(el, 'element rendered').toBeVisible(); @@ -24,7 +24,7 @@ test.describe('Server islands', () => { }); test('Can be in an MDX file', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/mdx')); + await page.goto(astro.resolveUrl('/base/mdx/')); let el = page.locator('#island'); await expect(el, 'element rendered').toBeVisible(); @@ -32,7 +32,7 @@ test.describe('Server islands', () => { }); test('Slots are provided back to the server islands', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/')); + await page.goto(astro.resolveUrl('/base/')); let el = page.locator('#children'); await expect(el, 'element rendered').toBeVisible(); @@ -55,7 +55,7 @@ test.describe('Server islands', () => { }); test('Only one component in prod', async ({ page, astro }) => { - await page.goto(astro.resolveUrl('/')); + await page.goto(astro.resolveUrl('/base/')); let el = page.locator('#island'); diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index f11d708f1bec..20eb91f63805 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -3324,6 +3324,7 @@ export interface SSRResult { * Whether the page has failed with a non-recoverable error, or the client disconnected. */ cancelled: boolean; + base: string; styles: Set; scripts: Set; links: Set; @@ -3352,6 +3353,7 @@ export interface SSRResult { pathname: string; cookies: AstroCookies | undefined; serverIslandNameMap: Map; + trailingSlash: AstroConfig['trailingSlash']; _metadata: SSRMetadata; } diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts index bc97212a86b0..902ba0fc234e 100644 --- a/packages/astro/src/core/render-context.ts +++ b/packages/astro/src/core/render-context.ts @@ -347,6 +347,7 @@ export class RenderContext { // This object starts here as an empty shell (not yet the result) but then // calling the render() function will populate the object with scripts, styles, etc. const result: SSRResult = { + base: manifest.base, cancelled: false, clientDirectives, inlinedScripts, @@ -368,6 +369,7 @@ export class RenderContext { styles, actionResult, serverIslandNameMap: manifest.serverIslandNameMap ?? new Map(), + trailingSlash: manifest.trailingSlash, _metadata: { hasHydrationScript: false, rendererSpecificHydrationScripts: new Set(), diff --git a/packages/astro/src/runtime/server/render/server-islands.ts b/packages/astro/src/runtime/server/render/server-islands.ts index 52b6b006e193..9a27fed51aa8 100644 --- a/packages/astro/src/runtime/server/render/server-islands.ts +++ b/packages/astro/src/runtime/server/render/server-islands.ts @@ -60,6 +60,7 @@ export function renderServerIsland( } const hostId = crypto.randomUUID(); + const serverIslandUrl = `${result.base}_server-islands/${componentId}${result.trailingSlash === 'always' ? '/' : ''}`; destination.write(`