Skip to content

Commit 958f322

Browse files
authored
refactor: switch to bundled middleware (#497)
1 parent 6ce5643 commit 958f322

File tree

9 files changed

+447
-287
lines changed

9 files changed

+447
-287
lines changed

.changeset/smooth-cups-allow.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
Switch to bundled middleware

packages/cloudflare/package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@
5151
"url": "https://github.com/opennextjs/opennextjs-cloudflare/issues"
5252
},
5353
"homepage": "https://github.com/opennextjs/opennextjs-cloudflare",
54+
"dependencies": {
55+
"@dotenvx/dotenvx": "catalog:",
56+
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@798",
57+
"enquirer": "^2.4.1",
58+
"glob": "catalog:"
59+
},
5460
"devDependencies": {
5561
"@cloudflare/workers-types": "catalog:",
5662
"@eslint/js": "catalog:",
@@ -70,12 +76,6 @@
7076
"typescript-eslint": "catalog:",
7177
"vitest": "catalog:"
7278
},
73-
"dependencies": {
74-
"@dotenvx/dotenvx": "catalog:",
75-
"@opennextjs/aws": "^3.5.3",
76-
"enquirer": "^2.4.1",
77-
"glob": "catalog:"
78-
},
7979
"peerDependencies": {
8080
"wrangler": "catalog:"
8181
}

packages/cloudflare/src/api/cloudflare-context.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ import { DOShardedTagCache } from "./durable-objects/sharded-tag-cache";
55

66
declare global {
77
interface CloudflareEnv {
8+
// Asset binding
9+
ASSETS?: Fetcher;
10+
11+
// Environment to use when loading Next `.env` files
12+
// Default to "production"
13+
NEXTJS_ENV?: string;
14+
815
// KV used for the incremental cache
916
NEXT_CACHE_WORKERS_KV?: KVNamespace;
1017
// D1 db used for the tag cache
@@ -28,9 +35,6 @@ declare global {
2835
// Entirely optional
2936
NEXT_CACHE_DO_SHARDED_DLQ?: Queue;
3037

31-
// Asset binding
32-
ASSETS?: Fetcher;
33-
3438
// Below are the potential environment variables that can be set by the user to configure the durable object queue handler
3539
// The max number of revalidations that can be processed by the durable worker at the same time
3640
MAX_REVALIDATION_BY_DURABLE_OBJECT?: string;

packages/cloudflare/src/api/config.ts

+1-9
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,12 @@ export function defineCloudflareConfig(config: CloudflareOverrides = {}): OpenNe
3939
override: {
4040
wrapper: "cloudflare-node",
4141
converter: "edge",
42+
proxyExternalRequest: "fetch",
4243
incrementalCache: resolveIncrementalCache(incrementalCache),
4344
tagCache: resolveTagCache(tagCache),
4445
queue: resolveQueue(queue),
4546
},
4647
},
47-
48-
middleware: {
49-
external: true,
50-
override: {
51-
wrapper: "cloudflare-edge",
52-
converter: "edge",
53-
proxyExternalRequest: "fetch",
54-
},
55-
},
5648
};
5749
}
5850

packages/cloudflare/src/cli/build/open-next/createServerBundle.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,10 @@ async function generateBundle(
234234
overrides,
235235
}),
236236

237-
openNextExternalMiddlewarePlugin(path.join(options.openNextDistDir, "core/edgeFunctionHandler.js")),
237+
// `openNextExternalMiddlewarePlugin` should only be used with an external middleware
238+
...(config.middleware?.external
239+
? [openNextExternalMiddlewarePlugin(path.join(options.openNextDistDir, "core/edgeFunctionHandler.js"))]
240+
: []),
238241

239242
openNextEdgePlugins({
240243
nextDir: path.join(options.appBuildOutputPath, ".next"),
@@ -247,6 +250,7 @@ async function generateBundle(
247250
{
248251
entryPoints: [path.join(options.openNextDistDir, "adapters", "server-adapter.js")],
249252
outfile: path.join(outputPath, packagePath, `index.${outfileExt}`),
253+
external: ["./middleware.mjs"],
250254
banner: {
251255
js: [
252256
`globalThis.monorepoPackagePath = "${normalizePath(packagePath)}";`,

packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts

+3-13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
1010
const requirements = {
1111
dftUseCloudflareWrapper: config.default?.override?.wrapper === "cloudflare-node",
1212
dftUseEdgeConverter: config.default?.override?.converter === "edge",
13+
dftUseFetchProxy: config.default?.override?.proxyExternalRequest === "fetch",
1314
dftMaybeUseCache:
1415
config.default?.override?.incrementalCache === "dummy" ||
1516
typeof config.default?.override?.incrementalCache === "function",
@@ -20,10 +21,7 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
2021
config.default?.override?.queue === "dummy" ||
2122
config.default?.override?.queue === "direct" ||
2223
typeof config.default?.override?.queue === "function",
23-
mwIsMiddlewareExternal: config.middleware?.external == true,
24-
mwUseCloudflareWrapper: config.middleware?.override?.wrapper === "cloudflare-edge",
25-
mwUseEdgeConverter: config.middleware?.override?.converter === "edge",
26-
mwUseFetchProxy: config.middleware?.override?.proxyExternalRequest === "fetch",
24+
mwIsMiddlewareIntegrated: config.middleware === undefined,
2725
};
2826

2927
if (config.default?.override?.queue === "direct") {
@@ -38,20 +36,12 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
3836
override: {
3937
wrapper: "cloudflare-node",
4038
converter: "edge",
39+
proxyExternalRequest: "fetch",
4140
incrementalCache: "dummy" | function,
4241
tagCache: "dummy",
4342
queue: "dummy" | "direct" | function,
4443
},
4544
},
46-
47-
middleware: {
48-
external: true,
49-
override: {
50-
wrapper: "cloudflare-edge",
51-
converter: "edge",
52-
proxyExternalRequest: "fetch",
53-
},
54-
},
5545
}\n\n`.replace(/^ {8}/gm, "")
5646
);
5747
}

packages/cloudflare/src/cli/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ async function runCommand(args: Arguments) {
2626
const openNextDistDir = path.dirname(require.resolve("@opennextjs/aws/index.js"));
2727

2828
await createOpenNextConfigIfNotExistent(baseDir);
29-
const { config, buildDir } = await compileOpenNextConfig(baseDir);
29+
const { config, buildDir } = await compileOpenNextConfig(baseDir, undefined, {
30+
compileEdge: true,
31+
});
3032

3133
ensureCloudflareConfig(config);
3234

packages/cloudflare/src/cli/templates/worker.ts

+16-21
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ import { AsyncLocalStorage } from "node:async_hooks";
33
import type { CloudflareContext } from "../../api";
44
// @ts-expect-error: resolved by wrangler build
55
import * as nextEnvVars from "./env/next-env.mjs";
6-
// @ts-expect-error: resolved by wrangler build
7-
import { handler as middlewareHandler } from "./middleware/handler.mjs";
8-
// @ts-expect-error: resolved by wrangler build
9-
import { handler as serverHandler } from "./server-functions/default/handler.mjs";
106

117
const cloudflareContextALS = new AsyncLocalStorage<CloudflareContext>();
128

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

33-
populateProcessEnv(url, env.NEXTJS_ENV);
29+
populateProcessEnv(url, env);
3430

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

4844
// Fallback for the Next default image loader.
4945
if (url.pathname === "/_next/image") {
5046
const imageUrl = url.searchParams.get("url") ?? "";
5147
return imageUrl.startsWith("/")
52-
? env.ASSETS.fetch(new URL(imageUrl, request.url))
48+
? env.ASSETS?.fetch(new URL(imageUrl, request.url))
5349
: fetch(imageUrl, { cf: { cacheEverything: true } });
5450
}
5551

56-
// The Middleware handler can return either a `Response` or a `Request`:
57-
// - `Response`s should be returned early
58-
// - `Request`s are handled by the Next server
59-
const reqOrResp = await middlewareHandler(request, env, ctx);
60-
61-
if (reqOrResp instanceof Response) {
62-
return reqOrResp;
63-
}
52+
// @ts-expect-error: resolved by wrangler build
53+
const { handler } = await import("./server-functions/default/handler.mjs");
6454

65-
return serverHandler(reqOrResp, env, ctx);
55+
return handler(request, env, ctx);
6656
});
6757
},
68-
} as ExportedHandler<{ ASSETS: Fetcher; NEXTJS_ENV?: string }>;
58+
} as ExportedHandler<CloudflareEnv>;
6959

7060
/**
7161
* Populate process.env with:
62+
* - the environment variables and secrets from the cloudflare platform
7263
* - the variables from Next .env* files
7364
* - the origin resolver information
74-
*
75-
* Note that cloudflare env string values are copied by the middleware handler.
7665
*/
77-
function populateProcessEnv(url: URL, nextJsEnv?: string) {
66+
function populateProcessEnv(url: URL, env: CloudflareEnv) {
7867
if (processEnvPopulated) {
7968
return;
8069
}
8170
processEnvPopulated = true;
82-
const mode = nextJsEnv ?? "production";
8371

72+
for (const [key, value] of Object.entries(env)) {
73+
if (typeof value === "string") {
74+
process.env[key] = value;
75+
}
76+
}
77+
78+
const mode = env.NEXTJS_ENV ?? "production";
8479
if (nextEnvVars[mode]) {
8580
for (const key in nextEnvVars[mode]) {
8681
process.env[key] = nextEnvVars[mode][key];

0 commit comments

Comments
 (0)