From 643b00a7de1fe568a16bf12c6b387c57efd72831 Mon Sep 17 00:00:00 2001 From: Aenri Lovehart Date: Mon, 30 Mar 2026 11:22:54 -0400 Subject: [PATCH 01/14] Fixes #15620 --- packages/kit/src/core/postbuild/analyse.js | 7 +++-- packages/kit/src/core/postbuild/prerender.js | 31 +++++++++++++------ .../basics/src/routes/linked-api/+page.svelte | 5 +++ .../my-awesome-endpoint.json/+server.js | 7 +++++ .../+page.svelte | 1 + .../prerender-with-server-endpoint/+server.js | 5 +++ .../prerendering/basics/test/tests.spec.js | 10 ++++++ 7 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 packages/kit/test/prerendering/basics/src/routes/linked-api/+page.svelte create mode 100644 packages/kit/test/prerendering/basics/src/routes/linked-api/my-awesome-endpoint.json/+server.js create mode 100644 packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte create mode 100644 packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index 9255dd40f5f3..3763d514cb18 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -122,7 +122,6 @@ async function analyse({ const route_config = page?.config ?? endpoint?.config ?? {}; const prerender = page?.prerender ?? endpoint?.prerender; - if (prerender !== true) { for (const feature of list_features( route, @@ -142,10 +141,12 @@ async function analyse({ config: route_config, methods: Array.from(new Set([...page_methods, ...api_methods])), page: { - methods: page_methods + methods: page_methods, + prerender: page?.prerender }, api: { - methods: api_methods + methods: api_methods, + prerender: endpoint?.prerender }, prerender, entries: diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 12223d2722ce..8b3fa44fa95d 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -214,15 +214,17 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { * @param {string} decoded * @param {string} [encoded] * @param {string} [generated_from_id] + * @param {boolean} [expect_html] */ - function enqueue(referrer, decoded, encoded, generated_from_id) { - if (seen.has(decoded)) return; - seen.add(decoded); + function enqueue(referrer, decoded, encoded, generated_from_id, expect_html) { + const key = expect_html ? decoded + '\x00page' : decoded + if (seen.has(key)) return; + seen.add(key); const file = decoded.slice(config.paths.base.length + 1); if (files.has(file)) return; - return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id)); + return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id, expect_html)); } /** @@ -230,17 +232,20 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { * @param {string} encoded * @param {string?} referrer * @param {string} [generated_from_id] + * @param {boolean} [expect_html] */ - async function visit(decoded, encoded, referrer, generated_from_id) { + async function visit(decoded, encoded, referrer, generated_from_id, expect_html) { if (!decoded.startsWith(config.paths.base)) { handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' }); return; } + + const requestHeaders = expect_html ? { Accept: 'text/html' } : {}; /** @type {Map} */ const dependencies = new Map(); - const response = await server.respond(new Request(config.prerender.origin + encoded), { + const response = await server.respond(new Request(config.prerender.origin + encoded, { headers: requestHeaders }), { getClientAddress() { throw new Error('Cannot read clientAddress during prerendering'); }, @@ -261,7 +266,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { // stuff in `static` return readFileSync(join(config.files.assets, file)); }, - emulator + emulator, }); const encoded_id = response.headers.get('x-sveltekit-routeid'); @@ -356,7 +361,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { /** @type {Set} */ (expected_hashlinks.get(key)).add(decoded); } - void enqueue(decoded, decode_uri(pathname), pathname); + void enqueue(decoded, decode_uri(pathname), pathname, undefined, true); } } } @@ -535,7 +540,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { if (processed_id.includes('[')) continue; const path = `/${get_route_segments(processed_id).join('/')}`; - void enqueue(null, config.paths.base + path); + + const route_data = metadata.routes.get(id); + if (route_data?.page.prerender === true) void enqueue(null, config.paths.base + path, undefined, undefined, true) + if (route_data?.api.prerender === true) void enqueue(null, config.paths.base + path, undefined, undefined, false) } } } else { @@ -544,8 +552,11 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { } for (const { id, entries } of route_level_entries) { + const route_data = metadata.routes.get(id); + for (const entry of entries) { - void enqueue(null, config.paths.base + entry, undefined, id); + if (route_data?.page.prerender === true) void enqueue(null, config.paths.base + entry, undefined, id, true); + if (route_data?.api.prerender === true) void enqueue(null, config.paths.base + entry, undefined, id, false); } } diff --git a/packages/kit/test/prerendering/basics/src/routes/linked-api/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/linked-api/+page.svelte new file mode 100644 index 000000000000..c454a66f70de --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/linked-api/+page.svelte @@ -0,0 +1,5 @@ + + +My Awesome Endpoint \ No newline at end of file diff --git a/packages/kit/test/prerendering/basics/src/routes/linked-api/my-awesome-endpoint.json/+server.js b/packages/kit/test/prerendering/basics/src/routes/linked-api/my-awesome-endpoint.json/+server.js new file mode 100644 index 000000000000..424e696be1bf --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/linked-api/my-awesome-endpoint.json/+server.js @@ -0,0 +1,7 @@ +export const prerender = true; + +export function GET() { + return new Response(JSON.stringify({ ok: true }), { + headers: { 'content-type': 'application/json' } + }); +} \ No newline at end of file diff --git a/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte new file mode 100644 index 000000000000..20dd3d8af17f --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte @@ -0,0 +1 @@ +

prerendered page with server endpoint

\ No newline at end of file diff --git a/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js b/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js new file mode 100644 index 000000000000..64b7237d1d66 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js @@ -0,0 +1,5 @@ +export function GET() { + return new Response(JSON.stringify({ ok: true }), { + headers: { 'content-type': 'application/json' } + }); +} \ No newline at end of file diff --git a/packages/kit/test/prerendering/basics/test/tests.spec.js b/packages/kit/test/prerendering/basics/test/tests.spec.js index adc1d198154f..4b6999f0ee23 100644 --- a/packages/kit/test/prerendering/basics/test/tests.spec.js +++ b/packages/kit/test/prerendering/basics/test/tests.spec.js @@ -158,6 +158,16 @@ test('does not prerender page with shadow endpoint with non-load handler', () => assert.isFalse(fs.existsSync(`${build}/shadowed-post/__data.json`)); }); +test('prerenders page that coexists with a GET server endpoint', () => { + assert.isTrue(fs.existsSync(`${build}/prerender-with-server-endpoint.html`)); +}); + +test('prerendering a page with a linked GET server endpoint processes properly', () => { + assert.isTrue(fs.existsSync(`${build}/linked-api.html`)); + assert.isTrue(fs.existsSync(`${build}/linked-api/my-awesome-endpoint.json`)); + assert.isFalse(fs.existsSync(`${build}/linked-api/my-awesome-endpoint.html`)); +}); + test('decodes paths when writing files', () => { let content = read('encoding/path with spaces.html'); expect(content).toMatch('

path with spaces

'); From bec5e627532a30de3926941efe1429f187e77377 Mon Sep 17 00:00:00 2001 From: Aenri Lovehart Date: Mon, 30 Mar 2026 11:36:21 -0400 Subject: [PATCH 02/14] Ran pnpm lint --- packages/kit/src/core/postbuild/prerender.js | 71 +++++++++++-------- .../basics/src/routes/linked-api/+page.svelte | 2 +- .../my-awesome-endpoint.json/+server.js | 8 +-- .../+page.svelte | 2 +- .../prerender-with-server-endpoint/+server.js | 8 +-- .../prerendering/basics/test/tests.spec.js | 6 +- 6 files changed, 53 insertions(+), 44 deletions(-) diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 8b3fa44fa95d..9c548503019d 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -217,14 +217,16 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { * @param {boolean} [expect_html] */ function enqueue(referrer, decoded, encoded, generated_from_id, expect_html) { - const key = expect_html ? decoded + '\x00page' : decoded + const key = expect_html ? decoded + '\x00page' : decoded; if (seen.has(key)) return; seen.add(key); const file = decoded.slice(config.paths.base.length + 1); if (files.has(file)) return; - return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id, expect_html)); + return q.add(() => + visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id, expect_html) + ); } /** @@ -239,35 +241,38 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' }); return; } - + const requestHeaders = expect_html ? { Accept: 'text/html' } : {}; /** @type {Map} */ const dependencies = new Map(); - const response = await server.respond(new Request(config.prerender.origin + encoded, { headers: requestHeaders }), { - getClientAddress() { - throw new Error('Cannot read clientAddress during prerendering'); - }, - prerendering: { - dependencies, - remote_responses - }, - read: (file) => { - // stuff we just wrote - const filepath = saved.get(file); - if (filepath) return readFileSync(filepath); - - // Static assets emitted during build - if (file.startsWith(config.appDir)) { - return readFileSync(`${out}/server/${file}`); - } + const response = await server.respond( + new Request(config.prerender.origin + encoded, { headers: requestHeaders }), + { + getClientAddress() { + throw new Error('Cannot read clientAddress during prerendering'); + }, + prerendering: { + dependencies, + remote_responses + }, + read: (file) => { + // stuff we just wrote + const filepath = saved.get(file); + if (filepath) return readFileSync(filepath); + + // Static assets emitted during build + if (file.startsWith(config.appDir)) { + return readFileSync(`${out}/server/${file}`); + } - // stuff in `static` - return readFileSync(join(config.files.assets, file)); - }, - emulator, - }); + // stuff in `static` + return readFileSync(join(config.files.assets, file)); + }, + emulator + } + ); const encoded_id = response.headers.get('x-sveltekit-routeid'); const decoded_id = encoded_id && decode_uri(encoded_id); @@ -540,10 +545,12 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { if (processed_id.includes('[')) continue; const path = `/${get_route_segments(processed_id).join('/')}`; - - const route_data = metadata.routes.get(id); - if (route_data?.page.prerender === true) void enqueue(null, config.paths.base + path, undefined, undefined, true) - if (route_data?.api.prerender === true) void enqueue(null, config.paths.base + path, undefined, undefined, false) + + const route_data = metadata.routes.get(id); + if (route_data?.page.prerender === true) + void enqueue(null, config.paths.base + path, undefined, undefined, true); + if (route_data?.api.prerender === true) + void enqueue(null, config.paths.base + path, undefined, undefined, false); } } } else { @@ -555,8 +562,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const route_data = metadata.routes.get(id); for (const entry of entries) { - if (route_data?.page.prerender === true) void enqueue(null, config.paths.base + entry, undefined, id, true); - if (route_data?.api.prerender === true) void enqueue(null, config.paths.base + entry, undefined, id, false); + if (route_data?.page.prerender === true) + void enqueue(null, config.paths.base + entry, undefined, id, true); + if (route_data?.api.prerender === true) + void enqueue(null, config.paths.base + entry, undefined, id, false); } } diff --git a/packages/kit/test/prerendering/basics/src/routes/linked-api/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/linked-api/+page.svelte index c454a66f70de..bde0a5406dea 100644 --- a/packages/kit/test/prerendering/basics/src/routes/linked-api/+page.svelte +++ b/packages/kit/test/prerendering/basics/src/routes/linked-api/+page.svelte @@ -2,4 +2,4 @@ import { resolve } from '$app/paths'; -My Awesome Endpoint \ No newline at end of file +My Awesome Endpoint diff --git a/packages/kit/test/prerendering/basics/src/routes/linked-api/my-awesome-endpoint.json/+server.js b/packages/kit/test/prerendering/basics/src/routes/linked-api/my-awesome-endpoint.json/+server.js index 424e696be1bf..f01a402dd8ed 100644 --- a/packages/kit/test/prerendering/basics/src/routes/linked-api/my-awesome-endpoint.json/+server.js +++ b/packages/kit/test/prerendering/basics/src/routes/linked-api/my-awesome-endpoint.json/+server.js @@ -1,7 +1,7 @@ export const prerender = true; export function GET() { - return new Response(JSON.stringify({ ok: true }), { - headers: { 'content-type': 'application/json' } - }); -} \ No newline at end of file + return new Response(JSON.stringify({ ok: true }), { + headers: { 'content-type': 'application/json' } + }); +} diff --git a/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte index 20dd3d8af17f..8c966b6e13b0 100644 --- a/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte +++ b/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte @@ -1 +1 @@ -

prerendered page with server endpoint

\ No newline at end of file +

prerendered page with server endpoint

diff --git a/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js b/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js index 64b7237d1d66..7956019cce17 100644 --- a/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js +++ b/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js @@ -1,5 +1,5 @@ export function GET() { - return new Response(JSON.stringify({ ok: true }), { - headers: { 'content-type': 'application/json' } - }); -} \ No newline at end of file + return new Response(JSON.stringify({ ok: true }), { + headers: { 'content-type': 'application/json' } + }); +} diff --git a/packages/kit/test/prerendering/basics/test/tests.spec.js b/packages/kit/test/prerendering/basics/test/tests.spec.js index 4b6999f0ee23..5ef22feeb1e9 100644 --- a/packages/kit/test/prerendering/basics/test/tests.spec.js +++ b/packages/kit/test/prerendering/basics/test/tests.spec.js @@ -159,13 +159,13 @@ test('does not prerender page with shadow endpoint with non-load handler', () => }); test('prerenders page that coexists with a GET server endpoint', () => { - assert.isTrue(fs.existsSync(`${build}/prerender-with-server-endpoint.html`)); + assert.isTrue(fs.existsSync(`${build}/prerender-with-server-endpoint.html`)); }); test('prerendering a page with a linked GET server endpoint processes properly', () => { assert.isTrue(fs.existsSync(`${build}/linked-api.html`)); - assert.isTrue(fs.existsSync(`${build}/linked-api/my-awesome-endpoint.json`)); - assert.isFalse(fs.existsSync(`${build}/linked-api/my-awesome-endpoint.html`)); + assert.isTrue(fs.existsSync(`${build}/linked-api/my-awesome-endpoint.json`)); + assert.isFalse(fs.existsSync(`${build}/linked-api/my-awesome-endpoint.html`)); }); test('decodes paths when writing files', () => { From 4c274a38a2a142ac7b7640d713693bd6cb3bfc1f Mon Sep 17 00:00:00 2001 From: Aenri Lovehart Date: Sat, 4 Apr 2026 21:15:14 -0400 Subject: [PATCH 03/14] made changeset, added add'l check to prerender.js, and edited test names to conform --- .changeset/brown-eggs-march.md | 5 +++++ packages/kit/src/core/postbuild/prerender.js | 10 +++++----- .../+page.svelte | 0 .../+server.js | 0 .../basics/src/routes/get-and-post/+page.svelte | 1 + .../basics/src/routes/get-and-post/+server.ts | 2 ++ .../kit/test/prerendering/basics/test/tests.spec.js | 8 ++++++-- 7 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 .changeset/brown-eggs-march.md rename packages/kit/test/prerendering/basics/src/routes/{prerender-with-server-endpoint => duplicate-get}/+page.svelte (100%) rename packages/kit/test/prerendering/basics/src/routes/{prerender-with-server-endpoint => duplicate-get}/+server.js (100%) create mode 100644 packages/kit/test/prerendering/basics/src/routes/get-and-post/+page.svelte create mode 100644 packages/kit/test/prerendering/basics/src/routes/get-and-post/+server.ts diff --git a/.changeset/brown-eggs-march.md b/.changeset/brown-eggs-march.md new file mode 100644 index 000000000000..79f946e8e712 --- /dev/null +++ b/.changeset/brown-eggs-march.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Fixed prerendering pipeline bug found in sveltejs/kit#15620 and sveltejs/kit#10735 diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 9c548503019d..b9fb35425d07 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -242,7 +242,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { return; } - const requestHeaders = expect_html ? { Accept: 'text/html' } : {}; + const requestHeaders = expect_html ? { Accept: 'text/html' } : undefined; /** @type {Map} */ const dependencies = new Map(); @@ -547,9 +547,9 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const path = `/${get_route_segments(processed_id).join('/')}`; const route_data = metadata.routes.get(id); - if (route_data?.page.prerender === true) + if (route_data?.page.prerender === true && route_data?.page.methods.includes('GET')) void enqueue(null, config.paths.base + path, undefined, undefined, true); - if (route_data?.api.prerender === true) + if (route_data?.api.prerender === true && route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) void enqueue(null, config.paths.base + path, undefined, undefined, false); } } @@ -562,9 +562,9 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const route_data = metadata.routes.get(id); for (const entry of entries) { - if (route_data?.page.prerender === true) + if (route_data?.page.prerender === true && route_data?.page.methods.includes('GET')) void enqueue(null, config.paths.base + entry, undefined, id, true); - if (route_data?.api.prerender === true) + if (route_data?.api.prerender === true && route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) void enqueue(null, config.paths.base + entry, undefined, id, false); } } diff --git a/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/duplicate-get/+page.svelte similarity index 100% rename from packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+page.svelte rename to packages/kit/test/prerendering/basics/src/routes/duplicate-get/+page.svelte diff --git a/packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js b/packages/kit/test/prerendering/basics/src/routes/duplicate-get/+server.js similarity index 100% rename from packages/kit/test/prerendering/basics/src/routes/prerender-with-server-endpoint/+server.js rename to packages/kit/test/prerendering/basics/src/routes/duplicate-get/+server.js diff --git a/packages/kit/test/prerendering/basics/src/routes/get-and-post/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/get-and-post/+page.svelte new file mode 100644 index 000000000000..6b51d324868e --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/get-and-post/+page.svelte @@ -0,0 +1 @@ +Hello World... \ No newline at end of file diff --git a/packages/kit/test/prerendering/basics/src/routes/get-and-post/+server.ts b/packages/kit/test/prerendering/basics/src/routes/get-and-post/+server.ts new file mode 100644 index 000000000000..b4c132caabef --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/get-and-post/+server.ts @@ -0,0 +1,2 @@ + +export async function POST() { return new Response('OK', { status: 200 }) } diff --git a/packages/kit/test/prerendering/basics/test/tests.spec.js b/packages/kit/test/prerendering/basics/test/tests.spec.js index 5ef22feeb1e9..e237bff122d1 100644 --- a/packages/kit/test/prerendering/basics/test/tests.spec.js +++ b/packages/kit/test/prerendering/basics/test/tests.spec.js @@ -158,8 +158,12 @@ test('does not prerender page with shadow endpoint with non-load handler', () => assert.isFalse(fs.existsSync(`${build}/shadowed-post/__data.json`)); }); -test('prerenders page that coexists with a GET server endpoint', () => { - assert.isTrue(fs.existsSync(`${build}/prerender-with-server-endpoint.html`)); +test('prerendering a page that coexists with a GET server endpoint', () => { + assert.isTrue(fs.existsSync(`${build}/duplicate-get.html`)); +}); + +test('prerendering a page that coexists with a POST server endpoint', () => { + assert.isTrue(fs.existsSync(`${build}/get-and-post.html`)); }); test('prerendering a page with a linked GET server endpoint processes properly', () => { From 7e47854ef7e85c018df7e8a5cb385666dde7ccfd Mon Sep 17 00:00:00 2001 From: Aenri Lovehart Date: Sat, 4 Apr 2026 21:23:36 -0400 Subject: [PATCH 04/14] fixed typecheck fail with new field to ServerMetadataRoute.[api,page] --- packages/kit/src/types/internal.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index c64d0e46fde6..1538f4209e58 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -399,9 +399,11 @@ export interface ServerMetadataRoute { config: any; api: { methods: Array; + prerender?: boolean | 'auto'; }; page: { methods: Array<'GET' | 'POST'>; + prerender?: boolean | 'auto'; }; methods: Array; prerender: PrerenderOption | undefined; From bacebc644a77f62d9afceb281b1160ec44521d22 Mon Sep 17 00:00:00 2001 From: Aenri Lovehart Date: Sat, 4 Apr 2026 21:28:30 -0400 Subject: [PATCH 05/14] should pass pnpm lint now, forgot to fix that --- packages/kit/src/core/postbuild/prerender.js | 10 ++++++++-- .../basics/src/routes/get-and-post/+page.svelte | 2 +- .../basics/src/routes/get-and-post/+server.ts | 5 +++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index b9fb35425d07..19b8bbb820aa 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -549,7 +549,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const route_data = metadata.routes.get(id); if (route_data?.page.prerender === true && route_data?.page.methods.includes('GET')) void enqueue(null, config.paths.base + path, undefined, undefined, true); - if (route_data?.api.prerender === true && route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) + if ( + (route_data?.api.prerender === true && route_data?.api.methods.includes('GET')) || + route_data?.api.methods.includes('*') + ) void enqueue(null, config.paths.base + path, undefined, undefined, false); } } @@ -564,7 +567,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { for (const entry of entries) { if (route_data?.page.prerender === true && route_data?.page.methods.includes('GET')) void enqueue(null, config.paths.base + entry, undefined, id, true); - if (route_data?.api.prerender === true && route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) + if ( + route_data?.api.prerender === true && + (route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) + ) void enqueue(null, config.paths.base + entry, undefined, id, false); } } diff --git a/packages/kit/test/prerendering/basics/src/routes/get-and-post/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/get-and-post/+page.svelte index 6b51d324868e..7f4c64a81129 100644 --- a/packages/kit/test/prerendering/basics/src/routes/get-and-post/+page.svelte +++ b/packages/kit/test/prerendering/basics/src/routes/get-and-post/+page.svelte @@ -1 +1 @@ -Hello World... \ No newline at end of file +Hello World... diff --git a/packages/kit/test/prerendering/basics/src/routes/get-and-post/+server.ts b/packages/kit/test/prerendering/basics/src/routes/get-and-post/+server.ts index b4c132caabef..0ac0f6ca06d8 100644 --- a/packages/kit/test/prerendering/basics/src/routes/get-and-post/+server.ts +++ b/packages/kit/test/prerendering/basics/src/routes/get-and-post/+server.ts @@ -1,2 +1,3 @@ - -export async function POST() { return new Response('OK', { status: 200 }) } +export async function POST() { + return new Response('OK', { status: 200 }); +} From f7aeeb8fa703fea3dc63af849736423ccb22959d Mon Sep 17 00:00:00 2001 From: Aenri Lovehart Date: Sat, 4 Apr 2026 22:33:43 -0400 Subject: [PATCH 06/14] fixed regression featuring me accidentally ===ing a value that only needed to be truthy --- packages/kit/src/core/postbuild/prerender.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 19b8bbb820aa..70cc366db741 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -547,11 +547,11 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const path = `/${get_route_segments(processed_id).join('/')}`; const route_data = metadata.routes.get(id); - if (route_data?.page.prerender === true && route_data?.page.methods.includes('GET')) + if (route_data?.page.prerender && route_data?.page.methods.includes('GET')) void enqueue(null, config.paths.base + path, undefined, undefined, true); if ( - (route_data?.api.prerender === true && route_data?.api.methods.includes('GET')) || - route_data?.api.methods.includes('*') + route_data?.api.prerender && + (route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) ) void enqueue(null, config.paths.base + path, undefined, undefined, false); } @@ -565,10 +565,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const route_data = metadata.routes.get(id); for (const entry of entries) { - if (route_data?.page.prerender === true && route_data?.page.methods.includes('GET')) + if (route_data?.page.prerender && route_data?.page.methods.includes('GET')) void enqueue(null, config.paths.base + entry, undefined, id, true); if ( - route_data?.api.prerender === true && + route_data?.api.prerender && (route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) ) void enqueue(null, config.paths.base + entry, undefined, id, false); From f42df3b02012ad312d6340aa24cc96ee28f41c48 Mon Sep 17 00:00:00 2001 From: aenri Date: Thu, 7 May 2026 14:23:12 -0400 Subject: [PATCH 07/14] Update .changeset/brown-eggs-march.md suggested change by sveltekit contributors! Co-authored-by: Tee Ming --- .changeset/brown-eggs-march.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/brown-eggs-march.md b/.changeset/brown-eggs-march.md index 79f946e8e712..3ec42c209029 100644 --- a/.changeset/brown-eggs-march.md +++ b/.changeset/brown-eggs-march.md @@ -2,4 +2,4 @@ '@sveltejs/kit': patch --- -Fixed prerendering pipeline bug found in sveltejs/kit#15620 and sveltejs/kit#10735 +fix: prerender pages that share the same route as a `+server.js` file even if it does not export a `GET` method From 79df286eb1fb445fd0996e46e41e31677ad6bcf3 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 18 May 2026 00:01:51 +0800 Subject: [PATCH 08/14] Apply suggestions from code review Co-authored-by: Tee Ming --- packages/kit/src/types/internal.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 1538f4209e58..6a5542221f9e 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -399,11 +399,11 @@ export interface ServerMetadataRoute { config: any; api: { methods: Array; - prerender?: boolean | 'auto'; + prerender: PrerenderOption | undefined; }; page: { methods: Array<'GET' | 'POST'>; - prerender?: boolean | 'auto'; + prerender: PrerenderOption | undefined; }; methods: Array; prerender: PrerenderOption | undefined; From c0d12bddc367d12b9bc6f93a91dc3d29c8c15f92 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 18 May 2026 00:20:25 +0800 Subject: [PATCH 09/14] Apply suggestions from code review Co-authored-by: Tee Ming --- packages/kit/test/prerendering/basics/test/tests.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/test/prerendering/basics/test/tests.spec.js b/packages/kit/test/prerendering/basics/test/tests.spec.js index e237bff122d1..075bceee3391 100644 --- a/packages/kit/test/prerendering/basics/test/tests.spec.js +++ b/packages/kit/test/prerendering/basics/test/tests.spec.js @@ -158,11 +158,11 @@ test('does not prerender page with shadow endpoint with non-load handler', () => assert.isFalse(fs.existsSync(`${build}/shadowed-post/__data.json`)); }); -test('prerendering a page that coexists with a GET server endpoint', () => { +test('prerenders a page that coexists with a GET endpoint', () => { assert.isTrue(fs.existsSync(`${build}/duplicate-get.html`)); }); -test('prerendering a page that coexists with a POST server endpoint', () => { +test('prerenders a page that coexists with a POST endpoint', () => { assert.isTrue(fs.existsSync(`${build}/get-and-post.html`)); }); From 02257d0a2c28d1ec25befd5113927473703ef50c Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 18 May 2026 00:31:26 +0800 Subject: [PATCH 10/14] Apply suggestions from code review Co-authored-by: Tee Ming --- packages/kit/src/core/postbuild/prerender.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 70cc366db741..8a0d3532e02b 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -242,13 +242,13 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { return; } - const requestHeaders = expect_html ? { Accept: 'text/html' } : undefined; + const request_headers = expect_html ? { Accept: 'text/html' } : undefined; /** @type {Map} */ const dependencies = new Map(); const response = await server.respond( - new Request(config.prerender.origin + encoded, { headers: requestHeaders }), + new Request(config.prerender.origin + encoded, { headers: request_headers }), { getClientAddress() { throw new Error('Cannot read clientAddress during prerendering'); From ac842d5af893bdc51db48002ceccf00efe09d7aa Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 18 May 2026 01:18:46 +0800 Subject: [PATCH 11/14] simplify --- packages/kit/src/core/postbuild/prerender.js | 25 ++++++++------------ 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 8a0d3532e02b..47d7839a49fb 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -1,3 +1,4 @@ +/** @import { ServerMetadataRoute } from 'types' */ import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { pathToFileURL } from 'node:url'; @@ -547,13 +548,13 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const path = `/${get_route_segments(processed_id).join('/')}`; const route_data = metadata.routes.get(id); - if (route_data?.page.prerender && route_data?.page.methods.includes('GET')) - void enqueue(null, config.paths.base + path, undefined, undefined, true); - if ( - route_data?.api.prerender && - (route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) - ) - void enqueue(null, config.paths.base + path, undefined, undefined, false); + void enqueue( + null, + config.paths.base + path, + undefined, + undefined, + !!route_data?.page.prerender + ); } } } else { @@ -563,15 +564,9 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { for (const { id, entries } of route_level_entries) { const route_data = metadata.routes.get(id); - + const expect_html = !!route_data?.page.prerender; for (const entry of entries) { - if (route_data?.page.prerender && route_data?.page.methods.includes('GET')) - void enqueue(null, config.paths.base + entry, undefined, id, true); - if ( - route_data?.api.prerender && - (route_data?.api.methods.includes('GET') || route_data?.api.methods.includes('*')) - ) - void enqueue(null, config.paths.base + entry, undefined, id, false); + void enqueue(null, config.paths.base + entry, undefined, undefined, expect_html); } } From 4caad8b1f817743cf0b47988588de7914f8eb171 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 18 May 2026 01:23:54 +0800 Subject: [PATCH 12/14] unused type --- packages/kit/src/core/postbuild/prerender.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 47d7839a49fb..f32880fb2159 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -1,4 +1,3 @@ -/** @import { ServerMetadataRoute } from 'types' */ import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { pathToFileURL } from 'node:url'; From 10e2cc562601c08aa0095548ba204c67b85873a0 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 18 May 2026 01:31:19 +0800 Subject: [PATCH 13/14] whoops --- packages/kit/src/core/postbuild/prerender.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index f32880fb2159..65160d3700f5 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -565,7 +565,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const route_data = metadata.routes.get(id); const expect_html = !!route_data?.page.prerender; for (const entry of entries) { - void enqueue(null, config.paths.base + entry, undefined, undefined, expect_html); + void enqueue(null, config.paths.base + entry, undefined, id, expect_html); } } From 116467fa738d50741f86a996958465e3185c5b99 Mon Sep 17 00:00:00 2001 From: aenri Date: Tue, 26 May 2026 11:36:20 +0000 Subject: [PATCH 14/14] tweak: expect_html -> false when crawling Co-authored-by: Tee Ming --- packages/kit/src/core/postbuild/prerender.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 65160d3700f5..f902a1705ad8 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -366,7 +366,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { /** @type {Set} */ (expected_hashlinks.get(key)).add(decoded); } - void enqueue(decoded, decode_uri(pathname), pathname, undefined, true); + void enqueue(decoded, decode_uri(pathname), pathname, undefined, false); } } }