@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.js CHANGED
@@ -77,8 +77,12 @@ const loggerMiddleware = async (c, next)=>{
77
77
  c.set('logger', child);
78
78
  await next();
79
79
  };
80
+ const external_node_os_namespaceObject = require("node:os");
81
+ var external_node_os_default = /*#__PURE__*/ __webpack_require__.n(external_node_os_namespaceObject);
80
82
  const zod_validator_namespaceObject = require("@hono/zod-validator");
81
83
  const external_nanoid_namespaceObject = require("nanoid");
84
+ const external_p_limit_namespaceObject = require("p-limit");
85
+ var external_p_limit_default = /*#__PURE__*/ __webpack_require__.n(external_p_limit_namespaceObject);
82
86
  const parseNormalizedKey = (key)=>{
83
87
  const res = key.split('@');
84
88
  return {
@@ -112,14 +116,15 @@ function normalizeConfig(config) {
112
116
  return normalizedConfig;
113
117
  }
114
118
  function extractBuildConfig(config, type) {
115
- const { shared, plugins, target, libraryType, usedExports } = config;
119
+ const { shared, plugins, target, libraryType, usedExports, hostName } = config;
116
120
  return {
117
121
  shared,
118
122
  plugins,
119
123
  target,
120
124
  libraryType,
121
125
  usedExports,
122
- type
126
+ type,
127
+ hostName
123
128
  };
124
129
  }
125
130
  const external_zod_namespaceObject = require("zod");
@@ -154,10 +159,8 @@ const CheckTreeShakingSchema = ConfigSchema.extend({
154
159
  uploadOptions: UploadOptionsSchema.optional()
155
160
  });
156
161
  const external_node_crypto_namespaceObject = require("node:crypto");
157
- const external_node_fs_namespaceObject = require("node:fs");
158
- var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
159
- const external_node_os_namespaceObject = require("node:os");
160
- var external_node_os_default = /*#__PURE__*/ __webpack_require__.n(external_node_os_namespaceObject);
162
+ const promises_namespaceObject = require("node:fs/promises");
163
+ var promises_default = /*#__PURE__*/ __webpack_require__.n(promises_namespaceObject);
161
164
  const external_node_path_namespaceObject = require("node:path");
162
165
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
163
166
  const STATS_NAME = 'mf-stats.json';
@@ -257,11 +260,11 @@ const startPeriodicPrune = (intervalMs = DEFAULT_INTERVAL)=>{
257
260
  maybePrune();
258
261
  }, intervalMs);
259
262
  };
260
- const createUniqueTempDirByKey = (key)=>{
263
+ const createUniqueTempDirByKey = async (key)=>{
261
264
  const base = external_node_path_default().join(external_node_os_default().tmpdir(), `re-shake-share-${key}`);
262
265
  let candidate = base;
263
266
  for(;;)try {
264
- external_node_fs_default().mkdirSync(candidate, {
267
+ await promises_default().mkdir(candidate, {
265
268
  recursive: false
266
269
  });
267
270
  return candidate;
@@ -270,15 +273,22 @@ const createUniqueTempDirByKey = (key)=>{
270
273
  candidate = `${base}-${rand}`;
271
274
  }
272
275
  };
273
- const prepareProject = (config, excludeShared)=>{
276
+ const prepareProject = async (config, excludeShared)=>{
274
277
  const key = (0, external_node_crypto_namespaceObject.createHash)('sha256').update(JSON.stringify(config)).digest('hex');
275
- const dir = createUniqueTempDirByKey(key);
276
- const templateDir = external_node_path_default().join(__dirname, '..', 'template', 're-shake-share');
277
- external_node_fs_default().cpSync(templateDir, dir, {
278
+ const dir = await createUniqueTempDirByKey(key);
279
+ const templateDir = external_node_path_default().join(__dirname, '.', 'template', 're-shake-share');
280
+ await promises_default().cp(templateDir, dir, {
278
281
  recursive: true
279
282
  });
280
283
  const pkgPath = external_node_path_default().join(dir, 'package.json');
281
- const pkg = JSON.parse(external_node_fs_default().readFileSync(pkgPath, 'utf-8'));
284
+ const indexPath = external_node_path_default().join(dir, 'index.ts');
285
+ const rspackConfigPath = external_node_path_default().join(dir, 'rspack.config.ts');
286
+ const [pkgContent, indexContent, rspackConfigContent] = await Promise.all([
287
+ promises_default().readFile(pkgPath, 'utf-8'),
288
+ promises_default().readFile(indexPath, 'utf-8'),
289
+ promises_default().readFile(rspackConfigPath, 'utf-8')
290
+ ]);
291
+ const pkg = JSON.parse(pkgContent);
282
292
  const deps = {
283
293
  ...pkg.dependencies || {}
284
294
  };
@@ -319,25 +329,26 @@ const prepareProject = (config, excludeShared)=>{
319
329
  });
320
330
  pluginOptionStr += '\n]';
321
331
  pkg.dependencies = deps;
322
- external_node_fs_default().writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
332
+ const newPkgContent = JSON.stringify(pkg, null, 2);
323
333
  const sharedImportPlaceholder = "${SHARED_IMPORT}";
334
+ const newIndexContent = indexContent.replace(sharedImportPlaceholder, sharedImport);
324
335
  const pluginsPlaceholder = "${ PLUGINS }";
325
336
  const mfConfigPlaceholder = "${ MF_CONFIG }";
326
- const indexPath = external_node_path_default().join(dir, 'index.ts');
327
- const indexContent = external_node_fs_default().readFileSync(indexPath, 'utf-8');
328
- external_node_fs_default().writeFileSync(indexPath, indexContent.replace(sharedImportPlaceholder, sharedImport));
329
- const rspackConfigPath = external_node_path_default().join(dir, 'rspack.config.ts');
330
- let cfg = external_node_fs_default().readFileSync(rspackConfigPath, 'utf-8');
337
+ let cfg = rspackConfigContent;
331
338
  cfg += pluginImportStr;
332
339
  cfg = cfg.replace(pluginsPlaceholder, pluginOptionStr);
333
340
  cfg = cfg.replace(mfConfigPlaceholder, JSON.stringify(mfConfig, null, 2));
334
- external_node_fs_default().writeFileSync(rspackConfigPath, cfg);
341
+ await Promise.all([
342
+ promises_default().writeFile(pkgPath, newPkgContent),
343
+ promises_default().writeFile(indexPath, newIndexContent),
344
+ promises_default().writeFile(rspackConfigPath, cfg)
345
+ ]);
335
346
  return dir;
336
347
  };
337
348
  const installDependencies = async (cwd)=>{
338
349
  markInstallStart();
339
350
  try {
340
- await runCommand('pnpm i', {
351
+ await runCommand("pnpm i --ignore-scripts --prefer-offline --reporter=silent ", {
341
352
  cwd,
342
353
  env: {
343
354
  npm_config_registry: 'https://registry.npmjs.org/'
@@ -355,12 +366,13 @@ const buildProject = async (cwd, type)=>{
355
366
  cwd
356
367
  })));
357
368
  };
358
- const retrieveSharedFilepaths = (projectDir, type)=>{
369
+ const retrieveSharedFilepaths = async (projectDir, type)=>{
359
370
  const sharedFilepaths = [];
360
- const collectSharedFilepaths = (t)=>{
371
+ const collectSharedFilepaths = async (t)=>{
361
372
  const dir = 'full' === t ? 'full-shared' : 'dist';
362
373
  const distDir = external_node_path_default().join(projectDir, dir);
363
- const stats = JSON.parse(external_node_fs_default().readFileSync(external_node_path_default().join(distDir, STATS_NAME), 'utf-8'));
374
+ const statsContent = await promises_default().readFile(external_node_path_default().join(distDir, STATS_NAME), 'utf-8');
375
+ const stats = JSON.parse(statsContent);
364
376
  stats.shared.forEach((s)=>{
365
377
  const { name, version, fallback, fallbackName } = s;
366
378
  if (fallback && fallbackName) {
@@ -378,22 +390,31 @@ const retrieveSharedFilepaths = (projectDir, type)=>{
378
390
  }
379
391
  });
380
392
  };
381
- collectSharedFilepaths(type);
393
+ await collectSharedFilepaths(type);
382
394
  return sharedFilepaths;
383
395
  };
384
396
  const runBuild = async (normalizedConfig, excludeShared, type)=>{
385
- const tmpDir = prepareProject(normalizedConfig, excludeShared);
397
+ const tStart = Date.now();
398
+ const tmpDir = await prepareProject(normalizedConfig, excludeShared);
399
+ const tPrepare = Date.now();
400
+ logger_logger.info(`prepareProject took ${tPrepare - tStart}ms`);
386
401
  await installDependencies(tmpDir);
402
+ const tInstall = Date.now();
403
+ logger_logger.info(`installDependencies took ${tInstall - tPrepare}ms`);
387
404
  await buildProject(tmpDir, type);
388
- const sharedFilePaths = retrieveSharedFilepaths(tmpDir, type);
405
+ const tBuild = Date.now();
406
+ logger_logger.info(`buildProject took ${tBuild - tInstall}ms`);
407
+ const sharedFilePaths = await retrieveSharedFilepaths(tmpDir, type);
408
+ const tRetrieve = Date.now();
409
+ logger_logger.info(`retrieveSharedFilepaths took ${tRetrieve - tBuild}ms`);
389
410
  return {
390
411
  sharedFilePaths,
391
412
  dir: tmpDir
392
413
  };
393
414
  };
394
- function cleanUp(tmpDir) {
415
+ async function cleanUp(tmpDir) {
395
416
  if (!tmpDir) return;
396
- external_node_fs_default().rmSync(tmpDir, {
417
+ await promises_default().rm(tmpDir, {
397
418
  recursive: true,
398
419
  force: true
399
420
  });
@@ -495,7 +516,7 @@ async function uploadToCacheStore(sharedFilePaths, normalizedConfig, store) {
495
516
  const jsonFilePath = filepath.replace(/\.js$/, '.json');
496
517
  const jsonFile = JSON.stringify(res);
497
518
  const jsonCdnUrl = cdnPath.replace(/\.js$/, '.json');
498
- external_node_fs_default().writeFileSync(jsonFilePath, jsonFile);
519
+ await promises_default().writeFile(jsonFilePath, jsonFile);
499
520
  await store.uploadFile(jsonFilePath, jsonCdnUrl);
500
521
  } catch (error) {
501
522
  logger_logger.error(`Failed to upload ${name}@${version} json file: ${error}`);
@@ -513,25 +534,26 @@ const downloadToFile = async (url, destPath)=>{
513
534
  const res = await fetch(url);
514
535
  if (!res.ok) throw new Error(`Download failed: ${res.status} ${res.statusText} - ${url}`);
515
536
  const buf = Buffer.from(await res.arrayBuffer());
516
- await external_node_fs_default().promises.mkdir(external_node_path_default().dirname(destPath), {
537
+ await promises_default().mkdir(external_node_path_default().dirname(destPath), {
517
538
  recursive: true
518
539
  });
519
- await external_node_fs_default().promises.writeFile(destPath, buf);
540
+ await promises_default().writeFile(destPath, buf);
520
541
  return destPath;
521
542
  };
522
- async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher) {
523
- const tmpDir = createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
543
+ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store) {
544
+ const tmpDir = await createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
524
545
  const uploaded = [];
525
546
  try {
526
547
  for (const item of uploadResults)try {
527
- const config = normalizedConfig[normalizedKey(item.name, item.version)];
548
+ const sharedKey = normalizedKey(item.name, item.version);
549
+ const config = normalizedConfig[sharedKey];
528
550
  if (!config) {
529
551
  logger_logger.error(`No config found for ${item.name}`);
530
552
  continue;
531
553
  }
532
554
  const { uploadOptions } = config;
533
555
  if (!uploadOptions) throw new Error(`No uploadOptions found for ${item.name}`);
534
- const filename = external_node_path_default().basename(new URL(item.cdnUrl).pathname);
556
+ const filename = external_node_path_default().basename(new URL(item.cdnUrl, 'http://dummy.com').pathname);
535
557
  const localPath = external_node_path_default().join(tmpDir, filename);
536
558
  const hash = createCacheHash({
537
559
  ...config,
@@ -600,9 +622,11 @@ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, p
600
622
  }
601
623
  return uploaded;
602
624
  } finally{
603
- external_node_fs_default().rmSync(tmpDir, {
625
+ promises_default().rm(tmpDir, {
604
626
  recursive: true,
605
627
  force: true
628
+ }).catch((err)=>{
629
+ logger_logger.error(`Failed to cleanup dir ${tmpDir}: ${err}`);
606
630
  });
607
631
  }
608
632
  }
@@ -611,14 +635,14 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
611
635
  if (!uploadOptions) {
612
636
  const hydrated = await Promise.all(uploadResults.map(async (item)=>{
613
637
  if ('full' !== item.type) return item;
614
- const tmpDir = createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
638
+ const tmpDir = await createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
615
639
  const jsonPath = external_node_path_default().join(tmpDir, `${item.name}-${item.version}.json`);
616
640
  try {
617
641
  const tJson0 = Date.now();
618
642
  await downloadToFile(item.cdnUrl.replace('.js', '.json'), jsonPath);
619
643
  const tJson = Date.now() - tJson0;
620
644
  logger_logger.info(`Downloaded ${item.name}@${item.version} json in ${tJson}ms`);
621
- const jsonContent = JSON.parse(external_node_fs_default().readFileSync(jsonPath, 'utf8'));
645
+ const jsonContent = JSON.parse(await promises_default().readFile(jsonPath, 'utf8'));
622
646
  return {
623
647
  ...item,
624
648
  canTreeShaking: jsonContent.canTreeShaking,
@@ -631,7 +655,7 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
631
655
  canTreeShaking: item.canTreeShaking ?? true
632
656
  };
633
657
  } finally{
634
- external_node_fs_default().rmSync(tmpDir, {
658
+ await promises_default().rm(tmpDir, {
635
659
  recursive: true,
636
660
  force: true
637
661
  });
@@ -643,9 +667,10 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
643
667
  ];
644
668
  }
645
669
  if (!publisher) throw new Error('uploadOptions provided but no projectPublisher configured (configure the selected adapter to enable it or omit uploadOptions)');
646
- const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher);
670
+ const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store);
647
671
  return projectUploadResults;
648
672
  }
673
+ const buildLimit = external_p_limit_default()(Math.max(1, external_node_os_default().cpus().length));
649
674
  const buildRoute = new external_hono_namespaceObject.Hono();
650
675
  buildRoute.post('/', (0, zod_validator_namespaceObject.zValidator)('json', ConfigSchema), async (c)=>{
651
676
  const logger = c.get('logger');
@@ -658,16 +683,27 @@ buildRoute.post('/', (0, zod_validator_namespaceObject.zValidator)('json', Confi
658
683
  const store = c.get('objectStore');
659
684
  const publisher = c.get('projectPublisher');
660
685
  try {
686
+ const t0 = Date.now();
661
687
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 're-shake', store);
688
+ const tRetrieveCache = Date.now();
689
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
662
690
  let sharedFilePaths = [];
663
691
  let dir;
664
692
  if (Object.keys(restConfig).length > 0) {
665
- const buildResult = await runBuild(normalizedConfig, excludeShared, 're-shake');
693
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 're-shake'));
666
694
  sharedFilePaths = buildResult.sharedFilePaths;
667
695
  dir = buildResult.dir;
668
696
  }
697
+ const tBuild = Date.now();
698
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
669
699
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
670
- cleanUp(dir);
700
+ const tUpload = Date.now();
701
+ logger.info(`upload took ${tUpload - tBuild}ms`);
702
+ cleanUp(dir).catch((err)=>{
703
+ logger.error(`Failed to cleanup dir ${dir}: ${err}`);
704
+ });
705
+ const tCleanUp = Date.now();
706
+ logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
671
707
  return c.json({
672
708
  jobId,
673
709
  status: 'success',
@@ -705,16 +741,27 @@ async function handleCheckTreeshake(c, body) {
705
741
  const store = c.get('objectStore');
706
742
  const publisher = c.get('projectPublisher');
707
743
  try {
744
+ const t0 = Date.now();
708
745
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 'full', store);
746
+ const tRetrieveCache = Date.now();
747
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
709
748
  let sharedFilePaths = [];
710
749
  let dir;
711
750
  if (Object.keys(restConfig).length > 0) {
712
- const buildResult = await runBuild(normalizedConfig, excludeShared, 'full');
751
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 'full'));
713
752
  sharedFilePaths = buildResult.sharedFilePaths;
714
753
  dir = buildResult.dir;
715
754
  }
755
+ const tBuild = Date.now();
756
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
716
757
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
717
- cleanUp(dir);
758
+ const tUpload = Date.now();
759
+ logger.info(`upload took ${tUpload - tBuild}ms`);
760
+ cleanUp(dir).catch((err)=>{
761
+ logger.error(`Failed to cleanup dir ${dir}: ${err}`);
762
+ });
763
+ const tCleanUp = Date.now();
764
+ logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
718
765
  return c.json({
719
766
  jobId,
720
767
  status: 'success',
@@ -759,6 +806,8 @@ maintenanceRoute.post('/', async (c)=>{
759
806
  const routes = new external_hono_namespaceObject.Hono();
760
807
  routes.route('/build', buildRoute);
761
808
  routes.route('/clean-cache', maintenanceRoute);
809
+ const external_node_fs_namespaceObject = require("node:fs");
810
+ var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
762
811
  function contentTypeByExt(filePath) {
763
812
  if (filePath.endsWith('.js')) return "application/javascript";
764
813
  if (filePath.endsWith('.json')) return 'application/json';
@@ -881,7 +930,8 @@ class LocalObjectStore {
881
930
  localObjectStore_define_property(this, "rootDir", void 0);
882
931
  localObjectStore_define_property(this, "publicBaseUrl", void 0);
883
932
  this.rootDir = (null == opts ? void 0 : opts.rootDir) ?? external_node_path_default().join(process.cwd(), 'log', 'static');
884
- const base = (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
933
+ const port = process.env.PORT || 3000;
934
+ const base = (null == opts ? void 0 : opts.publicBaseUrl) === '/' ? `http://localhost:${port}/` : (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
885
935
  this.publicBaseUrl = base.endsWith('/') ? base : `${base}/`;
886
936
  }
887
937
  }