Skip to content

Commit 27a18df

Browse files
make sure all code patch steps are validated (#186)
make sure all code patch steps are validated and improve the code patching validation by removing the validation for each single patch function and introducing a new `patchCodeWithValidations` that applies all the patches with validations all in a single place
1 parent ea2fbfa commit 27a18df

File tree

13 files changed

+88
-83
lines changed

13 files changed

+88
-83
lines changed

packages/cloudflare/src/cli/build/bundle-server.ts

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,7 @@ import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
77
import { build, Plugin } from "esbuild";
88

99
import { Config } from "../config";
10-
import { copyPackageCliFiles } from "./patches/investigated/copy-package-cli-files";
11-
import { patchCache } from "./patches/investigated/patch-cache";
12-
import { patchRequire } from "./patches/investigated/patch-require";
13-
import { updateWebpackChunksFile } from "./patches/investigated/update-webpack-chunks-file";
14-
import { inlineEvalManifest } from "./patches/to-investigate/inline-eval-manifest";
15-
import { inlineMiddlewareManifestRequire } from "./patches/to-investigate/inline-middleware-manifest-require";
16-
import { inlineNextRequire } from "./patches/to-investigate/inline-next-require";
17-
import { patchExceptionBubbling } from "./patches/to-investigate/patch-exception-bubbling";
18-
import { patchFindDir } from "./patches/to-investigate/patch-find-dir";
19-
import { patchLoadInstrumentationModule } from "./patches/to-investigate/patch-load-instrumentation-module";
20-
import { patchReadFile } from "./patches/to-investigate/patch-read-file";
21-
import { patchWranglerDeps } from "./patches/to-investigate/wrangler-deps";
10+
import * as patches from "./patches";
2211
import { copyPrerenderedRoutes } from "./utils";
2312

2413
/** The dist directory of the Cloudflare adapter package */
@@ -31,7 +20,7 @@ export async function bundleServer(config: Config, openNextOptions: BuildOptions
3120
// Copy over prerendered assets (e.g. SSG routes)
3221
copyPrerenderedRoutes(config);
3322

34-
copyPackageCliFiles(packageDistDir, config, openNextOptions);
23+
patches.copyPackageCliFiles(packageDistDir, config, openNextOptions);
3524

3625
const nextConfigStr =
3726
fs
@@ -40,8 +29,8 @@ export async function bundleServer(config: Config, openNextOptions: BuildOptions
4029

4130
console.log(`\x1b[35m⚙️ Bundling the OpenNext server...\n\x1b[0m`);
4231

43-
patchWranglerDeps(config);
44-
updateWebpackChunksFile(config);
32+
patches.patchWranglerDeps(config);
33+
patches.updateWebpackChunksFile(config);
4534

4635
const { appBuildOutputPath, appPath, outputDir, monorepoRoot } = openNextOptions;
4736
const outputPath = path.join(outputDir, "server-functions", "default");
@@ -155,25 +144,35 @@ async function updateWorkerBundledCode(
155144
config: Config,
156145
openNextOptions: BuildOptions
157146
): Promise<void> {
158-
const originalCode = await readFile(workerOutputFile, "utf8");
159-
160-
let patchedCode = originalCode;
161-
162-
patchedCode = patchRequire(patchedCode);
163-
patchedCode = patchReadFile(patchedCode, config);
164-
patchedCode = inlineNextRequire(patchedCode, config);
165-
patchedCode = patchFindDir(patchedCode, config);
166-
patchedCode = inlineEvalManifest(patchedCode, config);
167-
patchedCode = await patchCache(patchedCode, openNextOptions);
168-
patchedCode = inlineMiddlewareManifestRequire(patchedCode, config);
169-
patchedCode = patchExceptionBubbling(patchedCode);
170-
patchedCode = patchLoadInstrumentationModule(patchedCode);
171-
172-
patchedCode = patchedCode
173-
// workers do not support dynamic require nor require.resolve
174-
// TODO: implement for cf (possibly in @opennextjs/aws)
175-
.replace("patchAsyncStorage();", "//patchAsyncStorage();")
176-
.replace('require.resolve("./cache.cjs")', '"unused"');
147+
const code = await readFile(workerOutputFile, "utf8");
148+
149+
const patchedCode = await patchCodeWithValidations(code, [
150+
["require", patches.patchRequire],
151+
["`buildId` function", (code) => patches.patchBuildId(code, config)],
152+
["`loadManifest` function", (code) => patches.patchLoadManifest(code, config)],
153+
["next's require", (code) => patches.inlineNextRequire(code, config)],
154+
["`findDir` function", (code) => patches.patchFindDir(code, config)],
155+
["`evalManifest` function", (code) => patches.inlineEvalManifest(code, config)],
156+
["cacheHandler", (code) => patches.patchCache(code, openNextOptions)],
157+
[
158+
"'require(this.middlewareManifestPath)'",
159+
(code) => patches.inlineMiddlewareManifestRequire(code, config),
160+
],
161+
["exception bubbling", patches.patchExceptionBubbling],
162+
["`loadInstrumentationModule` function", patches.patchLoadInstrumentationModule],
163+
[
164+
"`patchAsyncStorage` call",
165+
(code) =>
166+
code
167+
// TODO: implement for cf (possibly in @opennextjs/aws)
168+
.replace("patchAsyncStorage();", "//patchAsyncStorage();"),
169+
],
170+
[
171+
"`require.resolve` call",
172+
// workers do not support dynamic require nor require.resolve
173+
(code) => code.replace('require.resolve("./cache.cjs")', '"unused"'),
174+
],
175+
]);
177176

178177
await writeFile(workerOutputFile, patchedCode);
179178
}
@@ -192,3 +191,34 @@ function createFixRequiresESBuildPlugin(config: Config): Plugin {
192191
},
193192
};
194193
}
194+
195+
/**
196+
* Applies multiple code patches in order to a given piece of code, at each step it validates that the code
197+
* has actually been patched/changed, if not an error is thrown
198+
*
199+
* @param code the code to apply the patches to
200+
* @param patches array of tuples, containing a string indicating the target of the patching (for logging) and
201+
* a patching function that takes a string (pre-patch code) and returns a string (post-patch code)
202+
* @returns the patched code
203+
*/
204+
async function patchCodeWithValidations(
205+
code: string,
206+
patches: [string, (code: string) => string | Promise<string>][]
207+
): Promise<string> {
208+
console.log(`Applying code patches:`);
209+
let patchedCode = code;
210+
211+
for (const [target, patchFunction] of patches) {
212+
console.log(` - patching ${target}`);
213+
214+
const prePatchCode = patchedCode;
215+
patchedCode = await patchFunction(patchedCode);
216+
217+
if (prePatchCode === patchedCode) {
218+
throw new Error(`Failed to patch ${target}`);
219+
}
220+
}
221+
222+
console.log(`All ${patches.length} patches applied\n`);
223+
return patchedCode;
224+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./investigated";
2+
export * from "./to-investigate";
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from "./copy-package-cli-files";
2+
export * from "./patch-cache";
3+
export * from "./patch-require";
4+
export * from "./update-webpack-chunks-file";

packages/cloudflare/src/cli/build/patches/investigated/patch-cache.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,17 @@ import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
1414
* instantiated with a dynamic require that uses a string literal for the path.
1515
*/
1616
export async function patchCache(code: string, openNextOptions: BuildOptions): Promise<string> {
17-
console.log("# patchCache");
18-
1917
const { appBuildOutputPath, outputDir, monorepoRoot } = openNextOptions;
2018

2119
// TODO: switch to cache.mjs
2220
const outputPath = path.join(outputDir, "server-functions", "default");
2321
const packagePath = path.relative(monorepoRoot, appBuildOutputPath);
2422
const cacheFile = path.join(outputPath, packagePath, "cache.cjs");
2523

26-
const patchedCode = code.replace(
24+
return code.replace(
2725
"const { cacheHandler } = this.nextConfig;",
2826
`const cacheHandler = null;
2927
CacheHandler = require('${cacheFile}').default;
3028
`
3129
);
32-
33-
if (patchedCode === code) {
34-
throw new Error("Patch `patchCache` not applied");
35-
}
36-
37-
return patchedCode;
3830
}

packages/cloudflare/src/cli/build/patches/investigated/patch-require.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@
55
* James on Aug 29: `module.createRequire()` is planned.
66
*/
77
export function patchRequire(code: string): string {
8-
console.log("# patchRequire");
98
return code.replace(/__require\d?\(/g, "require(").replace(/__require\d?\./g, "require.");
109
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export * from "./inline-eval-manifest";
2+
export * from "./inline-middleware-manifest-require";
3+
export * from "./inline-next-require";
4+
export * from "./patch-exception-bubbling";
5+
export * from "./patch-find-dir";
6+
export * from "./patch-load-instrumentation-module";
7+
export * from "./patch-read-file";
8+
export * from "./wrangler-deps";

packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { normalizePath } from "../../utils";
1313
* there is a vm `runInNewContext` call which we also don't support (source: https://github.com/vercel/next.js/blob/b1e32c5d1f/packages/next/src/server/load-manifest.ts#L88)
1414
*/
1515
export function inlineEvalManifest(code: string, config: Config): string {
16-
console.log("# inlineEvalManifest");
1716
const manifestJss = globSync(
1817
normalizePath(join(config.paths.output.standaloneAppDotNext, "**", "*_client-reference-manifest.js"))
1918
).map((file) =>

packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,11 @@ import { Config } from "../../../config";
88
* as they result in runtime failures.
99
*/
1010
export function inlineMiddlewareManifestRequire(code: string, config: Config) {
11-
console.log("# inlineMiddlewareManifestRequire");
12-
1311
const middlewareManifestPath = join(config.paths.output.standaloneAppServer, "middleware-manifest.json");
1412

1513
const middlewareManifest = existsSync(middlewareManifestPath)
1614
? JSON.parse(readFileSync(middlewareManifestPath, "utf-8"))
1715
: {};
1816

19-
const patchedCode = code.replace(
20-
"require(this.middlewareManifestPath)",
21-
JSON.stringify(middlewareManifest)
22-
);
23-
24-
if (patchedCode === code) {
25-
throw new Error("Patch `inlineMiddlewareManifestRequire` not applied");
26-
}
27-
28-
return patchedCode;
17+
return code.replace("require(this.middlewareManifestPath)", JSON.stringify(middlewareManifest));
2918
}

packages/cloudflare/src/cli/build/patches/to-investigate/inline-next-require.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { Config } from "../../../config";
88
* and inline their content during build time
99
*/
1010
export function inlineNextRequire(code: string, config: Config) {
11-
console.log("# inlineNextRequire");
1211
const pagesManifestFile = join(config.paths.output.standaloneAppServer, "pages-manifest.json");
1312
const appPathsManifestFile = join(config.paths.output.standaloneAppServer, "app-paths-manifest.json");
1413

packages/cloudflare/src/cli/build/patches/to-investigate/patch-exception-bubbling.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,5 @@
55
* promises.
66
*/
77
export function patchExceptionBubbling(code: string) {
8-
console.log("# patchExceptionBubbling");
9-
10-
const patchedCode = code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined");
11-
12-
if (patchedCode === code) {
13-
throw new Error("Patch `patchExceptionBubbling` not applied");
14-
}
15-
16-
return patchedCode;
8+
return code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined");
179
}

0 commit comments

Comments
 (0)