@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,21 @@
1
- import type { Plugin } from "rolldown";
2
- import type { CloudflarePluginOptions } from "../plugin.js";
1
+ import path from "node:path";
2
+ import type * as vite from "vite";
3
+ import { createPlugin } from "../factory.js";
4
+ import type { CloudflarePluginOptions } from "../options.js";
3
5
  import { hasNodejsCompat } from "../utils.js";
6
+ import { WORKER_ENTRY_PREFIX } from "./virtual-modules.js";
4
7
 
5
- const DEFAULT_RESOLVE_CONDITION_NAMES = ["workerd", "worker", "module", "browser", "production"];
8
+ function getConditions(options: CloudflarePluginOptions): Array<string> {
9
+ return hasNodejsCompat(options.compatibilityFlags)
10
+ ? ["workerd", "worker", "node", "module"]
11
+ : ["workerd", "worker", "module", "browser"];
12
+ }
13
+
14
+ function getMainFields(options: CloudflarePluginOptions): Array<string> {
15
+ return hasNodejsCompat(options.compatibilityFlags)
16
+ ? ["module", "main", "jsnext:main", "jsnext"]
17
+ : ["browser", "module", "jsnext:main", "jsnext"];
18
+ }
6
19
 
7
20
  const DEFAULT_RESOLVE_EXTENSIONS = [
8
21
  ".mjs",
@@ -17,35 +30,168 @@ const DEFAULT_RESOLVE_EXTENSIONS = [
17
30
  ".ctx",
18
31
  ];
19
32
 
20
- export function makeOptionsPlugin(pluginOptions: CloudflarePluginOptions) {
21
- return {
22
- name: "rolldown-plugin-cloudflare:options",
33
+ const TARGET = "es2024";
34
+
35
+ export const optionsPlugin = createPlugin("options", (pluginOptions) => ({
36
+ rolldown: {
23
37
  options(options) {
38
+ options.input = wrapEntryInput(pluginOptions.main ?? options.input ?? {});
39
+ options.preserveEntrySignatures ??= "strict";
24
40
  options.platform ??= "neutral";
25
41
  options.resolve ??= {};
26
- options.resolve.conditionNames ??= DEFAULT_RESOLVE_CONDITION_NAMES;
42
+ options.resolve.conditionNames ??= [...getConditions(pluginOptions), "production"];
43
+ options.resolve.mainFields ??= getMainFields(pluginOptions);
27
44
  options.resolve.extensions ??= DEFAULT_RESOLVE_EXTENSIONS;
28
45
  options.transform ??= {};
29
- options.transform.target ??= "es2024";
46
+ options.transform.target ??= TARGET;
30
47
  options.transform.define ??= {};
31
- Object.assign(options.transform.define, {
32
- "process.env.NODE_ENV": '"production"',
33
- "global.process.env.NODE_ENV": '"production"',
34
- "globalThis.process.env.NODE_ENV": '"production"',
35
- ...(hasNodejsCompat(pluginOptions.compatibilityFlags)
36
- ? {}
37
- : {
38
- "process.env": "{}",
39
- "global.process.env": "{}",
40
- "globalThis.process.env": "{}",
41
- }),
42
- ...(pluginOptions.compatibilityDate && pluginOptions.compatibilityDate >= "2022-03-21"
43
- ? {
44
- "navigator.userAgent": '"Cloudflare-Workers"',
45
- }
46
- : {}),
47
- });
48
+ Object.assign(options.transform.define, getDefine(pluginOptions, "production"));
48
49
  return options;
49
50
  },
50
- } satisfies Plugin;
51
+ },
52
+ vite: {
53
+ async config(userConfig) {
54
+ const vite = await import("vite");
55
+ const isRolldown = "rolldownVersion" in this.meta;
56
+ const input =
57
+ pluginOptions.main ??
58
+ userConfig.environments?.ssr?.build?.rolldownOptions?.input ??
59
+ userConfig.environments?.ssr?.build?.rollupOptions?.input;
60
+ const rollupOptions: vite.Rollup.RollupOptions = {
61
+ input: wrapEntryInput(input ?? {}),
62
+ preserveEntrySignatures: "strict",
63
+ };
64
+ const define = getDefine(
65
+ pluginOptions,
66
+ process.env.NODE_ENV || userConfig.mode || "production",
67
+ );
68
+ const conditions = getConditions(pluginOptions);
69
+ const mainFields = getMainFields(pluginOptions);
70
+ return {
71
+ appType: "custom",
72
+ ssr: {
73
+ noExternal: true,
74
+ resolve: {
75
+ conditions: [...conditions, "development|production"],
76
+ },
77
+ },
78
+ builder: {
79
+ buildApp: async (app) => {
80
+ await app.build(app.environments.ssr);
81
+ await app.build(app.environments.client);
82
+ },
83
+ },
84
+ environments: {
85
+ client: {
86
+ build: {
87
+ outDir: getOutputDirectory(userConfig, "client"),
88
+ },
89
+ },
90
+ ssr: {
91
+ resolve: {
92
+ noExternal: true,
93
+ conditions: [...conditions, "development|production"],
94
+ },
95
+ build: {
96
+ ssr: true,
97
+ target: TARGET,
98
+ emitAssets: true,
99
+ copyPublicDir: false,
100
+ outDir: getOutputDirectory(userConfig, "ssr"),
101
+ ...(isRolldown
102
+ ? {
103
+ rolldownOptions: {
104
+ ...rollupOptions,
105
+ platform: "neutral",
106
+ resolve: {
107
+ mainFields,
108
+ extensions: DEFAULT_RESOLVE_EXTENSIONS,
109
+ },
110
+ },
111
+ }
112
+ : { rollupOptions }),
113
+ },
114
+ optimizeDeps: {
115
+ noDiscovery: false,
116
+ ignoreOutdatedRequests: true,
117
+ entries: pluginOptions.main ? vite.normalizePath(pluginOptions.main) : undefined,
118
+ ...(isRolldown
119
+ ? {
120
+ rolldownOptions: {
121
+ platform: "neutral",
122
+ resolve: {
123
+ conditionNames: [...conditions, "development|production"],
124
+ mainFields,
125
+ extensions: DEFAULT_RESOLVE_EXTENSIONS,
126
+ },
127
+ transform: {
128
+ target: TARGET,
129
+ define,
130
+ },
131
+ },
132
+ }
133
+ : {
134
+ esbuildOptions: {
135
+ platform: "neutral",
136
+ conditions: [...conditions, "development|production"],
137
+ resolveExtensions: DEFAULT_RESOLVE_EXTENSIONS,
138
+ mainFields,
139
+ target: TARGET,
140
+ define,
141
+ },
142
+ }),
143
+ },
144
+ keepProcessEnv: true,
145
+ },
146
+ },
147
+ };
148
+ },
149
+ },
150
+ }));
151
+
152
+ function wrapEntryInput(input: string | Array<string> | Record<string, string>) {
153
+ const virtualEntryId = (id: string) => `${WORKER_ENTRY_PREFIX}${id}` as const;
154
+ if (typeof input === "string") {
155
+ return { [path.parse(input).name || "index"]: virtualEntryId(input) };
156
+ }
157
+ if (Array.isArray(input)) {
158
+ return Object.fromEntries(input.map((p) => [path.parse(p).name, virtualEntryId(p)]));
159
+ }
160
+ return Object.fromEntries(
161
+ Object.entries(input).map(([key, value]) => [key, virtualEntryId(value)]),
162
+ );
163
+ }
164
+
165
+ function getDefine(options: CloudflarePluginOptions, nodeEnv: string): Record<string, string> {
166
+ return {
167
+ "process.env.NODE_ENV": JSON.stringify(nodeEnv),
168
+ "global.process.env.NODE_ENV": JSON.stringify(nodeEnv),
169
+ "globalThis.process.env.NODE_ENV": JSON.stringify(nodeEnv),
170
+ ...(hasNodejsCompat(options.compatibilityFlags)
171
+ ? {}
172
+ : {
173
+ "process.env": "{}",
174
+ "global.process.env": "{}",
175
+ "globalThis.process.env": "{}",
176
+ }),
177
+ ...(options.compatibilityDate && options.compatibilityDate >= "2022-03-21"
178
+ ? {
179
+ "navigator.userAgent": '"Cloudflare-Workers"',
180
+ }
181
+ : {}),
182
+ ...(nodeEnv === "production"
183
+ ? {
184
+ "import.meta.hot": "false",
185
+ }
186
+ : {}),
187
+ };
188
+ }
189
+
190
+ function getOutputDirectory(userConfig: vite.UserConfig, environmentName: string) {
191
+ const rootOutputDirectory = userConfig.build?.outDir ?? "dist";
192
+
193
+ return (
194
+ userConfig.environments?.[environmentName]?.build?.outDir ??
195
+ path.join(rootOutputDirectory, environmentName)
196
+ );
51
197
  }
@@ -0,0 +1,137 @@
1
+ import type { Plugin } from "rolldown";
2
+ import { createPlugin } from "../factory.js";
3
+ import type { UnenvApi } from "./nodejs-compat.js";
4
+
5
+ // oxlint-disable-next-line no-control-regex
6
+ const VIRTUAL_MODULE_REGEXP = /^\0distilled:.*$/;
7
+
8
+ export const WORKER_ENTRY_PREFIX = "\0distilled:worker-entry:" as const;
9
+ const USER_ENTRY_PREFIX = "\0distilled:user-entry:" as const;
10
+ const INJECT_PREFIX = "\0distilled:inject:" as const;
11
+ const EXPORT_TYPES_ID = "\0distilled:export-types" as const;
12
+
13
+ export const virtualModulesPlugin = createPlugin("virtual-modules", (options) => {
14
+ let unenvApi: UnenvApi | undefined;
15
+ const inject = () => {
16
+ if (!unenvApi) return [];
17
+ return [
18
+ ...unenvApi.polyfill.map((module) => `import "${module}";`),
19
+ ...Object.keys(unenvApi.inject).map(
20
+ (injectedName) => `import "${INJECT_PREFIX}${injectedName}";`,
21
+ ),
22
+ ];
23
+ };
24
+ return {
25
+ vite: {
26
+ enforce: "pre",
27
+ },
28
+ shared: {
29
+ buildStart({ plugins }) {
30
+ unenvApi = plugins.find(
31
+ (plugin): plugin is Plugin<UnenvApi> =>
32
+ "name" in plugin && plugin.name === "distilled-cloudflare:nodejs-unenv",
33
+ )?.api;
34
+ },
35
+ resolveId: {
36
+ filter: { id: VIRTUAL_MODULE_REGEXP },
37
+ handler(id) {
38
+ if (
39
+ id.startsWith(WORKER_ENTRY_PREFIX) ||
40
+ id.startsWith(INJECT_PREFIX) ||
41
+ id === EXPORT_TYPES_ID
42
+ ) {
43
+ return { id };
44
+ }
45
+ if (id.startsWith(USER_ENTRY_PREFIX)) {
46
+ return this.resolve(id.slice(USER_ENTRY_PREFIX.length), undefined, {
47
+ isEntry: false,
48
+ kind: "import-statement",
49
+ });
50
+ }
51
+ },
52
+ },
53
+ load: {
54
+ filter: { id: VIRTUAL_MODULE_REGEXP },
55
+ handler(id) {
56
+ if (id.startsWith(WORKER_ENTRY_PREFIX)) {
57
+ const userEntryId = id.replace(WORKER_ENTRY_PREFIX, USER_ENTRY_PREFIX);
58
+
59
+ return [
60
+ ...inject(),
61
+ ...(options.exports
62
+ ? [`export { ${options.exports.join(", ")} } from "${userEntryId}";`]
63
+ : [
64
+ `import * as userEntry from "${userEntryId}";`,
65
+ `export * from "${userEntryId}";`,
66
+ `export default userEntry.default ?? {};`,
67
+ ]),
68
+ "if (import.meta.hot) {",
69
+ ` const { getExportTypes } = await import("${EXPORT_TYPES_ID}");`,
70
+ " import.meta.hot.accept((module) => {",
71
+ " const exportTypes = getExportTypes(module);",
72
+ ' import.meta.hot.send("distilled-cloudflare:worker-export-types", exportTypes);',
73
+ " });",
74
+ "}",
75
+ ].join("\n");
76
+ }
77
+ if (id === EXPORT_TYPES_ID) {
78
+ return `
79
+ import {
80
+ WorkerEntrypoint,
81
+ DurableObject,
82
+ WorkflowEntrypoint,
83
+ } from "cloudflare:workers";
84
+
85
+ const baseClasses = new Map([
86
+ ["WorkerEntrypoint", WorkerEntrypoint],
87
+ ["DurableObject", DurableObject],
88
+ ["WorkflowEntrypoint", WorkflowEntrypoint],
89
+ ]);
90
+
91
+ export function getExportTypes(module) {
92
+ const exportTypes = {};
93
+
94
+ for (const [key, value] of Object.entries(module)) {
95
+ if (key === "default") {
96
+ continue;
97
+ }
98
+
99
+ let exportType;
100
+
101
+ if (typeof value === "function") {
102
+ for (const [type, baseClass] of baseClasses) {
103
+ if (baseClass.prototype.isPrototypeOf(value.prototype)) {
104
+ exportType = type;
105
+ break;
106
+ }
107
+ }
108
+
109
+ if (!exportType) {
110
+ exportType = "DurableObject";
111
+ }
112
+ } else if (typeof value === "object" && value !== null) {
113
+ exportType = "WorkerEntrypoint";
114
+ }
115
+
116
+ exportTypes[key] = exportType;
117
+ }
118
+
119
+ return exportTypes;
120
+ }`;
121
+ }
122
+ if (id.startsWith(INJECT_PREFIX)) {
123
+ const injectedName = id.slice(INJECT_PREFIX.length);
124
+ const moduleSpecifier = unenvApi?.inject[injectedName];
125
+ if (!moduleSpecifier) {
126
+ throw new Error(`Expected module specifier for "${injectedName}" to be defined`);
127
+ }
128
+ return [
129
+ `import ${injectedName} from "${moduleSpecifier}";`,
130
+ `globalThis.${injectedName} = ${injectedName};`,
131
+ ].join("\n");
132
+ }
133
+ },
134
+ },
135
+ },
136
+ };
137
+ });
@@ -1,20 +1,24 @@
1
- import type { Plugin } from "rolldown";
1
+ import { createPlugin } from "../factory.js";
2
2
  import { sanitizePath } from "../utils.js";
3
3
 
4
4
  const WASM_INIT_QUERY = /\.wasm\?init$/;
5
5
 
6
- export const wasmInitPlugin: Plugin = {
7
- name: "rolldown-plugin-cloudflare:wasm-init",
8
- load: {
9
- filter: { id: WASM_INIT_QUERY },
10
- handler(id) {
11
- return [
12
- `import wasmModule from "${sanitizePath(id)}";`,
13
- `export default async (imports) => {`,
14
- ` const result = await WebAssembly.instantiate(wasmModule, imports);`,
15
- ` return "instance" in result ? result.instance : result;`,
16
- `};`,
17
- ].join("\n");
6
+ export const wasmInitPlugin = createPlugin("wasm-init", () => ({
7
+ vite: {
8
+ enforce: "pre",
9
+ },
10
+ shared: {
11
+ load: {
12
+ filter: { id: WASM_INIT_QUERY },
13
+ handler(id) {
14
+ return [
15
+ `import wasmModule from "${sanitizePath(id)}";`,
16
+ `export default async (imports) => {`,
17
+ ` const result = await WebAssembly.instantiate(wasmModule, imports);`,
18
+ ` return "instance" in result ? result.instance : result;`,
19
+ `};`,
20
+ ].join("\n");
21
+ },
18
22
  },
19
23
  },
20
- };
24
+ }));