@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.mjs CHANGED
@@ -4,15 +4,18 @@ import node_path from "node:path";
4
4
  import pino from "pino";
5
5
  import { Hono } from "hono";
6
6
  import { cors } from "hono/cors";
7
+ import node_os from "node:os";
7
8
  import { zValidator } from "@hono/zod-validator";
8
9
  import { nanoid } from "nanoid";
10
+ import p_limit from "p-limit";
9
11
  import { z } from "zod";
10
12
  import { createHash } from "node:crypto";
11
- import node_os from "node:os";
13
+ import promises from "node:fs/promises";
12
14
  import { spawn } from "node:child_process";
13
15
  import json_stable_stringify from "json-stable-stringify";
16
+ import { timeout } from "hono/timeout";
14
17
  import { serve } from "@hono/node-server";
15
- const SERVER_VERSION = 'v0-011501';
18
+ const SERVER_VERSION = 'v0-0205';
16
19
  const UPLOADED_DIR = '_shared-tree-shaking';
17
20
  const createLogger = (opts)=>pino({
18
21
  level: (null == opts ? void 0 : opts.level) ?? 'info',
@@ -71,7 +74,8 @@ class LocalObjectStore {
71
74
  _define_property(this, "rootDir", void 0);
72
75
  _define_property(this, "publicBaseUrl", void 0);
73
76
  this.rootDir = (null == opts ? void 0 : opts.rootDir) ?? node_path.join(process.cwd(), 'log', 'static');
74
- const base = (null == opts ? void 0 : opts.publicBaseUrl) ?? '/';
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) ?? '/';
75
79
  this.publicBaseUrl = base.endsWith('/') ? base : `${base}/`;
76
80
  }
77
81
  }
@@ -238,14 +242,15 @@ function normalizeConfig(config) {
238
242
  return normalizedConfig;
239
243
  }
240
244
  function extractBuildConfig(config, type) {
241
- const { shared, plugins, target, libraryType, usedExports } = config;
245
+ const { shared, plugins, target, libraryType, usedExports, hostName } = config;
242
246
  return {
243
247
  shared,
244
248
  plugins,
245
249
  target,
246
250
  libraryType,
247
251
  usedExports,
248
- type
252
+ type,
253
+ hostName
249
254
  };
250
255
  }
251
256
  const UploadOptionsSchema = z.object({
@@ -374,11 +379,11 @@ const startPeriodicPrune = (intervalMs = DEFAULT_INTERVAL)=>{
374
379
  maybePrune();
375
380
  }, intervalMs);
376
381
  };
377
- const createUniqueTempDirByKey = (key)=>{
382
+ const createUniqueTempDirByKey = async (key)=>{
378
383
  const base = node_path.join(node_os.tmpdir(), `re-shake-share-${key}`);
379
384
  let candidate = base;
380
385
  for(;;)try {
381
- node_fs.mkdirSync(candidate, {
386
+ await promises.mkdir(candidate, {
382
387
  recursive: false
383
388
  });
384
389
  return candidate;
@@ -387,15 +392,22 @@ const createUniqueTempDirByKey = (key)=>{
387
392
  candidate = `${base}-${rand}`;
388
393
  }
389
394
  };
390
- const prepareProject = (config, excludeShared)=>{
395
+ const prepareProject = async (config, excludeShared)=>{
391
396
  const key = createHash('sha256').update(JSON.stringify(config)).digest('hex');
392
- const dir = createUniqueTempDirByKey(key);
393
- const templateDir = node_path.join(__dirname, '..', 'template', 're-shake-share');
394
- node_fs.cpSync(templateDir, dir, {
397
+ const dir = await createUniqueTempDirByKey(key);
398
+ const templateDir = node_path.join(__dirname, '.', 'template', 're-shake-share');
399
+ await promises.cp(templateDir, dir, {
395
400
  recursive: true
396
401
  });
397
402
  const pkgPath = node_path.join(dir, 'package.json');
398
- const pkg = JSON.parse(node_fs.readFileSync(pkgPath, 'utf-8'));
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);
399
411
  const deps = {
400
412
  ...pkg.dependencies || {}
401
413
  };
@@ -436,28 +448,29 @@ const prepareProject = (config, excludeShared)=>{
436
448
  });
437
449
  pluginOptionStr += '\n]';
438
450
  pkg.dependencies = deps;
439
- node_fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
451
+ const newPkgContent = JSON.stringify(pkg, null, 2);
440
452
  const sharedImportPlaceholder = "${SHARED_IMPORT}";
453
+ const newIndexContent = indexContent.replace(sharedImportPlaceholder, sharedImport);
441
454
  const pluginsPlaceholder = "${ PLUGINS }";
442
455
  const mfConfigPlaceholder = "${ MF_CONFIG }";
443
- const indexPath = node_path.join(dir, 'index.ts');
444
- const indexContent = node_fs.readFileSync(indexPath, 'utf-8');
445
- node_fs.writeFileSync(indexPath, indexContent.replace(sharedImportPlaceholder, sharedImport));
446
- const rspackConfigPath = node_path.join(dir, 'rspack.config.ts');
447
- let cfg = node_fs.readFileSync(rspackConfigPath, 'utf-8');
456
+ let cfg = rspackConfigContent;
448
457
  cfg += pluginImportStr;
449
458
  cfg = cfg.replace(pluginsPlaceholder, pluginOptionStr);
450
459
  cfg = cfg.replace(mfConfigPlaceholder, JSON.stringify(mfConfig, null, 2));
451
- node_fs.writeFileSync(rspackConfigPath, cfg);
460
+ await Promise.all([
461
+ promises.writeFile(pkgPath, newPkgContent),
462
+ promises.writeFile(indexPath, newIndexContent),
463
+ promises.writeFile(rspackConfigPath, cfg)
464
+ ]);
452
465
  return dir;
453
466
  };
454
467
  const installDependencies = async (cwd)=>{
455
468
  markInstallStart();
456
469
  try {
457
- await runCommand('pnpm i', {
470
+ await runCommand("pnpm i --ignore-scripts --prefer-offline --reporter=silent ", {
458
471
  cwd,
459
472
  env: {
460
- npm_config_registry: 'https://registry.npmjs.org/'
473
+ npm_config_registry: process.env.MF_NPM_REGISTRY || 'https://registry.npmjs.org/'
461
474
  }
462
475
  });
463
476
  } finally{
@@ -472,12 +485,13 @@ const buildProject = async (cwd, type)=>{
472
485
  cwd
473
486
  })));
474
487
  };
475
- const retrieveSharedFilepaths = (projectDir, type)=>{
488
+ const retrieveSharedFilepaths = async (projectDir, type)=>{
476
489
  const sharedFilepaths = [];
477
- const collectSharedFilepaths = (t)=>{
490
+ const collectSharedFilepaths = async (t)=>{
478
491
  const dir = 'full' === t ? 'full-shared' : 'dist';
479
492
  const distDir = node_path.join(projectDir, dir);
480
- const stats = JSON.parse(node_fs.readFileSync(node_path.join(distDir, STATS_NAME), 'utf-8'));
493
+ const statsContent = await promises.readFile(node_path.join(distDir, STATS_NAME), 'utf-8');
494
+ const stats = JSON.parse(statsContent);
481
495
  stats.shared.forEach((s)=>{
482
496
  const { name, version, fallback, fallbackName } = s;
483
497
  if (fallback && fallbackName) {
@@ -495,22 +509,31 @@ const retrieveSharedFilepaths = (projectDir, type)=>{
495
509
  }
496
510
  });
497
511
  };
498
- collectSharedFilepaths(type);
512
+ await collectSharedFilepaths(type);
499
513
  return sharedFilepaths;
500
514
  };
501
515
  const runBuild = async (normalizedConfig, excludeShared, type)=>{
502
- const tmpDir = prepareProject(normalizedConfig, excludeShared);
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`);
503
520
  await installDependencies(tmpDir);
521
+ const tInstall = Date.now();
522
+ logger_logger.info(`installDependencies took ${tInstall - tPrepare}ms`);
504
523
  await buildProject(tmpDir, type);
505
- const sharedFilePaths = retrieveSharedFilepaths(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`);
506
529
  return {
507
530
  sharedFilePaths,
508
531
  dir: tmpDir
509
532
  };
510
533
  };
511
- function cleanUp(tmpDir) {
534
+ async function cleanUp(tmpDir) {
512
535
  if (!tmpDir) return;
513
- node_fs.rmSync(tmpDir, {
536
+ await promises.rm(tmpDir, {
514
537
  recursive: true,
515
538
  force: true
516
539
  });
@@ -608,7 +631,7 @@ async function uploadToCacheStore(sharedFilePaths, normalizedConfig, store) {
608
631
  const jsonFilePath = filepath.replace(/\.js$/, '.json');
609
632
  const jsonFile = JSON.stringify(res);
610
633
  const jsonCdnUrl = cdnPath.replace(/\.js$/, '.json');
611
- node_fs.writeFileSync(jsonFilePath, jsonFile);
634
+ await promises.writeFile(jsonFilePath, jsonFile);
612
635
  await store.uploadFile(jsonFilePath, jsonCdnUrl);
613
636
  } catch (error) {
614
637
  logger_logger.error(`Failed to upload ${name}@${version} json file: ${error}`);
@@ -626,25 +649,26 @@ const downloadToFile = async (url, destPath)=>{
626
649
  const res = await fetch(url);
627
650
  if (!res.ok) throw new Error(`Download failed: ${res.status} ${res.statusText} - ${url}`);
628
651
  const buf = Buffer.from(await res.arrayBuffer());
629
- await node_fs.promises.mkdir(node_path.dirname(destPath), {
652
+ await promises.mkdir(node_path.dirname(destPath), {
630
653
  recursive: true
631
654
  });
632
- await node_fs.promises.writeFile(destPath, buf);
655
+ await promises.writeFile(destPath, buf);
633
656
  return destPath;
634
657
  };
635
- async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher) {
636
- const tmpDir = createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
658
+ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store) {
659
+ const tmpDir = await createUniqueTempDirByKey(`upload-project${Date.now().toString()}`);
637
660
  const uploaded = [];
638
661
  try {
639
662
  for (const item of uploadResults)try {
640
- const config = normalizedConfig[normalizedKey(item.name, item.version)];
663
+ const sharedKey = normalizedKey(item.name, item.version);
664
+ const config = normalizedConfig[sharedKey];
641
665
  if (!config) {
642
666
  logger_logger.error(`No config found for ${item.name}`);
643
667
  continue;
644
668
  }
645
669
  const { uploadOptions } = config;
646
670
  if (!uploadOptions) throw new Error(`No uploadOptions found for ${item.name}`);
647
- const filename = node_path.basename(new URL(item.cdnUrl).pathname);
671
+ const filename = node_path.basename(new URL(item.cdnUrl, 'http://dummy.com').pathname);
648
672
  const localPath = node_path.join(tmpDir, filename);
649
673
  const hash = createCacheHash({
650
674
  ...config,
@@ -706,16 +730,20 @@ async function uploadProject(uploadResults, sharedFilePaths, normalizedConfig, p
706
730
  version: s.version,
707
731
  globalName: s.globalName,
708
732
  cdnUrl,
709
- type: s.type
733
+ type: s.type,
734
+ modules: s.modules,
735
+ canTreeShaking: s.canTreeShaking
710
736
  });
711
737
  } catch (error) {
712
738
  logger_logger.error(`Failed to upload ${s.name}@${s.version}: ${error}`);
713
739
  }
714
740
  return uploaded;
715
741
  } finally{
716
- node_fs.rmSync(tmpDir, {
742
+ promises.rm(tmpDir, {
717
743
  recursive: true,
718
744
  force: true
745
+ }).catch((err)=>{
746
+ logger_logger.error(`Failed to cleanup dir ${tmpDir}: ${err}`);
719
747
  });
720
748
  }
721
749
  }
@@ -724,14 +752,14 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
724
752
  if (!uploadOptions) {
725
753
  const hydrated = await Promise.all(uploadResults.map(async (item)=>{
726
754
  if ('full' !== item.type) return item;
727
- const tmpDir = createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
755
+ const tmpDir = await createUniqueTempDirByKey(`download-full-json${Date.now().toString()}`);
728
756
  const jsonPath = node_path.join(tmpDir, `${item.name}-${item.version}.json`);
729
757
  try {
730
758
  const tJson0 = Date.now();
731
759
  await downloadToFile(item.cdnUrl.replace('.js', '.json'), jsonPath);
732
760
  const tJson = Date.now() - tJson0;
733
761
  logger_logger.info(`Downloaded ${item.name}@${item.version} json in ${tJson}ms`);
734
- const jsonContent = JSON.parse(node_fs.readFileSync(jsonPath, 'utf8'));
762
+ const jsonContent = JSON.parse(await promises.readFile(jsonPath, 'utf8'));
735
763
  return {
736
764
  ...item,
737
765
  canTreeShaking: jsonContent.canTreeShaking,
@@ -744,7 +772,7 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
744
772
  canTreeShaking: item.canTreeShaking ?? true
745
773
  };
746
774
  } finally{
747
- node_fs.rmSync(tmpDir, {
775
+ await promises.rm(tmpDir, {
748
776
  recursive: true,
749
777
  force: true
750
778
  });
@@ -756,9 +784,10 @@ async function upload(sharedFilePaths, uploadResults, normalizedConfig, uploadOp
756
784
  ];
757
785
  }
758
786
  if (!publisher) throw new Error('uploadOptions provided but no projectPublisher configured (configure the selected adapter to enable it or omit uploadOptions)');
759
- const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher);
787
+ const projectUploadResults = await uploadProject(uploadResults, sharedFilePaths, normalizedConfig, publisher, store);
760
788
  return projectUploadResults;
761
789
  }
790
+ const buildLimit = p_limit(Math.max(1, node_os.cpus().length));
762
791
  const buildRoute = new Hono();
763
792
  buildRoute.post('/', zValidator('json', ConfigSchema), async (c)=>{
764
793
  const logger = c.get('logger');
@@ -771,16 +800,27 @@ buildRoute.post('/', zValidator('json', ConfigSchema), async (c)=>{
771
800
  const store = c.get('objectStore');
772
801
  const publisher = c.get('projectPublisher');
773
802
  try {
803
+ const t0 = Date.now();
774
804
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 're-shake', store);
805
+ const tRetrieveCache = Date.now();
806
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
775
807
  let sharedFilePaths = [];
776
808
  let dir;
777
809
  if (Object.keys(restConfig).length > 0) {
778
- const buildResult = await runBuild(normalizedConfig, excludeShared, 're-shake');
810
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 're-shake'));
779
811
  sharedFilePaths = buildResult.sharedFilePaths;
780
812
  dir = buildResult.dir;
781
813
  }
814
+ const tBuild = Date.now();
815
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
782
816
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
783
- cleanUp(dir);
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`);
784
824
  return c.json({
785
825
  jobId,
786
826
  status: 'success',
@@ -818,16 +858,27 @@ async function handleCheckTreeshake(c, body) {
818
858
  const store = c.get('objectStore');
819
859
  const publisher = c.get('projectPublisher');
820
860
  try {
861
+ const t0 = Date.now();
821
862
  const { cacheItems, excludeShared, restConfig } = await retrieveCacheItems(normalizedConfig, 'full', store);
863
+ const tRetrieveCache = Date.now();
864
+ logger.info(`retrieveCacheItems took ${tRetrieveCache - t0}ms`);
822
865
  let sharedFilePaths = [];
823
866
  let dir;
824
867
  if (Object.keys(restConfig).length > 0) {
825
- const buildResult = await runBuild(normalizedConfig, excludeShared, 'full');
868
+ const buildResult = await buildLimit(()=>runBuild(normalizedConfig, excludeShared, 'full'));
826
869
  sharedFilePaths = buildResult.sharedFilePaths;
827
870
  dir = buildResult.dir;
828
871
  }
872
+ const tBuild = Date.now();
873
+ logger.info(`runBuild took ${tBuild - tRetrieveCache}ms`);
829
874
  const uploadResults = await upload(sharedFilePaths, cacheItems, normalizedConfig, body.uploadOptions, store, publisher);
830
- cleanUp(dir);
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`);
831
882
  return c.json({
832
883
  jobId,
833
884
  status: 'success',
@@ -893,6 +944,7 @@ function createApp(deps, opts) {
893
944
  }));
894
945
  app.use('*', loggerMiddleware);
895
946
  app.use('*', createDiMiddleware(deps));
947
+ app.use('*', timeout(60000));
896
948
  if (null == opts ? void 0 : null == (_opts_appExtensions = opts.appExtensions) ? void 0 : _opts_appExtensions.length) for (const extend of opts.appExtensions)extend(app);
897
949
  app.get('/tree-shaking-shared/healthz', (c)=>c.json({
898
950
  status: 'ok',
@@ -985,7 +1037,8 @@ function createServer(opts) {
985
1037
  return serve({
986
1038
  fetch: opts.app.fetch,
987
1039
  port,
988
- hostname
1040
+ hostname,
1041
+ overrideGlobalObjects: false
989
1042
  });
990
1043
  }
991
1044
  const hasIndexHtml = (dir)=>Boolean(dir && node_fs.existsSync(node_path.join(dir, 'index.html')));
@@ -1046,11 +1099,8 @@ async function main() {
1046
1099
  port: resolved.port,
1047
1100
  hostname: resolved.hostname
1048
1101
  });
1049
- console.log(`Build service listening on http://${resolved.hostname}:${resolved.port}`);
1050
- if (frontendAdapters.length) {
1051
- const basePath = process.env.TREESHAKE_FRONTEND_BASE_PATH ?? '/tree-shaking';
1052
- console.log(`Treeshake UI available at http://${resolved.hostname}:${resolved.port}${basePath}`);
1053
- }
1102
+ console.log('Build service listening.');
1103
+ if (frontendAdapters.length) console.log('Treeshake UI available.');
1054
1104
  }
1055
1105
  main().catch((err)=>{
1056
1106
  console.error(err);
@@ -1,8 +1,8 @@
1
1
  import { type BuildType, type NormalizedConfig } from '../domain/build/normalize-config';
2
2
  import type { SharedFilePath } from './uploadService';
3
- export declare const createUniqueTempDirByKey: (key: string) => string;
3
+ export declare const createUniqueTempDirByKey: (key: string) => Promise<string>;
4
4
  export declare const runBuild: (normalizedConfig: NormalizedConfig, excludeShared: Array<[string, string]>, type: BuildType) => Promise<{
5
5
  sharedFilePaths: SharedFilePath[];
6
6
  dir: string;
7
7
  }>;
8
- export declare function cleanUp(tmpDir?: string): void;
8
+ export declare function cleanUp(tmpDir?: string): Promise<void>;
@@ -32,5 +32,5 @@ export interface UploadOpts {
32
32
  cdnRegion: string;
33
33
  publicRoot: string;
34
34
  }
35
- export declare function uploadProject(uploadResults: UploadResult[], sharedFilePaths: SharedFilePath[], normalizedConfig: NormalizedConfig, publisher: ProjectPublisher): Promise<UploadResult[]>;
35
+ export declare function uploadProject(uploadResults: UploadResult[], sharedFilePaths: SharedFilePath[], normalizedConfig: NormalizedConfig, publisher: ProjectPublisher, store: ObjectStore): Promise<UploadResult[]>;
36
36
  export declare function upload(sharedFilePaths: SharedFilePath[], uploadResults: UploadResult[], normalizedConfig: NormalizedConfig, uploadOptions: Config['uploadOptions'], store: ObjectStore, publisher?: ProjectPublisher): Promise<UploadResult[]>;
@@ -7,8 +7,8 @@
7
7
  },
8
8
  "license": "ISC",
9
9
  "dependencies": {
10
- "@rspack/cli": "^1.5.8",
11
- "@rspack/core": "^1.5.8"
10
+ "@rspack/cli": "1.5.8",
11
+ "@rspack/core": "1.5.8"
12
12
  },
13
13
  "pnpm": {
14
14
  "overrides": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/treeshake-server",
3
- "version": "0.0.1",
3
+ "version": "2.0.1",
4
4
  "description": "Build service powered by Hono that installs dependencies, builds with Rspack, and uploads artifacts.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -13,17 +13,10 @@
13
13
  }
14
14
  },
15
15
  "bin": {
16
- "treeshake-server": "dist/server.js"
17
- },
18
- "scripts": {
19
- "build": "pnpm -C ../treeshake-frontend build && rslib build && node scripts/copy-frontend.js",
20
- "lint": "biome lint src scripts",
21
- "prestart": "npm run build",
22
- "dev": "node scripts/dev.js",
23
- "start": "node dist/server.js",
24
- "test": "rstest"
16
+ "treeshake-server": "bin/treeshake-server.js"
25
17
  },
26
18
  "files": [
19
+ "bin/",
27
20
  "dist/",
28
21
  "README.md"
29
22
  ],
@@ -34,11 +27,12 @@
34
27
  "@hono/node-server": "1.19.5",
35
28
  "@hono/zod-validator": "0.7.4",
36
29
  "dotenv": "16.4.5",
37
- "hono": "4.10.2",
30
+ "hono": "4.11.7",
38
31
  "json-stable-stringify": "1.3.0",
39
32
  "nanoid": "5.1.6",
33
+ "p-limit": "^7.2.0",
40
34
  "pino": "10.1.0",
41
- "undici": "5.29.0",
35
+ "undici": "6.23.0",
42
36
  "zod": "4.1.12"
43
37
  },
44
38
  "devDependencies": {
@@ -57,5 +51,18 @@
57
51
  },
58
52
  "publishConfig": {
59
53
  "access": "public"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "git+https://github.com/module-federation/core.git",
58
+ "directory": "packages/treeshake-server"
59
+ },
60
+ "scripts": {
61
+ "build": "pnpm -C ../treeshake-frontend build && rslib build && node scripts/copy-frontend.js",
62
+ "lint": "biome lint src scripts",
63
+ "prestart": "npm run build",
64
+ "dev": "node scripts/dev.js",
65
+ "start": "node dist/server.js",
66
+ "test": "node ../../scripts/ensure-playwright.js && rstest"
60
67
  }
61
- }
68
+ }