From ba9af720e3a672b1fbfa9d2360c14bf582f6b53c Mon Sep 17 00:00:00 2001 From: James Anderson Date: Mon, 7 Oct 2024 09:24:43 +0100 Subject: [PATCH] feat: use incremental cache during rendering (#64) * feat: use incremental cache during rendering * patch exception bubbling --- .../cloudflare/src/cli/build/build-worker.ts | 8 +++--- .../build/patches/investigated/patch-cache.ts | 2 +- .../inline-middleware-manifest-require.ts | 28 +++++++++++++++++++ .../patch-exception-bubbling.ts | 17 +++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts 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 2acdd63e..85b358f8 100644 --- a/packages/cloudflare/src/cli/build/build-worker.ts +++ b/packages/cloudflare/src/cli/build/build-worker.ts @@ -5,8 +5,10 @@ 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 { 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"; @@ -90,10 +92,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 +164,8 @@ async function updateWorkerBundledCode(workerOutputFile: string, config: Config) patchedCode = patchFindDir(patchedCode, 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/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 new file mode 100644 index 00000000..a630294b --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts @@ -0,0 +1,28 @@ +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")) + : {}; + + const patchedCode = code.replace( + "require(this.middlewareManifestPath)", + JSON.stringify(middlewareManifest) + ); + + if (patchedCode === code) { + 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 new file mode 100644 index 00000000..63e43275 --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts @@ -0,0 +1,17 @@ +/** + * 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"); + + const patchedCode = code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined"); + + if (patchedCode === code) { + throw new Error("Patch `patchExceptionBubbling` not applied"); + } + + return patchedCode; +}