From 2c78e2897dafe1b91c0e6c6a70f86784dc133b21 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 3 May 2022 21:10:14 -0600 Subject: [PATCH 1/7] feat: Failing test --- .../basics/src/routes/prerendering/__error.svelte | 15 +++++++++++++++ .../routes/prerendering/mutative-endpoint.svelte | 5 +++++ .../src/routes/prerendering/mutative-endpoint.ts | 3 +++ packages/kit/test/apps/basics/test/test.js | 9 +++++++++ 4 files changed, 32 insertions(+) create mode 100644 packages/kit/test/apps/basics/src/routes/prerendering/__error.svelte create mode 100644 packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.svelte create mode 100644 packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.ts diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/__error.svelte b/packages/kit/test/apps/basics/src/routes/prerendering/__error.svelte new file mode 100644 index 000000000000..50113b8d0cd4 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/prerendering/__error.svelte @@ -0,0 +1,15 @@ + + + + +

{title}

diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.svelte b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.svelte new file mode 100644 index 000000000000..17e79b8eb97b --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.svelte @@ -0,0 +1,5 @@ + + +

I have a mutative endpoint!

diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.ts b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.ts new file mode 100644 index 000000000000..a2acc131372c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/prerendering/mutative-endpoint.ts @@ -0,0 +1,3 @@ +export const post = () => { + return {}; +}; diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index e9b0e2c71b9b..4ac2550bb65f 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1074,6 +1074,15 @@ test.describe.parallel('Errors', () => { expect(response.status()).toBe(500); expect(await response.text()).toMatch('thisvariableisnotdefined is not defined'); }); + + test('prerendering a page with a mutative page endpoint results in a catchable error', async ({ + page + }) => { + await page.goto('/prerendering/mutative-endpoint'); + expect(await page.textContent('h1')).toBe( + '500: Cannot prerender pages that have endpoints with mutative methods' + ); + }); }); test.describe.parallel('ETags', () => { From 4064494c5673052d9d7459dba4d03c009fca9a7a Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 3 May 2022 21:11:16 -0600 Subject: [PATCH 2/7] feat: Prerendered pages with mutative endpoints fail at runtime --- packages/kit/src/runtime/server/page/load_node.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index aa6efacad20f..522278549aa2 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -58,7 +58,7 @@ export async function load_node({ /** @type {import('types').SSRPage} */ (route), event, options, - !!state.prerender + node.module.prerender ?? state?.prerender?.default ?? false ) : {}; From 19b1ba2bc7f7c88c2cd4d03d3dc7db8cf61f1347 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 3 May 2022 21:19:29 -0600 Subject: [PATCH 3/7] feat: Changeset --- .changeset/brave-jobs-occur.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/brave-jobs-occur.md diff --git a/.changeset/brave-jobs-occur.md b/.changeset/brave-jobs-occur.md new file mode 100644 index 000000000000..6aef9677f283 --- /dev/null +++ b/.changeset/brave-jobs-occur.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +feat: Pages marked for prerendering fail during ssr at runtime From d5880ffe63e52e73e4e199a18d9f62a99375e575 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 3 May 2022 21:32:10 -0600 Subject: [PATCH 4/7] fix: clarify with variable --- packages/kit/src/runtime/server/page/load_node.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 522278549aa2..8756c3b8ff8f 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -52,13 +52,15 @@ export async function load_node({ /** @type {import('types').LoadOutput} */ let loaded; + const should_prerender = node.module.prerender ?? state?.prerender?.default ?? false; + /** @type {import('types').ShadowData} */ const shadow = is_leaf ? await load_shadow_data( /** @type {import('types').SSRPage} */ (route), event, options, - node.module.prerender ?? state?.prerender?.default ?? false + should_prerender ) : {}; From f7fad6a7ef9348bf3da7866bd917ecc4dd2daead Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 18 May 2022 17:32:35 -0400 Subject: [PATCH 5/7] determine prerenderability based on config, not state --- packages/kit/src/core/build/build_server.js | 5 ++++- packages/kit/src/core/dev/plugin.js | 5 ++++- packages/kit/src/runtime/server/page/load_node.js | 2 +- packages/kit/types/internal.d.ts | 5 ++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/core/build/build_server.js b/packages/kit/src/core/build/build_server.js index 6181eec6241d..9b387524d6b7 100644 --- a/packages/kit/src/core/build/build_server.js +++ b/packages/kit/src/core/build/build_server.js @@ -71,7 +71,10 @@ export class Server { method_override: ${s(config.kit.methodOverride)}, paths: { base, assets }, prefix: assets + '/${config.kit.appDir}/', - prerender: ${config.kit.prerender.enabled}, + prerender: { + default: ${config.kit.prerender.default}, + enabled: ${config.kit.prerender.enabled} + }, read, root, service_worker: ${has_service_worker ? "base + '/service-worker.js'" : 'null'}, diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js index a5269fa77bab..4712edd95881 100644 --- a/packages/kit/src/core/dev/plugin.js +++ b/packages/kit/src/core/dev/plugin.js @@ -320,7 +320,10 @@ export async function create_plugin(config, cwd) { assets }, prefix: '', - prerender: config.kit.prerender.enabled, + prerender: { + default: config.kit.prerender.default, + enabled: config.kit.prerender.enabled + }, read: (file) => fs.readFileSync(path.join(config.kit.files.assets, file)), root, router: config.kit.browser.router, diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 8756c3b8ff8f..9b4252ccb531 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -52,7 +52,7 @@ export async function load_node({ /** @type {import('types').LoadOutput} */ let loaded; - const should_prerender = node.module.prerender ?? state?.prerender?.default ?? false; + const should_prerender = node.module.prerender ?? options.prerender.default; /** @type {import('types').ShadowData} */ const shadow = is_leaf diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 82f32402ec7c..37dca8b511fa 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -255,7 +255,10 @@ export interface SSROptions { assets: string; }; prefix: string; - prerender: boolean; + prerender: { + default: boolean; + enabled: boolean; + }; read(file: string): Buffer; root: SSRComponent['default']; router: boolean; From cf5503dbcfdfd25742f0bc95f1ab9396df4577f6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 18 May 2022 17:55:27 -0400 Subject: [PATCH 6/7] rename state.prerender to state.prerendering, remove state.prerendering.default --- packages/kit/src/core/build/prerender/prerender.js | 2 -- packages/kit/src/runtime/server/index.js | 10 +++++----- packages/kit/src/runtime/server/page/load_node.js | 10 +++++----- packages/kit/src/runtime/server/page/render.js | 10 +++++----- packages/kit/src/runtime/server/page/respond.js | 4 ++-- packages/kit/types/internal.d.ts | 3 +-- 6 files changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/kit/src/core/build/prerender/prerender.js b/packages/kit/src/core/build/prerender/prerender.js index 8b231689d9f4..a414351f4b23 100644 --- a/packages/kit/src/core/build/prerender/prerender.js +++ b/packages/kit/src/core/build/prerender/prerender.js @@ -132,7 +132,6 @@ export async function prerender({ config, entries, files, log }) { const response = await server.respond(new Request(`http://sveltekit-prerender${encoded}`), { getClientAddress, prerender: { - default: config.kit.prerender.default, dependencies } }); @@ -274,7 +273,6 @@ export async function prerender({ config, entries, files, log }) { getClientAddress, prerender: { fallback: true, - default: false, dependencies: new Map() } }); diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 3b86da85bf4f..d9296f8eacae 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -49,7 +49,7 @@ export async function respond(request, options, state) { /** @type {Record} */ let params = {}; - if (options.paths.base && !state.prerender?.fallback) { + if (options.paths.base && !state.prerendering?.fallback) { if (!decoded.startsWith(options.paths.base)) { return new Response(undefined, { status: 404 }); } @@ -63,7 +63,7 @@ export async function respond(request, options, state) { url = new URL(url.origin + url.pathname.slice(0, -DATA_SUFFIX.length) + url.search); } - if (!state.prerender || !state.prerender.fallback) { + if (!state.prerendering?.fallback) { const matchers = await options.manifest._.matchers(); for (const candidate of options.manifest._.routes) { @@ -82,7 +82,7 @@ export async function respond(request, options, state) { if (route?.type === 'page') { const normalized = normalize_path(url.pathname, options.trailing_slash); - if (normalized !== url.pathname && !state.prerender?.fallback) { + if (normalized !== url.pathname && !state.prerendering?.fallback) { return new Response(undefined, { status: 301, headers: { @@ -173,7 +173,7 @@ export async function respond(request, options, state) { }; } - if (state.prerender && state.prerender.fallback) { + if (state.prerendering?.fallback) { return await render_response({ event, options, @@ -275,7 +275,7 @@ export async function respond(request, options, state) { }); } - if (state.prerender) { + if (state.prerendering) { return new Response('not found', { status: 404 }); } diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 9b4252ccb531..bc96fc9e4290 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -83,7 +83,7 @@ export async function load_node({ } else if (module.load) { /** @type {import('types').LoadInput} */ const load_input = { - url: state.prerender ? create_prerendering_url_proxy(event.url) : event.url, + url: state.prerendering ? create_prerendering_url_proxy(event.url) : event.url, params: event.params, props: shadow.body || {}, routeId: event.routeId, @@ -219,9 +219,9 @@ export async function load_node({ } ); - if (state.prerender) { + if (state.prerendering) { dependency = { response, body: null }; - state.prerender.dependencies.set(resolved, dependency); + state.prerendering.dependencies.set(resolved, dependency); } } else { // external @@ -366,7 +366,7 @@ export async function load_node({ } // generate __data.json files when prerendering - if (shadow.body && state.prerender) { + if (shadow.body && state.prerendering) { const pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`; const dependency = { @@ -374,7 +374,7 @@ export async function load_node({ body: JSON.stringify(shadow.body) }; - state.prerender.dependencies.set(pathname, dependency); + state.prerendering.dependencies.set(pathname, dependency); } return { diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 34f0f9a9167c..7982dc8ca013 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -40,7 +40,7 @@ export async function render_response({ resolve_opts, stuff }) { - if (state.prerender) { + if (state.prerendering) { if (options.csp.mode === 'nonce') { throw new Error('Cannot use prerendering if config.kit.csp.mode === "nonce"'); } @@ -108,7 +108,7 @@ export async function render_response({ routeId: event.routeId, status, stuff, - url: state.prerender ? create_prerendering_url_proxy(event.url) : event.url + url: state.prerendering ? create_prerendering_url_proxy(event.url) : event.url }, components: branch.map(({ node }) => node.module.default) }; @@ -148,7 +148,7 @@ export async function render_response({ await csp_ready; const csp = new Csp(options.csp, { dev: options.dev, - prerender: !!state.prerender, + prerender: !!state.prerendering, needs_nonce: options.template_contains_nonce }); @@ -276,7 +276,7 @@ export async function render_response({ } } - if (state.prerender && !options.amp) { + if (state.prerendering && !options.amp) { const http_equiv = []; const csp_headers = csp.get_meta(); @@ -314,7 +314,7 @@ export async function render_response({ headers.set('permissions-policy', 'interest-cohort=()'); } - if (!state.prerender) { + if (!state.prerendering) { const csp_header = csp.get_header(); if (csp_header) { headers.set('content-security-policy', csp_header); diff --git a/packages/kit/src/runtime/server/page/respond.js b/packages/kit/src/runtime/server/page/respond.js index cfb7e15d3068..2d94c8e340b2 100644 --- a/packages/kit/src/runtime/server/page/respond.js +++ b/packages/kit/src/runtime/server/page/respond.js @@ -68,11 +68,11 @@ export async function respond(opts) { let page_config = get_page_config(leaf, options); - if (state.prerender) { + if (state.prerendering) { // if the page isn't marked as prerenderable (or is explicitly // marked NOT prerenderable, if `prerender.default` is `true`), // then bail out at this point - const should_prerender = leaf.prerender ?? state.prerender.default; + const should_prerender = leaf.prerender ?? options.prerender.default; if (!should_prerender) { return new Response(undefined, { status: 204 diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 37dca8b511fa..19b47430ed77 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -147,7 +147,6 @@ export interface PrerenderDependency { export interface PrerenderOptions { fallback?: boolean; - default: boolean; dependencies: Map; } @@ -312,7 +311,7 @@ export interface SSRState { getClientAddress: () => string; initiator?: SSRPage | null; platform?: any; - prerender?: PrerenderOptions; + prerendering?: PrerenderOptions; } export type StrictBody = string | Uint8Array; From b9aca900fcafeecdf1d5de37f558b24975a5ca01 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 23 May 2022 14:20:41 -0400 Subject: [PATCH 7/7] fix type --- packages/kit/src/core/build/prerender/prerender.js | 4 ++-- packages/kit/types/internal.d.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/core/build/prerender/prerender.js b/packages/kit/src/core/build/prerender/prerender.js index a414351f4b23..070180ece180 100644 --- a/packages/kit/src/core/build/prerender/prerender.js +++ b/packages/kit/src/core/build/prerender/prerender.js @@ -131,7 +131,7 @@ export async function prerender({ config, entries, files, log }) { const response = await server.respond(new Request(`http://sveltekit-prerender${encoded}`), { getClientAddress, - prerender: { + prerendering: { dependencies } }); @@ -271,7 +271,7 @@ export async function prerender({ config, entries, files, log }) { const rendered = await server.respond(new Request('http://sveltekit-prerender/[fallback]'), { getClientAddress, - prerender: { + prerendering: { fallback: true, dependencies: new Map() } diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 91ce772ffaa3..ae38f18d052a 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -94,7 +94,7 @@ export class InternalServer extends Server { respond( request: Request, options: RequestOptions & { - prerender?: PrerenderOptions; + prerendering?: PrerenderOptions; } ): Promise; }