Skip to content

Commit

Permalink
fix(cloudflare-pages): exclude assets from function call (#965)
Browse files Browse the repository at this point in the history
Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
danielroe and pi0 committed Feb 21, 2023
1 parent 9bbf667 commit c82287a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 5 deletions.
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
58 changes: 57 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,51 @@ 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
);

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

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

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

await fse.writeFile(
resolve(nitro.options.output.publicDir, "_routes.json"),
JSON.stringify(routes, undefined, 2)
);
},
},
});

function comparePaths(a: string, b: string) {
return a.split("/").length - b.split("/").length || a.localeCompare(b);
}
35 changes: 34 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,35 @@ 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",
"/icon.png",
"/api/hello",
"/prerender/index.html",
"/prerender/index.html.br",
"/prerender/index.html.gz",
"/api/hey/index.html",
"/api/param/foo.json/index.html",
"/api/param/prerender1/index.html",
"/api/param/prerender3/index.html",
"/api/param/prerender4/index.html",
],
"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

0 comments on commit c82287a

Please sign in to comment.