From 6d536c438a03e7c02246fed78d94d25451af7d4e Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Wed, 5 Jun 2024 00:25:31 +0800 Subject: [PATCH 01/42] wip --- packages/adapter-vercel/files/reroute.js | 50 +++++++++++ packages/adapter-vercel/index.js | 103 ++++++++++++++++++++++- 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 packages/adapter-vercel/files/reroute.js diff --git a/packages/adapter-vercel/files/reroute.js b/packages/adapter-vercel/files/reroute.js new file mode 100644 index 000000000000..b714447ed139 --- /dev/null +++ b/packages/adapter-vercel/files/reroute.js @@ -0,0 +1,50 @@ +import { reroute } from 'HOOKS'; + +// we copy the rewrite function from `@vercel/edge` because that package can't co-exist with `@types/node`. +// see https://github.com/sveltejs/kit/pull/9280#issuecomment-1452110035 + +/** + * https://github.com/vercel/vercel/blob/4337ea0654c4ee2c91c4464540f879d43da6696f/packages/edge/src/middleware-helpers.ts#L38-L55 + * @param {*} init + * @param {Headers} headers + */ +function handleMiddlewareField(init, headers) { + if (init?.request?.headers) { + if (!(init.request.headers instanceof Headers)) { + throw new Error('request.headers must be an instance of Headers'); + } + + const keys = []; + for (const [key, value] of init.request.headers) { + headers.set('x-middleware-request-' + key, value); + keys.push(key); + } + + headers.set('x-middleware-override-headers', keys.join(',')); + } +} + +/** + * https://github.com/vercel/vercel/blob/4337ea0654c4ee2c91c4464540f879d43da6696f/packages/edge/src/middleware-helpers.ts#L101-L114 + * @param {string | URL} destination + * @returns {Response} + */ +export function rewrite(destination) { + const headers = new Headers({}); + headers.set('x-middleware-rewrite', String(destination)); + + handleMiddlewareField(undefined, headers); + + return new Response(null, { + headers + }); +} + +/** + * @param {Request} request + * @returns {Response} + */ +export default function middleware(request) { + const pathname = reroute({ url: new URL(request.url) }); + return rewrite(new URL(pathname, request.url)); +} diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 7506a90d782d..d8a31146946a 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -220,7 +220,7 @@ const plugin = function (defaults = {}) { } const node_runtime = /nodejs([0-9]+)\.x/.exec(runtime); - if (runtime !== 'edge' && (!node_runtime || node_runtime[1] < 18)) { + if (runtime !== 'edge' && (!node_runtime || +node_runtime[1] < 18)) { throw new Error( `Invalid runtime '${runtime}' for route ${route.id}. Valid runtimes are 'edge' and 'nodejs18.x' or higher ` + '(see the Node.js Version section in your Vercel project settings for info on the currently supported versions).' @@ -293,6 +293,107 @@ const plugin = function (defaults = {}) { const singular = groups.size === 1; + const hooks_filename = builder.config.kit.files.hooks.universal.split('/').at(-1); + const hooks_output_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; + + if (!singular && hooks_output_path) { + /** + * @param {string} name + * @param {import('.').EdgeConfig} config + */ + async function generate_middleware(name, config) { + const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); + const relativePath = path.posix.relative(tmp, hooks_output_path); + + builder.copy(`${files}/${name}.js`, `${tmp}/${name}.js`, { + replace: { + HOOKS: relativePath + } + }); + + try { + const result = await esbuild.build({ + entryPoints: [`${tmp}/${name}.js`], + outfile: `${dirs.functions}/${name}.func/index.js`, + target: 'es2020', // TODO verify what the edge runtime supports + bundle: true, + platform: 'browser', + format: 'esm', + external: [ + ...compatible_node_modules, + ...compatible_node_modules.map((id) => `node:${id}`), + ...(config.external || []) + ], + sourcemap: 'linked', + banner: { js: 'globalThis.global = globalThis;' }, + loader: { + '.wasm': 'copy' + } + }); + + if (result.warnings.length > 0) { + const formatted = await esbuild.formatMessages(result.warnings, { + kind: 'warning', + color: true + }); + + console.error(formatted.join('\n')); + } + } catch (error) { + for (const e of error.errors) { + for (const node of e.notes) { + const match = + /The package "(.+)" wasn't found on the file system but is built into node/.exec( + node.text + ); + + if (match) { + node.text = `Cannot use "${match[1]}" when deploying to Vercel Edge Functions.`; + } + } + } + + const formatted = await esbuild.formatMessages(error.errors, { + kind: 'error', + color: true + }); + + console.error(formatted.join('\n')); + + throw new Error( + `Bundling with esbuild failed with ${error.errors.length} ${ + error.errors.length === 1 ? 'error' : 'errors' + }` + ); + } + + write( + `${dirs.functions}/${name}.func/.vc-config.json`, + JSON.stringify( + { + runtime: 'edge', + regions: config.regions, + entrypoint: 'index.js', + framework: { + slug: 'sveltekit', + version: VERSION + } + }, + null, + '\t' + ) + ); + } + + static_config.routes.push({ + src: '/.*', + middlewarePath: 'reroute', + continue: true + }); + + await generate_middleware('reroute', { external: defaults?.external }); + } + for (const group of groups.values()) { const generate_function = group.config.runtime === 'edge' ? generate_edge_function : generate_serverless_function; From 58db3cec08159325664cc397c18d5165fd464b06 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Wed, 5 Jun 2024 00:40:02 +0800 Subject: [PATCH 02/42] fix types --- packages/adapter-vercel/files/reroute.js | 21 ++++++++++++-- packages/adapter-vercel/index.d.ts | 37 ++++++++++++++++++++++++ packages/adapter-vercel/internal.d.ts | 5 ++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/adapter-vercel/files/reroute.js b/packages/adapter-vercel/files/reroute.js index b714447ed139..3ed5d2802da5 100644 --- a/packages/adapter-vercel/files/reroute.js +++ b/packages/adapter-vercel/files/reroute.js @@ -5,7 +5,7 @@ import { reroute } from 'HOOKS'; /** * https://github.com/vercel/vercel/blob/4337ea0654c4ee2c91c4464540f879d43da6696f/packages/edge/src/middleware-helpers.ts#L38-L55 - * @param {*} init + * @param {import('index.js').ExtraResponseInit | undefined} init * @param {Headers} headers */ function handleMiddlewareField(init, headers) { @@ -40,11 +40,28 @@ export function rewrite(destination) { }); } +/** + * + * @param {import('index.js').ExtraResponseInit=} init + * @returns {Response} + */ +export function next(init) { + const headers = new Headers(init?.headers ?? {}); + headers.set('x-middleware-next', '1'); + + handleMiddlewareField(init, headers); + + return new Response(null, { + ...init, + headers + }); +} + /** * @param {Request} request * @returns {Response} */ export default function middleware(request) { const pathname = reroute({ url: new URL(request.url) }); - return rewrite(new URL(pathname, request.url)); + return pathname ? rewrite(pathname) : next(request); } diff --git a/packages/adapter-vercel/index.d.ts b/packages/adapter-vercel/index.d.ts index 6d9f8d359891..511b26adc86b 100644 --- a/packages/adapter-vercel/index.d.ts +++ b/packages/adapter-vercel/index.d.ts @@ -157,3 +157,40 @@ export interface RequestContext { */ promise: Promise ): void; } + +export interface ModifiedRequest { + /** + * If set, overwrites the incoming headers to the origin request. + * + * This is useful when you want to pass data between a Middleware and a + * Serverless or Edge Function. + * + * @example + * Add a `x-user-id` header and remove the `Authorization` header + * + * ```ts + * import { rewrite } from '@vercel/edge'; + * export default async function middleware(request: Request): Promise { + * const newHeaders = new Headers(request.headers); + * newHeaders.set('x-user-id', 'user_123'); + * newHeaders.delete('authorization'); + * return rewrite(request.url, { + * request: { headers: newHeaders } + * }) + * } + * ``` + */ + headers?: Headers; +} + +export interface ExtraResponseInit extends Omit { + /** + * These headers will be sent to the user response + * along with the response headers from the origin. + */ + headers?: HeadersInit; + /** + * Fields to rewrite for the upstream request. + */ + request?: ModifiedRequest; +} diff --git a/packages/adapter-vercel/internal.d.ts b/packages/adapter-vercel/internal.d.ts index 537f7cc041d1..43c7fe9f93aa 100644 --- a/packages/adapter-vercel/internal.d.ts +++ b/packages/adapter-vercel/internal.d.ts @@ -6,3 +6,8 @@ declare module 'MANIFEST' { import { SSRManifest } from '@sveltejs/kit'; export const manifest: SSRManifest; } + +declare module 'HOOKS' { + import { Reroute } from '@sveltejs/kit'; + export const reroute: Reroute; +} From 12c1dc50467dd731bf593021616836f809269024 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Wed, 5 Jun 2024 22:16:18 +0800 Subject: [PATCH 03/42] use import alias instead of builder.copy replace --- packages/adapter-vercel/files/reroute.js | 2 +- packages/adapter-vercel/index.js | 10 ++++------ packages/adapter-vercel/internal.d.ts | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/adapter-vercel/files/reroute.js b/packages/adapter-vercel/files/reroute.js index 3ed5d2802da5..57a6923cc47c 100644 --- a/packages/adapter-vercel/files/reroute.js +++ b/packages/adapter-vercel/files/reroute.js @@ -1,4 +1,4 @@ -import { reroute } from 'HOOKS'; +import { reroute } from '__HOOKS__'; // we copy the rewrite function from `@vercel/edge` because that package can't co-exist with `@types/node`. // see https://github.com/sveltejs/kit/pull/9280#issuecomment-1452110035 diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index d8a31146946a..1a5f7acc54dd 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -303,13 +303,8 @@ const plugin = function (defaults = {}) { */ async function generate_middleware(name, config) { const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); - const relativePath = path.posix.relative(tmp, hooks_output_path); - builder.copy(`${files}/${name}.js`, `${tmp}/${name}.js`, { - replace: { - HOOKS: relativePath - } - }); + builder.copy(`${files}/${name}.js`, `${tmp}/${name}.js`); try { const result = await esbuild.build({ @@ -328,6 +323,9 @@ const plugin = function (defaults = {}) { banner: { js: 'globalThis.global = globalThis;' }, loader: { '.wasm': 'copy' + }, + alias: { + __HOOKS__: hooks_output_path } }); diff --git a/packages/adapter-vercel/internal.d.ts b/packages/adapter-vercel/internal.d.ts index 43c7fe9f93aa..d1a3caa010b8 100644 --- a/packages/adapter-vercel/internal.d.ts +++ b/packages/adapter-vercel/internal.d.ts @@ -7,7 +7,7 @@ declare module 'MANIFEST' { export const manifest: SSRManifest; } -declare module 'HOOKS' { +declare module '__HOOKS__' { import { Reroute } from '@sveltejs/kit'; export const reroute: Reroute; } From 56821208df7c24047510c7c332f2005c10010d83 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Wed, 5 Jun 2024 23:27:32 +0800 Subject: [PATCH 04/42] add netlify support --- packages/adapter-netlify/ambient.d.ts | 6 +++ packages/adapter-netlify/index.js | 70 +++++++++++++++++++++++++ packages/adapter-netlify/src/reroute.js | 11 ++++ 3 files changed, 87 insertions(+) create mode 100644 packages/adapter-netlify/src/reroute.js diff --git a/packages/adapter-netlify/ambient.d.ts b/packages/adapter-netlify/ambient.d.ts index 450140da9871..abea43f41169 100644 --- a/packages/adapter-netlify/ambient.d.ts +++ b/packages/adapter-netlify/ambient.d.ts @@ -8,3 +8,9 @@ declare module 'MANIFEST' { export const manifest: SSRManifest; export const prerendered: Set; } + +declare module '__HOOKS__' { + import { Reroute } from '@sveltejs/kit'; + + export const reroute: Reroute; +} diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index 7bfec980ef9a..40ee02eb776b 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -17,10 +17,12 @@ import toml from '@iarna/toml'; * functions: Array< * | { * function: string; + * name?: string; * path: string; * } * | { * function: string; + * name?: string; * pattern: string; * } * >; @@ -93,6 +95,13 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { await generate_edge_functions({ builder }); } else { await generate_lambda_functions({ builder, split, publish }); + + const hooks_filename = builder.config.kit.files.hooks.universal.split('/').at(-1); + const hooks_output_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; + + if (split && hooks_output_path) { + await generate_reroute_middleware({ builder, hooks_output_path }); + } } }, @@ -110,6 +119,7 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { } }; } + /** * @param { object } params * @param {import('@sveltejs/kit').Builder} params.builder @@ -176,6 +186,66 @@ async function generate_edge_functions({ builder }) { writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest)); } + +/** + * @param {object} params + * @param {import('@sveltejs/kit').Builder} params.builder + * @param {string} params.hooks_output_path + */ +async function generate_reroute_middleware({ builder, hooks_output_path }) { + const tmp = builder.getBuildDirectory('netlify-tmp'); + builder.rimraf(tmp); + builder.mkdirp(tmp); + + builder.mkdirp('.netlify/edge-functions'); + + // Don't match the static directory + const pattern = '^/.*$'; + + // Go doesn't support lookarounds, so we can't do this + // const pattern = appDir ? `^/(?!${escapeStringRegexp(appDir)}).*$` : '^/.*$'; + + /** @type {HandlerManifest} */ + const edge_manifest = { + functions: [ + { + function: 'reroute', + pattern + }, + { + function: 'render', + pattern + } + ], + version: 1 + }; + + builder.log.minor('Generating Reroute Edge Function...'); + + builder.copy(`${files}/reroute.js`, `${tmp}/entry.js`); + + await esbuild.build({ + entryPoints: [`${tmp}/entry.js`], + // + outfile: '.netlify/edge-functions/reroute.js', + bundle: true, + format: 'esm', + platform: 'browser', + sourcemap: 'linked', + target: 'es2020', + + // Node built-ins are allowed, but must be prefixed with `node:` + // https://docs.netlify.com/edge-functions/api/#runtime-environment + external: builtinModules.map((id) => `node:${id}`), + alias: { + ...Object.fromEntries(builtinModules.map((id) => [id, `node:${id}`])), + __HOOKS__: hooks_output_path + } + }); + + writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest)); +} + /** * @param { object } params * @param {import('@sveltejs/kit').Builder} params.builder diff --git a/packages/adapter-netlify/src/reroute.js b/packages/adapter-netlify/src/reroute.js new file mode 100644 index 000000000000..5587b2cf40fd --- /dev/null +++ b/packages/adapter-netlify/src/reroute.js @@ -0,0 +1,11 @@ +import { reroute } from '__HOOKS__'; + +/** + * @param {Request} request + * @returns {Promise} + */ +export default async function middleware(request) { + const url = new URL(request.url); + const pathname = reroute({ url }); + return pathname ? new URL(pathname, request.url) : undefined; +} From 91aeaaf44373be9733eaa9ed3fb8c2676f54ae6c Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Wed, 5 Jun 2024 23:31:32 +0800 Subject: [PATCH 05/42] oops we only need one edge function for serverless split --- packages/adapter-netlify/index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index 40ee02eb776b..9ae364ee5e4d 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -17,12 +17,10 @@ import toml from '@iarna/toml'; * functions: Array< * | { * function: string; - * name?: string; * path: string; * } * | { * function: string; - * name?: string; * pattern: string; * } * >; @@ -211,10 +209,6 @@ async function generate_reroute_middleware({ builder, hooks_output_path }) { { function: 'reroute', pattern - }, - { - function: 'render', - pattern } ], version: 1 From 06337fdd4a13f3d3b91c0aa4ef2feae20741543e Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Wed, 5 Jun 2024 23:41:39 +0800 Subject: [PATCH 06/42] readability --- packages/adapter-netlify/src/reroute.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/adapter-netlify/src/reroute.js b/packages/adapter-netlify/src/reroute.js index 5587b2cf40fd..446cce5bce7d 100644 --- a/packages/adapter-netlify/src/reroute.js +++ b/packages/adapter-netlify/src/reroute.js @@ -7,5 +7,8 @@ import { reroute } from '__HOOKS__'; export default async function middleware(request) { const url = new URL(request.url); const pathname = reroute({ url }); - return pathname ? new URL(pathname, request.url) : undefined; + + if (pathname) { + return new URL(pathname, request.url); + } } From 25c38a053797c7e82d37a435c4fc99fee91471c0 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Fri, 7 Jun 2024 02:42:04 +0800 Subject: [PATCH 07/42] cleanup vercel implementation --- packages/adapter-vercel/index.js | 159 ++++++++++--------------------- 1 file changed, 50 insertions(+), 109 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 2d79e5d1e104..a4b4272f1d9d 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -98,29 +98,15 @@ const plugin = function (defaults = {}) { } /** + * @param {string} file * @param {string} name - * @param {import('./index.js').EdgeConfig} config - * @param {import('@sveltejs/kit').RouteDefinition[]} routes + * @param {import('./index.js').Config} config + * @param {import('esbuild').BuildOptions=} esbuild_options */ - async function generate_edge_function(name, config, routes) { - const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); - const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); - - builder.copy(`${files}/edge.js`, `${tmp}/edge.js`, { - replace: { - SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js' - } - }); - - write( - `${tmp}/manifest.js`, - `export const manifest = ${builder.generateManifest({ relativePath, routes })};\n` - ); - + async function bundle_edge_function(file, name, config, esbuild_options) { try { const result = await esbuild.build({ - entryPoints: [`${tmp}/edge.js`], + entryPoints: [`${tmp}/${file}.js`], outfile: `${dirs.functions}/${name}.func/index.js`, target: 'es2020', // TODO verify what the edge runtime supports bundle: true, @@ -129,13 +115,14 @@ const plugin = function (defaults = {}) { external: [ ...compatible_node_modules, ...compatible_node_modules.map((id) => `node:${id}`), - ...(config.external || []) + ...((config.runtime === 'edge' && config.external) || []) ], sourcemap: 'linked', banner: { js: 'globalThis.global = globalThis;' }, loader: { '.wasm': 'copy' - } + }, + ...(esbuild_options || {}) }); if (result.warnings.length > 0) { @@ -179,7 +166,7 @@ const plugin = function (defaults = {}) { `${dirs.functions}/${name}.func/.vc-config.json`, JSON.stringify( { - runtime: config.runtime, + runtime: 'edge', regions: config.regions, entrypoint: 'index.js', framework: { @@ -193,6 +180,46 @@ const plugin = function (defaults = {}) { ); } + /** + * @param {string} name + * @param {import('./index.js').EdgeConfig} config + * @param {import('@sveltejs/kit').RouteDefinition[]} routes + */ + async function generate_edge_function(name, config, routes) { + const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); + const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); + + builder.copy(`${files}/edge.js`, `${tmp}/edge.js`, { + replace: { + SERVER: `${relativePath}/index.js`, + MANIFEST: './manifest.js' + } + }); + + write( + `${tmp}/manifest.js`, + `export const manifest = ${builder.generateManifest({ relativePath, routes })};\n` + ); + + await bundle_edge_function('edge', name, config); + } + + /** + * @param {string} name + * @param {import('index.js').Config} config + */ + async function generate_edge_middleware(name, config) { + const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); + + builder.copy(`${files}/${name}.js`, `${tmp}/${name}.js`); + + await bundle_edge_function(name, name, config, { + alias: { + __HOOKS__: hooks_output_path + } + }); + } + /** @type {Map[] }>} */ const groups = new Map(); @@ -298,99 +325,13 @@ const plugin = function (defaults = {}) { const hooks_output_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; if (!singular && hooks_output_path) { - /** - * @param {string} name - * @param {import('.').EdgeConfig} config - */ - async function generate_middleware(name, config) { - const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); - - builder.copy(`${files}/${name}.js`, `${tmp}/${name}.js`); - - try { - const result = await esbuild.build({ - entryPoints: [`${tmp}/${name}.js`], - outfile: `${dirs.functions}/${name}.func/index.js`, - target: 'es2020', // TODO verify what the edge runtime supports - bundle: true, - platform: 'browser', - format: 'esm', - external: [ - ...compatible_node_modules, - ...compatible_node_modules.map((id) => `node:${id}`), - ...(config.external || []) - ], - sourcemap: 'linked', - banner: { js: 'globalThis.global = globalThis;' }, - loader: { - '.wasm': 'copy' - }, - alias: { - __HOOKS__: hooks_output_path - } - }); - - if (result.warnings.length > 0) { - const formatted = await esbuild.formatMessages(result.warnings, { - kind: 'warning', - color: true - }); - - console.error(formatted.join('\n')); - } - } catch (error) { - for (const e of error.errors) { - for (const node of e.notes) { - const match = - /The package "(.+)" wasn't found on the file system but is built into node/.exec( - node.text - ); - - if (match) { - node.text = `Cannot use "${match[1]}" when deploying to Vercel Edge Functions.`; - } - } - } - - const formatted = await esbuild.formatMessages(error.errors, { - kind: 'error', - color: true - }); - - console.error(formatted.join('\n')); - - throw new Error( - `Bundling with esbuild failed with ${error.errors.length} ${ - error.errors.length === 1 ? 'error' : 'errors' - }` - ); - } - - write( - `${dirs.functions}/${name}.func/.vc-config.json`, - JSON.stringify( - { - runtime: 'edge', - regions: config.regions, - entrypoint: 'index.js', - framework: { - slug: 'sveltekit', - version: VERSION - } - }, - null, - '\t' - ) - ); - } - static_config.routes.push({ src: '/.*', middlewarePath: 'reroute', continue: true }); - await generate_middleware('reroute', { external: defaults?.external }); + await generate_edge_middleware('reroute', defaults); } for (const group of groups.values()) { From 28e7f3960b8ae7883b0dbf40946921b19ab91544 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Fri, 7 Jun 2024 02:45:45 +0800 Subject: [PATCH 08/42] this can be sync --- packages/adapter-netlify/src/reroute.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/adapter-netlify/src/reroute.js b/packages/adapter-netlify/src/reroute.js index 446cce5bce7d..c243e0d70abd 100644 --- a/packages/adapter-netlify/src/reroute.js +++ b/packages/adapter-netlify/src/reroute.js @@ -2,9 +2,9 @@ import { reroute } from '__HOOKS__'; /** * @param {Request} request - * @returns {Promise} + * @returns {URL | undefined} */ -export default async function middleware(request) { +export default function middleware(request) { const url = new URL(request.url); const pathname = reroute({ url }); From 766d4f7251768aa840c3e85bbe349792e97e11e8 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Sat, 8 Jun 2024 01:48:57 +0800 Subject: [PATCH 09/42] make temp file a variable --- packages/adapter-vercel/index.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index a4b4272f1d9d..748b7ee65b3c 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -98,15 +98,15 @@ const plugin = function (defaults = {}) { } /** - * @param {string} file + * @param {string} entry_point * @param {string} name * @param {import('./index.js').Config} config * @param {import('esbuild').BuildOptions=} esbuild_options */ - async function bundle_edge_function(file, name, config, esbuild_options) { + async function bundle_edge_function(entry_point, name, config, esbuild_options) { try { const result = await esbuild.build({ - entryPoints: [`${tmp}/${file}.js`], + entryPoints: [entry_point], outfile: `${dirs.functions}/${name}.func/index.js`, target: 'es2020', // TODO verify what the edge runtime supports bundle: true, @@ -189,7 +189,9 @@ const plugin = function (defaults = {}) { const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); - builder.copy(`${files}/edge.js`, `${tmp}/edge.js`, { + const dest = `${tmp}/edge.js`; + + builder.copy(`${files}/edge.js`, dest, { replace: { SERVER: `${relativePath}/index.js`, MANIFEST: './manifest.js' @@ -201,7 +203,7 @@ const plugin = function (defaults = {}) { `export const manifest = ${builder.generateManifest({ relativePath, routes })};\n` ); - await bundle_edge_function('edge', name, config); + await bundle_edge_function(dest, name, config); } /** @@ -211,9 +213,11 @@ const plugin = function (defaults = {}) { async function generate_edge_middleware(name, config) { const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); - builder.copy(`${files}/${name}.js`, `${tmp}/${name}.js`); + const dest = `${tmp}/${name}.js`; + + builder.copy(`${files}/${name}.js`, dest); - await bundle_edge_function(name, name, config, { + await bundle_edge_function(dest, name, config, { alias: { __HOOKS__: hooks_output_path } From 2f40af2fe2d8b86a9ac3f39c3f30de25cc60eedd Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Sat, 8 Jun 2024 13:46:29 +0800 Subject: [PATCH 10/42] fix node types with @vercel/edge --- packages/adapter-vercel/ambient-fix.d.ts | 9 +++ packages/adapter-vercel/ambient.d.ts | 3 +- packages/adapter-vercel/files/reroute.js | 58 +-------------- packages/adapter-vercel/index.d.ts | 93 ------------------------ packages/adapter-vercel/package.json | 1 + pnpm-lock.yaml | 8 ++ 6 files changed, 21 insertions(+), 151 deletions(-) create mode 100644 packages/adapter-vercel/ambient-fix.d.ts diff --git a/packages/adapter-vercel/ambient-fix.d.ts b/packages/adapter-vercel/ambient-fix.d.ts new file mode 100644 index 000000000000..76c6a5c9f478 --- /dev/null +++ b/packages/adapter-vercel/ambient-fix.d.ts @@ -0,0 +1,9 @@ +// we redeclare node process and import this file before `@vercel/edge` +// cso that it an co-exist with `@types/node`. +// see https://github.com/sveltejs/kit/pull/9280#issuecomment-1452110035 + +declare global { + var process: NodeJS.Process; +} + +export {}; diff --git a/packages/adapter-vercel/ambient.d.ts b/packages/adapter-vercel/ambient.d.ts index a106f64e3f12..f9d2dc1ab3b0 100644 --- a/packages/adapter-vercel/ambient.d.ts +++ b/packages/adapter-vercel/ambient.d.ts @@ -1,4 +1,5 @@ -import { RequestContext } from './index.js'; +import 'ambient-fix.js'; +import type { RequestContext } from '@vercel/edge'; declare global { namespace App { diff --git a/packages/adapter-vercel/files/reroute.js b/packages/adapter-vercel/files/reroute.js index 57a6923cc47c..9118b7f44093 100644 --- a/packages/adapter-vercel/files/reroute.js +++ b/packages/adapter-vercel/files/reroute.js @@ -1,61 +1,5 @@ import { reroute } from '__HOOKS__'; - -// we copy the rewrite function from `@vercel/edge` because that package can't co-exist with `@types/node`. -// see https://github.com/sveltejs/kit/pull/9280#issuecomment-1452110035 - -/** - * https://github.com/vercel/vercel/blob/4337ea0654c4ee2c91c4464540f879d43da6696f/packages/edge/src/middleware-helpers.ts#L38-L55 - * @param {import('index.js').ExtraResponseInit | undefined} init - * @param {Headers} headers - */ -function handleMiddlewareField(init, headers) { - if (init?.request?.headers) { - if (!(init.request.headers instanceof Headers)) { - throw new Error('request.headers must be an instance of Headers'); - } - - const keys = []; - for (const [key, value] of init.request.headers) { - headers.set('x-middleware-request-' + key, value); - keys.push(key); - } - - headers.set('x-middleware-override-headers', keys.join(',')); - } -} - -/** - * https://github.com/vercel/vercel/blob/4337ea0654c4ee2c91c4464540f879d43da6696f/packages/edge/src/middleware-helpers.ts#L101-L114 - * @param {string | URL} destination - * @returns {Response} - */ -export function rewrite(destination) { - const headers = new Headers({}); - headers.set('x-middleware-rewrite', String(destination)); - - handleMiddlewareField(undefined, headers); - - return new Response(null, { - headers - }); -} - -/** - * - * @param {import('index.js').ExtraResponseInit=} init - * @returns {Response} - */ -export function next(init) { - const headers = new Headers(init?.headers ?? {}); - headers.set('x-middleware-next', '1'); - - handleMiddlewareField(init, headers); - - return new Response(null, { - ...init, - headers - }); -} +import { rewrite, next } from '@vercel/edge'; /** * @param {Request} request diff --git a/packages/adapter-vercel/index.d.ts b/packages/adapter-vercel/index.d.ts index 75eebb066e37..4c08c9103c4b 100644 --- a/packages/adapter-vercel/index.d.ts +++ b/packages/adapter-vercel/index.d.ts @@ -101,96 +101,3 @@ export type Config = (EdgeConfig | ServerlessConfig) & { */ images?: ImagesConfig; }; - -// we copy the RequestContext interface from `@vercel/edge` because that package can't co-exist with `@types/node`. -// see https://github.com/sveltejs/kit/pull/9280#issuecomment-1452110035 - -/** - * An extension to the standard `Request` object that is passed to every Edge Function. - * - * @example - * ```ts - * import type { RequestContext } from '@vercel/edge'; - * - * export default async function handler(request: Request, ctx: RequestContext): Promise { - * // ctx is the RequestContext - * } - * ``` - */ -export interface RequestContext { - /** - * A method that can be used to keep the function running after a response has been sent. - * This is useful when you have an async task that you want to keep running even after the - * response has been sent and the request has ended. - * - * @example - * - * Sending an internal error to an error tracking service - * - * ```ts - * import type { RequestContext } from '@vercel/edge'; - * - * export async function handleRequest(request: Request, ctx: RequestContext): Promise { - * try { - * return await myFunctionThatReturnsResponse(); - * } catch (e) { - * ctx.waitUntil((async () => { - * // report this error to your error tracking service - * await fetch('https://my-error-tracking-service.com', { - * method: 'POST', - * body: JSON.stringify({ - * stack: e.stack, - * message: e.message, - * name: e.name, - * url: request.url, - * }), - * }); - * })()); - * return new Response('Internal Server Error', { status: 500 }); - * } - * } - * ``` - */ - waitUntil( - /** - * A promise that will be kept alive until it resolves or rejects. - */ promise: Promise - ): void; -} - -export interface ModifiedRequest { - /** - * If set, overwrites the incoming headers to the origin request. - * - * This is useful when you want to pass data between a Middleware and a - * Serverless or Edge Function. - * - * @example - * Add a `x-user-id` header and remove the `Authorization` header - * - * ```ts - * import { rewrite } from '@vercel/edge'; - * export default async function middleware(request: Request): Promise { - * const newHeaders = new Headers(request.headers); - * newHeaders.set('x-user-id', 'user_123'); - * newHeaders.delete('authorization'); - * return rewrite(request.url, { - * request: { headers: newHeaders } - * }) - * } - * ``` - */ - headers?: Headers; -} - -export interface ExtraResponseInit extends Omit { - /** - * These headers will be sent to the user response - * along with the response headers from the origin. - */ - headers?: HeadersInit; - /** - * Fields to rewrite for the upstream request. - */ - request?: ModifiedRequest; -} diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index f0aa1208dcdb..9dacdfd43b0a 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -38,6 +38,7 @@ "@sveltejs/kit": "workspace:^", "@sveltejs/vite-plugin-svelte": "^3.0.1", "@types/node": "^18.19.3", + "@vercel/edge": "^1.1.1", "typescript": "^5.3.3", "vitest": "^1.5.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7dfe0ad6ef40..f396e555eb2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -284,6 +284,9 @@ importers: '@types/node': specifier: ^18.19.3 version: 18.19.31 + '@vercel/edge': + specifier: ^1.1.1 + version: 1.1.1 typescript: specifier: ^5.3.3 version: 5.4.5 @@ -2361,6 +2364,9 @@ packages: '@typescript/vfs@1.3.5': resolution: {integrity: sha512-pI8Saqjupf9MfLw7w2+og+fmb0fZS0J6vsKXXrp4/PDXEFvntgzXmChCXC/KefZZS0YGS6AT8e0hGAJcTsdJlg==} + '@vercel/edge@1.1.1': + resolution: {integrity: sha512-NtKiIbn9Cq6HWGy+qRudz28mz5nxfOJWls5Pnckjw1yCfSX8rhXdvY/il3Sy3Zd5n/sKCM2h7VSCCpJF/oaDrQ==} + '@vercel/nft@0.27.1': resolution: {integrity: sha512-K6upzYHCV1cq2gP83r1o8uNV1vwvAlozvMqp7CEjYWxo0CMI8/4jKcDkVjlypVhrfZ54SXwh9QbH0ZIk/vQCsw==} engines: {node: '>=16'} @@ -6000,6 +6006,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@vercel/edge@1.1.1': {} + '@vercel/nft@0.27.1': dependencies: '@mapbox/node-pre-gyp': 1.0.11 From 6ccef504e6e5e426f3e1636ed09757aa6ee37a7c Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Sun, 9 Jun 2024 01:09:59 +0800 Subject: [PATCH 11/42] add separate tsconfig for edge files --- packages/adapter-vercel/ambient-fix.d.ts | 9 --- packages/adapter-vercel/ambient.d.ts | 5 +- .../adapter-vercel/files/{ => edge}/edge.js | 2 +- .../files/{ => edge}/reroute.js | 0 .../adapter-vercel/files/edge/tsconfig.json | 15 +++++ packages/adapter-vercel/index.d.ts | 56 +++++++++++++++++++ packages/adapter-vercel/index.js | 6 +- packages/adapter-vercel/package.json | 3 +- packages/adapter-vercel/tsconfig.json | 3 +- pnpm-lock.yaml | 6 +- 10 files changed, 85 insertions(+), 20 deletions(-) delete mode 100644 packages/adapter-vercel/ambient-fix.d.ts rename packages/adapter-vercel/files/{ => edge}/edge.js (89%) rename packages/adapter-vercel/files/{ => edge}/reroute.js (100%) create mode 100644 packages/adapter-vercel/files/edge/tsconfig.json diff --git a/packages/adapter-vercel/ambient-fix.d.ts b/packages/adapter-vercel/ambient-fix.d.ts deleted file mode 100644 index 76c6a5c9f478..000000000000 --- a/packages/adapter-vercel/ambient-fix.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -// we redeclare node process and import this file before `@vercel/edge` -// cso that it an co-exist with `@types/node`. -// see https://github.com/sveltejs/kit/pull/9280#issuecomment-1452110035 - -declare global { - var process: NodeJS.Process; -} - -export {}; diff --git a/packages/adapter-vercel/ambient.d.ts b/packages/adapter-vercel/ambient.d.ts index f9d2dc1ab3b0..2c94fea274a8 100644 --- a/packages/adapter-vercel/ambient.d.ts +++ b/packages/adapter-vercel/ambient.d.ts @@ -1,5 +1,4 @@ -import 'ambient-fix.js'; -import type { RequestContext } from '@vercel/edge'; +import type { RequestContext } from "./index.js"; declare global { namespace App { @@ -11,3 +10,5 @@ declare global { } } } + +export {}; diff --git a/packages/adapter-vercel/files/edge.js b/packages/adapter-vercel/files/edge/edge.js similarity index 89% rename from packages/adapter-vercel/files/edge.js rename to packages/adapter-vercel/files/edge/edge.js index 9834559a2235..2343d11c0a8c 100644 --- a/packages/adapter-vercel/files/edge.js +++ b/packages/adapter-vercel/files/edge/edge.js @@ -8,7 +8,7 @@ const initialized = server.init({ /** * @param {Request} request - * @param {import('../index.js').RequestContext} context + * @param {import('@vercel/edge').RequestContext} context */ export default async (request, context) => { await initialized; diff --git a/packages/adapter-vercel/files/reroute.js b/packages/adapter-vercel/files/edge/reroute.js similarity index 100% rename from packages/adapter-vercel/files/reroute.js rename to packages/adapter-vercel/files/edge/reroute.js diff --git a/packages/adapter-vercel/files/edge/tsconfig.json b/packages/adapter-vercel/files/edge/tsconfig.json new file mode 100644 index 000000000000..111b433e9599 --- /dev/null +++ b/packages/adapter-vercel/files/edge/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "strict": true, + "noEmit": true, + "noImplicitAny": true, + "target": "es2022", + "module": "es2022", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "baseUrl": "." + }, + "include": ["*.js", "../../internal.d.ts"] +} diff --git a/packages/adapter-vercel/index.d.ts b/packages/adapter-vercel/index.d.ts index 4c08c9103c4b..74aaed8cd620 100644 --- a/packages/adapter-vercel/index.d.ts +++ b/packages/adapter-vercel/index.d.ts @@ -101,3 +101,59 @@ export type Config = (EdgeConfig | ServerlessConfig) & { */ images?: ImagesConfig; }; + +// we copy the RequestContext interface from `@vercel/edge` because that package can't co-exist with `@types/node`. +// see https://github.com/sveltejs/kit/pull/9280#issuecomment-1452110035 + +/** + * An extension to the standard `Request` object that is passed to every Edge Function. + * + * @example + * ```ts + * import type { RequestContext } from '@vercel/edge'; + * + * export default async function handler(request: Request, ctx: RequestContext): Promise { + * // ctx is the RequestContext + * } + * ``` + */ +export interface RequestContext { + /** + * A method that can be used to keep the function running after a response has been sent. + * This is useful when you have an async task that you want to keep running even after the + * response has been sent and the request has ended. + * + * @example + * + * Sending an internal error to an error tracking service + * + * ```ts + * import type { RequestContext } from '@vercel/edge'; + * + * export async function handleRequest(request: Request, ctx: RequestContext): Promise { + * try { + * return await myFunctionThatReturnsResponse(); + * } catch (e) { + * ctx.waitUntil((async () => { + * // report this error to your error tracking service + * await fetch('https://my-error-tracking-service.com', { + * method: 'POST', + * body: JSON.stringify({ + * stack: e.stack, + * message: e.message, + * name: e.name, + * url: request.url, + * }), + * }); + * })()); + * return new Response('Internal Server Error', { status: 500 }); + * } + * } + * ``` + */ + waitUntil( + /** + * A promise that will be kept alive until it resolves or rejects. + */ promise: Promise + ): void; +} diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 748b7ee65b3c..60ebe735255b 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -191,7 +191,7 @@ const plugin = function (defaults = {}) { const dest = `${tmp}/edge.js`; - builder.copy(`${files}/edge.js`, dest, { + builder.copy(`${files}/edge/edge.js`, dest, { replace: { SERVER: `${relativePath}/index.js`, MANIFEST: './manifest.js' @@ -208,14 +208,14 @@ const plugin = function (defaults = {}) { /** * @param {string} name - * @param {import('index.js').Config} config + * @param {import('./index.js').Config} config */ async function generate_edge_middleware(name, config) { const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); const dest = `${tmp}/${name}.js`; - builder.copy(`${files}/${name}.js`, dest); + builder.copy(`${files}/edge/${name}.js`, dest); await bundle_edge_function(dest, name, config, { alias: { diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index 9dacdfd43b0a..8f7cab08c8f1 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -20,6 +20,7 @@ "types": "index.d.ts", "files": [ "files", + "!files/edge/tsconfig.json", "index.js", "utils.js", "index.d.ts" @@ -31,6 +32,7 @@ "test": "vitest run" }, "dependencies": { + "@vercel/edge": "^1.1.1", "@vercel/nft": "^0.27.1", "esbuild": "^0.20.2" }, @@ -38,7 +40,6 @@ "@sveltejs/kit": "workspace:^", "@sveltejs/vite-plugin-svelte": "^3.0.1", "@types/node": "^18.19.3", - "@vercel/edge": "^1.1.1", "typescript": "^5.3.3", "vitest": "^1.5.0" }, diff --git a/packages/adapter-vercel/tsconfig.json b/packages/adapter-vercel/tsconfig.json index 3d157ebc29e5..d010921fa995 100644 --- a/packages/adapter-vercel/tsconfig.json +++ b/packages/adapter-vercel/tsconfig.json @@ -13,5 +13,6 @@ "paths": { "@sveltejs/kit": ["../kit/types/index"] } - } + }, + "exclude": ["files/edge"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f396e555eb2b..491f8bdd7a1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -268,6 +268,9 @@ importers: packages/adapter-vercel: dependencies: + '@vercel/edge': + specifier: ^1.1.1 + version: 1.1.1 '@vercel/nft': specifier: ^0.27.1 version: 0.27.1 @@ -284,9 +287,6 @@ importers: '@types/node': specifier: ^18.19.3 version: 18.19.31 - '@vercel/edge': - specifier: ^1.1.1 - version: 1.1.1 typescript: specifier: ^5.3.3 version: 5.4.5 From 322ba6cedf94e16120604fb8dd2b1dd3c848f6ff Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Sun, 9 Jun 2024 01:11:51 +0800 Subject: [PATCH 12/42] prettier --- packages/adapter-vercel/ambient.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-vercel/ambient.d.ts b/packages/adapter-vercel/ambient.d.ts index 2c94fea274a8..5b3c0a534f8b 100644 --- a/packages/adapter-vercel/ambient.d.ts +++ b/packages/adapter-vercel/ambient.d.ts @@ -1,4 +1,4 @@ -import type { RequestContext } from "./index.js"; +import type { RequestContext } from './index.js'; declare global { namespace App { From 2306806dd7ffe7dc0ea5eb0d2f684edb3aa9cc76 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Sun, 9 Jun 2024 21:32:51 +0800 Subject: [PATCH 13/42] cleanup netlify --- packages/adapter-netlify/index.js | 101 ++++++++++++------------------ packages/adapter-vercel/index.js | 29 +++++---- 2 files changed, 57 insertions(+), 73 deletions(-) diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index 2ad8083075c2..c68fcdf55d5c 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -95,10 +95,10 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { generate_lambda_functions({ builder, split, publish }); const hooks_filename = builder.config.kit.files.hooks.universal.split('/').at(-1); - const hooks_output_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; + const hooks_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; - if (split && hooks_output_path) { - await generate_reroute_middleware({ builder, hooks_output_path }); + if (split && hooks_path) { + await generate_reroute_middleware({ builder, hooks_path }); } } }, @@ -129,24 +129,8 @@ async function generate_edge_functions({ builder }) { builder.mkdirp('.netlify/edge-functions'); - // Don't match the static directory - const pattern = '^/.*$'; - - // Go doesn't support lookarounds, so we can't do this - // const pattern = appDir ? `^/(?!${escapeStringRegexp(appDir)}).*$` : '^/.*$'; - - /** @type {HandlerManifest} */ - const edge_manifest = { - functions: [ - { - function: 'render', - pattern - } - ], - version: 1 - }; - builder.log.minor('Generating Edge Function...'); + const relativePath = posix.relative(tmp, builder.getServerDirectory()); builder.copy(`${files}/edge.js`, `${tmp}/entry.js`, { @@ -167,9 +151,44 @@ async function generate_edge_functions({ builder }) { )});\n` ); + await bundle_edge_function({ builder, name: 'render' }); +} + +/** + * @param {object} params + * @param {import('@sveltejs/kit').Builder} params.builder + * @param {string} params.hooks_path + */ +async function generate_reroute_middleware({ builder, hooks_path }) { + const tmp = builder.getBuildDirectory('netlify-tmp'); + builder.rimraf(tmp); + builder.mkdirp(tmp); + + builder.mkdirp('.netlify/edge-functions'); + + builder.log.minor('Generating Reroute Edge Function...'); + + builder.copy(`${files}/reroute.js`, `${tmp}/entry.js`, { + replace: { + __HOOKS__: hooks_path + } + }); + + await bundle_edge_function({ builder, name: 'reroute' }); +} + +/** + * + * @param {object} params + * @param {import('@sveltejs/kit').Builder} params.builder + * @param {string} params.name + */ +async function bundle_edge_function({ builder, name }) { + const tmp = builder.getBuildDirectory('netlify-tmp'); + await esbuild.build({ entryPoints: [`${tmp}/entry.js`], - outfile: '.netlify/edge-functions/render.js', + outfile: `.netlify/edge-functions/${name}.js`, bundle: true, format: 'esm', platform: 'browser', @@ -182,21 +201,6 @@ async function generate_edge_functions({ builder }) { alias: Object.fromEntries(builtinModules.map((id) => [id, `node:${id}`])) }); - writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest)); -} - -/** - * @param {object} params - * @param {import('@sveltejs/kit').Builder} params.builder - * @param {string} params.hooks_output_path - */ -async function generate_reroute_middleware({ builder, hooks_output_path }) { - const tmp = builder.getBuildDirectory('netlify-tmp'); - builder.rimraf(tmp); - builder.mkdirp(tmp); - - builder.mkdirp('.netlify/edge-functions'); - // Don't match the static directory const pattern = '^/.*$'; @@ -207,36 +211,13 @@ async function generate_reroute_middleware({ builder, hooks_output_path }) { const edge_manifest = { functions: [ { - function: 'reroute', + function: name, pattern } ], version: 1 }; - builder.log.minor('Generating Reroute Edge Function...'); - - builder.copy(`${files}/reroute.js`, `${tmp}/entry.js`); - - await esbuild.build({ - entryPoints: [`${tmp}/entry.js`], - // - outfile: '.netlify/edge-functions/reroute.js', - bundle: true, - format: 'esm', - platform: 'browser', - sourcemap: 'linked', - target: 'es2020', - - // Node built-ins are allowed, but must be prefixed with `node:` - // https://docs.netlify.com/edge-functions/api/#runtime-environment - external: builtinModules.map((id) => `node:${id}`), - alias: { - ...Object.fromEntries(builtinModules.map((id) => [id, `node:${id}`])), - __HOOKS__: hooks_output_path - } - }); - writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest)); } diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 60ebe735255b..1785352cb9b7 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -98,15 +98,13 @@ const plugin = function (defaults = {}) { } /** - * @param {string} entry_point + * @param {import('esbuild').BuildOptions & Required>} esbuild_options * @param {string} name - * @param {import('./index.js').Config} config - * @param {import('esbuild').BuildOptions=} esbuild_options + * @param {import('./index.js').Config} adapter_config */ - async function bundle_edge_function(entry_point, name, config, esbuild_options) { + async function bundle_edge_function(esbuild_options, name, adapter_config) { try { const result = await esbuild.build({ - entryPoints: [entry_point], outfile: `${dirs.functions}/${name}.func/index.js`, target: 'es2020', // TODO verify what the edge runtime supports bundle: true, @@ -115,7 +113,7 @@ const plugin = function (defaults = {}) { external: [ ...compatible_node_modules, ...compatible_node_modules.map((id) => `node:${id}`), - ...((config.runtime === 'edge' && config.external) || []) + ...((adapter_config.runtime === 'edge' && adapter_config.external) || []) ], sourcemap: 'linked', banner: { js: 'globalThis.global = globalThis;' }, @@ -167,7 +165,7 @@ const plugin = function (defaults = {}) { JSON.stringify( { runtime: 'edge', - regions: config.regions, + regions: adapter_config.regions, entrypoint: 'index.js', framework: { slug: 'sveltekit', @@ -203,7 +201,7 @@ const plugin = function (defaults = {}) { `export const manifest = ${builder.generateManifest({ relativePath, routes })};\n` ); - await bundle_edge_function(dest, name, config); + await bundle_edge_function({ entryPoints: [dest] }, name, config); } /** @@ -217,11 +215,16 @@ const plugin = function (defaults = {}) { builder.copy(`${files}/edge/${name}.js`, dest); - await bundle_edge_function(dest, name, config, { - alias: { - __HOOKS__: hooks_output_path - } - }); + await bundle_edge_function( + { + entryPoints: [dest], + alias: { + __HOOKS__: hooks_output_path + } + }, + name, + config + ); } /** @type {Map[] }>} */ From 5cfc8eb2c2d24a9818bf0f8f7c795f3be51377ed Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 17 Jun 2024 02:00:02 +0800 Subject: [PATCH 14/42] docs --- .../docs/25-build-and-deploy/80-adapter-netlify.md | 6 ++++++ documentation/docs/25-build-and-deploy/90-adapter-vercel.md | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/documentation/docs/25-build-and-deploy/80-adapter-netlify.md b/documentation/docs/25-build-and-deploy/80-adapter-netlify.md index a9d5702b3b14..90efe3ef38c0 100644 --- a/documentation/docs/25-build-and-deploy/80-adapter-netlify.md +++ b/documentation/docs/25-build-and-deploy/80-adapter-netlify.md @@ -107,6 +107,12 @@ Additionally, you can add your own Netlify functions by creating a directory for directory = "functions" ``` +## Notes + +### Individual functions and `reroute` + +If the `split` option is set to `true` in the adapter config, the [`reroute`](/docs/hooks#universal-hooks-reroute) function will be deployed as an edge middleware that runs before any individual function. + ## Troubleshooting ### Accessing the file system diff --git a/documentation/docs/25-build-and-deploy/90-adapter-vercel.md b/documentation/docs/25-build-and-deploy/90-adapter-vercel.md index f772d0dd85dd..1250184a748c 100644 --- a/documentation/docs/25-build-and-deploy/90-adapter-vercel.md +++ b/documentation/docs/25-build-and-deploy/90-adapter-vercel.md @@ -171,6 +171,10 @@ If you have Vercel functions contained in the `api` directory at the project's r Projects created before a certain date may default to using an older Node version than what SvelteKit currently requires. You can [change the Node version in your project settings](https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/node-js#node.js-version). +### Individual functions and `reroute` + +If `split` is set to `true` for a route, or at the adapter level, the [`reroute`](/docs/hooks#universal-hooks-reroute) function will be deployed as an edge middleware that runs before any individual function. + ## Troubleshooting ### Accessing the file system From d8c29f3c455528ac134eee4895c45a2a636a0641 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 17 Jun 2024 02:09:01 +0800 Subject: [PATCH 15/42] changeset --- .changeset/hot-guests-enjoy.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/hot-guests-enjoy.md diff --git a/.changeset/hot-guests-enjoy.md b/.changeset/hot-guests-enjoy.md new file mode 100644 index 000000000000..518fa33f1819 --- /dev/null +++ b/.changeset/hot-guests-enjoy.md @@ -0,0 +1,6 @@ +--- +"@sveltejs/adapter-netlify": patch +"@sveltejs/adapter-vercel": patch +--- + +fix: run `reroute` in an edge middleware before invoking an individual function From 58dfb9de321de321eb22854618fba5ffc8b50a88 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Thu, 10 Oct 2024 13:33:22 +0800 Subject: [PATCH 16/42] Update .changeset/hot-guests-enjoy.md --- .changeset/hot-guests-enjoy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/hot-guests-enjoy.md b/.changeset/hot-guests-enjoy.md index 518fa33f1819..9eb5203aa831 100644 --- a/.changeset/hot-guests-enjoy.md +++ b/.changeset/hot-guests-enjoy.md @@ -1,6 +1,6 @@ --- -"@sveltejs/adapter-netlify": patch -"@sveltejs/adapter-vercel": patch +"@sveltejs/adapter-netlify": minor +"@sveltejs/adapter-vercel": minor --- fix: run `reroute` in an edge middleware before invoking an individual function From 1f9d964eb4922daea95ead0b633e8d4c39295256 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 29 Oct 2024 19:53:47 +0800 Subject: [PATCH 17/42] fix broken lockfile --- pnpm-lock.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1a40dcd6f72..1d13e5020e3e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -264,7 +264,7 @@ importers: dependencies: '@vercel/edge': specifier: ^1.1.1 - version: 1.1.1 + version: 1.1.2 '@vercel/nft': specifier: ^0.27.1 version: 0.27.1 @@ -2058,6 +2058,9 @@ packages: resolution: {integrity: sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vercel/edge@1.1.2': + resolution: {integrity: sha512-wt5SnhsMahWX8U9ZZhFUQoiXhMn/CUxA5xeMdZX1cwyOL1ZbDR3rNI8HRT9RSU73nDxeF6jlnqJyp/0Jy0VM2A==} + '@vercel/nft@0.27.1': resolution: {integrity: sha512-K6upzYHCV1cq2gP83r1o8uNV1vwvAlozvMqp7CEjYWxo0CMI8/4jKcDkVjlypVhrfZ54SXwh9QbH0ZIk/vQCsw==} engines: {node: '>=16'} @@ -4683,6 +4686,8 @@ snapshots: '@typescript-eslint/types': 8.4.0 eslint-visitor-keys: 3.4.3 + '@vercel/edge@1.1.2': {} + '@vercel/nft@0.27.1': dependencies: '@mapbox/node-pre-gyp': 1.0.11 From 2d82cac37e2500263532edb2e1bb83759ffff049 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Mon, 2 Dec 2024 13:59:49 +0800 Subject: [PATCH 18/42] check if reroute hook exists before generating reroute middleware --- packages/adapter-netlify/index.js | 5 ++++- packages/adapter-vercel/index.js | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index 63fe15e6c603..221e8c0a9b81 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -98,7 +98,10 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { const hooks_filename = builder.config.kit.files.hooks.universal.split('/').at(-1); const hooks_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; - if (split && hooks_path) { + const has_reroute_hook = + existsSync(hooks_path) && (await import(hooks_path).then((m) => 'reroute' in m)); + + if (split && has_reroute_hook) { await generate_reroute_middleware({ builder, hooks_path }); } } diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index ca0fb67fd108..0fa9c3192990 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -351,7 +351,11 @@ const plugin = function (defaults = {}) { const hooks_filename = builder.config.kit.files.hooks.universal.split('/').at(-1); const hooks_output_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; - if (!singular && hooks_output_path) { + const has_reroute_hook = + fs.existsSync(hooks_output_path) && + (await import(hooks_output_path).then((m) => 'reroute' in m)); + + if (!singular && has_reroute_hook) { static_config.routes.push({ src: '/.*', middlewarePath: 'reroute', From 21251156b698a393e6fbaf21bb4c0baa832defe0 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Mon, 2 Dec 2024 14:15:23 +0800 Subject: [PATCH 19/42] bump @vercel/edge to 1.1.2 --- packages/adapter-vercel/package.json | 2 +- pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index cb7ac5cb2c4f..4ee4d15ece95 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -40,7 +40,7 @@ "test": "vitest run" }, "dependencies": { - "@vercel/edge": "^1.1.1", + "@vercel/edge": "^1.1.2", "@vercel/nft": "^0.27.7", "esbuild": "^0.24.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1803dd31b96d..0b821ce41ec9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -263,7 +263,7 @@ importers: packages/adapter-vercel: dependencies: '@vercel/edge': - specifier: ^1.1.1 + specifier: ^1.1.2 version: 1.1.2 '@vercel/nft': specifier: ^0.27.7 From 59b5f4b1f1fa657f0d835eb5ab086bc3bea6049f Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Mon, 2 Dec 2024 14:31:59 +0800 Subject: [PATCH 20/42] copy over reroute.js --- packages/adapter-netlify/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json index c407c76922ba..6a9cbc803774 100644 --- a/packages/adapter-netlify/package.json +++ b/packages/adapter-netlify/package.json @@ -33,7 +33,7 @@ ], "scripts": { "dev": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -cw", - "build": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -c && node -e \"fs.cpSync('src/edge.js', 'files/edge.js')\"", + "build": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -c && node -e \"fs.cpSync('src/edge.js', 'files/edge.js')\" && node -e \"fs.cpSync('src/reroute.js', 'files/reroute.js')\"", "test": "vitest run", "check": "tsc", "lint": "prettier --check .", From dc440c1a6f73cb6ea6bc956aef8bded2be885d04 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Mon, 2 Dec 2024 14:52:34 +0800 Subject: [PATCH 21/42] how do I get esbuild to bundle vercel/edge? --- packages/adapter-vercel/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index 4ee4d15ece95..01716c1cf668 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -40,7 +40,6 @@ "test": "vitest run" }, "dependencies": { - "@vercel/edge": "^1.1.2", "@vercel/nft": "^0.27.7", "esbuild": "^0.24.0" }, @@ -52,6 +51,7 @@ "vitest": "^2.1.6" }, "peerDependencies": { - "@sveltejs/kit": "^2.4.0" + "@sveltejs/kit": "^2.4.0", + "@vercel/edge": "^1.1.2" } } From 1d1a67dcc92f6e6a1d8e4dd519b124d57b81d2c2 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 3 Dec 2024 10:47:26 +0800 Subject: [PATCH 22/42] add rollup to bundle @vercel/edge --- packages/adapter-netlify/package.json | 2 +- packages/adapter-vercel/.gitignore | 1 + packages/adapter-vercel/index.js | 2 +- packages/adapter-vercel/package.json | 11 ++++++++--- packages/adapter-vercel/rollup.config.js | 17 +++++++++++++++++ .../adapter-vercel/{files => src}/edge/edge.js | 0 .../{files => src}/edge/reroute.js | 0 .../{files => src}/edge/tsconfig.json | 0 .../adapter-vercel/{files => src}/serverless.js | 0 packages/adapter-vercel/tsconfig.json | 2 +- pnpm-lock.yaml | 6 ++++++ 11 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 packages/adapter-vercel/rollup.config.js rename packages/adapter-vercel/{files => src}/edge/edge.js (100%) rename packages/adapter-vercel/{files => src}/edge/reroute.js (100%) rename packages/adapter-vercel/{files => src}/edge/tsconfig.json (100%) rename packages/adapter-vercel/{files => src}/serverless.js (100%) diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json index 6a9cbc803774..1282c3925212 100644 --- a/packages/adapter-netlify/package.json +++ b/packages/adapter-netlify/package.json @@ -33,7 +33,7 @@ ], "scripts": { "dev": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -cw", - "build": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -c && node -e \"fs.cpSync('src/edge.js', 'files/edge.js')\" && node -e \"fs.cpSync('src/reroute.js', 'files/reroute.js')\"", + "build": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -c && node -e \"fs.cpSync('src/edge.js', 'files/edge.js'); fs.cpSync('src/reroute.js', 'files/reroute.js')\"", "test": "vitest run", "check": "tsc", "lint": "prettier --check .", diff --git a/packages/adapter-vercel/.gitignore b/packages/adapter-vercel/.gitignore index 9daa8247da45..1f664acc2b82 100644 --- a/packages/adapter-vercel/.gitignore +++ b/packages/adapter-vercel/.gitignore @@ -1,2 +1,3 @@ .DS_Store node_modules +/files \ No newline at end of file diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 0fa9c3192990..56a0fe51ff94 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -229,7 +229,7 @@ const plugin = function (defaults = {}) { * @param {import('./index.js').Config} config */ async function generate_edge_middleware(name, config) { - const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); + const tmp = builder.getBuildDirectory('vercel-tmp'); const dest = `${tmp}/${name}.js`; diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index 01716c1cf668..2ecc6c6c563a 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -34,24 +34,29 @@ "index.d.ts" ], "scripts": { + "dev": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -cw", + "build": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -c && node -e \"fs.cpSync('src/serverless.js', 'files/serverless.js'); fs.cpSync('src/edge/edge.js', 'files/edge/edge.js')\"", "lint": "prettier --check .", "format": "pnpm lint --write", "check": "tsc", - "test": "vitest run" + "test": "vitest run", + "prepublishOnly": "pnpm build" }, "dependencies": { + "@vercel/edge": "^1.1.2", "@vercel/nft": "^0.27.7", "esbuild": "^0.24.0" }, "devDependencies": { + "@rollup/plugin-node-resolve": "^15.3.0", "@sveltejs/kit": "workspace:^", "@sveltejs/vite-plugin-svelte": "^5.0.1", "@types/node": "^18.19.48", + "rollup": "^4.14.2", "typescript": "^5.3.3", "vitest": "^2.1.6" }, "peerDependencies": { - "@sveltejs/kit": "^2.4.0", - "@vercel/edge": "^1.1.2" + "@sveltejs/kit": "^2.4.0" } } diff --git a/packages/adapter-vercel/rollup.config.js b/packages/adapter-vercel/rollup.config.js new file mode 100644 index 000000000000..b2a500299755 --- /dev/null +++ b/packages/adapter-vercel/rollup.config.js @@ -0,0 +1,17 @@ +import { nodeResolve } from '@rollup/plugin-node-resolve'; + +/** @type {import('rollup').RollupOptions} */ +const config = { + input: { + reroute: 'src/edge/reroute.js', + }, + output: { + dir: 'files/edge', + format: 'esm' + }, + plugins: [nodeResolve({ preferBuiltins: true })], + external: (id) => id === '__HOOKS__', + preserveEntrySignatures: 'exports-only' +}; + +export default config; diff --git a/packages/adapter-vercel/files/edge/edge.js b/packages/adapter-vercel/src/edge/edge.js similarity index 100% rename from packages/adapter-vercel/files/edge/edge.js rename to packages/adapter-vercel/src/edge/edge.js diff --git a/packages/adapter-vercel/files/edge/reroute.js b/packages/adapter-vercel/src/edge/reroute.js similarity index 100% rename from packages/adapter-vercel/files/edge/reroute.js rename to packages/adapter-vercel/src/edge/reroute.js diff --git a/packages/adapter-vercel/files/edge/tsconfig.json b/packages/adapter-vercel/src/edge/tsconfig.json similarity index 100% rename from packages/adapter-vercel/files/edge/tsconfig.json rename to packages/adapter-vercel/src/edge/tsconfig.json diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/src/serverless.js similarity index 100% rename from packages/adapter-vercel/files/serverless.js rename to packages/adapter-vercel/src/serverless.js diff --git a/packages/adapter-vercel/tsconfig.json b/packages/adapter-vercel/tsconfig.json index d010921fa995..4f8cee472937 100644 --- a/packages/adapter-vercel/tsconfig.json +++ b/packages/adapter-vercel/tsconfig.json @@ -14,5 +14,5 @@ "@sveltejs/kit": ["../kit/types/index"] } }, - "exclude": ["files/edge"] + "exclude": ["src/edge", "files"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b821ce41ec9..724cc6c09464 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -272,6 +272,9 @@ importers: specifier: ^0.24.0 version: 0.24.0 devDependencies: + '@rollup/plugin-node-resolve': + specifier: ^15.3.0 + version: 15.3.0(rollup@4.27.4) '@sveltejs/kit': specifier: workspace:^ version: link:../kit @@ -281,6 +284,9 @@ importers: '@types/node': specifier: ^18.19.48 version: 18.19.50 + rollup: + specifier: ^4.14.2 + version: 4.27.4 typescript: specifier: ^5.3.3 version: 5.6.3 From c19a4ae67e81a1fd724e2a42845d72ef767c0602 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 3 Dec 2024 10:49:20 +0800 Subject: [PATCH 23/42] format --- packages/adapter-vercel/rollup.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-vercel/rollup.config.js b/packages/adapter-vercel/rollup.config.js index b2a500299755..394510d7a56a 100644 --- a/packages/adapter-vercel/rollup.config.js +++ b/packages/adapter-vercel/rollup.config.js @@ -3,7 +3,7 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'; /** @type {import('rollup').RollupOptions} */ const config = { input: { - reroute: 'src/edge/reroute.js', + reroute: 'src/edge/reroute.js' }, output: { dir: 'files/edge', From aa125d5b8f5d031faf5ade9d0d405bf12fa52b4b Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 21 Jan 2025 11:35:37 +0800 Subject: [PATCH 24/42] disable duplicate import eslint rule for line --- packages/adapter-netlify/internal.d.ts | 1 + packages/adapter-vercel/internal.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/adapter-netlify/internal.d.ts b/packages/adapter-netlify/internal.d.ts index abea43f41169..d23ffb5a0727 100644 --- a/packages/adapter-netlify/internal.d.ts +++ b/packages/adapter-netlify/internal.d.ts @@ -10,6 +10,7 @@ declare module 'MANIFEST' { } declare module '__HOOKS__' { + // eslint-disable-next-line no-duplicate-imports import { Reroute } from '@sveltejs/kit'; export const reroute: Reroute; diff --git a/packages/adapter-vercel/internal.d.ts b/packages/adapter-vercel/internal.d.ts index d1a3caa010b8..253d06ade4fa 100644 --- a/packages/adapter-vercel/internal.d.ts +++ b/packages/adapter-vercel/internal.d.ts @@ -8,6 +8,7 @@ declare module 'MANIFEST' { } declare module '__HOOKS__' { + // eslint-disable-next-line no-duplicate-imports import { Reroute } from '@sveltejs/kit'; export const reroute: Reroute; } From 014e5dd4a5704b2446f455bd75215f82c7044219 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 21 Jan 2025 11:35:46 +0800 Subject: [PATCH 25/42] bump @vercel/edge --- packages/adapter-vercel/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index 434ad7f66e50..bcd5a27c440a 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -44,7 +44,7 @@ "prepublishOnly": "pnpm build" }, "dependencies": { - "@vercel/edge": "^1.1.2", + "@vercel/edge": "^1.2.1", "@vercel/nft": "^0.29.0", "esbuild": "^0.24.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94d9e3810017..5f629214f560 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -266,8 +266,8 @@ importers: packages/adapter-vercel: dependencies: '@vercel/edge': - specifier: ^1.1.2 - version: 1.1.2 + specifier: ^1.2.1 + version: 1.2.1 '@vercel/nft': specifier: ^0.29.0 version: 0.29.0(rollup@4.30.1) @@ -2080,8 +2080,8 @@ packages: resolution: {integrity: sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vercel/edge@1.1.2': - resolution: {integrity: sha512-wt5SnhsMahWX8U9ZZhFUQoiXhMn/CUxA5xeMdZX1cwyOL1ZbDR3rNI8HRT9RSU73nDxeF6jlnqJyp/0Jy0VM2A==} + '@vercel/edge@1.2.1': + resolution: {integrity: sha512-1++yncEyIAi68D3UEOlytYb1IUcIulMWdoSzX2h9LuSeeyR7JtaIgR8DcTQ6+DmYOQn+5MCh6LY+UmK6QBByNA==} '@vercel/nft@0.29.0': resolution: {integrity: sha512-LAkWyznNySxZ57ibqEGKnWFPqiRxyLvewFyB9iCHFfMsZlVyiu8MNFbjrGk3eV0vuyim5HzBloqlvSrG4BpZ7g==} @@ -4547,7 +4547,7 @@ snapshots: '@typescript-eslint/types': 8.4.0 eslint-visitor-keys: 3.4.3 - '@vercel/edge@1.1.2': {} + '@vercel/edge@1.2.1': {} '@vercel/nft@0.29.0(rollup@4.30.1)': dependencies: From 11c4a7015f9491b302ae0a05830433f3a243aa1b Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 21 Jan 2025 12:31:45 +0800 Subject: [PATCH 26/42] strip sveltekit url internals before passing url to reroute --- packages/adapter-netlify/src/reroute.js | 43 ++++++++++++++++- packages/adapter-vercel/src/edge/reroute.js | 51 ++++++++++++++++++++- packages/kit/src/runtime/shared.js | 2 + packages/kit/src/utils/url.js | 2 + 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/packages/adapter-netlify/src/reroute.js b/packages/adapter-netlify/src/reroute.js index c243e0d70abd..72be34447aaf 100644 --- a/packages/adapter-netlify/src/reroute.js +++ b/packages/adapter-netlify/src/reroute.js @@ -6,9 +6,50 @@ import { reroute } from '__HOOKS__'; */ export default function middleware(request) { const url = new URL(request.url); + + const is_data_request = has_data_suffix(url.pathname); + if (is_data_request) { + url.pathname = + strip_data_suffix(url.pathname) + + (url.searchParams.get(TRAILING_SLASH_PARAM) === '1' ? '/' : '') || '/'; + url.searchParams.delete(TRAILING_SLASH_PARAM); + url.searchParams.delete(INVALIDATED_PARAM); + } + const pathname = reroute({ url }); if (pathname) { - return new URL(pathname, request.url); + const new_url = new URL(request.url); + new_url.pathname = is_data_request ? add_data_suffix(pathname) : pathname; + return new_url; } } + +// These constants/functions are duplicated in kit and adapter-vercel + +const INVALIDATED_PARAM = 'x-sveltekit-invalidated'; + +const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash'; + +const DATA_SUFFIX = '/__data.json'; +const HTML_DATA_SUFFIX = '.html__data.json'; + +/** @param {string} pathname */ +function has_data_suffix(pathname) { + return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX); +} + +/** @param {string} pathname */ +function add_data_suffix(pathname) { + if (pathname.endsWith('.html')) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX); + return pathname.replace(/\/$/, '') + DATA_SUFFIX; +} + +/** @param {string} pathname */ +function strip_data_suffix(pathname) { + if (pathname.endsWith(HTML_DATA_SUFFIX)) { + return pathname.slice(0, -HTML_DATA_SUFFIX.length) + '.html'; + } + + return pathname.slice(0, -DATA_SUFFIX.length); +} diff --git a/packages/adapter-vercel/src/edge/reroute.js b/packages/adapter-vercel/src/edge/reroute.js index 9118b7f44093..266da348ac76 100644 --- a/packages/adapter-vercel/src/edge/reroute.js +++ b/packages/adapter-vercel/src/edge/reroute.js @@ -6,6 +6,53 @@ import { rewrite, next } from '@vercel/edge'; * @returns {Response} */ export default function middleware(request) { - const pathname = reroute({ url: new URL(request.url) }); - return pathname ? rewrite(pathname) : next(request); + const url = new URL(request.url); + + const is_data_request = has_data_suffix(url.pathname); + if (is_data_request) { + url.pathname = + strip_data_suffix(url.pathname) + + (url.searchParams.get(TRAILING_SLASH_PARAM) === '1' ? '/' : '') || '/'; + url.searchParams.delete(TRAILING_SLASH_PARAM); + url.searchParams.delete(INVALIDATED_PARAM); + } + + const pathname = reroute({ url }); + + if (pathname) { + const new_url = new URL(request.url); + new_url.pathname = is_data_request ? add_data_suffix(pathname) : pathname; + return rewrite(new_url); + } + + return next(request); +} + +// These constants/functions are duplicated in kit and adapter-netlify + +const INVALIDATED_PARAM = 'x-sveltekit-invalidated'; + +const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash'; + +const DATA_SUFFIX = '/__data.json'; +const HTML_DATA_SUFFIX = '.html__data.json'; + +/** @param {string} pathname */ +function has_data_suffix(pathname) { + return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX); +} + +/** @param {string} pathname */ +function add_data_suffix(pathname) { + if (pathname.endsWith('.html')) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX); + return pathname.replace(/\/$/, '') + DATA_SUFFIX; +} + +/** @param {string} pathname */ +function strip_data_suffix(pathname) { + if (pathname.endsWith(HTML_DATA_SUFFIX)) { + return pathname.slice(0, -HTML_DATA_SUFFIX.length) + '.html'; + } + + return pathname.slice(0, -DATA_SUFFIX.length); } diff --git a/packages/kit/src/runtime/shared.js b/packages/kit/src/runtime/shared.js index b5c559b4292c..33b6c2eb0759 100644 --- a/packages/kit/src/runtime/shared.js +++ b/packages/kit/src/runtime/shared.js @@ -11,6 +11,8 @@ export function validate_depends(route_id, dep) { } } +// These constants are duplicated in adapter-vercel and adapter-netlify + export const INVALIDATED_PARAM = 'x-sveltekit-invalidated'; export const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash'; diff --git a/packages/kit/src/utils/url.js b/packages/kit/src/utils/url.js index 0d0af684f6a2..e1f7430a91bd 100644 --- a/packages/kit/src/utils/url.js +++ b/packages/kit/src/utils/url.js @@ -200,6 +200,8 @@ function allow_nodejs_console_log(url) { } } +// These constants/functions are duplicated in adapter-vercel and adapter-netlify + const DATA_SUFFIX = '/__data.json'; const HTML_DATA_SUFFIX = '.html__data.json'; From e9a2f85021c88caf709840124ab0a3f81674ce6b Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 21 Jan 2025 12:39:37 +0800 Subject: [PATCH 27/42] revert --- packages/adapter-vercel/ambient.d.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/adapter-vercel/ambient.d.ts b/packages/adapter-vercel/ambient.d.ts index 5b3c0a534f8b..a106f64e3f12 100644 --- a/packages/adapter-vercel/ambient.d.ts +++ b/packages/adapter-vercel/ambient.d.ts @@ -1,4 +1,4 @@ -import type { RequestContext } from './index.js'; +import { RequestContext } from './index.js'; declare global { namespace App { @@ -10,5 +10,3 @@ declare global { } } } - -export {}; From a589063a9ee6b5e3e26e403f21ce80d68eaa341d Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Tue, 21 Jan 2025 12:41:33 +0800 Subject: [PATCH 28/42] Update .changeset/hot-guests-enjoy.md --- .changeset/hot-guests-enjoy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/hot-guests-enjoy.md b/.changeset/hot-guests-enjoy.md index 9eb5203aa831..b5a2abc5abb3 100644 --- a/.changeset/hot-guests-enjoy.md +++ b/.changeset/hot-guests-enjoy.md @@ -3,4 +3,4 @@ "@sveltejs/adapter-vercel": minor --- -fix: run `reroute` in an edge middleware before invoking an individual function +fix: run `reroute` in an edge middleware before invoking a split function From 46ac4dffc4b33570dd61a375ac621ff53e87af82 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Tue, 21 Jan 2025 12:43:52 +0800 Subject: [PATCH 29/42] Update documentation/docs/25-build-and-deploy/90-adapter-vercel.md --- documentation/docs/25-build-and-deploy/90-adapter-vercel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/25-build-and-deploy/90-adapter-vercel.md b/documentation/docs/25-build-and-deploy/90-adapter-vercel.md index 771c5f563a95..fa2e8c119910 100644 --- a/documentation/docs/25-build-and-deploy/90-adapter-vercel.md +++ b/documentation/docs/25-build-and-deploy/90-adapter-vercel.md @@ -191,7 +191,7 @@ Projects created before a certain date may default to using an older Node versio ### Individual functions and `reroute` -If `split` is set to `true` for a route, or at the adapter level, the [`reroute`](/docs/hooks#universal-hooks-reroute) function will be deployed as an edge middleware that runs before any individual function. +If the `split` option is set to `true` for a route, or at the adapter level, the [`reroute`](/docs/hooks#universal-hooks-reroute) function will be deployed as an edge middleware that runs before any individual function. ## Troubleshooting From bb7418f6e6a9dbfb0590a9d73aceafd924fa335c Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 12:58:32 +0800 Subject: [PATCH 30/42] restore original path and export middleware reroute function --- packages/adapter-netlify/index.js | 17 +++---- packages/adapter-netlify/package.json | 2 +- packages/adapter-netlify/rollup.config.js | 40 ++++++++++------ packages/adapter-netlify/src/reroute.js | 50 ++------------------ packages/adapter-vercel/index.js | 21 ++++----- packages/adapter-vercel/src/edge/reroute.js | 51 ++------------------- packages/kit/package.json | 12 +++-- packages/kit/scripts/generate-dts.js | 1 + packages/kit/src/core/adapt/builder.js | 20 ++++++++ packages/kit/src/exports/adapter/index.js | 39 ++++++++++++++++ packages/kit/src/exports/public.d.ts | 5 ++ packages/kit/src/runtime/server/respond.js | 31 +++++++++---- packages/kit/src/runtime/shared.js | 4 +- packages/kit/src/utils/url.js | 2 - packages/kit/types/index.d.ts | 18 ++++++++ 15 files changed, 166 insertions(+), 147 deletions(-) create mode 100644 packages/kit/src/exports/adapter/index.js diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index 221e8c0a9b81..15eb6a8ccbc6 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -95,14 +95,11 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) { } else { generate_lambda_functions({ builder, split, publish }); - const hooks_filename = builder.config.kit.files.hooks.universal.split('/').at(-1); - const hooks_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; + /** @type {string | void} */ + let reroute_path; - const has_reroute_hook = - existsSync(hooks_path) && (await import(hooks_path).then((m) => 'reroute' in m)); - - if (split && has_reroute_hook) { - await generate_reroute_middleware({ builder, hooks_path }); + if (split && (reroute_path = await builder.getReroutePath())) { + await generate_reroute_middleware({ builder, reroute_path }); } } }, @@ -161,9 +158,9 @@ async function generate_edge_functions({ builder }) { /** * @param {object} params * @param {import('@sveltejs/kit').Builder} params.builder - * @param {string} params.hooks_path + * @param {string} params.reroute_path */ -async function generate_reroute_middleware({ builder, hooks_path }) { +async function generate_reroute_middleware({ builder, reroute_path }) { const tmp = builder.getBuildDirectory('netlify-tmp'); builder.rimraf(tmp); builder.mkdirp(tmp); @@ -174,7 +171,7 @@ async function generate_reroute_middleware({ builder, hooks_path }) { builder.copy(`${files}/reroute.js`, `${tmp}/entry.js`, { replace: { - __HOOKS__: hooks_path + __HOOKS__: reroute_path } }); diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json index 95d85faa0139..6601bdc5e8d9 100644 --- a/packages/adapter-netlify/package.json +++ b/packages/adapter-netlify/package.json @@ -33,7 +33,7 @@ ], "scripts": { "dev": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -cw", - "build": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -c && node -e \"fs.cpSync('src/edge.js', 'files/edge.js'); fs.cpSync('src/reroute.js', 'files/reroute.js')\"", + "build": "node -e \"fs.rmSync('files', { force: true, recursive: true })\" && rollup -c && node -e \"fs.cpSync('src/edge.js', 'files/edge.js')\"", "test": "vitest run", "check": "tsc", "lint": "prettier --check .", diff --git a/packages/adapter-netlify/rollup.config.js b/packages/adapter-netlify/rollup.config.js index b69d62886502..c77f061d83b6 100644 --- a/packages/adapter-netlify/rollup.config.js +++ b/packages/adapter-netlify/rollup.config.js @@ -2,20 +2,32 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; -/** @type {import('rollup').RollupOptions} */ -const config = { - input: { - serverless: 'src/serverless.js', - shims: 'src/shims.js' +/** @type {import('rollup').RollupOptions[]} */ +const config = [ + { + input: { + serverless: 'src/serverless.js', + shims: 'src/shims.js' + }, + output: { + dir: 'files/esm', + format: 'esm' + }, + // @ts-ignore https://github.com/rollup/plugins/issues/1329 + plugins: [nodeResolve({ preferBuiltins: true }), commonjs(), json()], + external: (id) => id === '0SERVER' || id.startsWith('node:'), + preserveEntrySignatures: 'exports-only' }, - output: { - dir: 'files/esm', - format: 'esm' - }, - // @ts-ignore https://github.com/rollup/plugins/issues/1329 - plugins: [nodeResolve({ preferBuiltins: true }), commonjs(), json()], - external: (id) => id === '0SERVER' || id.startsWith('node:'), - preserveEntrySignatures: 'exports-only' -}; + { + input: 'src/reroute.js', + output: { + file: 'files/reroute.js', + format: 'esm' + }, + plugins: [nodeResolve({ preferBuiltins: true })], + external: (id) => id === '__HOOKS__', + preserveEntrySignatures: 'exports-only' + } +]; export default config; diff --git a/packages/adapter-netlify/src/reroute.js b/packages/adapter-netlify/src/reroute.js index 72be34447aaf..ebdabb32879e 100644 --- a/packages/adapter-netlify/src/reroute.js +++ b/packages/adapter-netlify/src/reroute.js @@ -1,55 +1,13 @@ import { reroute } from '__HOOKS__'; +import { applyReroute } from '@sveltejs/kit/adapter'; /** * @param {Request} request * @returns {URL | undefined} */ export default function middleware(request) { - const url = new URL(request.url); - - const is_data_request = has_data_suffix(url.pathname); - if (is_data_request) { - url.pathname = - strip_data_suffix(url.pathname) + - (url.searchParams.get(TRAILING_SLASH_PARAM) === '1' ? '/' : '') || '/'; - url.searchParams.delete(TRAILING_SLASH_PARAM); - url.searchParams.delete(INVALIDATED_PARAM); - } - - const pathname = reroute({ url }); - - if (pathname) { - const new_url = new URL(request.url); - new_url.pathname = is_data_request ? add_data_suffix(pathname) : pathname; - return new_url; - } -} - -// These constants/functions are duplicated in kit and adapter-vercel - -const INVALIDATED_PARAM = 'x-sveltekit-invalidated'; - -const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash'; - -const DATA_SUFFIX = '/__data.json'; -const HTML_DATA_SUFFIX = '.html__data.json'; - -/** @param {string} pathname */ -function has_data_suffix(pathname) { - return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX); -} - -/** @param {string} pathname */ -function add_data_suffix(pathname) { - if (pathname.endsWith('.html')) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX); - return pathname.replace(/\/$/, '') + DATA_SUFFIX; -} - -/** @param {string} pathname */ -function strip_data_suffix(pathname) { - if (pathname.endsWith(HTML_DATA_SUFFIX)) { - return pathname.slice(0, -HTML_DATA_SUFFIX.length) + '.html'; + const rerouted_path = applyReroute(new URL(request.url), reroute); + if (rerouted_path) { + return rerouted_path; } - - return pathname.slice(0, -DATA_SUFFIX.length); } diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 56a0fe51ff94..b62cf2b873e2 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -227,8 +227,9 @@ const plugin = function (defaults = {}) { /** * @param {string} name * @param {import('./index.js').Config} config + * @param {Record=} alias */ - async function generate_edge_middleware(name, config) { + async function generate_edge_middleware(name, config, alias) { const tmp = builder.getBuildDirectory('vercel-tmp'); const dest = `${tmp}/${name}.js`; @@ -238,9 +239,7 @@ const plugin = function (defaults = {}) { await bundle_edge_function( { entryPoints: [dest], - alias: { - __HOOKS__: hooks_output_path - } + alias }, name, config @@ -348,21 +347,19 @@ const plugin = function (defaults = {}) { const singular = groups.size === 1; - const hooks_filename = builder.config.kit.files.hooks.universal.split('/').at(-1); - const hooks_output_path = `${builder.getServerDirectory()}/chunks/${hooks_filename}.js`; - - const has_reroute_hook = - fs.existsSync(hooks_output_path) && - (await import(hooks_output_path).then((m) => 'reroute' in m)); + /** @type {string | void} */ + let reroute_path; - if (!singular && has_reroute_hook) { + if (!singular && (reroute_path = await builder.getReroutePath())) { static_config.routes.push({ src: '/.*', middlewarePath: 'reroute', continue: true }); - await generate_edge_middleware('reroute', defaults); + await generate_edge_middleware('reroute', defaults, { + __HOOKS__: reroute_path + }); } for (const group of groups.values()) { diff --git a/packages/adapter-vercel/src/edge/reroute.js b/packages/adapter-vercel/src/edge/reroute.js index 266da348ac76..c84edb1fa78c 100644 --- a/packages/adapter-vercel/src/edge/reroute.js +++ b/packages/adapter-vercel/src/edge/reroute.js @@ -1,58 +1,15 @@ import { reroute } from '__HOOKS__'; import { rewrite, next } from '@vercel/edge'; +import { applyReroute } from '@sveltejs/kit/adapter'; /** * @param {Request} request * @returns {Response} */ export default function middleware(request) { - const url = new URL(request.url); - - const is_data_request = has_data_suffix(url.pathname); - if (is_data_request) { - url.pathname = - strip_data_suffix(url.pathname) + - (url.searchParams.get(TRAILING_SLASH_PARAM) === '1' ? '/' : '') || '/'; - url.searchParams.delete(TRAILING_SLASH_PARAM); - url.searchParams.delete(INVALIDATED_PARAM); + const rerouted_path = applyReroute(new URL(request.url), reroute); + if (rerouted_path) { + return rewrite(rerouted_path); } - - const pathname = reroute({ url }); - - if (pathname) { - const new_url = new URL(request.url); - new_url.pathname = is_data_request ? add_data_suffix(pathname) : pathname; - return rewrite(new_url); - } - return next(request); } - -// These constants/functions are duplicated in kit and adapter-netlify - -const INVALIDATED_PARAM = 'x-sveltekit-invalidated'; - -const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash'; - -const DATA_SUFFIX = '/__data.json'; -const HTML_DATA_SUFFIX = '.html__data.json'; - -/** @param {string} pathname */ -function has_data_suffix(pathname) { - return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX); -} - -/** @param {string} pathname */ -function add_data_suffix(pathname) { - if (pathname.endsWith('.html')) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX); - return pathname.replace(/\/$/, '') + DATA_SUFFIX; -} - -/** @param {string} pathname */ -function strip_data_suffix(pathname) { - if (pathname.endsWith(HTML_DATA_SUFFIX)) { - return pathname.slice(0, -HTML_DATA_SUFFIX.length) + '.html'; - } - - return pathname.slice(0, -DATA_SUFFIX.length); -} diff --git a/packages/kit/package.json b/packages/kit/package.json index a8ba2b6c5b87..9ae00a536d6f 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -80,6 +80,14 @@ "types": "./types/index.d.ts", "import": "./src/exports/index.js" }, + "./adapter": { + "types": "./types/index.d.ts", + "import": "./src/exports/adapter/index.js" + }, + "./hooks": { + "types": "./types/index.d.ts", + "import": "./src/exports/hooks/index.js" + }, "./node": { "types": "./types/index.d.ts", "import": "./src/exports/node/index.js" @@ -88,10 +96,6 @@ "types": "./types/index.d.ts", "import": "./src/exports/node/polyfills.js" }, - "./hooks": { - "types": "./types/index.d.ts", - "import": "./src/exports/hooks/index.js" - }, "./vite": { "types": "./types/index.d.ts", "import": "./src/exports/vite/index.js" diff --git a/packages/kit/scripts/generate-dts.js b/packages/kit/scripts/generate-dts.js index e8579ec59054..421f5867d81c 100644 --- a/packages/kit/scripts/generate-dts.js +++ b/packages/kit/scripts/generate-dts.js @@ -5,6 +5,7 @@ await createBundle({ output: 'types/index.d.ts', modules: { '@sveltejs/kit': 'src/exports/public.d.ts', + '@sveltejs/kit/adapter': 'src/exports/adapter/index.js', '@sveltejs/kit/hooks': 'src/exports/hooks/index.js', '@sveltejs/kit/node': 'src/exports/node/index.js', '@sveltejs/kit/node/polyfills': 'src/exports/node/polyfills.js', diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index e02efbdd1e28..2a9f1d52f926 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -208,6 +208,26 @@ export function create_builder({ return build_data.app_path; }, + async getReroutePath() { + const hooks = build_data.manifest_data.hooks.universal; + console.log(build_data.manifest_data.hooks) + if (!hooks) return; + + const hooks_path = `${config.kit.outDir}/output/server/${build_data.server_manifest[hooks]}`; + + console.log({ + hooks_path, + exists: existsSync(hooks_path) + }) + + const has_reroute_hook = + existsSync(hooks_path) && (await import(hooks_path).then((m) => 'reroute' in m)); + + if (has_reroute_hook) { + return hooks_path; + } + }, + writeClient(dest) { return copy(`${config.kit.outDir}/output/client`, dest, { // avoid making vite build artefacts public diff --git a/packages/kit/src/exports/adapter/index.js b/packages/kit/src/exports/adapter/index.js new file mode 100644 index 000000000000..087eee72f847 --- /dev/null +++ b/packages/kit/src/exports/adapter/index.js @@ -0,0 +1,39 @@ +import { + INVALIDATED_PARAM, + ORIGINAL_PATH_PARAM, + TRAILING_SLASH_PARAM +} from '../../runtime/shared.js'; +import { has_data_suffix, strip_data_suffix } from '../../utils/url.js'; + +/** + * If your deployment platform supports splitting your app into multiple functions, + * you should run this in a middleware that runs before the main handler + * to reroute the request to the correct function. + * + * @param {URL} url + * @param {import("@sveltejs/kit").Reroute} reroute + * @returns {URL | void} + * @since 2.17.0 + */ +export function applyReroute(url, reroute) { + const url_copy = new URL(url); + + const is_data_request = has_data_suffix(url.pathname); + if (is_data_request) { + url_copy.pathname = + strip_data_suffix(url_copy.pathname) + + (url_copy.searchParams.get(TRAILING_SLASH_PARAM) === '1' ? '/' : '') || '/'; + url_copy.searchParams.delete(TRAILING_SLASH_PARAM); + url_copy.searchParams.delete(INVALIDATED_PARAM); + } + + // reroute could alter the given URL, so we pass a copy + const pathname = reroute({ url: url_copy }); + + if (pathname) { + const new_url = new URL(url); + new_url.searchParams.set(ORIGINAL_PATH_PARAM, url.pathname); + new_url.pathname = pathname; + return new_url; + } +} diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 0d5888be0699..bcaec9ff4363 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -145,6 +145,11 @@ export interface Builder { getServerDirectory: () => string; /** Get the application path including any configured `base` path, e.g. `my-base-path/_app`. */ getAppPath: () => string; + /** + * Get the fully resolved path to the file containing the `reroute` hook if it exists. + * @since 2.17.0 + */ + getReroutePath: () => Promise; /** * Write client assets to `dest`. diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index ab51505a897c..08641522227f 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -29,7 +29,7 @@ import { import { get_option } from '../../utils/options.js'; import { json, text } from '../../exports/index.js'; import { action_json_redirect, is_action_json_request } from './page/actions.js'; -import { INVALIDATED_PARAM, TRAILING_SLASH_PARAM } from '../shared.js'; +import { INVALIDATED_PARAM, ORIGINAL_PATH_PARAM, TRAILING_SLASH_PARAM } from '../shared.js'; import { get_public_env } from './env_module.js'; import { load_page_nodes } from './page/load_page_nodes.js'; import { get_page_config } from '../../utils/route_config.js'; @@ -85,6 +85,17 @@ export async function respond(request, options, manifest, state) { return text('Not found', { status: 404 }); } + /** @type {string | undefined} */ + let rerouted_path = undefined; + + // if reroute already ran in an edge middleware we need to restore the original path + const original_path = url.searchParams.get(ORIGINAL_PATH_PARAM); + if (original_path) { + rerouted_path = url.pathname; + url.pathname = original_path; + url.searchParams.delete(ORIGINAL_PATH_PARAM); + } + const is_data_request = has_data_suffix(url.pathname); /** @type {boolean[] | undefined} */ let invalidated_data_nodes; @@ -100,16 +111,18 @@ export async function respond(request, options, manifest, state) { url.searchParams.delete(INVALIDATED_PARAM); } - // reroute could alter the given URL, so we pass a copy - let rerouted_path; - try { - rerouted_path = options.hooks.reroute({ url: new URL(url) }) ?? url.pathname; - } catch { - return text('Internal Server Error', { - status: 500 - }); + if (!rerouted_path) { + try { + // reroute could alter the given URL, so we pass a copy + rerouted_path = options.hooks.reroute({ url: new URL(url) }) ?? url.pathname; + } catch { + return text('Internal Server Error', { + status: 500 + }); + } } + /** @type {string} */ let decoded; try { decoded = decode_pathname(rerouted_path); diff --git a/packages/kit/src/runtime/shared.js b/packages/kit/src/runtime/shared.js index 33b6c2eb0759..6570156e4a1b 100644 --- a/packages/kit/src/runtime/shared.js +++ b/packages/kit/src/runtime/shared.js @@ -11,8 +11,8 @@ export function validate_depends(route_id, dep) { } } -// These constants are duplicated in adapter-vercel and adapter-netlify - export const INVALIDATED_PARAM = 'x-sveltekit-invalidated'; export const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash'; + +export const ORIGINAL_PATH_PARAM = 'x-sveltekit-original-path'; diff --git a/packages/kit/src/utils/url.js b/packages/kit/src/utils/url.js index e1f7430a91bd..0d0af684f6a2 100644 --- a/packages/kit/src/utils/url.js +++ b/packages/kit/src/utils/url.js @@ -200,8 +200,6 @@ function allow_nodejs_console_log(url) { } } -// These constants/functions are duplicated in adapter-vercel and adapter-netlify - const DATA_SUFFIX = '/__data.json'; const HTML_DATA_SUFFIX = '.html__data.json'; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index a7b9cf1e4a09..a500fc5ede81 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -127,6 +127,11 @@ declare module '@sveltejs/kit' { getServerDirectory: () => string; /** Get the application path including any configured `base` path, e.g. `my-base-path/_app`. */ getAppPath: () => string; + /** + * Get the fully resolved path to the file containing the `reroute` hook if it exists. + * @since 2.17.0 + */ + getReroutePath: () => Promise; /** * Write client assets to `dest`. @@ -1970,6 +1975,19 @@ declare module '@sveltejs/kit' { export {}; } +declare module '@sveltejs/kit/adapter' { + /** + * If your deployment platform supports splitting your app into multiple functions, + * you should run this in a middleware that runs before the main handler + * to reroute the request to the correct function. + * + * @since 2.17.0 + */ + export function applyReroute(url: URL, reroute: import("@sveltejs/kit").Reroute): URL | void; + + export {}; +} + declare module '@sveltejs/kit/hooks' { /** * A helper function for sequencing multiple `handle` calls in a middleware-like manner. From 1da5a79d54d5f5112f48dcbefa2ffb755fdce2c8 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 13:12:22 +0800 Subject: [PATCH 31/42] remove logs --- packages/kit/src/core/adapt/builder.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 2a9f1d52f926..d3224f2547f4 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -210,19 +210,11 @@ export function create_builder({ async getReroutePath() { const hooks = build_data.manifest_data.hooks.universal; - console.log(build_data.manifest_data.hooks) if (!hooks) return; - const hooks_path = `${config.kit.outDir}/output/server/${build_data.server_manifest[hooks]}`; - - console.log({ - hooks_path, - exists: existsSync(hooks_path) - }) - + const hooks_path = `${config.kit.outDir}/output/server/${build_data.server_manifest[hooks].file}`; const has_reroute_hook = existsSync(hooks_path) && (await import(hooks_path).then((m) => 'reroute' in m)); - if (has_reroute_hook) { return hooks_path; } From d13fb42e314f03398e6f61996e5d43a4ba11f9fd Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 13:14:03 +0800 Subject: [PATCH 32/42] bump adapter kit peer version --- packages/adapter-netlify/package.json | 2 +- packages/adapter-vercel/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json index bcc1d4d2ed0f..8a45c7d918fe 100644 --- a/packages/adapter-netlify/package.json +++ b/packages/adapter-netlify/package.json @@ -59,6 +59,6 @@ "vitest": "^3.0.1" }, "peerDependencies": { - "@sveltejs/kit": "^2.4.0" + "@sveltejs/kit": "^2.17.0" } } diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index bcd5a27c440a..2325a0b0962d 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -58,6 +58,6 @@ "vitest": "^3.0.1" }, "peerDependencies": { - "@sveltejs/kit": "^2.4.0" + "@sveltejs/kit": "^2.17.0" } } From 25a88d099881eafedbbf0e6b644cccd77cf9c77d Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 13:17:10 +0800 Subject: [PATCH 33/42] format --- packages/kit/src/exports/adapter/index.js | 4 ++-- packages/kit/src/exports/public.d.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/exports/adapter/index.js b/packages/kit/src/exports/adapter/index.js index 087eee72f847..9ab51e12edf5 100644 --- a/packages/kit/src/exports/adapter/index.js +++ b/packages/kit/src/exports/adapter/index.js @@ -9,7 +9,7 @@ import { has_data_suffix, strip_data_suffix } from '../../utils/url.js'; * If your deployment platform supports splitting your app into multiple functions, * you should run this in a middleware that runs before the main handler * to reroute the request to the correct function. - * + * * @param {URL} url * @param {import("@sveltejs/kit").Reroute} reroute * @returns {URL | void} @@ -27,7 +27,7 @@ export function applyReroute(url, reroute) { url_copy.searchParams.delete(INVALIDATED_PARAM); } - // reroute could alter the given URL, so we pass a copy + // reroute could alter the given URL, so we pass a copy const pathname = reroute({ url: url_copy }); if (pathname) { diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index bcaec9ff4363..8962d5173f3f 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -149,7 +149,7 @@ export interface Builder { * Get the fully resolved path to the file containing the `reroute` hook if it exists. * @since 2.17.0 */ - getReroutePath: () => Promise; + getReroutePath: () => Promise; /** * Write client assets to `dest`. From 435e12ed83cdeb829c26fc90abdefde858b009fe Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 13:18:29 +0800 Subject: [PATCH 34/42] reword changeset --- .changeset/hot-guests-enjoy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/hot-guests-enjoy.md b/.changeset/hot-guests-enjoy.md index b5a2abc5abb3..b481541eccc4 100644 --- a/.changeset/hot-guests-enjoy.md +++ b/.changeset/hot-guests-enjoy.md @@ -3,4 +3,4 @@ "@sveltejs/adapter-vercel": minor --- -fix: run `reroute` in an edge middleware before invoking a split function +fix: run `reroute` in an edge middleware if the app has been split into multiple functions From 8d667e6a8148d779219cb350adb770ad355d8158 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 13:57:13 +0800 Subject: [PATCH 35/42] fix incorrect merge --- packages/adapter-netlify/index.js | 65 ++++++++++++------------------- 1 file changed, 24 insertions(+), 41 deletions(-) diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index d348bbabe1cd..925e8990ac8a 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -151,42 +151,6 @@ async function generate_edge_functions({ builder }) { writeFileSync(`${tmp}/manifest.js`, `export const manifest = ${manifest};\n`); - /** @type {{ assets: Set }} */ - const { assets } = (await import(`${tmp}/manifest.js`)).manifest; - - const path = '/*'; - // We only need to specify paths without the trailing slash because - // Netlify will handle the optional trailing slash for us - const excludedPath = [ - // Contains static files - `/${builder.getAppPath()}/*`, - ...builder.prerendered.paths, - ...Array.from(assets).flatMap((asset) => { - if (asset.endsWith('/index.html')) { - const dir = asset.replace(/\/index\.html$/, ''); - return [ - `${builder.config.kit.paths.base}/${asset}`, - `${builder.config.kit.paths.base}/${dir}` - ]; - } - return `${builder.config.kit.paths.base}/${asset}`; - }), - // Should not be served by SvelteKit at all - '/.netlify/*' - ]; - - /** @type {HandlerManifest} */ - const edge_manifest = { - functions: [ - { - function: 'render', - path, - excludedPath - } - ], - version: 1 - }; - await bundle_edge_function({ builder, name: 'render' }); } @@ -244,18 +208,37 @@ async function bundle_edge_function({ builder, name }) { alias: Object.fromEntries(builtinModules.map((id) => [id, `node:${id}`])) }); - // Don't match the static directory - const pattern = '^/.*$'; + /** @type {{ assets: Set }} */ + const { assets } = (await import(`${tmp}/manifest.js`)).manifest; - // Go doesn't support lookarounds, so we can't do this - // const pattern = appDir ? `^/(?!${escapeStringRegexp(appDir)}).*$` : '^/.*$'; + const path = '/*'; + // We only need to specify paths without the trailing slash because + // Netlify will handle the optional trailing slash for us + const excludedPath = [ + // Contains static files + `/${builder.getAppPath()}/*`, + ...builder.prerendered.paths, + ...Array.from(assets).flatMap((asset) => { + if (asset.endsWith('/index.html')) { + const dir = asset.replace(/\/index\.html$/, ''); + return [ + `${builder.config.kit.paths.base}/${asset}`, + `${builder.config.kit.paths.base}/${dir}` + ]; + } + return `${builder.config.kit.paths.base}/${asset}`; + }), + // Should not be served by SvelteKit at all + '/.netlify/*' + ]; /** @type {HandlerManifest} */ const edge_manifest = { functions: [ { function: name, - pattern + path, + excludedPath } ], version: 1 From 95d0d42b24cd2df21f8246fcb97690a19ca3d2cb Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 13:59:32 +0800 Subject: [PATCH 36/42] format --- packages/kit/types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index a500fc5ede81..e8912a558d2f 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -131,7 +131,7 @@ declare module '@sveltejs/kit' { * Get the fully resolved path to the file containing the `reroute` hook if it exists. * @since 2.17.0 */ - getReroutePath: () => Promise; + getReroutePath: () => Promise; /** * Write client assets to `dest`. From 4fb32a5a394fade90b71c3812a900376f7a47aa5 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 17:09:09 +0800 Subject: [PATCH 37/42] apparently the edge middleware preserves the original url so we don't need to --- packages/kit/src/exports/adapter/index.js | 10 ++++------ packages/kit/src/runtime/server/respond.js | 21 +++++---------------- packages/kit/src/runtime/shared.js | 2 -- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/packages/kit/src/exports/adapter/index.js b/packages/kit/src/exports/adapter/index.js index 9ab51e12edf5..75b5eac099f4 100644 --- a/packages/kit/src/exports/adapter/index.js +++ b/packages/kit/src/exports/adapter/index.js @@ -1,9 +1,8 @@ import { INVALIDATED_PARAM, - ORIGINAL_PATH_PARAM, TRAILING_SLASH_PARAM } from '../../runtime/shared.js'; -import { has_data_suffix, strip_data_suffix } from '../../utils/url.js'; +import { add_data_suffix, has_data_suffix, strip_data_suffix } from '../../utils/url.js'; /** * If your deployment platform supports splitting your app into multiple functions, @@ -28,12 +27,11 @@ export function applyReroute(url, reroute) { } // reroute could alter the given URL, so we pass a copy - const pathname = reroute({ url: url_copy }); + const reroute_path = reroute({ url: url_copy }); - if (pathname) { + if (reroute_path) { const new_url = new URL(url); - new_url.searchParams.set(ORIGINAL_PATH_PARAM, url.pathname); - new_url.pathname = pathname; + new_url.pathname = is_data_request ? add_data_suffix(reroute_path) : reroute_path; return new_url; } } diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index f77431510f40..919452b1b6b7 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -29,7 +29,7 @@ import { import { get_option } from '../../utils/options.js'; import { json, text } from '../../exports/index.js'; import { action_json_redirect, is_action_json_request } from './page/actions.js'; -import { INVALIDATED_PARAM, ORIGINAL_PATH_PARAM, TRAILING_SLASH_PARAM } from '../shared.js'; +import { INVALIDATED_PARAM, TRAILING_SLASH_PARAM } from '../shared.js'; import { get_public_env } from './env_module.js'; import { load_page_nodes } from './page/load_page_nodes.js'; import { get_page_config } from '../../utils/route_config.js'; @@ -85,17 +85,6 @@ export async function respond(request, options, manifest, state) { return text('Not found', { status: 404 }); } - /** @type {string | undefined} */ - let rerouted_path = undefined; - - // if reroute already ran in an edge middleware we need to restore the original path - const original_path = url.searchParams.get(ORIGINAL_PATH_PARAM); - if (original_path) { - rerouted_path = url.pathname; - url.pathname = original_path; - url.searchParams.delete(ORIGINAL_PATH_PARAM); - } - const is_data_request = has_data_suffix(url.pathname); /** @type {boolean[] | undefined} */ let invalidated_data_nodes; @@ -110,17 +99,17 @@ export async function respond(request, options, manifest, state) { .map((node) => node === '1'); url.searchParams.delete(INVALIDATED_PARAM); } - - if (!rerouted_path) { + + // reroute could alter the given URL, so we pass a copy + /** @type {string} */ + let rerouted_path; try { - // reroute could alter the given URL, so we pass a copy rerouted_path = options.hooks.reroute({ url: new URL(url) }) ?? url.pathname; } catch { return text('Internal Server Error', { status: 500 }); } - } /** @type {string} */ let decoded; diff --git a/packages/kit/src/runtime/shared.js b/packages/kit/src/runtime/shared.js index 6570156e4a1b..b5c559b4292c 100644 --- a/packages/kit/src/runtime/shared.js +++ b/packages/kit/src/runtime/shared.js @@ -14,5 +14,3 @@ export function validate_depends(route_id, dep) { export const INVALIDATED_PARAM = 'x-sveltekit-invalidated'; export const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash'; - -export const ORIGINAL_PATH_PARAM = 'x-sveltekit-original-path'; From 9934caaf4d38f70499a573176d9cf1f7ace8a88b Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 17:13:21 +0800 Subject: [PATCH 38/42] format --- packages/kit/src/exports/adapter/index.js | 5 +---- packages/kit/src/runtime/server/respond.js | 18 +++++++++--------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/kit/src/exports/adapter/index.js b/packages/kit/src/exports/adapter/index.js index 75b5eac099f4..6cfc9d36a49b 100644 --- a/packages/kit/src/exports/adapter/index.js +++ b/packages/kit/src/exports/adapter/index.js @@ -1,7 +1,4 @@ -import { - INVALIDATED_PARAM, - TRAILING_SLASH_PARAM -} from '../../runtime/shared.js'; +import { INVALIDATED_PARAM, TRAILING_SLASH_PARAM } from '../../runtime/shared.js'; import { add_data_suffix, has_data_suffix, strip_data_suffix } from '../../utils/url.js'; /** diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index 919452b1b6b7..c1fb1fd63d0a 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -99,17 +99,17 @@ export async function respond(request, options, manifest, state) { .map((node) => node === '1'); url.searchParams.delete(INVALIDATED_PARAM); } - - // reroute could alter the given URL, so we pass a copy + /** @type {string} */ let rerouted_path; - try { - rerouted_path = options.hooks.reroute({ url: new URL(url) }) ?? url.pathname; - } catch { - return text('Internal Server Error', { - status: 500 - }); - } + try { + // reroute could alter the given URL, so we pass a copy + rerouted_path = options.hooks.reroute({ url: new URL(url) }) ?? url.pathname; + } catch { + return text('Internal Server Error', { + status: 500 + }); + } /** @type {string} */ let decoded; From 705f2d1ed5da96d16c9b4dcd32fa286196464ef3 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 23 Jan 2025 19:01:25 +0800 Subject: [PATCH 39/42] fix merge discrepencies --- packages/adapter-netlify/index.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index 925e8990ac8a..25dbfb5d9900 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -145,12 +145,6 @@ async function generate_edge_functions({ builder }) { } }); - const manifest = builder.generateManifest({ - relativePath - }); - - writeFileSync(`${tmp}/manifest.js`, `export const manifest = ${manifest};\n`); - await bundle_edge_function({ builder, name: 'render' }); } @@ -186,6 +180,10 @@ async function generate_reroute_middleware({ builder, reroute_path }) { async function bundle_edge_function({ builder, name }) { const tmp = builder.getBuildDirectory('netlify-tmp'); + const relativePath = posix.relative(tmp, builder.getServerDirectory()); + const manifest = builder.generateManifest({ relativePath }); + writeFileSync(`${tmp}/manifest.js`, `export const manifest = ${manifest};\n`); + await esbuild.build({ entryPoints: [`${tmp}/entry.js`], outfile: `.netlify/edge-functions/${name}.js`, From e38a660252687376b3c8aff473f99c4671674a85 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Fri, 24 Jan 2025 12:43:54 +0800 Subject: [PATCH 40/42] fix endless loop on Netlify --- packages/adapter-netlify/src/reroute.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/adapter-netlify/src/reroute.js b/packages/adapter-netlify/src/reroute.js index ebdabb32879e..d13ec4faa612 100644 --- a/packages/adapter-netlify/src/reroute.js +++ b/packages/adapter-netlify/src/reroute.js @@ -6,8 +6,13 @@ import { applyReroute } from '@sveltejs/kit/adapter'; * @returns {URL | undefined} */ export default function middleware(request) { - const rerouted_path = applyReroute(new URL(request.url), reroute); - if (rerouted_path) { + const url = new URL(request.url); + const rerouted_path = applyReroute(url, reroute); + + // a rewrite on Netlify will cause this function to run again with the new URL + // instead of moving onto the route function, so we only return a URL if + // the reroute path is different from the original to avoid an endless loop + if (rerouted_path && url.pathname !== rerouted_path.pathname) { return rerouted_path; } } From 5783e9bb778983e1d8bac90a3e4f57bdf2a5c64d Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Fri, 24 Jan 2025 13:22:07 +0800 Subject: [PATCH 41/42] restore original path --- packages/kit/src/exports/adapter/index.js | 3 ++- packages/kit/src/runtime/server/respond.js | 11 +++++++++-- packages/kit/src/runtime/shared.js | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/exports/adapter/index.js b/packages/kit/src/exports/adapter/index.js index 6cfc9d36a49b..cf1a91b12699 100644 --- a/packages/kit/src/exports/adapter/index.js +++ b/packages/kit/src/exports/adapter/index.js @@ -1,4 +1,4 @@ -import { INVALIDATED_PARAM, TRAILING_SLASH_PARAM } from '../../runtime/shared.js'; +import { INVALIDATED_PARAM, ORIGINAL_PATH_PARAM, TRAILING_SLASH_PARAM } from '../../runtime/shared.js'; import { add_data_suffix, has_data_suffix, strip_data_suffix } from '../../utils/url.js'; /** @@ -28,6 +28,7 @@ export function applyReroute(url, reroute) { if (reroute_path) { const new_url = new URL(url); + new_url.searchParams.set(ORIGINAL_PATH_PARAM, url.pathname); new_url.pathname = is_data_request ? add_data_suffix(reroute_path) : reroute_path; return new_url; } diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index c1fb1fd63d0a..a1bddc1b7bc8 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -29,7 +29,7 @@ import { import { get_option } from '../../utils/options.js'; import { json, text } from '../../exports/index.js'; import { action_json_redirect, is_action_json_request } from './page/actions.js'; -import { INVALIDATED_PARAM, TRAILING_SLASH_PARAM } from '../shared.js'; +import { INVALIDATED_PARAM, ORIGINAL_PATH_PARAM, TRAILING_SLASH_PARAM } from '../shared.js'; import { get_public_env } from './env_module.js'; import { load_page_nodes } from './page/load_page_nodes.js'; import { get_page_config } from '../../utils/route_config.js'; @@ -57,9 +57,16 @@ const allowed_page_methods = new Set(['GET', 'HEAD', 'OPTIONS']); * @returns {Promise} */ export async function respond(request, options, manifest, state) { - /** URL but stripped from the potential `/__data.json` suffix and its search param */ + // URL but stripped from the potential `/__data.json` suffix and its search param const url = new URL(request.url); + // if the url has been rewritten by a middleware, we need to restore the original path + const original_path = url.searchParams.get(ORIGINAL_PATH_PARAM); + if (original_path) { + url.pathname = original_path; + url.searchParams.delete(ORIGINAL_PATH_PARAM); + } + if (options.csrf_check_origin) { const forbidden = is_form_content_type(request) && diff --git a/packages/kit/src/runtime/shared.js b/packages/kit/src/runtime/shared.js index b5c559b4292c..6570156e4a1b 100644 --- a/packages/kit/src/runtime/shared.js +++ b/packages/kit/src/runtime/shared.js @@ -14,3 +14,5 @@ export function validate_depends(route_id, dep) { export const INVALIDATED_PARAM = 'x-sveltekit-invalidated'; export const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash'; + +export const ORIGINAL_PATH_PARAM = 'x-sveltekit-original-path'; From 09a744bbbd3b08494a1c4a11c547ea47cd8dea67 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Fri, 24 Jan 2025 15:11:22 +0800 Subject: [PATCH 42/42] format --- packages/kit/src/exports/adapter/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/exports/adapter/index.js b/packages/kit/src/exports/adapter/index.js index cf1a91b12699..39c7fa9d8a8e 100644 --- a/packages/kit/src/exports/adapter/index.js +++ b/packages/kit/src/exports/adapter/index.js @@ -1,4 +1,8 @@ -import { INVALIDATED_PARAM, ORIGINAL_PATH_PARAM, TRAILING_SLASH_PARAM } from '../../runtime/shared.js'; +import { + INVALIDATED_PARAM, + ORIGINAL_PATH_PARAM, + TRAILING_SLASH_PARAM +} from '../../runtime/shared.js'; import { add_data_suffix, has_data_suffix, strip_data_suffix } from '../../utils/url.js'; /**