Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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" });
});
2 changes: 2 additions & 0 deletions packages/cloudflare/src/cli/build/bundle-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -89,6 +90,7 @@ export async function bundleServer(buildOpts: BuildOptions): Promise<void> {
plugins: [
shimRequireHook(buildOpts),
inlineRequirePage(updater, buildOpts),
inlineNodeModuleLoader(updater, buildOpts),
setWranglerExternal(),
fixRequire(updater),
handleOptionalDependencies(optionalDependencies),
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
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;
}