From 7ce553c784e2d414c869f93139c7abb92b92d590 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Mon, 20 Feb 2023 23:53:02 -0700 Subject: [PATCH 01/14] feat: Validate Vercel cron paths --- packages/adapter-vercel/index.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index e9b4909a5b71..be69c8ff15cc 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -39,6 +39,32 @@ const plugin = function (defaults = {}) { builder.rimraf(dir); builder.rimraf(tmp); + if (fs.existsSync('./vercel.json')) { + const vercelFile = fs.readFileSync('./vercel.json', 'utf-8'); + const vercelConfig = JSON.parse(vercelFile); + const crons = /** @type {Array} */ ( + Array.isArray(vercelConfig?.crons) ? vercelConfig.crons : [] + ); + crons.forEach((cron) => { + if (!(typeof cron === 'object' && cron !== null && 'path' in cron)) { + return; + } + + const { path } = cron; + if (typeof path !== 'string') { + return; + } + + if (!builder.routes.some((route) => route.pattern.test(path))) { + builder.log.warn( + `Cron job path ${path} does not match any routes. If this path is handled in your \`handle\` hook, you can ignore this warning.` + ); + } + }); + } + + builder.routes.some((route) => route.pattern.test()); + const files = fileURLToPath(new URL('./files', import.meta.url).href); const dirs = { From 4d0b4fd2c2806d5b8898c5c69ec6c4001d1bbccb Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 21 Feb 2023 00:03:19 -0700 Subject: [PATCH 02/14] fix: Don't make Rich mad with bad variable names, changeset --- .changeset/tiny-deers-complain.md | 5 +++++ packages/adapter-vercel/index.js | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 .changeset/tiny-deers-complain.md diff --git a/.changeset/tiny-deers-complain.md b/.changeset/tiny-deers-complain.md new file mode 100644 index 000000000000..c61d1dae3422 --- /dev/null +++ b/.changeset/tiny-deers-complain.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-vercel': minor +--- + +feat: Validate that Vercel cron paths match an API path diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index be69c8ff15cc..067f390ce0f0 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -40,11 +40,13 @@ const plugin = function (defaults = {}) { builder.rimraf(tmp); if (fs.existsSync('./vercel.json')) { - const vercelFile = fs.readFileSync('./vercel.json', 'utf-8'); - const vercelConfig = JSON.parse(vercelFile); + const vercel_file = fs.readFileSync('./vercel.json', 'utf-8'); + const vercel_config = JSON.parse(vercel_file); const crons = /** @type {Array} */ ( - Array.isArray(vercelConfig?.crons) ? vercelConfig.crons : [] + Array.isArray(vercel_config?.crons) ? vercel_config.crons : [] ); + const GET_routes = builder.routes.filter((route) => route.methods.includes('GET')); + crons.forEach((cron) => { if (!(typeof cron === 'object' && cron !== null && 'path' in cron)) { return; @@ -55,7 +57,7 @@ const plugin = function (defaults = {}) { return; } - if (!builder.routes.some((route) => route.pattern.test(path))) { + if (!GET_routes.some((route) => route.pattern.test(path))) { builder.log.warn( `Cron job path ${path} does not match any routes. If this path is handled in your \`handle\` hook, you can ignore this warning.` ); @@ -63,8 +65,6 @@ const plugin = function (defaults = {}) { }); } - builder.routes.some((route) => route.pattern.test()); - const files = fileURLToPath(new URL('./files', import.meta.url).href); const dirs = { From 6aa3e2977dc25070d67966c1aa223f08ed131350 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Feb 2023 11:20:09 -0500 Subject: [PATCH 03/14] Update .changeset/tiny-deers-complain.md Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- .changeset/tiny-deers-complain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/tiny-deers-complain.md b/.changeset/tiny-deers-complain.md index c61d1dae3422..6da8db783e64 100644 --- a/.changeset/tiny-deers-complain.md +++ b/.changeset/tiny-deers-complain.md @@ -2,4 +2,4 @@ '@sveltejs/adapter-vercel': minor --- -feat: Validate that Vercel cron paths match an API path +feat: validate that Vercel cron paths match an API path From 69ea8dc6ec72a801f43f2d0d7d3ad7bb35615130 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 21 Feb 2023 13:51:13 -0700 Subject: [PATCH 04/14] fix: Better warning message + semantics --- packages/adapter-vercel/index.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 067f390ce0f0..10d684dc0533 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -46,23 +46,29 @@ const plugin = function (defaults = {}) { Array.isArray(vercel_config?.crons) ? vercel_config.crons : [] ); const GET_routes = builder.routes.filter((route) => route.methods.includes('GET')); + /** @type {Array} */ + const unmatched_paths = []; - crons.forEach((cron) => { - if (!(typeof cron === 'object' && cron !== null && 'path' in cron)) { - return; + for (const cron of crons) { + if (typeof cron !== 'object' || cron === null || !('path' in cron)) { + continue; } const { path } = cron; if (typeof path !== 'string') { - return; + continue; } if (!GET_routes.some((route) => route.pattern.test(path))) { - builder.log.warn( - `Cron job path ${path} does not match any routes. If this path is handled in your \`handle\` hook, you can ignore this warning.` - ); + unmatched_paths.push(path); } - }); + } + + builder.log.warn( + `The following paths are not matched by any route:\n - ${unmatched_paths.join( + '\n - ' + )}\nIf these paths are handled in your \`handle\` hook, you can safely ignore this warning.` + ); } const files = fileURLToPath(new URL('./files', import.meta.url).href); From d712205ddac8fb937f63c94a5d8e071912aeed0c Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 21 Feb 2023 14:52:09 -0700 Subject: [PATCH 05/14] feat: Add type differentiator for endpoints / pages --- packages/adapter-vercel/index.js | 4 +++- packages/kit/src/core/adapt/builder.js | 1 + packages/kit/types/index.d.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 10d684dc0533..ea6cf09e32ba 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -45,7 +45,9 @@ const plugin = function (defaults = {}) { const crons = /** @type {Array} */ ( Array.isArray(vercel_config?.crons) ? vercel_config.crons : [] ); - const GET_routes = builder.routes.filter((route) => route.methods.includes('GET')); + const GET_routes = builder.routes.filter( + (route) => route.type === 'endpoint' && route.methods.includes('GET') + ); /** @type {Array} */ const unmatched_paths = []; diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 499fa7c4230f..c5532bd9bb56 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -51,6 +51,7 @@ export function create_builder({ /** @type {import('types').RouteDefinition} */ const facade = { id: route.id, + type: route.endpoint ? 'endpoint' : 'page', segments: get_route_segments(route.id).map((segment) => ({ dynamic: segment.includes('['), rest: segment.includes('[...'), diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 73c5683ca8fe..b1688f8942f6 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -983,6 +983,7 @@ export interface ResolveOptions { export interface RouteDefinition { id: string; + type: 'endpoint' | 'page'; pattern: RegExp; prerender: PrerenderOption; segments: RouteSegment[]; From 633e49f6e1fe7365809aa77ae69b5db98f6fc0ab Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Tue, 21 Feb 2023 16:00:47 -0700 Subject: [PATCH 06/14] fix: Better differentiators --- packages/adapter-vercel/index.js | 3 ++- packages/kit/src/core/adapt/builder.js | 3 ++- packages/kit/types/index.d.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index ea6cf09e32ba..dd57a9de5aa8 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -45,8 +45,9 @@ const plugin = function (defaults = {}) { const crons = /** @type {Array} */ ( Array.isArray(vercel_config?.crons) ? vercel_config.crons : [] ); + // This will false-positive on pages that have endpoints other than `GET` const GET_routes = builder.routes.filter( - (route) => route.type === 'endpoint' && route.methods.includes('GET') + (route) => route.hasEndpoint && route.methods.includes('GET') ); /** @type {Array} */ const unmatched_paths = []; diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index c5532bd9bb56..356e09c09f88 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -51,7 +51,8 @@ export function create_builder({ /** @type {import('types').RouteDefinition} */ const facade = { id: route.id, - type: route.endpoint ? 'endpoint' : 'page', + hasEndpoint: !!route.endpoint, + hasPage: !!route.page, segments: get_route_segments(route.id).map((segment) => ({ dynamic: segment.includes('['), rest: segment.includes('[...'), diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index b1688f8942f6..0524e9dedbee 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -983,7 +983,8 @@ export interface ResolveOptions { export interface RouteDefinition { id: string; - type: 'endpoint' | 'page'; + hasEndpoint: boolean; + hasPage: boolean; pattern: RegExp; prerender: PrerenderOption; segments: RouteSegment[]; From 38512089e52e6dfd4feb4dcf97dbc73246523e62 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Wed, 22 Feb 2023 21:35:39 -0700 Subject: [PATCH 07/14] feat: Add endpoint and page methods to adapter contract --- packages/adapter-vercel/index.js | 7 +++--- packages/kit/src/core/adapt/builder.js | 17 ++++++++++---- packages/kit/src/core/postbuild/analyse.js | 27 ++++++++++++++-------- packages/kit/types/index.d.ts | 8 +++++-- packages/kit/types/internal.d.ts | 2 ++ 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index dd57a9de5aa8..7d246470b48b 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -45,9 +45,8 @@ const plugin = function (defaults = {}) { const crons = /** @type {Array} */ ( Array.isArray(vercel_config?.crons) ? vercel_config.crons : [] ); - // This will false-positive on pages that have endpoints other than `GET` - const GET_routes = builder.routes.filter( - (route) => route.hasEndpoint && route.methods.includes('GET') + const GET_endpoints = builder.routes.filter((route) => + route?.endpoint?.methods?.includes('GET') ); /** @type {Array} */ const unmatched_paths = []; @@ -62,7 +61,7 @@ const plugin = function (defaults = {}) { continue; } - if (!GET_routes.some((route) => route.pattern.test(path))) { + if (!GET_endpoints.some((route) => route.pattern.test(path))) { unmatched_paths.push(path); } } diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 356e09c09f88..73b3214b1915 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -43,16 +43,23 @@ export function create_builder({ * we expose a stable type that adapters can use to group/filter routes */ const routes = route_data.map((route) => { - const methods = - /** @type {import('types').HttpMethod[]} */ - (server_metadata.routes.get(route.id)?.methods); + const { methods, pageMethods, endpointMethods } = + /** @type {NonNullable>} */ ( + server_metadata.routes.get(route.id) + ); const config = server_metadata.routes.get(route.id)?.config; + server_metadata.routes.get(route.id)?.methods; + /** @type {import('types').RouteDefinition} */ const facade = { id: route.id, - hasEndpoint: !!route.endpoint, - hasPage: !!route.page, + endpoint: { + methods: endpointMethods + }, + page: { + methods: pageMethods + }, segments: get_route_segments(route.id).map((segment) => ({ dynamic: segment.includes('['), rest: segment.includes('[...'), diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index 1588c4a4a8b3..2a32803c8d71 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -62,8 +62,10 @@ async function analyse({ manifest_path, env }) { // analyse routes for (const route of manifest._.routes) { + /** @type {Set>} */ + const pageMethodsSet = new Set(); /** @type {Set} */ - const methods = new Set(); + const endpointMethodsSet = new Set(); /** @type {import('types').PrerenderOption | undefined} */ let prerender = undefined; @@ -84,12 +86,12 @@ async function analyse({ manifest_path, env }) { prerender = mod.prerender; } - if (mod.GET) methods.add('GET'); - if (mod.POST) methods.add('POST'); - if (mod.PUT) methods.add('PUT'); - if (mod.PATCH) methods.add('PATCH'); - if (mod.DELETE) methods.add('DELETE'); - if (mod.OPTIONS) methods.add('OPTIONS'); + if (mod.GET) endpointMethodsSet.add('GET'); + if (mod.POST) endpointMethodsSet.add('POST'); + if (mod.PUT) endpointMethodsSet.add('PUT'); + if (mod.PATCH) endpointMethodsSet.add('PATCH'); + if (mod.DELETE) endpointMethodsSet.add('DELETE'); + if (mod.OPTIONS) endpointMethodsSet.add('OPTIONS'); config = mod.config; } @@ -112,8 +114,8 @@ async function analyse({ manifest_path, env }) { } if (page) { - methods.add('GET'); - if (page.server?.actions) methods.add('POST'); + pageMethodsSet.add('GET'); + if (page.server?.actions) pageMethodsSet.add('POST'); validate_page_server_exports(page.server, page.server_id); validate_common_exports(page.universal, page.universal_id); @@ -132,10 +134,15 @@ async function analyse({ manifest_path, env }) { config = get_config(nodes); } + const pageMethods = Array.from(pageMethodsSet); + const endpointMethods = Array.from(endpointMethodsSet); + const methods = [...pageMethods, ...endpointMethods]; metadata.routes.set(route.id, { prerender, config, - methods: Array.from(methods) + pageMethods, + endpointMethods, + methods }); } diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 0524e9dedbee..d2f4a6ac9294 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -983,8 +983,12 @@ export interface ResolveOptions { export interface RouteDefinition { id: string; - hasEndpoint: boolean; - hasPage: boolean; + endpoint: { + methods: HttpMethod[]; + }; + page: { + methods: Extract[]; + }; pattern: RegExp; prerender: PrerenderOption; segments: RouteSegment[]; diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index bad1376c09c2..cf73de0f4829 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -269,6 +269,8 @@ export interface ServerMetadata { string, { prerender: PrerenderOption | undefined; + endpointMethods: HttpMethod[]; + pageMethods: Extract[]; methods: HttpMethod[]; config: any; } From 07b650f6ed2ac6601c9e60c9254cf567b8322952 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Feb 2023 22:46:39 -0500 Subject: [PATCH 08/14] create ServerMetadataRoute interface --- packages/kit/src/core/adapt/builder.js | 4 +--- packages/kit/types/internal.d.ts | 19 +++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 73b3214b1915..4a9a8b157fd8 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -44,9 +44,7 @@ export function create_builder({ */ const routes = route_data.map((route) => { const { methods, pageMethods, endpointMethods } = - /** @type {NonNullable>} */ ( - server_metadata.routes.get(route.id) - ); + /** @type {import('types').ServerMetadataRoute} */ (server_metadata.routes.get(route.id)); const config = server_metadata.routes.get(route.id)?.config; server_metadata.routes.get(route.id)?.methods; diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index cf73de0f4829..95d46fb78bef 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -263,18 +263,17 @@ export interface ServerErrorNode { status?: number; } +export interface ServerMetadataRoute { + prerender: PrerenderOption | undefined; + endpointMethods: HttpMethod[]; + pageMethods: Extract[]; + methods: HttpMethod[]; + config: any; +} + export interface ServerMetadata { nodes: Array<{ has_server_load: boolean }>; - routes: Map< - string, - { - prerender: PrerenderOption | undefined; - endpointMethods: HttpMethod[]; - pageMethods: Extract[]; - methods: HttpMethod[]; - config: any; - } - >; + routes: Map; } export interface SSRComponent { From 764e948516b2221b7b00e31ecd8bddc375d7a02d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Feb 2023 22:50:34 -0500 Subject: [PATCH 09/14] simplify --- packages/kit/src/core/adapt/builder.js | 16 +++++----------- packages/kit/src/core/postbuild/analyse.js | 12 ++++++++---- packages/kit/types/internal.d.ts | 12 ++++++++---- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 4a9a8b157fd8..94fc219dde55 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -43,21 +43,15 @@ export function create_builder({ * we expose a stable type that adapters can use to group/filter routes */ const routes = route_data.map((route) => { - const { methods, pageMethods, endpointMethods } = - /** @type {import('types').ServerMetadataRoute} */ (server_metadata.routes.get(route.id)); - const config = server_metadata.routes.get(route.id)?.config; - - server_metadata.routes.get(route.id)?.methods; + const { config, methods, page, endpoint } = /** @type {import('types').ServerMetadataRoute} */ ( + server_metadata.routes.get(route.id) + ); /** @type {import('types').RouteDefinition} */ const facade = { id: route.id, - endpoint: { - methods: endpointMethods - }, - page: { - methods: pageMethods - }, + endpoint, + page, segments: get_route_segments(route.id).map((segment) => ({ dynamic: segment.includes('['), rest: segment.includes('[...'), diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index 2a32803c8d71..d22bd74f35e1 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -138,11 +138,15 @@ async function analyse({ manifest_path, env }) { const endpointMethods = Array.from(endpointMethodsSet); const methods = [...pageMethods, ...endpointMethods]; metadata.routes.set(route.id, { - prerender, config, - pageMethods, - endpointMethods, - methods + methods, + page: { + methods: Array.from(pageMethodsSet) + }, + endpoint: { + methods: Array.from(endpointMethodsSet) + }, + prerender }); } diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 95d46fb78bef..df063c93a867 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -264,11 +264,15 @@ export interface ServerErrorNode { } export interface ServerMetadataRoute { - prerender: PrerenderOption | undefined; - endpointMethods: HttpMethod[]; - pageMethods: Extract[]; - methods: HttpMethod[]; config: any; + endpoint: { + methods: HttpMethod[]; + }; + page: { + methods: Array<'GET' | 'POST'>; + }; + methods: HttpMethod[]; + prerender: PrerenderOption | undefined; } export interface ServerMetadata { From 798ac47b7d252896e630fd06a3b6d00ef6a18ca0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Feb 2023 22:52:43 -0500 Subject: [PATCH 10/14] simplify --- packages/kit/src/core/postbuild/analyse.js | 34 ++++++++++------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index d22bd74f35e1..a041fe6124e1 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -62,10 +62,11 @@ async function analyse({ manifest_path, env }) { // analyse routes for (const route of manifest._.routes) { - /** @type {Set>} */ - const pageMethodsSet = new Set(); - /** @type {Set} */ - const endpointMethodsSet = new Set(); + /** @type {Array<'GET' | 'POST'>} */ + const page_methods = []; + + /** @type {import('types').HttpMethod[]} */ + const endpoint_methods = []; /** @type {import('types').PrerenderOption | undefined} */ let prerender = undefined; @@ -86,12 +87,12 @@ async function analyse({ manifest_path, env }) { prerender = mod.prerender; } - if (mod.GET) endpointMethodsSet.add('GET'); - if (mod.POST) endpointMethodsSet.add('POST'); - if (mod.PUT) endpointMethodsSet.add('PUT'); - if (mod.PATCH) endpointMethodsSet.add('PATCH'); - if (mod.DELETE) endpointMethodsSet.add('DELETE'); - if (mod.OPTIONS) endpointMethodsSet.add('OPTIONS'); + if (mod.GET) endpoint_methods.push('GET'); + if (mod.POST) endpoint_methods.push('POST'); + if (mod.PUT) endpoint_methods.push('PUT'); + if (mod.PATCH) endpoint_methods.push('PATCH'); + if (mod.DELETE) endpoint_methods.push('DELETE'); + if (mod.OPTIONS) endpoint_methods.push('OPTIONS'); config = mod.config; } @@ -114,8 +115,8 @@ async function analyse({ manifest_path, env }) { } if (page) { - pageMethodsSet.add('GET'); - if (page.server?.actions) pageMethodsSet.add('POST'); + page_methods.push('GET'); + if (page.server?.actions) page_methods.push('POST'); validate_page_server_exports(page.server, page.server_id); validate_common_exports(page.universal, page.universal_id); @@ -134,17 +135,14 @@ async function analyse({ manifest_path, env }) { config = get_config(nodes); } - const pageMethods = Array.from(pageMethodsSet); - const endpointMethods = Array.from(endpointMethodsSet); - const methods = [...pageMethods, ...endpointMethods]; metadata.routes.set(route.id, { config, - methods, + methods: Array.from(new Set([...page_methods, ...endpoint_methods])), page: { - methods: Array.from(pageMethodsSet) + methods: page_methods }, endpoint: { - methods: Array.from(endpointMethodsSet) + methods: endpoint_methods }, prerender }); From 15bf1389336ae3dd47970e159ee9299c324448fc Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Feb 2023 23:05:31 -0500 Subject: [PATCH 11/14] move validation into separate function --- packages/adapter-vercel/index.js | 83 ++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 7d246470b48b..a2bd9f6c4edf 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -39,38 +39,10 @@ const plugin = function (defaults = {}) { builder.rimraf(dir); builder.rimraf(tmp); - if (fs.existsSync('./vercel.json')) { - const vercel_file = fs.readFileSync('./vercel.json', 'utf-8'); + if (fs.existsSync('vercel.json')) { + const vercel_file = fs.readFileSync('vercel.json', 'utf-8'); const vercel_config = JSON.parse(vercel_file); - const crons = /** @type {Array} */ ( - Array.isArray(vercel_config?.crons) ? vercel_config.crons : [] - ); - const GET_endpoints = builder.routes.filter((route) => - route?.endpoint?.methods?.includes('GET') - ); - /** @type {Array} */ - const unmatched_paths = []; - - for (const cron of crons) { - if (typeof cron !== 'object' || cron === null || !('path' in cron)) { - continue; - } - - const { path } = cron; - if (typeof path !== 'string') { - continue; - } - - if (!GET_endpoints.some((route) => route.pattern.test(path))) { - unmatched_paths.push(path); - } - } - - builder.log.warn( - `The following paths are not matched by any route:\n - ${unmatched_paths.join( - '\n - ' - )}\nIf these paths are handled in your \`handle\` hook, you can safely ignore this warning.` - ); + validate_vercel_json(builder, vercel_config); } const files = fileURLToPath(new URL('./files', import.meta.url).href); @@ -531,4 +503,53 @@ async function create_function_bundle(builder, entry, dir, config) { write(`${dir}/package.json`, JSON.stringify({ type: 'module' })); } +/** + * + * @param {import('@sveltejs/kit').Builder} builder + * @param {any} vercel_config + */ +function validate_vercel_json(builder, vercel_config) { + if (builder.routes.length > 0 && !builder.routes[0].endpoint) { + // bail — we're on an older SvelteKit version that doesn't + // populate `route.endpoint.methods`, so we can't check + // to see if cron paths are valid + return; + } + + const crons = /** @type {Array} */ ( + Array.isArray(vercel_config?.crons) ? vercel_config.crons : [] + ); + + /** For a route to be considered 'valid', it must be an API route with a GET handler */ + const valid_routes = builder.routes.filter((route) => route.endpoint.methods.includes('GET')); + + /** @type {Array} */ + const unmatched_paths = []; + + for (const cron of crons) { + if (typeof cron !== 'object' || cron === null || !('path' in cron)) { + continue; + } + + const { path } = cron; + if (typeof path !== 'string') { + continue; + } + + for (const route of valid_routes) { + if (route.pattern.test(path)) { + continue; + } + } + + unmatched_paths.push(path); + } + + builder.log.warn( + `The following paths are not matched by any route:\n - ${unmatched_paths.join( + '\n - ' + )}\nIf these paths are handled in your \`handle\` hook, you can safely ignore this warning.` + ); +} + export default plugin; From 1a830861b892f20ae6531193eb2a8cb34732606f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Feb 2023 23:21:03 -0500 Subject: [PATCH 12/14] flesh out error message --- packages/adapter-vercel/index.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index a2bd9f6c4edf..fa194d226c4c 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -546,10 +546,14 @@ function validate_vercel_json(builder, vercel_config) { } builder.log.warn( - `The following paths are not matched by any route:\n - ${unmatched_paths.join( - '\n - ' - )}\nIf these paths are handled in your \`handle\` hook, you can safely ignore this warning.` + `\nvercel.json defines cron tasks that use paths that do not correspond to an API route with a GET handler (ignore this if the request is handled in your \`handle\` hook):` ); + + for (const path of unmatched_paths) { + console.log(` - ${path}`); + } + + console.log(''); } export default plugin; From 206495b50cf8647a7698ee0febd0f70bacae5c66 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 27 Feb 2023 16:55:40 -0500 Subject: [PATCH 13/14] endpoint -> api --- packages/kit/src/core/adapt/builder.js | 4 ++-- packages/kit/src/core/postbuild/analyse.js | 20 ++++++++++---------- packages/kit/types/index.d.ts | 6 +++--- packages/kit/types/internal.d.ts | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 94fc219dde55..3b766db6acc4 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -43,14 +43,14 @@ export function create_builder({ * we expose a stable type that adapters can use to group/filter routes */ const routes = route_data.map((route) => { - const { config, methods, page, endpoint } = /** @type {import('types').ServerMetadataRoute} */ ( + const { config, methods, page, api } = /** @type {import('types').ServerMetadataRoute} */ ( server_metadata.routes.get(route.id) ); /** @type {import('types').RouteDefinition} */ const facade = { id: route.id, - endpoint, + api, page, segments: get_route_segments(route.id).map((segment) => ({ dynamic: segment.includes('['), diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index a041fe6124e1..b31df2c5f4e2 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -66,7 +66,7 @@ async function analyse({ manifest_path, env }) { const page_methods = []; /** @type {import('types').HttpMethod[]} */ - const endpoint_methods = []; + const api_methods = []; /** @type {import('types').PrerenderOption | undefined} */ let prerender = undefined; @@ -87,12 +87,12 @@ async function analyse({ manifest_path, env }) { prerender = mod.prerender; } - if (mod.GET) endpoint_methods.push('GET'); - if (mod.POST) endpoint_methods.push('POST'); - if (mod.PUT) endpoint_methods.push('PUT'); - if (mod.PATCH) endpoint_methods.push('PATCH'); - if (mod.DELETE) endpoint_methods.push('DELETE'); - if (mod.OPTIONS) endpoint_methods.push('OPTIONS'); + if (mod.GET) api_methods.push('GET'); + if (mod.POST) api_methods.push('POST'); + if (mod.PUT) api_methods.push('PUT'); + if (mod.PATCH) api_methods.push('PATCH'); + if (mod.DELETE) api_methods.push('DELETE'); + if (mod.OPTIONS) api_methods.push('OPTIONS'); config = mod.config; } @@ -137,12 +137,12 @@ async function analyse({ manifest_path, env }) { metadata.routes.set(route.id, { config, - methods: Array.from(new Set([...page_methods, ...endpoint_methods])), + methods: Array.from(new Set([...page_methods, ...api_methods])), page: { methods: page_methods }, - endpoint: { - methods: endpoint_methods + api: { + methods: api_methods }, prerender }); diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index d2f4a6ac9294..a03461e158fd 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -892,7 +892,7 @@ export interface RequestEvent< */ locals: App.Locals; /** - * The parameters of the current page or endpoint - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object + * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object */ params: Params; /** @@ -936,7 +936,7 @@ export interface RequestEvent< */ setHeaders(headers: Record): void; /** - * The URL of the current page or endpoint. + * The requested URL. */ url: URL; /** @@ -983,7 +983,7 @@ export interface ResolveOptions { export interface RouteDefinition { id: string; - endpoint: { + api: { methods: HttpMethod[]; }; page: { diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index df063c93a867..640db8eed685 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -265,7 +265,7 @@ export interface ServerErrorNode { export interface ServerMetadataRoute { config: any; - endpoint: { + api: { methods: HttpMethod[]; }; page: { From b26c6041edb240dc86ec81f2a1badc42e5bc085e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 27 Feb 2023 16:56:34 -0500 Subject: [PATCH 14/14] endpoint -> api --- packages/adapter-vercel/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index fa194d226c4c..748642b73a21 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -509,9 +509,9 @@ async function create_function_bundle(builder, entry, dir, config) { * @param {any} vercel_config */ function validate_vercel_json(builder, vercel_config) { - if (builder.routes.length > 0 && !builder.routes[0].endpoint) { + if (builder.routes.length > 0 && !builder.routes[0].api) { // bail — we're on an older SvelteKit version that doesn't - // populate `route.endpoint.methods`, so we can't check + // populate `route.api.methods`, so we can't check // to see if cron paths are valid return; } @@ -521,7 +521,7 @@ function validate_vercel_json(builder, vercel_config) { ); /** For a route to be considered 'valid', it must be an API route with a GET handler */ - const valid_routes = builder.routes.filter((route) => route.endpoint.methods.includes('GET')); + const valid_routes = builder.routes.filter((route) => route.api.methods.includes('GET')); /** @type {Array} */ const unmatched_paths = [];