Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cloudflare-pages): exclude assets from function call #965

Merged
merged 12 commits into from
Feb 21, 2023
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,6 @@ Temporary Items
.netlify
.vercel
staticwebapp.config.json
.eslintcache
.eslintcache

test/fixture/functions
53 changes: 52 additions & 1 deletion src/presets/cloudflare.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { resolve } from "pathe";
import { join, resolve } from "pathe";
import fse from "fs-extra";
import { joinURL, withLeadingSlash, withoutLeadingSlash } from "ufo";
import { globby } from "globby";
import { writeFile } from "../utils";
import { defineNitroPreset } from "../preset";
import type { Nitro } from "../types";
Expand All @@ -25,6 +27,15 @@ export const cloudflare = defineNitroPreset({
},
});

/**
* https://developers.cloudflare.com/pages/platform/functions/routing/#functions-invocation-routes
*/
interface CloudflarePagesRoutes {
version: 1;
include: string[];
exclude: string[];
}

export const cloudflarePages = defineNitroPreset({
extends: "cloudflare",
entry: "#internal/nitro/entries/cloudflare-pages",
Expand Down Expand Up @@ -56,6 +67,46 @@ export const cloudflarePages = defineNitroPreset({
resolve(nitro.options.output.serverDir, "path.js.map"),
resolve(nitro.options.output.serverDir, "[[path]].js.map")
);

const routes: CloudflarePagesRoutes = {
version: 1,
include: ["/*"],
exclude: [],
};

// Exclude public assets from hitting the worker
const explicitPublicAssets = nitro.options.publicAssets.filter(
(i) => !i.fallthrough
pi0 marked this conversation as resolved.
Show resolved Hide resolved
);

// Explicit prefixes
routes.exclude.push(
...explicitPublicAssets.map((dir) => joinURL(dir.baseURL, "*"))
);

// Unprefixed assets
const publicAssetFiles = await globby("**", {
cwd: nitro.options.output.publicDir,
absolute: false,
dot: true,
ignore: [
...explicitPublicAssets.map((dir) =>
withoutLeadingSlash(joinURL(dir.baseURL, "**"))
),
// TODO!
".nitro/**",
".output/**",
],
});
routes.exclude.push(...publicAssetFiles.map((i) => withLeadingSlash(i)));

// Only allow 100 routes
routes.exclude.splice(100);

await fse.writeFile(
resolve(nitro.options.output.publicDir, "_routes.json"),
JSON.stringify(routes, undefined, 2)
);
},
},
});
25 changes: 24 additions & 1 deletion test/presets/cloudflare.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { promises as fsp } from "node:fs";
import { resolve } from "pathe";
import { Miniflare } from "miniflare";
import { describe } from "vitest";
import { describe, it, expect } from "vitest";

import { setupTest, testNitro } from "../tests";

Expand All @@ -20,3 +21,25 @@ describe("nitro:preset:cloudflare", async () => {
};
});
});

describe("nitro:preset:cloudflare-pages", async () => {
const ctx = await setupTest("cloudflare-pages");

it("should generate a _routes.json", async () => {
const config = await fsp
.readFile(resolve(ctx.outDir, "public/_routes.json"), "utf8")
.then((r) => JSON.parse(r));
expect(config).toMatchInlineSnapshot(`
{
"exclude": [
"/build/*",
"/favicon.ico",
],
"include": [
"/*",
],
"version": 1,
}
`);
});
});
13 changes: 11 additions & 2 deletions test/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,17 @@ export async function setupTest(preset: string) {
dev: ctx.isDev,
rootDir: ctx.rootDir,
serveStatic:
preset !== "cloudflare" && preset !== "vercel-edge" && !ctx.isDev,
output: { dir: ctx.outDir },
preset !== "cloudflare" &&
preset !== "cloudflare-pages" &&
preset !== "vercel-edge" &&
!ctx.isDev,
output: {
dir: ctx.outDir,
serverDir:
preset === "cloudflare-pages"
? "{{ output.dir }}/functions"
: undefined,
},
routeRules: {
"/rules/headers": { headers: { "cache-control": "s-maxage=60" } },
"/rules/cors": {
Expand Down