Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: opennextjs/opennextjs-cloudflare
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: @opennextjs/cloudflare@0.5.7
Choose a base ref
...
head repository: opennextjs/opennextjs-cloudflare
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: @opennextjs/cloudflare@0.5.8
Choose a head ref
  • 2 commits
  • 9 files changed
  • 3 contributors

Commits on Mar 3, 2025

  1. patch node-module-loader dynamic require (#431)

    HyperKiko authored Mar 3, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    9ad6714 View commit details
  2. Version Packages (#434)

    Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
    workers-frameworks and github-actions[bot] authored Mar 3, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    ff5cd39 View commit details
8 changes: 8 additions & 0 deletions examples/e2e/pages-router/e2e/api.test.ts
Original file line number Diff line number Diff line change
@@ -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" });
});
8 changes: 8 additions & 0 deletions packages/cloudflare/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @opennextjs/cloudflare

## 0.5.8

### Patch Changes

- [#431](https://github.com/opennextjs/opennextjs-cloudflare/pull/431) [`9ad6714`](https://github.com/opennextjs/opennextjs-cloudflare/commit/9ad67145ee718c67b94bbfcbc144a565b3fd0dae) Thanks [@HyperKiko](https://github.com/HyperKiko)! - fix pages api routes

fixed pages api routes by inlining a dynamic require in the `NodeModuleLoader` class

## 0.5.7

### Patch Changes
2 changes: 1 addition & 1 deletion packages/cloudflare/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@opennextjs/cloudflare",
"description": "Cloudflare builder for next apps",
"version": "0.5.7",
"version": "0.5.8",
"type": "module",
"scripts": {
"clean": "rimraf dist",
4 changes: 2 additions & 2 deletions packages/cloudflare/src/cli/build/bundle-server.ts
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ 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";
@@ -20,7 +21,6 @@ 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";

@@ -88,7 +88,7 @@ export async function bundleServer(buildOpts: BuildOptions): Promise<void> {
conditions: [],
plugins: [
shimRequireHook(buildOpts),
inlineRequirePage(updater, buildOpts),
inlineDynamicRequires(updater, buildOpts),
setWranglerExternal(),
fixRequire(updater),
handleOptionalDependencies(optionalDependencies),
143 changes: 143 additions & 0 deletions packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { readFile } from "node:fs/promises";
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";

async function getPagesManifests(serverDir: string): Promise<string[]> {
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<string[]> {
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);

const 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: required_parameter
pattern: $ID
inside:
stopBy:
kind: class_declaration
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 pagePath = getPagePath($$$ARGS).replaceAll(${JSON.stringify(sep)}, ${JSON.stringify(posix.sep)});
// 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;
}
Original file line number Diff line number Diff line change
@@ -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!\`);
}`,
5 changes: 2 additions & 3 deletions packages/cloudflare/src/cli/build/patches/plugins/find-dir.ts
Original file line number Diff line number Diff line change
@@ -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};
Original file line number Diff line number Diff line change
@@ -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!\`);
}`,
92 changes: 0 additions & 92 deletions packages/cloudflare/src/cli/build/patches/plugins/require-page.ts

This file was deleted.