@distilled.cloud/cloudflare-rolldown-plugin 0.3.0 → 0.4.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/factory.d.ts +19 -0
  2. package/dist/factory.d.ts.map +1 -0
  3. package/dist/factory.js +30 -0
  4. package/dist/factory.js.map +1 -0
  5. package/dist/options.d.ts +32 -0
  6. package/dist/options.d.ts.map +1 -0
  7. package/dist/options.js +1 -0
  8. package/dist/options.js.map +1 -0
  9. package/dist/plugin.d.ts +3 -6
  10. package/dist/plugin.d.ts.map +1 -1
  11. package/dist/plugin.js +10 -11
  12. package/dist/plugin.js.map +1 -1
  13. package/dist/plugins/additional-modules.d.ts +11 -2
  14. package/dist/plugins/additional-modules.d.ts.map +1 -1
  15. package/dist/plugins/additional-modules.js +68 -54
  16. package/dist/plugins/additional-modules.js.map +1 -1
  17. package/dist/plugins/cloudflare-externals.d.ts +1 -2
  18. package/dist/plugins/cloudflare-externals.d.ts.map +1 -1
  19. package/dist/plugins/cloudflare-externals.js +37 -15
  20. package/dist/plugins/cloudflare-externals.js.map +1 -1
  21. package/dist/plugins/dev.d.ts +2 -0
  22. package/dist/plugins/dev.d.ts.map +1 -0
  23. package/dist/plugins/dev.js +1 -0
  24. package/dist/plugins/dev.js.map +1 -0
  25. package/dist/plugins/index.d.ts +7 -0
  26. package/dist/plugins/index.d.ts.map +1 -0
  27. package/dist/plugins/index.js +7 -0
  28. package/dist/plugins/index.js.map +1 -0
  29. package/dist/plugins/nodejs-als.d.ts +2 -0
  30. package/dist/plugins/nodejs-als.d.ts.map +1 -0
  31. package/dist/plugins/nodejs-als.js +32 -0
  32. package/dist/plugins/nodejs-als.js.map +1 -0
  33. package/dist/plugins/nodejs-compat.d.ts +9 -3
  34. package/dist/plugins/nodejs-compat.d.ts.map +1 -1
  35. package/dist/plugins/nodejs-compat.js +187 -115
  36. package/dist/plugins/nodejs-compat.js.map +1 -1
  37. package/dist/plugins/nodejs-import-warning.d.ts +2 -0
  38. package/dist/plugins/nodejs-import-warning.d.ts.map +1 -0
  39. package/dist/plugins/nodejs-import-warning.js +62 -0
  40. package/dist/plugins/nodejs-import-warning.js.map +1 -0
  41. package/dist/plugins/nodejs-unenv.d.ts +8 -0
  42. package/dist/plugins/nodejs-unenv.d.ts.map +1 -0
  43. package/dist/plugins/nodejs-unenv.js +133 -0
  44. package/dist/plugins/nodejs-unenv.js.map +1 -0
  45. package/dist/plugins/options.d.ts +1 -5
  46. package/dist/plugins/options.d.ts.map +1 -1
  47. package/dist/plugins/options.js +155 -23
  48. package/dist/plugins/options.js.map +1 -1
  49. package/dist/plugins/virtual-modules.d.ts +3 -0
  50. package/dist/plugins/virtual-modules.d.ts.map +1 -0
  51. package/dist/plugins/virtual-modules.js +126 -0
  52. package/dist/plugins/virtual-modules.js.map +1 -0
  53. package/dist/plugins/wasm-init.d.ts +1 -2
  54. package/dist/plugins/wasm-init.d.ts.map +1 -1
  55. package/dist/plugins/wasm-init.js +18 -13
  56. package/dist/plugins/wasm-init.js.map +1 -1
  57. package/dist/plugins/wasm-init.plugin.d.ts +2 -0
  58. package/dist/plugins/wasm-init.plugin.d.ts.map +1 -0
  59. package/dist/plugins/wasm-init.plugin.js +20 -0
  60. package/dist/plugins/wasm-init.plugin.js.map +1 -0
  61. package/package.json +16 -1
  62. package/src/factory.ts +60 -0
  63. package/src/options.ts +31 -0
  64. package/src/plugin.ts +22 -18
  65. package/src/plugins/additional-modules.ts +90 -56
  66. package/src/plugins/cloudflare-externals.ts +37 -16
  67. package/src/plugins/index.ts +6 -0
  68. package/src/plugins/nodejs-compat.ts +206 -140
  69. package/src/plugins/options.ts +172 -26
  70. package/src/plugins/virtual-modules.ts +137 -0
  71. package/src/plugins/wasm-init.ts +18 -14
@@ -1,8 +1,16 @@
1
+ import MagicString from "magic-string";
1
2
  import path from "node:path";
2
- import { RolldownMagicString, type Plugin } from "rolldown";
3
+ import type {
4
+ Plugin,
5
+ PluginContext,
6
+ RenderedChunk,
7
+ RolldownMagicString,
8
+ SourceMap,
9
+ } from "rolldown";
10
+ import { createPlugin } from "../factory.js";
3
11
  import { sanitizePath } from "../utils.js";
4
12
 
5
- const MODULE_RULES = [
13
+ export const MODULE_RULES = [
6
14
  { type: "CompiledWasm", pattern: /\.wasm(\?module)?$/ },
7
15
  { type: "Data", pattern: /\.bin$/ },
8
16
  { type: "Text", pattern: /\.(txt|html|sql)$/ },
@@ -12,71 +20,97 @@ const MODULE_REFERENCE_PATTERN = `__CLOUDFLARE_MODULE__(${MODULE_RULES.map((rule
12
20
  const MODULE_REFERENCE_REGEX = new RegExp(MODULE_REFERENCE_PATTERN);
13
21
  const MODULE_REFERENCE_GLOBAL_REGEX = new RegExp(MODULE_REFERENCE_PATTERN, "g");
14
22
 
15
- export function makeAdditionalModulesPlugin(): Plugin {
23
+ export const additionalModulesPlugin = createPlugin("additional-modules", () => {
16
24
  const additionalModulePaths = new Set<string>();
17
-
18
25
  return {
19
- name: "rolldown-plugin-cloudflare:additional-modules",
20
- // TODO: these are Vite options, not Rolldown
21
- // enforce: "pre",
22
- // applyToEnvironment(environment) {
23
- // return environment.name === "development";
24
- // },
25
- // hotUpdate(options) {
26
- // if (additionalModulePaths.has(options.file)) {
27
- // void options.server.restart();
28
- // return [];
29
- // }
30
- // },
31
- resolveId: {
32
- filter: { id: MODULE_RULES.map((rule) => rule.pattern) },
33
- async handler(source, importer, options) {
34
- const resolved = await this.resolve(source, importer, options);
35
- if (!resolved) {
36
- return;
26
+ vite: {
27
+ enforce: "pre",
28
+ hotUpdate(options) {
29
+ if (additionalModulePaths.has(options.file)) {
30
+ void options.server.restart();
31
+ return [];
37
32
  }
33
+ },
34
+ },
35
+ shared: {
36
+ resolveId: {
37
+ filter: { id: MODULE_RULES.map((rule) => rule.pattern) },
38
+ async handler(source, importer, options) {
39
+ const resolved = await this.resolve(source, importer, options);
40
+ if (!resolved) {
41
+ return;
42
+ }
38
43
 
39
- const rule = MODULE_RULES.find((rule) => rule.pattern.test(resolved.id));
40
- if (!rule) {
41
- return resolved;
42
- }
44
+ const rule = MODULE_RULES.find((rule) => rule.pattern.test(resolved.id));
45
+ if (!rule) {
46
+ return resolved;
47
+ }
43
48
 
44
- const filePath = sanitizePath(resolved.id);
45
- additionalModulePaths.add(filePath);
49
+ const filePath = sanitizePath(resolved.id);
50
+ additionalModulePaths.add(filePath);
46
51
 
47
- return {
48
- external: true,
49
- id: moduleReferenceId(rule.type, filePath),
50
- };
52
+ return {
53
+ id: moduleReferenceId(rule.type, filePath),
54
+ external: true,
55
+ };
56
+ },
51
57
  },
52
- },
53
- renderChunk: {
54
- filter: { code: { include: MODULE_REFERENCE_REGEX } },
55
- async handler(code, chunk) {
56
- const matches = code.matchAll(MODULE_REFERENCE_GLOBAL_REGEX);
57
- let magicString: RolldownMagicString | undefined;
58
- for (const match of matches) {
59
- magicString ??= new RolldownMagicString(code);
60
- const [full, , id] = match;
61
- const source = await this.fs.readFile(id);
62
- const referenceId = this.emitFile({
63
- type: "asset",
64
- name: path.basename(id),
65
- source,
66
- });
67
- const fileName = this.getFileName(referenceId);
68
- const relativePath = path.relative(path.dirname(chunk.fileName), fileName);
69
- const importPath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
70
- magicString.update(match.index, match.index + full.length, importPath);
71
- }
72
- if (magicString) {
73
- return magicString;
74
- }
58
+ renderChunk: {
59
+ filter: { code: { include: MODULE_REFERENCE_REGEX } },
60
+ handler: withMagicString(async function (code, chunk, magicString) {
61
+ const matches = code.matchAll(MODULE_REFERENCE_GLOBAL_REGEX);
62
+ for (const match of matches) {
63
+ const [full, , id] = match;
64
+ const source = await this.fs.readFile(id);
65
+ const referenceId = this.emitFile({
66
+ type: "asset",
67
+ name: path.basename(id),
68
+ source,
69
+ });
70
+ const fileName = this.getFileName(referenceId);
71
+ const relativePath = path.relative(path.dirname(chunk.fileName), fileName);
72
+ const importPath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
73
+ magicString.update(match.index, match.index + full.length, importPath);
74
+ }
75
+ }),
75
76
  },
76
77
  },
77
78
  };
78
- }
79
+ });
79
80
 
80
81
  function moduleReferenceId(type: "CompiledWasm" | "Data" | "Text", id: string) {
81
82
  return `__CLOUDFLARE_MODULE__${type}__${id}__CLOUDFLARE_MODULE__` as const;
82
83
  }
84
+
85
+ type PluginHandler<T extends keyof Plugin> = Plugin[T] extends infer T
86
+ ? T extends (...args: any) => any
87
+ ? T
88
+ : never
89
+ : never;
90
+
91
+ /**
92
+ * Returns a `renderChunk` handler that transforms the chunk using a magic string.
93
+ * Uses Rolldown's native magic string if available, or the `magic-string` library otherwise.
94
+ */
95
+ function withMagicString(
96
+ renderChunk: (
97
+ this: PluginContext,
98
+ code: string,
99
+ chunk: RenderedChunk,
100
+ magicString: MagicString | RolldownMagicString,
101
+ ) => Promise<void>,
102
+ ): PluginHandler<"renderChunk"> {
103
+ return async function (code, chunk, outputOptions, meta) {
104
+ const magicString = meta.magicString ?? new MagicString(code);
105
+ await renderChunk.call(this, code, chunk, magicString);
106
+ if ("isRolldownMagicString" in magicString && magicString.isRolldownMagicString) {
107
+ return magicString;
108
+ }
109
+ return {
110
+ code: magicString.toString(),
111
+ map: outputOptions.sourcemap
112
+ ? (magicString.generateMap({ hires: "boundary" }) as SourceMap)
113
+ : null,
114
+ };
115
+ };
116
+ }
@@ -1,6 +1,6 @@
1
- import type { Plugin } from "rolldown";
1
+ import { createPlugin } from "../factory.js";
2
2
 
3
- const CLOUDFLARE_BUILTIN_MODULES = [
3
+ const CLOUDFLARE_BUILT_IN_MODULES = [
4
4
  "cloudflare:email",
5
5
  "cloudflare:node",
6
6
  "cloudflare:sockets",
@@ -8,19 +8,40 @@ const CLOUDFLARE_BUILTIN_MODULES = [
8
8
  "cloudflare:workflows",
9
9
  ];
10
10
 
11
- export const cloudflareExternalsPlugin: Plugin = {
12
- name: "rolldown-plugin-cloudflare:cloudflare-externals",
13
- resolveId: {
14
- filter: { id: /^cloudflare:/ },
15
- handler(id) {
16
- if (!CLOUDFLARE_BUILTIN_MODULES.includes(id)) {
17
- return;
18
- }
11
+ export const cloudflareExternalsPlugin = createPlugin("cloudflare-externals", () => {
12
+ return {
13
+ rolldown: {
14
+ resolveId: {
15
+ filter: { id: /^cloudflare:/ },
16
+ handler(id) {
17
+ if (!CLOUDFLARE_BUILT_IN_MODULES.includes(id)) {
18
+ return;
19
+ }
19
20
 
20
- return {
21
- id,
22
- external: true,
23
- };
21
+ return {
22
+ id,
23
+ external: true,
24
+ };
25
+ },
26
+ },
24
27
  },
25
- },
26
- };
28
+ vite: {
29
+ enforce: "pre",
30
+ configEnvironment(name) {
31
+ if (name === "client") {
32
+ // Some frameworks allow users to mix client and server code in the same file and then extract the server code.
33
+ // As the dependency optimization may happen before the server code is extracted, we should exclude Cloudflare built-ins from client optimization.
34
+ return { optimizeDeps: { exclude: CLOUDFLARE_BUILT_IN_MODULES } };
35
+ }
36
+ return {
37
+ resolve: {
38
+ builtins: CLOUDFLARE_BUILT_IN_MODULES,
39
+ },
40
+ optimizeDeps: {
41
+ exclude: CLOUDFLARE_BUILT_IN_MODULES,
42
+ },
43
+ };
44
+ },
45
+ },
46
+ };
47
+ });
@@ -0,0 +1,6 @@
1
+ export * from "./additional-modules.js";
2
+ export * from "./cloudflare-externals.js";
3
+ export * from "./nodejs-compat.js";
4
+ export * from "./options.js";
5
+ export * from "./virtual-modules.js";
6
+ export * from "./wasm-init.js";
@@ -2,174 +2,240 @@ import { getCloudflarePreset, nonPrefixedNodeModules } from "@cloudflare/unenv-p
2
2
  import assert from "node:assert";
3
3
  import { createRequire } from "node:module";
4
4
  import path from "node:path";
5
- import type { Plugin } from "rolldown";
6
5
  import { esmExternalRequirePlugin } from "rolldown/plugins";
7
6
  import { defineEnv } from "unenv";
8
- import type { CloudflarePluginOptions } from "../plugin.js";
7
+ import { createPlugin } from "../factory.js";
9
8
  import { hasNodejsAls, hasNodejsCompat } from "../utils.js";
10
9
 
10
+ const ASYNC_HOOKS_REGEXP = /^(node:)?async_hooks$/;
11
11
  const NODE_BUILTIN_MODULES_REGEXP = new RegExp(`^(${nonPrefixedNodeModules.join("|")}|node:.+)$`);
12
- const VIRTUAL_MODULE_ID_REGEXP = /^virtual:nodejs-global-inject\/.+$/;
13
12
 
14
- export function makeNodejsCompatPlugin(options: CloudflarePluginOptions): Plugin | Array<Plugin> {
15
- if (hasNodejsCompat(options.compatibilityFlags)) {
16
- return makeUnenvPlugin(options);
17
- }
18
- if (hasNodejsAls(options.compatibilityFlags)) {
19
- return makeNodeJsAlsPlugin();
20
- }
21
- return makeNodeJsImportWarningPlugin();
22
- }
23
-
24
- function makeUnenvPlugin(options: CloudflarePluginOptions): Array<Plugin> {
25
- const { alias, inject, external, polyfill } = defineEnv({
26
- presets: [
27
- getCloudflarePreset({
28
- compatibilityDate: options.compatibilityDate,
29
- compatibilityFlags: options.compatibilityFlags,
30
- }),
31
- ],
32
- }).env;
33
-
34
- const injectVirtualModules = makeInjectVirtualModules(inject);
35
- const require = createRequire(import.meta.url);
36
-
37
- return [
38
- esmExternalRequirePlugin({
39
- external: [...external],
40
- skipDuplicateCheck: true,
41
- }),
42
- {
43
- name: "rolldown-plugin-cloudflare:nodejs-compat:injects",
13
+ export const nodejsAlsPlugin = createPlugin("nodejs-als", (options) => {
14
+ if (!hasNodejsAls(options.compatibilityFlags)) return;
15
+ return {
16
+ rolldown: {
44
17
  resolveId: {
45
- filter: { id: VIRTUAL_MODULE_ID_REGEXP },
18
+ filter: { id: ASYNC_HOOKS_REGEXP },
46
19
  handler(id) {
47
- if (injectVirtualModules.has(id)) {
48
- return { id };
49
- }
50
- },
51
- },
52
- load: {
53
- filter: { id: VIRTUAL_MODULE_ID_REGEXP },
54
- handler(id) {
55
- return injectVirtualModules.get(id);
20
+ return { id, external: true };
56
21
  },
57
22
  },
58
23
  },
59
- {
60
- name: "rolldown-plugin-cloudflare:nodejs-compat:unenv-preset-imports",
61
- resolveId: {
62
- filter: { id: /^@cloudflare\/unenv-preset\// },
63
- async handler(id, importer, options) {
64
- const resolved = await this.resolve(id, importer, options);
65
- if (resolved) return resolved;
66
- return { id: require.resolve(id) };
67
- },
24
+ vite: {
25
+ enforce: "pre",
26
+ configEnvironment(name) {
27
+ if (name === "client") return;
28
+ return {
29
+ resolve: {
30
+ builtins: ["async_hooks", "node:async_hooks"],
31
+ },
32
+ optimizeDeps: {
33
+ exclude: ["async_hooks", "node:async_hooks"],
34
+ },
35
+ };
68
36
  },
69
37
  },
70
- {
71
- name: "rolldown-plugin-cloudflare:nodejs-compat",
72
- resolveId: {
73
- filter: { id: NODE_BUILTIN_MODULES_REGEXP },
74
- handler(source, importer, options) {
75
- const aliased = alias[source];
76
- if (!aliased) {
77
- return;
78
- }
79
- if (external.includes(aliased)) {
80
- return {
81
- id: aliased,
82
- external: true,
83
- };
84
- }
85
- return this.resolve(aliased, importer, options);
38
+ };
39
+ });
40
+
41
+ export interface UnenvApi {
42
+ polyfill: ReadonlyArray<string>;
43
+ inject: { [injectedName: string]: string };
44
+ }
45
+
46
+ export const nodejsUnenvPlugin = createPlugin<"nodejs-unenv", UnenvApi>(
47
+ "nodejs-unenv",
48
+ (options) => {
49
+ if (!hasNodejsCompat(options.compatibilityFlags)) return;
50
+ const { alias, inject, external, polyfill } = defineEnv({
51
+ presets: [
52
+ getCloudflarePreset({
53
+ compatibilityDate: options.compatibilityDate,
54
+ compatibilityFlags: options.compatibilityFlags,
55
+ }),
56
+ ],
57
+ }).env;
58
+ const entries = new Set(Object.values(alias));
59
+ for (const globalInject of Object.values(inject)) {
60
+ if (typeof globalInject === "string") {
61
+ entries.add(globalInject);
62
+ } else {
63
+ entries.add(globalInject[0]);
64
+ }
65
+ }
66
+ polyfill.forEach((module) => entries.add(module));
67
+ external.forEach((module) => entries.delete(module));
68
+ const require = createRequire(import.meta.url);
69
+ const resolve = (id: string) => {
70
+ if (alias[id] && !external.includes(alias[id])) {
71
+ return require.resolve(alias[id]);
72
+ }
73
+ if (entries.has(id)) {
74
+ return require.resolve(id);
75
+ }
76
+ };
77
+ const RESOLVE_ID_FILTER = {
78
+ id: [NODE_BUILTIN_MODULES_REGEXP, /^unenv\//, /^@cloudflare\/unenv-preset\//],
79
+ };
80
+ return {
81
+ shared: {
82
+ api: {
83
+ polyfill,
84
+ inject: Object.fromEntries(
85
+ Object.entries(inject).map(([injectedName, moduleSpecifier]) => {
86
+ assert(
87
+ typeof moduleSpecifier === "string",
88
+ `expected moduleSpecifier to be a string`,
89
+ );
90
+ return [injectedName, moduleSpecifier];
91
+ }),
92
+ ),
86
93
  },
87
94
  },
88
- transform(code, id) {
89
- const info = this.getModuleInfo(id);
90
- if (!info?.isEntry) {
91
- return;
92
- }
93
- return [
94
- ...polyfill.map((module) => `import "${module}";`),
95
- ...Array.from(injectVirtualModules.keys()).map((module) => `import "${module}";`),
96
- code,
97
- ].join("\n");
95
+ rolldown: {
96
+ async options(options) {
97
+ options.plugins = [
98
+ esmExternalRequirePlugin({
99
+ external: [...external],
100
+ skipDuplicateCheck: true,
101
+ }),
102
+ options.plugins,
103
+ ];
104
+ return options;
105
+ },
106
+ resolveId: {
107
+ filter: RESOLVE_ID_FILTER,
108
+ handler(source, importer, options) {
109
+ const resolved = resolve(source);
110
+ if (!resolved) return;
111
+ return this.resolve(resolved, importer, options);
112
+ },
113
+ },
98
114
  },
99
- } satisfies Plugin,
100
- ];
101
- }
102
-
103
- function makeNodeJsAlsPlugin(): Plugin {
104
- return {
105
- name: "rolldown-plugin-cloudflare:nodejs-als",
106
- resolveId: {
107
- filter: { id: /^(node:)?async_hooks$/ },
108
- handler(id) {
109
- return { id, external: true };
115
+ vite: {
116
+ enforce: "pre",
117
+ async configEnvironment(name) {
118
+ if (name === "client") return;
119
+ return {
120
+ resolve: {
121
+ builtins: [...external],
122
+ },
123
+ ...(this.meta.rolldownVersion
124
+ ? {
125
+ build: {
126
+ rolldownOptions: {
127
+ plugins: [
128
+ esmExternalRequirePlugin({
129
+ external: [...external],
130
+ skipDuplicateCheck: true,
131
+ }),
132
+ ],
133
+ },
134
+ },
135
+ }
136
+ : {}),
137
+ optimizeDeps: {
138
+ exclude: [
139
+ ...nonPrefixedNodeModules,
140
+ ...nonPrefixedNodeModules.map((module) => `node:${module}`),
141
+ // New Node.js built-in modules are only published with the `node:` prefix.
142
+ ...["node:sea", "node:sqlite", "node:test", "node:test/reporters"],
143
+ ],
144
+ },
145
+ };
146
+ },
147
+ async configureServer(server) {
148
+ await Promise.all(
149
+ Object.values(server.environments).flatMap(async (environment) => {
150
+ const depsOptimizer = environment.depsOptimizer;
151
+ if (!depsOptimizer) return;
152
+ await depsOptimizer.init();
153
+ return Array.from(entries).map((entry) => {
154
+ const resolved = resolve(entry);
155
+ if (!resolved) return;
156
+ const registration = depsOptimizer.registerMissingImport(entry, resolved);
157
+ return registration?.processing;
158
+ });
159
+ }),
160
+ );
161
+ },
162
+ resolveId: {
163
+ filter: RESOLVE_ID_FILTER,
164
+ handler(source, importer, options) {
165
+ const resolved = resolve(source);
166
+ if (!resolved) return;
167
+ if (this.environment.mode === "dev" && this.environment.depsOptimizer) {
168
+ // We are in dev mode (rather than build).
169
+ // So let's pre-bundle this polyfill entry-point using the dependency optimizer.
170
+ const { id } = this.environment.depsOptimizer.registerMissingImport(source, resolved);
171
+ // We use the unresolved path to the polyfill and let the dependency optimizer's
172
+ // resolver find the resolved path to the bundled version.
173
+ return this.resolve(id, importer, options);
174
+ }
175
+ return this.resolve(resolved, importer, options);
176
+ },
177
+ },
110
178
  },
111
- },
112
- };
113
- }
179
+ };
180
+ },
181
+ );
114
182
 
115
- function makeNodeJsImportWarningPlugin(): Plugin {
183
+ export const nodejsImportWarningPlugin = createPlugin("nodejs-import-warning", (options) => {
184
+ if (hasNodejsCompat(options.compatibilityFlags)) return;
116
185
  const imports = new Map<string, Set<string>>();
117
186
  let root = process.cwd();
118
187
  return {
119
- name: "rolldown-plugin-cloudflare:nodejs-import-warnings",
120
- options(options) {
121
- if (options.cwd) {
122
- root = options.cwd;
123
- }
124
- },
125
- resolveId: {
126
- filter: { id: NODE_BUILTIN_MODULES_REGEXP },
127
- async handler(id, importer) {
128
- if (importer) {
129
- if (!imports.has(id)) {
130
- imports.set(id, new Set());
131
- }
132
- imports.get(id)?.add(importer);
188
+ rolldown: {
189
+ options(options) {
190
+ if (options.cwd) {
191
+ root = options.cwd;
133
192
  }
134
- return { id, external: true };
135
193
  },
136
194
  },
137
- buildStart() {
138
- imports.clear();
195
+ vite: {
196
+ enforce: "pre",
197
+ configResolved(config) {
198
+ root = config.root;
199
+ },
139
200
  },
140
- buildEnd() {
141
- if (imports.size > 0) {
142
- let message =
143
- `Unexpected Node.js imports. ` +
144
- `Do you need to enable the "nodejs_compat" compatibility flag? ` +
145
- "Refer to https://developers.cloudflare.com/workers/runtime-apis/nodejs/ for more details.\n";
201
+ shared: {
202
+ buildStart() {
203
+ imports.clear();
204
+ },
205
+ resolveId: {
206
+ filter: { id: NODE_BUILTIN_MODULES_REGEXP },
207
+ async handler(id, importer) {
208
+ if (hasNodejsAls(options.compatibilityFlags) && ASYNC_HOOKS_REGEXP.test(id)) return;
209
+ if (importer) {
210
+ if (!imports.has(id)) {
211
+ imports.set(id, new Set());
212
+ }
213
+ imports.get(id)?.add(importer);
214
+ }
215
+ return { id, external: true };
216
+ },
217
+ },
218
+ buildEnd() {
219
+ const filteredImports: Array<{ id: string; importer: string }> = [];
146
220
  for (const [id, importers] of imports.entries()) {
147
221
  for (const importer of importers) {
148
- message += ` - "${id}" imported from "${path.relative(root, importer)}"\n`;
222
+ if (this.getModuleInfo(importer)) {
223
+ filteredImports.push({ id, importer });
224
+ }
149
225
  }
150
226
  }
151
- this.warn(message);
152
- }
227
+ if (filteredImports.length > 0) {
228
+ const message = [
229
+ "Unexpected Node.js imports. ",
230
+ 'Do you need to enable the "nodejs_compat" compatibility flag? ',
231
+ "Refer to https://developers.cloudflare.com/workers/runtime-apis/nodejs/ for more details.\n",
232
+ ...filteredImports.map(
233
+ ({ id, importer }) => ` - "${id}" imported from "${path.relative(root, importer)}"\n`,
234
+ ),
235
+ ].join("");
236
+ this.warn(message);
237
+ }
238
+ },
153
239
  },
154
240
  };
155
- }
156
-
157
- function makeInjectVirtualModules(
158
- inject: Readonly<Record<string, string | ReadonlyArray<string>>>,
159
- ) {
160
- const virtualModules = new Map<string, string>();
161
- for (const [injectedName, moduleSpecifier] of Object.entries(inject)) {
162
- // The type from unenv is string | string[], but the Cloudflare preset only uses string.
163
- // This indicates a default export that we set on globalThis.
164
- assert(typeof moduleSpecifier === "string", `expected moduleSpecifier to be a string`);
165
- virtualModules.set(
166
- injectVirtualModuleId(injectedName),
167
- `import virtualModule from "${moduleSpecifier}"; globalThis.${injectedName} = virtualModule;`,
168
- );
169
- }
170
- return virtualModules;
171
- }
172
-
173
- function injectVirtualModuleId(module: string) {
174
- return `virtual:nodejs-global-inject/${module}` as const;
175
- }
241
+ });