From 2edb99ad738b739b74654d45baafc8133ac36486 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 20 Feb 2025 10:45:20 +0100 Subject: [PATCH 1/3] refactor: patch getBuildId using ast-grep --- .../cloudflare/src/cli/build/bundle-server.ts | 3 +- .../cloudflare/src/cli/build/patches/index.ts | 2 +- .../src/cli/build/patches/plugins/build-id.ts | 31 +++++++++++++++++++ .../cli/build/patches/to-investigate/index.ts | 2 -- ...quire.ts => inline-middleware-manifest.ts} | 0 .../patches/to-investigate/patch-read-file.ts | 11 ------- 6 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 packages/cloudflare/src/cli/build/patches/plugins/build-id.ts delete mode 100644 packages/cloudflare/src/cli/build/patches/to-investigate/index.ts rename packages/cloudflare/src/cli/build/patches/to-investigate/{inline-middleware-manifest-require.ts => inline-middleware-manifest.ts} (100%) delete mode 100644 packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts diff --git a/packages/cloudflare/src/cli/build/bundle-server.ts b/packages/cloudflare/src/cli/build/bundle-server.ts index 68b790ca..ce2d2b68 100644 --- a/packages/cloudflare/src/cli/build/bundle-server.ts +++ b/packages/cloudflare/src/cli/build/bundle-server.ts @@ -9,6 +9,7 @@ import { build } from "esbuild"; 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 { inlineBuildId } from "./patches/plugins/build-id.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"; @@ -95,6 +96,7 @@ export async function bundleServer(buildOpts: BuildOptions): Promise { inlineEvalManifest(updater, buildOpts), inlineFindDir(updater, buildOpts), inlineLoadManifest(updater, buildOpts), + inlineBuildId(updater), // Apply updater updaters, must be the last plugin updater.plugin, ], @@ -197,7 +199,6 @@ export async function updateWorkerBundledCode( const patchedCode = await patchCodeWithValidations(code, [ ["require", patches.patchRequire], - ["`buildId` function", (code) => patches.patchBuildId(code, buildOpts)], ["cacheHandler", (code) => patches.patchCache(code, buildOpts)], [ "'require(this.middlewareManifestPath)'", diff --git a/packages/cloudflare/src/cli/build/patches/index.ts b/packages/cloudflare/src/cli/build/patches/index.ts index d556d6cb..dd308a05 100644 --- a/packages/cloudflare/src/cli/build/patches/index.ts +++ b/packages/cloudflare/src/cli/build/patches/index.ts @@ -1,2 +1,2 @@ export * from "./investigated/index.js"; -export * from "./to-investigate/index.js"; +export * from "./to-investigate/inline-middleware-manifest.js"; diff --git a/packages/cloudflare/src/cli/build/patches/plugins/build-id.ts b/packages/cloudflare/src/cli/build/patches/plugins/build-id.ts new file mode 100644 index 00000000..8751cf43 --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/plugins/build-id.ts @@ -0,0 +1,31 @@ +/** + * Inline `getBuildId` as it relies on `readFileSync`that is not supported by workerd. + */ + +import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; + +import { patchCode } from "../ast/util.js"; +import type { ContentUpdater } from "./content-updater.js"; + +export function inlineBuildId(updater: ContentUpdater) { + return updater.updateContent( + "inline-build-id", + { + filter: getCrossPlatformPathRegex(String.raw`/next/dist/server/next-server\.js$`, { escape: false }), + contentFilter: /getBuildId\(/, + }, + async ({ contents }) => patchCode(contents, rule) + ); +} + +const rule = ` +rule: + kind: method_definition + has: + field: name + regex: ^getBuildId$ +fix: |- + getBuildId() { + return process.env.NEXT_BUILD_ID; + } +`; diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/index.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/index.ts deleted file mode 100644 index c3e5da37..00000000 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./inline-middleware-manifest-require.js"; -export * from "./patch-read-file.js"; diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest.ts similarity index 100% rename from packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts rename to packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest.ts diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts deleted file mode 100644 index 950c1794..00000000 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { type BuildOptions, getBuildId } from "@opennextjs/aws/build/helper.js"; - -export function patchBuildId(code: string, buildOpts: BuildOptions): string { - // The Next code gets the buildId from the filesystem so we hardcode the value at build time. - return code.replace( - "getBuildId() {", - `getBuildId() { - return ${JSON.stringify(getBuildId(buildOpts))}; - ` - ); -} From 675abe4ba645810fc0e2ca0cf6adc9a76a7ffcd4 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 20 Feb 2025 11:28:55 +0100 Subject: [PATCH 2/3] fixup! add test --- .../src/cli/build/patches/plugins/build-id.ts | 4 +- .../build/patches/plugins/find-dir.spec.ts | 85 +++++++++++++++++++ .../build/patches/plugins/load-manifest.ts | 2 +- 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 packages/cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts diff --git a/packages/cloudflare/src/cli/build/patches/plugins/build-id.ts b/packages/cloudflare/src/cli/build/patches/plugins/build-id.ts index 8751cf43..242b69ab 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/build-id.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/build-id.ts @@ -1,5 +1,5 @@ /** - * Inline `getBuildId` as it relies on `readFileSync`that is not supported by workerd. + * Inline `getBuildId` as it relies on `readFileSync` that is not supported by workerd. */ import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; @@ -18,7 +18,7 @@ export function inlineBuildId(updater: ContentUpdater) { ); } -const rule = ` +export const rule = ` rule: kind: method_definition has: diff --git a/packages/cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts b/packages/cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts new file mode 100644 index 00000000..f6fa9148 --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts @@ -0,0 +1,85 @@ +import { describe, expect, test } from "vitest"; + +import { patchCode } from "../ast/util.js"; +import { rule } from "./build-id.js"; + +describe("getBuildId", () => { + test("patch", () => { + const code = ` +class NextNodeServer extends _baseserver.default { + constructor(options){ + // Initialize super class + super(options); + this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ }; + } + async handleUpgrade() { + // The web server does not support web sockets, it's only used for HMR in + // development. + } + loadEnvConfig({ dev, forceReload, silent }) { + (0, _env.loadEnvConfig)(this.dir, dev, silent ? { + info: ()=>{}, + error: ()=>{} + } : _log, forceReload); + } + async hasPage(pathname) { + var _this_nextConfig_i18n; + return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app); + } + getBuildId() { + const buildIdFile = (0, _path.join)(this.distDir, _constants.BUILD_ID_FILE); + try { + return _fs.default.readFileSync(buildIdFile, "utf8").trim(); + } catch (err) { + if (err.code === "ENOENT") { + throw new Error(\`Could not find a production build in the '${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id\`); + } + throw err; + } + } + getEnabledDirectories(dev) { + const dir = dev ? this.dir : this.serverDistDir; + return { + app: (0, _findpagesdir.findDir)(dir, "app") ? true : false, + pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false + }; + } + // ... +}`; + + expect(patchCode(code, rule)).toMatchInlineSnapshot(` + "class NextNodeServer extends _baseserver.default { + constructor(options){ + // Initialize super class + super(options); + this.handleNextImageRequest = async (req, res, parsedUrl) => { /* ... */ }; + } + async handleUpgrade() { + // The web server does not support web sockets, it's only used for HMR in + // development. + } + loadEnvConfig({ dev, forceReload, silent }) { + (0, _env.loadEnvConfig)(this.dir, dev, silent ? { + info: ()=>{}, + error: ()=>{} + } : _log, forceReload); + } + async hasPage(pathname) { + var _this_nextConfig_i18n; + return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.enabledDirectories.app); + } + getBuildId() { + return process.env.NEXT_BUILD_ID; + } + getEnabledDirectories(dev) { + const dir = dev ? this.dir : this.serverDistDir; + return { + app: (0, _findpagesdir.findDir)(dir, "app") ? true : false, + pages: (0, _findpagesdir.findDir)(dir, "pages") ? true : false + }; + } + // ... + }" + `); + }); +}); 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 7689e0c1..9d2fef04 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts @@ -1,5 +1,5 @@ /** - * Inline `loadManifest` as it relies on `readFileSync`that is not supported by workerd. + * Inline `loadManifest` as it relies on `readFileSync` that is not supported by workerd. */ import { readFile } from "node:fs/promises"; From 0422e42a1887d571b3ba1ef8b9405ad82777a166 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 20 Feb 2025 11:34:53 +0100 Subject: [PATCH 3/3] fixup! escape --- .../cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts b/packages/cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts index f6fa9148..6a532a5f 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/find-dir.spec.ts @@ -32,7 +32,7 @@ class NextNodeServer extends _baseserver.default { return _fs.default.readFileSync(buildIdFile, "utf8").trim(); } catch (err) { if (err.code === "ENOENT") { - throw new Error(\`Could not find a production build in the '${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id\`); + throw new Error(\`Could not find a production build in the '\${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id\`); } throw err; }