Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: migrate the requirePage patch to ast-grep #287

Merged
merged 5 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from 3 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
17 changes: 14 additions & 3 deletions packages/cloudflare/src/cli/build/bundle-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { build, Plugin } from "esbuild";

import { patchOptionalDependencies } from "./patches/ast/optional-deps.js";
import * as patches from "./patches/index.js";
import inlineRequirePagePlugin from "./patches/plugins/require-page.js";
import setWranglerExternal from "./patches/plugins/wrangler-external.js";
import { normalizePath, patchCodeWithValidations } from "./utils/index.js";

/** The dist directory of the Cloudflare adapter package */
Expand Down Expand Up @@ -48,8 +50,18 @@ export async function bundleServer(buildOpts: BuildOptions): Promise<void> {
format: "esm",
target: "esnext",
minify: false,
plugins: [createFixRequiresESBuildPlugin(buildOpts)],
external: ["./middleware/handler.mjs", "caniuse-lite"],
plugins: [
createFixRequiresESBuildPlugin(buildOpts),
inlineRequirePagePlugin(buildOpts),
setWranglerExternal(),
],
external: [
"./middleware/handler.mjs",
// Next optional dependencies.
vicb marked this conversation as resolved.
Show resolved Hide resolved
"caniuse-lite",
"jimp",
"probe-image-size",
],
alias: {
// Note: we apply an empty shim to next/dist/compiled/ws because it generates two `eval`s:
// eval("require")("bufferutil");
Expand Down Expand Up @@ -146,7 +158,6 @@ async function updateWorkerBundledCode(workerOutputFile: string, buildOpts: Buil
["require", patches.patchRequire],
["`buildId` function", (code) => patches.patchBuildId(code, buildOpts)],
["`loadManifest` function", (code) => patches.patchLoadManifest(code, buildOpts)],
["next's require", (code) => patches.inlineNextRequire(code, buildOpts)],
["`findDir` function", (code) => patches.patchFindDir(code, buildOpts)],
["`evalManifest` function", (code) => patches.inlineEvalManifest(code, buildOpts)],
["cacheHandler", (code) => patches.patchCache(code, buildOpts)],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type SgNode } from "@ast-grep/napi";
import { applyRule } from "./util.js";

/**
* Handle optional dependencies.
* Handles optional dependencies.
*
* A top level `require(optionalDep)` would throw when the dep is not installed.
*
Expand All @@ -16,7 +16,7 @@ rule:
pattern: $MOD
kind: string_fragment
stopBy: end
regex: ^caniuse-lite(/|$)
regex: ^caniuse-lite(/|$)|jimp(/|$)|probe-image-size(/|$)
vicb marked this conversation as resolved.
Show resolved Hide resolved
not:
inside:
kind: try_statement
Expand Down
88 changes: 88 additions & 0 deletions packages/cloudflare/src/cli/build/patches/plugins/require-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { existsSync, readFileSync } from "node:fs";
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 type { PluginBuild } from "esbuild";

import { patchCode, type RuleConfig } from "../ast/util.js";

export default function inlineRequirePagePlugin(buildOpts: BuildOptions) {
return {
name: "inline-require-page",

setup: async (build: PluginBuild) => {
build.onLoad(
{
filter: getCrossPlatformPathRegex(String.raw`/next/dist/server/require\.js$`, { escape: false }),
},
async ({ path }) => {
const jsCode = await readFile(path, "utf8");
if (/function requirePage\(/.test(jsCode)) {
return { contents: patchCode(jsCode, getRule(buildOpts)) };
}
}
);
},
};
}

function getRule(buildOpts: BuildOptions) {
// Load manifests
vicb marked this conversation as resolved.
Show resolved Hide resolved
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");

const pagesManifests: string[] = existsSync(pagesManifestFile)
? Object.values(JSON.parse(readFileSync(pagesManifestFile, "utf-8")))
: [];
const appPathsManifests: string[] = existsSync(appPathsManifestFile)
? Object.values(JSON.parse(readFileSync(appPathsManifestFile, "utf-8")))
: [];
const manifests = pagesManifests.concat(appPathsManifests);

const htmlFiles = manifests.filter((file) => file.endsWith(".html"));
const jsFiles = manifests.filter((file) => file.endsWith(".js"));

const fnBody = `
// html
${htmlFiles
.map(
(file) => `if (pagePath.endsWith("${file}")) {
return ${JSON.stringify(readFileSync(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_APPP_ATH) {
const $_ = getPagePath($$$ARGS);
$$$_BODY
}`,
},
fix: `
function requirePage($PAGE, $DIST_DIR, $IS_APPP_ATH) {
const pagePath = getPagePath($$$ARGS);
${fnBody}
}`,
} satisfies RuleConfig;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Makes files handled by wrangler external.
*
* Paths need to be absolute so that they are valid in the output bundle.
*/
vicb marked this conversation as resolved.
Show resolved Hide resolved

import { dirname, resolve } from "node:path";

import type { PluginBuild } from "esbuild";

export default function setWranglerExternal() {
return {
name: "wrangler-externals",

setup: async (build: PluginBuild) => {
const namespace = "wrangler-externals-plugin";

build.onResolve({ filter: /(\.bin|\.wasm\?module)$/ }, ({ path, importer }) => {
return {
path: resolve(dirname(importer), path),
namespace,
external: true,
};
});

build.onLoad({ filter: /.*/, namespace }, async ({ path }) => {
return {
contents: `export * from '${path}';`,
};
});
},
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export * from "./inline-eval-manifest.js";
export * from "./inline-middleware-manifest-require.js";
export * from "./inline-next-require.js";
export * from "./patch-exception-bubbling.js";
export * from "./patch-find-dir.js";
export * from "./patch-load-instrumentation-module.js";
Expand Down

This file was deleted.

Loading