Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/smooth-cups-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/cloudflare": patch
---

Switch to bundle middleware
12 changes: 6 additions & 6 deletions packages/cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@
"url": "https://github.com/opennextjs/opennextjs-cloudflare/issues"
},
"homepage": "https://github.com/opennextjs/opennextjs-cloudflare",
"dependencies": {
"@dotenvx/dotenvx": "catalog:",
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@798",
"enquirer": "^2.4.1",
"glob": "catalog:"
},
"devDependencies": {
"@cloudflare/workers-types": "catalog:",
"@eslint/js": "catalog:",
Expand All @@ -70,12 +76,6 @@
"typescript-eslint": "catalog:",
"vitest": "catalog:"
},
"dependencies": {
"@dotenvx/dotenvx": "catalog:",
"@opennextjs/aws": "^3.5.3",
"enquirer": "^2.4.1",
"glob": "catalog:"
},
"peerDependencies": {
"wrangler": "catalog:"
}
Expand Down
10 changes: 7 additions & 3 deletions packages/cloudflare/src/api/cloudflare-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { DOShardedTagCache } from "./durable-objects/sharded-tag-cache";

declare global {
interface CloudflareEnv {
// Asset binding
ASSETS?: Fetcher;

// Environment to use when loading Next `.env` files
// Default to "production"
NEXTJS_ENV?: string;

// KV used for the incremental cache
NEXT_CACHE_WORKERS_KV?: KVNamespace;
// D1 db used for the tag cache
Expand All @@ -24,9 +31,6 @@ declare global {
// Durables object namespace to use for the sharded tag cache
NEXT_CACHE_D1_SHARDED?: DurableObjectNamespace<DOShardedTagCache>;

// Asset binding
ASSETS?: Fetcher;

// Below are the potential environment variables that can be set by the user to configure the durable object queue handler
// The max number of revalidations that can be processed by the durable worker at the same time
MAX_REVALIDATION_BY_DURABLE_OBJECT?: string;
Expand Down
10 changes: 1 addition & 9 deletions packages/cloudflare/src/api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,12 @@ export function defineCloudflareConfig(config: CloudflareOverrides = {}): OpenNe
override: {
wrapper: "cloudflare-node",
converter: "edge",
proxyExternalRequest: "fetch",
incrementalCache: resolveIncrementalCache(incrementalCache),
tagCache: resolveTagCache(tagCache),
queue: resolveQueue(queue),
},
},

middleware: {
external: true,
override: {
wrapper: "cloudflare-edge",
converter: "edge",
proxyExternalRequest: "fetch",
},
},
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ async function generateBundle(
overrides,
}),

openNextExternalMiddlewarePlugin(path.join(options.openNextDistDir, "core/edgeFunctionHandler.js")),
...(config.middleware?.external
? [openNextExternalMiddlewarePlugin(path.join(options.openNextDistDir, "core/edgeFunctionHandler.js"))]
: []),

openNextEdgePlugins({
nextDir: path.join(options.appBuildOutputPath, ".next"),
Expand All @@ -247,6 +249,7 @@ async function generateBundle(
{
entryPoints: [path.join(options.openNextDistDir, "adapters", "server-adapter.js")],
outfile: path.join(outputPath, packagePath, `index.${outfileExt}`),
external: ["./middleware.mjs"],
banner: {
js: [
`globalThis.monorepoPackagePath = "${normalizePath(packagePath)}";`,
Expand Down
16 changes: 3 additions & 13 deletions packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
const requirements = {
dftUseCloudflareWrapper: config.default?.override?.wrapper === "cloudflare-node",
dftUseEdgeConverter: config.default?.override?.converter === "edge",
dftUseFetchProxy: config.default?.override?.proxyExternalRequest === "fetch",
dftMaybeUseCache:
config.default?.override?.incrementalCache === "dummy" ||
typeof config.default?.override?.incrementalCache === "function",
Expand All @@ -20,10 +21,7 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
config.default?.override?.queue === "dummy" ||
config.default?.override?.queue === "direct" ||
typeof config.default?.override?.queue === "function",
mwIsMiddlewareExternal: config.middleware?.external == true,
mwUseCloudflareWrapper: config.middleware?.override?.wrapper === "cloudflare-edge",
mwUseEdgeConverter: config.middleware?.override?.converter === "edge",
mwUseFetchProxy: config.middleware?.override?.proxyExternalRequest === "fetch",
mwIsMiddlewareIntegrated: config.middleware === undefined,
};

if (config.default?.override?.queue === "direct") {
Expand All @@ -38,20 +36,12 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
override: {
wrapper: "cloudflare-node",
converter: "edge",
proxyExternalRequest: "fetch",
incrementalCache: "dummy" | function,
tagCache: "dummy",
queue: "dummy" | "direct" | function,
},
},

middleware: {
external: true,
override: {
wrapper: "cloudflare-edge",
converter: "edge",
proxyExternalRequest: "fetch",
},
},
}\n\n`.replace(/^ {8}/gm, "")
);
}
Expand Down
4 changes: 3 additions & 1 deletion packages/cloudflare/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ async function runCommand(args: Arguments) {
const openNextDistDir = path.dirname(require.resolve("@opennextjs/aws/index.js"));

await createOpenNextConfigIfNotExistent(baseDir);
const { config, buildDir } = await compileOpenNextConfig(baseDir);
const { config, buildDir } = await compileOpenNextConfig(baseDir, undefined, {
compileEdge: true,
});

ensureCloudflareConfig(config);

Expand Down
37 changes: 16 additions & 21 deletions packages/cloudflare/src/cli/templates/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import { AsyncLocalStorage } from "node:async_hooks";
import type { CloudflareContext } from "../../api";
// @ts-expect-error: resolved by wrangler build
import * as nextEnvVars from "./env/next-env.mjs";
// @ts-expect-error: resolved by wrangler build
import { handler as middlewareHandler } from "./middleware/handler.mjs";
// @ts-expect-error: resolved by wrangler build
import { handler as serverHandler } from "./server-functions/default/handler.mjs";

const cloudflareContextALS = new AsyncLocalStorage<CloudflareContext>();

Expand All @@ -30,7 +26,7 @@ export default {
return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => {
const url = new URL(request.url);

populateProcessEnv(url, env.NEXTJS_ENV);
populateProcessEnv(url, env);

// Serve images in development.
// Note: "/cdn-cgi/image/..." requests do not reach production workers.
Expand All @@ -42,45 +38,44 @@ export default {
const imageUrl = m.groups!.url!;
return imageUrl.match(/^https?:\/\//)
? fetch(imageUrl, { cf: { cacheEverything: true } })
: env.ASSETS.fetch(new URL(`/${imageUrl}`, url));
: env.ASSETS?.fetch(new URL(`/${imageUrl}`, url));
}

// Fallback for the Next default image loader.
if (url.pathname === "/_next/image") {
const imageUrl = url.searchParams.get("url") ?? "";
return imageUrl.startsWith("/")
? env.ASSETS.fetch(new URL(imageUrl, request.url))
? env.ASSETS?.fetch(new URL(imageUrl, request.url))
: fetch(imageUrl, { cf: { cacheEverything: true } });
}

// The Middleware handler can return either a `Response` or a `Request`:
// - `Response`s should be returned early
// - `Request`s are handled by the Next server
const reqOrResp = await middlewareHandler(request, env, ctx);

if (reqOrResp instanceof Response) {
return reqOrResp;
}
// @ts-expect-error: resolved by wrangler build
const { handler } = await import("./server-functions/default/handler.mjs");

return serverHandler(reqOrResp, env, ctx);
return handler(request, env, ctx);
});
},
} as ExportedHandler<{ ASSETS: Fetcher; NEXTJS_ENV?: string }>;
} as ExportedHandler<CloudflareEnv>;

/**
* Populate process.env with:
* - the environment variables and secrets from the cloudflare platform
* - the variables from Next .env* files
* - the origin resolver information
*
* Note that cloudflare env string values are copied by the middleware handler.
*/
function populateProcessEnv(url: URL, nextJsEnv?: string) {
function populateProcessEnv(url: URL, env: CloudflareEnv) {
if (processEnvPopulated) {
return;
}
processEnvPopulated = true;
const mode = nextJsEnv ?? "production";

for (const [key, value] of Object.entries(env)) {
if (typeof value === "string") {
process.env[key] = value;
}
}

const mode = env.NEXTJS_ENV ?? "production";
if (nextEnvVars[mode]) {
for (const key in nextEnvVars[mode]) {
process.env[key] = nextEnvVars[mode][key];
Expand Down
Loading