diff --git a/src/rollup/plugins/public-assets.ts b/src/rollup/plugins/public-assets.ts index ab2a2d60a0..ecd4059356 100644 --- a/src/rollup/plugins/public-assets.ts +++ b/src/rollup/plugins/public-assets.ts @@ -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] } diff --git a/src/runtime/entries/cloudflare-module.ts b/src/runtime/entries/cloudflare-module.ts index 53db2c6010..75fb536b49 100644 --- a/src/runtime/entries/cloudflare-module.ts +++ b/src/runtime/entries/cloudflare-module.ts @@ -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; @@ -23,27 +27,31 @@ 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()); @@ -51,7 +59,6 @@ export default { // Expose latest env to the global context globalThis.__env__ = env; - return nitroApp.localFetch(url.pathname + url.search, { context: { cf: (request as any).cf, diff --git a/src/runtime/entries/cloudflare-pages.ts b/src/runtime/entries/cloudflare-pages.ts index 2c420f2a44..83cf3a146b 100644 --- a/src/runtime/entries/cloudflare-pages.ts +++ b/src/runtime/entries/cloudflare-pages.ts @@ -5,7 +5,10 @@ import type { } 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 @@ -28,7 +31,10 @@ export default { ) { 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)) + ); } let body; diff --git a/src/runtime/entries/cloudflare.ts b/src/runtime/entries/cloudflare.ts index 5277b5e141..1192783462 100644 --- a/src/runtime/entries/cloudflare.ts +++ b/src/runtime/entries/cloudflare.ts @@ -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()); diff --git a/src/runtime/virtual/public-assets-data.d.ts b/src/runtime/virtual/public-assets-data.d.ts new file mode 100644 index 0000000000..8b101a8170 --- /dev/null +++ b/src/runtime/virtual/public-assets-data.d.ts @@ -0,0 +1,2 @@ +const assets: Record; +export default assets; diff --git a/src/runtime/virtual/public-assets.d.ts b/src/runtime/virtual/public-assets.d.ts index 9c650edfd4..50795167d8 100644 --- a/src/runtime/virtual/public-assets.d.ts +++ b/src/runtime/virtual/public-assets.d.ts @@ -1,7 +1,8 @@ -export const publicAssetBases: string[]; +export const publicAssetBases: Record; export const isPublicAssetURL: (id: string) => boolean; export const getPublicAssetMeta: (id: string) => { maxAge?: number }; export const readAsset: (id: string) => Promise; +export const getPublicAssetMatch: (id: string) => [string, string] | null; export const getAsset: (id: string) => PublicAsset; export interface PublicAsset { diff --git a/test/presets/cloudflare-module.test.ts b/test/presets/cloudflare-module.test.ts index ba45918004..a192c61633 100644 --- a/test/presets/cloudflare-module.test.ts +++ b/test/presets/cloudflare-module.test.ts @@ -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"; @@ -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); - }, - }, }, }); diff --git a/test/tests.ts b/test/tests.ts index acc3bd0c41..6df6eafe4b 100644 --- a/test/tests.ts +++ b/test/tests.ts @@ -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"); });