From 20187560fa127e8ffd91cf158a5fc23f440cd39e Mon Sep 17 00:00:00 2001 From: Kiko <52105584+HyperKiko@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:51:54 +0100 Subject: [PATCH 1/9] patch node-module-loader dynamic require --- .../cloudflare/src/cli/build/bundle-server.ts | 2 + .../patches/plugins/node-module-loader.ts | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts diff --git a/packages/cloudflare/src/cli/build/bundle-server.ts b/packages/cloudflare/src/cli/build/bundle-server.ts index 458bcccd..d2e01090 100644 --- a/packages/cloudflare/src/cli/build/bundle-server.ts +++ b/packages/cloudflare/src/cli/build/bundle-server.ts @@ -16,6 +16,7 @@ import { patchFetchCacheSetMissingWaitUntil } from "./patches/plugins/fetch-cach import { inlineFindDir } from "./patches/plugins/find-dir.js"; import { patchInstrumentation } from "./patches/plugins/instrumentation.js"; import { inlineLoadManifest } from "./patches/plugins/load-manifest.js"; +import { inlineNodeModuleLoader } from "./patches/plugins/node-module-loader.js"; import { handleOptionalDependencies } from "./patches/plugins/optional-deps.js"; import { patchDepdDeprecations } from "./patches/plugins/patch-depd-deprecations.js"; import { fixRequire } from "./patches/plugins/require.js"; @@ -89,6 +90,7 @@ export async function bundleServer(buildOpts: BuildOptions): Promise { plugins: [ shimRequireHook(buildOpts), inlineRequirePage(updater, buildOpts), + inlineNodeModuleLoader(updater, buildOpts), setWranglerExternal(), fixRequire(updater), handleOptionalDependencies(optionalDependencies), diff --git a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts new file mode 100644 index 00000000..abc73400 --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts @@ -0,0 +1,66 @@ +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; + +import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; +import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; + +import { normalizePath } from "../../utils/normalize-path.js"; +import { patchCode, type RuleConfig } from "../ast/util.js"; +import type { ContentUpdater } from "./content-updater.js"; + +export function inlineNodeModuleLoader(updater: ContentUpdater, buildOpts: BuildOptions) { + return updater.updateContent( + "inline-node-module-loader", + { + filter: getCrossPlatformPathRegex( + String.raw`/next/dist/server/lib/module-loader/node-module-loader\.js$`, + { escape: false } + ), + contentFilter: /class NodeModuleLoader {/, + }, + async ({ contents }) => patchCode(contents, await getRule(buildOpts)) + ); +} + +async function getRule(buildOpts: BuildOptions) { + const { outputDir } = buildOpts; + const serverDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next/server"); + + const pagesManifestFile = join(serverDir, "pages-manifest.json"); + + let pagesManifests: string[] = []; + try { + pagesManifests = Object.values(JSON.parse(await readFile(pagesManifestFile, "utf-8"))); + } catch { + // The file does not exist + pagesManifests = []; + } + + const files = pagesManifests.map((path) => normalizePath(path)); + + // Inline fs access and dynamic requires that are not supported by workerd. + const fnBody = ` +${files + .map( + (file) => `if (id.endsWith("${file}")) { + return require(${JSON.stringify(join(serverDir, file))}); +}` + ) + .join("\n")} +`; + + return { + rule: { + pattern: `class NodeModuleLoader { + async load($ID) { + $$$_BODY + } +}`, + }, + fix: `class NodeModuleLoader { + async load($ID) { + ${fnBody} + } +}`, + } satisfies RuleConfig; +} From f1cc83c6af552872a0edfeb05be30e10197571d0 Mon Sep 17 00:00:00 2001 From: Kiko <52105584+HyperKiko@users.noreply.github.com> Date: Sat, 1 Mar 2025 14:30:54 +0100 Subject: [PATCH 2/9] only require javascript files in node module loader patch --- .../src/cli/build/patches/plugins/node-module-loader.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts index abc73400..9cda39b6 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts @@ -36,7 +36,9 @@ async function getRule(buildOpts: BuildOptions) { pagesManifests = []; } - const files = pagesManifests.map((path) => normalizePath(path)); + const manifests = pagesManifests.map((path) => normalizePath(path)); + + const files = manifests.filter((file) => file.endsWith(".js")); // Inline fs access and dynamic requires that are not supported by workerd. const fnBody = ` From 1a073fffb0309084ac0f40de45da071f0683c3fa Mon Sep 17 00:00:00 2001 From: Kiko <52105584+HyperKiko@users.noreply.github.com> Date: Sat, 1 Mar 2025 20:40:49 +0100 Subject: [PATCH 3/9] clean up node module loader patch --- .../src/cli/build/patches/plugins/node-module-loader.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts index 9cda39b6..a6710977 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts @@ -33,18 +33,15 @@ async function getRule(buildOpts: BuildOptions) { pagesManifests = Object.values(JSON.parse(await readFile(pagesManifestFile, "utf-8"))); } catch { // The file does not exist - pagesManifests = []; } - const manifests = pagesManifests.map((path) => normalizePath(path)); - - const files = manifests.filter((file) => file.endsWith(".js")); + const files = pagesManifests.filter((file) => file.endsWith(".js")).map(normalizePath); // Inline fs access and dynamic requires that are not supported by workerd. const fnBody = ` ${files .map( - (file) => `if (id.endsWith("${file}")) { + (file) => `if ($ID.endsWith("${file}")) { return require(${JSON.stringify(join(serverDir, file))}); }` ) From 4c7b8f40735fefd861cb8009b19b251a1ba6189d Mon Sep 17 00:00:00 2001 From: Kiko <52105584+HyperKiko@users.noreply.github.com> Date: Sat, 1 Mar 2025 20:58:20 +0100 Subject: [PATCH 4/9] add pages api route e2e test --- examples/e2e/pages-router/e2e/api.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 examples/e2e/pages-router/e2e/api.test.ts diff --git a/examples/e2e/pages-router/e2e/api.test.ts b/examples/e2e/pages-router/e2e/api.test.ts new file mode 100644 index 00000000..12800f56 --- /dev/null +++ b/examples/e2e/pages-router/e2e/api.test.ts @@ -0,0 +1,8 @@ +import { expect, test } from "@playwright/test"; + +test("should not fail on an api route", async ({ page }) => { + const result = await page.goto("/api/hello"); + expect(result?.status()).toBe(200); + const body = await result?.json(); + expect(body).toEqual({ hello: "world" }); +}); From 6c84a10f60aa31823621fa14f3d0f0d78b30a2f7 Mon Sep 17 00:00:00 2001 From: Kiko <52105584+HyperKiko@users.noreply.github.com> Date: Sat, 1 Mar 2025 21:22:27 +0100 Subject: [PATCH 5/9] normalize id variable and escape file path --- .../src/cli/build/patches/plugins/node-module-loader.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts index a6710977..732a0a34 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts @@ -7,6 +7,7 @@ import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; import { normalizePath } from "../../utils/normalize-path.js"; import { patchCode, type RuleConfig } from "../ast/util.js"; import type { ContentUpdater } from "./content-updater.js"; +import { posix, sep } from "node:path"; export function inlineNodeModuleLoader(updater: ContentUpdater, buildOpts: BuildOptions) { return updater.updateContent( @@ -41,7 +42,7 @@ async function getRule(buildOpts: BuildOptions) { const fnBody = ` ${files .map( - (file) => `if ($ID.endsWith("${file}")) { + (file) => `if ($ID.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}).endsWith(${JSON.stringify(file)})) { return require(${JSON.stringify(join(serverDir, file))}); }` ) From d87c9c13e07645ab3048b4e2549c8076c4c2c17a Mon Sep 17 00:00:00 2001 From: Kiko <52105584+HyperKiko@users.noreply.github.com> Date: Sun, 2 Mar 2025 11:46:24 +0100 Subject: [PATCH 6/9] fix up node module loader rule and merge with require page plugin --- .../cloudflare/src/cli/build/bundle-server.ts | 6 +- .../build/patches/plugins/dynamic-requires.ts | 145 ++++++++++++++++++ .../patches/plugins/node-module-loader.ts | 66 -------- .../cli/build/patches/plugins/require-page.ts | 92 ----------- 4 files changed, 147 insertions(+), 162 deletions(-) create mode 100644 packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts delete mode 100644 packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts delete mode 100644 packages/cloudflare/src/cli/build/patches/plugins/require-page.ts diff --git a/packages/cloudflare/src/cli/build/bundle-server.ts b/packages/cloudflare/src/cli/build/bundle-server.ts index d2e01090..d3ae8a71 100644 --- a/packages/cloudflare/src/cli/build/bundle-server.ts +++ b/packages/cloudflare/src/cli/build/bundle-server.ts @@ -16,12 +16,11 @@ import { patchFetchCacheSetMissingWaitUntil } from "./patches/plugins/fetch-cach import { inlineFindDir } from "./patches/plugins/find-dir.js"; import { patchInstrumentation } from "./patches/plugins/instrumentation.js"; import { inlineLoadManifest } from "./patches/plugins/load-manifest.js"; -import { inlineNodeModuleLoader } from "./patches/plugins/node-module-loader.js"; +import { inlineDynamicRequires } from "./patches/plugins/dynamic-requires.js"; import { handleOptionalDependencies } from "./patches/plugins/optional-deps.js"; import { patchDepdDeprecations } from "./patches/plugins/patch-depd-deprecations.js"; import { fixRequire } from "./patches/plugins/require.js"; import { shimRequireHook } from "./patches/plugins/require-hook.js"; -import { inlineRequirePage } from "./patches/plugins/require-page.js"; import { setWranglerExternal } from "./patches/plugins/wrangler-external.js"; import { normalizePath, patchCodeWithValidations } from "./utils/index.js"; @@ -89,8 +88,7 @@ export async function bundleServer(buildOpts: BuildOptions): Promise { conditions: [], plugins: [ shimRequireHook(buildOpts), - inlineRequirePage(updater, buildOpts), - inlineNodeModuleLoader(updater, buildOpts), + inlineDynamicRequires(updater, buildOpts), setWranglerExternal(), fixRequire(updater), handleOptionalDependencies(optionalDependencies), diff --git a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts new file mode 100644 index 00000000..5f87386b --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts @@ -0,0 +1,145 @@ +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; + +import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; +import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; + +import { normalizePath } from "../../utils/normalize-path.js"; +import { patchCode, type RuleConfig } from "../ast/util.js"; +import type { ContentUpdater } from "./content-updater.js"; +import { posix, sep } from "node:path"; +import type { Plugin } from "esbuild"; + +async function getPagesManifests(serverDir: string): Promise { + try { + return Object.values(JSON.parse(await readFile(join(serverDir, "pages-manifest.json"), "utf-8"))); + } catch { + // The file does not exist + return []; + } +} + +async function getAppPathsManifests(serverDir: string): Promise { + try { + return Object.values(JSON.parse(await readFile(join(serverDir, "app-paths-manifest.json"), "utf-8"))); + } catch { + // The file does not exist + return []; + } +} + +function getServerDir(buildOpts: BuildOptions) { + return join(buildOpts.outputDir, "server-functions/default", getPackagePath(buildOpts), ".next/server"); +} + +function getRequires(idVariable: string, files: string[], serverDir: string) { + // Inline fs access and dynamic requires that are not supported by workerd. + return files + .map( + ( + file + ) => `if (${idVariable}.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}).endsWith(${JSON.stringify(normalizePath(file))})) { + return require(${JSON.stringify(join(serverDir, file))}); + }` + ) + .join("\n"); +} + +export function inlineDynamicRequires(updater: ContentUpdater, buildOpts: BuildOptions): Plugin { + updater.updateContent( + "inline-node-module-loader", + { + filter: getCrossPlatformPathRegex( + String.raw`/next/dist/server/lib/module-loader/node-module-loader\.js$`, + { escape: false } + ), + contentFilter: /class NodeModuleLoader {/, + }, + async ({ contents }) => patchCode(contents, await getNodeModuleLoaderRule(buildOpts)) + ); + updater.updateContent( + "inline-require-page", + { + filter: getCrossPlatformPathRegex(String.raw`/next/dist/server/require\.js$`, { escape: false }), + contentFilter: /function requirePage\(/, + }, + async ({ contents }) => patchCode(contents, await getRequirePageRule(buildOpts)) + ); + return { name: "inline-dynamic-requires", setup() {} }; +} + +async function getNodeModuleLoaderRule(buildOpts: BuildOptions) { + const serverDir = getServerDir(buildOpts); + + let manifests = await getPagesManifests(serverDir); + + const files = manifests.filter((file) => file.endsWith(".js")); + + return ` +rule: + kind: method_definition + all: + - has: + field: name + regex: ^load$ + - has: + field: parameters + has: + kind: identifier + pattern: $ID + inside: + stopBy: + kind: class_declaration + has: + field: name + regex: ^NodeModuleLoader$ +fix: | + async load($ID) { + ${getRequires("$ID", files, serverDir)} + }`; +} + +async function getRequirePageRule(buildOpts: BuildOptions) { + const serverDir = getServerDir(buildOpts); + + const pagesManifests = await getPagesManifests(serverDir); + const appPathsManifests = await getAppPathsManifests(serverDir); + + const manifests = pagesManifests.concat(appPathsManifests); + + const htmlFiles = manifests.filter((file) => file.endsWith(".html")); + const jsFiles = manifests.filter((file) => file.endsWith(".js")); + + return { + rule: { + pattern: ` +function requirePage($PAGE, $DIST_DIR, $IS_APP_PATH) { + const $_ = getPagePath($$$ARGS); + $$$_BODY +}`, + }, // Inline fs access and dynamic require that are not supported by workerd. + fix: ` +function requirePage($PAGE, $DIST_DIR, $IS_APP_PATH) { + const { platform } = require('process'); + const pagePath = platform === 'win32' ? getPagePath($$$ARGS).replaceAll('\\\\', '/') : getPagePath($$$ARGS); + + // html + ${( + await Promise.all( + htmlFiles.map( + async (file) => `if (pagePath.endsWith(${JSON.stringify(normalizePath(file))})) { + return ${JSON.stringify(await readFile(join(serverDir, file), "utf-8"))}; + }` + ) + ) + ).join("\n")} + // js + process.env.__NEXT_PRIVATE_RUNTIME_TYPE = $IS_APP_PATH ? 'app' : 'pages'; + try { + ${getRequires("pagePath", jsFiles, serverDir)} + } finally { + process.env.__NEXT_PRIVATE_RUNTIME_TYPE = ''; + } +}`, + } satisfies RuleConfig; +} diff --git a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts b/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts deleted file mode 100644 index 732a0a34..00000000 --- a/packages/cloudflare/src/cli/build/patches/plugins/node-module-loader.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { readFile } from "node:fs/promises"; -import { join } from "node:path"; - -import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; -import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; - -import { normalizePath } from "../../utils/normalize-path.js"; -import { patchCode, type RuleConfig } from "../ast/util.js"; -import type { ContentUpdater } from "./content-updater.js"; -import { posix, sep } from "node:path"; - -export function inlineNodeModuleLoader(updater: ContentUpdater, buildOpts: BuildOptions) { - return updater.updateContent( - "inline-node-module-loader", - { - filter: getCrossPlatformPathRegex( - String.raw`/next/dist/server/lib/module-loader/node-module-loader\.js$`, - { escape: false } - ), - contentFilter: /class NodeModuleLoader {/, - }, - async ({ contents }) => patchCode(contents, await getRule(buildOpts)) - ); -} - -async function getRule(buildOpts: BuildOptions) { - const { outputDir } = buildOpts; - const serverDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next/server"); - - const pagesManifestFile = join(serverDir, "pages-manifest.json"); - - let pagesManifests: string[] = []; - try { - pagesManifests = Object.values(JSON.parse(await readFile(pagesManifestFile, "utf-8"))); - } catch { - // The file does not exist - } - - const files = pagesManifests.filter((file) => file.endsWith(".js")).map(normalizePath); - - // Inline fs access and dynamic requires that are not supported by workerd. - const fnBody = ` -${files - .map( - (file) => `if ($ID.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}).endsWith(${JSON.stringify(file)})) { - return require(${JSON.stringify(join(serverDir, file))}); -}` - ) - .join("\n")} -`; - - return { - rule: { - pattern: `class NodeModuleLoader { - async load($ID) { - $$$_BODY - } -}`, - }, - fix: `class NodeModuleLoader { - async load($ID) { - ${fnBody} - } -}`, - } 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 deleted file mode 100644 index 393a1576..00000000 --- a/packages/cloudflare/src/cli/build/patches/plugins/require-page.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { readFile } from "node:fs/promises"; -import { join } from "node:path"; - -import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; -import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; - -import { normalizePath } from "../../utils/normalize-path.js"; -import { patchCode, type RuleConfig } from "../ast/util.js"; -import type { ContentUpdater } from "./content-updater.js"; - -export function inlineRequirePage(updater: ContentUpdater, buildOpts: BuildOptions) { - return updater.updateContent( - "inline-require-page", - { - filter: getCrossPlatformPathRegex(String.raw`/next/dist/server/require\.js$`, { escape: false }), - contentFilter: /function requirePage\(/, - }, - async ({ contents }) => patchCode(contents, await getRule(buildOpts)) - ); -} - -async function getRule(buildOpts: BuildOptions) { - const { outputDir } = buildOpts; - const serverDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next/server"); - - const pagesManifestFile = join(serverDir, "pages-manifest.json"); - const appPathsManifestFile = join(serverDir, "app-paths-manifest.json"); - - let pagesManifests: string[] = []; - try { - pagesManifests = Object.values(JSON.parse(await readFile(pagesManifestFile, "utf-8"))); - } catch { - // The file does not exists - pagesManifests = []; - } - - let appPathsManifests: string[]; - try { - appPathsManifests = Object.values(JSON.parse(await readFile(appPathsManifestFile, "utf-8"))); - } catch { - // The file does not exists - appPathsManifests = []; - } - - const manifests = pagesManifests.concat(appPathsManifests).map((path) => normalizePath(path)); - - const htmlFiles = manifests.filter((file) => file.endsWith(".html")); - const jsFiles = manifests.filter((file) => file.endsWith(".js")); - - // Inline fs access and dynamic require that are not supported by workerd. - const fnBody = ` -// html -${( - await Promise.all( - htmlFiles.map( - async (file) => `if (pagePath.endsWith("${file}")) { - return ${JSON.stringify(await readFile(join(serverDir, file), "utf-8"))}; - }` - ) - ) -).join("\n")} -// js -process.env.__NEXT_PRIVATE_RUNTIME_TYPE = isAppPath ? 'app' : 'pages'; -try { - ${jsFiles - .map( - (file) => `if (pagePath.endsWith("${file}")) { - return require(${JSON.stringify(join(serverDir, file))}); - }` - ) - .join("\n")} -} finally { - process.env.__NEXT_PRIVATE_RUNTIME_TYPE = ''; -} -`; - - return { - rule: { - pattern: ` -function requirePage($PAGE, $DIST_DIR, $IS_APP_PATH) { - const $_ = getPagePath($$$ARGS); - $$$_BODY -}`, - }, - fix: ` -function requirePage($PAGE, $DIST_DIR, $IS_APP_PATH) { - const { platform } = require('process'); - const pagePath = platform === 'win32' ? getPagePath($$$ARGS).replaceAll('\\\\', '/') : getPagePath($$$ARGS); - ${fnBody} -}`, - } satisfies RuleConfig; -} From 6afac13284d9ee60232934ebf0c49c5e5babadb0 Mon Sep 17 00:00:00 2001 From: Kiko <52105584+HyperKiko@users.noreply.github.com> Date: Sun, 2 Mar 2025 19:11:18 +0100 Subject: [PATCH 7/9] pnpm fix and change whitespace in getRequires --- .../cli/build/patches/plugins/dynamic-requires.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts index 5f87386b..e562b185 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts @@ -1,14 +1,13 @@ import { readFile } from "node:fs/promises"; -import { join } from "node:path"; +import { join , posix, sep } from "node:path"; import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; +import type { Plugin } from "esbuild"; import { normalizePath } from "../../utils/normalize-path.js"; import { patchCode, type RuleConfig } from "../ast/util.js"; import type { ContentUpdater } from "./content-updater.js"; -import { posix, sep } from "node:path"; -import type { Plugin } from "esbuild"; async function getPagesManifests(serverDir: string): Promise { try { @@ -36,9 +35,8 @@ function getRequires(idVariable: string, files: string[], serverDir: string) { // Inline fs access and dynamic requires that are not supported by workerd. return files .map( - ( - file - ) => `if (${idVariable}.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}).endsWith(${JSON.stringify(normalizePath(file))})) { + (file) => ` + if (${idVariable}.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}).endsWith(${JSON.stringify(normalizePath(file))})) { return require(${JSON.stringify(join(serverDir, file))}); }` ) @@ -71,7 +69,7 @@ export function inlineDynamicRequires(updater: ContentUpdater, buildOpts: BuildO async function getNodeModuleLoaderRule(buildOpts: BuildOptions) { const serverDir = getServerDir(buildOpts); - let manifests = await getPagesManifests(serverDir); + const manifests = await getPagesManifests(serverDir); const files = manifests.filter((file) => file.endsWith(".js")); From 4a30231ee4f2776c969cb273186ae5c719c4a12c Mon Sep 17 00:00:00 2001 From: Kiko <52105584+HyperKiko@users.noreply.github.com> Date: Sun, 2 Mar 2025 21:51:38 +0100 Subject: [PATCH 8/9] fix node module loader rule and add changeset --- .changeset/strange-laws-hang.md | 7 +++++++ packages/cloudflare/src/cli/build/bundle-server.ts | 2 +- .../src/cli/build/patches/plugins/dynamic-requires.ts | 7 ++++--- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 .changeset/strange-laws-hang.md diff --git a/.changeset/strange-laws-hang.md b/.changeset/strange-laws-hang.md new file mode 100644 index 00000000..74c58e01 --- /dev/null +++ b/.changeset/strange-laws-hang.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +fix pages api routes + +fixed pages api routes by inlining a dynamic require in the `NodeModuleLoader` class diff --git a/packages/cloudflare/src/cli/build/bundle-server.ts b/packages/cloudflare/src/cli/build/bundle-server.ts index d3ae8a71..4ee0a329 100644 --- a/packages/cloudflare/src/cli/build/bundle-server.ts +++ b/packages/cloudflare/src/cli/build/bundle-server.ts @@ -11,12 +11,12 @@ import { patchWebpackRuntime } from "./patches/ast/webpack-runtime.js"; import * as patches from "./patches/index.js"; import { inlineBuildId } from "./patches/plugins/build-id.js"; import { ContentUpdater } from "./patches/plugins/content-updater.js"; +import { inlineDynamicRequires } from "./patches/plugins/dynamic-requires.js"; import { inlineEvalManifest } from "./patches/plugins/eval-manifest.js"; import { patchFetchCacheSetMissingWaitUntil } from "./patches/plugins/fetch-cache-wait-until.js"; import { inlineFindDir } from "./patches/plugins/find-dir.js"; import { patchInstrumentation } from "./patches/plugins/instrumentation.js"; import { inlineLoadManifest } from "./patches/plugins/load-manifest.js"; -import { inlineDynamicRequires } from "./patches/plugins/dynamic-requires.js"; import { handleOptionalDependencies } from "./patches/plugins/optional-deps.js"; import { patchDepdDeprecations } from "./patches/plugins/patch-depd-deprecations.js"; import { fixRequire } from "./patches/plugins/require.js"; diff --git a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts index e562b185..146b0715 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts @@ -1,5 +1,5 @@ import { readFile } from "node:fs/promises"; -import { join , posix, sep } from "node:path"; +import { join, posix, sep } from "node:path"; import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; @@ -83,11 +83,12 @@ rule: - has: field: parameters has: - kind: identifier + kind: required_parameter pattern: $ID inside: - stopBy: + stopBy: kind: class_declaration + kind: class_declaration has: field: name regex: ^NodeModuleLoader$ From df638ffece7312358b4631e4179ac2e9569554e8 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Mon, 3 Mar 2025 08:47:30 +0100 Subject: [PATCH 9/9] fixup! path separator --- .../src/cli/build/patches/plugins/dynamic-requires.ts | 7 +++---- .../src/cli/build/patches/plugins/eval-manifest.ts | 5 ++--- .../cloudflare/src/cli/build/patches/plugins/find-dir.ts | 5 ++--- .../src/cli/build/patches/plugins/load-manifest.ts | 5 ++--- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts index 146b0715..436ee54b 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts @@ -93,7 +93,7 @@ rule: field: name regex: ^NodeModuleLoader$ fix: | - async load($ID) { + async load($ID) { ${getRequires("$ID", files, serverDir)} }`; } @@ -119,9 +119,8 @@ function requirePage($PAGE, $DIST_DIR, $IS_APP_PATH) { }, // Inline fs access and dynamic require that are not supported by workerd. fix: ` function requirePage($PAGE, $DIST_DIR, $IS_APP_PATH) { - const { platform } = require('process'); - const pagePath = platform === 'win32' ? getPagePath($$$ARGS).replaceAll('\\\\', '/') : getPagePath($$$ARGS); - + const pagePath = getPagePath($$$ARGS).replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}); + // html ${( await Promise.all( diff --git a/packages/cloudflare/src/cli/build/patches/plugins/eval-manifest.ts b/packages/cloudflare/src/cli/build/patches/plugins/eval-manifest.ts index d2204785..0f7a464f 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/eval-manifest.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/eval-manifest.ts @@ -3,7 +3,7 @@ * that are not supported by workerd. */ -import { join, relative } from "node:path"; +import { join, posix, relative, sep } from "node:path"; import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; @@ -62,8 +62,7 @@ function evalManifest($PATH, $$$ARGS) { }, fix: ` function evalManifest($PATH, $$$ARGS) { - const { platform } = require('process'); - $PATH = platform === 'win32' ? $PATH.replaceAll('\\\\', '/') : $PATH; + $PATH = $PATH.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}); ${returnManifests} throw new Error(\`Unexpected evalManifest(\${$PATH}) call!\`); }`, diff --git a/packages/cloudflare/src/cli/build/patches/plugins/find-dir.ts b/packages/cloudflare/src/cli/build/patches/plugins/find-dir.ts index 1cc0ee40..6da7a428 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/find-dir.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/find-dir.ts @@ -3,7 +3,7 @@ */ import { existsSync } from "node:fs"; -import { join } from "node:path"; +import { join, posix, sep } from "node:path"; import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; @@ -35,8 +35,7 @@ rule: pattern: function findDir($DIR, $NAME) { $$$_ } fix: |- function findDir($DIR, $NAME) { - const { platform } = require('process'); - $DIR = platform === 'win32' ? $DIR.replaceAll('\\\\', '/') : $DIR; + $DIR = $DIR.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}); if ($DIR.endsWith(".next/server")) { if ($NAME === "app") { return ${appExists}; diff --git a/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts b/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts index e471be62..e756d19e 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts @@ -3,7 +3,7 @@ */ import { readFile } from "node:fs/promises"; -import { join, relative } from "node:path"; +import { join, posix, relative, sep } from "node:path"; import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; @@ -53,8 +53,7 @@ function loadManifest($PATH, $$$ARGS) { }, fix: ` function loadManifest($PATH, $$$ARGS) { - const { platform } = require('process'); - $PATH = platform === 'win32' ? $PATH.replaceAll('\\\\', '/') : $PATH; + $PATH = $PATH.replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)}); ${returnManifests} throw new Error(\`Unexpected loadManifest(\${$PATH}) call!\`); }`,