react-native-bundle-discovery-ui 2.8.0 → 2.9.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/CHANGELOG.md +34 -0
- package/README.md +12 -255
- package/assets/global.css +1606 -105
- package/dist/bin.js +63 -20
- package/dist/client/fonts.js +45 -0
- package/dist/client/issues.js +5 -0
- package/dist/client/pages/_common.js +279 -98
- package/dist/client/pages/_module.js +347 -0
- package/dist/client/pages/default.js +144 -63
- package/dist/client/pages/module.js +2 -309
- package/dist/client/pages/moduleDiff.js +76 -0
- package/dist/client/pages/package.js +172 -154
- package/dist/client/prepare.js +15 -0
- package/dist/client/queryHelpers.js +487 -91
- package/dist/client/sizeHeat.js +7 -0
- package/dist/client/views/{_theme.js → _colorScheme.js} +2 -9
- package/dist/client/views/_html.js +52 -0
- package/dist/client/views/codeDiff.js +66 -0
- package/dist/client/views/compare.js +237 -0
- package/dist/client/views/importGraph.js +255 -0
- package/dist/client/views/insights.js +81 -0
- package/dist/client/views/moduleOverview.js +122 -0
- package/dist/client/views/packageOverview.js +116 -0
- package/dist/client/views/persistedFilterInput.js +27 -0
- package/dist/client/views/reportBar.js +89 -0
- package/dist/client/views/treemap.js +598 -0
- package/dist/discoveryrc.js +27 -7
- package/dist/rsdoctor.js +19 -99
- package/dist/src-DV3VzcQY.js +1548 -0
- package/package.json +9 -5
- package/assets/highcharts.css +0 -1
- package/dist/client/_virtual/_rolldown/runtime.js +0 -37
- package/dist/client/views/foamtree.js +0 -103
- package/dist/client/views/highcharts.js +0 -33
- package/dist/src-DPAqywOJ.js +0 -201
- package/vendors/foamtree.js +0 -9008
- package/vendors/high-contrast-dark.js +0 -1
- package/vendors/highcharts-exporting.js +0 -13
- package/vendors/highcharts-networkgraph.js +0 -1
- package/vendors/highcharts.js +0 -10
|
@@ -1,4 +1,56 @@
|
|
|
1
|
+
const require_sizeHeat = require("./sizeHeat.js");
|
|
1
2
|
//#region src/client/queryHelpers.ts
|
|
3
|
+
const HIGH_SEVERITY_SHARE = 0.01;
|
|
4
|
+
const TOP_LIST_SIZE = 8;
|
|
5
|
+
const EXT_COLORS = {
|
|
6
|
+
js: "#d4a72c",
|
|
7
|
+
jsx: "#0ea5c6",
|
|
8
|
+
mjs: "#84a62b",
|
|
9
|
+
cjs: "#a3872b",
|
|
10
|
+
ts: "#3178c6",
|
|
11
|
+
tsx: "#2b8a89",
|
|
12
|
+
json: "#e0503a",
|
|
13
|
+
svg: "#c27a0e",
|
|
14
|
+
css: "#7a4fc0",
|
|
15
|
+
png: "#c2347a",
|
|
16
|
+
jpg: "#a33aa8",
|
|
17
|
+
jpeg: "#a33aa8",
|
|
18
|
+
gif: "#5a63d6",
|
|
19
|
+
webp: "#16936b",
|
|
20
|
+
ttf: "#8a6a4f",
|
|
21
|
+
otf: "#8a6a4f",
|
|
22
|
+
wasm: "#654ff0",
|
|
23
|
+
};
|
|
24
|
+
const isNodeModule = (m) => m.path.includes("node_modules/");
|
|
25
|
+
const moduleSize = (m) => m.output.sizeInBytes;
|
|
26
|
+
function toListModule(m) {
|
|
27
|
+
return {
|
|
28
|
+
ext: helpers.getFileExtension(m.path),
|
|
29
|
+
name: m.path,
|
|
30
|
+
size: helpers.formatBytes(moduleSize(m)),
|
|
31
|
+
sizeInBytes: moduleSize(m),
|
|
32
|
+
isEntry: !!m.isEntry,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function stripVersion(id) {
|
|
36
|
+
const at = id.lastIndexOf("@");
|
|
37
|
+
return at > 0 ? id.slice(0, at) : id;
|
|
38
|
+
}
|
|
39
|
+
function getFindingPackageNames(packages) {
|
|
40
|
+
if (!packages) return [];
|
|
41
|
+
if (typeof packages === "string")
|
|
42
|
+
return [packages.split(/ x\d+:/)[0].trim()].filter(Boolean);
|
|
43
|
+
return Array.from(new Set(packages.map(stripVersion)));
|
|
44
|
+
}
|
|
45
|
+
const modulesByAbsolutePath = /* @__PURE__ */ new WeakMap();
|
|
46
|
+
function getModulesByAbsolutePath(report) {
|
|
47
|
+
let map = modulesByAbsolutePath.get(report);
|
|
48
|
+
if (!map) {
|
|
49
|
+
map = new Map(report.modules.map((m) => [m.absolutePath, m]));
|
|
50
|
+
modulesByAbsolutePath.set(report, map);
|
|
51
|
+
}
|
|
52
|
+
return map;
|
|
53
|
+
}
|
|
2
54
|
const helpers = {
|
|
3
55
|
prettifyMap: /* @__PURE__ */ new Map(),
|
|
4
56
|
prettifyJS(sourceStr) {
|
|
@@ -20,16 +72,6 @@ const helpers = {
|
|
|
20
72
|
return sourceStr;
|
|
21
73
|
});
|
|
22
74
|
},
|
|
23
|
-
askChatGPTAboutPackages() {
|
|
24
|
-
return `https://chat.openai.com/?prompt=${encodeURIComponent(`I have a list of JavaScript packages from my React Native bundle (see below). I want to optimize bundle size by identifying similar or redundant packages — such as multiple versions of similar libraries (e.g., lodash, lodash-es, underscore, etc.), duplicate utilities, or overlapping functionality (e.g., date libraries like moment, dayjs, date-fns).
|
|
25
|
-
|
|
26
|
-
Please do the following:
|
|
27
|
-
|
|
28
|
-
1. Group similar or overlapping packages together.
|
|
29
|
-
2. For each group, suggest which one to keep and which ones to consider removing.
|
|
30
|
-
3. For each group, provide a regex string that can be used to filter those packages from the list (e.g., in grep, find, or search tools).
|
|
31
|
-
4. Keep the output concise and copy-paste friendly.`)}`;
|
|
32
|
-
},
|
|
33
75
|
plural(count, [singular, plural]) {
|
|
34
76
|
return count === 1 ? singular : plural;
|
|
35
77
|
},
|
|
@@ -79,6 +121,7 @@ Please do the following:
|
|
|
79
121
|
const result = [];
|
|
80
122
|
let entryPointPath = null;
|
|
81
123
|
const data = [];
|
|
124
|
+
const nodes = [];
|
|
82
125
|
while (queue.length > 0) {
|
|
83
126
|
const { module: currentModule, parentId, level } = queue.shift();
|
|
84
127
|
if (level >= Number(maxParentDepth)) break;
|
|
@@ -87,6 +130,15 @@ Please do the following:
|
|
|
87
130
|
if (visited.has(id)) continue;
|
|
88
131
|
visited.add(id);
|
|
89
132
|
}
|
|
133
|
+
nodes.push({
|
|
134
|
+
id,
|
|
135
|
+
size: currentModule.output.sizeInBytes,
|
|
136
|
+
level,
|
|
137
|
+
isCurrent: level === 0,
|
|
138
|
+
isRoot:
|
|
139
|
+
!!currentModule.isEntry || currentModule.dependents.length === 0,
|
|
140
|
+
isNodeModule: id.includes("node_modules/"),
|
|
141
|
+
});
|
|
90
142
|
if (parentId) {
|
|
91
143
|
const isEntryPoint = currentModule.dependents.length === 0;
|
|
92
144
|
result.push({
|
|
@@ -112,22 +164,40 @@ Please do the following:
|
|
|
112
164
|
break;
|
|
113
165
|
}
|
|
114
166
|
}
|
|
167
|
+
const links = data.map(([parentId, id]) => ({
|
|
168
|
+
source: id,
|
|
169
|
+
target: parentId,
|
|
170
|
+
}));
|
|
115
171
|
return {
|
|
116
172
|
entryPointPath,
|
|
117
173
|
data,
|
|
174
|
+
nodes,
|
|
175
|
+
links,
|
|
118
176
|
};
|
|
119
177
|
},
|
|
120
178
|
getExtColor(extName) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
179
|
+
const known = EXT_COLORS[extName];
|
|
180
|
+
if (known) return known;
|
|
181
|
+
let hash = 0;
|
|
182
|
+
for (const char of String(extName))
|
|
183
|
+
hash = (hash * 31 + char.charCodeAt(0)) % 360;
|
|
184
|
+
return `hsl(${hash} 55% 45%)`;
|
|
185
|
+
},
|
|
186
|
+
withSizeHeat(items, field) {
|
|
187
|
+
const top = items
|
|
188
|
+
.map((item) => item[field])
|
|
189
|
+
.filter((size) => size > require_sizeHeat.SIZE_HEAT_MIN)
|
|
190
|
+
.sort((a, b) => b - a)
|
|
191
|
+
.slice(0, 3);
|
|
192
|
+
return items.map((item) => {
|
|
193
|
+
const rank = top.indexOf(item[field]);
|
|
194
|
+
return rank === -1
|
|
195
|
+
? item
|
|
196
|
+
: {
|
|
197
|
+
...item,
|
|
198
|
+
sizeHeat: 3 - rank,
|
|
199
|
+
};
|
|
200
|
+
});
|
|
131
201
|
},
|
|
132
202
|
getFileExtension(filename) {
|
|
133
203
|
const idx = filename.lastIndexOf(".");
|
|
@@ -163,6 +233,387 @@ Please do the following:
|
|
|
163
233
|
i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
164
234
|
return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
|
|
165
235
|
},
|
|
236
|
+
moduleOverview(report, id) {
|
|
237
|
+
const module = report.modules.find((m) => m.path === id);
|
|
238
|
+
if (!module) return null;
|
|
239
|
+
const size = (m) => m.output.sizeInBytes;
|
|
240
|
+
const totalSize = report.modules.reduce((acc, m) => acc + size(m), 0);
|
|
241
|
+
const largest = report.modules.reduce(
|
|
242
|
+
(acc, m) => Math.max(acc, size(m)),
|
|
243
|
+
0,
|
|
244
|
+
);
|
|
245
|
+
const rank =
|
|
246
|
+
report.modules.filter((m) => size(m) > size(module)).length + 1;
|
|
247
|
+
const byAbsolutePath = new Map(
|
|
248
|
+
report.modules.map((m) => [m.absolutePath, m]),
|
|
249
|
+
);
|
|
250
|
+
const slash = module.path.lastIndexOf("/");
|
|
251
|
+
const toModule = (m) => ({
|
|
252
|
+
ext: helpers.getFileExtension(m.path),
|
|
253
|
+
name: m.path,
|
|
254
|
+
size: helpers.formatBytes(size(m)),
|
|
255
|
+
sizeInBytes: size(m),
|
|
256
|
+
isEntry: m.isEntry,
|
|
257
|
+
});
|
|
258
|
+
let pkg = null;
|
|
259
|
+
if (module.path.includes("node_modules/")) {
|
|
260
|
+
const name = helpers.getModulesName(module.path);
|
|
261
|
+
const marker = `node_modules/${name}/`;
|
|
262
|
+
const path = module.path.slice(
|
|
263
|
+
0,
|
|
264
|
+
module.path.lastIndexOf(marker) + marker.length - 1,
|
|
265
|
+
);
|
|
266
|
+
const info = report.packages.find((p) => p.path === path);
|
|
267
|
+
const files = report.modules.filter((m) => m.path.startsWith(`${path}/`));
|
|
268
|
+
const pkgSize = files.reduce((acc, m) => acc + size(m), 0);
|
|
269
|
+
pkg = {
|
|
270
|
+
name,
|
|
271
|
+
path,
|
|
272
|
+
version: info?.version,
|
|
273
|
+
metadata: info?.metadata,
|
|
274
|
+
size: pkgSize,
|
|
275
|
+
files: files.length,
|
|
276
|
+
share: pkgSize ? size(module) / pkgSize : 0,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
const imports = module.dependencies
|
|
280
|
+
.map((d) => {
|
|
281
|
+
const m = byAbsolutePath.get(d.absolutePath);
|
|
282
|
+
return m
|
|
283
|
+
? {
|
|
284
|
+
specifier: d.name,
|
|
285
|
+
...toModule(m),
|
|
286
|
+
missing: false,
|
|
287
|
+
}
|
|
288
|
+
: {
|
|
289
|
+
specifier: d.name,
|
|
290
|
+
ext: helpers.getFileExtension(d.path),
|
|
291
|
+
name: d.path,
|
|
292
|
+
size: "",
|
|
293
|
+
sizeInBytes: -1,
|
|
294
|
+
isEntry: false,
|
|
295
|
+
missing: true,
|
|
296
|
+
};
|
|
297
|
+
})
|
|
298
|
+
.sort((a, b) => b.sizeInBytes - a.sizeInBytes);
|
|
299
|
+
const importerPackages = new Set(
|
|
300
|
+
module.dependents.map((m) =>
|
|
301
|
+
m.path.includes("node_modules/") ? helpers.getModulesName(m.path) : "",
|
|
302
|
+
),
|
|
303
|
+
);
|
|
304
|
+
return {
|
|
305
|
+
path: module.path,
|
|
306
|
+
dir: module.path.slice(0, slash + 1),
|
|
307
|
+
file: module.path.slice(slash + 1),
|
|
308
|
+
ext: helpers.getFileExtension(module.path),
|
|
309
|
+
isEntry: !!module.isEntry,
|
|
310
|
+
injectedBy:
|
|
311
|
+
module.path === "__runtime__"
|
|
312
|
+
? "Webpack/Rspack"
|
|
313
|
+
: module.path === "__prelude__" ||
|
|
314
|
+
module.path.includes("@react-native/js-polyfills") ||
|
|
315
|
+
module.path.includes("metro-runtime")
|
|
316
|
+
? "Metro"
|
|
317
|
+
: null,
|
|
318
|
+
outputSize: size(module),
|
|
319
|
+
sourceSize: module.source.sizeInBytes,
|
|
320
|
+
outputLines: module.output.lineCount,
|
|
321
|
+
sourceLines: module.source.lineCount,
|
|
322
|
+
totalSize,
|
|
323
|
+
bundleShare: totalSize ? size(module) / totalSize : 0,
|
|
324
|
+
largestShare: largest ? size(module) / largest : 0,
|
|
325
|
+
rank,
|
|
326
|
+
modulesCount: report.modules.length,
|
|
327
|
+
pkg,
|
|
328
|
+
imports,
|
|
329
|
+
missingImports: imports.filter((i) => i.missing).length,
|
|
330
|
+
importedBy: module.dependents.length,
|
|
331
|
+
importerPackages: importerPackages.size,
|
|
332
|
+
importedByOwnCode: importerPackages.has(""),
|
|
333
|
+
duplicates: module.duplicates.length,
|
|
334
|
+
duplicatesSize: module.duplicates.reduce((acc, m) => acc + size(m), 0),
|
|
335
|
+
};
|
|
336
|
+
},
|
|
337
|
+
reportSummary(report) {
|
|
338
|
+
let totalSize = 0;
|
|
339
|
+
let nodeModulesSize = 0;
|
|
340
|
+
for (const m of report.modules) {
|
|
341
|
+
totalSize += moduleSize(m);
|
|
342
|
+
if (isNodeModule(m)) nodeModulesSize += moduleSize(m);
|
|
343
|
+
}
|
|
344
|
+
const options = report.transformOptions;
|
|
345
|
+
return {
|
|
346
|
+
platform: options?.platform ?? null,
|
|
347
|
+
dev: typeof options?.dev === "boolean" ? options.dev : null,
|
|
348
|
+
minify: typeof options?.minify === "boolean" ? options.minify : null,
|
|
349
|
+
totalSize,
|
|
350
|
+
sourceSize: totalSize - nodeModulesSize,
|
|
351
|
+
nodeModulesSize,
|
|
352
|
+
date: report.date ?? null,
|
|
353
|
+
};
|
|
354
|
+
},
|
|
355
|
+
bundleInsights(report) {
|
|
356
|
+
const totalSize = report.modules.reduce((acc, m) => acc + moduleSize(m), 0);
|
|
357
|
+
const share = (bytes) => (totalSize ? bytes / totalSize : 0);
|
|
358
|
+
const modulesByPath = new Map(report.modules.map((m) => [m.path, m]));
|
|
359
|
+
const packages = /* @__PURE__ */ new Map();
|
|
360
|
+
let nodeModulesSize = 0;
|
|
361
|
+
const ownModules = [];
|
|
362
|
+
for (const m of report.modules) {
|
|
363
|
+
if (!isNodeModule(m)) {
|
|
364
|
+
ownModules.push(m);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
nodeModulesSize += moduleSize(m);
|
|
368
|
+
const name = helpers.getModulesName(m.path);
|
|
369
|
+
const marker = `node_modules/${name}/`;
|
|
370
|
+
const pkg = packages.get(name) ?? {
|
|
371
|
+
size: 0,
|
|
372
|
+
copies: /* @__PURE__ */ new Set(),
|
|
373
|
+
};
|
|
374
|
+
pkg.size += moduleSize(m);
|
|
375
|
+
pkg.copies.add(m.path.slice(0, m.path.lastIndexOf(marker)));
|
|
376
|
+
packages.set(name, pkg);
|
|
377
|
+
}
|
|
378
|
+
const sourceSize = totalSize - nodeModulesSize;
|
|
379
|
+
const largestPackage = Math.max(
|
|
380
|
+
0,
|
|
381
|
+
...[...packages.values()].map((p) => p.size),
|
|
382
|
+
);
|
|
383
|
+
const largestOwnModule = Math.max(0, ...ownModules.map(moduleSize));
|
|
384
|
+
const countedModules = /* @__PURE__ */ new Set();
|
|
385
|
+
let potentialSavings = 0;
|
|
386
|
+
const findings = (report.recommendations ?? [])
|
|
387
|
+
.map((finding) => {
|
|
388
|
+
const savings = finding.sizeInBytes ?? 0;
|
|
389
|
+
const affected = (finding.modules ?? [])
|
|
390
|
+
.map((path) => modulesByPath.get(path))
|
|
391
|
+
.filter((m) => !!m)
|
|
392
|
+
.sort((a, b) => moduleSize(b) - moduleSize(a));
|
|
393
|
+
const severity =
|
|
394
|
+
savings && share(savings) >= HIGH_SEVERITY_SHARE
|
|
395
|
+
? "high"
|
|
396
|
+
: savings
|
|
397
|
+
? "medium"
|
|
398
|
+
: "low";
|
|
399
|
+
return {
|
|
400
|
+
...finding,
|
|
401
|
+
severity,
|
|
402
|
+
savings,
|
|
403
|
+
savingsShare: share(savings),
|
|
404
|
+
packageNames: getFindingPackageNames(finding.packages),
|
|
405
|
+
packagesText:
|
|
406
|
+
typeof finding.packages === "string" ? finding.packages : null,
|
|
407
|
+
docsUrls: [finding.docsUrl ?? []].flat(),
|
|
408
|
+
affected: affected.map(toListModule),
|
|
409
|
+
affectedSize: affected.reduce((acc, m) => acc + moduleSize(m), 0),
|
|
410
|
+
};
|
|
411
|
+
})
|
|
412
|
+
.sort((a, b) => b.savings - a.savings);
|
|
413
|
+
for (const finding of findings) {
|
|
414
|
+
if (!finding.savings) continue;
|
|
415
|
+
const overlap = finding.affected
|
|
416
|
+
.filter((m) => countedModules.has(m.name))
|
|
417
|
+
.reduce((acc, m) => acc + m.sizeInBytes, 0);
|
|
418
|
+
potentialSavings += Math.max(finding.savings - overlap, 0);
|
|
419
|
+
for (const m of finding.affected) countedModules.add(m.name);
|
|
420
|
+
}
|
|
421
|
+
const duplicatePackages = [...packages.values()].filter(
|
|
422
|
+
(p) => p.copies.size > 1,
|
|
423
|
+
).length;
|
|
424
|
+
return {
|
|
425
|
+
platform: report.transformOptions?.platform,
|
|
426
|
+
isDev: report.transformOptions?.dev !== false,
|
|
427
|
+
hasRecommendations: report.recommendations !== null,
|
|
428
|
+
totalSize,
|
|
429
|
+
modulesCount: report.modules.length,
|
|
430
|
+
sourceSize,
|
|
431
|
+
sourceShare: share(sourceSize),
|
|
432
|
+
ownModulesCount: ownModules.length,
|
|
433
|
+
nodeModulesSize,
|
|
434
|
+
nodeModulesShare: share(nodeModulesSize),
|
|
435
|
+
packagesCount: packages.size,
|
|
436
|
+
duplicatePackages,
|
|
437
|
+
duplicateModules: report.modules.filter((m) => m.duplicates.length)
|
|
438
|
+
.length,
|
|
439
|
+
potentialSavings,
|
|
440
|
+
potentialSavingsShare: share(potentialSavings),
|
|
441
|
+
findings,
|
|
442
|
+
findingsWithSavings: findings.filter((f) => f.savings).length,
|
|
443
|
+
topPackages: [...packages.entries()]
|
|
444
|
+
.sort((a, b) => b[1].size - a[1].size)
|
|
445
|
+
.slice(0, TOP_LIST_SIZE)
|
|
446
|
+
.map(([name, pkg]) => ({
|
|
447
|
+
name,
|
|
448
|
+
size: pkg.size,
|
|
449
|
+
share: share(pkg.size),
|
|
450
|
+
bar: largestPackage ? pkg.size / largestPackage : 0,
|
|
451
|
+
copies: pkg.copies.size,
|
|
452
|
+
})),
|
|
453
|
+
topPackagesShare: share(
|
|
454
|
+
[...packages.values()]
|
|
455
|
+
.map((p) => p.size)
|
|
456
|
+
.sort((a, b) => b - a)
|
|
457
|
+
.slice(0, 5)
|
|
458
|
+
.reduce((acc, size) => acc + size, 0),
|
|
459
|
+
),
|
|
460
|
+
topOwnModules: [...ownModules]
|
|
461
|
+
.sort((a, b) => moduleSize(b) - moduleSize(a))
|
|
462
|
+
.slice(0, TOP_LIST_SIZE)
|
|
463
|
+
.map((m) => ({
|
|
464
|
+
name: m.path,
|
|
465
|
+
size: moduleSize(m),
|
|
466
|
+
share: share(moduleSize(m)),
|
|
467
|
+
bar: largestOwnModule ? moduleSize(m) / largestOwnModule : 0,
|
|
468
|
+
})),
|
|
469
|
+
};
|
|
470
|
+
},
|
|
471
|
+
packageOverview(report, name) {
|
|
472
|
+
const isOwn = (m) =>
|
|
473
|
+
isNodeModule(m) && helpers.getModulesName(m.path) === name;
|
|
474
|
+
const files = report.modules.filter(isOwn);
|
|
475
|
+
if (files.length === 0) return null;
|
|
476
|
+
const marker = `node_modules/${name}/`;
|
|
477
|
+
const instancePath = (m) =>
|
|
478
|
+
m.path.slice(0, m.path.lastIndexOf(marker) + marker.length - 1);
|
|
479
|
+
const instances = /* @__PURE__ */ new Map();
|
|
480
|
+
for (const m of files) {
|
|
481
|
+
const path = instancePath(m);
|
|
482
|
+
instances.set(path, [...(instances.get(path) ?? []), m]);
|
|
483
|
+
}
|
|
484
|
+
const totalSize = report.modules.reduce((acc, m) => acc + moduleSize(m), 0);
|
|
485
|
+
const size = files.reduce((acc, m) => acc + moduleSize(m), 0);
|
|
486
|
+
const packageSizes = /* @__PURE__ */ new Map();
|
|
487
|
+
for (const m of report.modules) {
|
|
488
|
+
if (!isNodeModule(m)) continue;
|
|
489
|
+
const pkgName = helpers.getModulesName(m.path);
|
|
490
|
+
packageSizes.set(
|
|
491
|
+
pkgName,
|
|
492
|
+
(packageSizes.get(pkgName) ?? 0) + moduleSize(m),
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
const largestPackage = Math.max(...packageSizes.values());
|
|
496
|
+
const rank = [...packageSizes.values()].filter((s) => s > size).length + 1;
|
|
497
|
+
const copies = [...instances.entries()]
|
|
498
|
+
.map(([path, modules]) => {
|
|
499
|
+
const info = report.packages.find((p) => p.path === path);
|
|
500
|
+
return {
|
|
501
|
+
path,
|
|
502
|
+
version: info?.version,
|
|
503
|
+
metadata: info?.metadata ?? null,
|
|
504
|
+
size: modules.reduce((acc, m) => acc + moduleSize(m), 0),
|
|
505
|
+
files: modules.length,
|
|
506
|
+
};
|
|
507
|
+
})
|
|
508
|
+
.sort((a, b) => b.size - a.size);
|
|
509
|
+
const fileSet = new Set(files);
|
|
510
|
+
const importers = /* @__PURE__ */ new Map();
|
|
511
|
+
for (const m of files)
|
|
512
|
+
for (const dependent of m.dependents) {
|
|
513
|
+
if (fileSet.has(dependent)) continue;
|
|
514
|
+
importers.set(
|
|
515
|
+
dependent,
|
|
516
|
+
(importers.get(dependent) ?? /* @__PURE__ */ new Set()).add(m),
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
const importerPackages = new Set(
|
|
520
|
+
[...importers.keys()].map((m) =>
|
|
521
|
+
isNodeModule(m) ? helpers.getModulesName(m.path) : "",
|
|
522
|
+
),
|
|
523
|
+
);
|
|
524
|
+
const largestFile = files.reduce((a, b) =>
|
|
525
|
+
moduleSize(b) > moduleSize(a) ? b : a,
|
|
526
|
+
);
|
|
527
|
+
const withMetadata = copies.find((c) => c.metadata)?.metadata ?? null;
|
|
528
|
+
const findings = (report.recommendations ?? []).filter((f) =>
|
|
529
|
+
getFindingPackageNames(f.packages).includes(name),
|
|
530
|
+
);
|
|
531
|
+
return {
|
|
532
|
+
name,
|
|
533
|
+
paths: copies.map((c) => c.path),
|
|
534
|
+
versions: Array.from(
|
|
535
|
+
new Set(copies.map((c) => c.version).filter(Boolean)),
|
|
536
|
+
),
|
|
537
|
+
size,
|
|
538
|
+
bundleShare: totalSize ? size / totalSize : 0,
|
|
539
|
+
largestShare: largestPackage ? size / largestPackage : 0,
|
|
540
|
+
rank,
|
|
541
|
+
packagesCount: packageSizes.size,
|
|
542
|
+
filesCount: files.length,
|
|
543
|
+
largestFile: largestFile.path,
|
|
544
|
+
largestFileSize: moduleSize(largestFile),
|
|
545
|
+
copies,
|
|
546
|
+
copiesSavings: size - copies[0].size,
|
|
547
|
+
deprecated: copies.filter((c) => c.metadata?.deprecated),
|
|
548
|
+
latestVersion: withMetadata?.latestVersion ?? null,
|
|
549
|
+
isOutdated: copies.some((c) => c.metadata && !c.metadata.isLatest),
|
|
550
|
+
publishedAt: withMetadata?.createdAt ?? null,
|
|
551
|
+
importedBy: importers.size,
|
|
552
|
+
importerPackages:
|
|
553
|
+
importerPackages.size - (importerPackages.has("") ? 1 : 0),
|
|
554
|
+
importedByOwnCode: importerPackages.has(""),
|
|
555
|
+
importers: [...importers.entries()]
|
|
556
|
+
.map(([m, imported]) => ({
|
|
557
|
+
...toListModule(m),
|
|
558
|
+
imports: [...imported].map(toListModule),
|
|
559
|
+
}))
|
|
560
|
+
.sort((a, b) => b.sizeInBytes - a.sizeInBytes),
|
|
561
|
+
findings: findings.map((f) => ({
|
|
562
|
+
id: f.id,
|
|
563
|
+
title: f.title,
|
|
564
|
+
})),
|
|
565
|
+
};
|
|
566
|
+
},
|
|
567
|
+
importChain(report, pkgName) {
|
|
568
|
+
const isTarget = (m) =>
|
|
569
|
+
isNodeModule(m) && helpers.getModulesName(m.path) === pkgName;
|
|
570
|
+
const byAbsolutePath = getModulesByAbsolutePath(report);
|
|
571
|
+
const search = (starts) => {
|
|
572
|
+
const prev = /* @__PURE__ */ new Map();
|
|
573
|
+
const queue = [...starts];
|
|
574
|
+
for (const start of starts) prev.set(start, null);
|
|
575
|
+
while (queue.length) {
|
|
576
|
+
const current = queue.shift();
|
|
577
|
+
if (isTarget(current)) {
|
|
578
|
+
const chain = [];
|
|
579
|
+
for (let m = current; m; m = prev.get(m) ?? null) chain.unshift(m);
|
|
580
|
+
return chain;
|
|
581
|
+
}
|
|
582
|
+
for (const dependency of current.dependencies) {
|
|
583
|
+
const next = byAbsolutePath.get(dependency.absolutePath);
|
|
584
|
+
if (next && !prev.has(next)) {
|
|
585
|
+
prev.set(next, current);
|
|
586
|
+
queue.push(next);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return null;
|
|
591
|
+
};
|
|
592
|
+
let chain = search(report.modules.filter((m) => m.isEntry));
|
|
593
|
+
let fromEntry = true;
|
|
594
|
+
if (!chain) {
|
|
595
|
+
chain = search(report.modules.filter((m) => m.dependents.length === 0));
|
|
596
|
+
fromEntry = false;
|
|
597
|
+
}
|
|
598
|
+
if (!chain) return null;
|
|
599
|
+
return {
|
|
600
|
+
fromEntry,
|
|
601
|
+
steps: chain.map((m, index) => {
|
|
602
|
+
const pkg = isNodeModule(m) ? helpers.getModulesName(m.path) : null;
|
|
603
|
+
const prevModule = chain[index - 1];
|
|
604
|
+
const prevPkg =
|
|
605
|
+
prevModule && isNodeModule(prevModule)
|
|
606
|
+
? helpers.getModulesName(prevModule.path)
|
|
607
|
+
: null;
|
|
608
|
+
return {
|
|
609
|
+
...toListModule(m),
|
|
610
|
+
pkg,
|
|
611
|
+
entersPackage: index > 0 && pkg !== prevPkg ? pkg : null,
|
|
612
|
+
isTarget: index === chain.length - 1,
|
|
613
|
+
};
|
|
614
|
+
}),
|
|
615
|
+
};
|
|
616
|
+
},
|
|
166
617
|
isPackageImport(moduleName) {
|
|
167
618
|
return moduleName?.[0] !== ".";
|
|
168
619
|
},
|
|
@@ -175,7 +626,7 @@ Please do the following:
|
|
|
175
626
|
children: {},
|
|
176
627
|
size: 0,
|
|
177
628
|
};
|
|
178
|
-
files.forEach(({ path, size }) => {
|
|
629
|
+
files.forEach(({ path, size, issues }) => {
|
|
179
630
|
if (path === "__prelude__") path = "node_modules/__prelude__";
|
|
180
631
|
if (path === "__runtime__") path = "node_modules/__runtime__";
|
|
181
632
|
const shortPath = path.replace(`${rootFolder}/`, "");
|
|
@@ -192,79 +643,26 @@ Please do the following:
|
|
|
192
643
|
current[part].size = size;
|
|
193
644
|
current[part].path = shortPath;
|
|
194
645
|
current[part].fullPath = path;
|
|
646
|
+
current[part].issues = issues;
|
|
195
647
|
}
|
|
196
648
|
current = current[part].children;
|
|
197
649
|
});
|
|
198
650
|
});
|
|
199
|
-
if (type === "
|
|
651
|
+
if (type === "treemap") {
|
|
200
652
|
sumSizes(nodeModulesMap);
|
|
201
653
|
sumSizes(sourceCodeMap);
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
(acc, group) => acc + group.weight,
|
|
211
|
-
0,
|
|
212
|
-
);
|
|
213
|
-
return topLevelNode;
|
|
214
|
-
}
|
|
215
|
-
if (type === "highcharts-treemap") {
|
|
216
|
-
const ROOT_ID_1 = "~";
|
|
217
|
-
const ROOT_ID_2 = ".";
|
|
218
|
-
return [
|
|
219
|
-
{
|
|
220
|
-
id: ROOT_ID_1,
|
|
221
|
-
name: "node_modules",
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
id: ROOT_ID_2,
|
|
225
|
-
name: "Source Code",
|
|
226
|
-
},
|
|
227
|
-
]
|
|
228
|
-
.concat(flattenTree(nodeModulesMap.children.node_modules, ROOT_ID_1))
|
|
229
|
-
.concat(flattenTree(sourceCodeMap, ROOT_ID_2));
|
|
654
|
+
const roots = [];
|
|
655
|
+
if (Object.keys(sourceCodeMap.children).length > 0)
|
|
656
|
+
roots.push(toTreemapNode(sourceCodeMap, "Source Code"));
|
|
657
|
+
if (nodeModulesMap.children.node_modules)
|
|
658
|
+
roots.push(
|
|
659
|
+
toTreemapNode(nodeModulesMap.children.node_modules, "node_modules"),
|
|
660
|
+
);
|
|
661
|
+
return roots;
|
|
230
662
|
}
|
|
231
663
|
throw new Error(`Unsupported type: ${type}`);
|
|
232
664
|
},
|
|
233
665
|
};
|
|
234
|
-
function flattenTree(
|
|
235
|
-
node,
|
|
236
|
-
parentId,
|
|
237
|
-
prevWasSkipped = false,
|
|
238
|
-
lvl = 0,
|
|
239
|
-
result = [],
|
|
240
|
-
overrideParentId,
|
|
241
|
-
) {
|
|
242
|
-
const nodeChildrenCount = Object.keys(node.children);
|
|
243
|
-
for (const key of nodeChildrenCount) {
|
|
244
|
-
const childNode = node.children[key];
|
|
245
|
-
const childId = `${parentId}/${key}`;
|
|
246
|
-
const childrenCount = Object.keys(childNode.children).length;
|
|
247
|
-
const hasChildren = childrenCount > 0;
|
|
248
|
-
const item = {
|
|
249
|
-
id: childId,
|
|
250
|
-
name: prevWasSkipped ? parentId : key,
|
|
251
|
-
parent: overrideParentId ?? parentId,
|
|
252
|
-
};
|
|
253
|
-
if (!hasChildren) item.value = childNode.size;
|
|
254
|
-
if (lvl === 0) item.color = Highcharts.getOptions().colors[randomInt(0, 9)];
|
|
255
|
-
const skipThisNode = nodeChildrenCount.length === 1 && childrenCount === 1;
|
|
256
|
-
if (!skipThisNode) result.push(item);
|
|
257
|
-
flattenTree(
|
|
258
|
-
childNode,
|
|
259
|
-
childId,
|
|
260
|
-
skipThisNode,
|
|
261
|
-
lvl + 1,
|
|
262
|
-
result,
|
|
263
|
-
skipThisNode ? (overrideParentId ?? parentId) : void 0,
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
return result;
|
|
267
|
-
}
|
|
268
666
|
function sumSizes(node) {
|
|
269
667
|
const isFile = Object.keys(node.children).length === 0;
|
|
270
668
|
let totalFiles = isFile ? 1 : 0;
|
|
@@ -282,24 +680,22 @@ function sumSizes(node) {
|
|
|
282
680
|
totalFiles,
|
|
283
681
|
};
|
|
284
682
|
}
|
|
285
|
-
function
|
|
683
|
+
function toTreemapNode(node, name) {
|
|
286
684
|
const keys = Object.keys(node.children);
|
|
287
685
|
const common = {
|
|
288
|
-
|
|
289
|
-
|
|
686
|
+
name,
|
|
687
|
+
value: node.size,
|
|
290
688
|
fullPath: node.fullPath,
|
|
291
689
|
files: node.files,
|
|
292
690
|
type: node.type,
|
|
293
691
|
size: helpers.formatBytes(node.size),
|
|
692
|
+
issues: node.issues,
|
|
294
693
|
};
|
|
295
694
|
if (keys.length === 0) return common;
|
|
296
695
|
return Object.assign(common, {
|
|
297
|
-
|
|
696
|
+
children: keys.map((key) => toTreemapNode(node.children[key], key)),
|
|
298
697
|
});
|
|
299
698
|
}
|
|
300
|
-
function randomInt(min, max) {
|
|
301
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
302
|
-
}
|
|
303
699
|
const nm = "node_modules/";
|
|
304
700
|
function shortenPath(path, nodeModulesMap) {
|
|
305
701
|
if (path.startsWith(`${nm}.pnpm/`)) path = path.replaceAll(`${nm}.pnpm/`, "");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
//#region src/client/views/
|
|
2
|
+
//#region src/client/views/_colorScheme.ts
|
|
3
3
|
function isDark() {
|
|
4
4
|
const isSystemDarkMode = window?.matchMedia?.(
|
|
5
5
|
"(prefers-color-scheme: dark)",
|
|
@@ -7,12 +7,5 @@ function isDark() {
|
|
|
7
7
|
const userMode = localStorage.getItem("discoveryjs:color-scheme");
|
|
8
8
|
return userMode === "auto" ? isSystemDarkMode : userMode === "dark";
|
|
9
9
|
}
|
|
10
|
-
function doTheming(el) {
|
|
11
|
-
const dark = isDark();
|
|
12
|
-
el.classList.add("highcharts-light");
|
|
13
|
-
el.classList[!dark ? "add" : "remove"]("my-light");
|
|
14
|
-
el.classList[dark ? "add" : "remove"]("highcharts-dark");
|
|
15
|
-
}
|
|
16
|
-
if (isDark()) require("../../../vendors/high-contrast-dark.js");
|
|
17
10
|
//#endregion
|
|
18
|
-
exports.
|
|
11
|
+
exports.isDark = isDark;
|