@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/bin/treeshake-server.js +35 -0
- package/dist/domain/build/normalize-config.d.ts +1 -0
- package/dist/frontend/index.html +1 -1
- package/dist/frontend/static/js/{954.dfe166a3.js → 296.084d1b43.js} +2 -2
- package/dist/frontend/static/js/async/873.21368adc.js +2 -0
- package/dist/frontend/static/js/async/{987.6bf8e9b0.js → 987.86ff6794.js} +2 -2
- package/dist/frontend/static/js/{index.db4e73c6.js → index.5488f626.js} +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +96 -46
- package/dist/index.mjs +92 -44
- package/dist/server.js +94 -44
- package/dist/server.mjs +91 -43
- package/dist/services/buildService.d.ts +2 -2
- package/dist/services/uploadService.d.ts +1 -1
- package/dist/template/re-shake-share/package.json +2 -2
- package/package.json +18 -11
- package/dist/frontend/static/js/async/873.6ccd5409.js +0 -2
- /package/dist/frontend/static/js/{954.dfe166a3.js.LICENSE.txt → 296.084d1b43.js.LICENSE.txt} +0 -0
- /package/dist/frontend/static/js/async/{987.6bf8e9b0.js.LICENSE.txt → 987.86ff6794.js.LICENSE.txt} +0 -0
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
|
|
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
|
-
|
|
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, '
|
|
231
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
489
|
+
await promises.mkdir(node_path.dirname(destPath), {
|
|
469
490
|
recursive: true
|
|
470
491
|
});
|
|
471
|
-
await
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
304
|
-
var
|
|
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
|
-
|
|
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, '
|
|
419
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
680
|
+
await promises_default().mkdir(external_node_path_default().dirname(destPath), {
|
|
657
681
|
recursive: true
|
|
658
682
|
});
|
|
659
|
-
await
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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',
|