@module-federation/treeshake-server 2.6.0 → 2.8.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 (34) hide show
  1. package/dist/876.mjs +940 -0
  2. package/dist/app.d.ts +1 -1
  3. package/dist/frontend/adapter/index.js +12 -8
  4. package/dist/frontend/index.html +1 -1
  5. package/dist/frontend/static/css/index.513418ff5d.css +1 -0
  6. package/dist/frontend/static/js/637.b47e0403af.js +2 -0
  7. package/dist/frontend/static/js/async/427.dbb8b74259.js +2 -0
  8. package/dist/frontend/static/js/async/503.7bd056737e.js +12 -0
  9. package/dist/frontend/static/js/async/515.1b7f26d7ec.js +2 -0
  10. package/dist/frontend/static/js/index.ace3fa3d0e.js +88 -0
  11. package/dist/frontend/static/js/lib-react.7c43a84261.js +2 -0
  12. package/dist/frontend/static/js/{lib-router.949393cc.js → lib-router.5077a93d5a.js} +3 -3
  13. package/dist/http/routes/build.d.ts +1 -1
  14. package/dist/http/routes/index.d.ts +1 -1
  15. package/dist/http/routes/maintenance.d.ts +1 -1
  16. package/dist/http/routes/static.d.ts +1 -1
  17. package/dist/index.js +29 -27
  18. package/dist/index.mjs +1 -942
  19. package/dist/nodeServer.d.ts +1 -1
  20. package/dist/server.js +23 -21
  21. package/dist/server.mjs +11 -949
  22. package/package.json +5 -5
  23. package/dist/frontend/static/css/index.16175e0f.css +0 -1
  24. package/dist/frontend/static/js/465.8ad65aff.js +0 -2
  25. package/dist/frontend/static/js/async/538.91f59a63.js +0 -12
  26. package/dist/frontend/static/js/async/873.03a2ef6b.js +0 -2
  27. package/dist/frontend/static/js/async/987.89efa6cc.js +0 -2
  28. package/dist/frontend/static/js/index.3cfd4422.js +0 -88
  29. package/dist/frontend/static/js/lib-react.c59642e3.js +0 -2
  30. /package/dist/frontend/static/js/{465.8ad65aff.js.LICENSE.txt → 637.b47e0403af.js.LICENSE.txt} +0 -0
  31. /package/dist/frontend/static/js/async/{538.91f59a63.js.LICENSE.txt → 427.dbb8b74259.js.LICENSE.txt} +0 -0
  32. /package/dist/frontend/static/js/async/{987.89efa6cc.js.LICENSE.txt → 503.7bd056737e.js.LICENSE.txt} +0 -0
  33. /package/dist/frontend/static/js/{lib-react.c59642e3.js.LICENSE.txt → lib-react.7c43a84261.js.LICENSE.txt} +0 -0
  34. /package/dist/frontend/static/js/{lib-router.949393cc.js.LICENSE.txt → lib-router.5077a93d5a.js.LICENSE.txt} +0 -0
package/dist/server.mjs CHANGED
@@ -1,965 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
  import node_fs from "node:fs";
3
3
  import node_path from "node:path";
4
- import pino from "pino";
5
- import { Hono } from "hono";
6
- import { cors } from "hono/cors";
7
- import node_os from "node:os";
8
- import { zValidator } from "@hono/zod-validator";
9
- import { nanoid } from "nanoid";
10
- import p_limit from "p-limit";
11
- import { z } from "zod";
12
- import { createHash } from "node:crypto";
13
- import promises from "node:fs/promises";
14
- import { spawn } from "node:child_process";
15
- import json_stable_stringify from "json-stable-stringify";
16
- import { timeout } from "hono/timeout";
17
- import { serve } from "@hono/node-server";
18
- const SERVER_VERSION = 'v0-0205';
19
- const UPLOADED_DIR = '_shared-tree-shaking';
20
- const createLogger = (opts)=>pino({
21
- level: (null == opts ? void 0 : opts.level) ?? 'info',
22
- formatters: {
23
- level (label) {
24
- return {
25
- level: label
26
- };
27
- }
28
- }
29
- });
30
- let logger_logger = createLogger();
31
- const setLogger = (next)=>{
32
- logger_logger = next;
33
- };
34
- async function createAdapterDeps(params) {
35
- const adapterId = params.adapterId;
36
- const adapter = params.registry.getAdapterById(adapterId);
37
- return adapter.create(params.adapterConfig ?? {}, {
38
- logger: params.logger ?? logger_logger,
39
- uploadedDir: params.uploadedDir ?? UPLOADED_DIR
40
- });
41
- }
42
- function _define_property(obj, key, value) {
43
- if (key in obj) Object.defineProperty(obj, key, {
44
- value: value,
45
- enumerable: true,
46
- configurable: true,
47
- writable: true
48
- });
49
- else obj[key] = value;
50
- return obj;
51
- }
52
- class LocalObjectStore {
53
- async exists(key) {
54
- const filePath = node_path.join(this.rootDir, key.replace(/^\//, ''));
55
- try {
56
- const stat = await node_fs.promises.stat(filePath);
57
- return stat.isFile();
58
- } catch {
59
- return false;
60
- }
61
- }
62
- async uploadFile(localPath, key) {
63
- const rel = key.replace(/^\//, '');
64
- const dest = node_path.join(this.rootDir, rel);
65
- await node_fs.promises.mkdir(node_path.dirname(dest), {
66
- recursive: true
67
- });
68
- await node_fs.promises.copyFile(localPath, dest);
69
- }
70
- publicUrl(key) {
71
- return `${this.publicBaseUrl}${key.replace(/^\//, '')}`;
72
- }
73
- constructor(opts){
74
- _define_property(this, "rootDir", void 0);
75
- _define_property(this, "publicBaseUrl", void 0);
76
- this.rootDir = (null == opts ? void 0 : opts.rootDir) ?? node_path.join(process.cwd(), 'log', 'static');
77
- const port = process.env.PORT || 3000;
78
- const base = (null == opts ? void 0 : opts.publicBaseUrl) === '/' ? `http://localhost:${port}/` : (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
79
- this.publicBaseUrl = base.endsWith('/') ? base : `${base}/`;
80
- }
81
- }
82
- function adapter_define_property(obj, key, value) {
83
- if (key in obj) Object.defineProperty(obj, key, {
84
- value: value,
85
- enumerable: true,
86
- configurable: true,
87
- writable: true
88
- });
89
- else obj[key] = value;
90
- return obj;
91
- }
92
- function envStr(env, name, fallback) {
93
- const v = env[name];
94
- if (void 0 === v || '' === v) return fallback;
95
- return v;
96
- }
97
- class LocalAdapter {
98
- fromEnv(env) {
99
- return {
100
- rootDir: envStr(env, 'LOCAL_STORE_DIR', node_path.join(process.cwd(), 'log', 'static')),
101
- publicBaseUrl: envStr(env, 'LOCAL_STORE_BASE_URL', '/')
102
- };
103
- }
104
- async create(config, _context) {
105
- const objectStore = new LocalObjectStore({
106
- rootDir: config.rootDir,
107
- publicBaseUrl: config.publicBaseUrl
108
- });
109
- return {
110
- objectStore
111
- };
112
- }
113
- constructor(){
114
- adapter_define_property(this, "id", 'local');
115
- }
116
- }
117
- function createAdapterRegistry(adapters) {
118
- return {
119
- adapters,
120
- getAdapterById: (id)=>{
121
- const found = adapters.find((a)=>a.id === id);
122
- if (!found) throw new Error(`Unknown ADAPTER_ID: ${id}`);
123
- return found;
124
- }
125
- };
126
- }
127
- function createOssAdapterRegistry() {
128
- return createAdapterRegistry([
129
- new LocalAdapter()
130
- ]);
131
- }
132
- function contentTypeByExt(filePath) {
133
- if (filePath.endsWith('.js')) return "application/javascript";
134
- if (filePath.endsWith('.json')) return 'application/json';
135
- return 'application/octet-stream';
136
- }
137
- const DEFAULT_STATIC_ROOT = node_path.join(process.cwd(), 'log', 'static');
138
- async function serveLocalFile(c, rootDir) {
139
- let requestPath = c.req.path;
140
- try {
141
- requestPath = decodeURIComponent(requestPath);
142
- } catch {
143
- return c.text('Not Found', 404);
144
- }
145
- const relPath = requestPath.replace(/^\/+/, '');
146
- const rootResolved = node_path.resolve(rootDir);
147
- const filePath = node_path.resolve(rootResolved, relPath);
148
- if (filePath !== rootResolved && !filePath.startsWith(`${rootResolved}${node_path.sep}`)) return c.text('Not Found', 404);
149
- try {
150
- const buf = await node_fs.promises.readFile(filePath);
151
- return new Response(new Uint8Array(buf), {
152
- status: 200,
153
- headers: {
154
- 'Content-Type': contentTypeByExt(filePath)
155
- }
156
- });
157
- } catch {
158
- return c.text('Not Found', 404);
159
- }
160
- }
161
- function createStaticRoute(opts) {
162
- const staticRoute = new Hono();
163
- const rootDir = (null == opts ? void 0 : opts.rootDir) ?? DEFAULT_STATIC_ROOT;
164
- staticRoute.get('/tree-shaking-shared/*', async (c)=>serveLocalFile(c, rootDir));
165
- return staticRoute;
166
- }
4
+ import { createServer, createOssAdapterRegistry, createLogger, DEFAULT_STATIC_ROOT, createApp, createAdapterDeps } from "./876.mjs";
167
5
  const DEFAULT_PRUNE_INTERVAL_MS = 3600000;
168
- function ossEnv_envStr(env, name, fallback) {
6
+ function envStr(env, name, fallback) {
169
7
  const v = env[name];
170
8
  if (void 0 === v || '' === v) return fallback;
171
9
  return v;
172
10
  }
173
11
  function envNum(env, name, fallback) {
174
- const v = ossEnv_envStr(env, name);
12
+ const v = envStr(env, name);
175
13
  if (!v) return fallback;
176
14
  const n = Number(v);
177
15
  return Number.isFinite(n) ? n : fallback;
178
16
  }
179
17
  function resolveOssEnv(params) {
180
- const adapterId = ossEnv_envStr(params.env, 'ADAPTER_ID', params.defaultAdapterId) ?? 'local';
18
+ const adapterId = envStr(params.env, 'ADAPTER_ID', params.defaultAdapterId) ?? 'local';
181
19
  const adapter = params.registry.getAdapterById(adapterId);
182
20
  const adapterConfig = adapter.fromEnv ? adapter.fromEnv(params.env) : {};
183
21
  return {
184
22
  adapterId,
185
23
  adapterConfig,
186
- corsOrigin: ossEnv_envStr(params.env, 'CORS_ORIGIN', '*') ?? '*',
187
- staticRootDir: ossEnv_envStr(params.env, 'LOCAL_STORE_DIR', DEFAULT_STATIC_ROOT) ?? DEFAULT_STATIC_ROOT,
188
- logLevel: ossEnv_envStr(params.env, 'LOG_LEVEL', 'info') ?? 'info',
24
+ corsOrigin: envStr(params.env, 'CORS_ORIGIN', '*') ?? '*',
25
+ staticRootDir: envStr(params.env, 'LOCAL_STORE_DIR', DEFAULT_STATIC_ROOT) ?? DEFAULT_STATIC_ROOT,
26
+ logLevel: envStr(params.env, 'LOG_LEVEL', 'info') ?? 'info',
189
27
  port: envNum(params.env, 'PORT', 3000),
190
- hostname: ossEnv_envStr(params.env, 'HOST', '0.0.0.0') ?? '0.0.0.0',
28
+ hostname: envStr(params.env, 'HOST', '0.0.0.0') ?? '0.0.0.0',
191
29
  pruneIntervalMs: envNum(params.env, 'PNPM_PRUNE_INTERVAL_MS', DEFAULT_PRUNE_INTERVAL_MS),
192
30
  runtimeEnv: params.env
193
31
  };
194
32
  }
195
- function createDiMiddleware(deps) {
196
- return async (c, next)=>{
197
- c.set('objectStore', deps.objectStore);
198
- if (deps.projectPublisher) c.set('projectPublisher', deps.projectPublisher);
199
- await next();
200
- };
201
- }
202
- const loggerMiddleware = async (c, next)=>{
203
- const category = c.req.path.split('/')[1] || 'root';
204
- const child = logger_logger.child({
205
- category,
206
- path: c.req.path,
207
- method: c.req.method
208
- });
209
- c.set('logger', child);
210
- await next();
211
- };
212
- const parseNormalizedKey = (key)=>{
213
- const res = key.split('@');
214
- return {
215
- name: res.slice(0, -1).join('@'),
216
- version: res[res.length - 1]
217
- };
218
- };
219
- const normalizedKey = (name, v)=>`${name}@${v}`;
220
- function normalizeConfig(config) {
221
- const { shared, plugins, target, libraryType, hostName, uploadOptions } = config;
222
- const commonNormalizedConfig = {
223
- plugins: (null == plugins ? void 0 : plugins.sort(([a], [b])=>a.localeCompare(b))) ?? [],
224
- target: Array.isArray(target) ? [
225
- ...target
226
- ].sort() : [
227
- target
228
- ],
229
- uploadOptions,
230
- libraryType,
231
- hostName
232
- };
233
- const normalizedConfig = {};
234
- shared.forEach(([sharedName, version, usedExports], index)=>{
235
- const key = normalizedKey(sharedName, version);
236
- normalizedConfig[key] = {
237
- ...commonNormalizedConfig,
238
- shared: shared.slice(0, index).concat(shared.slice(index + 1)).sort(([s, v, u], [b, v2, u2])=>`${s}${v}${u.sort().join('')}`.localeCompare(`${b}${v2}${u2.sort().join('')}`)),
239
- usedExports
240
- };
241
- });
242
- return normalizedConfig;
243
- }
244
- function extractBuildConfig(config, type) {
245
- const { shared, plugins, target, libraryType, usedExports, hostName } = config;
246
- return {
247
- shared,
248
- plugins,
249
- target,
250
- libraryType,
251
- usedExports,
252
- type,
253
- hostName
254
- };
255
- }
256
- const UploadOptionsSchema = z.object({
257
- scmName: z.string(),
258
- bucketName: z.string(),
259
- publicFilePath: z.string(),
260
- publicPath: z.string(),
261
- cdnRegion: z.string()
262
- });
263
- const BasicSchema = z.object({
264
- shared: z.array(z.tuple([
265
- z.string(),
266
- z.string(),
267
- z.array(z.string())
268
- ])).describe('List of plugins: [name, version, usedExports]'),
269
- plugins: z.array(z.tuple([
270
- z.string(),
271
- z.string().optional()
272
- ])).describe('Specify extra build plugin names, support specify version in second item').optional(),
273
- target: z.union([
274
- z.string(),
275
- z.array(z.string())
276
- ]).describe('Used to configure the target environment of Rspack output and the ECMAScript version of Rspack runtime code, the same with rspack#target'),
277
- libraryType: z.string(),
278
- hostName: z.string().describe('The name of the host app / mf')
279
- });
280
- const ConfigSchema = BasicSchema.extend({
281
- uploadOptions: UploadOptionsSchema.optional()
282
- });
283
- const CheckTreeShakingSchema = ConfigSchema.extend({
284
- uploadOptions: UploadOptionsSchema.optional()
285
- });
286
- const STATS_NAME = 'mf-stats.json';
287
- let runtimeEnv = {};
288
- const setRuntimeEnv = (env)=>{
289
- runtimeEnv = env;
290
- };
291
- const getRuntimeEnv = ()=>runtimeEnv;
292
- function runCommand_define_property(obj, key, value) {
293
- if (key in obj) Object.defineProperty(obj, key, {
294
- value: value,
295
- enumerable: true,
296
- configurable: true,
297
- writable: true
298
- });
299
- else obj[key] = value;
300
- return obj;
301
- }
302
- class CommandExecutionError extends Error {
303
- constructor(command, exitCode, stdout, stderr){
304
- super(`Command "${command}" exited with code ${exitCode}`), runCommand_define_property(this, "command", void 0), runCommand_define_property(this, "exitCode", void 0), runCommand_define_property(this, "stdout", void 0), runCommand_define_property(this, "stderr", void 0);
305
- this.name = 'CommandExecutionError';
306
- this.command = command;
307
- this.exitCode = exitCode;
308
- this.stdout = stdout;
309
- this.stderr = stderr;
310
- }
311
- }
312
- const runCommand = (command, options = {})=>new Promise((resolve, reject)=>{
313
- const stdoutChunks = [];
314
- const stderrChunks = [];
315
- const child = spawn(command, {
316
- cwd: options.cwd,
317
- env: {
318
- ...getRuntimeEnv(),
319
- ...options.env
320
- },
321
- shell: true,
322
- stdio: [
323
- 'ignore',
324
- 'pipe',
325
- 'pipe'
326
- ]
327
- });
328
- child.stdout.on('data', (chunk)=>{
329
- stdoutChunks.push(chunk.toString());
330
- });
331
- child.stderr.on('data', (chunk)=>{
332
- stderrChunks.push(chunk.toString());
333
- });
334
- child.on('error', (error)=>{
335
- reject(error);
336
- });
337
- child.on('close', (exitCode)=>{
338
- const stdout = stdoutChunks.join('');
339
- const stderr = stderrChunks.join('');
340
- if (0 === exitCode) return void resolve({
341
- stdout,
342
- stderr,
343
- exitCode
344
- });
345
- reject(new CommandExecutionError(command, exitCode ?? -1, stdout, stderr));
346
- });
347
- });
348
- let installs = 0;
349
- let pruneScheduled = false;
350
- let pruning = false;
351
- const DEFAULT_INTERVAL = 3600000;
352
- const markInstallStart = ()=>{
353
- installs++;
354
- };
355
- const markInstallEnd = ()=>{
356
- installs = Math.max(0, installs - 1);
357
- schedulePruneSoon();
358
- };
359
- const schedulePruneSoon = (delay = 30000)=>{
360
- if (pruneScheduled) return;
361
- pruneScheduled = true;
362
- setTimeout(()=>{
363
- pruneScheduled = false;
364
- maybePrune();
365
- }, delay);
366
- };
367
- const maybePrune = async ()=>{
368
- if (pruning || installs > 0) return void schedulePruneSoon(60000);
369
- pruning = true;
370
- try {
371
- await runCommand('pnpm store prune');
372
- } catch {} finally{
373
- pruning = false;
374
- }
375
- };
376
- const startPeriodicPrune = (intervalMs = DEFAULT_INTERVAL)=>{
377
- if (intervalMs <= 0) return;
378
- setInterval(()=>{
379
- maybePrune();
380
- }, intervalMs);
381
- };
382
- const createUniqueTempDirByKey = async (key)=>{
383
- const base = node_path.join(node_os.tmpdir(), `re-shake-share-${key}`);
384
- let candidate = base;
385
- for(;;)try {
386
- await promises.mkdir(candidate, {
387
- recursive: false
388
- });
389
- return candidate;
390
- } catch {
391
- const rand = Math.floor(1e9 * Math.random());
392
- candidate = `${base}-${rand}`;
393
- }
394
- };
395
- const prepareProject = async (config, excludeShared)=>{
396
- const key = createHash('sha256').update(JSON.stringify(config)).digest('hex');
397
- const dir = await createUniqueTempDirByKey(key);
398
- const templateDir = node_path.join(__dirname, '.', 'template', 're-shake-share');
399
- await promises.cp(templateDir, dir, {
400
- recursive: true
401
- });
402
- const pkgPath = node_path.join(dir, 'package.json');
403
- const indexPath = node_path.join(dir, 'index.ts');
404
- const rspackConfigPath = node_path.join(dir, 'rspack.config.ts');
405
- const [pkgContent, indexContent, rspackConfigContent] = await Promise.all([
406
- promises.readFile(pkgPath, 'utf-8'),
407
- promises.readFile(indexPath, 'utf-8'),
408
- promises.readFile(rspackConfigPath, 'utf-8')
409
- ]);
410
- const pkg = JSON.parse(pkgContent);
411
- const deps = {
412
- ...pkg.dependencies || {}
413
- };
414
- const shared = {};
415
- const mfConfig = {
416
- name: 're_shake',
417
- library: {
418
- type: 'global'
419
- },
420
- manifest: true,
421
- shared
422
- };
423
- let pluginImportStr = '';
424
- let pluginOptionStr = '[\n';
425
- let sharedImport = '';
426
- Object.entries(config).forEach(([key, { plugins = [], libraryType, usedExports, hostName }], index)=>{
427
- const { name, version } = parseNormalizedKey(key);
428
- deps[name] = version;
429
- if (!index) {
430
- plugins.forEach(([pluginName, pluginVersion], pIndex)=>{
431
- deps[pluginName] = pluginVersion ?? 'latest';
432
- const pluginImportName = `plugin_${pIndex}`;
433
- pluginImportStr += `import ${pluginImportName} from '${pluginName}';\n`;
434
- pluginOptionStr += `new ${pluginImportName}(),`;
435
- });
436
- mfConfig.library.type = libraryType;
437
- mfConfig.name = hostName;
438
- }
439
- shared[name] = {
440
- requiredVersion: version,
441
- version,
442
- treeShaking: excludeShared.some(([n, v])=>n === name && v === version) ? void 0 : {
443
- usedExports,
444
- mode: 'server-calc'
445
- }
446
- };
447
- sharedImport += `import shared_${index} from '${name}';\n`;
448
- });
449
- pluginOptionStr += '\n]';
450
- pkg.dependencies = deps;
451
- const newPkgContent = JSON.stringify(pkg, null, 2);
452
- const sharedImportPlaceholder = "${SHARED_IMPORT}";
453
- const newIndexContent = indexContent.replace(sharedImportPlaceholder, sharedImport);
454
- const pluginsPlaceholder = "${ PLUGINS }";
455
- const mfConfigPlaceholder = "${ MF_CONFIG }";
456
- let cfg = rspackConfigContent;
457
- cfg += pluginImportStr;
458
- cfg = cfg.replace(pluginsPlaceholder, pluginOptionStr);
459
- cfg = cfg.replace(mfConfigPlaceholder, JSON.stringify(mfConfig, null, 2));
460
- await Promise.all([
461
- promises.writeFile(pkgPath, newPkgContent),
462
- promises.writeFile(indexPath, newIndexContent),
463
- promises.writeFile(rspackConfigPath, cfg)
464
- ]);
465
- return dir;
466
- };
467
- const installDependencies = async (cwd)=>{
468
- markInstallStart();
469
- try {
470
- await runCommand("pnpm i --ignore-scripts --prefer-offline --reporter=silent ", {
471
- cwd,
472
- env: {
473
- npm_config_registry: process.env.MF_NPM_REGISTRY || 'https://registry.npmjs.org/'
474
- }
475
- });
476
- } finally{
477
- markInstallEnd();
478
- }
479
- };
480
- const buildProject = async (cwd, type)=>{
481
- const scripts = [];
482
- if ('re-shake' === type) scripts.push('pnpm build');
483
- else scripts.push('pnpm build:full');
484
- await Promise.all(scripts.map((script)=>runCommand(script, {
485
- cwd
486
- })));
487
- };
488
- const retrieveSharedFilepaths = async (projectDir, type)=>{
489
- const sharedFilepaths = [];
490
- const collectSharedFilepaths = async (t)=>{
491
- const dir = 'full' === t ? 'full-shared' : 'dist';
492
- const distDir = node_path.join(projectDir, dir);
493
- const statsContent = await promises.readFile(node_path.join(distDir, STATS_NAME), 'utf-8');
494
- const stats = JSON.parse(statsContent);
495
- stats.shared.forEach((s)=>{
496
- const { name, version, fallback, fallbackName } = s;
497
- if (fallback && fallbackName) {
498
- var _s_usedExports;
499
- const filepath = node_path.join(distDir, fallback);
500
- sharedFilepaths.push({
501
- name,
502
- version,
503
- filepath,
504
- globalName: fallbackName,
505
- type: t,
506
- modules: s.usedExports,
507
- canTreeShaking: s.canTreeShaking ?? (null == (_s_usedExports = s.usedExports) ? void 0 : _s_usedExports.length) !== 0
508
- });
509
- }
510
- });
511
- };
512
- await collectSharedFilepaths(type);
513
- return sharedFilepaths;
514
- };
515
- const runBuild = async (normalizedConfig, excludeShared, type)=>{
516
- const tStart = Date.now();
517
- const tmpDir = await prepareProject(normalizedConfig, excludeShared);
518
- const tPrepare = Date.now();
519
- logger_logger.info(`prepareProject took ${tPrepare - tStart}ms`);
520
- await installDependencies(tmpDir);
521
- const tInstall = Date.now();
522
- logger_logger.info(`installDependencies took ${tInstall - tPrepare}ms`);
523
- await buildProject(tmpDir, type);
524
- const tBuild = Date.now();
525
- logger_logger.info(`buildProject took ${tBuild - tInstall}ms`);
526
- const sharedFilePaths = await retrieveSharedFilepaths(tmpDir, type);
527
- const tRetrieve = Date.now();
528
- logger_logger.info(`retrieveSharedFilepaths took ${tRetrieve - tBuild}ms`);
529
- return {
530
- sharedFilePaths,
531
- dir: tmpDir
532
- };
533
- };
534
- async function cleanUp(tmpDir) {
535
- if (!tmpDir) return;
536
- await promises.rm(tmpDir, {
537
- recursive: true,
538
- force: true
539
- });
540
- }
541
- const encodeName = function(name, prefix = '', withExt = false) {
542
- const ext = withExt ? '.js' : '';
543
- return `${prefix}${name.replace(/@/g, 'scope_').replace(/-/g, '_').replace(/\//g, '__').replace(/\./g, '')}${ext}`;
544
- };
545
- function retrieveGlobalName(mfName, sharedName, version) {
546
- return encodeName(`${mfName}_${sharedName}_${version}`);
547
- }
548
- function createCacheHash(config, type) {
549
- const relevant = extractBuildConfig({
550
- ...config,
551
- usedExports: 'full' === type ? [] : config.usedExports
552
- }, type);
553
- const json = json_stable_stringify(relevant);
554
- if (!json) throw new Error('Can not stringify build config!');
555
- return createHash('sha256').update(json).digest('hex');
556
- }
557
- function retrieveCDNPath({ config, sharedKey, type }) {
558
- const configHash = createCacheHash(config, type);
559
- return `tree-shaking-shared/${SERVER_VERSION}/${sharedKey}/${configHash}.js`;
560
- }
561
- async function cacheService_hitCache(sharedKey, config, type, store) {
562
- const cdnPath = retrieveCDNPath({
563
- config,
564
- sharedKey,
565
- type
566
- });
567
- const exists = await store.exists(cdnPath);
568
- return exists ? store.publicUrl(cdnPath) : null;
569
- }
570
- const retrieveCacheItems = async (normalizedConfig, type, store)=>{
571
- const cacheItems = [];
572
- const restConfig = {};
573
- const excludeShared = [];
574
- for (const [sharedKey, config] of Object.entries(normalizedConfig)){
575
- let cache = false;
576
- const { name, version } = parseNormalizedKey(sharedKey);
577
- const cdnUrl = await cacheService_hitCache(sharedKey, config, type, store);
578
- if (cdnUrl) {
579
- cache = true;
580
- cacheItems.push({
581
- type,
582
- name,
583
- version,
584
- cdnUrl,
585
- globalName: retrieveGlobalName(config.hostName, name, version)
586
- });
587
- }
588
- if (cache) excludeShared.push([
589
- name,
590
- version
591
- ]);
592
- else if (config.usedExports.length || 're-shake' !== type) restConfig[sharedKey] = config;
593
- else excludeShared.push([
594
- name,
595
- version
596
- ]);
597
- }
598
- return {
599
- cacheItems,
600
- excludeShared,
601
- restConfig
602
- };
603
- };
604
- async function uploadToCacheStore(sharedFilePaths, normalizedConfig, store) {
605
- const results = [];
606
- for (const file of sharedFilePaths){
607
- const { name, version, filepath, globalName, type, modules, canTreeShaking } = file;
608
- try {
609
- const sharedKey = normalizedKey(name, version);
610
- logger_logger.info(`Uploading ${sharedKey} to CDN`);
611
- const config = normalizedConfig[sharedKey];
612
- const cdnPath = retrieveCDNPath({
613
- config,
614
- sharedKey,
615
- type
616
- });
617
- const t0 = Date.now();
618
- await store.uploadFile(filepath, cdnPath);
619
- const tUpload = Date.now() - t0;
620
- const cdnUrl = store.publicUrl(cdnPath);
621
- const res = {
622
- name: name,
623
- version: version,
624
- globalName: globalName,
625
- cdnUrl,
626
- type,
627
- modules,
628
- canTreeShaking
629
- };
630
- try {
631
- const jsonFilePath = filepath.replace(/\.js$/, '.json');
632
- const jsonFile = JSON.stringify(res);
633
- const jsonCdnUrl = cdnPath.replace(/\.js$/, '.json');
634
- await promises.writeFile(jsonFilePath, jsonFile);
635
- await store.uploadFile(jsonFilePath, jsonCdnUrl);
636
- } catch (error) {
637
- logger_logger.error(`Failed to upload ${name}@${version} json file: ${error}`);
638
- }
639
- results.push(res);
640
- logger_logger.info(`Successfully uploaded ${name}@${version} to ${cdnUrl} in ${tUpload}ms`);
641
- } catch (error) {
642
- logger_logger.error(`Failed to upload ${name}@${version}: ${error}`);
643
- throw error;
644
- }
645
- }
646
- return results;
647
- }
648
- const downloadToFile = async (url, destPath)=>{
649
- const res = await fetch(url);
650
- if (!res.ok) throw new Error(`Download failed: ${res.status} ${res.statusText} - ${url}`);
651
- const buf = Buffer.from(await res.arrayBuffer());
652
- await promises.mkdir(node_path.dirname(destPath), {
653
- recursive: true
654
- });
655
- await promises.writeFile(destPath, buf);
656
- return destPath;
657
- };
658
- async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store) {
659
- const tmpDir = await createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
660
- const uploaded = [];
661
- try {
662
- for (const item of uploadResults)try {
663
- const sharedKey = normalizedKey(item.name, item.version);
664
- const config = normalizedConfig[sharedKey];
665
- if (!config) {
666
- logger_logger.error(`No config found for ${item.name}`);
667
- continue;
668
- }
669
- const { uploadOptions } = config;
670
- if (!uploadOptions) throw new Error(`No uploadOptions found for ${item.name}`);
671
- const filename = node_path.basename(new URL(item.cdnUrl, 'http://dummy.com').pathname);
672
- const localPath = node_path.join(tmpDir, filename);
673
- const hash = createCacheHash({
674
- ...config,
675
- plugins: config.plugins || []
676
- }, item.type);
677
- const cdnUrl = publisher.publicUrl({
678
- sharedName: item.name,
679
- hash,
680
- fileName: filename,
681
- options: uploadOptions
682
- });
683
- const hitCache = await publisher.exists(cdnUrl);
684
- if (hitCache) {
685
- logger_logger.info(`Hit cache for ${item.name}@${item.version} -> ${cdnUrl}`);
686
- uploaded.push({
687
- ...item,
688
- cdnUrl
689
- });
690
- continue;
691
- }
692
- logger_logger.info(`Downloading ${item.name}@${item.version} -> ${localPath}`);
693
- const t0 = Date.now();
694
- await downloadToFile(item.cdnUrl, localPath);
695
- const tDownload = Date.now() - t0;
696
- logger_logger.info(`Downloaded ${item.name}@${item.version} in ${tDownload}ms`);
697
- const newCdnUrl = await publisher.publishFile({
698
- localPath,
699
- sharedName: item.name,
700
- hash,
701
- options: uploadOptions
702
- });
703
- uploaded.push({
704
- ...item,
705
- cdnUrl: newCdnUrl
706
- });
707
- } catch (error) {
708
- logger_logger.error(`Failed to upload ${item.name}@${item.version}: ${error}`);
709
- }
710
- for (const s of sharedFilePaths)try {
711
- const config = normalizedConfig[normalizedKey(s.name, s.version)];
712
- if (!config) {
713
- logger_logger.error(`No config found for ${s.name}`);
714
- continue;
715
- }
716
- const { uploadOptions } = config;
717
- if (!uploadOptions) throw new Error(`No uploadOptions found for ${s.name}`);
718
- const hash = createCacheHash({
719
- ...config,
720
- plugins: config.plugins || []
721
- }, s.type);
722
- const cdnUrl = await publisher.publishFile({
723
- localPath: s.filepath,
724
- sharedName: s.name,
725
- hash,
726
- options: uploadOptions
727
- });
728
- uploaded.push({
729
- name: s.name,
730
- version: s.version,
731
- globalName: s.globalName,
732
- cdnUrl,
733
- type: s.type,
734
- modules: s.modules,
735
- canTreeShaking: s.canTreeShaking
736
- });
737
- } catch (error) {
738
- logger_logger.error(`Failed to upload ${s.name}@${s.version}: ${error}`);
739
- }
740
- return uploaded;
741
- } finally{
742
- promises.rm(tmpDir, {
743
- recursive: true,
744
- force: true
745
- }).catch((err)=>{
746
- logger_logger.error(`Failed to cleanup dir ${tmpDir}: ${err}`);
747
- });
748
- }
749
- }
750
- async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOptions, store, publisher) {
751
- const cacheUploaded = await uploadToCacheStore(sharedFilePaths, normalizedConfig, store);
752
- if (!uploadOptions) {
753
- const hydrated = await Promise.all(uploadResults.map(async (item)=>{
754
- if ('full' !== item.type) return item;
755
- const tmpDir = await createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
756
- const jsonPath = node_path.join(tmpDir, `${item.name}-${item.version}.json`);
757
- try {
758
- const tJson0 = Date.now();
759
- await downloadToFile(item.cdnUrl.replace('.js', '.json'), jsonPath);
760
- const tJson = Date.now() - tJson0;
761
- logger_logger.info(`Downloaded ${item.name}@${item.version} json in ${tJson}ms`);
762
- const jsonContent = JSON.parse(await promises.readFile(jsonPath, 'utf8'));
763
- return {
764
- ...item,
765
- canTreeShaking: jsonContent.canTreeShaking,
766
- modules: jsonContent.modules
767
- };
768
- } catch (jsonError) {
769
- logger_logger.error(`Failed to download ${item.name}@${item.version} json: ${jsonError}`);
770
- return {
771
- ...item,
772
- canTreeShaking: item.canTreeShaking ?? true
773
- };
774
- } finally{
775
- await promises.rm(tmpDir, {
776
- recursive: true,
777
- force: true
778
- });
779
- }
780
- }));
781
- return [
782
- ...cacheUploaded,
783
- ...hydrated
784
- ];
785
- }
786
- if (!publisher) throw new Error('uploadOptions provided but no projectPublisher configured (configure the selected adapter to enable it or omit uploadOptions)');
787
- const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store);
788
- return projectUploadResults;
789
- }
790
- const buildLimit = p_limit(Math.max(1, node_os.cpus().length));
791
- const buildRoute = new Hono();
792
- buildRoute.post('/', zValidator('json', ConfigSchema), async (c)=>{
793
- const logger = c.get('logger');
794
- const startTime = Date.now();
795
- const jobId = nanoid();
796
- logger.info(jobId);
797
- const body = c.req.valid('json');
798
- logger.info(JSON.stringify(body));
799
- const normalizedConfig = normalizeConfig(body);
800
- const store = c.get('objectStore');
801
- const publisher = c.get('projectPublisher');
802
- try {
803
- const t0 = Date.now();
804
- const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 're-shake', store);
805
- const tRetrieveCache = Date.now();
806
- logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
807
- let sharedFilePaths = [];
808
- let dir;
809
- if (Object.keys(restConfig).length > 0) {
810
- const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 're-shake'));
811
- sharedFilePaths = buildResult.sharedFilePaths;
812
- dir = buildResult.dir;
813
- }
814
- const tBuild = Date.now();
815
- logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
816
- const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
817
- const tUpload = Date.now();
818
- logger.info(`upload took ${tUpload - tBuild}ms`);
819
- cleanUp(dir).catch((err)=>{
820
- logger.error(`Failed to cleanup dir ${dir}: ${err}`);
821
- });
822
- const tCleanUp = Date.now();
823
- logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
824
- return c.json({
825
- jobId,
826
- status: 'success',
827
- data: uploadResults,
828
- cached: cacheItems,
829
- duration: Date.now() - startTime
830
- });
831
- } catch (error) {
832
- if (error instanceof CommandExecutionError) {
833
- if (137 === error.exitCode) maybePrune();
834
- return c.json({
835
- jobId,
836
- status: 'failed',
837
- error: error.message,
838
- command: error.command,
839
- exitCode: error.exitCode,
840
- stdout: error.stdout,
841
- stderr: error.stderr
842
- });
843
- }
844
- return c.json({
845
- jobId,
846
- status: 'failed',
847
- error: error instanceof Error ? error.message : 'Unknown error'
848
- });
849
- }
850
- });
851
- async function handleCheckTreeshake(c, body) {
852
- const logger = c.get('logger');
853
- const startTime = Date.now();
854
- const jobId = nanoid();
855
- logger.info(jobId);
856
- logger.info(JSON.stringify(body));
857
- const normalizedConfig = normalizeConfig(body);
858
- const store = c.get('objectStore');
859
- const publisher = c.get('projectPublisher');
860
- try {
861
- const t0 = Date.now();
862
- const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 'full', store);
863
- const tRetrieveCache = Date.now();
864
- logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
865
- let sharedFilePaths = [];
866
- let dir;
867
- if (Object.keys(restConfig).length > 0) {
868
- const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 'full'));
869
- sharedFilePaths = buildResult.sharedFilePaths;
870
- dir = buildResult.dir;
871
- }
872
- const tBuild = Date.now();
873
- logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
874
- const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
875
- const tUpload = Date.now();
876
- logger.info(`upload took ${tUpload - tBuild}ms`);
877
- cleanUp(dir).catch((err)=>{
878
- logger.error(`Failed to cleanup dir ${dir}: ${err}`);
879
- });
880
- const tCleanUp = Date.now();
881
- logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
882
- return c.json({
883
- jobId,
884
- status: 'success',
885
- data: uploadResults,
886
- cached: cacheItems,
887
- duration: Date.now() - startTime
888
- });
889
- } catch (error) {
890
- if (error instanceof CommandExecutionError) {
891
- if (137 === error.exitCode) maybePrune();
892
- return c.json({
893
- jobId,
894
- status: 'failed',
895
- error: error.message,
896
- command: error.command,
897
- exitCode: error.exitCode,
898
- stdout: error.stdout,
899
- stderr: error.stderr
900
- });
901
- }
902
- return c.json({
903
- jobId,
904
- status: 'failed',
905
- error: error instanceof Error ? error.message : 'Unknown error'
906
- });
907
- }
908
- }
909
- buildRoute.post('/check-tree-shaking', zValidator('json', CheckTreeShakingSchema), async (c)=>handleCheckTreeshake(c, c.req.valid('json')));
910
- const maintenanceRoute = new Hono();
911
- maintenanceRoute.post('/', async (c)=>{
912
- const logger = c.get('logger');
913
- const jobId = nanoid();
914
- const startTime = Date.now();
915
- logger.info(jobId);
916
- await maybePrune();
917
- return c.json({
918
- jobId,
919
- status: 'success',
920
- duration: Date.now() - startTime
921
- });
922
- });
923
- const routes = new Hono();
924
- routes.route('/build', buildRoute);
925
- routes.route('/clean-cache', maintenanceRoute);
926
- function createApp(deps, opts) {
927
- var _opts_appExtensions, _opts_frontendAdapters;
928
- if (null == opts ? void 0 : opts.logger) setLogger(opts.logger);
929
- setRuntimeEnv((null == opts ? void 0 : opts.runtimeEnv) ?? process.env);
930
- const app = new Hono();
931
- const corsOrigin = (null == opts ? void 0 : opts.corsOrigin) ?? '*';
932
- const staticRootDir = (null == opts ? void 0 : opts.staticRootDir) ?? DEFAULT_STATIC_ROOT;
933
- app.use('*', cors({
934
- origin: corsOrigin,
935
- allowMethods: [
936
- 'GET',
937
- 'POST',
938
- 'OPTIONS'
939
- ],
940
- allowHeaders: [
941
- 'Content-Type',
942
- 'Authorization'
943
- ]
944
- }));
945
- app.use('*', loggerMiddleware);
946
- app.use('*', createDiMiddleware(deps));
947
- app.use('*', timeout(60000));
948
- if (null == opts ? void 0 : null == (_opts_appExtensions = opts.appExtensions) ? void 0 : _opts_appExtensions.length) for (const extend of opts.appExtensions)extend(app);
949
- app.get('/tree-shaking-shared/healthz', (c)=>c.json({
950
- status: 'ok',
951
- timestamp: new Date().toISOString()
952
- }));
953
- app.route('/tree-shaking-shared', routes);
954
- app.route('/', createStaticRoute({
955
- rootDir: staticRootDir
956
- }));
957
- if (null == opts ? void 0 : null == (_opts_frontendAdapters = opts.frontendAdapters) ? void 0 : _opts_frontendAdapters.length) for (const adapter of opts.frontendAdapters)adapter.register(app);
958
- startPeriodicPrune(null == opts ? void 0 : opts.pruneIntervalMs);
959
- return app;
960
- }
961
33
  const defaultBasePath = '/tree-shaking';
962
- const embeddedAdapter_contentTypeByExt = (filePath)=>{
34
+ const contentTypeByExt = (filePath)=>{
963
35
  if (filePath.endsWith('.html')) return 'text/html; charset=utf-8';
964
36
  if (filePath.endsWith('.js')) return "application/javascript";
965
37
  if (filePath.endsWith('.css')) return 'text/css';
@@ -1002,7 +74,7 @@ function createEmbeddedFrontendAdapter(opts) {
1002
74
  return new Response(new Uint8Array(buf), {
1003
75
  status: 200,
1004
76
  headers: {
1005
- 'Content-Type': embeddedAdapter_contentTypeByExt(filePath)
77
+ 'Content-Type': contentTypeByExt(filePath)
1006
78
  }
1007
79
  });
1008
80
  }
@@ -1015,7 +87,7 @@ function createEmbeddedFrontendAdapter(opts) {
1015
87
  return new Response(new Uint8Array(buf), {
1016
88
  status: 200,
1017
89
  headers: {
1018
- 'Content-Type': embeddedAdapter_contentTypeByExt(fallbackPath)
90
+ 'Content-Type': contentTypeByExt(fallbackPath)
1019
91
  }
1020
92
  });
1021
93
  } catch {
@@ -1031,16 +103,6 @@ function createEmbeddedFrontendAdapter(opts) {
1031
103
  }
1032
104
  };
1033
105
  }
1034
- function createServer(opts) {
1035
- const port = opts.port ?? 3000;
1036
- const hostname = opts.hostname ?? '0.0.0.0';
1037
- return serve({
1038
- fetch: opts.app.fetch,
1039
- port,
1040
- hostname,
1041
- overrideGlobalObjects: false
1042
- });
1043
- }
1044
106
  const hasIndexHtml = (dir)=>Boolean(dir && node_fs.existsSync(node_path.join(dir, 'index.html')));
1045
107
  const resolveFrontendDistDir = ()=>{
1046
108
  const envDir = process.env.TREESHAKE_FRONTEND_DIST;