@module-federation/treeshake-server 0.0.1 → 0.23.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/index.mjs CHANGED
@@ -1,15 +1,17 @@
1
1
  import { Hono } from "hono";
2
2
  import { cors } from "hono/cors";
3
3
  import pino from "pino";
4
+ import node_os from "node:os";
4
5
  import { zValidator } from "@hono/zod-validator";
5
6
  import { nanoid } from "nanoid";
7
+ import p_limit from "p-limit";
6
8
  import { z } from "zod";
7
9
  import { createHash } from "node:crypto";
8
- import node_fs from "node:fs";
9
- import node_os from "node:os";
10
+ import promises from "node:fs/promises";
10
11
  import node_path from "node:path";
11
12
  import { spawn } from "node:child_process";
12
13
  import json_stable_stringify from "json-stable-stringify";
14
+ import node_fs from "node:fs";
13
15
  import { serve } from "@hono/node-server";
14
16
  function createDiMiddleware(deps) {
15
17
  return async (c, next)=>{
@@ -75,14 +77,15 @@ function normalizeConfig(config) {
75
77
  return normalizedConfig;
76
78
  }
77
79
  function extractBuildConfig(config, type) {
78
- const { shared, plugins, target, libraryType, usedExports } = config;
80
+ const { shared, plugins, target, libraryType, usedExports, hostName } = config;
79
81
  return {
80
82
  shared,
81
83
  plugins,
82
84
  target,
83
85
  libraryType,
84
86
  usedExports,
85
- type
87
+ type,
88
+ hostName
86
89
  };
87
90
  }
88
91
  const UploadOptionsSchema = z.object({
@@ -211,11 +214,11 @@ const startPeriodicPrune = (intervalMs = DEFAULT_INTERVAL)=>{
211
214
  maybePrune();
212
215
  }, intervalMs);
213
216
  };
214
- const createUniqueTempDirByKey = (key)=>{
217
+ const createUniqueTempDirByKey = async (key)=>{
215
218
  const base = node_path.join(node_os.tmpdir(), `re-shake-share-${key}`);
216
219
  let candidate = base;
217
220
  for(;;)try {
218
- node_fs.mkdirSync(candidate, {
221
+ await promises.mkdir(candidate, {
219
222
  recursive: false
220
223
  });
221
224
  return candidate;
@@ -224,15 +227,22 @@ const createUniqueTempDirByKey = (key)=>{
224
227
  candidate = `${base}-${rand}`;
225
228
  }
226
229
  };
227
- const prepareProject = (config, excludeShared)=>{
230
+ const prepareProject = async (config, excludeShared)=>{
228
231
  const key = createHash('sha256').update(JSON.stringify(config)).digest('hex');
229
- const dir = createUniqueTempDirByKey(key);
230
- const templateDir = node_path.join(__dirname, '..', 'template', 're-shake-share');
231
- node_fs.cpSync(templateDir, dir, {
232
+ const dir = await createUniqueTempDirByKey(key);
233
+ const templateDir = node_path.join(__dirname, '.', 'template', 're-shake-share');
234
+ await promises.cp(templateDir, dir, {
232
235
  recursive: true
233
236
  });
234
237
  const pkgPath = node_path.join(dir, 'package.json');
235
- const pkg = JSON.parse(node_fs.readFileSync(pkgPath, 'utf-8'));
238
+ const indexPath = node_path.join(dir, 'index.ts');
239
+ const rspackConfigPath = node_path.join(dir, 'rspack.config.ts');
240
+ const [pkgContent, indexContent, rspackConfigContent] = await Promise.all([
241
+ promises.readFile(pkgPath, 'utf-8'),
242
+ promises.readFile(indexPath, 'utf-8'),
243
+ promises.readFile(rspackConfigPath, 'utf-8')
244
+ ]);
245
+ const pkg = JSON.parse(pkgContent);
236
246
  const deps = {
237
247
  ...pkg.dependencies || {}
238
248
  };
@@ -273,25 +283,26 @@ const prepareProject = (config, excludeShared)=>{
273
283
  });
274
284
  pluginOptionStr += '\n]';
275
285
  pkg.dependencies = deps;
276
- node_fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
286
+ const newPkgContent = JSON.stringify(pkg, null, 2);
277
287
  const sharedImportPlaceholder = "${SHARED_IMPORT}";
288
+ const newIndexContent = indexContent.replace(sharedImportPlaceholder, sharedImport);
278
289
  const pluginsPlaceholder = "${ PLUGINS }";
279
290
  const mfConfigPlaceholder = "${ MF_CONFIG }";
280
- const indexPath = node_path.join(dir, 'index.ts');
281
- const indexContent = node_fs.readFileSync(indexPath, 'utf-8');
282
- node_fs.writeFileSync(indexPath, indexContent.replace(sharedImportPlaceholder, sharedImport));
283
- const rspackConfigPath = node_path.join(dir, 'rspack.config.ts');
284
- let cfg = node_fs.readFileSync(rspackConfigPath, 'utf-8');
291
+ let cfg = rspackConfigContent;
285
292
  cfg += pluginImportStr;
286
293
  cfg = cfg.replace(pluginsPlaceholder, pluginOptionStr);
287
294
  cfg = cfg.replace(mfConfigPlaceholder, JSON.stringify(mfConfig, null, 2));
288
- node_fs.writeFileSync(rspackConfigPath, cfg);
295
+ await Promise.all([
296
+ promises.writeFile(pkgPath, newPkgContent),
297
+ promises.writeFile(indexPath, newIndexContent),
298
+ promises.writeFile(rspackConfigPath, cfg)
299
+ ]);
289
300
  return dir;
290
301
  };
291
302
  const installDependencies = async (cwd)=>{
292
303
  markInstallStart();
293
304
  try {
294
- await runCommand('pnpm i', {
305
+ await runCommand("pnpm i --ignore-scripts --prefer-offline --reporter=silent ", {
295
306
  cwd,
296
307
  env: {
297
308
  npm_config_registry: 'https://registry.npmjs.org/'
@@ -309,12 +320,13 @@ const buildProject = async (cwd, type)=>{
309
320
  cwd
310
321
  })));
311
322
  };
312
- const retrieveSharedFilepaths = (projectDir, type)=>{
323
+ const retrieveSharedFilepaths = async (projectDir, type)=>{
313
324
  const sharedFilepaths = [];
314
- const collectSharedFilepaths = (t)=>{
325
+ const collectSharedFilepaths = async (t)=>{
315
326
  const dir = 'full' === t ? 'full-shared' : 'dist';
316
327
  const distDir = node_path.join(projectDir, dir);
317
- const stats = JSON.parse(node_fs.readFileSync(node_path.join(distDir, STATS_NAME), 'utf-8'));
328
+ const statsContent = await promises.readFile(node_path.join(distDir, STATS_NAME), 'utf-8');
329
+ const stats = JSON.parse(statsContent);
318
330
  stats.shared.forEach((s)=>{
319
331
  const { name, version, fallback, fallbackName } = s;
320
332
  if (fallback && fallbackName) {
@@ -332,22 +344,31 @@ const retrieveSharedFilepaths = (projectDir, type)=>{
332
344
  }
333
345
  });
334
346
  };
335
- collectSharedFilepaths(type);
347
+ await collectSharedFilepaths(type);
336
348
  return sharedFilepaths;
337
349
  };
338
350
  const runBuild = async (normalizedConfig, excludeShared, type)=>{
339
- const tmpDir = prepareProject(normalizedConfig, excludeShared);
351
+ const tStart = Date.now();
352
+ const tmpDir = await prepareProject(normalizedConfig, excludeShared);
353
+ const tPrepare = Date.now();
354
+ logger_logger.info(`prepareProject took ${tPrepare - tStart}ms`);
340
355
  await installDependencies(tmpDir);
356
+ const tInstall = Date.now();
357
+ logger_logger.info(`installDependencies took ${tInstall - tPrepare}ms`);
341
358
  await buildProject(tmpDir, type);
342
- const sharedFilePaths = retrieveSharedFilepaths(tmpDir, type);
359
+ const tBuild = Date.now();
360
+ logger_logger.info(`buildProject took ${tBuild - tInstall}ms`);
361
+ const sharedFilePaths = await retrieveSharedFilepaths(tmpDir, type);
362
+ const tRetrieve = Date.now();
363
+ logger_logger.info(`retrieveSharedFilepaths took ${tRetrieve - tBuild}ms`);
343
364
  return {
344
365
  sharedFilePaths,
345
366
  dir: tmpDir
346
367
  };
347
368
  };
348
- function cleanUp(tmpDir) {
369
+ async function cleanUp(tmpDir) {
349
370
  if (!tmpDir) return;
350
- node_fs.rmSync(tmpDir, {
371
+ await promises.rm(tmpDir, {
351
372
  recursive: true,
352
373
  force: true
353
374
  });
@@ -447,7 +468,7 @@ async function uploadToCacheStore(sharedFilePaths, normalizedConfig, store) {
447
468
  const jsonFilePath = filepath.replace(/\.js$/, '.json');
448
469
  const jsonFile = JSON.stringify(res);
449
470
  const jsonCdnUrl = cdnPath.replace(/\.js$/, '.json');
450
- node_fs.writeFileSync(jsonFilePath, jsonFile);
471
+ await promises.writeFile(jsonFilePath, jsonFile);
451
472
  await store.uploadFile(jsonFilePath, jsonCdnUrl);
452
473
  } catch (error) {
453
474
  logger_logger.error(`Failed to upload ${name}@${version} json file: ${error}`);
@@ -465,25 +486,26 @@ const downloadToFile = async (url, destPath)=>{
465
486
  const res = await fetch(url);
466
487
  if (!res.ok) throw new Error(`Download failed: ${res.status} ${res.statusText} - ${url}`);
467
488
  const buf = Buffer.from(await res.arrayBuffer());
468
- await node_fs.promises.mkdir(node_path.dirname(destPath), {
489
+ await promises.mkdir(node_path.dirname(destPath), {
469
490
  recursive: true
470
491
  });
471
- await node_fs.promises.writeFile(destPath, buf);
492
+ await promises.writeFile(destPath, buf);
472
493
  return destPath;
473
494
  };
474
- async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher) {
475
- const tmpDir = createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
495
+ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store) {
496
+ const tmpDir = await createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
476
497
  const uploaded = [];
477
498
  try {
478
499
  for (const item of uploadResults)try {
479
- const config = normalizedConfig[normalizedKey(item.name, item.version)];
500
+ const sharedKey = normalizedKey(item.name, item.version);
501
+ const config = normalizedConfig[sharedKey];
480
502
  if (!config) {
481
503
  logger_logger.error(`No config found for ${item.name}`);
482
504
  continue;
483
505
  }
484
506
  const { uploadOptions } = config;
485
507
  if (!uploadOptions) throw new Error(`No uploadOptions found for ${item.name}`);
486
- const filename = node_path.basename(new URL(item.cdnUrl).pathname);
508
+ const filename = node_path.basename(new URL(item.cdnUrl, 'http://dummy.com').pathname);
487
509
  const localPath = node_path.join(tmpDir, filename);
488
510
  const hash = createCacheHash({
489
511
  ...config,
@@ -552,9 +574,11 @@ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, p
552
574
  }
553
575
  return uploaded;
554
576
  } finally{
555
- node_fs.rmSync(tmpDir, {
577
+ promises.rm(tmpDir, {
556
578
  recursive: true,
557
579
  force: true
580
+ }).catch((err)=>{
581
+ logger_logger.error(`Failed to cleanup dir ${tmpDir}: ${err}`);
558
582
  });
559
583
  }
560
584
  }
@@ -563,14 +587,14 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
563
587
  if (!uploadOptions) {
564
588
  const hydrated = await Promise.all(uploadResults.map(async (item)=>{
565
589
  if ('full' !== item.type) return item;
566
- const tmpDir = createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
590
+ const tmpDir = await createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
567
591
  const jsonPath = node_path.join(tmpDir, `${item.name}-${item.version}.json`);
568
592
  try {
569
593
  const tJson0 = Date.now();
570
594
  await downloadToFile(item.cdnUrl.replace('.js', '.json'), jsonPath);
571
595
  const tJson = Date.now() - tJson0;
572
596
  logger_logger.info(`Downloaded ${item.name}@${item.version} json in ${tJson}ms`);
573
- const jsonContent = JSON.parse(node_fs.readFileSync(jsonPath, 'utf8'));
597
+ const jsonContent = JSON.parse(await promises.readFile(jsonPath, 'utf8'));
574
598
  return {
575
599
  ...item,
576
600
  canTreeShaking: jsonContent.canTreeShaking,
@@ -583,7 +607,7 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
583
607
  canTreeShaking: item.canTreeShaking ?? true
584
608
  };
585
609
  } finally{
586
- node_fs.rmSync(tmpDir, {
610
+ await promises.rm(tmpDir, {
587
611
  recursive: true,
588
612
  force: true
589
613
  });
@@ -595,9 +619,10 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
595
619
  ];
596
620
  }
597
621
  if (!publisher) throw new Error('uploadOptions provided but no projectPublisher configured (configure the selected adapter to enable it or omit uploadOptions)');
598
- const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher);
622
+ const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store);
599
623
  return projectUploadResults;
600
624
  }
625
+ const buildLimit = p_limit(Math.max(1, node_os.cpus().length));
601
626
  const buildRoute = new Hono();
602
627
  buildRoute.post('/', zValidator('json', ConfigSchema), async (c)=>{
603
628
  const logger = c.get('logger');
@@ -610,16 +635,27 @@ buildRoute.post('/', zValidator('json', ConfigSchema), async (c)=>{
610
635
  const store = c.get('objectStore');
611
636
  const publisher = c.get('projectPublisher');
612
637
  try {
638
+ const t0 = Date.now();
613
639
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 're-shake', store);
640
+ const tRetrieveCache = Date.now();
641
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
614
642
  let sharedFilePaths = [];
615
643
  let dir;
616
644
  if (Object.keys(restConfig).length > 0) {
617
- const buildResult = await runBuild(normalizedConfig, excludeShared, 're-shake');
645
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 're-shake'));
618
646
  sharedFilePaths = buildResult.sharedFilePaths;
619
647
  dir = buildResult.dir;
620
648
  }
649
+ const tBuild = Date.now();
650
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
621
651
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
622
- cleanUp(dir);
652
+ const tUpload = Date.now();
653
+ logger.info(`upload took ${tUpload - tBuild}ms`);
654
+ cleanUp(dir).catch((err)=>{
655
+ logger.error(`Failed to cleanup dir ${dir}: ${err}`);
656
+ });
657
+ const tCleanUp = Date.now();
658
+ logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
623
659
  return c.json({
624
660
  jobId,
625
661
  status: 'success',
@@ -657,16 +693,27 @@ async function handleCheckTreeshake(c, body) {
657
693
  const store = c.get('objectStore');
658
694
  const publisher = c.get('projectPublisher');
659
695
  try {
696
+ const t0 = Date.now();
660
697
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 'full', store);
698
+ const tRetrieveCache = Date.now();
699
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
661
700
  let sharedFilePaths = [];
662
701
  let dir;
663
702
  if (Object.keys(restConfig).length > 0) {
664
- const buildResult = await runBuild(normalizedConfig, excludeShared, 'full');
703
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 'full'));
665
704
  sharedFilePaths = buildResult.sharedFilePaths;
666
705
  dir = buildResult.dir;
667
706
  }
707
+ const tBuild = Date.now();
708
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
668
709
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
669
- cleanUp(dir);
710
+ const tUpload = Date.now();
711
+ logger.info(`upload took ${tUpload - tBuild}ms`);
712
+ cleanUp(dir).catch((err)=>{
713
+ logger.error(`Failed to cleanup dir ${dir}: ${err}`);
714
+ });
715
+ const tCleanUp = Date.now();
716
+ logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
670
717
  return c.json({
671
718
  jobId,
672
719
  status: 'success',
@@ -832,7 +879,8 @@ class LocalObjectStore {
832
879
  localObjectStore_define_property(this, "rootDir", void 0);
833
880
  localObjectStore_define_property(this, "publicBaseUrl", void 0);
834
881
  this.rootDir = (null == opts ? void 0 : opts.rootDir) ?? node_path.join(process.cwd(), 'log', 'static');
835
- const base = (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
882
+ const port = process.env.PORT || 3000;
883
+ const base = (null == opts ? void 0 : opts.publicBaseUrl) === '/' ? `http://localhost:${port}/` : (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
836
884
  this.publicBaseUrl = base.endsWith('/') ? base : `${base}/`;
837
885
  }
838
886
  }
package/dist/server.js CHANGED
@@ -87,7 +87,8 @@ class LocalObjectStore {
87
87
  _define_property(this, "rootDir", void 0);
88
88
  _define_property(this, "publicBaseUrl", void 0);
89
89
  this.rootDir = (null == opts ? void 0 : opts.rootDir) ?? external_node_path_default().join(process.cwd(), 'log', 'static');
90
- const base = (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
90
+ const port = process.env.PORT || 3000;
91
+ const base = (null == opts ? void 0 : opts.publicBaseUrl) === '/' ? `http://localhost:${port}/` : (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
91
92
  this.publicBaseUrl = base.endsWith('/') ? base : `${base}/`;
92
93
  }
93
94
  }
@@ -223,8 +224,12 @@ const loggerMiddleware = async (c, next)=>{
223
224
  c.set('logger', child);
224
225
  await next();
225
226
  };
227
+ const external_node_os_namespaceObject = require("node:os");
228
+ var external_node_os_default = /*#__PURE__*/ __webpack_require__.n(external_node_os_namespaceObject);
226
229
  const zod_validator_namespaceObject = require("@hono/zod-validator");
227
230
  const external_nanoid_namespaceObject = require("nanoid");
231
+ const external_p_limit_namespaceObject = require("p-limit");
232
+ var external_p_limit_default = /*#__PURE__*/ __webpack_require__.n(external_p_limit_namespaceObject);
228
233
  const parseNormalizedKey = (key)=>{
229
234
  const res = key.split('@');
230
235
  return {
@@ -258,14 +263,15 @@ function normalizeConfig(config) {
258
263
  return normalizedConfig;
259
264
  }
260
265
  function extractBuildConfig(config, type) {
261
- const { shared, plugins, target, libraryType, usedExports } = config;
266
+ const { shared, plugins, target, libraryType, usedExports, hostName } = config;
262
267
  return {
263
268
  shared,
264
269
  plugins,
265
270
  target,
266
271
  libraryType,
267
272
  usedExports,
268
- type
273
+ type,
274
+ hostName
269
275
  };
270
276
  }
271
277
  const external_zod_namespaceObject = require("zod");
@@ -300,8 +306,8 @@ const CheckTreeShakingSchema = ConfigSchema.extend({
300
306
  uploadOptions: UploadOptionsSchema.optional()
301
307
  });
302
308
  const external_node_crypto_namespaceObject = require("node:crypto");
303
- const external_node_os_namespaceObject = require("node:os");
304
- var external_node_os_default = /*#__PURE__*/ __webpack_require__.n(external_node_os_namespaceObject);
309
+ const promises_namespaceObject = require("node:fs/promises");
310
+ var promises_default = /*#__PURE__*/ __webpack_require__.n(promises_namespaceObject);
305
311
  const STATS_NAME = 'mf-stats.json';
306
312
  const external_node_child_process_namespaceObject = require("node:child_process");
307
313
  let runtimeEnv = {};
@@ -399,11 +405,11 @@ const startPeriodicPrune = (intervalMs = DEFAULT_INTERVAL)=>{
399
405
  maybePrune();
400
406
  }, intervalMs);
401
407
  };
402
- const createUniqueTempDirByKey = (key)=>{
408
+ const createUniqueTempDirByKey = async (key)=>{
403
409
  const base = external_node_path_default().join(external_node_os_default().tmpdir(), `re-shake-share-${key}`);
404
410
  let candidate = base;
405
411
  for(;;)try {
406
- external_node_fs_default().mkdirSync(candidate, {
412
+ await promises_default().mkdir(candidate, {
407
413
  recursive: false
408
414
  });
409
415
  return candidate;
@@ -412,15 +418,22 @@ const createUniqueTempDirByKey = (key)=>{
412
418
  candidate = `${base}-${rand}`;
413
419
  }
414
420
  };
415
- const prepareProject = (config, excludeShared)=>{
421
+ const prepareProject = async (config, excludeShared)=>{
416
422
  const key = (0, external_node_crypto_namespaceObject.createHash)('sha256').update(JSON.stringify(config)).digest('hex');
417
- const dir = createUniqueTempDirByKey(key);
418
- const templateDir = external_node_path_default().join(__dirname, '..', 'template', 're-shake-share');
419
- external_node_fs_default().cpSync(templateDir, dir, {
423
+ const dir = await createUniqueTempDirByKey(key);
424
+ const templateDir = external_node_path_default().join(__dirname, '.', 'template', 're-shake-share');
425
+ await promises_default().cp(templateDir, dir, {
420
426
  recursive: true
421
427
  });
422
428
  const pkgPath = external_node_path_default().join(dir, 'package.json');
423
- const pkg = JSON.parse(external_node_fs_default().readFileSync(pkgPath, 'utf-8'));
429
+ const indexPath = external_node_path_default().join(dir, 'index.ts');
430
+ const rspackConfigPath = external_node_path_default().join(dir, 'rspack.config.ts');
431
+ const [pkgContent, indexContent, rspackConfigContent] = await Promise.all([
432
+ promises_default().readFile(pkgPath, 'utf-8'),
433
+ promises_default().readFile(indexPath, 'utf-8'),
434
+ promises_default().readFile(rspackConfigPath, 'utf-8')
435
+ ]);
436
+ const pkg = JSON.parse(pkgContent);
424
437
  const deps = {
425
438
  ...pkg.dependencies || {}
426
439
  };
@@ -461,25 +474,26 @@ const prepareProject = (config, excludeShared)=>{
461
474
  });
462
475
  pluginOptionStr += '\n]';
463
476
  pkg.dependencies = deps;
464
- external_node_fs_default().writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
477
+ const newPkgContent = JSON.stringify(pkg, null, 2);
465
478
  const sharedImportPlaceholder = "${SHARED_IMPORT}";
479
+ const newIndexContent = indexContent.replace(sharedImportPlaceholder, sharedImport);
466
480
  const pluginsPlaceholder = "${ PLUGINS }";
467
481
  const mfConfigPlaceholder = "${ MF_CONFIG }";
468
- const indexPath = external_node_path_default().join(dir, 'index.ts');
469
- const indexContent = external_node_fs_default().readFileSync(indexPath, 'utf-8');
470
- external_node_fs_default().writeFileSync(indexPath, indexContent.replace(sharedImportPlaceholder, sharedImport));
471
- const rspackConfigPath = external_node_path_default().join(dir, 'rspack.config.ts');
472
- let cfg = external_node_fs_default().readFileSync(rspackConfigPath, 'utf-8');
482
+ let cfg = rspackConfigContent;
473
483
  cfg += pluginImportStr;
474
484
  cfg = cfg.replace(pluginsPlaceholder, pluginOptionStr);
475
485
  cfg = cfg.replace(mfConfigPlaceholder, JSON.stringify(mfConfig, null, 2));
476
- external_node_fs_default().writeFileSync(rspackConfigPath, cfg);
486
+ await Promise.all([
487
+ promises_default().writeFile(pkgPath, newPkgContent),
488
+ promises_default().writeFile(indexPath, newIndexContent),
489
+ promises_default().writeFile(rspackConfigPath, cfg)
490
+ ]);
477
491
  return dir;
478
492
  };
479
493
  const installDependencies = async (cwd)=>{
480
494
  markInstallStart();
481
495
  try {
482
- await runCommand('pnpm i', {
496
+ await runCommand("pnpm i --ignore-scripts --prefer-offline --reporter=silent ", {
483
497
  cwd,
484
498
  env: {
485
499
  npm_config_registry: 'https://registry.npmjs.org/'
@@ -497,12 +511,13 @@ const buildProject = async (cwd, type)=>{
497
511
  cwd
498
512
  })));
499
513
  };
500
- const retrieveSharedFilepaths = (projectDir, type)=>{
514
+ const retrieveSharedFilepaths = async (projectDir, type)=>{
501
515
  const sharedFilepaths = [];
502
- const collectSharedFilepaths = (t)=>{
516
+ const collectSharedFilepaths = async (t)=>{
503
517
  const dir = 'full' === t ? 'full-shared' : 'dist';
504
518
  const distDir = external_node_path_default().join(projectDir, dir);
505
- const stats = JSON.parse(external_node_fs_default().readFileSync(external_node_path_default().join(distDir, STATS_NAME), 'utf-8'));
519
+ const statsContent = await promises_default().readFile(external_node_path_default().join(distDir, STATS_NAME), 'utf-8');
520
+ const stats = JSON.parse(statsContent);
506
521
  stats.shared.forEach((s)=>{
507
522
  const { name, version, fallback, fallbackName } = s;
508
523
  if (fallback && fallbackName) {
@@ -520,22 +535,31 @@ const retrieveSharedFilepaths = (projectDir, type)=>{
520
535
  }
521
536
  });
522
537
  };
523
- collectSharedFilepaths(type);
538
+ await collectSharedFilepaths(type);
524
539
  return sharedFilepaths;
525
540
  };
526
541
  const runBuild = async (normalizedConfig, excludeShared, type)=>{
527
- const tmpDir = prepareProject(normalizedConfig, excludeShared);
542
+ const tStart = Date.now();
543
+ const tmpDir = await prepareProject(normalizedConfig, excludeShared);
544
+ const tPrepare = Date.now();
545
+ logger_logger.info(`prepareProject took ${tPrepare - tStart}ms`);
528
546
  await installDependencies(tmpDir);
547
+ const tInstall = Date.now();
548
+ logger_logger.info(`installDependencies took ${tInstall - tPrepare}ms`);
529
549
  await buildProject(tmpDir, type);
530
- const sharedFilePaths = retrieveSharedFilepaths(tmpDir, type);
550
+ const tBuild = Date.now();
551
+ logger_logger.info(`buildProject took ${tBuild - tInstall}ms`);
552
+ const sharedFilePaths = await retrieveSharedFilepaths(tmpDir, type);
553
+ const tRetrieve = Date.now();
554
+ logger_logger.info(`retrieveSharedFilepaths took ${tRetrieve - tBuild}ms`);
531
555
  return {
532
556
  sharedFilePaths,
533
557
  dir: tmpDir
534
558
  };
535
559
  };
536
- function cleanUp(tmpDir) {
560
+ async function cleanUp(tmpDir) {
537
561
  if (!tmpDir) return;
538
- external_node_fs_default().rmSync(tmpDir, {
562
+ await promises_default().rm(tmpDir, {
539
563
  recursive: true,
540
564
  force: true
541
565
  });
@@ -635,7 +659,7 @@ async function uploadToCacheStore(sharedFilePaths, normalizedConfig, store) {
635
659
  const jsonFilePath = filepath.replace(/\.js$/, '.json');
636
660
  const jsonFile = JSON.stringify(res);
637
661
  const jsonCdnUrl = cdnPath.replace(/\.js$/, '.json');
638
- external_node_fs_default().writeFileSync(jsonFilePath, jsonFile);
662
+ await promises_default().writeFile(jsonFilePath, jsonFile);
639
663
  await store.uploadFile(jsonFilePath, jsonCdnUrl);
640
664
  } catch (error) {
641
665
  logger_logger.error(`Failed to upload ${name}@${version} json file: ${error}`);
@@ -653,25 +677,26 @@ const downloadToFile = async (url, destPath)=>{
653
677
  const res = await fetch(url);
654
678
  if (!res.ok) throw new Error(`Download failed: ${res.status} ${res.statusText} - ${url}`);
655
679
  const buf = Buffer.from(await res.arrayBuffer());
656
- await external_node_fs_default().promises.mkdir(external_node_path_default().dirname(destPath), {
680
+ await promises_default().mkdir(external_node_path_default().dirname(destPath), {
657
681
  recursive: true
658
682
  });
659
- await external_node_fs_default().promises.writeFile(destPath, buf);
683
+ await promises_default().writeFile(destPath, buf);
660
684
  return destPath;
661
685
  };
662
- async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher) {
663
- const tmpDir = createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
686
+ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store) {
687
+ const tmpDir = await createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
664
688
  const uploaded = [];
665
689
  try {
666
690
  for (const item of uploadResults)try {
667
- const config = normalizedConfig[normalizedKey(item.name, item.version)];
691
+ const sharedKey = normalizedKey(item.name, item.version);
692
+ const config = normalizedConfig[sharedKey];
668
693
  if (!config) {
669
694
  logger_logger.error(`No config found for ${item.name}`);
670
695
  continue;
671
696
  }
672
697
  const { uploadOptions } = config;
673
698
  if (!uploadOptions) throw new Error(`No uploadOptions found for ${item.name}`);
674
- const filename = external_node_path_default().basename(new URL(item.cdnUrl).pathname);
699
+ const filename = external_node_path_default().basename(new URL(item.cdnUrl, 'http://dummy.com').pathname);
675
700
  const localPath = external_node_path_default().join(tmpDir, filename);
676
701
  const hash = createCacheHash({
677
702
  ...config,
@@ -740,9 +765,11 @@ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, p
740
765
  }
741
766
  return uploaded;
742
767
  } finally{
743
- external_node_fs_default().rmSync(tmpDir, {
768
+ promises_default().rm(tmpDir, {
744
769
  recursive: true,
745
770
  force: true
771
+ }).catch((err)=>{
772
+ logger_logger.error(`Failed to cleanup dir ${tmpDir}: ${err}`);
746
773
  });
747
774
  }
748
775
  }
@@ -751,14 +778,14 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
751
778
  if (!uploadOptions) {
752
779
  const hydrated = await Promise.all(uploadResults.map(async (item)=>{
753
780
  if ('full' !== item.type) return item;
754
- const tmpDir = createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
781
+ const tmpDir = await createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
755
782
  const jsonPath = external_node_path_default().join(tmpDir, `${item.name}-${item.version}.json`);
756
783
  try {
757
784
  const tJson0 = Date.now();
758
785
  await downloadToFile(item.cdnUrl.replace('.js', '.json'), jsonPath);
759
786
  const tJson = Date.now() - tJson0;
760
787
  logger_logger.info(`Downloaded ${item.name}@${item.version} json in ${tJson}ms`);
761
- const jsonContent = JSON.parse(external_node_fs_default().readFileSync(jsonPath, 'utf8'));
788
+ const jsonContent = JSON.parse(await promises_default().readFile(jsonPath, 'utf8'));
762
789
  return {
763
790
  ...item,
764
791
  canTreeShaking: jsonContent.canTreeShaking,
@@ -771,7 +798,7 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
771
798
  canTreeShaking: item.canTreeShaking ?? true
772
799
  };
773
800
  } finally{
774
- external_node_fs_default().rmSync(tmpDir, {
801
+ await promises_default().rm(tmpDir, {
775
802
  recursive: true,
776
803
  force: true
777
804
  });
@@ -783,9 +810,10 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
783
810
  ];
784
811
  }
785
812
  if (!publisher) throw new Error('uploadOptions provided but no projectPublisher configured (configure the selected adapter to enable it or omit uploadOptions)');
786
- const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher);
813
+ const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store);
787
814
  return projectUploadResults;
788
815
  }
816
+ const buildLimit = external_p_limit_default()(Math.max(1, external_node_os_default().cpus().length));
789
817
  const buildRoute = new external_hono_namespaceObject.Hono();
790
818
  buildRoute.post('/', (0, zod_validator_namespaceObject.zValidator)('json', ConfigSchema), async (c)=>{
791
819
  const logger = c.get('logger');
@@ -798,16 +826,27 @@ buildRoute.post('/', (0, zod_validator_namespaceObject.zValidator)('json', Confi
798
826
  const store = c.get('objectStore');
799
827
  const publisher = c.get('projectPublisher');
800
828
  try {
829
+ const t0 = Date.now();
801
830
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 're-shake', store);
831
+ const tRetrieveCache = Date.now();
832
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
802
833
  let sharedFilePaths = [];
803
834
  let dir;
804
835
  if (Object.keys(restConfig).length > 0) {
805
- const buildResult = await runBuild(normalizedConfig, excludeShared, 're-shake');
836
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 're-shake'));
806
837
  sharedFilePaths = buildResult.sharedFilePaths;
807
838
  dir = buildResult.dir;
808
839
  }
840
+ const tBuild = Date.now();
841
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
809
842
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
810
- cleanUp(dir);
843
+ const tUpload = Date.now();
844
+ logger.info(`upload took ${tUpload - tBuild}ms`);
845
+ cleanUp(dir).catch((err)=>{
846
+ logger.error(`Failed to cleanup dir ${dir}: ${err}`);
847
+ });
848
+ const tCleanUp = Date.now();
849
+ logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
811
850
  return c.json({
812
851
  jobId,
813
852
  status: 'success',
@@ -845,16 +884,27 @@ async function handleCheckTreeshake(c, body) {
845
884
  const store = c.get('objectStore');
846
885
  const publisher = c.get('projectPublisher');
847
886
  try {
887
+ const t0 = Date.now();
848
888
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 'full', store);
889
+ const tRetrieveCache = Date.now();
890
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
849
891
  let sharedFilePaths = [];
850
892
  let dir;
851
893
  if (Object.keys(restConfig).length > 0) {
852
- const buildResult = await runBuild(normalizedConfig, excludeShared, 'full');
894
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 'full'));
853
895
  sharedFilePaths = buildResult.sharedFilePaths;
854
896
  dir = buildResult.dir;
855
897
  }
898
+ const tBuild = Date.now();
899
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
856
900
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
857
- cleanUp(dir);
901
+ const tUpload = Date.now();
902
+ logger.info(`upload took ${tUpload - tBuild}ms`);
903
+ cleanUp(dir).catch((err)=>{
904
+ logger.error(`Failed to cleanup dir ${dir}: ${err}`);
905
+ });
906
+ const tCleanUp = Date.now();
907
+ logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
858
908
  return c.json({
859
909
  jobId,
860
910
  status: 'success',