hermex 1.1.0 → 1.1.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/README.md +81 -58
- package/dist/cli.js +122 -159
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1,27 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var lockfile = require('@yarnpkg/lockfile');
|
|
13
|
-
|
|
14
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
-
|
|
16
|
-
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
17
|
-
var chalk6__default = /*#__PURE__*/_interopDefault(chalk6);
|
|
18
|
-
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
19
|
-
var Table__default = /*#__PURE__*/_interopDefault(Table);
|
|
20
|
-
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
21
|
-
var yaml__default = /*#__PURE__*/_interopDefault(yaml);
|
|
22
|
-
var lockfile__default = /*#__PURE__*/_interopDefault(lockfile);
|
|
23
|
-
|
|
24
|
-
// src/cli.ts
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import chalk6 from 'chalk';
|
|
5
|
+
import { parseSync } from '@swc/core';
|
|
6
|
+
import fs3 from 'fs';
|
|
7
|
+
import Table from 'cli-table3';
|
|
8
|
+
import { glob } from 'glob';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import yaml from 'js-yaml';
|
|
11
|
+
import lockfile from '@yarnpkg/lockfile';
|
|
25
12
|
|
|
26
13
|
// src/swc-parser/core/state.ts
|
|
27
14
|
function createState() {
|
|
@@ -74,40 +61,37 @@ function analyzeImportDeclaration(node, state) {
|
|
|
74
61
|
}
|
|
75
62
|
}
|
|
76
63
|
function analyzeDefaultImport(spec, source, node, state) {
|
|
77
|
-
var _a;
|
|
78
64
|
const name = spec.local.value;
|
|
79
65
|
state.usagePatterns.defaultImports.add({
|
|
80
66
|
name,
|
|
81
67
|
source,
|
|
82
|
-
line:
|
|
68
|
+
line: node.span?.start || 0
|
|
83
69
|
});
|
|
84
70
|
state.componentNames.add(name);
|
|
85
71
|
}
|
|
86
72
|
function analyzeNamespaceImport(spec, source, node, state) {
|
|
87
|
-
var _a;
|
|
88
73
|
const name = spec.local.value;
|
|
89
74
|
state.usagePatterns.namespaceImports.add({
|
|
90
75
|
name,
|
|
91
76
|
source,
|
|
92
|
-
line:
|
|
77
|
+
line: node.span?.start || 0
|
|
93
78
|
});
|
|
94
79
|
state.allIdentifiers.add(name);
|
|
95
80
|
}
|
|
96
81
|
function analyzeNamedImport(spec, source, node, state) {
|
|
97
|
-
var _a, _b;
|
|
98
82
|
const importedName = spec.imported ? spec.imported.value : spec.local.value;
|
|
99
83
|
const localName = spec.local.value;
|
|
100
84
|
state.usagePatterns.namedImports.add({
|
|
101
85
|
name: importedName,
|
|
102
86
|
source,
|
|
103
|
-
line:
|
|
87
|
+
line: node.span?.start || 0
|
|
104
88
|
});
|
|
105
89
|
if (importedName !== localName) {
|
|
106
90
|
state.usagePatterns.aliasedImports.set(localName, {
|
|
107
91
|
imported: importedName,
|
|
108
92
|
local: localName,
|
|
109
93
|
source,
|
|
110
|
-
line:
|
|
94
|
+
line: node.span?.start || 0
|
|
111
95
|
});
|
|
112
96
|
}
|
|
113
97
|
state.componentNames.add(localName);
|
|
@@ -126,7 +110,7 @@ function getJSXElementName(nameNode) {
|
|
|
126
110
|
}
|
|
127
111
|
}
|
|
128
112
|
function isMemberExpressionComponent(nameNode, state) {
|
|
129
|
-
if (
|
|
113
|
+
if (nameNode?.type === "JSXMemberExpression") {
|
|
130
114
|
const objectName = getJSXElementName(nameNode.object);
|
|
131
115
|
return state.allIdentifiers.has(objectName);
|
|
132
116
|
}
|
|
@@ -135,10 +119,9 @@ function isMemberExpressionComponent(nameNode, state) {
|
|
|
135
119
|
function extractJSXProps(attributes) {
|
|
136
120
|
if (!attributes) return [];
|
|
137
121
|
return attributes.map((attr) => {
|
|
138
|
-
var _a, _b, _c;
|
|
139
122
|
if (attr.type === "JSXAttribute") {
|
|
140
123
|
return {
|
|
141
|
-
name:
|
|
124
|
+
name: attr.name?.value || attr.name?.name?.value,
|
|
142
125
|
value: extractJSXAttributeValue(attr.value)
|
|
143
126
|
};
|
|
144
127
|
}
|
|
@@ -203,7 +186,6 @@ function getUsageContext(parent) {
|
|
|
203
186
|
|
|
204
187
|
// src/swc-parser/patterns/props.ts
|
|
205
188
|
function analyzePropsInDetail(attributes, componentName, state) {
|
|
206
|
-
var _a, _b, _c;
|
|
207
189
|
const analysis = {
|
|
208
190
|
namedProps: [],
|
|
209
191
|
hasSpread: false,
|
|
@@ -214,7 +196,7 @@ function analyzePropsInDetail(attributes, componentName, state) {
|
|
|
214
196
|
if (!attributes) return analysis;
|
|
215
197
|
for (const attr of attributes) {
|
|
216
198
|
if (attr.type === "JSXAttribute") {
|
|
217
|
-
const propName =
|
|
199
|
+
const propName = attr.name?.value || attr.name?.name?.value;
|
|
218
200
|
if (propName) {
|
|
219
201
|
analysis.namedProps.push(propName);
|
|
220
202
|
const propDetail = {
|
|
@@ -296,7 +278,6 @@ function analyzeJSXElement(node, state) {
|
|
|
296
278
|
}
|
|
297
279
|
}
|
|
298
280
|
function analyzeJSXOpeningElement(node, state, parent) {
|
|
299
|
-
var _a;
|
|
300
281
|
const elementName = getJSXElementName(node.name);
|
|
301
282
|
if (!state.componentNames.has(elementName) && !isMemberExpressionComponent(node.name, state)) {
|
|
302
283
|
return;
|
|
@@ -310,7 +291,7 @@ function analyzeJSXOpeningElement(node, state, parent) {
|
|
|
310
291
|
component: elementName,
|
|
311
292
|
props: extractJSXProps(node.attributes).map((p) => p.name),
|
|
312
293
|
propsAnalysis,
|
|
313
|
-
line:
|
|
294
|
+
line: node.span?.start || 0,
|
|
314
295
|
context: getUsageContext(parent)
|
|
315
296
|
};
|
|
316
297
|
if (!state.usagePatterns.jsxUsage.has(elementName)) {
|
|
@@ -326,39 +307,37 @@ function isKnownComponent(name, state) {
|
|
|
326
307
|
|
|
327
308
|
// src/swc-parser/patterns/variables.ts
|
|
328
309
|
function analyzeVariableDeclaration(node, state) {
|
|
329
|
-
var _a, _b, _c;
|
|
330
310
|
if (!node.declarations) return;
|
|
331
311
|
for (const decl of node.declarations) {
|
|
332
|
-
if (
|
|
312
|
+
if (decl.id?.type === "Identifier") {
|
|
333
313
|
const varName = decl.id.value;
|
|
334
314
|
if (decl.init) {
|
|
335
315
|
const assignment = extractAssignmentInfo(decl.init);
|
|
336
316
|
if (assignment && isKnownComponent(assignment, state)) {
|
|
337
317
|
state.usagePatterns.variableAssignments.set(varName, {
|
|
338
318
|
assignment,
|
|
339
|
-
line:
|
|
319
|
+
line: node.span?.start || 0
|
|
340
320
|
});
|
|
341
321
|
state.componentNames.add(varName);
|
|
342
322
|
console.log(`\u{1F4DD} Variable assignment: ${varName} = ${assignment}`);
|
|
343
323
|
}
|
|
344
324
|
}
|
|
345
325
|
}
|
|
346
|
-
if (
|
|
326
|
+
if (decl.id?.type === "ObjectPattern") {
|
|
347
327
|
analyzeDestructuringPattern(decl.id, decl.init, state);
|
|
348
328
|
}
|
|
349
329
|
}
|
|
350
330
|
}
|
|
351
331
|
function analyzeDestructuringPattern(pattern, init, state) {
|
|
352
|
-
var _a, _b;
|
|
353
332
|
if (!pattern.properties) return;
|
|
354
333
|
for (const prop of pattern.properties) {
|
|
355
|
-
if (prop.type === "AssignmentPatternProperty" &&
|
|
334
|
+
if (prop.type === "AssignmentPatternProperty" && prop.key?.type === "Identifier") {
|
|
356
335
|
const propName = prop.key.value;
|
|
357
|
-
if (
|
|
336
|
+
if (init?.type === "Identifier" && state.allIdentifiers.has(init.value)) {
|
|
358
337
|
state.usagePatterns.destructuredUsage.add({
|
|
359
338
|
property: propName,
|
|
360
339
|
source: init.value,
|
|
361
|
-
line:
|
|
340
|
+
line: pattern.span?.start || 0
|
|
362
341
|
});
|
|
363
342
|
state.componentNames.add(propName);
|
|
364
343
|
console.log(`\u{1F527} Destructuring: ${propName} from ${init.value}`);
|
|
@@ -381,14 +360,13 @@ function extractAssignmentInfo(node) {
|
|
|
381
360
|
|
|
382
361
|
// src/swc-parser/patterns/conditionals.ts
|
|
383
362
|
function analyzeConditionalExpression(node, state) {
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
const alternate = ((_b = node.alternate) == null ? void 0 : _b.type) === "Identifier" ? node.alternate.value : null;
|
|
363
|
+
const consequent = node.consequent?.type === "Identifier" ? node.consequent.value : null;
|
|
364
|
+
const alternate = node.alternate?.type === "Identifier" ? node.alternate.value : null;
|
|
387
365
|
if (consequent && state.componentNames.has(consequent) || alternate && state.componentNames.has(alternate)) {
|
|
388
366
|
state.usagePatterns.conditionalUsage.add({
|
|
389
367
|
consequent: consequent || "",
|
|
390
368
|
alternate: alternate || "",
|
|
391
|
-
line:
|
|
369
|
+
line: node.span?.start || 0
|
|
392
370
|
});
|
|
393
371
|
console.log("\u{1F500} Conditional component usage found");
|
|
394
372
|
}
|
|
@@ -396,40 +374,34 @@ function analyzeConditionalExpression(node, state) {
|
|
|
396
374
|
|
|
397
375
|
// src/swc-parser/patterns/collections.ts
|
|
398
376
|
function analyzeArrayExpression(node, state) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if ((elem == null ? void 0 : elem.type) === "Identifier") {
|
|
377
|
+
const hasComponents = node.elements?.some((elem) => {
|
|
378
|
+
if (elem?.type === "Identifier") {
|
|
402
379
|
return state.componentNames.has(elem.value);
|
|
403
380
|
}
|
|
404
381
|
return false;
|
|
405
382
|
});
|
|
406
383
|
if (hasComponents) {
|
|
407
384
|
state.usagePatterns.arrayMappings.add({
|
|
408
|
-
components:
|
|
409
|
-
line:
|
|
385
|
+
components: node.elements?.map((elem) => elem?.value).filter(Boolean),
|
|
386
|
+
line: node.span?.start || 0
|
|
410
387
|
});
|
|
411
388
|
console.log("\u{1F4CB} Array with components found");
|
|
412
389
|
}
|
|
413
390
|
}
|
|
414
391
|
function analyzeObjectExpression(node, state) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
var _a2;
|
|
418
|
-
if (prop.type === "KeyValueProperty" && ((_a2 = prop.value) == null ? void 0 : _a2.type) === "Identifier") {
|
|
392
|
+
const componentProps = node.properties?.filter((prop) => {
|
|
393
|
+
if (prop.type === "KeyValueProperty" && prop.value?.type === "Identifier") {
|
|
419
394
|
return state.componentNames.has(prop.value.value);
|
|
420
395
|
}
|
|
421
396
|
return false;
|
|
422
397
|
});
|
|
423
|
-
if (
|
|
398
|
+
if (componentProps?.length > 0) {
|
|
424
399
|
state.usagePatterns.objectMappings.add({
|
|
425
|
-
mappings: componentProps.map((prop) => {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
};
|
|
431
|
-
}),
|
|
432
|
-
line: ((_b = node.span) == null ? void 0 : _b.start) || 0
|
|
400
|
+
mappings: componentProps.map((prop) => ({
|
|
401
|
+
key: prop.key?.value || "[computed]",
|
|
402
|
+
component: prop.value?.value
|
|
403
|
+
})),
|
|
404
|
+
line: node.span?.start || 0
|
|
433
405
|
});
|
|
434
406
|
console.log("\u{1F5FA}\uFE0F Object mapping with components found");
|
|
435
407
|
}
|
|
@@ -437,16 +409,15 @@ function analyzeObjectExpression(node, state) {
|
|
|
437
409
|
|
|
438
410
|
// src/swc-parser/patterns/lazy-dynamic.ts
|
|
439
411
|
function analyzeLazyImport(node, state) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if ((arg == null ? void 0 : arg.type) === "ArrowFunctionExpression" && ((_b = arg.body) == null ? void 0 : _b.type) === "CallExpression") {
|
|
412
|
+
const arg = node.arguments?.[0];
|
|
413
|
+
if (arg?.type === "ArrowFunctionExpression" && arg.body?.type === "CallExpression") {
|
|
443
414
|
const importCall = arg.body;
|
|
444
|
-
if (
|
|
445
|
-
const source =
|
|
415
|
+
if (importCall.callee?.type === "Import") {
|
|
416
|
+
const source = importCall.arguments?.[0]?.value;
|
|
446
417
|
if (source) {
|
|
447
418
|
state.usagePatterns.lazyImports.add({
|
|
448
419
|
source,
|
|
449
|
-
line:
|
|
420
|
+
line: node.span?.start || 0
|
|
450
421
|
});
|
|
451
422
|
console.log(`\u{1F504} Found lazy import: ${source}`);
|
|
452
423
|
}
|
|
@@ -454,12 +425,11 @@ function analyzeLazyImport(node, state) {
|
|
|
454
425
|
}
|
|
455
426
|
}
|
|
456
427
|
function analyzeDynamicImport(node, state) {
|
|
457
|
-
|
|
458
|
-
const source = (_b = (_a = node.arguments) == null ? void 0 : _a[0]) == null ? void 0 : _b.value;
|
|
428
|
+
const source = node.arguments?.[0]?.value;
|
|
459
429
|
if (source) {
|
|
460
430
|
state.usagePatterns.dynamicImports.add({
|
|
461
431
|
source,
|
|
462
|
-
line:
|
|
432
|
+
line: node.span?.start || 0
|
|
463
433
|
});
|
|
464
434
|
console.log(`\u26A1 Found dynamic import: ${source}`);
|
|
465
435
|
}
|
|
@@ -467,44 +437,39 @@ function analyzeDynamicImport(node, state) {
|
|
|
467
437
|
|
|
468
438
|
// src/swc-parser/patterns/advanced.ts
|
|
469
439
|
function analyzeHOCUsage(node, state) {
|
|
470
|
-
var _a, _b, _c, _d;
|
|
471
440
|
state.usagePatterns.hocUsage.add({
|
|
472
|
-
function:
|
|
473
|
-
component:
|
|
474
|
-
line:
|
|
441
|
+
function: node.callee?.value || "[unknown]",
|
|
442
|
+
component: node.arguments?.[0]?.value || "[unknown]",
|
|
443
|
+
line: node.span?.start || 0
|
|
475
444
|
});
|
|
476
445
|
console.log(`\u{1F381} HOC usage found`);
|
|
477
446
|
}
|
|
478
447
|
function analyzeMemoUsage(node, state) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if ((component == null ? void 0 : component.type) === "Identifier" && state.componentNames.has(component.value)) {
|
|
448
|
+
const component = node.arguments?.[0];
|
|
449
|
+
if (component?.type === "Identifier" && state.componentNames.has(component.value)) {
|
|
482
450
|
state.usagePatterns.memoizedComponents.add({
|
|
483
451
|
component: component.value,
|
|
484
|
-
line:
|
|
452
|
+
line: node.span?.start || 0
|
|
485
453
|
});
|
|
486
454
|
console.log(`\u{1F9E0} Memoized component: ${component.value}`);
|
|
487
455
|
}
|
|
488
456
|
}
|
|
489
457
|
function analyzeForwardRefUsage(node, state) {
|
|
490
|
-
var _a;
|
|
491
458
|
state.usagePatterns.forwardedRefs.add({
|
|
492
|
-
line:
|
|
459
|
+
line: node.span?.start || 0
|
|
493
460
|
});
|
|
494
461
|
console.log("\u2197\uFE0F ForwardRef usage found");
|
|
495
462
|
}
|
|
496
463
|
function analyzePortalUsage(node, state) {
|
|
497
|
-
var _a;
|
|
498
464
|
state.usagePatterns.portalUsage.add({
|
|
499
|
-
line:
|
|
465
|
+
line: node.span?.start || 0
|
|
500
466
|
});
|
|
501
467
|
console.log("\u{1F300} Portal usage found");
|
|
502
468
|
}
|
|
503
469
|
function analyzeMemberExpression(node, state) {
|
|
504
|
-
|
|
505
|
-
if (((_a = node.object) == null ? void 0 : _a.type) === "Identifier" && state.allIdentifiers.has(node.object.value)) {
|
|
470
|
+
if (node.object?.type === "Identifier" && state.allIdentifiers.has(node.object.value)) {
|
|
506
471
|
const namespaceName = node.object.value;
|
|
507
|
-
const propertyName =
|
|
472
|
+
const propertyName = node.property?.value;
|
|
508
473
|
if (propertyName) {
|
|
509
474
|
state.componentNames.add(propertyName);
|
|
510
475
|
console.log(`\u{1F517} Namespace access: ${namespaceName}.${propertyName}`);
|
|
@@ -512,10 +477,9 @@ function analyzeMemberExpression(node, state) {
|
|
|
512
477
|
}
|
|
513
478
|
}
|
|
514
479
|
function isHOCPattern(node, state) {
|
|
515
|
-
|
|
516
|
-
return ((_a = node.callee) == null ? void 0 : _a.type) === "Identifier" && ((_b = node.arguments) == null ? void 0 : _b.some(
|
|
480
|
+
return node.callee?.type === "Identifier" && node.arguments?.some(
|
|
517
481
|
(arg) => arg.type === "Identifier" && state.componentNames.has(arg.value)
|
|
518
|
-
)
|
|
482
|
+
);
|
|
519
483
|
}
|
|
520
484
|
|
|
521
485
|
// src/swc-parser/core/visitor.ts
|
|
@@ -585,24 +549,23 @@ function visitNode(node, state, context = {}) {
|
|
|
585
549
|
}
|
|
586
550
|
}
|
|
587
551
|
function analyzeCallExpression(node, state, context) {
|
|
588
|
-
|
|
589
|
-
if (((_a = node.callee) == null ? void 0 : _a.value) === "lazy" || ((_c = (_b = node.callee) == null ? void 0 : _b.object) == null ? void 0 : _c.value) === "React" && ((_e = (_d = node.callee) == null ? void 0 : _d.property) == null ? void 0 : _e.value) === "lazy") {
|
|
552
|
+
if (node.callee?.value === "lazy" || node.callee?.object?.value === "React" && node.callee?.property?.value === "lazy") {
|
|
590
553
|
analyzeLazyImport(node, state);
|
|
591
554
|
}
|
|
592
|
-
if (
|
|
555
|
+
if (node.callee?.type === "Import") {
|
|
593
556
|
analyzeDynamicImport(node, state);
|
|
594
557
|
}
|
|
595
558
|
if (isHOCPattern(node, state)) {
|
|
596
559
|
analyzeHOCUsage(node, state);
|
|
597
560
|
}
|
|
598
|
-
if (
|
|
599
|
-
if (
|
|
561
|
+
if (node.callee?.object?.value === "React") {
|
|
562
|
+
if (node.callee?.property?.value === "memo") {
|
|
600
563
|
analyzeMemoUsage(node, state);
|
|
601
|
-
} else if (
|
|
564
|
+
} else if (node.callee?.property?.value === "forwardRef") {
|
|
602
565
|
analyzeForwardRefUsage(node, state);
|
|
603
566
|
}
|
|
604
567
|
}
|
|
605
|
-
if (
|
|
568
|
+
if (node.callee?.property?.value === "createPortal" || node.callee?.value === "createPortal") {
|
|
606
569
|
analyzePortalUsage(node, state);
|
|
607
570
|
}
|
|
608
571
|
visitChildren(node, state, context);
|
|
@@ -687,7 +650,7 @@ function calculateTotalPatterns(state) {
|
|
|
687
650
|
// src/swc-parser/index.ts
|
|
688
651
|
function parseCode(code, options = {}) {
|
|
689
652
|
const state = createState();
|
|
690
|
-
const ast =
|
|
653
|
+
const ast = parseSync(code, {
|
|
691
654
|
syntax: "typescript",
|
|
692
655
|
tsx: true,
|
|
693
656
|
decorators: true,
|
|
@@ -704,7 +667,7 @@ function parseFile(filePath, options = {}) {
|
|
|
704
667
|
console.log(`
|
|
705
668
|
\u{1F4C1} Analyzing: ${filePath}`);
|
|
706
669
|
try {
|
|
707
|
-
const code =
|
|
670
|
+
const code = fs3.readFileSync(filePath, "utf8");
|
|
708
671
|
return parseCode(code, options);
|
|
709
672
|
} catch (error) {
|
|
710
673
|
console.error(`\u274C Error parsing ${filePath}:`, error.message);
|
|
@@ -807,6 +770,7 @@ function aggregateReports(reports, versions = {}) {
|
|
|
807
770
|
}
|
|
808
771
|
function resolvePackageFromImportPath(importPath, availablePackages) {
|
|
809
772
|
if (importPath.startsWith(".") || importPath.startsWith("/")) {
|
|
773
|
+
console.log("importPath", importPath);
|
|
810
774
|
return "local";
|
|
811
775
|
}
|
|
812
776
|
const sortedPackages = [...availablePackages].sort(
|
|
@@ -986,17 +950,11 @@ function formatDuration(seconds) {
|
|
|
986
950
|
|
|
987
951
|
// src/utils/print-summary.ts
|
|
988
952
|
function printHeader() {
|
|
989
|
-
console.log(
|
|
953
|
+
console.log(chalk6.green.bold("\n\u{1F4CA} Summary\n"));
|
|
990
954
|
}
|
|
991
955
|
function printSummary(aggregated, elapsedTimeSeconds) {
|
|
992
|
-
console.log(
|
|
993
|
-
chalk6__default.default.green(
|
|
994
|
-
`Analysis completed successfully in ${formatDuration(elapsedTimeSeconds)}
|
|
995
|
-
`
|
|
996
|
-
)
|
|
997
|
-
);
|
|
998
956
|
printHeader();
|
|
999
|
-
const table = new
|
|
957
|
+
const table = new Table({
|
|
1000
958
|
head: ["Metric", "Count"],
|
|
1001
959
|
style: {
|
|
1002
960
|
head: ["cyan"],
|
|
@@ -1017,23 +975,27 @@ function printSummary(aggregated, elapsedTimeSeconds) {
|
|
|
1017
975
|
["Total Usages", formatCount(totalExternalUsage)]
|
|
1018
976
|
);
|
|
1019
977
|
console.log(table.toString());
|
|
978
|
+
console.log(
|
|
979
|
+
chalk6.green(
|
|
980
|
+
`Analysis completed successfully in ${formatDuration(elapsedTimeSeconds)}
|
|
981
|
+
`
|
|
982
|
+
)
|
|
983
|
+
);
|
|
1020
984
|
}
|
|
1021
985
|
function printHeader2() {
|
|
1022
|
-
console.log(
|
|
986
|
+
console.log(chalk6.cyan.bold("\n\u{1F4CB} Details\n"));
|
|
1023
987
|
}
|
|
1024
988
|
function printDetails(aggregated) {
|
|
1025
989
|
printHeader2();
|
|
1026
990
|
console.log(
|
|
1027
|
-
|
|
1028
|
-
` Total usage patterns: ${aggregated.totalUsagePatterns
|
|
991
|
+
chalk6.cyan(
|
|
992
|
+
` Total usage patterns: ${formatCount(aggregated.totalUsagePatterns)}`
|
|
1029
993
|
)
|
|
1030
994
|
);
|
|
1031
995
|
for (const pattern of aggregated.patternCounts) {
|
|
1032
996
|
if (pattern.count > 0) {
|
|
1033
997
|
console.log(
|
|
1034
|
-
|
|
1035
|
-
` ${pattern.displayName}: ${pattern.count.toLocaleString()}`
|
|
1036
|
-
)
|
|
998
|
+
chalk6.cyan(` ${pattern.displayName}: ${formatCount(pattern.count)}`)
|
|
1037
999
|
);
|
|
1038
1000
|
}
|
|
1039
1001
|
}
|
|
@@ -1046,12 +1008,12 @@ function renderBarChart(data, options = {}) {
|
|
|
1046
1008
|
emptyChar = "\u2591"
|
|
1047
1009
|
} = options;
|
|
1048
1010
|
if (data.length === 0) {
|
|
1049
|
-
console.log(
|
|
1011
|
+
console.log(chalk6.gray(" No data to display"));
|
|
1050
1012
|
return;
|
|
1051
1013
|
}
|
|
1052
1014
|
const maxValue = Math.max(...data.map((d) => d.value));
|
|
1053
1015
|
if (maxValue === 0) {
|
|
1054
|
-
console.log(
|
|
1016
|
+
console.log(chalk6.gray(" All values are zero"));
|
|
1055
1017
|
return;
|
|
1056
1018
|
}
|
|
1057
1019
|
const maxLabelLength = Math.max(...data.map((d) => d.label.length));
|
|
@@ -1060,8 +1022,8 @@ function renderBarChart(data, options = {}) {
|
|
|
1060
1022
|
const barLength = Math.round(percentage * maxWidth);
|
|
1061
1023
|
const emptyLength = maxWidth - barLength;
|
|
1062
1024
|
const paddedLabel = item.label.padEnd(maxLabelLength, " ");
|
|
1063
|
-
const bar =
|
|
1064
|
-
const valueStr = showValues ? ` ${item.value
|
|
1025
|
+
const bar = chalk6.green(barChar.repeat(barLength)) + chalk6.gray(emptyChar.repeat(emptyLength));
|
|
1026
|
+
const valueStr = showValues ? ` ${formatCount(item.value)}` : "";
|
|
1065
1027
|
console.log(`${paddedLabel} ${bar}${valueStr}
|
|
1066
1028
|
`);
|
|
1067
1029
|
}
|
|
@@ -1069,7 +1031,7 @@ function renderBarChart(data, options = {}) {
|
|
|
1069
1031
|
|
|
1070
1032
|
// src/utils/print-components.ts
|
|
1071
1033
|
function printHeader3() {
|
|
1072
|
-
console.log(
|
|
1034
|
+
console.log(chalk6.magenta.bold("\n\u269B\uFE0F Components\n"));
|
|
1073
1035
|
}
|
|
1074
1036
|
function printComponents(aggregated, mode) {
|
|
1075
1037
|
const components = aggregated.topComponents;
|
|
@@ -1085,10 +1047,10 @@ function printComponentsTable(components) {
|
|
|
1085
1047
|
(comp) => comp.source !== "unknown" && comp.source !== "local"
|
|
1086
1048
|
);
|
|
1087
1049
|
if (externalComponents.length === 0) {
|
|
1088
|
-
console.log(
|
|
1050
|
+
console.log(chalk6.gray(" No external components found"));
|
|
1089
1051
|
return;
|
|
1090
1052
|
}
|
|
1091
|
-
const table = new
|
|
1053
|
+
const table = new Table({
|
|
1092
1054
|
head: ["Component", "Package", "Count"],
|
|
1093
1055
|
style: {
|
|
1094
1056
|
head: ["cyan"],
|
|
@@ -1106,7 +1068,7 @@ function printComponentsChart(components) {
|
|
|
1106
1068
|
(comp) => comp.source !== "unknown" && comp.source !== "local"
|
|
1107
1069
|
);
|
|
1108
1070
|
if (externalComponents.length === 0) {
|
|
1109
|
-
console.log(
|
|
1071
|
+
console.log(chalk6.gray(" No external components found"));
|
|
1110
1072
|
return;
|
|
1111
1073
|
}
|
|
1112
1074
|
const data = externalComponents.map((comp) => ({
|
|
@@ -1116,7 +1078,7 @@ function printComponentsChart(components) {
|
|
|
1116
1078
|
renderBarChart(data, { maxWidth: 50 });
|
|
1117
1079
|
}
|
|
1118
1080
|
function printHeader4() {
|
|
1119
|
-
console.log(
|
|
1081
|
+
console.log(chalk6.blue.bold("\n\u{1F50D} Code Patterns\n"));
|
|
1120
1082
|
}
|
|
1121
1083
|
function printPatterns(aggregated, mode) {
|
|
1122
1084
|
const patterns = aggregated.patternCounts.filter((p) => p.count > 0);
|
|
@@ -1129,10 +1091,10 @@ function printPatterns(aggregated, mode) {
|
|
|
1129
1091
|
function printPatternsTable(patterns) {
|
|
1130
1092
|
printHeader4();
|
|
1131
1093
|
if (patterns.length === 0) {
|
|
1132
|
-
console.log(
|
|
1094
|
+
console.log(chalk6.gray(" No patterns found"));
|
|
1133
1095
|
return;
|
|
1134
1096
|
}
|
|
1135
|
-
const table = new
|
|
1097
|
+
const table = new Table({
|
|
1136
1098
|
head: ["Pattern", "Count"],
|
|
1137
1099
|
style: {
|
|
1138
1100
|
head: ["cyan"],
|
|
@@ -1144,13 +1106,13 @@ function printPatternsTable(patterns) {
|
|
|
1144
1106
|
});
|
|
1145
1107
|
console.log(table.toString());
|
|
1146
1108
|
const totalPatterns = patterns.reduce((sum, p) => sum + p.count, 0);
|
|
1147
|
-
console.log(
|
|
1109
|
+
console.log(chalk6.gray(`
|
|
1148
1110
|
Total: ${totalPatterns} patterns detected`));
|
|
1149
1111
|
}
|
|
1150
1112
|
function printPatternsChart(patterns) {
|
|
1151
1113
|
printHeader4();
|
|
1152
1114
|
if (patterns.length === 0) {
|
|
1153
|
-
console.log(
|
|
1115
|
+
console.log(chalk6.gray(" No patterns found"));
|
|
1154
1116
|
return;
|
|
1155
1117
|
}
|
|
1156
1118
|
const data = patterns.map((pattern) => ({
|
|
@@ -1160,7 +1122,7 @@ function printPatternsChart(patterns) {
|
|
|
1160
1122
|
renderBarChart(data, { maxWidth: 50 });
|
|
1161
1123
|
}
|
|
1162
1124
|
function printHeader5() {
|
|
1163
|
-
console.log(
|
|
1125
|
+
console.log(chalk6.blueBright.bold("\n\u{1F4E6} Packages\n"));
|
|
1164
1126
|
}
|
|
1165
1127
|
function printPackages(aggregated, mode) {
|
|
1166
1128
|
const packages = aggregated.packageDistribution;
|
|
@@ -1173,10 +1135,10 @@ function printPackages(aggregated, mode) {
|
|
|
1173
1135
|
function printPackagesTable(packages) {
|
|
1174
1136
|
printHeader5();
|
|
1175
1137
|
if (packages.length === 0) {
|
|
1176
|
-
console.log(
|
|
1138
|
+
console.log(chalk6.gray(" No packages found"));
|
|
1177
1139
|
return;
|
|
1178
1140
|
}
|
|
1179
|
-
const table = new
|
|
1141
|
+
const table = new Table({
|
|
1180
1142
|
head: ["Package", "Version", "Components", "Usage", "Percentage"],
|
|
1181
1143
|
style: {
|
|
1182
1144
|
head: ["cyan"],
|
|
@@ -1199,7 +1161,7 @@ function printPackagesTable(packages) {
|
|
|
1199
1161
|
);
|
|
1200
1162
|
const totalExternalUsage = packages.reduce((sum, p) => sum + p.usageCount, 0);
|
|
1201
1163
|
console.log(
|
|
1202
|
-
|
|
1164
|
+
chalk6.gray(
|
|
1203
1165
|
`
|
|
1204
1166
|
Total: ${formatCount(packages.length)} packages | ${formatCount(totalComponents)} unique components | ${formatCount(totalExternalUsage)} total usages`
|
|
1205
1167
|
)
|
|
@@ -1208,7 +1170,7 @@ Total: ${formatCount(packages.length)} packages | ${formatCount(totalComponents)
|
|
|
1208
1170
|
function printPackagesChart(packages) {
|
|
1209
1171
|
printHeader5();
|
|
1210
1172
|
if (packages.length === 0) {
|
|
1211
|
-
console.log(
|
|
1173
|
+
console.log(chalk6.gray(" No packages found"));
|
|
1212
1174
|
return;
|
|
1213
1175
|
}
|
|
1214
1176
|
const maxBarWidth = 40;
|
|
@@ -1220,14 +1182,14 @@ function printPackagesChart(packages) {
|
|
|
1220
1182
|
);
|
|
1221
1183
|
const emptyLength = maxBarWidth - barLength;
|
|
1222
1184
|
const paddedLabel = pkg.packageName.padEnd(maxLabelLength, " ");
|
|
1223
|
-
const bar =
|
|
1185
|
+
const bar = chalk6.green("\u2588".repeat(barLength)) + chalk6.gray("\u2591".repeat(emptyLength));
|
|
1224
1186
|
console.log(
|
|
1225
|
-
`${paddedLabel} ${bar} ${
|
|
1187
|
+
`${paddedLabel} ${bar} ${chalk6.bold(pkg.percentage.toFixed(1) + "%")} (${pkg.usageCount})`
|
|
1226
1188
|
);
|
|
1227
1189
|
});
|
|
1228
1190
|
}
|
|
1229
1191
|
async function findFiles(pattern, ignorePatterns) {
|
|
1230
|
-
const files = await glob
|
|
1192
|
+
const files = await glob(pattern, {
|
|
1231
1193
|
ignore: ignorePatterns,
|
|
1232
1194
|
nodir: true,
|
|
1233
1195
|
absolute: true,
|
|
@@ -1241,12 +1203,12 @@ var NpmLockfileAdapter = class {
|
|
|
1241
1203
|
this.supportedVersions = ["v2", "v3"];
|
|
1242
1204
|
}
|
|
1243
1205
|
detect(projectPath) {
|
|
1244
|
-
const lockfilePath =
|
|
1245
|
-
return
|
|
1206
|
+
const lockfilePath = path.join(projectPath, "package-lock.json");
|
|
1207
|
+
return fs3.existsSync(lockfilePath) ? lockfilePath : null;
|
|
1246
1208
|
}
|
|
1247
1209
|
parse(lockFilePath) {
|
|
1248
1210
|
try {
|
|
1249
|
-
const content =
|
|
1211
|
+
const content = fs3.readFileSync(lockFilePath, "utf8");
|
|
1250
1212
|
const lockData = JSON.parse(content);
|
|
1251
1213
|
const versions = {};
|
|
1252
1214
|
if (lockData.packages) {
|
|
@@ -1288,13 +1250,13 @@ var PnpmLockfileAdapter = class {
|
|
|
1288
1250
|
this.supportedVersions = ["v5", "v6", "v9"];
|
|
1289
1251
|
}
|
|
1290
1252
|
detect(projectPath) {
|
|
1291
|
-
const lockfilePath =
|
|
1292
|
-
return
|
|
1253
|
+
const lockfilePath = path.join(projectPath, "pnpm-lock.yaml");
|
|
1254
|
+
return fs3.existsSync(lockfilePath) ? lockfilePath : null;
|
|
1293
1255
|
}
|
|
1294
1256
|
parse(lockFilePath) {
|
|
1295
1257
|
try {
|
|
1296
|
-
const content =
|
|
1297
|
-
const lockData =
|
|
1258
|
+
const content = fs3.readFileSync(lockFilePath, "utf8");
|
|
1259
|
+
const lockData = yaml.load(content);
|
|
1298
1260
|
const versions = {};
|
|
1299
1261
|
if (lockData.importers) {
|
|
1300
1262
|
const rootImporter = lockData.importers["."];
|
|
@@ -1353,13 +1315,13 @@ var YarnLockfileAdapter = class {
|
|
|
1353
1315
|
this.supportedVersions = ["v1", "v2+"];
|
|
1354
1316
|
}
|
|
1355
1317
|
detect(projectPath) {
|
|
1356
|
-
const lockfilePath =
|
|
1357
|
-
return
|
|
1318
|
+
const lockfilePath = path.join(projectPath, "yarn.lock");
|
|
1319
|
+
return fs3.existsSync(lockfilePath) ? lockfilePath : null;
|
|
1358
1320
|
}
|
|
1359
1321
|
parse(lockFilePath) {
|
|
1360
1322
|
try {
|
|
1361
|
-
const content =
|
|
1362
|
-
const parsed =
|
|
1323
|
+
const content = fs3.readFileSync(lockFilePath, "utf8");
|
|
1324
|
+
const parsed = lockfile.parse(content);
|
|
1363
1325
|
if (parsed.type !== "success") {
|
|
1364
1326
|
console.warn("Warning: Failed to parse yarn.lock");
|
|
1365
1327
|
return {};
|
|
@@ -1468,21 +1430,22 @@ function normalizeOptions(options) {
|
|
|
1468
1430
|
}
|
|
1469
1431
|
async function executeScan(pattern, options) {
|
|
1470
1432
|
const startTime = Date.now();
|
|
1471
|
-
const spinner =
|
|
1433
|
+
const spinner = ora("Parsing lockfile...").start();
|
|
1472
1434
|
try {
|
|
1473
1435
|
const lockfileResult = findAndParseLockfile(process.cwd());
|
|
1474
1436
|
spinner.succeed(
|
|
1475
|
-
|
|
1437
|
+
chalk6.blue(
|
|
1476
1438
|
`\u{1F4E6} Found ${lockfileResult.lockfileType} lockfile (supports: ${lockfileResult.supportedVersions.join(", ")}) - ${Object.keys(lockfileResult.versions).length} packages`
|
|
1477
1439
|
)
|
|
1478
1440
|
);
|
|
1441
|
+
console.log("availablePackages", lockfileResult);
|
|
1479
1442
|
spinner.start("Finding files...");
|
|
1480
1443
|
const files = await findFiles(pattern, options.ignore);
|
|
1481
1444
|
if (files.length === 0) {
|
|
1482
1445
|
spinner.fail("TEST");
|
|
1483
1446
|
return;
|
|
1484
1447
|
}
|
|
1485
|
-
spinner.succeed(
|
|
1448
|
+
spinner.succeed(chalk6.green(` Found ${files.length} files`));
|
|
1486
1449
|
spinner.start("Analyzing files...");
|
|
1487
1450
|
const reports = [];
|
|
1488
1451
|
for (let i = 0; i < files.length; i++) {
|
|
@@ -1497,12 +1460,12 @@ async function executeScan(pattern, options) {
|
|
|
1497
1460
|
}
|
|
1498
1461
|
} catch (error) {
|
|
1499
1462
|
spinner.stop();
|
|
1500
|
-
console.error(
|
|
1463
|
+
console.error(chalk6.red(`Error analyzing ${file}: ${error.message}`));
|
|
1501
1464
|
spinner.start();
|
|
1502
1465
|
}
|
|
1503
1466
|
}
|
|
1504
1467
|
spinner.succeed(
|
|
1505
|
-
|
|
1468
|
+
chalk6.green(`Analysis complete! Analyzed ${reports.length} files`)
|
|
1506
1469
|
);
|
|
1507
1470
|
const elapsedTime = (Date.now() - startTime) / 1e3;
|
|
1508
1471
|
const aggregated = aggregateReports(reports, lockfileResult.versions);
|
|
@@ -1522,7 +1485,7 @@ async function executeScan(pattern, options) {
|
|
|
1522
1485
|
printSummary(aggregated, elapsedTime);
|
|
1523
1486
|
}
|
|
1524
1487
|
} catch (error) {
|
|
1525
|
-
spinner.fail(
|
|
1488
|
+
spinner.fail(chalk6.red("Analysis failed: " + error.message));
|
|
1526
1489
|
console.error(error);
|
|
1527
1490
|
process.exit(1);
|
|
1528
1491
|
}
|
|
@@ -1530,14 +1493,14 @@ async function executeScan(pattern, options) {
|
|
|
1530
1493
|
|
|
1531
1494
|
// package.json
|
|
1532
1495
|
var package_default = {
|
|
1533
|
-
version: "1.
|
|
1496
|
+
version: "1.1.1-beta.1"};
|
|
1534
1497
|
|
|
1535
1498
|
// src/cli.ts
|
|
1536
|
-
var program = new
|
|
1499
|
+
var program = new Command();
|
|
1537
1500
|
program.name("hermex").description("Analyze React component usage patterns in your codebase").version(package_default.version);
|
|
1538
1501
|
registerScanCommand(program);
|
|
1539
1502
|
program.parse(process.argv);
|
|
1540
1503
|
|
|
1541
|
-
|
|
1504
|
+
export { program };
|
|
1542
1505
|
//# sourceMappingURL=cli.js.map
|
|
1543
1506
|
//# sourceMappingURL=cli.js.map
|