@module-federation/treeshake-server 0.0.1 → 2.0.1

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/server.js CHANGED
@@ -26,7 +26,7 @@ const external_node_fs_namespaceObject = require("node:fs");
26
26
  var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
27
27
  const external_node_path_namespaceObject = require("node:path");
28
28
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
29
- const SERVER_VERSION = 'v0-011501';
29
+ const SERVER_VERSION = 'v0-0205';
30
30
  const UPLOADED_DIR = '_shared-tree-shaking';
31
31
  const external_pino_namespaceObject = require("pino");
32
32
  var external_pino_default = /*#__PURE__*/ __webpack_require__.n(external_pino_namespaceObject);
@@ -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,28 +474,29 @@ 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
- npm_config_registry: 'https://registry.npmjs.org/'
499
+ npm_config_registry: process.env.MF_NPM_REGISTRY || 'https://registry.npmjs.org/'
486
500
  }
487
501
  });
488
502
  } finally{
@@ -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,
@@ -733,16 +758,20 @@ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, p
733
758
  version: s.version,
734
759
  globalName: s.globalName,
735
760
  cdnUrl,
736
- type: s.type
761
+ type: s.type,
762
+ modules: s.modules,
763
+ canTreeShaking: s.canTreeShaking
737
764
  });
738
765
  } catch (error) {
739
766
  logger_logger.error(`Failed to upload ${s.name}@${s.version}: ${error}`);
740
767
  }
741
768
  return uploaded;
742
769
  } finally{
743
- external_node_fs_default().rmSync(tmpDir, {
770
+ promises_default().rm(tmpDir, {
744
771
  recursive: true,
745
772
  force: true
773
+ }).catch((err)=>{
774
+ logger_logger.error(`Failed to cleanup dir ${tmpDir}: ${err}`);
746
775
  });
747
776
  }
748
777
  }
@@ -751,14 +780,14 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
751
780
  if (!uploadOptions) {
752
781
  const hydrated = await Promise.all(uploadResults.map(async (item)=>{
753
782
  if ('full' !== item.type) return item;
754
- const tmpDir = createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
783
+ const tmpDir = await createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
755
784
  const jsonPath = external_node_path_default().join(tmpDir, `${item.name}-${item.version}.json`);
756
785
  try {
757
786
  const tJson0 = Date.now();
758
787
  await downloadToFile(item.cdnUrl.replace('.js', '.json'), jsonPath);
759
788
  const tJson = Date.now() - tJson0;
760
789
  logger_logger.info(`Downloaded ${item.name}@${item.version} json in ${tJson}ms`);
761
- const jsonContent = JSON.parse(external_node_fs_default().readFileSync(jsonPath, 'utf8'));
790
+ const jsonContent = JSON.parse(await promises_default().readFile(jsonPath, 'utf8'));
762
791
  return {
763
792
  ...item,
764
793
  canTreeShaking: jsonContent.canTreeShaking,
@@ -771,7 +800,7 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
771
800
  canTreeShaking: item.canTreeShaking ?? true
772
801
  };
773
802
  } finally{
774
- external_node_fs_default().rmSync(tmpDir, {
803
+ await promises_default().rm(tmpDir, {
775
804
  recursive: true,
776
805
  force: true
777
806
  });
@@ -783,9 +812,10 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
783
812
  ];
784
813
  }
785
814
  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);
815
+ const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store);
787
816
  return projectUploadResults;
788
817
  }
818
+ const buildLimit = external_p_limit_default()(Math.max(1, external_node_os_default().cpus().length));
789
819
  const buildRoute = new external_hono_namespaceObject.Hono();
790
820
  buildRoute.post('/', (0, zod_validator_namespaceObject.zValidator)('json', ConfigSchema), async (c)=>{
791
821
  const logger = c.get('logger');
@@ -798,16 +828,27 @@ buildRoute.post('/', (0, zod_validator_namespaceObject.zValidator)('json', Confi
798
828
  const store = c.get('objectStore');
799
829
  const publisher = c.get('projectPublisher');
800
830
  try {
831
+ const t0 = Date.now();
801
832
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 're-shake', store);
833
+ const tRetrieveCache = Date.now();
834
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
802
835
  let sharedFilePaths = [];
803
836
  let dir;
804
837
  if (Object.keys(restConfig).length > 0) {
805
- const buildResult = await runBuild(normalizedConfig, excludeShared, 're-shake');
838
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 're-shake'));
806
839
  sharedFilePaths = buildResult.sharedFilePaths;
807
840
  dir = buildResult.dir;
808
841
  }
842
+ const tBuild = Date.now();
843
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
809
844
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
810
- cleanUp(dir);
845
+ const tUpload = Date.now();
846
+ logger.info(`upload took ${tUpload - tBuild}ms`);
847
+ cleanUp(dir).catch((err)=>{
848
+ logger.error(`Failed to cleanup dir ${dir}: ${err}`);
849
+ });
850
+ const tCleanUp = Date.now();
851
+ logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
811
852
  return c.json({
812
853
  jobId,
813
854
  status: 'success',
@@ -845,16 +886,27 @@ async function handleCheckTreeshake(c, body) {
845
886
  const store = c.get('objectStore');
846
887
  const publisher = c.get('projectPublisher');
847
888
  try {
889
+ const t0 = Date.now();
848
890
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 'full', store);
891
+ const tRetrieveCache = Date.now();
892
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
849
893
  let sharedFilePaths = [];
850
894
  let dir;
851
895
  if (Object.keys(restConfig).length > 0) {
852
- const buildResult = await runBuild(normalizedConfig, excludeShared, 'full');
896
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 'full'));
853
897
  sharedFilePaths = buildResult.sharedFilePaths;
854
898
  dir = buildResult.dir;
855
899
  }
900
+ const tBuild = Date.now();
901
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
856
902
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
857
- cleanUp(dir);
903
+ const tUpload = Date.now();
904
+ logger.info(`upload took ${tUpload - tBuild}ms`);
905
+ cleanUp(dir).catch((err)=>{
906
+ logger.error(`Failed to cleanup dir ${dir}: ${err}`);
907
+ });
908
+ const tCleanUp = Date.now();
909
+ logger.info(`cleanUp scheduled (non-blocking) took ${tCleanUp - tUpload}ms`);
858
910
  return c.json({
859
911
  jobId,
860
912
  status: 'success',
@@ -899,6 +951,7 @@ maintenanceRoute.post('/', async (c)=>{
899
951
  const routes = new external_hono_namespaceObject.Hono();
900
952
  routes.route('/build', buildRoute);
901
953
  routes.route('/clean-cache', maintenanceRoute);
954
+ const timeout_namespaceObject = require("hono/timeout");
902
955
  function createApp(deps, opts) {
903
956
  var _opts_appExtensions, _opts_frontendAdapters;
904
957
  if (null == opts ? void 0 : opts.logger) setLogger(opts.logger);
@@ -920,6 +973,7 @@ function createApp(deps, opts) {
920
973
  }));
921
974
  app.use('*', loggerMiddleware);
922
975
  app.use('*', createDiMiddleware(deps));
976
+ app.use('*', (0, timeout_namespaceObject.timeout)(60000));
923
977
  if (null == opts ? void 0 : null == (_opts_appExtensions = opts.appExtensions) ? void 0 : _opts_appExtensions.length) for (const extend of opts.appExtensions)extend(app);
924
978
  app.get('/tree-shaking-shared/healthz', (c)=>c.json({
925
979
  status: 'ok',
@@ -1013,7 +1067,8 @@ function createServer(opts) {
1013
1067
  return (0, node_server_namespaceObject.serve)({
1014
1068
  fetch: opts.app.fetch,
1015
1069
  port,
1016
- hostname
1070
+ hostname,
1071
+ overrideGlobalObjects: false
1017
1072
  });
1018
1073
  }
1019
1074
  const hasIndexHtml = (dir)=>Boolean(dir && external_node_fs_default().existsSync(external_node_path_default().join(dir, 'index.html')));
@@ -1074,11 +1129,8 @@ async function main() {
1074
1129
  port: resolved.port,
1075
1130
  hostname: resolved.hostname
1076
1131
  });
1077
- console.log(`Build service listening on http://${resolved.hostname}:${resolved.port}`);
1078
- if (frontendAdapters.length) {
1079
- const basePath = process.env.TREESHAKE_FRONTEND_BASE_PATH ?? '/tree-shaking';
1080
- console.log(`Treeshake UI available at http://${resolved.hostname}:${resolved.port}${basePath}`);
1081
- }
1132
+ console.log('Build service listening.');
1133
+ if (frontendAdapters.length) console.log('Treeshake UI available.');
1082
1134
  }
1083
1135
  main().catch((err)=>{
1084
1136
  console.error(err);