From b580a8dcf2e8fa5af87b3bc285927b39ef276250 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Fri, 20 Dec 2024 08:58:51 +0000 Subject: [PATCH 01/15] Implement Cloudflare Workers exports in adapter-cloudflare --- packages/adapter-cloudflare/index.d.ts | 5 +++++ packages/adapter-cloudflare/index.js | 15 ++++++++++++++- packages/adapter-cloudflare/package.json | 2 +- packages/adapter-cloudflare/placeholders.d.ts | 8 ++++++++ packages/adapter-cloudflare/src/worker.js | 2 ++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/adapter-cloudflare/index.d.ts b/packages/adapter-cloudflare/index.d.ts index a1cacb498e24..1416d4fce350 100644 --- a/packages/adapter-cloudflare/index.d.ts +++ b/packages/adapter-cloudflare/index.d.ts @@ -17,6 +17,11 @@ export interface AdapterOptions { */ fallback?: 'plaintext' | 'spa'; + /** + * + */ + exports?: string; + /** * Customize the automatically-generated `_routes.json` file. * https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 677661dfefbb..805e9a3c3dc4 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -1,5 +1,6 @@ import { existsSync, writeFileSync } from 'node:fs'; import * as path from 'node:path'; +import * as process from 'node:process'; import { fileURLToPath } from 'node:url'; import * as esbuild from 'esbuild'; import { getPlatformProxy } from 'wrangler'; @@ -74,10 +75,22 @@ export default function (options = {}) { }); } + if (options.exports) { + const exports_file = path.posix.relative(process.cwd(), options.exports); + writeFileSync( + `${tmp}/_exports.js`, + `import * as exports from "${exports_file}"; + export default exports;` + ); + } else { + writeFileSync(`${tmp}/_exports.js`, 'export default {};'); + } + builder.copy(`${files}/worker.js`, `${tmp}/_worker.js`, { replace: { SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js' + MANIFEST: './manifest.js', + EXPORTS: './_exports.js' } }); diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json index d3c908e82cd2..786271303484 100644 --- a/packages/adapter-cloudflare/package.json +++ b/packages/adapter-cloudflare/package.json @@ -33,7 +33,7 @@ "ambient.d.ts" ], "scripts": { - "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:SERVER --external:MANIFEST --format=esm", + "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:SERVER --external:MANIFEST --external:EXPORTS --format=esm", "lint": "prettier --check .", "format": "pnpm lint --write", "check": "tsc --skipLibCheck", diff --git a/packages/adapter-cloudflare/placeholders.d.ts b/packages/adapter-cloudflare/placeholders.d.ts index 6c79569f7f7f..634dea6c560c 100644 --- a/packages/adapter-cloudflare/placeholders.d.ts +++ b/packages/adapter-cloudflare/placeholders.d.ts @@ -10,3 +10,11 @@ declare module 'MANIFEST' { export const app_path: string; export const base_path: string; } + +declare module 'EXPORTS' { + import { Module } from 'worktop/cfw'; + + const exports: Omit; + + export default exports; +} diff --git a/packages/adapter-cloudflare/src/worker.js b/packages/adapter-cloudflare/src/worker.js index c3c27a0b041f..4778fe43bc38 100644 --- a/packages/adapter-cloudflare/src/worker.js +++ b/packages/adapter-cloudflare/src/worker.js @@ -1,5 +1,6 @@ import { Server } from 'SERVER'; import { manifest, prerendered, base_path } from 'MANIFEST'; +import * as exports from 'EXPORTS'; import * as Cache from 'worktop/cfw.cache'; const server = new Server(manifest); @@ -11,6 +12,7 @@ const version_file = `${app_path}/version.json`; /** @type {import('worktop/cfw').Module.Worker<{ ASSETS: import('worktop/cfw.durable').Durable.Object }>} */ const worker = { + ...exports, async fetch(req, env, context) { // @ts-ignore await server.init({ env }); From a7c9854bd98af9b53dc49a41c27e554b568f209b Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Fri, 20 Dec 2024 09:43:09 +0000 Subject: [PATCH 02/15] Update implementation to correctly resolve paths. --- packages/adapter-cloudflare/index.js | 5 ++--- packages/adapter-cloudflare/placeholders.d.ts | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 805e9a3c3dc4..88fffebe4c54 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -76,11 +76,10 @@ export default function (options = {}) { } if (options.exports) { - const exports_file = path.posix.relative(process.cwd(), options.exports); writeFileSync( `${tmp}/_exports.js`, - `import * as exports from "${exports_file}"; - export default exports;` + `import * as exports from "${path.resolve(process.cwd(), options.exports)}";\n\n` + + 'export default exports;' ); } else { writeFileSync(`${tmp}/_exports.js`, 'export default {};'); diff --git a/packages/adapter-cloudflare/placeholders.d.ts b/packages/adapter-cloudflare/placeholders.d.ts index 634dea6c560c..9c55bf3bdab7 100644 --- a/packages/adapter-cloudflare/placeholders.d.ts +++ b/packages/adapter-cloudflare/placeholders.d.ts @@ -12,9 +12,9 @@ declare module 'MANIFEST' { } declare module 'EXPORTS' { - import { Module } from 'worktop/cfw'; + import { ExportedHandler } from '@cloudflare/workers-types'; - const exports: Omit; + const exports: Omit; export default exports; } From 2337ab434503dcb9f8c16a8a723d670a48557dcd Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Fri, 20 Dec 2024 09:43:35 +0000 Subject: [PATCH 03/15] Add additional exports functionality to adapter-cloudflare-workers. --- packages/adapter-cloudflare-workers/files/entry.js | 2 ++ packages/adapter-cloudflare-workers/index.d.ts | 1 + packages/adapter-cloudflare-workers/index.js | 14 ++++++++++++-- .../adapter-cloudflare-workers/placeholders.d.ts | 8 ++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js index 5f022e5096b9..5cdbd2fb2e22 100644 --- a/packages/adapter-cloudflare-workers/files/entry.js +++ b/packages/adapter-cloudflare-workers/files/entry.js @@ -1,5 +1,6 @@ import { Server } from 'SERVER'; import { manifest, prerendered, base_path } from 'MANIFEST'; +import exports from 'EXPORTS'; import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'; import static_asset_manifest_json from '__STATIC_CONTENT_MANIFEST'; const static_asset_manifest = JSON.parse(static_asset_manifest_json); @@ -12,6 +13,7 @@ const immutable = `${app_path}/immutable/`; const version_file = `${app_path}/version.json`; export default { + ...exports, /** * @param {Request} req * @param {any} env diff --git a/packages/adapter-cloudflare-workers/index.d.ts b/packages/adapter-cloudflare-workers/index.d.ts index b76003735910..34e898986304 100644 --- a/packages/adapter-cloudflare-workers/index.d.ts +++ b/packages/adapter-cloudflare-workers/index.d.ts @@ -6,6 +6,7 @@ export default function plugin(options?: AdapterOptions): Adapter; export interface AdapterOptions { config?: string; + exports?: string; /** * Config object passed to {@link https://developers.cloudflare.com/workers/wrangler/api/#getplatformproxy | getPlatformProxy} * during development and preview. diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index 02f8c7caff86..093abfe73d7b 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -1,6 +1,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs'; -import { posix, dirname } from 'node:path'; +import { posix, dirname, resolve } from 'node:path'; import { execSync } from 'node:child_process'; +import { cwd } from 'node:process'; import esbuild from 'esbuild'; import toml from '@iarna/toml'; import { fileURLToPath } from 'node:url'; @@ -32,7 +33,7 @@ const compatible_node_modules = [ ]; /** @type {import('./index.js').default} */ -export default function ({ config = 'wrangler.toml', platformProxy = {} } = {}) { +export default function ({ config = 'wrangler.toml', platformProxy = {}, exports } = {}) { return { name: '@sveltejs/adapter-cloudflare-workers', @@ -78,6 +79,15 @@ export default function ({ config = 'wrangler.toml', platformProxy = {} } = {}) `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n` ); + if (exports) { + writeFileSync( + `${tmp}/_exports.js`, + `import * as exports from "${resolve(cwd(), exports)}";\n\n` + 'export default exports;' + ); + } else { + writeFileSync(`${tmp}/_exports.js`, 'export default {};'); + } + const external = ['__STATIC_CONTENT_MANIFEST', 'cloudflare:*']; if (compatibility_flags && compatibility_flags.includes('nodejs_compat')) { external.push(...compatible_node_modules.map((id) => `node:${id}`)); diff --git a/packages/adapter-cloudflare-workers/placeholders.d.ts b/packages/adapter-cloudflare-workers/placeholders.d.ts index 3877ad52f4a5..b3668a462463 100644 --- a/packages/adapter-cloudflare-workers/placeholders.d.ts +++ b/packages/adapter-cloudflare-workers/placeholders.d.ts @@ -14,3 +14,11 @@ declare module '__STATIC_CONTENT_MANIFEST' { const json: string; export default json; } + +declare module 'EXPORTS' { + import { ExportedHandler } from '@cloudflare/workers-types'; + + const exports: Omit; + + export default exports; +} From 05b8ad99b206c00144260cca1fddc8b421a32d16 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Fri, 20 Dec 2024 10:15:05 +0000 Subject: [PATCH 04/15] Rename 'exports' to 'handlers'. --- packages/adapter-cloudflare-workers/files/entry.js | 4 ++-- packages/adapter-cloudflare-workers/index.d.ts | 5 ++++- packages/adapter-cloudflare-workers/index.js | 13 +++++++------ .../adapter-cloudflare-workers/placeholders.d.ts | 6 +++--- packages/adapter-cloudflare/index.d.ts | 4 ++-- packages/adapter-cloudflare/index.js | 12 ++++++------ packages/adapter-cloudflare/package.json | 2 +- packages/adapter-cloudflare/placeholders.d.ts | 6 +++--- packages/adapter-cloudflare/src/worker.js | 4 ++-- 9 files changed, 30 insertions(+), 26 deletions(-) diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js index 5cdbd2fb2e22..891138bc517e 100644 --- a/packages/adapter-cloudflare-workers/files/entry.js +++ b/packages/adapter-cloudflare-workers/files/entry.js @@ -1,6 +1,6 @@ import { Server } from 'SERVER'; import { manifest, prerendered, base_path } from 'MANIFEST'; -import exports from 'EXPORTS'; +import handlers from 'HANDLERS'; import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'; import static_asset_manifest_json from '__STATIC_CONTENT_MANIFEST'; const static_asset_manifest = JSON.parse(static_asset_manifest_json); @@ -13,7 +13,7 @@ const immutable = `${app_path}/immutable/`; const version_file = `${app_path}/version.json`; export default { - ...exports, + ...handlers, /** * @param {Request} req * @param {any} env diff --git a/packages/adapter-cloudflare-workers/index.d.ts b/packages/adapter-cloudflare-workers/index.d.ts index 34e898986304..aac35a9055b3 100644 --- a/packages/adapter-cloudflare-workers/index.d.ts +++ b/packages/adapter-cloudflare-workers/index.d.ts @@ -6,7 +6,10 @@ export default function plugin(options?: AdapterOptions): Adapter; export interface AdapterOptions { config?: string; - exports?: string; + /** + * Path to a file with additional handlers which will be added to the generated worker. + */ + handlers?: string; /** * Config object passed to {@link https://developers.cloudflare.com/workers/wrangler/api/#getplatformproxy | getPlatformProxy} * during development and preview. diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index 093abfe73d7b..f43c8188fd84 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -33,7 +33,7 @@ const compatible_node_modules = [ ]; /** @type {import('./index.js').default} */ -export default function ({ config = 'wrangler.toml', platformProxy = {}, exports } = {}) { +export default function ({ config = 'wrangler.toml', platformProxy = {}, handlers } = {}) { return { name: '@sveltejs/adapter-cloudflare-workers', @@ -59,7 +59,8 @@ export default function ({ config = 'wrangler.toml', platformProxy = {}, exports builder.copy(`${files}/entry.js`, `${tmp}/entry.js`, { replace: { SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js' + MANIFEST: './manifest.js', + HANDLERS: './_handlers.js' } }); @@ -79,13 +80,13 @@ export default function ({ config = 'wrangler.toml', platformProxy = {}, exports `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n` ); - if (exports) { + if (handlers) { writeFileSync( - `${tmp}/_exports.js`, - `import * as exports from "${resolve(cwd(), exports)}";\n\n` + 'export default exports;' + `${tmp}/_handlers.js`, + `import handlers from "${resolve(cwd(), handlers)}";\n\n` + 'export default handlers;' ); } else { - writeFileSync(`${tmp}/_exports.js`, 'export default {};'); + writeFileSync(`${tmp}/_handlers.js`, 'export default {};'); } const external = ['__STATIC_CONTENT_MANIFEST', 'cloudflare:*']; diff --git a/packages/adapter-cloudflare-workers/placeholders.d.ts b/packages/adapter-cloudflare-workers/placeholders.d.ts index b3668a462463..1de6e71b1561 100644 --- a/packages/adapter-cloudflare-workers/placeholders.d.ts +++ b/packages/adapter-cloudflare-workers/placeholders.d.ts @@ -15,10 +15,10 @@ declare module '__STATIC_CONTENT_MANIFEST' { export default json; } -declare module 'EXPORTS' { +declare module 'HANDLERS' { import { ExportedHandler } from '@cloudflare/workers-types'; - const exports: Omit; + const handlers: Omit; - export default exports; + export default handlers; } diff --git a/packages/adapter-cloudflare/index.d.ts b/packages/adapter-cloudflare/index.d.ts index 1416d4fce350..582c075f757f 100644 --- a/packages/adapter-cloudflare/index.d.ts +++ b/packages/adapter-cloudflare/index.d.ts @@ -18,9 +18,9 @@ export interface AdapterOptions { fallback?: 'plaintext' | 'spa'; /** - * + * A path to a file with additional handlers which will be added to the generated worker. */ - exports?: string; + handlers?: string; /** * Customize the automatically-generated `_routes.json` file. diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 88fffebe4c54..6b1f958bb3a9 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -75,21 +75,21 @@ export default function (options = {}) { }); } - if (options.exports) { + if (options.handlers) { writeFileSync( - `${tmp}/_exports.js`, - `import * as exports from "${path.resolve(process.cwd(), options.exports)}";\n\n` + - 'export default exports;' + `${tmp}/_handlers.js`, + `import handlers from "${path.resolve(process.cwd(), options.handlers)}";\n\n` + + 'export default handlers;' ); } else { - writeFileSync(`${tmp}/_exports.js`, 'export default {};'); + writeFileSync(`${tmp}/_handlers.js`, 'export default {};'); } builder.copy(`${files}/worker.js`, `${tmp}/_worker.js`, { replace: { SERVER: `${relativePath}/index.js`, MANIFEST: './manifest.js', - EXPORTS: './_exports.js' + HANDLERS: './_handlers.js' } }); diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json index 786271303484..a1d7d5b39c4f 100644 --- a/packages/adapter-cloudflare/package.json +++ b/packages/adapter-cloudflare/package.json @@ -33,7 +33,7 @@ "ambient.d.ts" ], "scripts": { - "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:SERVER --external:MANIFEST --external:EXPORTS --format=esm", + "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:SERVER --external:MANIFEST --external:HANDLERS --format=esm", "lint": "prettier --check .", "format": "pnpm lint --write", "check": "tsc --skipLibCheck", diff --git a/packages/adapter-cloudflare/placeholders.d.ts b/packages/adapter-cloudflare/placeholders.d.ts index 9c55bf3bdab7..0f0c2f365179 100644 --- a/packages/adapter-cloudflare/placeholders.d.ts +++ b/packages/adapter-cloudflare/placeholders.d.ts @@ -11,10 +11,10 @@ declare module 'MANIFEST' { export const base_path: string; } -declare module 'EXPORTS' { +declare module 'HANDLERS' { import { ExportedHandler } from '@cloudflare/workers-types'; - const exports: Omit; + const handlers: Omit; - export default exports; + export default handlers; } diff --git a/packages/adapter-cloudflare/src/worker.js b/packages/adapter-cloudflare/src/worker.js index 4778fe43bc38..3434916939b5 100644 --- a/packages/adapter-cloudflare/src/worker.js +++ b/packages/adapter-cloudflare/src/worker.js @@ -1,6 +1,6 @@ import { Server } from 'SERVER'; import { manifest, prerendered, base_path } from 'MANIFEST'; -import * as exports from 'EXPORTS'; +import handlers from 'HANDLERS'; import * as Cache from 'worktop/cfw.cache'; const server = new Server(manifest); @@ -12,7 +12,7 @@ const version_file = `${app_path}/version.json`; /** @type {import('worktop/cfw').Module.Worker<{ ASSETS: import('worktop/cfw.durable').Durable.Object }>} */ const worker = { - ...exports, + ...handlers, async fetch(req, env, context) { // @ts-ignore await server.init({ env }); From 2d154111b64fd609bd96baa8ee0b500bfd7c96e2 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Fri, 20 Dec 2024 21:31:55 +1100 Subject: [PATCH 05/15] Add changeset. --- .changeset/thirty-ghosts-cover.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/thirty-ghosts-cover.md diff --git a/.changeset/thirty-ghosts-cover.md b/.changeset/thirty-ghosts-cover.md new file mode 100644 index 000000000000..1db1c62a50ba --- /dev/null +++ b/.changeset/thirty-ghosts-cover.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-cloudflare-workers': minor +'@sveltejs/adapter-cloudflare': minor +--- + +feat: allow additional handlers to be included in generated worker From 8a418d98bc65cd3019457a574ef151212110f695 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Mon, 23 Dec 2024 21:17:37 +1100 Subject: [PATCH 06/15] Remove handlers from adapter-cloudflare --- packages/adapter-cloudflare/index.d.ts | 5 ----- packages/adapter-cloudflare/index.js | 14 +------------- packages/adapter-cloudflare/package.json | 2 +- packages/adapter-cloudflare/placeholders.d.ts | 8 -------- packages/adapter-cloudflare/src/worker.js | 2 -- 5 files changed, 2 insertions(+), 29 deletions(-) diff --git a/packages/adapter-cloudflare/index.d.ts b/packages/adapter-cloudflare/index.d.ts index 582c075f757f..a1cacb498e24 100644 --- a/packages/adapter-cloudflare/index.d.ts +++ b/packages/adapter-cloudflare/index.d.ts @@ -17,11 +17,6 @@ export interface AdapterOptions { */ fallback?: 'plaintext' | 'spa'; - /** - * A path to a file with additional handlers which will be added to the generated worker. - */ - handlers?: string; - /** * Customize the automatically-generated `_routes.json` file. * https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 6b1f958bb3a9..677661dfefbb 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -1,6 +1,5 @@ import { existsSync, writeFileSync } from 'node:fs'; import * as path from 'node:path'; -import * as process from 'node:process'; import { fileURLToPath } from 'node:url'; import * as esbuild from 'esbuild'; import { getPlatformProxy } from 'wrangler'; @@ -75,21 +74,10 @@ export default function (options = {}) { }); } - if (options.handlers) { - writeFileSync( - `${tmp}/_handlers.js`, - `import handlers from "${path.resolve(process.cwd(), options.handlers)}";\n\n` + - 'export default handlers;' - ); - } else { - writeFileSync(`${tmp}/_handlers.js`, 'export default {};'); - } - builder.copy(`${files}/worker.js`, `${tmp}/_worker.js`, { replace: { SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js', - HANDLERS: './_handlers.js' + MANIFEST: './manifest.js' } }); diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json index a1d7d5b39c4f..d3c908e82cd2 100644 --- a/packages/adapter-cloudflare/package.json +++ b/packages/adapter-cloudflare/package.json @@ -33,7 +33,7 @@ "ambient.d.ts" ], "scripts": { - "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:SERVER --external:MANIFEST --external:HANDLERS --format=esm", + "build": "esbuild src/worker.js --bundle --outfile=files/worker.js --external:SERVER --external:MANIFEST --format=esm", "lint": "prettier --check .", "format": "pnpm lint --write", "check": "tsc --skipLibCheck", diff --git a/packages/adapter-cloudflare/placeholders.d.ts b/packages/adapter-cloudflare/placeholders.d.ts index 0f0c2f365179..6c79569f7f7f 100644 --- a/packages/adapter-cloudflare/placeholders.d.ts +++ b/packages/adapter-cloudflare/placeholders.d.ts @@ -10,11 +10,3 @@ declare module 'MANIFEST' { export const app_path: string; export const base_path: string; } - -declare module 'HANDLERS' { - import { ExportedHandler } from '@cloudflare/workers-types'; - - const handlers: Omit; - - export default handlers; -} diff --git a/packages/adapter-cloudflare/src/worker.js b/packages/adapter-cloudflare/src/worker.js index 3434916939b5..c3c27a0b041f 100644 --- a/packages/adapter-cloudflare/src/worker.js +++ b/packages/adapter-cloudflare/src/worker.js @@ -1,6 +1,5 @@ import { Server } from 'SERVER'; import { manifest, prerendered, base_path } from 'MANIFEST'; -import handlers from 'HANDLERS'; import * as Cache from 'worktop/cfw.cache'; const server = new Server(manifest); @@ -12,7 +11,6 @@ const version_file = `${app_path}/version.json`; /** @type {import('worktop/cfw').Module.Worker<{ ASSETS: import('worktop/cfw.durable').Durable.Object }>} */ const worker = { - ...handlers, async fetch(req, env, context) { // @ts-ignore await server.init({ env }); From 69c2ff061c777e13a36ea7e91f16c3b5ca7cefba Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Mon, 23 Dec 2024 21:21:42 +1100 Subject: [PATCH 07/15] Export named exports as well as default export form handlers file. --- packages/adapter-cloudflare-workers/files/entry.js | 3 +++ packages/adapter-cloudflare-workers/index.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js index 891138bc517e..165396584b59 100644 --- a/packages/adapter-cloudflare-workers/files/entry.js +++ b/packages/adapter-cloudflare-workers/files/entry.js @@ -3,6 +3,9 @@ import { manifest, prerendered, base_path } from 'MANIFEST'; import handlers from 'HANDLERS'; import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'; import static_asset_manifest_json from '__STATIC_CONTENT_MANIFEST'; + +export * from 'HANDLERS'; + const static_asset_manifest = JSON.parse(static_asset_manifest_json); const server = new Server(manifest); diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index f43c8188fd84..e35238b732fb 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -81,9 +81,12 @@ export default function ({ config = 'wrangler.toml', platformProxy = {}, handler ); if (handlers) { + const handlers_file = resolve(cwd(), handlers); writeFileSync( `${tmp}/_handlers.js`, - `import handlers from "${resolve(cwd(), handlers)}";\n\n` + 'export default handlers;' + `import handlers from "${handlers_file}";\n\n` + + `export * from "${handlers_file}"` + + 'export default handlers;' ); } else { writeFileSync(`${tmp}/_handlers.js`, 'export default {};'); From 4989a485feb18245a8ed6d02440780b81ea801e3 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Mon, 23 Dec 2024 21:47:53 +1100 Subject: [PATCH 08/15] Document `handlers` option. --- .../25-build-and-deploy/70-adapter-cloudflare-workers.md | 8 ++++++++ packages/adapter-cloudflare-workers/index.d.ts | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md index e9ab68b4f085..3bfd1924dc06 100644 --- a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md +++ b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md @@ -36,6 +36,14 @@ export default { Path to your custom `wrangler.toml` or `wrangler.json` config file. +### handlers + +Path to a file with additional [handlers](https://developers.cloudflare.com/workers/runtime-apis/handlers/) and (optionally) [Durable Objects](https://developers.cloudflare.com/durable-objects/) to be exported from the file the adapter generates. This allows you to, for example, include handlers for scheduled or queue triggers alongside the fetch handler your SvelteKit app. + +> [!NOTE] The adapter expects the `handlers` file to have a default export. If you only want to export a Durable Object, add `export default {};` to the file. + +> [!NOTE] The adapter will overwrite any [fetch handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/) exported from the `handlers` file in the generated worker. Most uses for a fetch handler are covered by endpoints or server hooks, so you should use those instead. + ### platformProxy Preferences for the emulated `platform.env` local bindings. See the [getPlatformProxy](https://developers.cloudflare.com/workers/wrangler/api/#syntax) Wrangler API documentation for a full list of options. diff --git a/packages/adapter-cloudflare-workers/index.d.ts b/packages/adapter-cloudflare-workers/index.d.ts index aac35a9055b3..72f979c42b9b 100644 --- a/packages/adapter-cloudflare-workers/index.d.ts +++ b/packages/adapter-cloudflare-workers/index.d.ts @@ -7,7 +7,7 @@ export default function plugin(options?: AdapterOptions): Adapter; export interface AdapterOptions { config?: string; /** - * Path to a file with additional handlers which will be added to the generated worker. + * Path to a file with additional {@link https://developers.cloudflare.com/workers/runtime-apis/handlers/ | handlers} and (optionally) {@link https://developers.cloudflare.com/durable-objects/ | Durable Objects} to be exported from the file the adapter generates. */ handlers?: string; /** From b9333f3bacdb22777be3922b22c5e995a19cdd19 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Tue, 24 Dec 2024 08:18:07 +1100 Subject: [PATCH 09/15] Fix missing semicolon and linebreaks. --- packages/adapter-cloudflare-workers/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index e35238b732fb..ba0f5a8ef0a6 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -81,14 +81,16 @@ export default function ({ config = 'wrangler.toml', platformProxy = {}, handler ); if (handlers) { + // TODO: find a more robust way to resolve files relative to svelte.config.js const handlers_file = resolve(cwd(), handlers); writeFileSync( `${tmp}/_handlers.js`, `import handlers from "${handlers_file}";\n\n` + - `export * from "${handlers_file}"` + + `export * from "${handlers_file}";\n\n` + 'export default handlers;' ); } else { + // The handlers file must export a plain object as its default export. writeFileSync(`${tmp}/_handlers.js`, 'export default {};'); } From c5c3a9938807b99aaac6307a9233f254ca6e8404 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Sun, 29 Dec 2024 20:22:00 +1100 Subject: [PATCH 10/15] Update changeset --- .changeset/thirty-ghosts-cover.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.changeset/thirty-ghosts-cover.md b/.changeset/thirty-ghosts-cover.md index 1db1c62a50ba..e9e064f4d0e8 100644 --- a/.changeset/thirty-ghosts-cover.md +++ b/.changeset/thirty-ghosts-cover.md @@ -1,6 +1,5 @@ --- '@sveltejs/adapter-cloudflare-workers': minor -'@sveltejs/adapter-cloudflare': minor --- -feat: allow additional handlers to be included in generated worker +feat: allow additional handlers and Durable Objects to be included in generated Cloudflare Worker From 78a617745731390d125ca15f87b78a27d757aabe Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Sun, 29 Dec 2024 20:23:34 +1100 Subject: [PATCH 11/15] Add handlers option to the example config --- .../docs/25-build-and-deploy/70-adapter-cloudflare-workers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md index 3bfd1924dc06..c287381ecdd3 100644 --- a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md +++ b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md @@ -19,6 +19,7 @@ export default { kit: { adapter: adapter({ config: 'wrangler.toml', + handlers: './src/handlers.js', platformProxy: { configPath: 'wrangler.toml', environment: undefined, From 5596beca504a515d7a5d1e44c7877d84bc6db096 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Tue, 31 Dec 2024 15:27:17 +1100 Subject: [PATCH 12/15] Update 70-adapter-cloudflare-workers.md --- .../70-adapter-cloudflare-workers.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md index c287381ecdd3..42645cc71246 100644 --- a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md +++ b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md @@ -41,6 +41,29 @@ Path to your custom `wrangler.toml` or `wrangler.json` config file. Path to a file with additional [handlers](https://developers.cloudflare.com/workers/runtime-apis/handlers/) and (optionally) [Durable Objects](https://developers.cloudflare.com/durable-objects/) to be exported from the file the adapter generates. This allows you to, for example, include handlers for scheduled or queue triggers alongside the fetch handler your SvelteKit app. +The handlers file should export a default object with any additional handlers, and any Durable Objects as named exports. Example below: + +```js +/// file: src/handlers.js +// export your durable objects here +import { DurableObject } from "cloudflare:workers"; + +export class MyDurableObject extends DurableObject { + constructor(state, env) {} + + async sayHello() { + return "Hello, World!"; + } +} + +export default { + async scheduled(event, env, ctx) { + console.log("Scheduled trigger!"); + }, + // additional handlers go here +} +`` + > [!NOTE] The adapter expects the `handlers` file to have a default export. If you only want to export a Durable Object, add `export default {};` to the file. > [!NOTE] The adapter will overwrite any [fetch handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/) exported from the `handlers` file in the generated worker. Most uses for a fetch handler are covered by endpoints or server hooks, so you should use those instead. From 33f0fab54482f685197dab60ac90298e9d08de18 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Tue, 31 Dec 2024 15:27:53 +1100 Subject: [PATCH 13/15] Add missing backtick. --- .../docs/25-build-and-deploy/70-adapter-cloudflare-workers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md index 42645cc71246..12461b6133c2 100644 --- a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md +++ b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md @@ -62,7 +62,7 @@ export default { }, // additional handlers go here } -`` +``` > [!NOTE] The adapter expects the `handlers` file to have a default export. If you only want to export a Durable Object, add `export default {};` to the file. From 615909a939c9451d7941a7c48b470ec507c36526 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Tue, 31 Dec 2024 13:49:07 +0800 Subject: [PATCH 14/15] Update documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md --- .../docs/25-build-and-deploy/70-adapter-cloudflare-workers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md index 12461b6133c2..4ac2c95510d9 100644 --- a/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md +++ b/documentation/docs/25-build-and-deploy/70-adapter-cloudflare-workers.md @@ -44,6 +44,7 @@ Path to a file with additional [handlers](https://developers.cloudflare.com/work The handlers file should export a default object with any additional handlers, and any Durable Objects as named exports. Example below: ```js +// @errors: 2307 2377 7006 /// file: src/handlers.js // export your durable objects here import { DurableObject } from "cloudflare:workers"; From d6bdb755722bf76a49900058d2e66f396f4226f7 Mon Sep 17 00:00:00 2001 From: Thomas Foster Date: Tue, 14 Jan 2025 09:16:40 +0000 Subject: [PATCH 15/15] Correctly add fetch method to Cloudflare RPC workers. --- .../adapter-cloudflare-workers/files/entry.js | 188 ++++++++++-------- .../placeholders.d.ts | 3 +- 2 files changed, 102 insertions(+), 89 deletions(-) diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js index 165396584b59..9acf3ce68308 100644 --- a/packages/adapter-cloudflare-workers/files/entry.js +++ b/packages/adapter-cloudflare-workers/files/entry.js @@ -3,6 +3,7 @@ import { manifest, prerendered, base_path } from 'MANIFEST'; import handlers from 'HANDLERS'; import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'; import static_asset_manifest_json from '__STATIC_CONTENT_MANIFEST'; +import { WorkerEntrypoint } from 'cloudflare:workers'; export * from 'HANDLERS'; @@ -15,101 +16,112 @@ const app_path = `/${manifest.appPath}`; const immutable = `${app_path}/immutable/`; const version_file = `${app_path}/version.json`; -export default { - ...handlers, - /** - * @param {Request} req - * @param {any} env - * @param {any} context - */ - async fetch(req, env, context) { - await server.init({ env }); - - const url = new URL(req.url); - - // static assets - if (url.pathname.startsWith(app_path)) { - /** @type {Response} */ - const res = await get_asset_from_kv(req, env, context); - if (is_error(res.status)) return res; - - const cache_control = url.pathname.startsWith(immutable) - ? 'public, immutable, max-age=31536000' - : 'no-cache'; - - return new Response(res.body, { - headers: { - // include original headers, minus cache-control which - // is overridden, and etag which is no longer useful - 'cache-control': cache_control, - 'content-type': res.headers.get('content-type'), - 'x-robots-tag': 'noindex' - } - }); - } +/** + * @param {Request} req + * @param {any} env + * @param {any} context + */ +async function fetch(req, env, context) { + await server.init({ env }); + + const url = new URL(req.url); + + // static assets + if (url.pathname.startsWith(app_path)) { + /** @type {Response} */ + const res = await get_asset_from_kv(req, env, context); + if (is_error(res.status)) return res; + + const cache_control = url.pathname.startsWith(immutable) + ? 'public, immutable, max-age=31536000' + : 'no-cache'; + + return new Response(res.body, { + headers: { + // include original headers, minus cache-control which + // is overridden, and etag which is no longer useful + 'cache-control': cache_control, + 'content-type': res.headers.get('content-type'), + 'x-robots-tag': 'noindex' + } + }); + } - let { pathname, search } = url; - try { - pathname = decodeURIComponent(pathname); - } catch { - // ignore invalid URI - } + let { pathname, search } = url; + try { + pathname = decodeURIComponent(pathname); + } catch { + // ignore invalid URI + } - const stripped_pathname = pathname.replace(/\/$/, ''); - - // prerendered pages and /static files - let is_static_asset = false; - const filename = stripped_pathname.slice(base_path.length + 1); - if (filename) { - is_static_asset = - manifest.assets.has(filename) || - manifest.assets.has(filename + '/index.html') || - filename in manifest._.server_assets || - filename + '/index.html' in manifest._.server_assets; - } + const stripped_pathname = pathname.replace(/\/$/, ''); + + // prerendered pages and /static files + let is_static_asset = false; + const filename = stripped_pathname.slice(base_path.length + 1); + if (filename) { + is_static_asset = + manifest.assets.has(filename) || + manifest.assets.has(filename + '/index.html') || + filename in manifest._.server_assets || + filename + '/index.html' in manifest._.server_assets; + } - let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/'; - - if ( - is_static_asset || - prerendered.has(pathname) || - pathname === version_file || - pathname.startsWith(immutable) - ) { - return get_asset_from_kv(req, env, context, (request, options) => { - if (prerendered.has(pathname)) { - url.pathname = '/' + prerendered.get(pathname).file; - return new Request(url.toString(), request); - } - - return mapRequestToAsset(request, options); - }); - } else if (location && prerendered.has(location)) { - if (search) location += search; - return new Response('', { - status: 308, - headers: { - location - } - }); - } + let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/'; + + if ( + is_static_asset || + prerendered.has(pathname) || + pathname === version_file || + pathname.startsWith(immutable) + ) { + return get_asset_from_kv(req, env, context, (request, options) => { + if (prerendered.has(pathname)) { + url.pathname = '/' + prerendered.get(pathname).file; + return new Request(url.toString(), request); + } - // dynamically-generated pages - return await server.respond(req, { - platform: { - env, - context, - // @ts-expect-error lib.dom is interfering with workers-types - caches, - // @ts-expect-error req is actually a Cloudflare request not a standard request - cf: req.cf - }, - getClientAddress() { - return req.headers.get('cf-connecting-ip'); + return mapRequestToAsset(request, options); + }); + } else if (location && prerendered.has(location)) { + if (search) location += search; + return new Response('', { + status: 308, + headers: { + location } }); } -}; + + // dynamically-generated pages + return await server.respond(req, { + platform: { + env, + context, + // @ts-expect-error lib.dom is interfering with workers-types + caches, + // @ts-expect-error req is actually a Cloudflare request not a standard request + cf: req.cf + }, + getClientAddress() { + return req.headers.get('cf-connecting-ip'); + } + }); +} + +export default 'prototype' in handlers && handlers.prototype instanceof WorkerEntrypoint + ? Object.defineProperty(handlers.prototype, 'fetch', { + value: fetch, + writable: true, + enumerable: false, + configurable: true + }) + : Object.defineProperty(handlers, 'fetch', { + value: fetch, + writable: true, + enumerable: true, + configurable: true + }); /** * @param {Request} req diff --git a/packages/adapter-cloudflare-workers/placeholders.d.ts b/packages/adapter-cloudflare-workers/placeholders.d.ts index 1de6e71b1561..d78b8bb9ce4c 100644 --- a/packages/adapter-cloudflare-workers/placeholders.d.ts +++ b/packages/adapter-cloudflare-workers/placeholders.d.ts @@ -17,8 +17,9 @@ declare module '__STATIC_CONTENT_MANIFEST' { declare module 'HANDLERS' { import { ExportedHandler } from '@cloudflare/workers-types'; + import { WorkerEntrypoint } from 'cloudflare:workers'; - const handlers: Omit; + const handlers: Omit | WorkerEntrypoint; export default handlers; }