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

perf(cloudflare): check for asset url #1236

Closed
wants to merge 9 commits into from
25 changes: 20 additions & 5 deletions src/rollup/plugins/public-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,31 @@ ${

export const publicAssetBases = ${JSON.stringify(publicAssetBases)}

export function isPublicAssetURL(id = '') {
if (assets[id]) {

const keyStartsWith = (literalObj, needle) => Object.keys(literalObj).some((k) => k.startsWith(needle));
const findKey = (literalObj, needle) => Object.keys(literalObj).find((k) => k.startsWith(needle));

export const isPublicAssetURL = (id = "") => {
if (
assets[id] ||
keyStartsWith(assets, id) ||
keyStartsWith(publicAssetBases, id)
) {
return true
}
for (const base in publicAssetBases) {
if (id.startsWith(base)) { return true }
}
return false
}

export const getPublicAssetMatch = (id = "") => {
const assetMatch = findKey(assets, id)
if(assetMatch) return [assetMatch, assets[assetMatch]]

const publicAssetMatch = findKey(publicAssetBases, id)
if(publicAssetMatch) return [publicAssetMatch, publicAssetBases[publicAssetMatch]]

return null
}

export function getPublicAssetMeta(id = '') {
for (const base in publicAssetBases) {
if (id.startsWith(base)) { return publicAssetBases[base] }
Expand Down
43 changes: 25 additions & 18 deletions src/runtime/entries/cloudflare-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import manifest from "__STATIC_CONTENT_MANIFEST";
import { requestHasBody } from "../utils";
import { nitroApp } from "#internal/nitro/app";
import { useRuntimeConfig } from "#internal/nitro";
import { getPublicAssetMeta } from "#internal/nitro/virtual/public-assets";
import {
getPublicAssetMatch,
getPublicAssetMeta,
isPublicAssetURL,
} from "#internal/nitro/virtual/public-assets";

interface CFModuleEnv {
[key: string]: any;
Expand All @@ -23,35 +27,38 @@ export default {
env: CFModuleEnv,
context: ExecutionContext
) {
const url = new URL(request.url);
try {
// https://github.com/cloudflare/kv-asset-handler#es-modules
return await getAssetFromKV(
{
request,
waitUntil(promise) {
return context.waitUntil(promise);
if (isPublicAssetURL(url.pathname)) {
const [match] = getPublicAssetMatch(url.pathname);
// https://github.com/cloudflare/kv-asset-handler#es-modules
return await getAssetFromKV(
{
request: new Request(new URL("http://localhost" + match)),
waitUntil(promise) {
return context.waitUntil(promise);
},
},
},
{
cacheControl: assetsCacheControl,
mapRequestToAsset: baseURLModifier,
ASSET_NAMESPACE: env.__STATIC_CONTENT,
ASSET_MANIFEST: JSON.parse(manifest),
}
);
{
cacheControl: assetsCacheControl,
mapRequestToAsset: baseURLModifier,
ASSET_NAMESPACE: env.__STATIC_CONTENT,
ASSET_MANIFEST: JSON.parse(manifest),
}
);
}
} catch {
// Ignore
// getAssetFromKV fail to return files with no extensions,
// so we can catch here and let Nitro handle it.
}

const url = new URL(request.url);
let body;
if (requestHasBody(request)) {
body = Buffer.from(await request.arrayBuffer());
}

// Expose latest env to the global context
globalThis.__env__ = env;

return nitroApp.localFetch(url.pathname + url.search, {
context: {
cf: (request as any).cf,
Expand Down
10 changes: 8 additions & 2 deletions src/runtime/entries/cloudflare-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
} from "@cloudflare/workers-types";
import { requestHasBody } from "#internal/nitro/utils";
import { nitroApp } from "#internal/nitro/app";
import { isPublicAssetURL } from "#internal/nitro/virtual/public-assets";
import {
isPublicAssetURL,
getPublicAssetMatch,
} from "#internal/nitro/virtual/public-assets";

/**
* Reference: https://developers.cloudflare.com/workers/runtime-apis/fetch-event/#parameters
Expand All @@ -28,7 +31,10 @@
) {
const url = new URL(request.url);
if (isPublicAssetURL(url.pathname)) {
return env.ASSETS.fetch(request);
const [match] = getPublicAssetMatch(url.pathname);
return await env.ASSETS.fetch(
new Request(new URL("http://localhost" + match))

Check failure on line 36 in src/runtime/entries/cloudflare-pages.ts

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest)

Argument of type 'Request' is not assignable to parameter of type 'Request<unknown, CfProperties<unknown>>'.
);
}

let body;
Expand Down
17 changes: 11 additions & 6 deletions src/runtime/entries/cloudflare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,28 @@ import { withoutBase } from "ufo";
import { requestHasBody } from "../utils";
import { nitroApp } from "#internal/nitro/app";
import { useRuntimeConfig } from "#internal/nitro";
import { getPublicAssetMeta } from "#internal/nitro/virtual/public-assets";
import {
getPublicAssetMeta,
isPublicAssetURL,
} from "#internal/nitro/virtual/public-assets";

addEventListener("fetch", (event: any) => {
event.respondWith(handleEvent(event));
});

async function handleEvent(event: FetchEvent) {
const url = new URL(event.request.url);
try {
return await getAssetFromKV(event, {
cacheControl: assetsCacheControl,
mapRequestToAsset: baseURLModifier,
});
if (isPublicAssetURL(url.pathname)) {
return await getAssetFromKV(event, {
cacheControl: assetsCacheControl,
mapRequestToAsset: baseURLModifier,
});
}
} catch {
// Ignore
}

const url = new URL(event.request.url);
let body;
if (requestHasBody(event.request)) {
body = Buffer.from(await event.request.arrayBuffer());
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/virtual/public-assets-data.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const assets: Record<string, unkown>;
export default assets;
3 changes: 2 additions & 1 deletion src/runtime/virtual/public-assets.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export const publicAssetBases: string[];
export const publicAssetBases: Record<string, string>;
export const isPublicAssetURL: (id: string) => boolean;
export const getPublicAssetMeta: (id: string) => { maxAge?: number };
export const readAsset: (id: string) => Promise<Buffer>;
export const getPublicAssetMatch: (id: string) => [string, string] | null;
export const getAsset: (id: string) => PublicAsset;

export interface PublicAsset {
Expand Down
14 changes: 2 additions & 12 deletions test/presets/cloudflare-module.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { promises as fsp } from "node:fs";
import { join, resolve } from "pathe";
import { resolve } from "pathe";
import { Miniflare } from "miniflare";
import { describe, it, expect } from "vitest";
import { Response as _Response } from "undici";
import { describe } from "vitest";

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

Expand All @@ -18,14 +16,6 @@ describe("nitro:preset:cloudflare-module", async () => {
globals: { __env__: {} },
bindings: {
...ctx.env,
ASSETS: {
fetch: async (request) => {
const contents = await fsp.readFile(
join(ctx.outDir, new URL(request.url).pathname)
);
return new _Response(contents);
},
},
},
});

Expand Down
3 changes: 3 additions & 0 deletions test/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ export function testNitro(
},
});
expect(status).toBe(503);
});

it("handles html api routes", async () => {
const { data: heyData } = await callHandler({ url: "/api/hey" });
expect(heyData).to.have.string("Hey API");
});
Expand Down
Loading