Skip to content

Commit

Permalink
feat: Cloudflare Pages _routes.json specification (#6441)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jrf0110 committed Sep 8, 2022
1 parent 842f69b commit bde732e
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .changeset/afraid-gifts-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sveltejs/adapter-cloudflare': patch
'@sveltejs/kit': patch
---

Support Cloudflare Pages \_routes.json specification
7 changes: 7 additions & 0 deletions packages/adapter-cloudflare/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
}
68 changes: 61 additions & 7 deletions packages/adapter-cloudflare/index.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -23,18 +23,32 @@ export default function () {
builder.rimraf(tmp);
builder.mkdirp(tmp);

builder.writeClient(dest);
const written_files = builder.writeClient(dest);
console.log('written_files', written_files);
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`,
Expand All @@ -55,3 +69,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();
}

0 comments on commit bde732e

Please sign in to comment.