@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.3 → 3.9.0-ultramodern.5
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/cjs/backend-federation-build/codegen.js +256 -0
- package/dist/cjs/backend-federation-build/config.js +204 -0
- package/dist/cjs/backend-federation-build/emit.js +150 -0
- package/dist/cjs/backend-federation-build/index.js +53 -0
- package/dist/cjs/build-config/build-environment.js +268 -0
- package/dist/cjs/build-config/precompress/plugin.js +124 -0
- package/dist/cjs/build-config/precompress/types.js +18 -0
- package/dist/cjs/build-config/public.js +46 -0
- package/dist/cjs/cloudflare/constants.js +1 -1
- package/dist/cjs/cloudflare/delivery-unit.js +9 -9
- package/dist/cjs/cloudflare-output-contract.js +37 -1
- package/dist/cjs/cloudflare-output-verifier/identity.js +6 -6
- package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +4 -1
- package/dist/cjs/config.js +29 -1
- package/dist/cjs/deploy-output/npmAliases.js +226 -0
- package/dist/cjs/deploy-output/plugin.js +76 -0
- package/dist/cjs/deploy-output/target.js +48 -0
- package/dist/cjs/native-type-checker.js +150 -0
- package/dist/cjs/release-envelope/framework-output.js +27 -27
- package/dist/cjs/release-envelope/plugin.js +2 -1
- package/dist/cjs/release-identity.js +3 -6
- package/dist/esm/backend-federation-build/codegen.mjs +205 -0
- package/dist/esm/backend-federation-build/config.mjs +128 -0
- package/dist/esm/backend-federation-build/emit.mjs +99 -0
- package/dist/esm/backend-federation-build/index.mjs +1 -0
- package/dist/esm/build-config/build-environment.mjs +224 -0
- package/dist/esm/build-config/precompress/plugin.mjs +76 -0
- package/dist/esm/build-config/precompress/types.mjs +0 -0
- package/dist/esm/build-config/public.mjs +1 -0
- package/dist/esm/cloudflare/constants.mjs +1 -1
- package/dist/esm/cloudflare/delivery-unit.mjs +1 -1
- package/dist/esm/cloudflare-output-contract.mjs +35 -2
- package/dist/esm/cloudflare-output-verifier/identity.mjs +1 -1
- package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +5 -2
- package/dist/esm/config.mjs +1 -0
- package/dist/esm/deploy-output/npmAliases.mjs +174 -0
- package/dist/esm/deploy-output/plugin.mjs +28 -0
- package/dist/esm/deploy-output/target.mjs +10 -0
- package/dist/esm/native-type-checker.mjs +99 -0
- package/dist/esm/release-envelope/framework-output.mjs +1 -1
- package/dist/esm/release-envelope/plugin.mjs +2 -1
- package/dist/esm/release-identity.mjs +3 -6
- package/dist/esm-node/backend-federation-build/codegen.mjs +206 -0
- package/dist/esm-node/backend-federation-build/config.mjs +129 -0
- package/dist/esm-node/backend-federation-build/emit.mjs +100 -0
- package/dist/esm-node/backend-federation-build/index.mjs +2 -0
- package/dist/esm-node/build-config/build-environment.mjs +225 -0
- package/dist/esm-node/build-config/precompress/plugin.mjs +77 -0
- package/dist/esm-node/build-config/precompress/types.mjs +1 -0
- package/dist/esm-node/build-config/public.mjs +2 -0
- package/dist/esm-node/cloudflare/constants.mjs +1 -1
- package/dist/esm-node/cloudflare/delivery-unit.mjs +1 -1
- package/dist/esm-node/cloudflare-output-contract.mjs +35 -2
- package/dist/esm-node/cloudflare-output-verifier/identity.mjs +1 -1
- package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +5 -2
- package/dist/esm-node/config.mjs +1 -0
- package/dist/esm-node/deploy-output/npmAliases.mjs +175 -0
- package/dist/esm-node/deploy-output/plugin.mjs +31 -0
- package/dist/esm-node/deploy-output/target.mjs +11 -0
- package/dist/esm-node/native-type-checker.mjs +100 -0
- package/dist/esm-node/release-envelope/framework-output.mjs +1 -1
- package/dist/esm-node/release-envelope/plugin.mjs +2 -1
- package/dist/esm-node/release-identity.mjs +3 -6
- package/dist/types/backend-federation-build/codegen.d.ts +86 -0
- package/dist/types/backend-federation-build/config.d.ts +103 -0
- package/dist/types/backend-federation-build/emit.d.ts +21 -0
- package/dist/types/backend-federation-build/index.d.ts +2 -0
- package/dist/types/build-config/build-environment.d.ts +22 -0
- package/dist/types/build-config/precompress/plugin.d.ts +4 -0
- package/dist/types/build-config/precompress/types.d.ts +19 -0
- package/dist/types/build-config/public.d.ts +2 -0
- package/dist/types/cloudflare/constants.d.ts +1 -1
- package/dist/types/cloudflare/delivery-unit.d.ts +1 -1
- package/dist/types/cloudflare/worker-manifest.d.ts +2 -2
- package/dist/types/cloudflare-output-contract.d.ts +2 -1
- package/dist/types/cloudflare-output-verifier/identity.d.ts +1 -1
- package/dist/types/config.d.ts +2 -0
- package/dist/types/deploy-output/npmAliases.d.ts +14 -0
- package/dist/types/deploy-output/plugin.d.ts +15 -0
- package/dist/types/deploy-output/target.d.ts +6 -0
- package/dist/types/native-type-checker.d.ts +15 -0
- package/dist/types/precompress/LICENSE +20 -0
- package/dist/types/precompress/index.d.ts +76 -0
- package/package.json +55 -8
- package/src/backend-federation-build/codegen.ts +245 -0
- package/src/backend-federation-build/config.ts +376 -0
- package/src/backend-federation-build/emit.ts +205 -0
- package/src/backend-federation-build/index.ts +2 -0
- package/src/build-config/build-environment.ts +404 -0
- package/src/build-config/precompress/plugin.ts +140 -0
- package/src/build-config/precompress/types.ts +26 -0
- package/src/build-config/public.ts +6 -0
- package/src/cloudflare/constants.ts +1 -1
- package/src/cloudflare/delivery-unit.ts +2 -2
- package/src/cloudflare/worker-manifest.ts +1 -0
- package/src/cloudflare-builder.ts +1 -0
- package/src/cloudflare-output-contract.ts +37 -0
- package/src/cloudflare-output-verifier/identity.ts +1 -1
- package/src/cloudflare-output-verifier/worker-bundles.ts +5 -3
- package/src/config.ts +7 -0
- package/src/deploy-output/npmAliases.ts +333 -0
- package/src/deploy-output/plugin.ts +49 -0
- package/src/deploy-output/target.ts +23 -0
- package/src/native-type-checker.ts +122 -0
- package/src/release-envelope/framework-output.ts +1 -1
- package/src/release-envelope/plugin.ts +5 -1
- package/src/release-identity.ts +3 -2
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import {
|
|
4
|
+
accessSync,
|
|
5
|
+
chmodSync,
|
|
6
|
+
constants,
|
|
7
|
+
lstatSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
realpathSync,
|
|
11
|
+
renameSync,
|
|
12
|
+
rmSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
} from 'node:fs';
|
|
15
|
+
import { createRequire } from 'node:module';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
18
|
+
import type { URL } from 'node:url';
|
|
19
|
+
import type { Rspack } from '@rsbuild/core';
|
|
20
|
+
|
|
21
|
+
const EFFECT_TSGO_PACKAGE = '@effect/tsgo';
|
|
22
|
+
const EFFECT_TSGO_BIN = 'effect-tsgo';
|
|
23
|
+
const EFFECT_TSGO_RESOLUTION_ERROR =
|
|
24
|
+
'Unable to resolve the Effect TS-Go compiler. Install "@effect/tsgo" and a native TypeScript backend for this build config, or set EFFECT_TSGO_BIN.';
|
|
25
|
+
const executableEffectTsgoCompilers = new Map<string, string>();
|
|
26
|
+
const BUILD_CONFIG_ENVIRONMENT_PLUGIN =
|
|
27
|
+
'ModernJsBuildConfigEnvironmentLeasePlugin';
|
|
28
|
+
const ENVIRONMENT_LEASE_REGISTRY_KEY = Symbol.for(
|
|
29
|
+
'@modern-js/app-tools/build-config-environment-lease-registry/v1',
|
|
30
|
+
);
|
|
31
|
+
const ENVIRONMENT_LEASE_REGISTRY_BRAND = Symbol.for(
|
|
32
|
+
'@modern-js/app-tools/build-config-environment-lease-registry-brand/v1',
|
|
33
|
+
);
|
|
34
|
+
const LIFECYCLE_HOOK_NAMES = [
|
|
35
|
+
'run',
|
|
36
|
+
'watchRun',
|
|
37
|
+
'afterDone',
|
|
38
|
+
'failed',
|
|
39
|
+
'shutdown',
|
|
40
|
+
'watchClose',
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
43
|
+
type PackageJson = {
|
|
44
|
+
bin?: Record<string, string> | string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type EnvironmentLease = {
|
|
48
|
+
originalValue: string | undefined;
|
|
49
|
+
owners: Set<symbol>;
|
|
50
|
+
value: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type EnvironmentLeaseRegistry = Readonly<{
|
|
54
|
+
acquire: (name: string, value: string) => () => void;
|
|
55
|
+
brand: symbol;
|
|
56
|
+
}>;
|
|
57
|
+
|
|
58
|
+
type LifecycleHookName = (typeof LIFECYCLE_HOOK_NAMES)[number];
|
|
59
|
+
|
|
60
|
+
type LifecycleHook = {
|
|
61
|
+
tap: (options: { name: string; stage: number }, handler: () => void) => void;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function restoreEnvironment(name: string, value: string | undefined): void {
|
|
65
|
+
if (value === undefined) {
|
|
66
|
+
delete process.env[name];
|
|
67
|
+
} else {
|
|
68
|
+
process.env[name] = value;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function createEnvironmentLeaseRegistry(): EnvironmentLeaseRegistry {
|
|
73
|
+
const environmentLeases = new Map<string, EnvironmentLease>();
|
|
74
|
+
|
|
75
|
+
return Object.freeze({
|
|
76
|
+
acquire(name: string, value: string): () => void {
|
|
77
|
+
let lease = environmentLeases.get(name);
|
|
78
|
+
|
|
79
|
+
if (lease) {
|
|
80
|
+
if (lease.value !== value) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Build config environment "${name}" already has an active lease for a different value.`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (process.env[name] !== lease.value) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Build config environment lease for "${name}" lost ownership before another lease was acquired.`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
lease = {
|
|
92
|
+
originalValue: process.env[name],
|
|
93
|
+
owners: new Set(),
|
|
94
|
+
value,
|
|
95
|
+
};
|
|
96
|
+
process.env[name] = value;
|
|
97
|
+
environmentLeases.set(name, lease);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const owner = Symbol(name);
|
|
101
|
+
lease.owners.add(owner);
|
|
102
|
+
let released = false;
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
if (released) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (environmentLeases.get(name) !== lease || !lease.owners.has(owner)) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Build config environment lease for "${name}" cannot be restored because its ownership record was lost.`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
released = true;
|
|
116
|
+
const lostOwnership = process.env[name] !== lease.value;
|
|
117
|
+
lease.owners.delete(owner);
|
|
118
|
+
|
|
119
|
+
if (lease.owners.size === 0) {
|
|
120
|
+
environmentLeases.delete(name);
|
|
121
|
+
restoreEnvironment(name, lease.originalValue);
|
|
122
|
+
} else if (lostOwnership) {
|
|
123
|
+
process.env[name] = lease.value;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (lostOwnership) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Build config environment lease for "${name}" lost ownership before restoration.`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
brand: ENVIRONMENT_LEASE_REGISTRY_BRAND,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getEnvironmentLeaseRegistry(): EnvironmentLeaseRegistry {
|
|
138
|
+
const descriptor = Object.getOwnPropertyDescriptor(
|
|
139
|
+
process,
|
|
140
|
+
ENVIRONMENT_LEASE_REGISTRY_KEY,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (descriptor) {
|
|
144
|
+
const registry = descriptor.value as
|
|
145
|
+
| Partial<EnvironmentLeaseRegistry>
|
|
146
|
+
| undefined;
|
|
147
|
+
if (
|
|
148
|
+
descriptor.configurable ||
|
|
149
|
+
descriptor.enumerable ||
|
|
150
|
+
descriptor.writable ||
|
|
151
|
+
registry?.brand !== ENVIRONMENT_LEASE_REGISTRY_BRAND ||
|
|
152
|
+
typeof registry.acquire !== 'function'
|
|
153
|
+
) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
'The process-global build config environment lease registry is occupied by an incompatible value.',
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return registry as EnvironmentLeaseRegistry;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const registry = createEnvironmentLeaseRegistry();
|
|
162
|
+
Object.defineProperty(process, ENVIRONMENT_LEASE_REGISTRY_KEY, {
|
|
163
|
+
configurable: false,
|
|
164
|
+
enumerable: false,
|
|
165
|
+
value: registry,
|
|
166
|
+
writable: false,
|
|
167
|
+
});
|
|
168
|
+
return registry;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function acquireEnvironmentLease(name: string, value: string): () => void {
|
|
172
|
+
return getEnvironmentLeaseRegistry().acquire(name, value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function releaseAfterFailure(
|
|
176
|
+
release: () => void,
|
|
177
|
+
originalError: unknown,
|
|
178
|
+
): never {
|
|
179
|
+
try {
|
|
180
|
+
release();
|
|
181
|
+
} catch (restorationError) {
|
|
182
|
+
throw new AggregateError(
|
|
183
|
+
[originalError, restorationError],
|
|
184
|
+
'Build config environment setup and restoration both failed.',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
throw originalError;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
class BuildConfigEnvironmentLeasePlugin {
|
|
192
|
+
constructor(private readonly release: () => void) {}
|
|
193
|
+
|
|
194
|
+
apply(compiler: Rspack.Compiler): void {
|
|
195
|
+
const hooks = compiler.hooks as unknown as Record<
|
|
196
|
+
string,
|
|
197
|
+
LifecycleHook | undefined
|
|
198
|
+
>;
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const lifecycleHooks = {} as Record<LifecycleHookName, LifecycleHook>;
|
|
202
|
+
for (const hookName of LIFECYCLE_HOOK_NAMES) {
|
|
203
|
+
const hook = hooks[hookName];
|
|
204
|
+
if (!hook || typeof hook.tap !== 'function') {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Rspack does not expose the "${hookName}" lifecycle hook required to restore build config environment leases.`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
lifecycleHooks[hookName] = hook;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let mode: 'pending' | 'run' | 'watch' = 'pending';
|
|
213
|
+
const modeTap = {
|
|
214
|
+
name: BUILD_CONFIG_ENVIRONMENT_PLUGIN,
|
|
215
|
+
stage: Number.MIN_SAFE_INTEGER,
|
|
216
|
+
};
|
|
217
|
+
const releaseTap = {
|
|
218
|
+
name: BUILD_CONFIG_ENVIRONMENT_PLUGIN,
|
|
219
|
+
stage: Number.MAX_SAFE_INTEGER,
|
|
220
|
+
};
|
|
221
|
+
const releaseAfterOneShot = () => {
|
|
222
|
+
if (mode !== 'watch' && compiler.watchMode !== true) {
|
|
223
|
+
this.release();
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
lifecycleHooks.run.tap(modeTap, () => {
|
|
228
|
+
mode = 'run';
|
|
229
|
+
});
|
|
230
|
+
lifecycleHooks.watchRun.tap(modeTap, () => {
|
|
231
|
+
mode = 'watch';
|
|
232
|
+
});
|
|
233
|
+
lifecycleHooks.afterDone.tap(releaseTap, releaseAfterOneShot);
|
|
234
|
+
lifecycleHooks.failed.tap(releaseTap, releaseAfterOneShot);
|
|
235
|
+
lifecycleHooks.shutdown.tap(releaseTap, this.release);
|
|
236
|
+
lifecycleHooks.watchClose.tap(releaseTap, this.release);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
releaseAfterFailure(this.release, error);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function resolveEffectTsgoCli(from: string | URL): string {
|
|
244
|
+
const projectRequire = createRequire(from);
|
|
245
|
+
const packageJsonPath = projectRequire.resolve(
|
|
246
|
+
`${EFFECT_TSGO_PACKAGE}/package.json`,
|
|
247
|
+
);
|
|
248
|
+
const packageJson = JSON.parse(
|
|
249
|
+
readFileSync(packageJsonPath, 'utf-8'),
|
|
250
|
+
) as PackageJson;
|
|
251
|
+
const bin =
|
|
252
|
+
typeof packageJson.bin === 'string'
|
|
253
|
+
? packageJson.bin
|
|
254
|
+
: packageJson.bin?.[EFFECT_TSGO_BIN];
|
|
255
|
+
|
|
256
|
+
if (!bin) {
|
|
257
|
+
throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return resolve(dirname(packageJsonPath), bin);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function resolveExecutableEffectTsgoCompiler(compilerPath: string): string {
|
|
264
|
+
if (process.platform === 'win32') {
|
|
265
|
+
return compilerPath;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const sourcePath = realpathSync(compilerPath);
|
|
269
|
+
const source = lstatSync(sourcePath);
|
|
270
|
+
if (!source.isFile()) {
|
|
271
|
+
throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
accessSync(sourcePath, constants.X_OK);
|
|
276
|
+
return compilerPath;
|
|
277
|
+
} catch {
|
|
278
|
+
// npm currently publishes Effect TS-Go's native Unix files without an
|
|
279
|
+
// execute bit. Keep node_modules immutable and materialize a private copy.
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const cachedCompiler = executableEffectTsgoCompilers.get(sourcePath);
|
|
283
|
+
if (cachedCompiler) {
|
|
284
|
+
try {
|
|
285
|
+
accessSync(cachedCompiler, constants.X_OK);
|
|
286
|
+
return cachedCompiler;
|
|
287
|
+
} catch {
|
|
288
|
+
executableEffectTsgoCompilers.delete(sourcePath);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const compilerBytes = readFileSync(sourcePath);
|
|
293
|
+
const compilerDigest = createHash('sha256')
|
|
294
|
+
.update(compilerBytes)
|
|
295
|
+
.digest('hex');
|
|
296
|
+
const compilerDirectory = join(
|
|
297
|
+
tmpdir(),
|
|
298
|
+
'modern-js',
|
|
299
|
+
'effect-tsgo',
|
|
300
|
+
compilerDigest,
|
|
301
|
+
);
|
|
302
|
+
mkdirSync(compilerDirectory, { recursive: true, mode: 0o700 });
|
|
303
|
+
const executableCompiler = join(compilerDirectory, basename(sourcePath));
|
|
304
|
+
try {
|
|
305
|
+
accessSync(executableCompiler, constants.X_OK);
|
|
306
|
+
} catch {
|
|
307
|
+
const temporaryCompiler = join(
|
|
308
|
+
compilerDirectory,
|
|
309
|
+
`.${basename(sourcePath)}.${process.pid}.tmp`,
|
|
310
|
+
);
|
|
311
|
+
rmSync(temporaryCompiler, { force: true });
|
|
312
|
+
try {
|
|
313
|
+
writeFileSync(temporaryCompiler, compilerBytes, {
|
|
314
|
+
flag: 'wx',
|
|
315
|
+
mode: 0o700,
|
|
316
|
+
});
|
|
317
|
+
chmodSync(temporaryCompiler, 0o700);
|
|
318
|
+
renameSync(temporaryCompiler, executableCompiler);
|
|
319
|
+
} finally {
|
|
320
|
+
rmSync(temporaryCompiler, { force: true });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
accessSync(executableCompiler, constants.X_OK);
|
|
324
|
+
executableEffectTsgoCompilers.set(sourcePath, executableCompiler);
|
|
325
|
+
return executableCompiler;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Reads an environment variable while keeping generated build configs free of
|
|
330
|
+
* direct process access.
|
|
331
|
+
*/
|
|
332
|
+
export function getBuildConfigEnvironment(name: string): string | undefined {
|
|
333
|
+
return process.env[name];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Runs a Rspack config setup under a leased environment value. The lease is
|
|
338
|
+
* restored when that compiler reaches any supported terminal hook.
|
|
339
|
+
*/
|
|
340
|
+
export function withBuildConfigEnvironment<
|
|
341
|
+
Config extends { plugins?: Rspack.Plugin[] },
|
|
342
|
+
SetupArguments extends unknown[],
|
|
343
|
+
>(
|
|
344
|
+
name: string,
|
|
345
|
+
value: string,
|
|
346
|
+
setup: (
|
|
347
|
+
config: Config,
|
|
348
|
+
...args: SetupArguments
|
|
349
|
+
) => Config | void | Promise<Config | void>,
|
|
350
|
+
): (config: Config, ...args: SetupArguments) => Promise<Config> {
|
|
351
|
+
return async (config, ...args) => {
|
|
352
|
+
const release = acquireEnvironmentLease(name, value);
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
const configured = (await setup(config, ...args)) ?? config;
|
|
356
|
+
configured.plugins = [
|
|
357
|
+
...(configured.plugins ?? []),
|
|
358
|
+
new BuildConfigEnvironmentLeasePlugin(release),
|
|
359
|
+
];
|
|
360
|
+
return configured;
|
|
361
|
+
} catch (error) {
|
|
362
|
+
releaseAfterFailure(release, error);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export type ResolveEffectTsgoCompilerOptions = {
|
|
368
|
+
/** Module URL or absolute filename used to resolve the consumer's compiler. */
|
|
369
|
+
from: string | URL;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Resolves the Effect TS-Go executable used by Module Federation DTS builds.
|
|
374
|
+
*/
|
|
375
|
+
export function resolveEffectTsgoCompiler(
|
|
376
|
+
options: ResolveEffectTsgoCompilerOptions,
|
|
377
|
+
): string {
|
|
378
|
+
const configuredCompiler =
|
|
379
|
+
getBuildConfigEnvironment('EFFECT_TSGO_BIN')?.trim();
|
|
380
|
+
|
|
381
|
+
if (configuredCompiler) {
|
|
382
|
+
try {
|
|
383
|
+
return resolveExecutableEffectTsgoCompiler(configuredCompiler);
|
|
384
|
+
} catch {
|
|
385
|
+
throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
const compiler = execFileSync(
|
|
391
|
+
process.execPath,
|
|
392
|
+
[resolveEffectTsgoCli(options.from), 'get-exe-path'],
|
|
393
|
+
{ encoding: 'utf-8' },
|
|
394
|
+
).trim();
|
|
395
|
+
|
|
396
|
+
if (compiler) {
|
|
397
|
+
return resolveExecutableEffectTsgoCompiler(compiler);
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
// Use one stable error for package, platform-binary, and CLI failures.
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
|
|
404
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { constants as zlibConstants } from 'node:zlib';
|
|
2
|
+
import type { RsbuildPlugin } from '@rsbuild/core';
|
|
3
|
+
import CompressionPlugin from 'compression-webpack-plugin';
|
|
4
|
+
import type { PrecompressConfig } from './types';
|
|
5
|
+
|
|
6
|
+
export type { PrecompressCodecOptions, PrecompressConfig } from './types';
|
|
7
|
+
|
|
8
|
+
type CompressionPluginOptions = NonNullable<
|
|
9
|
+
ConstructorParameters<typeof CompressionPlugin>[0]
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
const DEFAULT_PRECOMPRESS_TEST = /\.(js|mjs|cjs|css|html|svg|json|map)$/i;
|
|
13
|
+
const DEFAULT_PRECOMPRESS_THRESHOLD = 10 * 1024;
|
|
14
|
+
const DEFAULT_PRECOMPRESS_MIN_RATIO = 0.8;
|
|
15
|
+
|
|
16
|
+
const createDefaultGzipOptions = (): CompressionPluginOptions => ({
|
|
17
|
+
algorithm: 'gzip',
|
|
18
|
+
filename: '[path][base].gz',
|
|
19
|
+
test: DEFAULT_PRECOMPRESS_TEST,
|
|
20
|
+
threshold: DEFAULT_PRECOMPRESS_THRESHOLD,
|
|
21
|
+
minRatio: DEFAULT_PRECOMPRESS_MIN_RATIO,
|
|
22
|
+
compressionOptions: {
|
|
23
|
+
level: 6,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const createDefaultBrotliOptions = (): CompressionPluginOptions => ({
|
|
28
|
+
algorithm: 'brotliCompress',
|
|
29
|
+
filename: '[path][base].br',
|
|
30
|
+
test: DEFAULT_PRECOMPRESS_TEST,
|
|
31
|
+
threshold: DEFAULT_PRECOMPRESS_THRESHOLD,
|
|
32
|
+
minRatio: DEFAULT_PRECOMPRESS_MIN_RATIO,
|
|
33
|
+
compressionOptions: {
|
|
34
|
+
params: {
|
|
35
|
+
[zlibConstants.BROTLI_PARAM_QUALITY]: 9,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const mergeCompressionOptions = (
|
|
41
|
+
base: CompressionPluginOptions,
|
|
42
|
+
next: Partial<CompressionPluginOptions>,
|
|
43
|
+
): CompressionPluginOptions => {
|
|
44
|
+
const merged = {
|
|
45
|
+
...base,
|
|
46
|
+
...next,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (base.compressionOptions || next.compressionOptions) {
|
|
50
|
+
const baseCompressionOptions = base.compressionOptions as
|
|
51
|
+
| Record<string, unknown>
|
|
52
|
+
| undefined;
|
|
53
|
+
const nextCompressionOptions = next.compressionOptions as
|
|
54
|
+
| Record<string, unknown>
|
|
55
|
+
| undefined;
|
|
56
|
+
const mergedCompressionOptions = {
|
|
57
|
+
...baseCompressionOptions,
|
|
58
|
+
...nextCompressionOptions,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (baseCompressionOptions?.params || nextCompressionOptions?.params) {
|
|
62
|
+
mergedCompressionOptions.params = {
|
|
63
|
+
...(baseCompressionOptions?.params as Record<string, unknown>),
|
|
64
|
+
...(nextCompressionOptions?.params as Record<string, unknown>),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
merged.compressionOptions = mergedCompressionOptions;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return merged;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const resolveCodecOptions = (
|
|
75
|
+
codecConfig: PrecompressConfig['gzip'] | PrecompressConfig['brotli'],
|
|
76
|
+
createDefaultOptions: () => CompressionPluginOptions,
|
|
77
|
+
): CompressionPluginOptions | null => {
|
|
78
|
+
if (codecConfig === false) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (codecConfig === undefined || codecConfig === true) {
|
|
83
|
+
return createDefaultOptions();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return mergeCompressionOptions(createDefaultOptions(), codecConfig);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const resolvePrecompressConfig = (
|
|
90
|
+
precompress: boolean | PrecompressConfig | undefined,
|
|
91
|
+
): PrecompressConfig | false => {
|
|
92
|
+
if (precompress === false || precompress === undefined) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (precompress === true) {
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return precompress;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const builderPluginAdapterPrecompress = (
|
|
104
|
+
precompress: boolean | PrecompressConfig | undefined,
|
|
105
|
+
): RsbuildPlugin => ({
|
|
106
|
+
name: 'builder-plugin-adapter-modern-precompress',
|
|
107
|
+
|
|
108
|
+
setup(api) {
|
|
109
|
+
api.modifyBundlerChain((chain, { isProd, target }) => {
|
|
110
|
+
if (!isProd || target !== 'web') {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const precompressConfig = resolvePrecompressConfig(precompress);
|
|
115
|
+
if (precompressConfig === false) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const gzipOptions = resolveCodecOptions(
|
|
120
|
+
precompressConfig.gzip,
|
|
121
|
+
createDefaultGzipOptions,
|
|
122
|
+
);
|
|
123
|
+
if (gzipOptions) {
|
|
124
|
+
chain
|
|
125
|
+
.plugin('modern-precompress-gzip')
|
|
126
|
+
.use(CompressionPlugin, [gzipOptions]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const brotliOptions = resolveCodecOptions(
|
|
130
|
+
precompressConfig.brotli,
|
|
131
|
+
createDefaultBrotliOptions,
|
|
132
|
+
);
|
|
133
|
+
if (brotliOptions) {
|
|
134
|
+
chain
|
|
135
|
+
.plugin('modern-precompress-brotli')
|
|
136
|
+
.use(CompressionPlugin, [brotliOptions]);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type CompressionPlugin from 'compression-webpack-plugin';
|
|
2
|
+
|
|
3
|
+
type CompressionPluginOptions = NonNullable<
|
|
4
|
+
ConstructorParameters<typeof CompressionPlugin>[0]
|
|
5
|
+
>;
|
|
6
|
+
|
|
7
|
+
export type PrecompressCodecOptions =
|
|
8
|
+
| boolean
|
|
9
|
+
| Partial<CompressionPluginOptions>;
|
|
10
|
+
|
|
11
|
+
export interface PrecompressConfig {
|
|
12
|
+
/**
|
|
13
|
+
* Configure gzip precompression options.
|
|
14
|
+
* `true` means using default options.
|
|
15
|
+
* `false` means disabling gzip precompression.
|
|
16
|
+
* @default true
|
|
17
|
+
*/
|
|
18
|
+
gzip?: PrecompressCodecOptions;
|
|
19
|
+
/**
|
|
20
|
+
* Configure brotli precompression options.
|
|
21
|
+
* `true` means using default options.
|
|
22
|
+
* `false` means disabling brotli precompression.
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
brotli?: PrecompressCodecOptions;
|
|
26
|
+
}
|
|
@@ -34,7 +34,7 @@ export const BFF_EFFECT_WORKER_ENTRY = `${WORKER_BUNDLE_DIRECTORY}/__modern_bff_
|
|
|
34
34
|
export const BFF_EFFECT_WORKER_DISPATCHER_EXPORT =
|
|
35
35
|
'__modern_create_effect_bff_dispatcher';
|
|
36
36
|
export const EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE =
|
|
37
|
-
'Ensure the Effect API entry exists at api/index.ts or bff.effect.entry, and import Cloudflare edge handlers from @modern-js/
|
|
37
|
+
'Ensure the Effect API entry exists at api/index.ts or bff.effect.entry, and import Cloudflare edge handlers from @modern-js/bff-effect/effect-edge instead of lambda/Hono server helpers.';
|
|
38
38
|
export const DEFAULT_COMPATIBILITY_DATE = '2026-06-02';
|
|
39
39
|
export const COMPATIBILITY_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
40
40
|
export const REQUIRED_COMPATIBILITY_FLAGS =
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { fs as fse } from '@modern-js/utils';
|
|
3
2
|
import {
|
|
4
3
|
type DeliveryUnitIdentity,
|
|
5
4
|
isUltramodernBuildArtifact,
|
|
@@ -7,7 +6,8 @@ import {
|
|
|
7
6
|
toDeliveryUnitIdentity,
|
|
8
7
|
ULTRAMODERN_BUILD_ARTIFACT_PATH,
|
|
9
8
|
ULTRAMODERN_BUILD_MODULE_PATH,
|
|
10
|
-
} from '@modern-js/
|
|
9
|
+
} from '@modern-js/backend-federation-contracts';
|
|
10
|
+
import { fs as fse } from '@modern-js/utils';
|
|
11
11
|
import { resolveUltramodernReleaseIdentity } from '../release-identity';
|
|
12
12
|
import { isRecord } from './utils';
|
|
13
13
|
|
|
@@ -3,6 +3,7 @@ import { createRequire } from 'node:module';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { SERVICE_WORKER_ENVIRONMENT_NAME } from '@modern-js/builder';
|
|
5
5
|
import type { BffUserConfig } from '@modern-js/server-core';
|
|
6
|
+
import type {} from '@modern-js/server-runtime-extensions/server-config';
|
|
6
7
|
import { isProd } from '@modern-js/utils';
|
|
7
8
|
import type {
|
|
8
9
|
EnvironmentConfig,
|
|
@@ -10,19 +10,56 @@ export const CLOUDFLARE_RUNTIME_TYPE = 'cloudflare-module-worker';
|
|
|
10
10
|
export const CLOUDFLARE_OUTPUT_PACKAGE_TYPE = 'module';
|
|
11
11
|
export const CLOUDFLARE_WORKER_PACKAGE_TYPE = 'commonjs';
|
|
12
12
|
export const CLOUDFLARE_WORKER_BUNDLE_FORMAT = 'commonjs';
|
|
13
|
+
// Import availability for the default 2026-06-02 nodejs_compat target.
|
|
14
|
+
// Some modules are runtime-provided stubs whose unsupported operations throw.
|
|
13
15
|
export const CLOUDFLARE_WORKER_NODE_BUILTINS = [
|
|
16
|
+
'assert',
|
|
17
|
+
'assert/strict',
|
|
14
18
|
'async_hooks',
|
|
15
19
|
'buffer',
|
|
20
|
+
'child_process',
|
|
16
21
|
'crypto',
|
|
22
|
+
'dgram',
|
|
23
|
+
'dns',
|
|
24
|
+
'domain',
|
|
17
25
|
'events',
|
|
26
|
+
'fs',
|
|
18
27
|
'fs/promises',
|
|
28
|
+
'http',
|
|
29
|
+
'http2',
|
|
30
|
+
'inspector',
|
|
19
31
|
'module',
|
|
32
|
+
'net',
|
|
33
|
+
'os',
|
|
20
34
|
'path',
|
|
21
35
|
'process',
|
|
36
|
+
'readline',
|
|
37
|
+
'readline/promises',
|
|
38
|
+
'repl',
|
|
39
|
+
'sqlite',
|
|
22
40
|
'stream',
|
|
41
|
+
'stream/consumers',
|
|
42
|
+
'stream/promises',
|
|
43
|
+
'stream/web',
|
|
23
44
|
'string_decoder',
|
|
45
|
+
'timers',
|
|
46
|
+
'timers/promises',
|
|
47
|
+
'tls',
|
|
48
|
+
'trace_events',
|
|
49
|
+
'tty',
|
|
24
50
|
'url',
|
|
25
51
|
'util',
|
|
52
|
+
'util/types',
|
|
53
|
+
'v8',
|
|
54
|
+
'vm',
|
|
55
|
+
'wasi',
|
|
56
|
+
'worker_threads',
|
|
57
|
+
'zlib',
|
|
58
|
+
] as const;
|
|
59
|
+
// Platform imports available with the default 2026-06-02 nodejs_compat target.
|
|
60
|
+
// Import availability does not imply support for every Node API operation.
|
|
61
|
+
export const CLOUDFLARE_WORKER_PLATFORM_MODULES = [
|
|
62
|
+
'cloudflare:sockets',
|
|
26
63
|
] as const;
|
|
27
64
|
export const CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS = [
|
|
28
65
|
'nodejs_compat',
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
DELIVERY_UNIT_IDENTITY_FIELDS,
|
|
3
3
|
type DeliveryUnitIdentity,
|
|
4
4
|
deliveryUnitIdentityFieldValue,
|
|
5
|
-
} from '@modern-js/
|
|
5
|
+
} from '@modern-js/backend-federation-contracts';
|
|
6
6
|
import type { CloudflareOutputVerifierIssue, JsonObject } from './issues';
|
|
7
7
|
import { addIssue, assertEqual } from './issues';
|
|
8
8
|
|