Skip to content

Commit 55a0c16

Browse files
authored
refactor(ContentUpdater): add a filterContent argument (#365)
It is used as a fast heuristics to trigger the update
1 parent c3dc401 commit 55a0c16

File tree

4 files changed

+63
-53
lines changed

4 files changed

+63
-53
lines changed

packages/cloudflare/src/cli/build/patches/plugins/content-updater.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,21 @@ import { readFile } from "node:fs/promises";
88

99
import { type OnLoadArgs, type OnLoadOptions, type Plugin, type PluginBuild } from "esbuild";
1010

11+
/**
12+
* The callbacks returns either an updated content or undefined if the content is unchanged.
13+
*/
1114
export type Callback = (args: {
1215
contents: string;
1316
path: string;
1417
}) => string | undefined | Promise<string | undefined>;
15-
export type Updater = OnLoadOptions & { callback: Callback };
18+
19+
/**
20+
* The callback is called only when `contentFilter` matches the content.
21+
* It can be used as a fast heuristic to prevent an expensive update.
22+
*/
23+
export type OnUpdateOptions = OnLoadOptions & { contentFilter: RegExp };
24+
25+
export type Updater = OnUpdateOptions & { callback: Callback };
1626

1727
export class ContentUpdater {
1828
updaters = new Map<string, Updater>();
@@ -23,11 +33,11 @@ export class ContentUpdater {
2333
* The callbacks are called in order of registration.
2434
*
2535
* @param name The name of the plugin (must be unique).
26-
* @param options Same options as the `onLoad` hook to restrict updates.
36+
* @param options Options.
2737
* @param callback The callback updating the content.
2838
* @returns A noop ESBuild plugin.
2939
*/
30-
updateContent(name: string, options: OnLoadOptions, callback: Callback): Plugin {
40+
updateContent(name: string, options: OnUpdateOptions, callback: Callback): Plugin {
3141
if (this.updaters.has(name)) {
3242
throw new Error(`Plugin "${name}" already registered`);
3343
}
@@ -48,13 +58,16 @@ export class ContentUpdater {
4858
setup: async (build: PluginBuild) => {
4959
build.onLoad({ filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/ }, async (args: OnLoadArgs) => {
5060
let contents = await readFile(args.path, "utf-8");
51-
for (const { filter, namespace, callback } of this.updaters.values()) {
61+
for (const { filter, namespace, contentFilter, callback } of this.updaters.values()) {
5262
if (namespace !== undefined && args.namespace !== namespace) {
5363
continue;
5464
}
5565
if (!filter.test(args.path)) {
5666
continue;
5767
}
68+
if (!contentFilter.test(contents)) {
69+
continue;
70+
}
5871
contents = (await callback({ contents, path: args.path })) ?? contents;
5972
}
6073
return { contents };

packages/cloudflare/src/cli/build/patches/plugins/load-instrumentation.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,7 @@ fix: async loadInstrumentationModule() { }
1818
export function patchLoadInstrumentation(updater: ContentUpdater) {
1919
return updater.updateContent(
2020
"patch-load-instrumentation",
21-
{ filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/ },
22-
({ contents }) => {
23-
if (/async loadInstrumentationModule\(/.test(contents)) {
24-
return patchCode(contents, instrumentationRule);
25-
}
26-
}
21+
{ filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/, contentFilter: /async loadInstrumentationModule\(/ },
22+
({ contents }) => patchCode(contents, instrumentationRule)
2723
);
2824
}

packages/cloudflare/src/cli/build/patches/plugins/require-page.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@ export function inlineRequirePagePlugin(updater: ContentUpdater, buildOpts: Buil
1212
"inline-require-page",
1313
{
1414
filter: getCrossPlatformPathRegex(String.raw`/next/dist/server/require\.js$`, { escape: false }),
15+
contentFilter: /function requirePage\(/,
1516
},
16-
async ({ contents }) => {
17-
if (/function requirePage\(/.test(contents)) {
18-
return patchCode(contents, await getRule(buildOpts));
19-
}
20-
}
17+
async ({ contents }) => patchCode(contents, await getRule(buildOpts))
2118
);
2219
}
2320

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,48 @@
11
import type { ContentUpdater } from "./content-updater";
22

33
export function fixRequire(updater: ContentUpdater) {
4-
return updater.updateContent("fix-require", { filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/ }, ({ contents }) => {
5-
// `eval(...)` is not supported by workerd.
6-
contents = contents.replaceAll(`eval("require")`, "require");
4+
return updater.updateContent(
5+
"fix-require",
6+
{ filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/, contentFilter: /.*/ },
7+
({ contents }) => {
8+
// `eval(...)` is not supported by workerd.
9+
contents = contents.replaceAll(`eval("require")`, "require");
710

8-
// `@opentelemetry` has a few issues.
9-
//
10-
// Next.js has the following code in `next/dist/server/lib/trace/tracer.js`:
11-
//
12-
// try {
13-
// api = require('@opentelemetry/api');
14-
// } catch (err) {
15-
// api = require('next/dist/compiled/@opentelemetry/api');
16-
// }
17-
//
18-
// The intent is to allow users to install their own version of `@opentelemetry/api`.
19-
//
20-
// The problem is that even when users do not explicitely install `@opentelemetry/api`,
21-
// `require('@opentelemetry/api')` resolves to the package which is a dependency
22-
// of Next.
23-
//
24-
// The second problem is that when Next traces files, it would not copy the `api/build/esm`
25-
// folder (used by the `module` conditions in package.json) it would only copy `api/build/src`.
26-
// This could be solved by updating the next config:
27-
//
28-
// const nextConfig: NextConfig = {
29-
// // ...
30-
// outputFileTracingIncludes: {
31-
// "*": ["./node_modules/@opentelemetry/api/build/**/*"],
32-
// },
33-
// };
34-
//
35-
// We can consider doing that when we want to enable users to install their own version
36-
// of `@opentelemetry/api`. For now we simply use the pre-compiled version.
37-
contents = contents.replace(
38-
/require\(.@opentelemetry\/api.\)/g,
39-
`require("next/dist/compiled/@opentelemetry/api")`
40-
);
11+
// `@opentelemetry` has a few issues.
12+
//
13+
// Next.js has the following code in `next/dist/server/lib/trace/tracer.js`:
14+
//
15+
// try {
16+
// api = require('@opentelemetry/api');
17+
// } catch (err) {
18+
// api = require('next/dist/compiled/@opentelemetry/api');
19+
// }
20+
//
21+
// The intent is to allow users to install their own version of `@opentelemetry/api`.
22+
//
23+
// The problem is that even when users do not explicitly install `@opentelemetry/api`,
24+
// `require('@opentelemetry/api')` resolves to the package which is a dependency
25+
// of Next.
26+
//
27+
// The second problem is that when Next traces files, it would not copy the `api/build/esm`
28+
// folder (used by the `module` conditions in package.json) it would only copy `api/build/src`.
29+
// This could be solved by updating the next config:
30+
//
31+
// const nextConfig: NextConfig = {
32+
// // ...
33+
// outputFileTracingIncludes: {
34+
// "*": ["./node_modules/@opentelemetry/api/build/**/*"],
35+
// },
36+
// };
37+
//
38+
// We can consider doing that when we want to enable users to install their own version
39+
// of `@opentelemetry/api`. For now we simply use the pre-compiled version.
40+
contents = contents.replace(
41+
/require\(.@opentelemetry\/api.\)/g,
42+
`require("next/dist/compiled/@opentelemetry/api")`
43+
);
4144

42-
return contents;
43-
});
45+
return contents;
46+
}
47+
);
4448
}

0 commit comments

Comments
 (0)