@distilled.cloud/cloudflare-rolldown-plugin 0.2.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.
- package/dist/factory.d.ts +19 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +30 -0
- package/dist/factory.js.map +1 -0
- package/dist/options.d.ts +26 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +1 -0
- package/dist/options.js.map +1 -0
- package/dist/plugin.d.ts +3 -6
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +10 -11
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/additional-modules.d.ts +1 -2
- package/dist/plugins/additional-modules.d.ts.map +1 -1
- package/dist/plugins/additional-modules.js +67 -53
- package/dist/plugins/additional-modules.js.map +1 -1
- package/dist/plugins/cloudflare-externals.d.ts +1 -2
- package/dist/plugins/cloudflare-externals.d.ts.map +1 -1
- package/dist/plugins/cloudflare-externals.js +36 -15
- package/dist/plugins/cloudflare-externals.js.map +1 -1
- package/dist/plugins/index.d.ts +7 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +7 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/nodejs-compat.d.ts +9 -3
- package/dist/plugins/nodejs-compat.d.ts.map +1 -1
- package/dist/plugins/nodejs-compat.js +180 -103
- package/dist/plugins/nodejs-compat.js.map +1 -1
- package/dist/plugins/options.d.ts +1 -5
- package/dist/plugins/options.d.ts.map +1 -1
- package/dist/plugins/options.js +128 -23
- package/dist/plugins/options.js.map +1 -1
- package/dist/plugins/virtual-modules.d.ts +3 -0
- package/dist/plugins/virtual-modules.d.ts.map +1 -0
- package/dist/plugins/virtual-modules.js +125 -0
- package/dist/plugins/virtual-modules.js.map +1 -0
- package/dist/plugins/wasm-init.d.ts +1 -2
- package/dist/plugins/wasm-init.d.ts.map +1 -1
- package/dist/plugins/wasm-init.js +15 -13
- package/dist/plugins/wasm-init.js.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -0
- package/dist/utils.js.map +1 -1
- package/package.json +17 -2
- package/src/factory.ts +60 -0
- package/src/options.ts +25 -0
- package/src/plugin.ts +22 -18
- package/src/plugins/additional-modules.ts +89 -55
- package/src/plugins/cloudflare-externals.ts +36 -16
- package/src/plugins/index.ts +6 -0
- package/src/plugins/nodejs-compat.ts +198 -126
- package/src/plugins/options.ts +145 -26
- package/src/plugins/virtual-modules.ts +135 -0
- package/src/plugins/wasm-init.ts +15 -14
- package/src/utils.ts +4 -0
|
@@ -2,159 +2,231 @@ 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
|
|
9
|
-
import { hasNodejsCompat } from "../utils.js";
|
|
7
|
+
import { createPlugin } from "../factory.js";
|
|
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
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return makeNodeJsImportWarningPlugin();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function makeUnenvPlugin(options: CloudflarePluginOptions): Array<Plugin> {
|
|
22
|
-
const { alias, inject, external, polyfill } = defineEnv({
|
|
23
|
-
presets: [
|
|
24
|
-
getCloudflarePreset({
|
|
25
|
-
compatibilityDate: options.compatibilityDate,
|
|
26
|
-
compatibilityFlags: options.compatibilityFlags,
|
|
27
|
-
}),
|
|
28
|
-
],
|
|
29
|
-
}).env;
|
|
30
|
-
|
|
31
|
-
const injectVirtualModules = makeInjectVirtualModules(inject);
|
|
32
|
-
const require = createRequire(import.meta.url);
|
|
33
|
-
|
|
34
|
-
return [
|
|
35
|
-
esmExternalRequirePlugin({
|
|
36
|
-
external: [...external],
|
|
37
|
-
skipDuplicateCheck: true,
|
|
38
|
-
}),
|
|
39
|
-
{
|
|
40
|
-
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: {
|
|
41
17
|
resolveId: {
|
|
42
|
-
filter: { id:
|
|
18
|
+
filter: { id: ASYNC_HOOKS_REGEXP },
|
|
43
19
|
handler(id) {
|
|
44
|
-
|
|
45
|
-
return { id };
|
|
46
|
-
}
|
|
20
|
+
return { id, external: true };
|
|
47
21
|
},
|
|
48
22
|
},
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
23
|
+
},
|
|
24
|
+
vite: {
|
|
25
|
+
configEnvironment(name) {
|
|
26
|
+
if (name === "client") return;
|
|
27
|
+
return {
|
|
28
|
+
resolve: {
|
|
29
|
+
builtins: ["async_hooks", "node:async_hooks"],
|
|
30
|
+
},
|
|
31
|
+
optimizeDeps: {
|
|
32
|
+
exclude: ["async_hooks", "node:async_hooks"],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
54
35
|
},
|
|
55
36
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export interface UnenvApi {
|
|
41
|
+
polyfill: ReadonlyArray<string>;
|
|
42
|
+
inject: { [injectedName: string]: string };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const nodejsUnenvPlugin = createPlugin<"nodejs-unenv", UnenvApi>(
|
|
46
|
+
"nodejs-unenv",
|
|
47
|
+
(options) => {
|
|
48
|
+
if (!hasNodejsCompat(options.compatibilityFlags)) return;
|
|
49
|
+
const { alias, inject, external, polyfill } = defineEnv({
|
|
50
|
+
presets: [
|
|
51
|
+
getCloudflarePreset({
|
|
52
|
+
compatibilityDate: options.compatibilityDate,
|
|
53
|
+
compatibilityFlags: options.compatibilityFlags,
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
}).env;
|
|
57
|
+
const entries = new Set(Object.values(alias));
|
|
58
|
+
for (const globalInject of Object.values(inject)) {
|
|
59
|
+
if (typeof globalInject === "string") {
|
|
60
|
+
entries.add(globalInject);
|
|
61
|
+
} else {
|
|
62
|
+
entries.add(globalInject[0]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
polyfill.forEach((module) => entries.add(module));
|
|
66
|
+
external.forEach((module) => entries.delete(module));
|
|
67
|
+
const require = createRequire(import.meta.url);
|
|
68
|
+
const resolve = (id: string) => {
|
|
69
|
+
if (alias[id] && !external.includes(alias[id])) {
|
|
70
|
+
return require.resolve(alias[id]);
|
|
71
|
+
}
|
|
72
|
+
if (entries.has(id)) {
|
|
73
|
+
return require.resolve(id);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const RESOLVE_ID_FILTER = {
|
|
77
|
+
id: [NODE_BUILTIN_MODULES_REGEXP, /^unenv\//, /^@cloudflare\/unenv-preset\//],
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
shared: {
|
|
81
|
+
api: {
|
|
82
|
+
polyfill,
|
|
83
|
+
inject: Object.fromEntries(
|
|
84
|
+
Object.entries(inject).map(([injectedName, moduleSpecifier]) => {
|
|
85
|
+
assert(
|
|
86
|
+
typeof moduleSpecifier === "string",
|
|
87
|
+
`expected moduleSpecifier to be a string`,
|
|
88
|
+
);
|
|
89
|
+
return [injectedName, moduleSpecifier];
|
|
90
|
+
}),
|
|
91
|
+
),
|
|
64
92
|
},
|
|
65
93
|
},
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
94
|
+
rolldown: {
|
|
95
|
+
async options(options) {
|
|
96
|
+
options.plugins = [
|
|
97
|
+
esmExternalRequirePlugin({
|
|
98
|
+
external: [...external],
|
|
99
|
+
skipDuplicateCheck: true,
|
|
100
|
+
}),
|
|
101
|
+
options.plugins,
|
|
102
|
+
];
|
|
103
|
+
return options;
|
|
104
|
+
},
|
|
105
|
+
resolveId: {
|
|
106
|
+
filter: RESOLVE_ID_FILTER,
|
|
107
|
+
handler(source, importer, options) {
|
|
108
|
+
const resolved = resolve(source);
|
|
109
|
+
if (!resolved) return;
|
|
110
|
+
return this.resolve(resolved, importer, options);
|
|
111
|
+
},
|
|
83
112
|
},
|
|
84
113
|
},
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
114
|
+
vite: {
|
|
115
|
+
enforce: "pre",
|
|
116
|
+
async configEnvironment(name) {
|
|
117
|
+
if (name === "client") return;
|
|
118
|
+
return {
|
|
119
|
+
resolve: {
|
|
120
|
+
builtins: [...external],
|
|
121
|
+
},
|
|
122
|
+
...(this.meta.rolldownVersion
|
|
123
|
+
? {
|
|
124
|
+
build: {
|
|
125
|
+
rolldownOptions: {
|
|
126
|
+
plugins: [
|
|
127
|
+
esmExternalRequirePlugin({
|
|
128
|
+
external: [...external],
|
|
129
|
+
skipDuplicateCheck: true,
|
|
130
|
+
}),
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
: {}),
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
async configureServer(server) {
|
|
139
|
+
await Promise.all(
|
|
140
|
+
Object.values(server.environments).flatMap(async (environment) => {
|
|
141
|
+
const depsOptimizer = environment.depsOptimizer;
|
|
142
|
+
if (!depsOptimizer) return;
|
|
143
|
+
await depsOptimizer.init();
|
|
144
|
+
return Array.from(entries).map((entry) => {
|
|
145
|
+
const resolved = resolve(entry);
|
|
146
|
+
if (!resolved) return;
|
|
147
|
+
const registration = depsOptimizer.registerMissingImport(entry, resolved);
|
|
148
|
+
return registration?.processing;
|
|
149
|
+
});
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
},
|
|
153
|
+
resolveId: {
|
|
154
|
+
filter: RESOLVE_ID_FILTER,
|
|
155
|
+
handler(source, importer, options) {
|
|
156
|
+
const resolved = resolve(source);
|
|
157
|
+
if (!resolved) return;
|
|
158
|
+
if (this.environment.mode === "dev" && this.environment.depsOptimizer) {
|
|
159
|
+
// We are in dev mode (rather than build).
|
|
160
|
+
// So let's pre-bundle this polyfill entry-point using the dependency optimizer.
|
|
161
|
+
const { id } = this.environment.depsOptimizer.registerMissingImport(source, resolved);
|
|
162
|
+
// We use the unresolved path to the polyfill and let the dependency optimizer's
|
|
163
|
+
// resolver find the resolved path to the bundled version.
|
|
164
|
+
return this.resolve(id, importer, options);
|
|
165
|
+
}
|
|
166
|
+
return this.resolve(resolved, importer, options);
|
|
167
|
+
},
|
|
168
|
+
},
|
|
95
169
|
},
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
);
|
|
99
173
|
|
|
100
|
-
|
|
174
|
+
export const nodejsImportWarningPlugin = createPlugin("nodejs-import-warning", (options) => {
|
|
175
|
+
if (hasNodejsCompat(options.compatibilityFlags)) return;
|
|
101
176
|
const imports = new Map<string, Set<string>>();
|
|
102
177
|
let root = process.cwd();
|
|
103
178
|
return {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
resolveId: {
|
|
111
|
-
filter: { id: NODE_BUILTIN_MODULES_REGEXP },
|
|
112
|
-
async handler(id, importer) {
|
|
113
|
-
if (importer) {
|
|
114
|
-
if (!imports.has(id)) {
|
|
115
|
-
imports.set(id, new Set());
|
|
116
|
-
}
|
|
117
|
-
imports.get(id)?.add(importer);
|
|
179
|
+
rolldown: {
|
|
180
|
+
options(options) {
|
|
181
|
+
if (options.cwd) {
|
|
182
|
+
root = options.cwd;
|
|
118
183
|
}
|
|
119
|
-
return { id, external: true };
|
|
120
184
|
},
|
|
121
185
|
},
|
|
122
|
-
|
|
123
|
-
|
|
186
|
+
vite: {
|
|
187
|
+
enforce: "pre",
|
|
188
|
+
configResolved(config) {
|
|
189
|
+
root = config.root;
|
|
190
|
+
},
|
|
124
191
|
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
192
|
+
shared: {
|
|
193
|
+
buildStart() {
|
|
194
|
+
imports.clear();
|
|
195
|
+
},
|
|
196
|
+
resolveId: {
|
|
197
|
+
filter: { id: NODE_BUILTIN_MODULES_REGEXP },
|
|
198
|
+
async handler(id, importer) {
|
|
199
|
+
if (hasNodejsAls(options.compatibilityFlags) && ASYNC_HOOKS_REGEXP.test(id)) return;
|
|
200
|
+
if (importer) {
|
|
201
|
+
if (!imports.has(id)) {
|
|
202
|
+
imports.set(id, new Set());
|
|
203
|
+
}
|
|
204
|
+
imports.get(id)?.add(importer);
|
|
205
|
+
}
|
|
206
|
+
return { id, external: true };
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
buildEnd() {
|
|
210
|
+
const filteredImports: Array<{ id: string; importer: string }> = [];
|
|
131
211
|
for (const [id, importers] of imports.entries()) {
|
|
132
212
|
for (const importer of importers) {
|
|
133
|
-
|
|
213
|
+
if (this.getModuleInfo(importer)) {
|
|
214
|
+
filteredImports.push({ id, importer });
|
|
215
|
+
}
|
|
134
216
|
}
|
|
135
217
|
}
|
|
136
|
-
|
|
137
|
-
|
|
218
|
+
if (filteredImports.length > 0) {
|
|
219
|
+
const message = [
|
|
220
|
+
"Unexpected Node.js imports. ",
|
|
221
|
+
'Do you need to enable the "nodejs_compat" compatibility flag? ',
|
|
222
|
+
"Refer to https://developers.cloudflare.com/workers/runtime-apis/nodejs/ for more details.\n",
|
|
223
|
+
...filteredImports.map(
|
|
224
|
+
({ id, importer }) => ` - "${id}" imported from "${path.relative(root, importer)}"\n`,
|
|
225
|
+
),
|
|
226
|
+
].join("");
|
|
227
|
+
this.warn(message);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
138
230
|
},
|
|
139
231
|
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function makeInjectVirtualModules(
|
|
143
|
-
inject: Readonly<Record<string, string | ReadonlyArray<string>>>,
|
|
144
|
-
) {
|
|
145
|
-
const virtualModules = new Map<string, string>();
|
|
146
|
-
for (const [injectedName, moduleSpecifier] of Object.entries(inject)) {
|
|
147
|
-
// The type from unenv is string | string[], but the Cloudflare preset only uses string.
|
|
148
|
-
// This indicates a default export that we set on globalThis.
|
|
149
|
-
assert(typeof moduleSpecifier === "string", `expected moduleSpecifier to be a string`);
|
|
150
|
-
virtualModules.set(
|
|
151
|
-
injectVirtualModuleId(injectedName),
|
|
152
|
-
`import virtualModule from "${moduleSpecifier}"; globalThis.${injectedName} = virtualModule;`,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
return virtualModules;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function injectVirtualModuleId(module: string) {
|
|
159
|
-
return `virtual:nodejs-global-inject/${module}` as const;
|
|
160
|
-
}
|
|
232
|
+
});
|
package/src/plugins/options.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type
|
|
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
|
|
8
|
+
const DEFAULT_CONDITIONS = ["workerd", "worker", "module", "browser"];
|
|
9
|
+
|
|
10
|
+
const DEFAULT_RESOLVE_MAIN_FIELDS = ["browser", "module", "jsnext:main", "jsnext"];
|
|
6
11
|
|
|
7
12
|
const DEFAULT_RESOLVE_EXTENSIONS = [
|
|
8
13
|
".mjs",
|
|
@@ -17,35 +22,149 @@ const DEFAULT_RESOLVE_EXTENSIONS = [
|
|
|
17
22
|
".ctx",
|
|
18
23
|
];
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
const TARGET = "es2024";
|
|
26
|
+
|
|
27
|
+
export const optionsPlugin = createPlugin("options", (pluginOptions) => ({
|
|
28
|
+
rolldown: {
|
|
23
29
|
options(options) {
|
|
30
|
+
options.input = wrapEntryInput(options.input ?? {});
|
|
31
|
+
options.preserveEntrySignatures ??= "strict";
|
|
24
32
|
options.platform ??= "neutral";
|
|
25
33
|
options.resolve ??= {};
|
|
26
|
-
options.resolve.conditionNames ??=
|
|
34
|
+
options.resolve.conditionNames ??= [...DEFAULT_CONDITIONS, "production"];
|
|
35
|
+
options.resolve.mainFields ??= DEFAULT_RESOLVE_MAIN_FIELDS;
|
|
27
36
|
options.resolve.extensions ??= DEFAULT_RESOLVE_EXTENSIONS;
|
|
28
37
|
options.transform ??= {};
|
|
29
|
-
options.transform.target ??=
|
|
38
|
+
options.transform.target ??= TARGET;
|
|
30
39
|
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
|
-
});
|
|
40
|
+
Object.assign(options.transform.define, getDefine(pluginOptions, "production"));
|
|
48
41
|
return options;
|
|
49
42
|
},
|
|
50
|
-
}
|
|
43
|
+
},
|
|
44
|
+
vite: {
|
|
45
|
+
config(userConfig) {
|
|
46
|
+
const isRolldown = "rolldownVersion" in this.meta;
|
|
47
|
+
const rollupOptions: vite.Rollup.RollupOptions = {
|
|
48
|
+
input: wrapEntryInput(userConfig.environments?.ssr?.build?.rollupOptions?.input ?? {}),
|
|
49
|
+
preserveEntrySignatures: "strict",
|
|
50
|
+
};
|
|
51
|
+
const define = getDefine(
|
|
52
|
+
pluginOptions,
|
|
53
|
+
process.env.NODE_ENV || userConfig.mode || "production",
|
|
54
|
+
);
|
|
55
|
+
return {
|
|
56
|
+
ssr: {
|
|
57
|
+
noExternal: true,
|
|
58
|
+
resolve: {
|
|
59
|
+
conditions: [...DEFAULT_CONDITIONS, "development|production"],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
environments: {
|
|
63
|
+
client: {
|
|
64
|
+
build: {
|
|
65
|
+
outDir: getOutputDirectory(userConfig, "client"),
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
ssr: {
|
|
69
|
+
resolve: {
|
|
70
|
+
noExternal: true,
|
|
71
|
+
conditions: [...DEFAULT_CONDITIONS, "development|production"],
|
|
72
|
+
},
|
|
73
|
+
build: {
|
|
74
|
+
ssr: true,
|
|
75
|
+
target: TARGET,
|
|
76
|
+
emitAssets: true,
|
|
77
|
+
copyPublicDir: false,
|
|
78
|
+
outDir: getOutputDirectory(userConfig, "ssr"),
|
|
79
|
+
...(isRolldown
|
|
80
|
+
? {
|
|
81
|
+
rolldownOptions: {
|
|
82
|
+
...rollupOptions,
|
|
83
|
+
platform: "neutral",
|
|
84
|
+
resolve: {
|
|
85
|
+
mainFields: DEFAULT_RESOLVE_MAIN_FIELDS,
|
|
86
|
+
extensions: DEFAULT_RESOLVE_EXTENSIONS,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
: { rollupOptions }),
|
|
91
|
+
},
|
|
92
|
+
optimizeDeps: {
|
|
93
|
+
noDiscovery: false,
|
|
94
|
+
ignoreOutdatedRequests: true,
|
|
95
|
+
...(isRolldown
|
|
96
|
+
? {
|
|
97
|
+
rolldownOptions: {
|
|
98
|
+
platform: "neutral",
|
|
99
|
+
resolve: {
|
|
100
|
+
conditionNames: [...DEFAULT_CONDITIONS, "development|production"],
|
|
101
|
+
mainFields: DEFAULT_RESOLVE_MAIN_FIELDS,
|
|
102
|
+
extensions: DEFAULT_RESOLVE_EXTENSIONS,
|
|
103
|
+
},
|
|
104
|
+
transform: {
|
|
105
|
+
target: TARGET,
|
|
106
|
+
define,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
: {
|
|
111
|
+
esbuildOptions: {
|
|
112
|
+
platform: "neutral",
|
|
113
|
+
conditions: [...DEFAULT_CONDITIONS, "development|production"],
|
|
114
|
+
resolveExtensions: DEFAULT_RESOLVE_EXTENSIONS,
|
|
115
|
+
mainFields: DEFAULT_RESOLVE_MAIN_FIELDS,
|
|
116
|
+
target: TARGET,
|
|
117
|
+
define,
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
keepProcessEnv: true,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
function wrapEntryInput(input: string | Array<string> | Record<string, string>) {
|
|
130
|
+
const virtualEntryId = (id: string) => `${WORKER_ENTRY_PREFIX}${id}` as const;
|
|
131
|
+
|
|
132
|
+
if (typeof input === "string") {
|
|
133
|
+
return virtualEntryId(input);
|
|
134
|
+
}
|
|
135
|
+
if (Array.isArray(input)) {
|
|
136
|
+
return input.map(virtualEntryId);
|
|
137
|
+
}
|
|
138
|
+
return Object.fromEntries(
|
|
139
|
+
Object.entries(input).map(([key, value]) => [virtualEntryId(key), value]),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getDefine(options: CloudflarePluginOptions, nodeEnv: string): Record<string, string> {
|
|
144
|
+
return {
|
|
145
|
+
"process.env.NODE_ENV": JSON.stringify(nodeEnv),
|
|
146
|
+
"global.process.env.NODE_ENV": JSON.stringify(nodeEnv),
|
|
147
|
+
"globalThis.process.env.NODE_ENV": JSON.stringify(nodeEnv),
|
|
148
|
+
...(hasNodejsCompat(options.compatibilityFlags)
|
|
149
|
+
? {}
|
|
150
|
+
: {
|
|
151
|
+
"process.env": "{}",
|
|
152
|
+
"global.process.env": "{}",
|
|
153
|
+
"globalThis.process.env": "{}",
|
|
154
|
+
}),
|
|
155
|
+
...(options.compatibilityDate && options.compatibilityDate >= "2022-03-21"
|
|
156
|
+
? {
|
|
157
|
+
"navigator.userAgent": '"Cloudflare-Workers"',
|
|
158
|
+
}
|
|
159
|
+
: {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getOutputDirectory(userConfig: vite.UserConfig, environmentName: string) {
|
|
164
|
+
const rootOutputDirectory = userConfig.build?.outDir ?? "dist";
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
userConfig.environments?.[environmentName]?.build?.outDir ??
|
|
168
|
+
path.join(rootOutputDirectory, environmentName)
|
|
169
|
+
);
|
|
51
170
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
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 PEAR_ENTRY_PREFIX = "\0distilled:pear-entry:" as const;
|
|
11
|
+
const INJECT_PREFIX = "\0distilled:inject:" as const;
|
|
12
|
+
const EXPORT_TYPES_ID = "\0distilled:export-types" as const;
|
|
13
|
+
|
|
14
|
+
export const virtualModulesPlugin = createPlugin("virtual-modules", (options) => {
|
|
15
|
+
let unenvApi: UnenvApi | undefined;
|
|
16
|
+
const inject = () => {
|
|
17
|
+
if (!unenvApi) return [];
|
|
18
|
+
return [
|
|
19
|
+
...unenvApi.polyfill.map((module) => `import "${module}";`),
|
|
20
|
+
...Object.keys(unenvApi.inject).map(
|
|
21
|
+
(injectedName) => `import "${INJECT_PREFIX}${injectedName}";`,
|
|
22
|
+
),
|
|
23
|
+
];
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
shared: {
|
|
27
|
+
buildStart({ plugins }) {
|
|
28
|
+
unenvApi = plugins.find(
|
|
29
|
+
(plugin): plugin is Plugin<UnenvApi> =>
|
|
30
|
+
"name" in plugin && plugin.name === "distilled-cloudflare:nodejs-unenv",
|
|
31
|
+
)?.api;
|
|
32
|
+
},
|
|
33
|
+
resolveId: {
|
|
34
|
+
filter: { id: VIRTUAL_MODULE_REGEXP },
|
|
35
|
+
handler(id) {
|
|
36
|
+
if (
|
|
37
|
+
id.startsWith(WORKER_ENTRY_PREFIX) ||
|
|
38
|
+
id.startsWith(PEAR_ENTRY_PREFIX) ||
|
|
39
|
+
id.startsWith(INJECT_PREFIX) ||
|
|
40
|
+
id === EXPORT_TYPES_ID
|
|
41
|
+
) {
|
|
42
|
+
return { id };
|
|
43
|
+
}
|
|
44
|
+
if (id.startsWith(USER_ENTRY_PREFIX)) {
|
|
45
|
+
return this.resolve(id.slice(USER_ENTRY_PREFIX.length), undefined, {
|
|
46
|
+
isEntry: true,
|
|
47
|
+
kind: "import-statement",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
load: {
|
|
53
|
+
filter: { id: VIRTUAL_MODULE_REGEXP },
|
|
54
|
+
handler(id) {
|
|
55
|
+
if (id.startsWith(WORKER_ENTRY_PREFIX)) {
|
|
56
|
+
const userEntryId = id.replace(WORKER_ENTRY_PREFIX, USER_ENTRY_PREFIX);
|
|
57
|
+
return [
|
|
58
|
+
...inject(),
|
|
59
|
+
`import { getExportTypes } from "${EXPORT_TYPES_ID}";`,
|
|
60
|
+
...(options.exports
|
|
61
|
+
? [`export { ${options.exports.join(", ")} } from "${userEntryId}";`]
|
|
62
|
+
: [
|
|
63
|
+
`import * as userEntry from "${userEntryId}";`,
|
|
64
|
+
`export * from "${userEntryId}";`,
|
|
65
|
+
`export default userEntry.default ?? {};`,
|
|
66
|
+
]),
|
|
67
|
+
"if (import.meta.hot) {",
|
|
68
|
+
" import.meta.hot.accept((module) => {",
|
|
69
|
+
" const exportTypes = getExportTypes(module);",
|
|
70
|
+
' import.meta.hot.send("distilled-cloudflare:worker-export-types", exportTypes);',
|
|
71
|
+
" });",
|
|
72
|
+
"}",
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
75
|
+
if (id === EXPORT_TYPES_ID) {
|
|
76
|
+
return `
|
|
77
|
+
import {
|
|
78
|
+
WorkerEntrypoint,
|
|
79
|
+
DurableObject,
|
|
80
|
+
WorkflowEntrypoint,
|
|
81
|
+
} from "cloudflare:workers";
|
|
82
|
+
|
|
83
|
+
const baseClasses = new Map([
|
|
84
|
+
["WorkerEntrypoint", WorkerEntrypoint],
|
|
85
|
+
["DurableObject", DurableObject],
|
|
86
|
+
["WorkflowEntrypoint", WorkflowEntrypoint],
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
export function getExportTypes(module) {
|
|
90
|
+
const exportTypes = {};
|
|
91
|
+
|
|
92
|
+
for (const [key, value] of Object.entries(module)) {
|
|
93
|
+
if (key === "default") {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let exportType;
|
|
98
|
+
|
|
99
|
+
if (typeof value === "function") {
|
|
100
|
+
for (const [type, baseClass] of baseClasses) {
|
|
101
|
+
if (baseClass.prototype.isPrototypeOf(value.prototype)) {
|
|
102
|
+
exportType = type;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!exportType) {
|
|
108
|
+
exportType = "DurableObject";
|
|
109
|
+
}
|
|
110
|
+
} else if (typeof value === "object" && value !== null) {
|
|
111
|
+
exportType = "WorkerEntrypoint";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
exportTypes[key] = exportType;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return exportTypes;
|
|
118
|
+
}`;
|
|
119
|
+
}
|
|
120
|
+
if (id.startsWith(INJECT_PREFIX)) {
|
|
121
|
+
const injectedName = id.slice(INJECT_PREFIX.length);
|
|
122
|
+
const moduleSpecifier = unenvApi?.inject[injectedName];
|
|
123
|
+
if (!moduleSpecifier) {
|
|
124
|
+
throw new Error(`Expected module specifier for "${injectedName}" to be defined`);
|
|
125
|
+
}
|
|
126
|
+
return [
|
|
127
|
+
`import ${injectedName} from "${moduleSpecifier}";`,
|
|
128
|
+
`globalThis.${injectedName} = ${injectedName};`,
|
|
129
|
+
].join("\n");
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
});
|