From 30d1f4698029ea8681346758e10aba6d1bf10dd3 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 28 Nov 2024 11:38:01 +0000 Subject: [PATCH 01/23] feat: use the new Workers Static Assets feature from Cloudflare This changes the adapter to stop using the old Workers Sites (kv-asset-handler) approach. Instead, making use of the new Workers Static Assets feature, which is embedded into Cloudflare natively. Also this change removes the extra esbuild step that was being run inside the adapter, relying upon Wrangler to do the bundling. The extra esbuild step required a hardcoded list of Node.js compatible modules. This is no longer needed since Wrangler now manages all of that. - This version of the adapter requires Wrangler version 3.87.0 or later. Run `npm add -D wrangler@latest` (or similar) in your project to update Wrangler. - The user's Wrangler configuration (`wrangler.toml`) must be migrated from using Workers Sites to using Workers Assets. Previously a user's `wrangler.toml` might look like: ```toml name = "" account_id = "" compatibility_date = "2021-11-12" main = "./.cloudflare/worker.js" # Workers Sites configuration site.bucket = "./.cloudflare/public" ``` Change it to to look like: ```toml name = "" account_id = "" compatibility_date = "2021-11-12"` main = ".svelte-kit/cloudflare/server/index.js" # Workers Assets configuration assets = { directory = ".svelte-kit/cloudflare/client" } ``` - Workers Assets defaults to serving assets directly for a matching request, rather than routing it through the Worker code. The previous adapter would add custom headers to assets responses (such as `cache-control`, `content-type`, and `x-robots-tag`. Such direct asset responses no longer contain these headers - but the will include eTag headers that have proven (in Pages) to be an effective caching strategy for assets. If you wish to always run the Worker before every request then add `serve_directly = false` to the assets configuration section. For example: ```toml assets = { directory = ".svelte-kit/cloudflare/client", serve_directly = false } ``` --- .changeset/smart-owls-trade.md | 53 +++++ packages/adapter-cloudflare-workers/README.md | 2 +- .../files/_package.json | 9 - .../adapter-cloudflare-workers/files/entry.js | 45 +---- packages/adapter-cloudflare-workers/index.js | 142 +++----------- .../adapter-cloudflare-workers/package.json | 3 +- .../placeholders.d.ts | 5 - pnpm-lock.yaml | 183 +++++++++++++++++- 8 files changed, 260 insertions(+), 182 deletions(-) create mode 100644 .changeset/smart-owls-trade.md delete mode 100644 packages/adapter-cloudflare-workers/files/_package.json diff --git a/.changeset/smart-owls-trade.md b/.changeset/smart-owls-trade.md new file mode 100644 index 000000000000..576b467b6134 --- /dev/null +++ b/.changeset/smart-owls-trade.md @@ -0,0 +1,53 @@ +--- +'@sveltejs/adapter-cloudflare-workers': major +--- + +feat: use the new Workers Static Assets feature from Cloudflare + +This changes the adapter to stop using the old Workers Sites (kv-asset-handler) approach. +Instead, making use of the new Workers Static Assets feature, which is embedded into Cloudflare natively. + +Also this change removes the extra esbuild step that was being run inside the adapter, relying upon Wrangler to do the bundling. +The extra esbuild step required a hardcoded list of Node.js compatible modules. +This is no longer needed since Wrangler now manages all of that. + +## Breaking changes and migration + +- This version of the adapter requires Wrangler version 3.87.0 or later. + + Run `npm add -D wrangler@latest` (or similar) in your project to update Wrangler. +- The user's Wrangler configuration (`wrangler.toml`) must be migrated from using Workers Sites to using Workers Assets. + + Previously a user's `wrangler.toml` might look like: + + ```toml + name = "" + account_id = "" + compatibility_date = "2021-11-12" + main = "./.cloudflare/worker.js" + + # Workers Sites configuration + site.bucket = "./.cloudflare/public" + ``` + + Change it to to look like: + + ```toml + name = "" + account_id = "" + compatibility_date = "2021-11-12"` + main = ".svelte-kit/cloudflare/server/index.js" + + # Workers Assets configuration + assets = { directory = ".svelte-kit/cloudflare/client" } + ``` + +- Workers Assets defaults to serving assets directly for a matching request, rather than routing it through the Worker code. + + The previous adapter would add custom headers to assets responses (such as `cache-control`, `content-type`, and `x-robots-tag`. Such direct asset responses no longer contain these headers - but the will include eTag headers that have proven (in Pages) to be an effective caching strategy for assets. + + If you wish to always run the Worker before every request then add `serve_directly = false` to the assets configuration section. For example: + + ```toml + assets = { directory = ".svelte-kit/cloudflare/client", serve_directly = false } + ``` diff --git a/packages/adapter-cloudflare-workers/README.md b/packages/adapter-cloudflare-workers/README.md index a3805020a4aa..ecf995809db8 100644 --- a/packages/adapter-cloudflare-workers/README.md +++ b/packages/adapter-cloudflare-workers/README.md @@ -2,7 +2,7 @@ SvelteKit adapter that creates a Cloudflare Workers site using a function for dynamic server rendering. -**Requires [Wrangler v2](https://developers.cloudflare.com/workers/wrangler/get-started/).** Wrangler v1 is no longer supported. +**Requires [Wrangler v3 or later](https://developers.cloudflare.com/workers/wrangler/get-started/).**. ## Docs diff --git a/packages/adapter-cloudflare-workers/files/_package.json b/packages/adapter-cloudflare-workers/files/_package.json deleted file mode 100644 index bc4c8d4aabac..000000000000 --- a/packages/adapter-cloudflare-workers/files/_package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "private": true, - "version": "0.0.1", - "description": "Worker site generated by SvelteKit", - "main": "index.js", - "dependencies": { - "@cloudflare/kv-asset-handler": "~0.1.3" - } -} diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js index 5f022e5096b9..997eee746884 100644 --- a/packages/adapter-cloudflare-workers/files/entry.js +++ b/packages/adapter-cloudflare-workers/files/entry.js @@ -1,8 +1,5 @@ import { Server } from 'SERVER'; import { manifest, prerendered, base_path } from 'MANIFEST'; -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); const server = new Server(manifest); @@ -25,7 +22,7 @@ export default { // static assets if (url.pathname.startsWith(app_path)) { /** @type {Response} */ - const res = await get_asset_from_kv(req, env, context); + const res = await env.ASSETS.fetch(req); if (is_error(res.status)) return res; const cache_control = url.pathname.startsWith(immutable) @@ -65,20 +62,11 @@ export default { 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); - }); + if (prerendered.has(pathname)) { + url.pathname = '/' + prerendered.get(pathname).file; + return env.ASSETS.fetch(new Request(url.toString(), req)); + } else if (is_static_asset || pathname === version_file || pathname.startsWith(immutable)) { + return env.ASSETS.fetch(req); } else if (location && prerendered.has(location)) { if (search) location += search; return new Response('', { @@ -106,27 +94,6 @@ export default { } }; -/** - * @param {Request} req - * @param {any} env - * @param {any} context - */ -async function get_asset_from_kv(req, env, context, map = mapRequestToAsset) { - return await getAssetFromKV( - { - request: req, - waitUntil(promise) { - return context.waitUntil(promise); - } - }, - { - ASSET_NAMESPACE: env.__STATIC_CONTENT, - ASSET_MANIFEST: static_asset_manifest, - mapRequestToAsset: map - } - ); -} - /** * @param {number} status * @returns {boolean} diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index 8a4f9252d1ac..5fe79ad8a1dd 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -1,7 +1,5 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs'; import { posix, dirname } from 'node:path'; -import { execSync } from 'node:child_process'; -import esbuild from 'esbuild'; import toml from '@iarna/toml'; import { fileURLToPath } from 'node:url'; import { getPlatformProxy } from 'wrangler'; @@ -9,144 +7,58 @@ import { getPlatformProxy } from 'wrangler'; /** * @typedef {{ * main: string; - * site: { - * bucket: string; + * assets: { + * directory: string; + * binding: string; * } - * compatibility_flags?: string[]; * }} WranglerConfig */ -// list from https://developers.cloudflare.com/workers/runtime-apis/nodejs/ -const compatible_node_modules = [ - 'assert', - 'async_hooks', - 'buffer', - 'crypto', - 'diagnostics_channel', - 'events', - 'path', - 'process', - 'stream', - 'string_decoder', - 'util' -]; - /** @type {import('./index.js').default} */ export default function ({ config = 'wrangler.toml', platformProxy = {} } = {}) { return { name: '@sveltejs/adapter-cloudflare-workers', - async adapt(builder) { - const { main, site, compatibility_flags } = validate_config(builder, config); - + adapt(builder) { + const { main, assets } = validate_config(builder, config); const files = fileURLToPath(new URL('./files', import.meta.url).href); - const tmp = builder.getBuildDirectory('cloudflare-workers-tmp'); - - builder.rimraf(site.bucket); - builder.rimraf(dirname(main)); - - builder.log.info('Installing worker dependencies...'); - builder.copy(`${files}/_package.json`, `${tmp}/package.json`); - - // TODO would be cool if we could make this step unnecessary somehow - const stdout = execSync('npm install', { cwd: tmp }); - builder.log.info(stdout.toString()); + const outDir = dirname(main); + const relativePath = posix.relative(outDir, builder.getServerDirectory()); builder.log.minor('Generating worker...'); - const relativePath = posix.relative(tmp, builder.getServerDirectory()); - builder.copy(`${files}/entry.js`, `${tmp}/entry.js`, { + // Clear out old files + builder.rimraf(assets.directory); + builder.rimraf(outDir); + + // Create the entry-point for the Worker + builder.copy(`${files}/entry.js`, main, { replace: { SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js' + MANIFEST: './manifest.js', + ASSETS: assets.binding || 'ASSETS' } }); + // Create the manifest for the Worker let prerendered_entries = Array.from(builder.prerendered.pages.entries()); - if (builder.config.kit.paths.base) { prerendered_entries = prerendered_entries.map(([path, { file }]) => [ path, { file: `${builder.config.kit.paths.base}/${file}` } ]); } - writeFileSync( - `${tmp}/manifest.js`, + `${outDir}/manifest.js`, `export const manifest = ${builder.generateManifest({ relativePath })};\n\n` + `export const prerendered = new Map(${JSON.stringify(prerendered_entries)});\n\n` + `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n` ); - const external = ['__STATIC_CONTENT_MANIFEST', 'cloudflare:*']; - if (compatibility_flags && compatibility_flags.includes('nodejs_compat')) { - external.push(...compatible_node_modules.map((id) => `node:${id}`)); - } - - try { - const result = await esbuild.build({ - platform: 'browser', - // https://github.com/cloudflare/workers-sdk/blob/a12b2786ce745f24475174bcec994ad691e65b0f/packages/wrangler/src/deployment-bundle/bundle.ts#L35-L36 - conditions: ['workerd', 'worker', 'browser'], - sourcemap: 'linked', - target: 'es2022', - entryPoints: [`${tmp}/entry.js`], - outfile: main, - bundle: true, - external, - alias: Object.fromEntries(compatible_node_modules.map((id) => [id, `node:${id}`])), - format: 'esm', - loader: { - '.wasm': 'copy', - '.woff': 'copy', - '.woff2': 'copy', - '.ttf': 'copy', - '.eot': 'copy', - '.otf': 'copy' - }, - logLevel: 'silent' - }); - - 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 Cloudflare.`; - } - } - } - - 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' - }` - ); - } - builder.log.minor('Copying assets...'); - const bucket_dir = `${site.bucket}${builder.config.kit.paths.base}`; - builder.writeClient(bucket_dir); - builder.writePrerendered(bucket_dir); + const assets_dir = `${assets.directory}${builder.config.kit.paths.base}`; + builder.writeClient(assets_dir); + builder.writePrerendered(assets_dir); }, async emulate() { @@ -198,9 +110,9 @@ function validate_config(builder, config_file) { throw err; } - if (!wrangler_config.site?.bucket) { + if (!wrangler_config.assets?.directory) { throw new Error( - `You must specify site.bucket in ${config_file}. Consult https://developers.cloudflare.com/workers/platform/sites/configuration` + `You must specify assets.directory in ${config_file}. Consult https://developers.cloudflare.com/workers/platform/sites/configuration` ); } @@ -223,14 +135,10 @@ function validate_config(builder, config_file) { name = "" account_id = "" - - main = "./.cloudflare/worker.js" - site.bucket = "./.cloudflare/public" - + main = ".svelte-kit/cloudflare/server/index.js" + assets = { directory = ".svelte-kit/cloudflare/client" } build.command = "npm run build" - - compatibility_date = "2021-11-12" - workers_dev = true` + compatibility_date = "2021-11-12"` .replace(/^\t+/gm, '') .trim() ); diff --git a/packages/adapter-cloudflare-workers/package.json b/packages/adapter-cloudflare-workers/package.json index 3e82abd677d4..6e28203fcfc8 100644 --- a/packages/adapter-cloudflare-workers/package.json +++ b/packages/adapter-cloudflare-workers/package.json @@ -43,13 +43,12 @@ "esbuild": "^0.21.5" }, "devDependencies": { - "@cloudflare/kv-asset-handler": "^0.3.0", "@sveltejs/kit": "workspace:^", "@types/node": "^18.19.48", "typescript": "^5.3.3" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0", - "wrangler": "^3.28.4" + "wrangler": "^3.87.0" } } diff --git a/packages/adapter-cloudflare-workers/placeholders.d.ts b/packages/adapter-cloudflare-workers/placeholders.d.ts index 3877ad52f4a5..50b543eb1452 100644 --- a/packages/adapter-cloudflare-workers/placeholders.d.ts +++ b/packages/adapter-cloudflare-workers/placeholders.d.ts @@ -9,8 +9,3 @@ declare module 'MANIFEST' { export const prerendered: Map; export const base_path: string; } - -declare module '__STATIC_CONTENT_MANIFEST' { - const json: string; - export default json; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a33bda6a51ed..ede9012eda1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,12 +92,9 @@ importers: specifier: ^0.21.5 version: 0.21.5 wrangler: - specifier: ^3.28.4 - version: 3.63.1(@cloudflare/workers-types@4.20240405.0) + specifier: ^3.87.0 + version: 3.90.0(@cloudflare/workers-types@4.20240405.0) devDependencies: - '@cloudflare/kv-asset-handler': - specifier: ^0.3.0 - version: 0.3.4 '@sveltejs/kit': specifier: workspace:^ version: link:../kit @@ -1207,30 +1204,64 @@ packages: cpu: [x64] os: [darwin] + '@cloudflare/workerd-darwin-64@1.20241106.1': + resolution: {integrity: sha512-zxvaToi1m0qzAScrxFt7UvFVqU8DxrCO2CinM1yQkv5no7pA1HolpIrwZ0xOhR3ny64Is2s/J6BrRjpO5dM9Zw==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + '@cloudflare/workerd-darwin-arm64@1.20240701.0': resolution: {integrity: sha512-w80ZVAgfH4UwTz7fXZtk7KmS2FzlXniuQm4ku4+cIgRTilBAuKqjpOjwUCbx5g13Gqcm9NuiHce+IDGtobRTIQ==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] + '@cloudflare/workerd-darwin-arm64@1.20241106.1': + resolution: {integrity: sha512-j3dg/42D/bPgfNP3cRUBxF+4waCKO/5YKwXNj+lnVOwHxDu+ne5pFw9TIkKYcWTcwn0ZUkbNZNM5rhJqRn4xbg==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + '@cloudflare/workerd-linux-64@1.20240701.0': resolution: {integrity: sha512-UWLr/Anxwwe/25nGv451MNd2jhREmPt/ws17DJJqTLAx6JxwGWA15MeitAIzl0dbxRFAJa+0+R8ag2WR3F/D6g==} engines: {node: '>=16'} cpu: [x64] os: [linux] + '@cloudflare/workerd-linux-64@1.20241106.1': + resolution: {integrity: sha512-Ih+Ye8E1DMBXcKrJktGfGztFqHKaX1CeByqshmTbODnWKHt6O65ax3oTecUwyC0+abuyraOpAtdhHNpFMhUkmw==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + '@cloudflare/workerd-linux-arm64@1.20240701.0': resolution: {integrity: sha512-3kCnF9kYgov1ggpuWbgpXt4stPOIYtVmPCa7MO2xhhA0TWP6JDUHRUOsnmIgKrvDjXuXqlK16cdg3v+EWsaPJg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] + '@cloudflare/workerd-linux-arm64@1.20241106.1': + resolution: {integrity: sha512-mdQFPk4+14Yywn7n1xIzI+6olWM8Ybz10R7H3h+rk0XulMumCWUCy1CzIDauOx6GyIcSgKIibYMssVHZR30ObA==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + '@cloudflare/workerd-windows-64@1.20240701.0': resolution: {integrity: sha512-6IPGITRAeS67j3BH1rN4iwYWDt47SqJG7KlZJ5bB4UaNAia4mvMBSy/p2p4vA89bbXoDRjMtEvRu7Robu6O7hQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] + '@cloudflare/workerd-windows-64@1.20241106.1': + resolution: {integrity: sha512-4rtcss31E/Rb/PeFocZfr+B9i1MdrkhsTBWizh8siNR4KMmkslU2xs2wPaH1z8+ErxkOsHrKRa5EPLh5rIiFeg==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cloudflare/workers-shared@0.8.0': + resolution: {integrity: sha512-1OvFkNtslaMZAJsaocTmbACApgmWv55uLpNj50Pn2MGcxdAjpqykXJFQw5tKc+lGV9TDZh9oO3Rsk17IEQDzIg==} + engines: {node: '>=16.7.0'} + '@cloudflare/workers-types@4.20240405.0': resolution: {integrity: sha512-sEVOhyOgXUwfLkgHqbLZa/sfkSYrh7/zLmI6EZNibPaVPvAnAcItbNNl3SAlLyLKuwf8m4wAIAgu9meKWCvXjg==} @@ -2192,6 +2223,10 @@ packages: resolution: {integrity: sha512-mxIojEAQcuEvT/lyXq+jf/3cO/KoA6z4CeNDGGevTybECPOMFCnQy3OPahluUkbqgPNGw5Bi78UC7Po6Lhy+NA==} engines: {node: '>= 14.16.0'} + chokidar@4.0.1: + resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} + engines: {node: '>= 14.16.0'} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -2276,6 +2311,9 @@ packages: date-fns@3.6.0: resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + debug@4.3.5: resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} @@ -2767,6 +2805,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + itty-time@1.0.6: + resolution: {integrity: sha512-+P8IZaLLBtFv8hCkIjcymZOp4UJ+xW6bSlQsXGqrkmJh7vSiMFSlNne0mCYagEE0N7HDNR5jJBRxwN0oYv61Rw==} + js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -2938,6 +2979,11 @@ packages: engines: {node: '>=16.13'} hasBin: true + miniflare@3.20241106.1: + resolution: {integrity: sha512-dM3RBlJE8rUFxnqlPCaFCq0E7qQqEQvKbYX7W/APGCK+rLcyLmEBzC4GQR/niXdNM/oV6gdg9AA50ghnn2ALuw==} + engines: {node: '>=16.13'} + hasBin: true + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3049,6 +3095,9 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + ohash@1.1.4: + resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -3127,6 +3176,9 @@ packages: path-to-regexp@6.2.2: resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3635,8 +3687,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -3648,6 +3700,9 @@ packages: unenv-nightly@1.10.0-1717606461.a117952: resolution: {integrity: sha512-u3TfBX02WzbHTpaEfWEKwDijDSFAHcgXkayUZ+MVDrjhLFvgAJzFGTSTmwlEhwWi2exyRQey23ah9wELMM6etg==} + unenv-nightly@2.0.0-20241111-080453-894aa31: + resolution: {integrity: sha512-0W39QQOQ9VE8kVVUpGwEG+pZcsCXk5wqNG6rDPE6Gr+fiA69LR0qERM61hW5KCOkC1/ArCFrfCGjwHyyv/bI0Q==} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -3803,6 +3858,11 @@ packages: engines: {node: '>=16'} hasBin: true + workerd@1.20241106.1: + resolution: {integrity: sha512-1GdKl0kDw8rrirr/ThcK66Kbl4/jd4h8uHx5g7YHBrnenY5SX1UPuop2cnCzYUxlg55kPjzIqqYslz1muRFgFw==} + engines: {node: '>=16'} + hasBin: true + worktop@0.8.0-next.18: resolution: {integrity: sha512-+TvsA6VAVoMC3XDKR5MoC/qlLqDixEfOBysDEKnPIPou/NvoPWCAuXHXMsswwlvmEuvX56lQjvELLyLuzTKvRw==} engines: {node: '>=12'} @@ -3817,6 +3877,16 @@ packages: '@cloudflare/workers-types': optional: true + wrangler@3.90.0: + resolution: {integrity: sha512-E/6E9ORAl987+3kP8wDiE3L1lj9r4vQ32/dl5toIxIkSMssmPRQVdxqwgMxbxJrytbFNo8Eo6swgjd4y4nUaLg==} + engines: {node: '>=16.17.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20241106.0 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -4029,18 +4099,38 @@ snapshots: '@cloudflare/workerd-darwin-64@1.20240701.0': optional: true + '@cloudflare/workerd-darwin-64@1.20241106.1': + optional: true + '@cloudflare/workerd-darwin-arm64@1.20240701.0': optional: true + '@cloudflare/workerd-darwin-arm64@1.20241106.1': + optional: true + '@cloudflare/workerd-linux-64@1.20240701.0': optional: true + '@cloudflare/workerd-linux-64@1.20241106.1': + optional: true + '@cloudflare/workerd-linux-arm64@1.20240701.0': optional: true + '@cloudflare/workerd-linux-arm64@1.20241106.1': + optional: true + '@cloudflare/workerd-windows-64@1.20240701.0': optional: true + '@cloudflare/workerd-windows-64@1.20241106.1': + optional: true + + '@cloudflare/workers-shared@0.8.0': + dependencies: + mime: 3.0.0 + zod: 3.22.4 + '@cloudflare/workers-types@4.20240405.0': {} '@cspotcode/source-map-support@0.8.1': @@ -4938,6 +5028,10 @@ snapshots: dependencies: readdirp: 4.0.1 + chokidar@4.0.1: + dependencies: + readdirp: 4.0.1 + chownr@2.0.0: {} ci-info@3.9.0: {} @@ -5011,6 +5105,8 @@ snapshots: date-fns@3.6.0: {} + date-fns@4.1.0: {} + debug@4.3.5: dependencies: ms: 2.1.2 @@ -5544,6 +5640,8 @@ snapshots: isexe@2.0.0: {} + itty-time@1.0.6: {} + js-yaml@3.14.1: dependencies: argparse: 1.0.10 @@ -5701,6 +5799,25 @@ snapshots: - supports-color - utf-8-validate + miniflare@3.20241106.1: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + acorn: 8.12.1 + acorn-walk: 8.3.2 + capnp-ts: 0.7.0 + exit-hook: 2.2.1 + glob-to-regexp: 0.4.1 + stoppable: 1.1.0 + undici: 5.28.4 + workerd: 1.20241106.1 + ws: 8.18.0 + youch: 3.3.3 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -5787,6 +5904,8 @@ snapshots: object-assign@4.1.1: {} + ohash@1.1.4: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -5855,6 +5974,8 @@ snapshots: path-to-regexp@6.2.2: {} + path-to-regexp@6.3.0: {} + path-type@4.0.0: {} pathe@1.1.2: {} @@ -6350,7 +6471,7 @@ snapshots: typescript@5.6.3: {} - ufo@1.5.3: {} + ufo@1.5.4: {} undici-types@5.26.5: {} @@ -6365,7 +6486,14 @@ snapshots: mime: 3.0.0 node-fetch-native: 1.6.4 pathe: 1.1.2 - ufo: 1.5.3 + ufo: 1.5.4 + + unenv-nightly@2.0.0-20241111-080453-894aa31: + dependencies: + defu: 6.1.4 + ohash: 1.1.4 + pathe: 1.1.2 + ufo: 1.5.4 universalify@0.1.2: {} @@ -6505,6 +6633,14 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20240701.0 '@cloudflare/workerd-windows-64': 1.20240701.0 + workerd@1.20241106.1: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20241106.1 + '@cloudflare/workerd-darwin-arm64': 1.20241106.1 + '@cloudflare/workerd-linux-64': 1.20241106.1 + '@cloudflare/workerd-linux-arm64': 1.20241106.1 + '@cloudflare/workerd-windows-64': 1.20241106.1 + worktop@0.8.0-next.18: dependencies: mrmime: 2.0.0 @@ -6536,6 +6672,35 @@ snapshots: - supports-color - utf-8-validate + wrangler@3.90.0(@cloudflare/workers-types@4.20240405.0): + dependencies: + '@cloudflare/kv-asset-handler': 0.3.4 + '@cloudflare/workers-shared': 0.8.0 + '@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19) + '@esbuild-plugins/node-modules-polyfill': 0.2.2(esbuild@0.17.19) + blake3-wasm: 2.1.5 + chokidar: 4.0.1 + date-fns: 4.1.0 + esbuild: 0.17.19 + itty-time: 1.0.6 + miniflare: 3.20241106.1 + nanoid: 3.3.7 + path-to-regexp: 6.3.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + selfsigned: 2.4.1 + source-map: 0.6.1 + unenv: unenv-nightly@2.0.0-20241111-080453-894aa31 + workerd: 1.20241106.1 + xxhash-wasm: 1.0.2 + optionalDependencies: + '@cloudflare/workers-types': 4.20240405.0 + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + wrappy@1.0.2: {} ws@8.18.0: {} From b667ac76725f596211b2546261d1e7f7af867c45 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Tue, 21 Jan 2025 15:00:35 +0800 Subject: [PATCH 02/23] upgrade workers types --- packages/adapter-cloudflare-workers/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-cloudflare-workers/package.json b/packages/adapter-cloudflare-workers/package.json index 1e37d40577d7..1f94f2879f47 100644 --- a/packages/adapter-cloudflare-workers/package.json +++ b/packages/adapter-cloudflare-workers/package.json @@ -38,7 +38,7 @@ "check": "tsc --skipLibCheck" }, "dependencies": { - "@cloudflare/workers-types": "^4.20231121.0", + "@cloudflare/workers-types": "^4.20241218.0", "@iarna/toml": "^2.2.5", "esbuild": "^0.24.0" }, From 8a0cb79364e2980ff58f50bfb106b254f9aded0d Mon Sep 17 00:00:00 2001 From: "Jake(Jaehak Song)" Date: Thu, 6 Feb 2025 15:34:53 +0900 Subject: [PATCH 03/23] docs: apply review feedback --- .changeset/smart-owls-trade.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.changeset/smart-owls-trade.md b/.changeset/smart-owls-trade.md index 576b467b6134..02ab6916d31b 100644 --- a/.changeset/smart-owls-trade.md +++ b/.changeset/smart-owls-trade.md @@ -4,12 +4,9 @@ feat: use the new Workers Static Assets feature from Cloudflare -This changes the adapter to stop using the old Workers Sites (kv-asset-handler) approach. -Instead, making use of the new Workers Static Assets feature, which is embedded into Cloudflare natively. +This changes the adapter to stop using the old Workers Sites (kv-asset-handler) approach in favor of the new Workers Static Assets feature, which is embedded into Cloudflare natively. -Also this change removes the extra esbuild step that was being run inside the adapter, relying upon Wrangler to do the bundling. -The extra esbuild step required a hardcoded list of Node.js compatible modules. -This is no longer needed since Wrangler now manages all of that. +Also, this change removes the extra esbuild step that was being run inside the adapter, instead relying upon Wrangler to do the bundling. ## Breaking changes and migration @@ -35,7 +32,7 @@ This is no longer needed since Wrangler now manages all of that. ```toml name = "" account_id = "" - compatibility_date = "2021-11-12"` + compatibility_date = "2021-11-12" main = ".svelte-kit/cloudflare/server/index.js" # Workers Assets configuration @@ -44,7 +41,7 @@ This is no longer needed since Wrangler now manages all of that. - Workers Assets defaults to serving assets directly for a matching request, rather than routing it through the Worker code. - The previous adapter would add custom headers to assets responses (such as `cache-control`, `content-type`, and `x-robots-tag`. Such direct asset responses no longer contain these headers - but the will include eTag headers that have proven (in Pages) to be an effective caching strategy for assets. + The previous adapter would add custom headers to assets responses (such as `cache-control`, `content-type`, and `x-robots-tag`. Such direct asset responses no longer contain these headers — but they will include eTag headers that have proven (in Pages) to be an effective caching strategy for assets. If you wish to always run the Worker before every request then add `serve_directly = false` to the assets configuration section. For example: From 65bc106c22ce16ded6aa493823cc9439bb4e13f9 Mon Sep 17 00:00:00 2001 From: "Jake(Jaehak Song)" Date: Thu, 6 Feb 2025 15:36:14 +0900 Subject: [PATCH 04/23] refactor: fix naming to match convention --- packages/adapter-cloudflare-workers/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index ab98000a9fb1..c41221a5ea8e 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -22,19 +22,19 @@ export default function ({ config = 'wrangler.toml', platformProxy = {} } = {}) adapt(builder) { const { main, assets } = validate_config(builder, config); const files = fileURLToPath(new URL('./files', import.meta.url).href); - const outDir = dirname(main); - const relativePath = posix.relative(outDir, builder.getServerDirectory()); + const out_dir = dirname(main); + const relative_path = posix.relative(out_dir, builder.getServerDirectory()); builder.log.minor('Generating worker...'); // Clear out old files builder.rimraf(assets.directory); - builder.rimraf(outDir); + builder.rimraf(out_dir); // Create the entry-point for the Worker builder.copy(`${files}/entry.js`, main, { replace: { - SERVER: `${relativePath}/index.js`, + SERVER: `${relative_path}/index.js`, MANIFEST: './manifest.js', ASSETS: assets.binding || 'ASSETS' } @@ -49,8 +49,8 @@ export default function ({ config = 'wrangler.toml', platformProxy = {} } = {}) ]); } writeFileSync( - `${outDir}/manifest.js`, - `export const manifest = ${builder.generateManifest({ relativePath })};\n\n` + + `${out_dir}/manifest.js`, + `export const manifest = ${builder.generateManifest({ relativePath: relative_path })};\n\n` + `export const prerendered = new Map(${JSON.stringify(prerendered_entries)});\n\n` + `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n` ); From a824fafda85a1a742b37a09756342a20c255e973 Mon Sep 17 00:00:00 2001 From: "Jake(Jaehak Song)" Date: Thu, 6 Feb 2025 15:46:55 +0900 Subject: [PATCH 05/23] docs: simplify CHANGELOG for Workers Assets migration - Removed detailed examples to keep the entry concise. - Provided documentation links for reference. --- .changeset/smart-owls-trade.md | 35 ++-------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/.changeset/smart-owls-trade.md b/.changeset/smart-owls-trade.md index 02ab6916d31b..2da24134c88c 100644 --- a/.changeset/smart-owls-trade.md +++ b/.changeset/smart-owls-trade.md @@ -13,38 +13,7 @@ Also, this change removes the extra esbuild step that was being run inside the a - This version of the adapter requires Wrangler version 3.87.0 or later. Run `npm add -D wrangler@latest` (or similar) in your project to update Wrangler. -- The user's Wrangler configuration (`wrangler.toml`) must be migrated from using Workers Sites to using Workers Assets. - - Previously a user's `wrangler.toml` might look like: - - ```toml - name = "" - account_id = "" - compatibility_date = "2021-11-12" - main = "./.cloudflare/worker.js" - - # Workers Sites configuration - site.bucket = "./.cloudflare/public" - ``` - Change it to to look like: - - ```toml - name = "" - account_id = "" - compatibility_date = "2021-11-12" - main = ".svelte-kit/cloudflare/server/index.js" - - # Workers Assets configuration - assets = { directory = ".svelte-kit/cloudflare/client" } - ``` - -- Workers Assets defaults to serving assets directly for a matching request, rather than routing it through the Worker code. - - The previous adapter would add custom headers to assets responses (such as `cache-control`, `content-type`, and `x-robots-tag`. Such direct asset responses no longer contain these headers — but they will include eTag headers that have proven (in Pages) to be an effective caching strategy for assets. - - If you wish to always run the Worker before every request then add `serve_directly = false` to the assets configuration section. For example: +- The user's Wrangler configuration (`wrangler.toml`) must be migrated from using Workers Sites to using Workers Assets. - ```toml - assets = { directory = ".svelte-kit/cloudflare/client", serve_directly = false } - ``` +For reference, see the previous [Workers Sites documentation](https://developers.cloudflare.com/workers/configuration/sites/configuration/) and the new [Workers Assets documentation](NEW_DOC_URL). From 094cc0fcfe4a708ed9d51e2740511efc8f887d6d Mon Sep 17 00:00:00 2001 From: "Jake(Jaehak Song)" Date: Thu, 6 Feb 2025 16:16:18 +0900 Subject: [PATCH 06/23] docs: add migration guide for Workers Static Assets --- .../70-adapter-cloudflare-workers.md | 73 ++++++++++++++++++- 1 file changed, 69 insertions(+), 4 deletions(-) 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 ad820327463a..65a6dbbdaa00 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 @@ -4,7 +4,9 @@ title: Cloudflare Workers To deploy to [Cloudflare Workers](https://workers.cloudflare.com/), use [`adapter-cloudflare-workers`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare-workers). -> [!NOTE] Unless you have a specific reason to use `adapter-cloudflare-workers`, it's recommended that you use `adapter-cloudflare` instead. Both adapters have equivalent functionality, but Cloudflare Pages offers features like GitHub integration with automatic builds and deploys, preview deployments, instant rollback and so on. +> [!NOTE] Cloudflare is actively working to bridge the gaps between Workers and Pages and is providing ways to migrate Pages projects to Workers. Many features of Cloudflare Pages are now available in Workers, with Beta support for additional functionality. +> +> Both Workers and Pages remain valid options, and the best choice depends on your specific needs. Review the latest feature parity in the [compatibility matrix](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/) to determine the best fit for your project. ## Usage @@ -42,7 +44,7 @@ Preferences for the emulated `platform.env` local bindings. See the [getPlatform ## Basic Configuration -This adapter expects to find a [wrangler.toml/wrangler.json](https://developers.cloudflare.com/workers/platform/sites/configuration) file in the project root. It should look something like this: +This adapter expects to find a [wrangler.toml/wrangler.toml](https://developers.cloudflare.com/workers/platform/sites/configuration) file in the project root. It should look something like this: ```toml /// file: wrangler.toml @@ -50,12 +52,15 @@ name = "" account_id = "" main = "./.cloudflare/worker.js" -site.bucket = "./.cloudflare/public" build.command = "npm run build" -compatibility_date = "2021-11-12" +compatibility_date = "2025-01-01" workers_dev = true + +[assets] +directory = "./.cloudflare/public" +binding = "ASSETS" ``` `` can be anything. `` can be found by logging into your [Cloudflare dashboard](https://dash.cloudflare.com) and grabbing it from the end of the URL: @@ -138,3 +143,63 @@ When deploying to workers, the server generated by SvelteKit is bundled into a s ### Accessing the file system You can't use `fs` in Cloudflare Workers — you must [prerender](page-options#prerender) the routes in question. + + + +## Migrating from Workers Sites to Workers Static Assets + +Cloudflare no longer recommends using Workers Sites. See the official deprecation notice: +➡ [Workers Sites Documentation (Deprecated)](https://developers.cloudflare.com/workers/configuration/sites/configuration/) + +Users should migrate to Workers Static Assets for improved performance and maintainability. +➡ [Workers Static Assets Documentation](https://developers.cloudflare.com/workers/static-assets/) + +This guide provides key information for updating your configuration. + +### Update `wrangler.toml` + +Replace the **Workers Sites** configuration: + +```toml +# Previous Workers Sites configuration +name = "" +account_id = "" +compatibility_date = "2021-11-12" +main = "./.cloudflare/worker.js" + +site.bucket = "./.cloudflare/public" +``` + +With the Workers Static Assets configuration: + +```toml +# New Workers Static Assets configuration +name = "" +account_id = "" +compatibility_date = "2025-01-01" +main = "./.cloudflare/worker.js" + +[assets] +directory = "./.cloudflare/public" +binding = "ASSETS" +``` + +The `site.bucket` property is no longer needed and should be removed. + +### Asset Serving Behavior Change + +In Workers Sites (used with `adapter-cloudflare-workers`), assets were served through the Worker, allowing custom headers such as: +- `cache-control` +- `content-type` +- `x-robots-tag` + +However, **Workers Static Assets serves assets directly by default**, bypassing the Worker execution. As a result, these custom headers are no longer applied automatically. Instead, assets now rely on **ETag-based caching** for optimization. + +If you need to retain the previous behavior and ensure the Worker runs for every request (allowing custom headers), you can disable direct asset serving: + +```toml +[assets] +directory = "./.cloudflare/public" +binding = "ASSETS" +serve_directly = false +``` From 7943188a3d2b45de2ffdeba5bf9b1ee75c526ca8 Mon Sep 17 00:00:00 2001 From: "Jake(Jaehak Song)" Date: Thu, 6 Feb 2025 16:42:27 +0900 Subject: [PATCH 07/23] fix: lock file --- pnpm-lock.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94b08cf56527..21218e925a03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6569,7 +6569,7 @@ snapshots: eslint: 9.6.0 eslint-plugin-es-x: 7.8.0(eslint@9.6.0) get-tsconfig: 4.10.0 - globals: 15.14.0 + globals: 15.13.0 ignore: 5.3.2 minimatch: 9.0.5 semver: 7.6.3 @@ -7466,7 +7466,7 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.25 chokidar: 4.0.3 - fdir: 6.4.0(picomatch@4.0.2) + fdir: 6.4.2(picomatch@4.0.2) picocolors: 1.1.1 sade: 1.8.1 svelte: 5.2.9 From 59495bbdaac1957d3328e46d5a3177dd34d3b519 Mon Sep 17 00:00:00 2001 From: "Jake(Jaehak Song)" Date: Fri, 7 Feb 2025 11:34:18 +0900 Subject: [PATCH 08/23] docs: fix mistake --- .../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 65a6dbbdaa00..a2116876160f 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,7 +44,7 @@ Preferences for the emulated `platform.env` local bindings. See the [getPlatform ## Basic Configuration -This adapter expects to find a [wrangler.toml/wrangler.toml](https://developers.cloudflare.com/workers/platform/sites/configuration) file in the project root. It should look something like this: +This adapter expects to find a [wrangler.toml/wrangler.json](https://developers.cloudflare.com/workers/platform/sites/configuration) file in the project root. It should look something like this: ```toml /// file: wrangler.toml From b27836172652ff1c236b0c0c2aa522e9bc638049 Mon Sep 17 00:00:00 2001 From: "Jake(Jaehak Song)" Date: Fri, 7 Feb 2025 11:35:35 +0900 Subject: [PATCH 09/23] docs: update Wrangler version requirement to v3.87.0+ --- packages/adapter-cloudflare-workers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-cloudflare-workers/README.md b/packages/adapter-cloudflare-workers/README.md index ecf995809db8..0d3b7eed6561 100644 --- a/packages/adapter-cloudflare-workers/README.md +++ b/packages/adapter-cloudflare-workers/README.md @@ -2,7 +2,7 @@ SvelteKit adapter that creates a Cloudflare Workers site using a function for dynamic server rendering. -**Requires [Wrangler v3 or later](https://developers.cloudflare.com/workers/wrangler/get-started/).**. +**Requires [Wrangler v3.87.0 or later](https://developers.cloudflare.com/workers/wrangler/get-started/).**. ## Docs From 6838afad7a64846f6dda44ed4299a69c64f7f781 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 20 Mar 2025 12:39:05 +0800 Subject: [PATCH 10/23] cleanup and parity with cloudflare pages adapter --- .changeset/green-students-lay.md | 5 + .changeset/smart-owls-trade.md | 16 +-- .../60-adapter-cloudflare.md | 10 +- .../70-adapter-cloudflare-workers.md | 100 ++--------------- .../adapter-cloudflare-workers/.gitignore | 3 +- packages/adapter-cloudflare-workers/README.md | 4 +- .../adapter-cloudflare-workers/ambient.d.ts | 5 +- .../adapter-cloudflare-workers/files/entry.js | 103 ------------------ packages/adapter-cloudflare-workers/index.js | 59 ++++++---- .../adapter-cloudflare-workers/package.json | 7 +- .../adapter-cloudflare-workers/src/worker.js | 88 +++++++++++++++ .../adapter-cloudflare-workers/tsconfig.json | 6 +- packages/adapter-cloudflare/README.md | 2 +- packages/adapter-cloudflare/ambient.d.ts | 5 +- packages/adapter-cloudflare/package.json | 3 +- packages/adapter-cloudflare/src/worker.js | 27 +++-- packages/adapter-cloudflare/tsconfig.json | 5 +- pnpm-lock.yaml | 25 ++--- 18 files changed, 195 insertions(+), 278 deletions(-) create mode 100644 .changeset/green-students-lay.md delete mode 100644 packages/adapter-cloudflare-workers/files/entry.js create mode 100644 packages/adapter-cloudflare-workers/src/worker.js diff --git a/.changeset/green-students-lay.md b/.changeset/green-students-lay.md new file mode 100644 index 000000000000..cbd3f456f863 --- /dev/null +++ b/.changeset/green-students-lay.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare-workers': major +--- + +feat: remove esbuild step diff --git a/.changeset/smart-owls-trade.md b/.changeset/smart-owls-trade.md index 2da24134c88c..ec9ca1b94e74 100644 --- a/.changeset/smart-owls-trade.md +++ b/.changeset/smart-owls-trade.md @@ -2,18 +2,4 @@ '@sveltejs/adapter-cloudflare-workers': major --- -feat: use the new Workers Static Assets feature from Cloudflare - -This changes the adapter to stop using the old Workers Sites (kv-asset-handler) approach in favor of the new Workers Static Assets feature, which is embedded into Cloudflare natively. - -Also, this change removes the extra esbuild step that was being run inside the adapter, instead relying upon Wrangler to do the bundling. - -## Breaking changes and migration - -- This version of the adapter requires Wrangler version 3.87.0 or later. - - Run `npm add -D wrangler@latest` (or similar) in your project to update Wrangler. - -- The user's Wrangler configuration (`wrangler.toml`) must be migrated from using Workers Sites to using Workers Assets. - -For reference, see the previous [Workers Sites documentation](https://developers.cloudflare.com/workers/configuration/sites/configuration/) and the new [Workers Assets documentation](NEW_DOC_URL). +feat: use Workers Static Assets instead of Workers Sites diff --git a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md index d603c4943b8a..0e0442920afa 100644 --- a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md +++ b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md @@ -4,13 +4,15 @@ title: Cloudflare Pages To deploy to [Cloudflare Pages](https://pages.cloudflare.com/), use [`adapter-cloudflare`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare). -This adapter will be installed by default when you use [`adapter-auto`](adapter-auto). If you plan on staying with Cloudflare Pages, you can switch from [`adapter-auto`](adapter-auto) to using this adapter directly so that `event.platform` is emulated during local development, type declarations are automatically applied, and the ability to set Cloudflare-specific options is provided. +This adapter will be installed by default when you use [`adapter-auto`](adapter-auto), but adding it to your project allows you to specify Cloudflare Pages-specific options. + +> [!NOTE] Unless you have a specific reason to use `adapter-cloudflare`, it's recommended that you use [`adapter-cloudflare-workers`](adapter-cloudflare-workers) instead since Cloudflare plans to deprecate Cloudflare Pages in favour of Cloudflare Workers. Refer to the [compatibility matrix](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/) for more information. ## Comparisons -- `adapter-cloudflare` – supports all SvelteKit features; builds for [Cloudflare Pages](https://blog.cloudflare.com/cloudflare-pages-goes-full-stack/) +- `adapter-cloudflare` – supports all SvelteKit features; builds for Cloudflare Pages - `adapter-cloudflare-workers` – supports all SvelteKit features; builds for Cloudflare Workers -- `adapter-static` – only produces client-side static assets; compatible with Cloudflare Pages +- `adapter-static` – only produces client-side static assets; compatible with Cloudflare Pages and Cloudflare Workers ## Usage @@ -62,7 +64,7 @@ Preferences for the emulated `platform.env` local bindings. See the [getPlatform Please follow the [Get Started Guide](https://developers.cloudflare.com/pages/get-started/) for Cloudflare Pages to begin. -When configuring your project settings, you must use the following settings: +If you're using the [Git integration](https://developers.cloudflare.com/pages/get-started/git-integration/), your build settings should look like this: - **Framework preset** – SvelteKit - **Build command** – `npm run build` or `vite build` 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 fbc80bffbff3..20122b26e594 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 @@ -4,10 +4,6 @@ title: Cloudflare Workers To deploy to [Cloudflare Workers](https://workers.cloudflare.com/), use [`adapter-cloudflare-workers`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare-workers). -> [!NOTE] Cloudflare is actively working to bridge the gaps between Workers and Pages and is providing ways to migrate Pages projects to Workers. Many features of Cloudflare Pages are now available in Workers, with Beta support for additional functionality. -> -> Both Workers and Pages remain valid options, and the best choice depends on your specific needs. Review the latest feature parity in the [compatibility matrix](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/) to determine the best fit for your project. - ## Usage Install with `npm i -D @sveltejs/adapter-cloudflare-workers`, then add the adapter to your `svelte.config.js`: @@ -44,42 +40,18 @@ This adapter expects to find a [Wrangler configuration file](https://developers. /// file: wrangler.jsonc { "name": "", - "account_id": "", - "main": "./.cloudflare/worker.js", - "site": { - "bucket": "./.cloudflare/public" - }, - "build": { - "command": "npm run build" - }, + "main": ".svelte-kit/cloudflare/_worker.js", "compatibility_date": "2025-01-01", - "assets": { - "directory": "./.cloudflare/public", - "binding": "ASSETS" - } + "assets": { + "binding": "ASSETS", + "directory": ".svelte-kit/cloudflare", + } } ``` -`` can be anything. `` can be found by running `wrangler whoami` using the Wrangler CLI tool or by logging into your [Cloudflare dashboard](https://dash.cloudflare.com) and grabbing it from the end of the URL: - -``` -https://dash.cloudflare.com//home -``` - -> [!NOTE] You should add the `.cloudflare` directory (or whichever directories you specified for `main` and `site.bucket`) and the `.wrangler` directory to your `.gitignore`. - -You will need to install [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/) and log in, if you haven't already: - -```sh -npm i -D wrangler -wrangler login -``` - -Then, you can build your app and deploy it: +## Deployment -```sh -wrangler deploy -``` +Please follow the [framework guide](https://developers.cloudflare.com/workers/frameworks/framework-guides/svelte/) for Cloudflare Workers to begin. ## Runtime APIs @@ -141,62 +113,6 @@ When deploying your application, the server generated by SvelteKit is bundled in You can't use `fs` in Cloudflare Workers — you must [prerender](page-options#prerender) the routes in question. - - ## Migrating from Workers Sites to Workers Static Assets -Cloudflare no longer recommends using Workers Sites. See the official deprecation notice: -➡ [Workers Sites Documentation (Deprecated)](https://developers.cloudflare.com/workers/configuration/sites/configuration/) - -Users should migrate to Workers Static Assets for improved performance and maintainability. -➡ [Workers Static Assets Documentation](https://developers.cloudflare.com/workers/static-assets/) - -This guide provides key information for updating your configuration. - -### Update `wrangler.toml` - -Replace the **Workers Sites** configuration: - -```toml -# Previous Workers Sites configuration -name = "" -account_id = "" -compatibility_date = "2021-11-12" -main = "./.cloudflare/worker.js" - -site.bucket = "./.cloudflare/public" -``` - -With the Workers Static Assets configuration: - -```toml -# New Workers Static Assets configuration -name = "" -account_id = "" -compatibility_date = "2025-01-01" -main = "./.cloudflare/worker.js" - -[assets] -directory = "./.cloudflare/public" -binding = "ASSETS" -``` - -The `site.bucket` property is no longer needed and should be removed. - -### Asset Serving Behavior Change - -In Workers Sites (used with `adapter-cloudflare-workers`), assets were served through the Worker, allowing custom headers such as: -- `cache-control` -- `content-type` -- `x-robots-tag` - -However, **Workers Static Assets serves assets directly by default**, bypassing the Worker execution. As a result, these custom headers are no longer applied automatically. Instead, assets now rely on **ETag-based caching** for optimization. - -If you need to retain the previous behavior and ensure the Worker runs for every request (allowing custom headers), you can disable direct asset serving: - -```toml -[assets] -directory = "./.cloudflare/public" -binding = "ASSETS" -serve_directly = false -``` +Cloudflare no longer recommends using [Workers Sites](https://developers.cloudflare.com/workers/configuration/sites/configuration/) and instead recommends using [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/). To migrate, remove all `site` configuration settings from your Wrangler configuration file. diff --git a/packages/adapter-cloudflare-workers/.gitignore b/packages/adapter-cloudflare-workers/.gitignore index 9daa8247da45..2c8afebd8eda 100644 --- a/packages/adapter-cloudflare-workers/.gitignore +++ b/packages/adapter-cloudflare-workers/.gitignore @@ -1,2 +1 @@ -.DS_Store -node_modules +/files diff --git a/packages/adapter-cloudflare-workers/README.md b/packages/adapter-cloudflare-workers/README.md index 0d3b7eed6561..02b6207dfd30 100644 --- a/packages/adapter-cloudflare-workers/README.md +++ b/packages/adapter-cloudflare-workers/README.md @@ -1,8 +1,8 @@ # adapter-cloudflare-workers -SvelteKit adapter that creates a Cloudflare Workers site using a function for dynamic server rendering. +[Adapter](https://svelte.dev/docs/kit/building-your-app) for building SvelteKit applications on [Cloudflare Workers](https://developers.cloudflare.com/workers/) with [static assets](https://developers.cloudflare.com/workers/static-assets/). -**Requires [Wrangler v3.87.0 or later](https://developers.cloudflare.com/workers/wrangler/get-started/).**. +**Requires [Wrangler v3.91.0 or later](https://developers.cloudflare.com/workers/wrangler/get-started/).**. ## Docs diff --git a/packages/adapter-cloudflare-workers/ambient.d.ts b/packages/adapter-cloudflare-workers/ambient.d.ts index fbd01afd6557..fa404bc98404 100644 --- a/packages/adapter-cloudflare-workers/ambient.d.ts +++ b/packages/adapter-cloudflare-workers/ambient.d.ts @@ -1,13 +1,14 @@ -import { CacheStorage, IncomingRequestCfProperties } from '@cloudflare/workers-types'; +import { CacheStorage, CfProperties } from '@cloudflare/workers-types'; declare global { namespace App { export interface Platform { + env: unknown; context: { waitUntil(promise: Promise): void; }; caches: CacheStorage; - cf?: IncomingRequestCfProperties; + cf?: CfProperties; } } } diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js deleted file mode 100644 index 997eee746884..000000000000 --- a/packages/adapter-cloudflare-workers/files/entry.js +++ /dev/null @@ -1,103 +0,0 @@ -import { Server } from 'SERVER'; -import { manifest, prerendered, base_path } from 'MANIFEST'; - -const server = new Server(manifest); - -const app_path = `/${manifest.appPath}`; - -const immutable = `${app_path}/immutable/`; -const version_file = `${app_path}/version.json`; - -export default { - /** - * @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 env.ASSETS.fetch(req); - 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 - } - - 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 (prerendered.has(pathname)) { - url.pathname = '/' + prerendered.get(pathname).file; - return env.ASSETS.fetch(new Request(url.toString(), req)); - } else if (is_static_asset || pathname === version_file || pathname.startsWith(immutable)) { - return env.ASSETS.fetch(req); - } 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'); - } - }); - } -}; - -/** - * @param {number} status - * @returns {boolean} - */ -function is_error(status) { - return status > 399; -} diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index 44edb4d862e1..fb8f6172d614 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -1,5 +1,4 @@ import { writeFileSync } from 'node:fs'; -import { posix, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { getPlatformProxy, unstable_readConfig } from 'wrangler'; @@ -11,23 +10,22 @@ export default function ({ config, platformProxy = {} } = {}) { adapt(builder) { const { main, assets } = validate_config(builder, config); const files = fileURLToPath(new URL('./files', import.meta.url).href); - const out_dir = dirname(main); - const relative_path = posix.relative(out_dir, builder.getServerDirectory()); builder.log.minor('Generating worker...'); // Clear out old files builder.rimraf(assets.directory); - builder.rimraf(out_dir); + builder.rimraf(main); // Create the entry-point for the Worker - builder.copy(`${files}/entry.js`, main, { + builder.copy(`${files}/worker.js`, `${main}/index.js`, { replace: { - SERVER: `${relative_path}/index.js`, + SERVER: `${main}/server/index.js`, MANIFEST: './manifest.js', ASSETS: assets.binding || 'ASSETS' } }); + builder.writeServer(`${main}/server`); // Create the manifest for the Worker let prerendered_entries = Array.from(builder.prerendered.pages.entries()); @@ -38,8 +36,8 @@ export default function ({ config, platformProxy = {} } = {}) { ]); } writeFileSync( - `${out_dir}/manifest.js`, - `export const manifest = ${builder.generateManifest({ relativePath: relative_path })};\n\n` + + `${main}/manifest.js`, + `export const manifest = ${builder.generateManifest({ relativePath: './server' })};\n\n` + `export const prerendered = new Map(${JSON.stringify(prerendered_entries)});\n\n` + `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n` ); @@ -48,6 +46,7 @@ export default function ({ config, platformProxy = {} } = {}) { const assets_dir = `${assets.directory}${builder.config.kit.paths.base}`; builder.writeClient(assets_dir); builder.writePrerendered(assets_dir); + writeFileSync(`${assets.directory}/.assetsignore`, generate_assetsignore(), { flag: 'a' }); }, emulate() { @@ -96,39 +95,51 @@ function validate_config(builder, config_file = undefined) { const wrangler_config = unstable_readConfig({ config: config_file }); if (!wrangler_config.configPath) { builder.log.error( - 'Consult https://developers.cloudflare.com/workers/platform/sites/configuration on how to setup your site' + 'Consult https://developers.cloudflare.com/workers/static-assets/ on how to setup your configuration' ); - builder.log( + builder.log.error( ` Sample wrangler.jsonc: { - "name": "", - "account_id": "", - "main": "./.cloudflare/worker.js", - "site": { - "bucket": "./.cloudflare/public" - }, - "build": { - "command": "npm run build" - }, - "compatibility_date": "2021-11-12" + "name": "", + "main": ".svelte-kit/cloudflare/_worker.js", + "compatibility_date": "2025-01-01", + "assets": { + "binding": "ASSETS", + "directory": ".svelte-kit/cloudflare" + } } `.trim() ); throw new Error('Missing a Wrangler configuration file'); } - if (!wrangler_config.site?.bucket) { + if (!wrangler_config.main) { throw new Error( - `You must specify the \`site.bucket\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/platform/sites/configuration` + `You must specify the \`main\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/static-assets/` ); } - if (!wrangler_config.main) { + if (!wrangler_config.assets?.directory) { throw new Error( - `You must specify the \`main\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/platform/sites/configuration` + `You must specify the \`assets.directory\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/static-assets/binding/` + ); + } + + if (!wrangler_config.assets?.binding) { + throw new Error( + `You must specify the \`assets.binding\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/static-assets/binding/` ); } return wrangler_config; } + +// this list comes from https://developers.cloudflare.com/workers/static-assets/binding/#ignoring-assets +function generate_assetsignore() { + return ` +_worker.js +_headers +_redirects +`; +} diff --git a/packages/adapter-cloudflare-workers/package.json b/packages/adapter-cloudflare-workers/package.json index c1397370fb76..6366641ec733 100644 --- a/packages/adapter-cloudflare-workers/package.json +++ b/packages/adapter-cloudflare-workers/package.json @@ -33,17 +33,20 @@ "index.d.ts" ], "scripts": { + "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" + "check": "tsc --skipLibCheck", + "prepublishOnly": "pnpm build" }, "dependencies": { "@cloudflare/workers-types": "^4.20250312.0", - "esbuild": "^0.24.0" + "worktop": "0.8.0-next.18" }, "devDependencies": { "@sveltejs/kit": "workspace:^", "@types/node": "^18.19.48", + "esbuild": "^0.24.0", "typescript": "^5.3.3" }, "peerDependencies": { diff --git a/packages/adapter-cloudflare-workers/src/worker.js b/packages/adapter-cloudflare-workers/src/worker.js new file mode 100644 index 000000000000..fed7e3a73895 --- /dev/null +++ b/packages/adapter-cloudflare-workers/src/worker.js @@ -0,0 +1,88 @@ +import { Server } from 'SERVER'; +import { manifest, prerendered, base_path } from 'MANIFEST'; +import * as Cache from 'worktop/cfw.cache'; + +const server = new Server(manifest); + +const app_path = `/${manifest.appPath}`; + +const immutable = `${app_path}/immutable/`; +const version_file = `${app_path}/version.json`; + +export default { + /** + * @param {Request} req + * @param {{ ASSETS: { fetch: typeof fetch } }} env + * @param {ExecutionContext} context + * @returns {Promise} + */ + async fetch(req, env, context) { + await server.init({ + // @ts-expect-error env contains environment variables and bindings + env + }); + + // skip cache if "cache-control: no-cache" in request + let pragma = req.headers.get('cache-control') || ''; + let res = !pragma.includes('no-cache') && (await Cache.lookup(req)); + if (res) return res; + + let { pathname, search } = new URL(req.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; + } + + let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/'; + + if ( + is_static_asset || + prerendered.has(pathname) || + pathname === version_file || + pathname.startsWith(immutable) + ) { + res = await env.ASSETS.fetch(req); + } else if (location && prerendered.has(location)) { + if (search) location += search; + res = new Response('', { + status: 308, + headers: { + location + } + }); + } else { + // dynamically-generated pages + res = await server.respond(req, { + platform: { + env, + context, + // @ts-expect-error webworker types from worktop are not compatible with Cloudflare Workers types + caches, + cf: req.cf + }, + getClientAddress() { + return req.headers.get('cf-connecting-ip'); + } + }); + } + + // write to `Cache` only if response is not an error, + // let `Cache.save` handle the Cache-Control and Vary headers + pragma = res.headers.get('cache-control') || ''; + return pragma && res.status < 400 ? Cache.save(req, res, context) : res; + } +}; diff --git a/packages/adapter-cloudflare-workers/tsconfig.json b/packages/adapter-cloudflare-workers/tsconfig.json index e4cdc5abd1b7..afc9ffc9de90 100644 --- a/packages/adapter-cloudflare-workers/tsconfig.json +++ b/packages/adapter-cloudflare-workers/tsconfig.json @@ -7,11 +7,13 @@ "target": "es2022", "module": "node16", "moduleResolution": "node16", - "allowSyntheticDefaultImports": true, "baseUrl": ".", "paths": { "@sveltejs/kit": ["../kit/types/index"] - } + }, + // taken from the Cloudflare Workers TypeScript template https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/templates/hello-world/ts/tsconfig.json + "lib": ["es2021"], + "types": ["@cloudflare/workers-types"] }, "include": ["**/*.js", "internal.d.ts"] } diff --git a/packages/adapter-cloudflare/README.md b/packages/adapter-cloudflare/README.md index fa00adbf1c94..ed61c6e65dad 100644 --- a/packages/adapter-cloudflare/README.md +++ b/packages/adapter-cloudflare/README.md @@ -1,6 +1,6 @@ # adapter-cloudflare -[Adapter](https://svelte.dev/docs/kit/building-your-app) for building SvelteKit applications on [Cloudflare Pages](https://developers.cloudflare.com/pages/) with [Workers integration](https://developers.cloudflare.com/pages/platform/functions). +[Adapter](https://svelte.dev/docs/kit/building-your-app) for building SvelteKit applications on [Cloudflare Pages](https://developers.cloudflare.com/pages/) with [Workers integration](https://developers.cloudflare.com/pages/functions/). ## Docs diff --git a/packages/adapter-cloudflare/ambient.d.ts b/packages/adapter-cloudflare/ambient.d.ts index fbd01afd6557..fa404bc98404 100644 --- a/packages/adapter-cloudflare/ambient.d.ts +++ b/packages/adapter-cloudflare/ambient.d.ts @@ -1,13 +1,14 @@ -import { CacheStorage, IncomingRequestCfProperties } from '@cloudflare/workers-types'; +import { CacheStorage, CfProperties } from '@cloudflare/workers-types'; declare global { namespace App { export interface Platform { + env: unknown; context: { waitUntil(promise: Promise): void; }; caches: CacheStorage; - cf?: IncomingRequestCfProperties; + cf?: CfProperties; } } } diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json index e2412209c324..17c271c36302 100644 --- a/packages/adapter-cloudflare/package.json +++ b/packages/adapter-cloudflare/package.json @@ -41,13 +41,12 @@ }, "dependencies": { "@cloudflare/workers-types": "^4.20250312.0", - "esbuild": "^0.24.0", "worktop": "0.8.0-next.18" }, "devDependencies": { "@sveltejs/kit": "workspace:^", "@types/node": "^18.19.48", - "@types/ws": "^8.5.10", + "esbuild": "^0.24.0", "typescript": "^5.3.3" }, "peerDependencies": { diff --git a/packages/adapter-cloudflare/src/worker.js b/packages/adapter-cloudflare/src/worker.js index c3c27a0b041f..fed7e3a73895 100644 --- a/packages/adapter-cloudflare/src/worker.js +++ b/packages/adapter-cloudflare/src/worker.js @@ -9,11 +9,19 @@ const app_path = `/${manifest.appPath}`; const immutable = `${app_path}/immutable/`; const version_file = `${app_path}/version.json`; -/** @type {import('worktop/cfw').Module.Worker<{ ASSETS: import('worktop/cfw.durable').Durable.Object }>} */ -const worker = { +export default { + /** + * @param {Request} req + * @param {{ ASSETS: { fetch: typeof fetch } }} env + * @param {ExecutionContext} context + * @returns {Promise} + */ async fetch(req, env, context) { - // @ts-ignore - await server.init({ env }); + await server.init({ + // @ts-expect-error env contains environment variables and bindings + env + }); + // skip cache if "cache-control: no-cache" in request let pragma = req.headers.get('cache-control') || ''; let res = !pragma.includes('no-cache') && (await Cache.lookup(req)); @@ -59,8 +67,13 @@ const worker = { } else { // dynamically-generated pages res = await server.respond(req, { - // @ts-ignore - platform: { env, context, caches, cf: req.cf }, + platform: { + env, + context, + // @ts-expect-error webworker types from worktop are not compatible with Cloudflare Workers types + caches, + cf: req.cf + }, getClientAddress() { return req.headers.get('cf-connecting-ip'); } @@ -73,5 +86,3 @@ const worker = { return pragma && res.status < 400 ? Cache.save(req, res, context) : res; } }; - -export default worker; diff --git a/packages/adapter-cloudflare/tsconfig.json b/packages/adapter-cloudflare/tsconfig.json index b258035a3555..75fb3c2b4665 100644 --- a/packages/adapter-cloudflare/tsconfig.json +++ b/packages/adapter-cloudflare/tsconfig.json @@ -10,7 +10,10 @@ "baseUrl": ".", "paths": { "@sveltejs/kit": ["../kit/types/index"] - } + }, + // taken from the Cloudflare Workers TypeScript template https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/templates/hello-world/ts/tsconfig.json + "lib": ["es2021"], + "types": ["@cloudflare/workers-types"] }, "include": ["index.js", "internal.d.ts", "src/worker.js"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de65397f4088..fd1d64d488d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,9 +60,6 @@ importers: '@cloudflare/workers-types': specifier: ^4.20250312.0 version: 4.20250312.0 - esbuild: - specifier: ^0.24.0 - version: 0.24.2 worktop: specifier: 0.8.0-next.18 version: 0.8.0-next.18 @@ -76,9 +73,9 @@ importers: '@types/node': specifier: ^18.19.48 version: 18.19.50 - '@types/ws': - specifier: ^8.5.10 - version: 8.5.10 + esbuild: + specifier: ^0.24.0 + version: 0.24.2 typescript: specifier: ^5.3.3 version: 5.6.3 @@ -88,9 +85,9 @@ importers: '@cloudflare/workers-types': specifier: ^4.20250312.0 version: 4.20250312.0 - esbuild: - specifier: ^0.24.0 - version: 0.24.2 + worktop: + specifier: 0.8.0-next.18 + version: 0.8.0-next.18 wrangler: specifier: ^3.91.0 || ^4.0.0 version: 4.0.0(@cloudflare/workers-types@4.20250312.0) @@ -101,6 +98,9 @@ importers: '@types/node': specifier: ^18.19.48 version: 18.19.50 + esbuild: + specifier: ^0.24.0 + version: 0.24.2 typescript: specifier: ^5.3.3 version: 5.6.3 @@ -1885,9 +1885,6 @@ packages: '@types/set-cookie-parser@2.4.7': resolution: {integrity: sha512-+ge/loa0oTozxip6zmhRIk8Z/boU51wl9Q6QdLZcokIGMzY5lFXYy/x7Htj2HTC6/KZP1hUbZ1ekx8DYXICvWg==} - '@types/ws@8.5.10': - resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} - '@typescript-eslint/eslint-plugin@8.26.0': resolution: {integrity: sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4196,10 +4193,6 @@ snapshots: dependencies: '@types/node': 18.19.50 - '@types/ws@8.5.10': - dependencies: - '@types/node': 18.19.50 - '@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.6.0)(typescript@5.6.3))(eslint@9.6.0)(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.12.1 From 13128a9c1228b812a57284156d7ea34976745ed4 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 20 Mar 2025 12:40:15 +0800 Subject: [PATCH 11/23] move comparisons to cloudflare workers page --- .../docs/25-build-and-deploy/60-adapter-cloudflare.md | 6 ------ .../25-build-and-deploy/70-adapter-cloudflare-workers.md | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md index 0e0442920afa..5b2815237e93 100644 --- a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md +++ b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md @@ -8,12 +8,6 @@ This adapter will be installed by default when you use [`adapter-auto`](adapter- > [!NOTE] Unless you have a specific reason to use `adapter-cloudflare`, it's recommended that you use [`adapter-cloudflare-workers`](adapter-cloudflare-workers) instead since Cloudflare plans to deprecate Cloudflare Pages in favour of Cloudflare Workers. Refer to the [compatibility matrix](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/) for more information. -## Comparisons - -- `adapter-cloudflare` – supports all SvelteKit features; builds for Cloudflare Pages -- `adapter-cloudflare-workers` – supports all SvelteKit features; builds for Cloudflare Workers -- `adapter-static` – only produces client-side static assets; compatible with Cloudflare Pages and Cloudflare Workers - ## Usage Install with `npm i -D @sveltejs/adapter-cloudflare`, then add the adapter to your `svelte.config.js`: 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 20122b26e594..711ecaec4812 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 @@ -4,6 +4,12 @@ title: Cloudflare Workers To deploy to [Cloudflare Workers](https://workers.cloudflare.com/), use [`adapter-cloudflare-workers`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare-workers). +## Comparisons + +- `adapter-cloudflare-workers` – supports all SvelteKit features; builds for Cloudflare Workers +- `adapter-cloudflare` – supports all SvelteKit features; builds for Cloudflare Pages +- `adapter-static` – only produces client-side static assets; compatible with Cloudflare Pages and Cloudflare Workers + ## Usage Install with `npm i -D @sveltejs/adapter-cloudflare-workers`, then add the adapter to your `svelte.config.js`: From 4d952a3b419238013f889efea8bfaa412fe72a22 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 20 Mar 2025 12:49:02 +0800 Subject: [PATCH 12/23] changesets --- .changeset/new-kings-talk.md | 5 +++++ .changeset/weak-chairs-think.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/new-kings-talk.md create mode 100644 .changeset/weak-chairs-think.md diff --git a/.changeset/new-kings-talk.md b/.changeset/new-kings-talk.md new file mode 100644 index 000000000000..8827bee988af --- /dev/null +++ b/.changeset/new-kings-talk.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': patch +--- + +fix: change `esbuild` to a dev dependency diff --git a/.changeset/weak-chairs-think.md b/.changeset/weak-chairs-think.md new file mode 100644 index 000000000000..0fc95fa48b0b --- /dev/null +++ b/.changeset/weak-chairs-think.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare-workers': minor +--- + +feat: optimize cache usage with `worktop` From cb73dd8b7850808c693bdb11efff9e1fa443a2fb Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 20 Mar 2025 15:55:40 +0800 Subject: [PATCH 13/23] fix lint --- packages/adapter-cloudflare-workers/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-cloudflare-workers/tsconfig.json b/packages/adapter-cloudflare-workers/tsconfig.json index afc9ffc9de90..75fb3c2b4665 100644 --- a/packages/adapter-cloudflare-workers/tsconfig.json +++ b/packages/adapter-cloudflare-workers/tsconfig.json @@ -15,5 +15,5 @@ "lib": ["es2021"], "types": ["@cloudflare/workers-types"] }, - "include": ["**/*.js", "internal.d.ts"] + "include": ["index.js", "internal.d.ts", "src/worker.js"] } From ccb02423086212741267199ad7073daf473c8203 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 20 Mar 2025 15:56:39 +0800 Subject: [PATCH 14/23] serve immutable assets from worker with cache control --- .../adapter-cloudflare-workers/src/worker.js | 101 ++++++++++-------- packages/adapter-cloudflare/index.js | 1 - packages/adapter-cloudflare/src/worker.js | 101 ++++++++++-------- 3 files changed, 116 insertions(+), 87 deletions(-) diff --git a/packages/adapter-cloudflare-workers/src/worker.js b/packages/adapter-cloudflare-workers/src/worker.js index fed7e3a73895..ae435b57a2d9 100644 --- a/packages/adapter-cloudflare-workers/src/worker.js +++ b/packages/adapter-cloudflare-workers/src/worker.js @@ -7,7 +7,6 @@ const server = new Server(manifest); const app_path = `/${manifest.appPath}`; const immutable = `${app_path}/immutable/`; -const version_file = `${app_path}/version.json`; export default { /** @@ -34,55 +33,71 @@ export default { // 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; - } + // immutable assets and version.json + if (pathname.startsWith(app_path)) { + res = await env.ASSETS.fetch(req); + if (is_error(res.status)) return res; - let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/'; + const cache_control = pathname.startsWith(immutable) + ? 'public, immutable, max-age=31536000' + : 'no-cache'; - if ( - is_static_asset || - prerendered.has(pathname) || - pathname === version_file || - pathname.startsWith(immutable) - ) { - res = await env.ASSETS.fetch(req); - } else if (location && prerendered.has(location)) { - if (search) location += search; - res = new Response('', { - status: 308, - headers: { - location - } - }); + res.headers.set('cache-control', cache_control); } else { - // dynamically-generated pages - res = await server.respond(req, { - platform: { - env, - context, - // @ts-expect-error webworker types from worktop are not compatible with Cloudflare Workers types - caches, - cf: req.cf - }, - getClientAddress() { - return req.headers.get('cf-connecting-ip'); - } - }); + const stripped_pathname = pathname.replace(/\/$/, ''); + + // /static files, the service worker, and Vite imported server assets + 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)) { + res = await env.ASSETS.fetch(req); + } else if (location && prerendered.has(location)) { + // trailing slash redirect for prerendered pages + if (search) location += search; + res = new Response('', { + status: 308, + headers: { + location + } + }); + } else { + // dynamically-generated pages + res = await server.respond(req, { + platform: { + env, + context, + // @ts-expect-error webworker types from worktop are not compatible with Cloudflare Workers types + caches, + cf: req.cf + }, + getClientAddress() { + return req.headers.get('cf-connecting-ip'); + } + }); + } } // write to `Cache` only if response is not an error, // let `Cache.save` handle the Cache-Control and Vary headers pragma = res.headers.get('cache-control') || ''; - return pragma && res.status < 400 ? Cache.save(req, res, context) : res; + return pragma && !is_error(res.status) ? Cache.save(req, res, context) : res; } }; + +/** + * @param {number} status + * @returns {boolean} + */ +function is_error(status) { + return status > 399; +} diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index ceac64d92a2a..5efedbd8a121 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -171,7 +171,6 @@ function generate_headers(app_dir) { return ` # === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === /${app_dir}/* - X-Robots-Tag: noindex Cache-Control: no-cache /${app_dir}/immutable/* ! Cache-Control diff --git a/packages/adapter-cloudflare/src/worker.js b/packages/adapter-cloudflare/src/worker.js index fed7e3a73895..ae435b57a2d9 100644 --- a/packages/adapter-cloudflare/src/worker.js +++ b/packages/adapter-cloudflare/src/worker.js @@ -7,7 +7,6 @@ const server = new Server(manifest); const app_path = `/${manifest.appPath}`; const immutable = `${app_path}/immutable/`; -const version_file = `${app_path}/version.json`; export default { /** @@ -34,55 +33,71 @@ export default { // 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; - } + // immutable assets and version.json + if (pathname.startsWith(app_path)) { + res = await env.ASSETS.fetch(req); + if (is_error(res.status)) return res; - let location = pathname.at(-1) === '/' ? stripped_pathname : pathname + '/'; + const cache_control = pathname.startsWith(immutable) + ? 'public, immutable, max-age=31536000' + : 'no-cache'; - if ( - is_static_asset || - prerendered.has(pathname) || - pathname === version_file || - pathname.startsWith(immutable) - ) { - res = await env.ASSETS.fetch(req); - } else if (location && prerendered.has(location)) { - if (search) location += search; - res = new Response('', { - status: 308, - headers: { - location - } - }); + res.headers.set('cache-control', cache_control); } else { - // dynamically-generated pages - res = await server.respond(req, { - platform: { - env, - context, - // @ts-expect-error webworker types from worktop are not compatible with Cloudflare Workers types - caches, - cf: req.cf - }, - getClientAddress() { - return req.headers.get('cf-connecting-ip'); - } - }); + const stripped_pathname = pathname.replace(/\/$/, ''); + + // /static files, the service worker, and Vite imported server assets + 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)) { + res = await env.ASSETS.fetch(req); + } else if (location && prerendered.has(location)) { + // trailing slash redirect for prerendered pages + if (search) location += search; + res = new Response('', { + status: 308, + headers: { + location + } + }); + } else { + // dynamically-generated pages + res = await server.respond(req, { + platform: { + env, + context, + // @ts-expect-error webworker types from worktop are not compatible with Cloudflare Workers types + caches, + cf: req.cf + }, + getClientAddress() { + return req.headers.get('cf-connecting-ip'); + } + }); + } } // write to `Cache` only if response is not an error, // let `Cache.save` handle the Cache-Control and Vary headers pragma = res.headers.get('cache-control') || ''; - return pragma && res.status < 400 ? Cache.save(req, res, context) : res; + return pragma && !is_error(res.status) ? Cache.save(req, res, context) : res; } }; + +/** + * @param {number} status + * @returns {boolean} + */ +function is_error(status) { + return status > 399; +} From 071f62f0a9b492f394f89430d77ae81c6509dc4d Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 20 Mar 2025 16:00:35 +0800 Subject: [PATCH 15/23] changesets --- .changeset/hot-tigers-occur.md | 6 ++++++ .changeset/ninety-fans-end.md | 5 +++++ 2 files changed, 11 insertions(+) create mode 100644 .changeset/hot-tigers-occur.md create mode 100644 .changeset/ninety-fans-end.md diff --git a/.changeset/hot-tigers-occur.md b/.changeset/hot-tigers-occur.md new file mode 100644 index 000000000000..8d4d935a4f80 --- /dev/null +++ b/.changeset/hot-tigers-occur.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-cloudflare-workers': patch +'@sveltejs/adapter-cloudflare': patch +--- + +fix: remove `x-robots-tag: noindex` header for `_app/*` files diff --git a/.changeset/ninety-fans-end.md b/.changeset/ninety-fans-end.md new file mode 100644 index 000000000000..b617cc09d02f --- /dev/null +++ b/.changeset/ninety-fans-end.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-cloudflare': patch +--- + +fix: serve files from `_app/*` from the worker if the `_app/*` route was not excluded From 5bd2e394f4c52330dbdd66cb45420bf950f0d353 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 20 Mar 2025 16:24:22 +0800 Subject: [PATCH 16/23] add static headers and redirects --- packages/adapter-cloudflare-workers/index.js | 35 ++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index fb8f6172d614..5a1528019305 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -47,6 +47,14 @@ export default function ({ config, platformProxy = {} } = {}) { builder.writeClient(assets_dir); builder.writePrerendered(assets_dir); writeFileSync(`${assets.directory}/.assetsignore`, generate_assetsignore(), { flag: 'a' }); + + writeFileSync(`${assets.directory}/_headers`, generate_headers(builder.getAppPath()), { flag: 'a' }); + + if (builder.prerendered.redirects.size > 0) { + writeFileSync(`${assets.directory}/_redirects`, generate_redirects(builder.prerendered.redirects), { + flag: 'a' + }); + } }, emulate() { @@ -135,6 +143,33 @@ Sample wrangler.jsonc: return wrangler_config; } +/** @param {string} app_dir */ +function generate_headers(app_dir) { + return ` +# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === +/${app_dir}/* + Cache-Control: no-cache +/${app_dir}/immutable/* + ! Cache-Control + Cache-Control: public, immutable, max-age=31536000 +# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === +`.trimEnd(); +} + +/** @param {Map} redirects */ +function generate_redirects(redirects) { + const rules = Array.from( + redirects.entries(), + ([path, redirect]) => `${path} ${redirect.location} ${redirect.status}` + ).join('\n'); + + return ` +# === START AUTOGENERATED SVELTE PRERENDERED REDIRECTS === +${rules} +# === END AUTOGENERATED SVELTE PRERENDERED REDIRECTS === +`.trimEnd(); +} + // this list comes from https://developers.cloudflare.com/workers/static-assets/binding/#ignoring-assets function generate_assetsignore() { return ` From 94b7b730ed8bfaf74af429a0cae2a0ef48fe2476 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Thu, 20 Mar 2025 17:33:20 +0800 Subject: [PATCH 17/23] add support for _headers and _redirects --- .../70-adapter-cloudflare-workers.md | 4 +++ packages/adapter-cloudflare-workers/index.js | 28 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) 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 711ecaec4812..e89c1a327286 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 @@ -98,6 +98,10 @@ Cloudflare Workers specific values in the `platform` property are emulated durin For testing the build, you should use [Wrangler](https://developers.cloudflare.com/workers/wrangler/) **version 3**. Once you have built your site, run `wrangler dev`. +## Notes + +The [`_headers`](https://developers.cloudflare.com/pages/configuration/headers/) and [`_redirects`](https://developers.cloudflare.com/pages/configuration/redirects/) files specific to Cloudflare Pages can be used for static asset responses (like images) by putting them into the project root folder. + ## Troubleshooting ### Node.js compatibility diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index 5a1528019305..b4bfea7d44d3 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -1,4 +1,4 @@ -import { writeFileSync } from 'node:fs'; +import { copyFileSync, existsSync, writeFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { getPlatformProxy, unstable_readConfig } from 'wrangler'; @@ -8,6 +8,18 @@ export default function ({ config, platformProxy = {} } = {}) { name: '@sveltejs/adapter-cloudflare-workers', adapt(builder) { + if (existsSync(`${builder.config.kit.files.assets}/_headers`)) { + throw new Error( + `The _headers file should be placed in the project root rather than the ${builder.config.kit.files.assets} directory` + ); + } + + if (existsSync(`${builder.config.kit.files.assets}/_redirects`)) { + throw new Error( + `The _redirects file should be placed in the project root rather than the ${builder.config.kit.files.assets} directory` + ); + } + const { main, assets } = validate_config(builder, config); const files = fileURLToPath(new URL('./files', import.meta.url).href); @@ -48,10 +60,20 @@ export default function ({ config, platformProxy = {} } = {}) { builder.writePrerendered(assets_dir); writeFileSync(`${assets.directory}/.assetsignore`, generate_assetsignore(), { flag: 'a' }); - writeFileSync(`${assets.directory}/_headers`, generate_headers(builder.getAppPath()), { flag: 'a' }); + const headers_file = `${assets.directory}/_headers`; + if (existsSync('_headers')) { + copyFileSync('_headers', headers_file); + } + writeFileSync(headers_file, generate_headers(builder.getAppPath()), { + flag: 'a' + }); + const redirects_file = `${assets.directory}/_redirects`; + if (existsSync('_redirects')) { + copyFileSync('_redirects', redirects_file); + } if (builder.prerendered.redirects.size > 0) { - writeFileSync(`${assets.directory}/_redirects`, generate_redirects(builder.prerendered.redirects), { + writeFileSync(redirects_file, generate_redirects(builder.prerendered.redirects), { flag: 'a' }); } From 909c2e7f7b452b81f4165ea81db3c1367981f234 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Fri, 21 Mar 2025 16:04:56 +0800 Subject: [PATCH 18/23] specify value of assets.binding config --- packages/adapter-cloudflare-workers/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index b4bfea7d44d3..257d1ef47a47 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -156,9 +156,9 @@ Sample wrangler.jsonc: ); } - if (!wrangler_config.assets?.binding) { + if (wrangler_config.assets?.binding !== 'ASSETS') { throw new Error( - `You must specify the \`assets.binding\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/static-assets/binding/` + `You must set the \`assets.binding\` key to 'ASSETS' in ${wrangler_config.configPath}.` ); } From 2b08bd36b0d1d2fe891e2ec365ec8459f46e6492 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Fri, 21 Mar 2025 16:23:09 +0800 Subject: [PATCH 19/23] add migration example --- .../60-adapter-cloudflare.md | 4 +-- .../70-adapter-cloudflare-workers.md | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md index e1edd8a8a58b..e663035c41ab 100644 --- a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md +++ b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md @@ -4,7 +4,7 @@ title: Cloudflare Pages To deploy to [Cloudflare Pages](https://pages.cloudflare.com/), use [`adapter-cloudflare`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare). -This adapter will be installed by default when you use [`adapter-auto`](adapter-auto), but adding it to your project allows you to specify Cloudflare Pages-specific options. +This adapter will be installed by default when you use [`adapter-auto`](adapter-auto). If you plan on staying with Cloudflare Pages, you can switch from [`adapter-auto`](adapter-auto) to using this adapter directly so that `event.platform` is emulated during local development, type declarations are automatically applied, and the ability to set Cloudflare-specific options is provided. > [!NOTE] Unless you have a specific reason to use `adapter-cloudflare`, it's recommended that you use [`adapter-cloudflare-workers`](adapter-cloudflare-workers) instead since Cloudflare plans to deprecate Cloudflare Pages in favour of Cloudflare Workers. Refer to the [compatibility matrix](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/) for more information. @@ -121,7 +121,7 @@ You may wish to refer to [Cloudflare's documentation for deploying a SvelteKit s If you would like to enable [Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/), you can add the `nodejs_compat` compatibility flag to your Wrangler configuration file: -```jsonc +```json /// file: wrangler.jsonc { "compatibility_flags": ["nodejs_compat"] 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 e89c1a327286..d0333738f87b 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 @@ -42,7 +42,7 @@ Preferences for the emulated `platform.env` local bindings. See the [getPlatform This adapter expects to find a [Wrangler configuration file](https://developers.cloudflare.com/workers/configuration/sites/configuration/) in the project root. It should look something like this: -```jsonc +```json /// file: wrangler.jsonc { "name": "", @@ -108,7 +108,7 @@ The [`_headers`](https://developers.cloudflare.com/pages/configuration/headers/) If you would like to enable [Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/), you can add the `nodejs_compat` compatibility flag to your Wrangler configuration file: -```jsonc +```json /// file: wrangler.jsonc { "compatibility_flags": ["nodejs_compat"] @@ -125,4 +125,24 @@ You can't use `fs` in Cloudflare Workers — you must [prerender](page-options#p ## Migrating from Workers Sites to Workers Static Assets -Cloudflare no longer recommends using [Workers Sites](https://developers.cloudflare.com/workers/configuration/sites/configuration/) and instead recommends using [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/). To migrate, remove all `site` configuration settings from your Wrangler configuration file. +Cloudflare no longer recommends using [Workers Sites](https://developers.cloudflare.com/workers/configuration/sites/configuration/) and instead recommends using [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/). To migrate, remove all `site` configuration settings from your Wrangler configuration file and add the `assets.directory` and `assets.binding` configuration settings: + +```toml +/// file: wrangler.toml +---site.bucket = ".cloudflare/public"--- ++++assets.directory = ".cloudflare/public" +assets.binding = "ASSETS"+++ +``` + +```json +/// file: wrangler.jsonc +{ +--- "site": { + "bucket": ".cloudflare/public" + },--- ++++ "assets": { + "directory": ".cloudflare/public", + "binding": "ASSETS" + }+++ +} +``` From 906c110eb13f2d67d06779231c00ae9b102419ae Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Fri, 21 Mar 2025 16:49:44 +0800 Subject: [PATCH 20/23] error if site key is in wrangler config --- .../25-build-and-deploy/60-adapter-cloudflare.md | 4 ++-- .../70-adapter-cloudflare-workers.md | 14 +++++++++----- packages/adapter-cloudflare-workers/index.js | 6 ++++++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md index e663035c41ab..72fd22a1eb3a 100644 --- a/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md +++ b/documentation/docs/25-build-and-deploy/60-adapter-cloudflare.md @@ -97,7 +97,7 @@ declare global { export {}; ``` -### Testing Locally +### Testing locally Cloudflare Workers specific values in the `platform` property are emulated during dev and preview modes. Local [bindings](https://developers.cloudflare.com/pages/functions/bindings/) are created based on your [Wrangler configuration file](https://developers.cloudflare.com/pages/functions/wrangler-configuration/#local-development) and are used to populate `platform.env` during development and preview. Use the adapter config [`platformProxy` option](#Options-platformProxy) to change your preferences for the bindings. @@ -121,7 +121,7 @@ You may wish to refer to [Cloudflare's documentation for deploying a SvelteKit s If you would like to enable [Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/), you can add the `nodejs_compat` compatibility flag to your Wrangler configuration file: -```json +```jsonc /// file: wrangler.jsonc { "compatibility_flags": ["nodejs_compat"] 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 d0333738f87b..551a5f1d27a8 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 @@ -38,11 +38,11 @@ Path to your [Wrangler configuration file](https://developers.cloudflare.com/wor Preferences for the emulated `platform.env` local bindings. See the [getPlatformProxy](https://developers.cloudflare.com/workers/wrangler/api/#parameters-1) Wrangler API documentation for a full list of options. -## Basic Configuration +## Basic configuration This adapter expects to find a [Wrangler configuration file](https://developers.cloudflare.com/workers/configuration/sites/configuration/) in the project root. It should look something like this: -```json +```jsonc /// file: wrangler.jsonc { "name": "", @@ -92,7 +92,7 @@ declare global { export {}; ``` -### Testing Locally +### Testing locally Cloudflare Workers specific values in the `platform` property are emulated during dev and preview modes. Local [bindings](https://developers.cloudflare.com/workers/wrangler/configuration/#bindings) are created based on your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/) and are used to populate `platform.env` during development and preview. Use the adapter config [`platformProxy` option](#Options-platformProxy) to change your preferences for the bindings. @@ -108,7 +108,7 @@ The [`_headers`](https://developers.cloudflare.com/pages/configuration/headers/) If you would like to enable [Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/), you can add the `nodejs_compat` compatibility flag to your Wrangler configuration file: -```json +```jsonc /// file: wrangler.jsonc { "compatibility_flags": ["nodejs_compat"] @@ -127,6 +127,8 @@ You can't use `fs` in Cloudflare Workers — you must [prerender](page-options#p Cloudflare no longer recommends using [Workers Sites](https://developers.cloudflare.com/workers/configuration/sites/configuration/) and instead recommends using [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/). To migrate, remove all `site` configuration settings from your Wrangler configuration file and add the `assets.directory` and `assets.binding` configuration settings: +### wrangler.toml + ```toml /// file: wrangler.toml ---site.bucket = ".cloudflare/public"--- @@ -134,7 +136,9 @@ Cloudflare no longer recommends using [Workers Sites](https://developers.cloudfl assets.binding = "ASSETS"+++ ``` -```json +### wrangler.jsonc + +```jsonc /// file: wrangler.jsonc { --- "site": { diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index 257d1ef47a47..c44bcac311e4 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -150,6 +150,12 @@ Sample wrangler.jsonc: ); } + if (wrangler_config.site) { + throw new Error( + `You must remove all \`site\` keys in ${wrangler_config.configPath}. Consult https://svelte.dev/docs/kit/adapter-cloudflare-workers#Migrating-from-Workers-Sites-to-Workers-Static-Assets` + ); + } + if (!wrangler_config.assets?.directory) { throw new Error( `You must specify the \`assets.directory\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/static-assets/binding/` From 26fbbbfec9de9acb1a3e20465030e3744420d076 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Mon, 24 Mar 2025 12:14:24 +0800 Subject: [PATCH 21/23] revert removing x-robots-tag: noindex from headers --- .changeset/hot-tigers-occur.md | 6 ------ packages/adapter-cloudflare-workers/index.js | 1 + packages/adapter-cloudflare-workers/src/worker.js | 1 + packages/adapter-cloudflare/index.js | 1 + packages/adapter-cloudflare/src/worker.js | 1 + 5 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 .changeset/hot-tigers-occur.md diff --git a/.changeset/hot-tigers-occur.md b/.changeset/hot-tigers-occur.md deleted file mode 100644 index 8d4d935a4f80..000000000000 --- a/.changeset/hot-tigers-occur.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@sveltejs/adapter-cloudflare-workers': patch -'@sveltejs/adapter-cloudflare': patch ---- - -fix: remove `x-robots-tag: noindex` header for `_app/*` files diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index c44bcac311e4..5bd9bd758f7b 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -176,6 +176,7 @@ function generate_headers(app_dir) { return ` # === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === /${app_dir}/* + X-Robots-Tag: noindex Cache-Control: no-cache /${app_dir}/immutable/* ! Cache-Control diff --git a/packages/adapter-cloudflare-workers/src/worker.js b/packages/adapter-cloudflare-workers/src/worker.js index ae435b57a2d9..a92bfa4d4684 100644 --- a/packages/adapter-cloudflare-workers/src/worker.js +++ b/packages/adapter-cloudflare-workers/src/worker.js @@ -43,6 +43,7 @@ export default { : 'no-cache'; res.headers.set('cache-control', cache_control); + res.headers.set('x-robots-tag', 'noindex'); } else { const stripped_pathname = pathname.replace(/\/$/, ''); diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index b749557e9888..70fde497aa08 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -189,6 +189,7 @@ function generate_headers(app_dir) { return ` # === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === /${app_dir}/* + X-Robots-Tag: noindex Cache-Control: no-cache /${app_dir}/immutable/* ! Cache-Control diff --git a/packages/adapter-cloudflare/src/worker.js b/packages/adapter-cloudflare/src/worker.js index ae435b57a2d9..a92bfa4d4684 100644 --- a/packages/adapter-cloudflare/src/worker.js +++ b/packages/adapter-cloudflare/src/worker.js @@ -43,6 +43,7 @@ export default { : 'no-cache'; res.headers.set('cache-control', cache_control); + res.headers.set('x-robots-tag', 'noindex'); } else { const stripped_pathname = pathname.replace(/\/$/, ''); From 68a214ad969c3d95e913675e3d2fe55b4f2a9d19 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Mon, 24 Mar 2025 13:16:47 +0800 Subject: [PATCH 22/23] copy to _worker.js file instead of dir --- packages/adapter-cloudflare-workers/index.js | 41 ++++++++++--------- .../adapter-cloudflare-workers/internal.d.ts | 2 +- packages/adapter-cloudflare/index.js | 38 +++++++++-------- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js index 5bd9bd758f7b..78e7e704cd8a 100644 --- a/packages/adapter-cloudflare-workers/index.js +++ b/packages/adapter-cloudflare-workers/index.js @@ -1,4 +1,5 @@ import { copyFileSync, existsSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { getPlatformProxy, unstable_readConfig } from 'wrangler'; @@ -22,44 +23,41 @@ export default function ({ config, platformProxy = {} } = {}) { const { main, assets } = validate_config(builder, config); const files = fileURLToPath(new URL('./files', import.meta.url).href); - - builder.log.minor('Generating worker...'); + const tmp = builder.getBuildDirectory('cloudflare-tmp'); // Clear out old files builder.rimraf(assets.directory); builder.rimraf(main); + builder.mkdirp(tmp); + + builder.log.minor('Generating worker...'); + // Create the entry-point for the Worker - builder.copy(`${files}/worker.js`, `${main}/index.js`, { + const relative_path = path.posix.relative(main, builder.getServerDirectory()); + builder.copy(`${files}/worker.js`, main, { replace: { - SERVER: `${main}/server/index.js`, - MANIFEST: './manifest.js', + SERVER: `${relative_path}/index.js`, + MANIFEST: `${path.posix.relative(main, tmp)}/manifest.js`, ASSETS: assets.binding || 'ASSETS' } }); - builder.writeServer(`${main}/server`); // Create the manifest for the Worker - let prerendered_entries = Array.from(builder.prerendered.pages.entries()); - if (builder.config.kit.paths.base) { - prerendered_entries = prerendered_entries.map(([path, { file }]) => [ - path, - { file: `${builder.config.kit.paths.base}/${file}` } - ]); - } writeFileSync( - `${main}/manifest.js`, - `export const manifest = ${builder.generateManifest({ relativePath: './server' })};\n\n` + - `export const prerendered = new Map(${JSON.stringify(prerendered_entries)});\n\n` + + `${tmp}/manifest.js`, + `export const manifest = ${builder.generateManifest({ relativePath: relative_path })};\n\n` + + `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n\n` + `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n` ); + // client assets and prerendered pages builder.log.minor('Copying assets...'); const assets_dir = `${assets.directory}${builder.config.kit.paths.base}`; builder.writeClient(assets_dir); builder.writePrerendered(assets_dir); - writeFileSync(`${assets.directory}/.assetsignore`, generate_assetsignore(), { flag: 'a' }); + // _headers const headers_file = `${assets.directory}/_headers`; if (existsSync('_headers')) { copyFileSync('_headers', headers_file); @@ -68,6 +66,7 @@ export default function ({ config, platformProxy = {} } = {}) { flag: 'a' }); + // _redirects const redirects_file = `${assets.directory}/_redirects`; if (existsSync('_redirects')) { copyFileSync('_redirects', redirects_file); @@ -77,6 +76,8 @@ export default function ({ config, platformProxy = {} } = {}) { flag: 'a' }); } + + writeFileSync(`${assets.directory}/.assetsignore`, generate_assetsignore(), { flag: 'a' }); }, emulate() { @@ -158,13 +159,13 @@ Sample wrangler.jsonc: if (!wrangler_config.assets?.directory) { throw new Error( - `You must specify the \`assets.directory\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/static-assets/binding/` + `You must specify the \`assets.directory\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/static-assets/binding/#directory` ); } - if (wrangler_config.assets?.binding !== 'ASSETS') { + if (!wrangler_config.assets?.binding) { throw new Error( - `You must set the \`assets.binding\` key to 'ASSETS' in ${wrangler_config.configPath}.` + `You must specify the \`assets.binding\` key in ${wrangler_config.configPath}. Consult https://developers.cloudflare.com/workers/static-assets/binding/#binding` ); } diff --git a/packages/adapter-cloudflare-workers/internal.d.ts b/packages/adapter-cloudflare-workers/internal.d.ts index 50b543eb1452..3a390e5cf2a0 100644 --- a/packages/adapter-cloudflare-workers/internal.d.ts +++ b/packages/adapter-cloudflare-workers/internal.d.ts @@ -6,6 +6,6 @@ declare module 'MANIFEST' { import { SSRManifest } from '@sveltejs/kit'; export const manifest: SSRManifest; - export const prerendered: Map; + export const prerendered: Set; export const base_path: string; } diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 70fde497aa08..f1448124fd80 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -28,12 +28,12 @@ export default function (options = {}) { const files = fileURLToPath(new URL('./files', import.meta.url).href); const dest = builder.getBuildDirectory('cloudflare'); - const worker_dest = `${dest}/_worker.js`; + const tmp = builder.getBuildDirectory('cloudflare-tmp'); builder.rimraf(dest); builder.mkdirp(dest); - builder.mkdirp(worker_dest); + builder.mkdirp(tmp); // generate plaintext 404.html first which can then be overridden by prerendering, if the user defined such a page const fallback = path.join(dest, '404.html'); @@ -43,47 +43,49 @@ export default function (options = {}) { writeFileSync(fallback, 'Not Found'); } + // client assets and prerendered pages const dest_dir = `${dest}${builder.config.kit.paths.base}`; const written_files = builder.writeClient(dest_dir); builder.writePrerendered(dest_dir); - builder.writeServer(`${worker_dest}/server`); + // _worker.js + const relative_path = path.posix.relative(dest, builder.getServerDirectory()); + builder.copy(`${files}/worker.js`, `${dest}/_worker.js`, { + replace: { + SERVER: `${relative_path}/index.js`, + MANIFEST: `${path.posix.relative(dest, tmp)}/manifest.js` + } + }); writeFileSync( - `${worker_dest}/manifest.js`, - `export const manifest = ${builder.generateManifest({ relativePath: './server' })};\n\n` + + `${tmp}/manifest.js`, + `export const manifest = ${builder.generateManifest({ relativePath: relative_path })};\n\n` + `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n\n` + `export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n` ); - writeFileSync( - `${dest}/_routes.json`, - JSON.stringify(get_routes_json(builder, written_files, options.routes ?? {}), null, '\t') - ); + // _headers if (existsSync('_headers')) { copyFileSync('_headers', `${dest}/_headers`); } - writeFileSync(`${dest}/_headers`, generate_headers(builder.getAppPath()), { flag: 'a' }); + // _redirects if (existsSync('_redirects')) { copyFileSync('_redirects', `${dest}/_redirects`); } - if (builder.prerendered.redirects.size > 0) { writeFileSync(`${dest}/_redirects`, generate_redirects(builder.prerendered.redirects), { flag: 'a' }); } - writeFileSync(`${dest}/.assetsignore`, generate_assetsignore(), { flag: 'a' }); + writeFileSync( + `${dest}/_routes.json`, + JSON.stringify(get_routes_json(builder, written_files, options.routes ?? {}), null, '\t') + ); - builder.copy(`${files}/worker.js`, `${worker_dest}/index.js`, { - replace: { - SERVER: './server/index.js', - MANIFEST: './manifest.js' - } - }); + writeFileSync(`${dest}/.assetsignore`, generate_assetsignore(), { flag: 'a' }); }, emulate() { // we want to invoke `getPlatformProxy` only once, but await it only when it is accessed. From e7f3bf8f754f0b8fe44f8dd81f422f53249fb865 Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Mon, 24 Mar 2025 13:20:39 +0800 Subject: [PATCH 23/23] format --- packages/adapter-cloudflare/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 97a16bc6396d..da6735783748 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -69,7 +69,6 @@ export default function (options = {}) { } }); - // _headers if (existsSync('_headers')) { copyFileSync('_headers', `${dest}/_headers`);