@distilled.cloud/cloudflare-rolldown-plugin 0.3.0 → 0.4.0

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 (51) 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 +26 -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 +1 -2
  14. package/dist/plugins/additional-modules.d.ts.map +1 -1
  15. package/dist/plugins/additional-modules.js +67 -53
  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 +36 -15
  20. package/dist/plugins/cloudflare-externals.js.map +1 -1
  21. package/dist/plugins/index.d.ts +7 -0
  22. package/dist/plugins/index.d.ts.map +1 -0
  23. package/dist/plugins/index.js +7 -0
  24. package/dist/plugins/index.js.map +1 -0
  25. package/dist/plugins/nodejs-compat.d.ts +9 -3
  26. package/dist/plugins/nodejs-compat.d.ts.map +1 -1
  27. package/dist/plugins/nodejs-compat.js +178 -115
  28. package/dist/plugins/nodejs-compat.js.map +1 -1
  29. package/dist/plugins/options.d.ts +1 -5
  30. package/dist/plugins/options.d.ts.map +1 -1
  31. package/dist/plugins/options.js +128 -23
  32. package/dist/plugins/options.js.map +1 -1
  33. package/dist/plugins/virtual-modules.d.ts +3 -0
  34. package/dist/plugins/virtual-modules.d.ts.map +1 -0
  35. package/dist/plugins/virtual-modules.js +125 -0
  36. package/dist/plugins/virtual-modules.js.map +1 -0
  37. package/dist/plugins/wasm-init.d.ts +1 -2
  38. package/dist/plugins/wasm-init.d.ts.map +1 -1
  39. package/dist/plugins/wasm-init.js +15 -13
  40. package/dist/plugins/wasm-init.js.map +1 -1
  41. package/package.json +16 -1
  42. package/src/factory.ts +60 -0
  43. package/src/options.ts +25 -0
  44. package/src/plugin.ts +22 -18
  45. package/src/plugins/additional-modules.ts +89 -55
  46. package/src/plugins/cloudflare-externals.ts +36 -16
  47. package/src/plugins/index.ts +6 -0
  48. package/src/plugins/nodejs-compat.ts +197 -140
  49. package/src/plugins/options.ts +145 -26
  50. package/src/plugins/virtual-modules.ts +135 -0
  51. package/src/plugins/wasm-init.ts +15 -14
@@ -0,0 +1,125 @@
1
+ import { createPlugin } from "../factory.js";
2
+ // oxlint-disable-next-line no-control-regex
3
+ const VIRTUAL_MODULE_REGEXP = /^\0distilled:.*$/;
4
+ export const WORKER_ENTRY_PREFIX = "\0distilled:worker-entry:";
5
+ const USER_ENTRY_PREFIX = "\0distilled:user-entry:";
6
+ const PEAR_ENTRY_PREFIX = "\0distilled:pear-entry:";
7
+ const INJECT_PREFIX = "\0distilled:inject:";
8
+ const EXPORT_TYPES_ID = "\0distilled:export-types";
9
+ export const virtualModulesPlugin = createPlugin("virtual-modules", (options) => {
10
+ let unenvApi;
11
+ const inject = () => {
12
+ if (!unenvApi)
13
+ return [];
14
+ return [
15
+ ...unenvApi.polyfill.map((module) => `import "${module}";`),
16
+ ...Object.keys(unenvApi.inject).map((injectedName) => `import "${INJECT_PREFIX}${injectedName}";`),
17
+ ];
18
+ };
19
+ return {
20
+ shared: {
21
+ buildStart({ plugins }) {
22
+ unenvApi = plugins.find((plugin) => "name" in plugin && plugin.name === "distilled-cloudflare:nodejs-unenv")?.api;
23
+ },
24
+ resolveId: {
25
+ filter: { id: VIRTUAL_MODULE_REGEXP },
26
+ handler(id) {
27
+ if (id.startsWith(WORKER_ENTRY_PREFIX) ||
28
+ id.startsWith(PEAR_ENTRY_PREFIX) ||
29
+ id.startsWith(INJECT_PREFIX) ||
30
+ id === EXPORT_TYPES_ID) {
31
+ return { id };
32
+ }
33
+ if (id.startsWith(USER_ENTRY_PREFIX)) {
34
+ return this.resolve(id.slice(USER_ENTRY_PREFIX.length), undefined, {
35
+ isEntry: true,
36
+ kind: "import-statement",
37
+ });
38
+ }
39
+ },
40
+ },
41
+ load: {
42
+ filter: { id: VIRTUAL_MODULE_REGEXP },
43
+ handler(id) {
44
+ if (id.startsWith(WORKER_ENTRY_PREFIX)) {
45
+ const userEntryId = id.replace(WORKER_ENTRY_PREFIX, USER_ENTRY_PREFIX);
46
+ return [
47
+ ...inject(),
48
+ `import { getExportTypes } from "${EXPORT_TYPES_ID}";`,
49
+ ...(options.exports
50
+ ? [`export { ${options.exports.join(", ")} } from "${userEntryId}";`]
51
+ : [
52
+ `import * as userEntry from "${userEntryId}";`,
53
+ `export * from "${userEntryId}";`,
54
+ `export default userEntry.default ?? {};`,
55
+ ]),
56
+ "if (import.meta.hot) {",
57
+ " import.meta.hot.accept((module) => {",
58
+ " const exportTypes = getExportTypes(module);",
59
+ ' import.meta.hot.send("distilled-cloudflare:worker-export-types", exportTypes);',
60
+ " });",
61
+ "}",
62
+ ].join("\n");
63
+ }
64
+ if (id === EXPORT_TYPES_ID) {
65
+ return `
66
+ import {
67
+ WorkerEntrypoint,
68
+ DurableObject,
69
+ WorkflowEntrypoint,
70
+ } from "cloudflare:workers";
71
+
72
+ const baseClasses = new Map([
73
+ ["WorkerEntrypoint", WorkerEntrypoint],
74
+ ["DurableObject", DurableObject],
75
+ ["WorkflowEntrypoint", WorkflowEntrypoint],
76
+ ]);
77
+
78
+ export function getExportTypes(module) {
79
+ const exportTypes = {};
80
+
81
+ for (const [key, value] of Object.entries(module)) {
82
+ if (key === "default") {
83
+ continue;
84
+ }
85
+
86
+ let exportType;
87
+
88
+ if (typeof value === "function") {
89
+ for (const [type, baseClass] of baseClasses) {
90
+ if (baseClass.prototype.isPrototypeOf(value.prototype)) {
91
+ exportType = type;
92
+ break;
93
+ }
94
+ }
95
+
96
+ if (!exportType) {
97
+ exportType = "DurableObject";
98
+ }
99
+ } else if (typeof value === "object" && value !== null) {
100
+ exportType = "WorkerEntrypoint";
101
+ }
102
+
103
+ exportTypes[key] = exportType;
104
+ }
105
+
106
+ return exportTypes;
107
+ }`;
108
+ }
109
+ if (id.startsWith(INJECT_PREFIX)) {
110
+ const injectedName = id.slice(INJECT_PREFIX.length);
111
+ const moduleSpecifier = unenvApi?.inject[injectedName];
112
+ if (!moduleSpecifier) {
113
+ throw new Error(`Expected module specifier for "${injectedName}" to be defined`);
114
+ }
115
+ return [
116
+ `import ${injectedName} from "${moduleSpecifier}";`,
117
+ `globalThis.${injectedName} = ${injectedName};`,
118
+ ].join("\n");
119
+ }
120
+ },
121
+ },
122
+ },
123
+ };
124
+ });
125
+ //# sourceMappingURL=virtual-modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtual-modules.js","sourceRoot":"","sources":["../../src/plugins/virtual-modules.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,2 @@
1
- import type { Plugin } from "rolldown";
2
- export declare const wasmInitPlugin: Plugin;
1
+ export declare const wasmInitPlugin: import("../factory.js").PluginOutput<any>;
3
2
  //# sourceMappingURL=wasm-init.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wasm-init.d.ts","sourceRoot":"","sources":["../../src/plugins/wasm-init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAKvC,eAAO,MAAM,cAAc,EAAE,MAc5B,CAAC"}
1
+ {"version":3,"file":"wasm-init.d.ts","sourceRoot":"","sources":["../../src/plugins/wasm-init.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,cAAc,2CAexB,CAAC"}
@@ -1,18 +1,20 @@
1
+ import { createPlugin } from "../factory.js";
1
2
  import { sanitizePath } from "../utils.js";
2
3
  const WASM_INIT_QUERY = /\.wasm\?init$/;
3
- export const wasmInitPlugin = {
4
- name: "rolldown-plugin-cloudflare:wasm-init",
5
- load: {
6
- filter: { id: WASM_INIT_QUERY },
7
- handler(id) {
8
- return [
9
- `import wasmModule from "${sanitizePath(id)}";`,
10
- `export default async (imports) => {`,
11
- ` const result = await WebAssembly.instantiate(wasmModule, imports);`,
12
- ` return "instance" in result ? result.instance : result;`,
13
- `};`,
14
- ].join("\n");
4
+ export const wasmInitPlugin = createPlugin("wasm-init", () => ({
5
+ shared: {
6
+ load: {
7
+ filter: { id: WASM_INIT_QUERY },
8
+ handler(id) {
9
+ return [
10
+ `import wasmModule from "${sanitizePath(id)}";`,
11
+ `export default async (imports) => {`,
12
+ ` const result = await WebAssembly.instantiate(wasmModule, imports);`,
13
+ ` return "instance" in result ? result.instance : result;`,
14
+ `};`,
15
+ ].join("\n");
16
+ },
15
17
  },
16
18
  },
17
- };
19
+ }));
18
20
  //# sourceMappingURL=wasm-init.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wasm-init.js","sourceRoot":"","sources":["../../src/plugins/wasm-init.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,eAAe,GAAG,eAAe,CAAC;AAExC,MAAM,CAAC,MAAM,cAAc,GAAW;IACpC,IAAI,EAAE,sCAAsC;IAC5C,IAAI,EAAE;QACJ,MAAM,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE;QAC/B,OAAO,CAAC,EAAE;YACR,OAAO;gBACL,2BAA2B,YAAY,CAAC,EAAE,CAAC,IAAI;gBAC/C,qCAAqC;gBACrC,sEAAsE;gBACtE,2DAA2D;gBAC3D,IAAI;aACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;KACF;CACF,CAAC"}
1
+ {"version":3,"file":"wasm-init.js","sourceRoot":"","sources":["../../src/plugins/wasm-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,eAAe,GAAG,eAAe,CAAC;AAExC,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7D,MAAM,EAAE;QACN,IAAI,EAAE;YACJ,MAAM,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE;YAC/B,OAAO,CAAC,EAAE;gBACR,OAAO;oBACL,2BAA2B,YAAY,CAAC,EAAE,CAAC,IAAI;oBAC/C,qCAAqC;oBACrC,sEAAsE;oBACtE,2DAA2D;oBAC3D,IAAI;iBACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,CAAC;SACF;KACF;CACF,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@distilled.cloud/cloudflare-rolldown-plugin",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Rolldown plugin for Cloudflare Workers.",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -35,6 +35,14 @@
35
35
  "bun": "./src/plugin.ts",
36
36
  "types": "./dist/plugin.d.ts",
37
37
  "import": "./dist/plugin.js"
38
+ },
39
+ "./plugins": {
40
+ "types": "./dist/plugins/index.d.ts",
41
+ "import": "./dist/plugins/index.js"
42
+ },
43
+ "./options": {
44
+ "types": "./dist/options.d.ts",
45
+ "import": "./dist/options.js"
38
46
  }
39
47
  },
40
48
  "publishConfig": {
@@ -51,14 +59,21 @@
51
59
  },
52
60
  "dependencies": {
53
61
  "@cloudflare/unenv-preset": "^2.16.0",
62
+ "magic-string": "^0.30.21",
54
63
  "unenv": "^2.0.0-rc.24"
55
64
  },
56
65
  "devDependencies": {
57
66
  "@cloudflare/workers-types": "^4.20260310.1",
58
67
  "@distilled.cloud/test-utils": "0.0.0",
68
+ "vite": "^8.0.3",
59
69
  "vitest": "^4.1.1"
60
70
  },
61
71
  "peerDependencies": {
62
72
  "rolldown": "^1.0.0-rc.9"
73
+ },
74
+ "peerDependenciesMeta": {
75
+ "rolldown": {
76
+ "optional": true
77
+ }
63
78
  }
64
79
  }
package/src/factory.ts ADDED
@@ -0,0 +1,60 @@
1
+ import type * as rolldown from "rolldown";
2
+ import type * as vite from "vite";
3
+ import type { CloudflarePluginOptions } from "./options.js";
4
+
5
+ export interface PluginInput<A = any> {
6
+ shared?: Omit<rolldown.Plugin<A>, "name">;
7
+ rolldown?: Omit<rolldown.Plugin<A>, "name">;
8
+ vite?: Omit<vite.Plugin<A>, "name">;
9
+ }
10
+
11
+ export interface NullablePluginOutput<A = any> {
12
+ rolldown: (options: CloudflarePluginOptions) => rolldown.Plugin<A> | null;
13
+ vite: (options: CloudflarePluginOptions) => vite.Plugin<A> | null;
14
+ }
15
+
16
+ export interface PluginOutput<A = any> {
17
+ rolldown: (options: CloudflarePluginOptions) => rolldown.Plugin<A>;
18
+ vite: (options: CloudflarePluginOptions) => vite.Plugin<A>;
19
+ }
20
+
21
+ export function createPlugin<TName extends string, A = any>(
22
+ pluginName: TName,
23
+ make: (options: CloudflarePluginOptions) => PluginInput<A>,
24
+ ): PluginOutput<A>;
25
+
26
+ export function createPlugin<TName extends string, A = any>(
27
+ pluginName: TName,
28
+ make: (options: CloudflarePluginOptions) => PluginInput<A> | undefined,
29
+ ): NullablePluginOutput<A>;
30
+
31
+ export function createPlugin<TName extends string, A = any>(
32
+ pluginName: TName,
33
+ make: (options: CloudflarePluginOptions) => PluginInput<A> | undefined,
34
+ ): PluginOutput<A> | NullablePluginOutput<A> {
35
+ const name = `distilled-cloudflare:${pluginName}`;
36
+ return {
37
+ rolldown: (options: CloudflarePluginOptions) => {
38
+ const plugin = make(options);
39
+ if (!plugin) return null;
40
+ return {
41
+ name,
42
+ ...plugin.shared,
43
+ ...plugin.rolldown,
44
+ };
45
+ },
46
+ vite: (options: CloudflarePluginOptions) => {
47
+ const plugin = make(options);
48
+ if (!plugin) return null;
49
+ return {
50
+ name,
51
+ sharedDuringBuild: true,
52
+ applyToEnvironment(environment) {
53
+ return environment.name !== "client";
54
+ },
55
+ ...plugin.shared,
56
+ ...plugin.vite,
57
+ } as vite.Plugin;
58
+ },
59
+ };
60
+ }
package/src/options.ts ADDED
@@ -0,0 +1,25 @@
1
+ export interface CloudflarePluginOptions {
2
+ /**
3
+ * The compatibility date to use. This is optional, but should be defined to avoid unexpected behavior.
4
+ * @default undefined
5
+ */
6
+ compatibilityDate?: string;
7
+ /**
8
+ * The compatibility flags to enable.
9
+ * @default []
10
+ * @example
11
+ * ```ts
12
+ * cloudflare({ compatibilityDate: "2026-04-01", compatibilityFlags: ["nodejs_compat"] });
13
+ * ```
14
+ */
15
+ compatibilityFlags?: Array<string>;
16
+ /**
17
+ * The exports to include in the bundle.
18
+ * By default, all exports are included. However, if you only want to include certain exports, you can use this option.
19
+ * @example
20
+ * ```ts
21
+ * cloudflare({ exports: ["default"] });
22
+ * ```
23
+ */
24
+ exports?: Array<string>;
25
+ }
package/src/plugin.ts CHANGED
@@ -1,24 +1,28 @@
1
- import type { RolldownPluginOption } from "rolldown";
2
- import { makeAdditionalModulesPlugin } from "./plugins/additional-modules.js";
3
- import { cloudflareExternalsPlugin } from "./plugins/cloudflare-externals.js";
4
- import { makeNodejsCompatPlugin } from "./plugins/nodejs-compat.js";
5
- import { makeOptionsPlugin } from "./plugins/options.js";
6
- import { wasmInitPlugin } from "./plugins/wasm-init.js";
1
+ import type * as rolldown from "rolldown";
2
+ import type { CloudflarePluginOptions } from "./options.js";
3
+ import {
4
+ additionalModulesPlugin,
5
+ cloudflareExternalsPlugin,
6
+ nodejsAlsPlugin,
7
+ nodejsImportWarningPlugin,
8
+ nodejsUnenvPlugin,
9
+ optionsPlugin,
10
+ virtualModulesPlugin,
11
+ wasmInitPlugin,
12
+ } from "./plugins/index.js";
7
13
 
8
- export interface CloudflarePluginOptions {
9
- compatibilityDate?: string;
10
- compatibilityFlags?: Array<string>;
11
- }
14
+ export type CloudflarePlugin = (options?: CloudflarePluginOptions) => Array<rolldown.Plugin | null>;
12
15
 
13
- export type CloudflarePlugin = (options?: CloudflarePluginOptions) => RolldownPluginOption;
14
-
15
- const cloudflare: CloudflarePlugin = async (options = {}) => {
16
+ const cloudflare: CloudflarePlugin = (options = {}) => {
16
17
  return [
17
- makeOptionsPlugin(options),
18
- cloudflareExternalsPlugin,
19
- makeNodejsCompatPlugin(options),
20
- wasmInitPlugin,
21
- makeAdditionalModulesPlugin(),
18
+ optionsPlugin.rolldown(options),
19
+ cloudflareExternalsPlugin.rolldown(options),
20
+ nodejsAlsPlugin.rolldown(options),
21
+ nodejsImportWarningPlugin.rolldown(options),
22
+ nodejsUnenvPlugin.rolldown(options),
23
+ virtualModulesPlugin.rolldown(options),
24
+ wasmInitPlugin.rolldown(options),
25
+ additionalModulesPlugin.rolldown(options),
22
26
  ];
23
27
  };
24
28
 
@@ -1,5 +1,13 @@
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
13
  const MODULE_RULES = [
@@ -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,39 @@ 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
+ configEnvironment(name) {
30
+ if (name === "client") {
31
+ // Some frameworks allow users to mix client and server code in the same file and then extract the server code.
32
+ // As the dependency optimization may happen before the server code is extracted, we should exclude Cloudflare built-ins from client optimization.
33
+ return { optimizeDeps: { exclude: CLOUDFLARE_BUILT_IN_MODULES } };
34
+ }
35
+ return {
36
+ resolve: {
37
+ builtins: CLOUDFLARE_BUILT_IN_MODULES,
38
+ },
39
+ optimizeDeps: {
40
+ exclude: CLOUDFLARE_BUILT_IN_MODULES,
41
+ },
42
+ };
43
+ },
44
+ },
45
+ };
46
+ });
@@ -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";