From 36638b3cad946845b36557802fb4a3f8472b2c10 Mon Sep 17 00:00:00 2001 From: John Fawcett Date: Mon, 29 Aug 2022 10:22:29 -0500 Subject: [PATCH 1/4] feat: Cloudflare Pages _routes.json specification (#6441) When a SvelteKit project is deployed to Cloudflare Pages, the server-side code is supported through Pages Functions (currently in beta). Unfortunately, by using Functions, each request must go through the server-side Functions code even if it doesn't need to. For instance, an immutable asset request to https://kit.svelte.dev/_app/immutable/assets/_layout-ab34ca4f.css would first have to route through Functions. This is problematic since the request would "count" as a request to Functions even though only a static asset was served. Further, there is a slight amount of added latency. This change exposes a set of includes and excludes based on static files generated. --- .changeset/afraid-gifts-act.md | 6 +++ packages/adapter-cloudflare/index.d.ts | 7 +++ packages/adapter-cloudflare/index.js | 67 +++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 .changeset/afraid-gifts-act.md diff --git a/.changeset/afraid-gifts-act.md b/.changeset/afraid-gifts-act.md new file mode 100644 index 000000000000..7971030a467a --- /dev/null +++ b/.changeset/afraid-gifts-act.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-cloudflare': patch +'@sveltejs/kit': patch +--- + +Support Cloudflare Pages \_routes.json specification diff --git a/packages/adapter-cloudflare/index.d.ts b/packages/adapter-cloudflare/index.d.ts index ed89676dadc4..1e47e1627045 100644 --- a/packages/adapter-cloudflare/index.d.ts +++ b/packages/adapter-cloudflare/index.d.ts @@ -2,3 +2,10 @@ import { Adapter } from '@sveltejs/kit'; import './ambient.js'; export default function plugin(): Adapter; + +export interface RoutesJSONSpec { + version: 1; + description: string; + include: string[]; + exclude: string[]; +} diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index bfb9d968aa1f..78761204efbd 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -1,4 +1,4 @@ -import { writeFileSync } from 'fs'; +import { writeFile } from 'fs/promises'; import { posix } from 'path'; import { fileURLToPath } from 'url'; import * as esbuild from 'esbuild'; @@ -23,18 +23,31 @@ export default function () { builder.rimraf(tmp); builder.mkdirp(tmp); - builder.writeClient(dest); + const written_files = builder.writeClient(dest); builder.writePrerendered(dest); const relativePath = posix.relative(tmp, builder.getServerDirectory()); - writeFileSync( - `${tmp}/manifest.js`, - `export const manifest = ${builder.generateManifest({ - relativePath - })};\n\nexport const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n` + builder.log.info( + `adapter-cloudfare is writing its own _headers file. If you have your own, you should duplicate the headers contained in: ${dest}/_headers` ); + await Promise.all([ + writeFile( + `${tmp}/manifest.js`, + `export const manifest = ${builder.generateManifest({ + relativePath + })};\n\nexport const prerendered = new Set(${JSON.stringify( + builder.prerendered.paths + )});\n` + ), + writeFile( + `${dest}/_routes.json`, + JSON.stringify(getRoutesJSON(builder.config.kit.appDir, written_files)) + ), + writeFile(`${dest}/_headers`, getAutogeneratedHeaders(builder.config.kit.appDir)) + ]); + builder.copy(`${files}/worker.js`, `${tmp}/_worker.js`, { replace: { SERVER: `${relativePath}/index.js`, @@ -55,3 +68,43 @@ export default function () { } }; } + +/** + * @param {string} app_dir + * @param {string[]} assets + * @returns {import('.').RoutesJSONSpec} + */ +function getRoutesJSON(app_dir, assets) { + return { + version: 1, + description: 'Generated by @sveltejs/adapter-cloudflare', + include: ['/*'], + exclude: [ + `/${app_dir}/immutable/*`, + ...assets + // We're being conservative by not excluding all assets in + // /static just yet. If there are any upstream auth rules to + // protect certain things (e.g. a PDF that requires auth), + // then we wouldn't want to prevent those requests from going + // to the user functions worker. + // We do want to show an example of a _routes.json that + // excludes more than just /_app/immutable/*, and favicons + // are a reasonable choice + .filter((filePath) => filePath.includes('favicon')) + ] + }; +} + +/** + * @param {string} app_dir + * @returns {string} + */ +function getAutogeneratedHeaders(app_dir) { + return ` +# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === +/${app_dir}/immutable/* + Cache-Control: public, immutable, max-age=31536000 + X-Robots-Tag: noindex +# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === + `.trim(); +} From 4d9420a025b2d2281a3be87ebe47574c585d94c6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 21 Sep 2022 15:33:33 -0400 Subject: [PATCH 2/4] use writeFileSync over writeFile, for consistency with rest of codebase --- packages/adapter-cloudflare/index.js | 31 +++++++++++++--------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 78761204efbd..52309d1506ad 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -1,4 +1,4 @@ -import { writeFile } from 'fs/promises'; +import { writeFileSync } from 'fs'; import { posix } from 'path'; import { fileURLToPath } from 'url'; import * as esbuild from 'esbuild'; @@ -32,21 +32,18 @@ export default function () { `adapter-cloudfare is writing its own _headers file. If you have your own, you should duplicate the headers contained in: ${dest}/_headers` ); - await Promise.all([ - writeFile( - `${tmp}/manifest.js`, - `export const manifest = ${builder.generateManifest({ - relativePath - })};\n\nexport const prerendered = new Set(${JSON.stringify( - builder.prerendered.paths - )});\n` - ), - writeFile( - `${dest}/_routes.json`, - JSON.stringify(getRoutesJSON(builder.config.kit.appDir, written_files)) - ), - writeFile(`${dest}/_headers`, getAutogeneratedHeaders(builder.config.kit.appDir)) - ]); + writeFileSync( + `${tmp}/manifest.js`, + `export const manifest = ${builder.generateManifest({ relativePath })};\n\n` + + `export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n` + ); + + writeFileSync( + `${dest}/_routes.json`, + JSON.stringify(getRoutesJSON(builder.config.kit.appDir, written_files)) + ); + + writeFileSync(`${dest}/_headers`, getAutogeneratedHeaders(builder.config.kit.appDir)); builder.copy(`${files}/worker.js`, `${tmp}/_worker.js`, { replace: { @@ -105,6 +102,6 @@ function getAutogeneratedHeaders(app_dir) { /${app_dir}/immutable/* Cache-Control: public, immutable, max-age=31536000 X-Robots-Tag: noindex -# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === +# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === `.trim(); } From cc179bc4e745af7d4630f525db399e00ab9a185b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 21 Sep 2022 15:35:55 -0400 Subject: [PATCH 3/4] code style stuff --- packages/adapter-cloudflare/index.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 52309d1506ad..5e6caf6a15ab 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -40,10 +40,10 @@ export default function () { writeFileSync( `${dest}/_routes.json`, - JSON.stringify(getRoutesJSON(builder.config.kit.appDir, written_files)) + JSON.stringify(get_routes_json(builder.config.kit.appDir, written_files)) ); - writeFileSync(`${dest}/_headers`, getAutogeneratedHeaders(builder.config.kit.appDir)); + writeFileSync(`${dest}/_headers`, generate_headers(builder.config.kit.appDir)); builder.copy(`${files}/worker.js`, `${tmp}/_worker.js`, { replace: { @@ -71,7 +71,7 @@ export default function () { * @param {string[]} assets * @returns {import('.').RoutesJSONSpec} */ -function getRoutesJSON(app_dir, assets) { +function get_routes_json(app_dir, assets) { return { version: 1, description: 'Generated by @sveltejs/adapter-cloudflare', @@ -92,11 +92,8 @@ function getRoutesJSON(app_dir, assets) { }; } -/** - * @param {string} app_dir - * @returns {string} - */ -function getAutogeneratedHeaders(app_dir) { +/** @param {string} app_dir */ +function generate_headers(app_dir) { return ` # === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === /${app_dir}/immutable/* From 9cc96c3a6f38ec64fff23fa3a3aede503be3fd23 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 21 Sep 2022 15:36:26 -0400 Subject: [PATCH 4/4] tweak changeset --- .changeset/afraid-gifts-act.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/afraid-gifts-act.md b/.changeset/afraid-gifts-act.md index 7971030a467a..7f4a0c421484 100644 --- a/.changeset/afraid-gifts-act.md +++ b/.changeset/afraid-gifts-act.md @@ -3,4 +3,4 @@ '@sveltejs/kit': patch --- -Support Cloudflare Pages \_routes.json specification +Support Cloudflare Pages `_routes.json` specification