diff --git a/packages/cloudflare/src/cli/build/bundle-server.ts b/packages/cloudflare/src/cli/build/bundle-server.ts index e4cc837e..7e8966c1 100644 --- a/packages/cloudflare/src/cli/build/bundle-server.ts +++ b/packages/cloudflare/src/cli/build/bundle-server.ts @@ -10,12 +10,13 @@ import { patchVercelOgLibrary } from "./patches/ast/patch-vercel-og-library.js"; import { patchWebpackRuntime } from "./patches/ast/webpack-runtime.js"; import * as patches from "./patches/index.js"; import { ContentUpdater } from "./patches/plugins/content-updater.js"; +import { inlineEvalManifest } from "./patches/plugins/eval-manifest.js"; import { patchFetchCacheSetMissingWaitUntil } from "./patches/plugins/fetch-cache-wait-until.js"; import { patchLoadInstrumentation } from "./patches/plugins/load-instrumentation.js"; import { handleOptionalDependencies } from "./patches/plugins/optional-deps.js"; import { fixRequire } from "./patches/plugins/require.js"; import { shimRequireHook } from "./patches/plugins/require-hook.js"; -import { inlineRequirePagePlugin } from "./patches/plugins/require-page.js"; +import { inlineRequirePage } from "./patches/plugins/require-page.js"; import { setWranglerExternal } from "./patches/plugins/wrangler-external.js"; import { normalizePath, patchCodeWithValidations } from "./utils/index.js"; @@ -83,12 +84,13 @@ export async function bundleServer(buildOpts: BuildOptions): Promise { conditions: [], plugins: [ shimRequireHook(buildOpts), - inlineRequirePagePlugin(updater, buildOpts), + inlineRequirePage(updater, buildOpts), setWranglerExternal(), fixRequire(updater), handleOptionalDependencies(optionalDependencies), patchLoadInstrumentation(updater), patchFetchCacheSetMissingWaitUntil(updater), + inlineEvalManifest(updater, buildOpts), // Apply updater updaters, must be the last plugin updater.plugin, ], @@ -194,7 +196,6 @@ export async function updateWorkerBundledCode( ["`buildId` function", (code) => patches.patchBuildId(code, buildOpts)], ["`loadManifest` function", (code) => patches.patchLoadManifest(code, buildOpts)], ["`findDir` function", (code) => patches.patchFindDir(code, buildOpts)], - ["`evalManifest` function", (code) => patches.inlineEvalManifest(code, buildOpts)], ["cacheHandler", (code) => patches.patchCache(code, buildOpts)], [ "'require(this.middlewareManifestPath)'", diff --git a/packages/cloudflare/src/cli/build/patches/plugins/eval-manifest.ts b/packages/cloudflare/src/cli/build/patches/plugins/eval-manifest.ts new file mode 100644 index 00000000..aeb679b0 --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/plugins/eval-manifest.ts @@ -0,0 +1,67 @@ +/** + * Inline `evalManifest` as it relies on `readFileSync` and `runInNewContext` + * that are not supported by workerd. + */ + +import { join, relative } from "node:path"; + +import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; +import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; +import { glob } from "glob"; + +import { normalizePath } from "../../utils/normalize-path.js"; +import { patchCode, type RuleConfig } from "../ast/util.js"; +import type { ContentUpdater } from "./content-updater.js"; + +export function inlineEvalManifest(updater: ContentUpdater, buildOpts: BuildOptions) { + return updater.updateContent( + "inline-eval-manifest", + { + filter: getCrossPlatformPathRegex(String.raw`/next/dist/server/load-manifest\.js$`, { escape: false }), + contentFilter: /function evalManifest\(/, + }, + async ({ contents }) => patchCode(contents, await getRule(buildOpts)) + ); +} + +async function getRule(buildOpts: BuildOptions) { + const { outputDir } = buildOpts; + + const baseDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next"); + const appDir = join(baseDir, "server/app"); + const manifests = await glob(join(baseDir, "**/*_client-reference-manifest.js")); + + const returnManifests = manifests + .map((manifest) => { + const endsWith = normalizePath(relative(baseDir, manifest)); + const key = normalizePath("/" + relative(appDir, manifest)).replace( + "_client-reference-manifest.js", + "" + ); + return ` + if ($PATH.endsWith("${endsWith}")) { + require(${JSON.stringify(manifest)}); + return { + __RSC_MANIFEST: { + "${key}": globalThis.__RSC_MANIFEST["${key}"], + }, + }; + } + `; + }) + .join("\n"); + + return { + rule: { + pattern: ` +function evalManifest($PATH, $$$ARGS) { + $$$_ +}`, + }, + fix: ` +function evalManifest($PATH, $$$ARGS) { + ${returnManifests} + throw new Error("Unknown evalManifest: " + $PATH); +}`, + } satisfies RuleConfig; +} diff --git a/packages/cloudflare/src/cli/build/patches/plugins/require-page.ts b/packages/cloudflare/src/cli/build/patches/plugins/require-page.ts index 92d56974..c1a12a68 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/require-page.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/require-page.ts @@ -7,7 +7,7 @@ import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; import { patchCode, type RuleConfig } from "../ast/util.js"; import type { ContentUpdater } from "./content-updater.js"; -export function inlineRequirePagePlugin(updater: ContentUpdater, buildOpts: BuildOptions) { +export function inlineRequirePage(updater: ContentUpdater, buildOpts: BuildOptions) { return updater.updateContent( "inline-require-page", { diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/index.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/index.ts index a98f9172..c6fd4fe2 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/index.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/index.ts @@ -1,4 +1,3 @@ -export * from "./inline-eval-manifest.js"; export * from "./inline-middleware-manifest-require.js"; export * from "./patch-exception-bubbling.js"; export * from "./patch-find-dir.js"; diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts deleted file mode 100644 index b0f34ce0..00000000 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { join, relative } from "node:path"; - -import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; -import { globSync } from "glob"; - -import { normalizePath } from "../../utils/index.js"; - -/** - * `evalManifest` relies on readFileSync so we need to patch the function so that it instead returns the content of the manifest files - * which are known at build time - * (source: https://github.com/vercel/next.js/blob/b1e32c5d1f/packages/next/src/server/load-manifest.ts#L72) - * Note: we could/should probably just patch readFileSync here or something, but here the issue is that after the readFileSync call - * there is a vm `runInNewContext` call which we also don't support (source: https://github.com/vercel/next.js/blob/b1e32c5d1f/packages/next/src/server/load-manifest.ts#L88) - */ -export function inlineEvalManifest(code: string, buildOpts: BuildOptions): string { - const { outputDir } = buildOpts; - - const baseDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next"); - const appDir = join(baseDir, "server/app"); - - const manifests = globSync(join(baseDir, "**/*_client-reference-manifest.js")); - - return code.replace( - /function evalManifest\((.+?), .+?\) {/, - `$& - ${manifests - .map((manifest) => { - const endsWith = normalizePath(relative(baseDir, manifest)); - const key = normalizePath("/" + relative(appDir, manifest)).replace( - "_client-reference-manifest.js", - "" - ); - return ` - if ($1.endsWith("${endsWith}")) { - require(${JSON.stringify(manifest)}); - return { - __RSC_MANIFEST: { - "${key}": globalThis.__RSC_MANIFEST["${key}"], - }, - }; - } - `; - }) - .join("\n")} - throw new Error("Unknown evalManifest: " + $1); - ` - ); -}