From 78946b2cc3539fdcbaeb414ad6d35755ffa57452 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 6 Oct 2024 19:30:39 +0100 Subject: [PATCH 1/5] feat: use incremental cache during rendering --- .../cloudflare/src/cli/build/build-worker.ts | 6 ++---- .../inline-middleware-manifest-require.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts diff --git a/packages/cloudflare/src/cli/build/build-worker.ts b/packages/cloudflare/src/cli/build/build-worker.ts index 2acdd63e..136d4da6 100644 --- a/packages/cloudflare/src/cli/build/build-worker.ts +++ b/packages/cloudflare/src/cli/build/build-worker.ts @@ -5,6 +5,7 @@ import { Config } from "../config"; import { copyPackageCliFiles } from "./patches/investigated/copy-package-cli-files"; import { fileURLToPath } from "node:url"; import { inlineEvalManifest } from "./patches/to-investigate/inline-eval-manifest"; +import { inlineMiddlewareManifestRequire } from "./patches/to-investigate/inline-middleware-manifest-require"; import { inlineNextRequire } from "./patches/to-investigate/inline-next-require"; import { patchCache } from "./patches/investigated/patch-cache"; import { patchFindDir } from "./patches/to-investigate/patch-find-dir"; @@ -90,10 +91,6 @@ export async function buildWorker(config: Config): Promise { // Note: we need the __non_webpack_require__ variable declared as it is used by next-server: // https://github.com/vercel/next.js/blob/be0c3283/packages/next/src/server/next-server.ts#L116-L119 __non_webpack_require__: "require", - // The next.js server can run in minimal mode: https://github.com/vercel/next.js/blob/aa90fe9bb/packages/next/src/server/base-server.ts#L510-L511 - // this avoids some extra (/problematic) `require` calls, such as here: https://github.com/vercel/next.js/blob/aa90fe9bb/packages/next/src/server/next-server.ts#L1259 - // that's wht we enable it - "process.env.NEXT_PRIVATE_MINIMAL_MODE": "true", // Ask mhart if he can explain why the `define`s below are necessary "process.env.NEXT_RUNTIME": '"nodejs"', "process.env.NODE_ENV": '"production"', @@ -166,6 +163,7 @@ async function updateWorkerBundledCode(workerOutputFile: string, config: Config) patchedCode = patchFindDir(patchedCode, config); patchedCode = inlineEvalManifest(patchedCode, config); patchedCode = patchCache(patchedCode, config); + patchedCode = inlineMiddlewareManifestRequire(patchedCode, config); await writeFile(workerOutputFile, patchedCode); } diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts new file mode 100644 index 00000000..791db33d --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts @@ -0,0 +1,19 @@ +import { existsSync, readFileSync } from "node:fs"; +import { Config } from "../../../config"; +import path from "node:path"; + +/** + * Inlines the middleware manifest from the build output to prevent a dynamic require statement + * as they result in runtime failures. + */ +export function inlineMiddlewareManifestRequire(code: string, config: Config) { + console.log("# inlineMiddlewareManifestRequire"); + + const middlewareManifestPath = path.join(config.paths.standaloneAppServer, "middleware-manifest.json"); + + const middlewareManifest = existsSync(middlewareManifestPath) + ? JSON.parse(readFileSync(middlewareManifestPath, "utf-8")) + : {}; + + return code.replace(/require\(this.middlewareManifestPath\)/, JSON.stringify(middlewareManifest)); +} From abdb2b8af60ed2c2b4302a01da3d52b5d85df517 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 7 Oct 2024 00:13:58 +0100 Subject: [PATCH 2/5] patch exception bubbling --- packages/cloudflare/src/cli/build/build-worker.ts | 2 ++ .../to-investigate/patch-exception-bubbling.ts | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts diff --git a/packages/cloudflare/src/cli/build/build-worker.ts b/packages/cloudflare/src/cli/build/build-worker.ts index 136d4da6..85b358f8 100644 --- a/packages/cloudflare/src/cli/build/build-worker.ts +++ b/packages/cloudflare/src/cli/build/build-worker.ts @@ -8,6 +8,7 @@ import { inlineEvalManifest } from "./patches/to-investigate/inline-eval-manifes import { inlineMiddlewareManifestRequire } from "./patches/to-investigate/inline-middleware-manifest-require"; import { inlineNextRequire } from "./patches/to-investigate/inline-next-require"; import { patchCache } from "./patches/investigated/patch-cache"; +import { patchExceptionBubbling } from "./patches/to-investigate/patch-exception-bubbling"; import { patchFindDir } from "./patches/to-investigate/patch-find-dir"; import { patchReadFile } from "./patches/to-investigate/patch-read-file"; import { patchRequire } from "./patches/investigated/patch-require"; @@ -164,6 +165,7 @@ async function updateWorkerBundledCode(workerOutputFile: string, config: Config) patchedCode = inlineEvalManifest(patchedCode, config); patchedCode = patchCache(patchedCode, config); patchedCode = inlineMiddlewareManifestRequire(patchedCode, config); + patchedCode = patchExceptionBubbling(patchedCode); await writeFile(workerOutputFile, patchedCode); } diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts new file mode 100644 index 00000000..61e74b8f --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts @@ -0,0 +1,11 @@ +/** + * When using SSG and `dynamicParams = false`, Next.js throws a NoFallbackError. This error is + * bubbled up by default in Node.js servers, however this causes issues in the workerd with + * the current response handling and streaming implementation we have, and leads to hanging + * promises. + */ +export function patchExceptionBubbling(code: string) { + console.log("# patchExceptionBubbling"); + + return code.replace(/_nextBubbleNoFallback = "1"/, "_nextBubbleNoFallback = undefined"); +} From ec9c0be33b2418f2b3ed8ee712a1ee1dbc176db4 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 7 Oct 2024 08:21:21 +0100 Subject: [PATCH 3/5] review comments --- .../inline-middleware-manifest-require.ts | 11 ++++++++++- .../to-investigate/patch-exception-bubbling.ts | 8 +++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts index 791db33d..c5381e9d 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts @@ -15,5 +15,14 @@ export function inlineMiddlewareManifestRequire(code: string, config: Config) { ? JSON.parse(readFileSync(middlewareManifestPath, "utf-8")) : {}; - return code.replace(/require\(this.middlewareManifestPath\)/, JSON.stringify(middlewareManifest)); + const patchedCode = code.replace( + "require(this.middlewareManifestPath)", + JSON.stringify(middlewareManifest) + ); + + if (patchedCode === code) { + throw new Error("Cache patch not applied"); + } + + return patchedCode; } diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts index 61e74b8f..420ef51f 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts @@ -7,5 +7,11 @@ export function patchExceptionBubbling(code: string) { console.log("# patchExceptionBubbling"); - return code.replace(/_nextBubbleNoFallback = "1"/, "_nextBubbleNoFallback = undefined"); + const patchedCode = code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined"); + + if (patchedCode === code) { + throw new Error("Cache patch not applied"); + } + + return patchedCode; } From c34a715eb4bbcb314e293478eb5bc33181aa2914 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 7 Oct 2024 09:03:48 +0100 Subject: [PATCH 4/5] review comments --- .../to-investigate/inline-middleware-manifest-require.ts | 2 +- .../build/patches/to-investigate/patch-exception-bubbling.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts index c5381e9d..fb003e19 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts @@ -21,7 +21,7 @@ export function inlineMiddlewareManifestRequire(code: string, config: Config) { ); if (patchedCode === code) { - throw new Error("Cache patch not applied"); + throw new Error("Cache patch `inlineMiddlewareManifestRequire` not applied"); } return patchedCode; diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts index 420ef51f..5ed525f7 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts @@ -10,7 +10,7 @@ export function patchExceptionBubbling(code: string) { const patchedCode = code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined"); if (patchedCode === code) { - throw new Error("Cache patch not applied"); + throw new Error("Cache patch `patchExceptionBubbling` not applied"); } return patchedCode; From e8e0b0c86a8f3df2991ec79791e32ff1049b5a76 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Mon, 7 Oct 2024 10:20:04 +0200 Subject: [PATCH 5/5] log tweaks --- .../src/cli/build/patches/investigated/patch-cache.ts | 2 +- .../to-investigate/inline-middleware-manifest-require.ts | 2 +- .../build/patches/to-investigate/patch-exception-bubbling.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cloudflare/src/cli/build/patches/investigated/patch-cache.ts b/packages/cloudflare/src/cli/build/patches/investigated/patch-cache.ts index 3c521d2f..0d8bd4f8 100644 --- a/packages/cloudflare/src/cli/build/patches/investigated/patch-cache.ts +++ b/packages/cloudflare/src/cli/build/patches/investigated/patch-cache.ts @@ -5,7 +5,7 @@ import path from "node:path"; * Install the cloudflare KV cache handler */ export function patchCache(code: string, config: Config): string { - console.log("# patchCached"); + console.log("# patchCache"); const cacheHandler = path.join(config.paths.internalPackage, "cli", "cache-handler.mjs"); diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts index fb003e19..a630294b 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts @@ -21,7 +21,7 @@ export function inlineMiddlewareManifestRequire(code: string, config: Config) { ); if (patchedCode === code) { - throw new Error("Cache patch `inlineMiddlewareManifestRequire` not applied"); + throw new Error("Patch `inlineMiddlewareManifestRequire` not applied"); } return patchedCode; diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts index 5ed525f7..63e43275 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts @@ -10,7 +10,7 @@ export function patchExceptionBubbling(code: string) { const patchedCode = code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined"); if (patchedCode === code) { - throw new Error("Cache patch `patchExceptionBubbling` not applied"); + throw new Error("Patch `patchExceptionBubbling` not applied"); } return patchedCode;