hermex 1.1.2 → 1.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js DELETED
@@ -1,1462 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import ora from 'ora';
4
- import chalk5 from 'chalk';
5
- import { minimatch } from 'minimatch';
6
- import { parseSync } from '@swc/core';
7
- import fs3 from 'fs';
8
- import Table from 'cli-table3';
9
- import { glob } from 'glob';
10
- import path from 'path';
11
- import yaml from 'js-yaml';
12
- import lockfile from '@yarnpkg/lockfile';
13
-
14
- // src/swc-parser/core/state.ts
15
- function createState() {
16
- const usagePatterns = {
17
- directImports: /* @__PURE__ */ new Set(),
18
- namedImports: /* @__PURE__ */ new Set(),
19
- namespaceImports: /* @__PURE__ */ new Set(),
20
- defaultImports: /* @__PURE__ */ new Set(),
21
- aliasedImports: /* @__PURE__ */ new Map(),
22
- variableAssignments: /* @__PURE__ */ new Map(),
23
- componentMappings: /* @__PURE__ */ new Set(),
24
- lazyImports: /* @__PURE__ */ new Set(),
25
- dynamicImports: /* @__PURE__ */ new Set(),
26
- conditionalUsage: /* @__PURE__ */ new Set(),
27
- arrayMappings: /* @__PURE__ */ new Set(),
28
- objectMappings: /* @__PURE__ */ new Set(),
29
- hocUsage: /* @__PURE__ */ new Set(),
30
- renderProps: /* @__PURE__ */ new Set(),
31
- contextUsage: /* @__PURE__ */ new Set(),
32
- forwardedRefs: /* @__PURE__ */ new Set(),
33
- memoizedComponents: /* @__PURE__ */ new Set(),
34
- portalUsage: /* @__PURE__ */ new Set(),
35
- jsxUsage: /* @__PURE__ */ new Map(),
36
- destructuredUsage: /* @__PURE__ */ new Set(),
37
- propsAnalysis: /* @__PURE__ */ new Map()
38
- };
39
- return {
40
- usagePatterns,
41
- componentNames: /* @__PURE__ */ new Set(),
42
- allIdentifiers: /* @__PURE__ */ new Set()
43
- };
44
- }
45
-
46
- // src/swc-parser/patterns/imports.ts
47
- function analyzeImportDeclaration(node, state) {
48
- const source = node.source.value;
49
- for (const spec of node.specifiers) {
50
- switch (spec.type) {
51
- case "ImportDefaultSpecifier":
52
- analyzeDefaultImport(spec, source, node, state);
53
- break;
54
- case "ImportNamespaceSpecifier":
55
- analyzeNamespaceImport(spec, source, node, state);
56
- break;
57
- case "ImportSpecifier":
58
- analyzeNamedImport(spec, source, node, state);
59
- break;
60
- }
61
- }
62
- }
63
- function analyzeDefaultImport(spec, source, node, state) {
64
- const name = spec.local.value;
65
- state.usagePatterns.defaultImports.add({
66
- name,
67
- source,
68
- line: node.span?.start || 0
69
- });
70
- state.componentNames.add(name);
71
- }
72
- function analyzeNamespaceImport(spec, source, node, state) {
73
- const name = spec.local.value;
74
- state.usagePatterns.namespaceImports.add({
75
- name,
76
- source,
77
- line: node.span?.start || 0
78
- });
79
- state.allIdentifiers.add(name);
80
- }
81
- function analyzeNamedImport(spec, source, node, state) {
82
- const importedName = spec.imported ? spec.imported.value : spec.local.value;
83
- const localName = spec.local.value;
84
- state.usagePatterns.namedImports.add({
85
- name: importedName,
86
- source,
87
- line: node.span?.start || 0
88
- });
89
- if (importedName !== localName) {
90
- state.usagePatterns.aliasedImports.set(localName, {
91
- imported: importedName,
92
- local: localName,
93
- source,
94
- line: node.span?.start || 0
95
- });
96
- }
97
- state.componentNames.add(localName);
98
- }
99
-
100
- // src/swc-parser/utils/jsx-helpers.ts
101
- function getJSXElementName(nameNode) {
102
- if (!nameNode) return "";
103
- switch (nameNode.type) {
104
- case "Identifier":
105
- return nameNode.value;
106
- case "JSXMemberExpression":
107
- return `${getJSXElementName(nameNode.object)}.${nameNode.property.value}`;
108
- default:
109
- return "";
110
- }
111
- }
112
- function isMemberExpressionComponent(nameNode, state) {
113
- if (nameNode?.type === "JSXMemberExpression") {
114
- const objectName = getJSXElementName(nameNode.object);
115
- return state.allIdentifiers.has(objectName);
116
- }
117
- return false;
118
- }
119
- function extractJSXProps(attributes) {
120
- if (!attributes) return [];
121
- return attributes.map((attr) => {
122
- if (attr.type === "JSXAttribute") {
123
- return {
124
- name: attr.name?.value || attr.name?.name?.value,
125
- value: extractJSXAttributeValue(attr.value)
126
- };
127
- }
128
- if (attr.type === "SpreadElement") {
129
- return {
130
- name: "...",
131
- value: "[spread]",
132
- isSpread: true
133
- };
134
- }
135
- return null;
136
- }).filter(Boolean);
137
- }
138
- function extractJSXAttributeValue(value) {
139
- if (!value) return true;
140
- switch (value.type) {
141
- case "StringLiteral":
142
- return value.value;
143
- case "JSXExpressionContainer":
144
- return extractExpressionValue(value.expression);
145
- default:
146
- return "[complex]";
147
- }
148
- }
149
- function extractExpressionValue(expr) {
150
- if (!expr) return "[unknown]";
151
- switch (expr.type) {
152
- case "StringLiteral":
153
- case "NumericLiteral":
154
- case "BooleanLiteral":
155
- return expr.value;
156
- case "Identifier":
157
- return `{${expr.value}}`;
158
- case "ArrowFunctionExpression":
159
- case "FunctionExpression":
160
- return "[function]";
161
- case "ObjectExpression":
162
- return "[object]";
163
- case "ArrayExpression":
164
- return "[array]";
165
- default:
166
- return "[expression]";
167
- }
168
- }
169
- function getUsageContext(parent) {
170
- if (!parent) return "direct";
171
- switch (parent.type) {
172
- case "ConditionalExpression":
173
- return "conditional";
174
- case "ArrayExpression":
175
- return "array";
176
- case "ObjectExpression":
177
- return "object";
178
- case "CallExpression":
179
- return "hoc";
180
- case "VariableDeclarator":
181
- return "variable";
182
- default:
183
- return "jsx";
184
- }
185
- }
186
-
187
- // src/swc-parser/patterns/props.ts
188
- function analyzePropsInDetail(attributes, componentName, state) {
189
- const analysis = {
190
- namedProps: [],
191
- hasSpread: false,
192
- hasComplexProps: false,
193
- hasEventHandlers: false,
194
- propDetails: []
195
- };
196
- if (!attributes) return analysis;
197
- for (const attr of attributes) {
198
- if (attr.type === "JSXAttribute") {
199
- const propName = attr.name?.value || attr.name?.name?.value;
200
- if (propName) {
201
- analysis.namedProps.push(propName);
202
- const propDetail = {
203
- name: propName,
204
- type: getPropType(attr.value),
205
- isEventHandler: propName.startsWith("on"),
206
- isComplex: isComplexProp(attr.value)
207
- };
208
- if (propDetail.isEventHandler) {
209
- analysis.hasEventHandlers = true;
210
- }
211
- if (propDetail.isComplex) {
212
- analysis.hasComplexProps = true;
213
- }
214
- analysis.propDetails.push(propDetail);
215
- }
216
- } else if (attr.type === "SpreadElement") {
217
- analysis.hasSpread = true;
218
- analysis.propDetails.push({
219
- name: "...",
220
- type: "spread",
221
- isSpread: true,
222
- isComplex: true,
223
- isEventHandler: false,
224
- warning: "Spread props cannot be statically analyzed"
225
- });
226
- analysis.hasComplexProps = true;
227
- }
228
- }
229
- state.usagePatterns.propsAnalysis.set(componentName, analysis);
230
- return analysis;
231
- }
232
- function getPropType(value) {
233
- if (!value) return "boolean";
234
- switch (value.type) {
235
- case "StringLiteral":
236
- return "string";
237
- case "JSXExpressionContainer": {
238
- const expr = value.expression;
239
- if (!expr) return "unknown";
240
- switch (expr.type) {
241
- case "NumericLiteral":
242
- return "number";
243
- case "BooleanLiteral":
244
- return "boolean";
245
- case "StringLiteral":
246
- return "string";
247
- case "ArrowFunctionExpression":
248
- case "FunctionExpression":
249
- return "function";
250
- case "ObjectExpression":
251
- return "object";
252
- case "ArrayExpression":
253
- return "array";
254
- case "Identifier":
255
- return "variable";
256
- default:
257
- return "expression";
258
- }
259
- }
260
- default:
261
- return "unknown";
262
- }
263
- }
264
- function isComplexProp(value) {
265
- if (!value) return false;
266
- if (value.type === "JSXExpressionContainer") {
267
- const expr = value.expression;
268
- if (!expr) return false;
269
- return expr.type === "ObjectExpression" || expr.type === "ArrayExpression" || expr.type === "CallExpression" || expr.type === "ConditionalExpression";
270
- }
271
- return false;
272
- }
273
-
274
- // src/swc-parser/patterns/jsx.ts
275
- function analyzeJSXElement(node, state) {
276
- if (node.opening) {
277
- analyzeJSXOpeningElement(node.opening, state, node);
278
- }
279
- }
280
- function analyzeJSXOpeningElement(node, state, parent) {
281
- const elementName = getJSXElementName(node.name);
282
- if (!state.componentNames.has(elementName) && !isMemberExpressionComponent(node.name, state)) {
283
- return;
284
- }
285
- const propsAnalysis = analyzePropsInDetail(
286
- node.attributes,
287
- elementName,
288
- state
289
- );
290
- const usage = {
291
- component: elementName,
292
- props: extractJSXProps(node.attributes).map((p) => p.name),
293
- propsAnalysis,
294
- line: node.span?.start || 0,
295
- context: getUsageContext(parent)
296
- };
297
- if (!state.usagePatterns.jsxUsage.has(elementName)) {
298
- state.usagePatterns.jsxUsage.set(elementName, usage);
299
- }
300
- }
301
-
302
- // src/swc-parser/utils/matchers.ts
303
- function isKnownComponent(name, state) {
304
- return state.componentNames.has(name) || state.allIdentifiers.has(name);
305
- }
306
-
307
- // src/swc-parser/patterns/variables.ts
308
- function analyzeVariableDeclaration(node, state) {
309
- if (!node.declarations) return;
310
- for (const decl of node.declarations) {
311
- if (decl.id?.type === "Identifier") {
312
- const varName = decl.id.value;
313
- if (decl.init) {
314
- const assignment = extractAssignmentInfo(decl.init);
315
- if (assignment && isKnownComponent(assignment, state)) {
316
- state.usagePatterns.variableAssignments.set(varName, {
317
- assignment,
318
- line: node.span?.start || 0
319
- });
320
- state.componentNames.add(varName);
321
- }
322
- }
323
- }
324
- if (decl.id?.type === "ObjectPattern") {
325
- analyzeDestructuringPattern(decl.id, decl.init, state);
326
- }
327
- }
328
- }
329
- function analyzeDestructuringPattern(pattern, init, state) {
330
- if (!pattern.properties) return;
331
- for (const prop of pattern.properties) {
332
- if (prop.type === "AssignmentPatternProperty" && prop.key?.type === "Identifier") {
333
- const propName = prop.key.value;
334
- if (init?.type === "Identifier" && state.allIdentifiers.has(init.value)) {
335
- state.usagePatterns.destructuredUsage.add({
336
- property: propName,
337
- source: init.value,
338
- line: pattern.span?.start || 0
339
- });
340
- state.componentNames.add(propName);
341
- }
342
- }
343
- }
344
- }
345
- function extractAssignmentInfo(node) {
346
- switch (node.type) {
347
- case "Identifier":
348
- return node.value;
349
- case "MemberExpression":
350
- return `${extractAssignmentInfo(node.object)}.${node.property.value}`;
351
- case "ConditionalExpression":
352
- return `${extractAssignmentInfo(node.consequent)} | ${extractAssignmentInfo(node.alternate)}`;
353
- default:
354
- return null;
355
- }
356
- }
357
-
358
- // src/swc-parser/patterns/conditionals.ts
359
- function analyzeConditionalExpression(node, state) {
360
- const consequent = node.consequent?.type === "Identifier" ? node.consequent.value : null;
361
- const alternate = node.alternate?.type === "Identifier" ? node.alternate.value : null;
362
- if (consequent && state.componentNames.has(consequent) || alternate && state.componentNames.has(alternate)) {
363
- state.usagePatterns.conditionalUsage.add({
364
- consequent: consequent || "",
365
- alternate: alternate || "",
366
- line: node.span?.start || 0
367
- });
368
- }
369
- }
370
-
371
- // src/swc-parser/patterns/collections.ts
372
- function analyzeArrayExpression(node, state) {
373
- const hasComponents = node.elements?.some((elem) => {
374
- if (elem?.type === "Identifier") {
375
- return state.componentNames.has(elem.value);
376
- }
377
- return false;
378
- });
379
- if (hasComponents) {
380
- state.usagePatterns.arrayMappings.add({
381
- components: node.elements?.map((elem) => elem?.value).filter(Boolean),
382
- line: node.span?.start || 0
383
- });
384
- }
385
- }
386
- function analyzeObjectExpression(node, state) {
387
- const componentProps = node.properties?.filter((prop) => {
388
- if (prop.type === "KeyValueProperty" && prop.value?.type === "Identifier") {
389
- return state.componentNames.has(prop.value.value);
390
- }
391
- return false;
392
- });
393
- if (componentProps?.length > 0) {
394
- state.usagePatterns.objectMappings.add({
395
- mappings: componentProps.map((prop) => ({
396
- key: prop.key?.value || "[computed]",
397
- component: prop.value?.value
398
- })),
399
- line: node.span?.start || 0
400
- });
401
- }
402
- }
403
-
404
- // src/swc-parser/patterns/lazy-dynamic.ts
405
- function analyzeLazyImport(node, state) {
406
- const arg = node.arguments?.[0];
407
- if (arg?.type === "ArrowFunctionExpression" && arg.body?.type === "CallExpression") {
408
- const importCall = arg.body;
409
- if (importCall.callee?.type === "Import") {
410
- const source = importCall.arguments?.[0]?.value;
411
- if (source) {
412
- state.usagePatterns.lazyImports.add({
413
- source,
414
- line: node.span?.start || 0
415
- });
416
- }
417
- }
418
- }
419
- }
420
- function analyzeDynamicImport(node, state) {
421
- const source = node.arguments?.[0]?.value;
422
- if (source) {
423
- state.usagePatterns.dynamicImports.add({
424
- source,
425
- line: node.span?.start || 0
426
- });
427
- }
428
- }
429
-
430
- // src/swc-parser/patterns/advanced.ts
431
- function analyzeHOCUsage(node, state) {
432
- state.usagePatterns.hocUsage.add({
433
- function: node.callee?.value || "[unknown]",
434
- component: node.arguments?.[0]?.value || "[unknown]",
435
- line: node.span?.start || 0
436
- });
437
- }
438
- function analyzeMemoUsage(node, state) {
439
- const component = node.arguments?.[0];
440
- if (component?.type === "Identifier" && state.componentNames.has(component.value)) {
441
- state.usagePatterns.memoizedComponents.add({
442
- component: component.value,
443
- line: node.span?.start || 0
444
- });
445
- }
446
- }
447
- function analyzeForwardRefUsage(node, state) {
448
- state.usagePatterns.forwardedRefs.add({
449
- line: node.span?.start || 0
450
- });
451
- }
452
- function analyzePortalUsage(node, state) {
453
- state.usagePatterns.portalUsage.add({
454
- line: node.span?.start || 0
455
- });
456
- }
457
- function analyzeMemberExpression(node, state) {
458
- if (node.object?.type === "Identifier" && state.allIdentifiers.has(node.object.value)) {
459
- const propertyName = node.property?.value;
460
- if (propertyName) {
461
- state.componentNames.add(propertyName);
462
- }
463
- }
464
- }
465
- function isHOCPattern(node, state) {
466
- return node.callee?.type === "Identifier" && node.arguments?.some(
467
- (arg) => arg.type === "Identifier" && state.componentNames.has(arg.value)
468
- );
469
- }
470
-
471
- // src/swc-parser/core/visitor.ts
472
- function visitNode(node, state, context = {}) {
473
- if (!node) return;
474
- switch (node.type) {
475
- case "Module":
476
- if (node.body) {
477
- for (const item of node.body) {
478
- if (item.type === "ImportDeclaration") {
479
- visitNode(item, state, context);
480
- }
481
- }
482
- for (const item of node.body) {
483
- if (item.type !== "ImportDeclaration") {
484
- visitNode(item, state, { ...context, parent: node });
485
- }
486
- }
487
- }
488
- break;
489
- case "ImportDeclaration":
490
- analyzeImportDeclaration(node, state);
491
- break;
492
- case "CallExpression":
493
- analyzeCallExpression(node, state, context);
494
- break;
495
- case "VariableDeclaration":
496
- analyzeVariableDeclaration(node, state);
497
- visitChildren(node, state, context);
498
- break;
499
- case "JSXElement":
500
- case "JSXFragment":
501
- analyzeJSXElement(node, state);
502
- visitChildren(node, state, context);
503
- break;
504
- case "JSXOpeningElement":
505
- analyzeJSXOpeningElement(node, state, context.parent);
506
- break;
507
- case "ArrayExpression":
508
- analyzeArrayExpression(node, state);
509
- visitChildren(node, state, context);
510
- break;
511
- case "ObjectExpression":
512
- analyzeObjectExpression(node, state);
513
- visitChildren(node, state, context);
514
- break;
515
- case "MemberExpression":
516
- analyzeMemberExpression(node, state);
517
- visitChildren(node, state, context);
518
- break;
519
- case "ConditionalExpression":
520
- analyzeConditionalExpression(node, state);
521
- visitChildren(node, state, context);
522
- break;
523
- case "FunctionDeclaration":
524
- case "ClassDeclaration":
525
- case "ExpressionStatement":
526
- case "ReturnStatement":
527
- case "VariableDeclarator":
528
- case "ArrowFunctionExpression":
529
- case "FunctionExpression":
530
- visitChildren(node, state, { ...context, parent: node });
531
- break;
532
- default:
533
- visitChildren(node, state, context);
534
- break;
535
- }
536
- }
537
- function analyzeCallExpression(node, state, context) {
538
- if (node.callee?.value === "lazy" || node.callee?.object?.value === "React" && node.callee?.property?.value === "lazy") {
539
- analyzeLazyImport(node, state);
540
- }
541
- if (node.callee?.type === "Import") {
542
- analyzeDynamicImport(node, state);
543
- }
544
- if (isHOCPattern(node, state)) {
545
- analyzeHOCUsage(node, state);
546
- }
547
- if (node.callee?.object?.value === "React") {
548
- if (node.callee?.property?.value === "memo") {
549
- analyzeMemoUsage(node, state);
550
- } else if (node.callee?.property?.value === "forwardRef") {
551
- analyzeForwardRefUsage(node, state);
552
- }
553
- }
554
- if (node.callee?.property?.value === "createPortal" || node.callee?.value === "createPortal") {
555
- analyzePortalUsage(node, state);
556
- }
557
- visitChildren(node, state, context);
558
- }
559
- function visitChildren(node, state, context) {
560
- if (!node) return;
561
- for (const key in node) {
562
- const value = node[key];
563
- if (Array.isArray(value)) {
564
- for (const item of value) {
565
- if (item && typeof item === "object") {
566
- visitNode(item, state, { ...context, parent: node });
567
- }
568
- }
569
- } else if (value && typeof value === "object" && value.type) {
570
- visitNode(value, state, { ...context, parent: node });
571
- }
572
- }
573
- }
574
-
575
- // src/swc-parser/core/report.ts
576
- function generateReport(state) {
577
- const report = {
578
- summary: {
579
- totalImports: state.usagePatterns.defaultImports.size + state.usagePatterns.namedImports.size + state.usagePatterns.namespaceImports.size,
580
- totalComponents: state.componentNames.size,
581
- totalUsagePatterns: calculateTotalPatterns(state)
582
- },
583
- patterns: {
584
- imports: {
585
- default: Array.from(state.usagePatterns.defaultImports),
586
- named: Array.from(state.usagePatterns.namedImports),
587
- namespace: Array.from(state.usagePatterns.namespaceImports),
588
- aliased: Array.from(state.usagePatterns.aliasedImports.values())
589
- },
590
- usage: {
591
- jsx: Array.from(state.usagePatterns.jsxUsage.values()),
592
- variables: Array.from(
593
- state.usagePatterns.variableAssignments.entries()
594
- ).map(([key, value]) => ({
595
- variable: key,
596
- assignment: value.assignment
597
- })),
598
- destructuring: Array.from(state.usagePatterns.destructuredUsage),
599
- conditional: Array.from(state.usagePatterns.conditionalUsage),
600
- arrays: Array.from(state.usagePatterns.arrayMappings),
601
- objects: Array.from(state.usagePatterns.objectMappings)
602
- },
603
- advanced: {
604
- lazy: Array.from(state.usagePatterns.lazyImports),
605
- dynamic: Array.from(state.usagePatterns.dynamicImports),
606
- hoc: Array.from(state.usagePatterns.hocUsage),
607
- memo: Array.from(state.usagePatterns.memoizedComponents),
608
- forwardRef: Array.from(state.usagePatterns.forwardedRefs),
609
- portal: Array.from(state.usagePatterns.portalUsage)
610
- },
611
- props: Array.from(state.usagePatterns.propsAnalysis.entries()).map(
612
- ([component, analysis]) => ({
613
- component,
614
- analysis
615
- })
616
- )
617
- },
618
- components: Array.from(state.componentNames).sort()
619
- };
620
- return report;
621
- }
622
- function calculateTotalPatterns(state) {
623
- let sum = 0;
624
- const patterns = state.usagePatterns;
625
- for (const key in patterns) {
626
- const pattern = patterns[key];
627
- if (pattern instanceof Set) {
628
- sum += pattern.size;
629
- } else if (pattern instanceof Map) {
630
- sum += pattern.size;
631
- }
632
- }
633
- return sum;
634
- }
635
-
636
- // src/swc-parser/index.ts
637
- function parseCode(code, options = {}) {
638
- const state = createState();
639
- const ast = parseSync(code, {
640
- syntax: "typescript",
641
- tsx: true,
642
- decorators: true,
643
- dynamicImport: true
644
- });
645
- visitNode(ast, state);
646
- const report = generateReport(state);
647
- if (options.libraryName) {
648
- return filterReportByLibrary(report, options.libraryName);
649
- }
650
- return report;
651
- }
652
- function parseFile(filePath, options = {}) {
653
- const code = fs3.readFileSync(filePath, "utf8");
654
- return parseCode(code, options);
655
- }
656
- function filterReportByLibrary(report, libraryName) {
657
- const isFromLibrary = (source) => source.startsWith(libraryName) || source.includes(libraryName);
658
- return {
659
- ...report,
660
- patterns: {
661
- imports: {
662
- default: report.patterns.imports.default.filter(
663
- (imp) => isFromLibrary(imp.source)
664
- ),
665
- named: report.patterns.imports.named.filter(
666
- (imp) => isFromLibrary(imp.source)
667
- ),
668
- namespace: report.patterns.imports.namespace.filter(
669
- (imp) => isFromLibrary(imp.source)
670
- ),
671
- aliased: report.patterns.imports.aliased.filter(
672
- (imp) => isFromLibrary(imp.source)
673
- )
674
- },
675
- usage: report.patterns.usage,
676
- advanced: {
677
- lazy: report.patterns.advanced.lazy.filter(
678
- (imp) => isFromLibrary(imp.source)
679
- ),
680
- dynamic: report.patterns.advanced.dynamic.filter(
681
- (imp) => isFromLibrary(imp.source)
682
- ),
683
- hoc: report.patterns.advanced.hoc,
684
- memo: report.patterns.advanced.memo,
685
- forwardRef: report.patterns.advanced.forwardRef,
686
- portal: report.patterns.advanced.portal
687
- },
688
- props: report.patterns.props
689
- }
690
- };
691
- }
692
-
693
- // src/utils/aggregator.ts
694
- function aggregateReports(reports, versions = {}) {
695
- const componentUsageMap = /* @__PURE__ */ new Map();
696
- let totalImports = 0;
697
- let totalUsagePatterns = 0;
698
- const patternCountMap = /* @__PURE__ */ new Map();
699
- const availablePackages = Object.keys(versions);
700
- for (const report of reports) {
701
- totalImports += report.summary.totalImports;
702
- totalUsagePatterns += report.summary.totalUsagePatterns;
703
- for (const jsx of report.patterns.usage.jsx) {
704
- const key = jsx.component;
705
- const existing = componentUsageMap.get(key);
706
- if (existing) {
707
- existing.count++;
708
- } else {
709
- const source = findComponentSource(
710
- jsx.component,
711
- report,
712
- availablePackages
713
- );
714
- componentUsageMap.set(key, {
715
- name: jsx.component,
716
- source,
717
- count: 1,
718
- files: /* @__PURE__ */ new Set()
719
- });
720
- }
721
- }
722
- countPatterns(report, patternCountMap);
723
- }
724
- const topComponents = Array.from(componentUsageMap.values()).sort(
725
- (a, b) => b.count - a.count
726
- );
727
- const allComponents = Array.from(componentUsageMap.keys()).sort();
728
- const patternCounts = Array.from(patternCountMap.entries()).map(([type, count]) => ({
729
- patternType: type,
730
- displayName: getPatternDisplayName(type),
731
- count
732
- })).sort((a, b) => b.count - a.count);
733
- const packageDistribution = calculatePackageDistribution(
734
- componentUsageMap,
735
- versions
736
- );
737
- return {
738
- filesAnalyzed: reports.length,
739
- totalImports,
740
- totalComponents: componentUsageMap.size,
741
- totalUsagePatterns,
742
- patternCounts,
743
- componentUsage: componentUsageMap,
744
- topComponents,
745
- allComponents,
746
- packageDistribution,
747
- reports
748
- };
749
- }
750
- function resolvePackageFromImportPath(importPath, availablePackages) {
751
- if (importPath.startsWith(".") || importPath.startsWith("/")) {
752
- console.log("importPath", importPath);
753
- return "local";
754
- }
755
- const sortedPackages = [...availablePackages].sort(
756
- (a, b) => b.length - a.length
757
- );
758
- for (const pkg of sortedPackages) {
759
- if (importPath === pkg) {
760
- return pkg;
761
- }
762
- if (importPath.startsWith(`${pkg}/`)) {
763
- return pkg;
764
- }
765
- }
766
- return "unknown";
767
- }
768
- function findComponentSource(componentName, report, availablePackages) {
769
- const namedImport = report.patterns.imports.named.find(
770
- (imp) => imp.name === componentName
771
- );
772
- if (namedImport)
773
- return resolvePackageFromImportPath(namedImport.source, availablePackages);
774
- const defaultImport = report.patterns.imports.default.find(
775
- (imp) => imp.name === componentName
776
- );
777
- if (defaultImport)
778
- return resolvePackageFromImportPath(
779
- defaultImport.source,
780
- availablePackages
781
- );
782
- const aliasedImport = report.patterns.imports.aliased.find(
783
- (imp) => imp.local === componentName
784
- );
785
- if (aliasedImport)
786
- return resolvePackageFromImportPath(
787
- aliasedImport.source,
788
- availablePackages
789
- );
790
- return "unknown";
791
- }
792
- function countPatterns(report, patternMap) {
793
- increment(
794
- patternMap,
795
- "imports.default",
796
- report.patterns.imports.default.length
797
- );
798
- increment(patternMap, "imports.named", report.patterns.imports.named.length);
799
- increment(
800
- patternMap,
801
- "imports.namespace",
802
- report.patterns.imports.namespace.length
803
- );
804
- increment(
805
- patternMap,
806
- "imports.aliased",
807
- report.patterns.imports.aliased.length
808
- );
809
- increment(patternMap, "usage.jsx", report.patterns.usage.jsx.length);
810
- increment(
811
- patternMap,
812
- "usage.variables",
813
- report.patterns.usage.variables.length
814
- );
815
- increment(
816
- patternMap,
817
- "usage.destructuring",
818
- report.patterns.usage.destructuring.length
819
- );
820
- increment(
821
- patternMap,
822
- "usage.conditional",
823
- report.patterns.usage.conditional.length
824
- );
825
- increment(patternMap, "usage.arrays", report.patterns.usage.arrays.length);
826
- increment(patternMap, "usage.objects", report.patterns.usage.objects.length);
827
- increment(patternMap, "advanced.lazy", report.patterns.advanced.lazy.length);
828
- increment(
829
- patternMap,
830
- "advanced.dynamic",
831
- report.patterns.advanced.dynamic.length
832
- );
833
- increment(patternMap, "advanced.hoc", report.patterns.advanced.hoc.length);
834
- increment(patternMap, "advanced.memo", report.patterns.advanced.memo.length);
835
- increment(
836
- patternMap,
837
- "advanced.forwardRef",
838
- report.patterns.advanced.forwardRef.length
839
- );
840
- increment(
841
- patternMap,
842
- "advanced.portal",
843
- report.patterns.advanced.portal.length
844
- );
845
- }
846
- function increment(map, key, value) {
847
- map.set(key, (map.get(key) || 0) + value);
848
- }
849
- function getPatternDisplayName(patternType) {
850
- const displayNames = {
851
- "imports.default": "Default Imports",
852
- "imports.named": "Named Imports",
853
- "imports.namespace": "Namespace Imports",
854
- "imports.aliased": "Aliased Imports",
855
- "usage.jsx": "JSX Usage",
856
- "usage.variables": "Variable Assignments",
857
- "usage.destructuring": "Destructuring",
858
- "usage.conditional": "Conditional Usage",
859
- "usage.arrays": "Array Mappings",
860
- "usage.objects": "Object Mappings",
861
- "advanced.lazy": "Lazy Loading",
862
- "advanced.dynamic": "Dynamic Imports",
863
- "advanced.hoc": "Higher-Order Components",
864
- "advanced.memo": "Memoized Components",
865
- "advanced.forwardRef": "Forward Refs",
866
- "advanced.portal": "Portal Usage"
867
- };
868
- return displayNames[patternType] || patternType;
869
- }
870
- function getPackageVersion(packageName, versions) {
871
- if (versions[packageName]) {
872
- return versions[packageName];
873
- }
874
- if (packageName.includes("/")) {
875
- const parts = packageName.split("/");
876
- if (packageName.startsWith("@") && parts.length > 2) {
877
- const basePackage = `${parts[0]}/${parts[1]}`;
878
- if (versions[basePackage]) {
879
- return versions[basePackage];
880
- }
881
- }
882
- if (!packageName.startsWith("@") && parts.length > 1) {
883
- const basePackage = parts[0];
884
- if (versions[basePackage]) {
885
- return versions[basePackage];
886
- }
887
- }
888
- }
889
- return null;
890
- }
891
- function calculatePackageDistribution(componentUsageMap, versions) {
892
- const packageMap = /* @__PURE__ */ new Map();
893
- for (const component of componentUsageMap.values()) {
894
- if (component.source === "unknown") continue;
895
- const existing = packageMap.get(component.source);
896
- if (existing) {
897
- existing.componentCount++;
898
- existing.usageCount += component.count;
899
- existing.components.push(component.name);
900
- } else {
901
- packageMap.set(component.source, {
902
- packageName: component.source,
903
- version: getPackageVersion(component.source, versions),
904
- componentCount: 1,
905
- usageCount: component.count,
906
- percentage: 0,
907
- components: [component.name]
908
- });
909
- }
910
- }
911
- const distribution = Array.from(packageMap.values());
912
- const totalExternalUsage = distribution.reduce(
913
- (sum, pkg) => sum + pkg.usageCount,
914
- 0
915
- );
916
- for (const pkg of distribution) {
917
- pkg.percentage = totalExternalUsage > 0 ? pkg.usageCount / totalExternalUsage * 100 : 0;
918
- }
919
- return distribution.sort((a, b) => b.usageCount - a.usageCount);
920
- }
921
-
922
- // src/utils/format-utils.ts
923
- function formatCount(num) {
924
- return num.toLocaleString();
925
- }
926
- function formatDuration(seconds) {
927
- return `${seconds.toFixed(2)}s`;
928
- }
929
-
930
- // src/utils/print-summary.ts
931
- function printHeader() {
932
- console.log(chalk5.green.bold("\n\u{1F4CA} Summary\n"));
933
- }
934
- function printSummary(aggregated) {
935
- printHeader();
936
- const table = new Table({
937
- head: ["Metric", "Count"],
938
- style: {
939
- head: ["cyan"],
940
- border: ["gray"]
941
- }
942
- });
943
- const externalComponents = aggregated.topComponents.filter(
944
- (comp) => comp.source !== "unknown" && comp.source !== "local"
945
- ).length;
946
- const totalExternalUsage = aggregated.packageDistribution.reduce(
947
- (sum, pkg) => sum + pkg.usageCount,
948
- 0
949
- );
950
- table.push(
951
- ["Files Analyzed", formatCount(aggregated.filesAnalyzed)],
952
- ["External Packages", formatCount(aggregated.packageDistribution.length)],
953
- ["External Components", formatCount(externalComponents)],
954
- ["Total Usages", formatCount(totalExternalUsage)]
955
- );
956
- console.log(table.toString());
957
- }
958
- function renderBarChart(data, options = {}) {
959
- const {
960
- maxWidth = 50,
961
- showValues = true,
962
- barChar = "\u2588",
963
- emptyChar = "\u2591"
964
- } = options;
965
- if (data.length === 0) {
966
- console.log(chalk5.gray(" No data to display"));
967
- return;
968
- }
969
- const maxValue = Math.max(...data.map((d) => d.value));
970
- if (maxValue === 0) {
971
- console.log(chalk5.gray(" All values are zero"));
972
- return;
973
- }
974
- const maxLabelLength = Math.max(...data.map((d) => d.label.length));
975
- for (const item of data) {
976
- const percentage = item.value / maxValue;
977
- const barLength = Math.round(percentage * maxWidth);
978
- const emptyLength = maxWidth - barLength;
979
- const paddedLabel = item.label.padEnd(maxLabelLength, " ");
980
- const bar = chalk5.green(barChar.repeat(barLength)) + chalk5.gray(emptyChar.repeat(emptyLength));
981
- const valueStr = showValues ? ` ${formatCount(item.value)}` : "";
982
- console.log(`${paddedLabel} ${bar}${valueStr}
983
- `);
984
- }
985
- }
986
-
987
- // src/utils/print-components.ts
988
- function printHeader2() {
989
- console.log(chalk5.magenta.bold("\n\u269B\uFE0F Components\n"));
990
- }
991
- function printComponents(aggregated, mode) {
992
- const components = aggregated.topComponents;
993
- if (mode === "table") {
994
- printComponentsTable(components);
995
- } else if (mode === "chart") {
996
- printComponentsChart(components);
997
- }
998
- }
999
- function printComponentsTable(components) {
1000
- printHeader2();
1001
- const externalComponents = components.filter(
1002
- (comp) => comp.source !== "unknown" && comp.source !== "local"
1003
- );
1004
- if (externalComponents.length === 0) {
1005
- console.log(chalk5.gray(" No external components found"));
1006
- return;
1007
- }
1008
- const table = new Table({
1009
- head: ["Component", "Package", "Count"],
1010
- style: {
1011
- head: ["cyan"],
1012
- border: ["gray"]
1013
- }
1014
- });
1015
- externalComponents.forEach((comp) => {
1016
- table.push([comp.name, comp.source, comp.count.toString()]);
1017
- });
1018
- console.log(table.toString());
1019
- }
1020
- function printComponentsChart(components) {
1021
- printHeader2();
1022
- const externalComponents = components.filter(
1023
- (comp) => comp.source !== "unknown" && comp.source !== "local"
1024
- );
1025
- if (externalComponents.length === 0) {
1026
- console.log(chalk5.gray(" No external components found"));
1027
- return;
1028
- }
1029
- const data = externalComponents.map((comp) => ({
1030
- label: comp.name,
1031
- value: comp.count
1032
- }));
1033
- renderBarChart(data, { maxWidth: 50 });
1034
- }
1035
- function printHeader3() {
1036
- console.log(chalk5.blue.bold("\n\u{1F50D} Code Patterns\n"));
1037
- }
1038
- function printPatterns(aggregated, mode) {
1039
- const patterns = aggregated.patternCounts.filter((p) => p.count > 0);
1040
- if (mode === "table") {
1041
- printPatternsTable(patterns);
1042
- } else if (mode === "chart") {
1043
- printPatternsChart(patterns);
1044
- }
1045
- }
1046
- function printPatternsTable(patterns) {
1047
- printHeader3();
1048
- if (patterns.length === 0) {
1049
- console.log(chalk5.gray(" No patterns found"));
1050
- return;
1051
- }
1052
- const table = new Table({
1053
- head: ["Pattern", "Count"],
1054
- style: {
1055
- head: ["cyan"],
1056
- border: ["gray"]
1057
- }
1058
- });
1059
- patterns.forEach((pattern) => {
1060
- table.push([pattern.displayName, pattern.count.toString()]);
1061
- });
1062
- console.log(table.toString());
1063
- const totalPatterns = patterns.reduce((sum, p) => sum + p.count, 0);
1064
- console.log(chalk5.gray(`
1065
- Total: ${totalPatterns} patterns detected`));
1066
- }
1067
- function printPatternsChart(patterns) {
1068
- printHeader3();
1069
- if (patterns.length === 0) {
1070
- console.log(chalk5.gray(" No patterns found"));
1071
- return;
1072
- }
1073
- const data = patterns.map((pattern) => ({
1074
- label: pattern.displayName,
1075
- value: pattern.count
1076
- }));
1077
- renderBarChart(data, { maxWidth: 50 });
1078
- }
1079
- function printHeader4() {
1080
- console.log(chalk5.blueBright.bold("\n\u{1F4E6} Packages\n"));
1081
- }
1082
- function printPackages(aggregated, mode) {
1083
- const packages = aggregated.packageDistribution;
1084
- if (mode === "table") {
1085
- printPackagesTable(packages);
1086
- } else if (mode === "chart") {
1087
- printPackagesChart(packages);
1088
- }
1089
- }
1090
- function printPackagesTable(packages) {
1091
- printHeader4();
1092
- if (packages.length === 0) {
1093
- console.log(chalk5.gray(" No packages found"));
1094
- return;
1095
- }
1096
- const table = new Table({
1097
- head: ["Package", "Version", "Components", "Usage", "Percentage"],
1098
- style: {
1099
- head: ["cyan"],
1100
- border: ["gray"]
1101
- }
1102
- });
1103
- packages.forEach((pkg) => {
1104
- table.push([
1105
- pkg.packageName,
1106
- pkg.version || "N/A",
1107
- formatCount(pkg.componentCount),
1108
- formatCount(pkg.usageCount),
1109
- `${pkg.percentage.toFixed(1)}%`
1110
- ]);
1111
- });
1112
- console.log(table.toString());
1113
- const totalComponents = packages.reduce(
1114
- (sum, p) => sum + p.componentCount,
1115
- 0
1116
- );
1117
- const totalExternalUsage = packages.reduce((sum, p) => sum + p.usageCount, 0);
1118
- console.log(
1119
- chalk5.gray(
1120
- `
1121
- Total: ${formatCount(packages.length)} packages | ${formatCount(totalComponents)} unique components | ${formatCount(totalExternalUsage)} total usages`
1122
- )
1123
- );
1124
- }
1125
- function printPackagesChart(packages) {
1126
- printHeader4();
1127
- if (packages.length === 0) {
1128
- console.log(chalk5.gray(" No packages found"));
1129
- return;
1130
- }
1131
- const maxBarWidth = 40;
1132
- const maxPercentage = Math.max(...packages.map((p) => p.percentage));
1133
- const maxLabelLength = Math.max(...packages.map((p) => p.packageName.length));
1134
- packages.forEach((pkg) => {
1135
- const barLength = Math.round(
1136
- pkg.percentage / maxPercentage * maxBarWidth
1137
- );
1138
- const emptyLength = maxBarWidth - barLength;
1139
- const paddedLabel = pkg.packageName.padEnd(maxLabelLength, " ");
1140
- const bar = chalk5.green("\u2588".repeat(barLength)) + chalk5.gray("\u2591".repeat(emptyLength));
1141
- console.log(
1142
- `${paddedLabel} ${bar} ${chalk5.bold(pkg.percentage.toFixed(1) + "%")} (${pkg.usageCount})`
1143
- );
1144
- });
1145
- }
1146
- async function findFiles(pattern, ignorePatterns) {
1147
- const files = await glob(pattern, {
1148
- ignore: ignorePatterns,
1149
- nodir: true,
1150
- absolute: true,
1151
- windowsPathsNoEscape: true
1152
- });
1153
- return files;
1154
- }
1155
- var NpmLockfileAdapter = class {
1156
- constructor() {
1157
- this.name = "npm";
1158
- this.supportedVersions = ["v2", "v3"];
1159
- }
1160
- detect(projectPath) {
1161
- const lockfilePath = path.join(projectPath, "package-lock.json");
1162
- return fs3.existsSync(lockfilePath) ? lockfilePath : null;
1163
- }
1164
- parse(lockFilePath) {
1165
- try {
1166
- const content = fs3.readFileSync(lockFilePath, "utf8");
1167
- const lockData = JSON.parse(content);
1168
- const versions = {};
1169
- if (lockData.packages) {
1170
- Object.entries(lockData.packages).forEach(
1171
- ([pkgPath, pkgData]) => {
1172
- if (!pkgPath || pkgPath === "") return;
1173
- const pkgName = pkgPath.replace(/^node_modules\//, "");
1174
- if (pkgData.version) {
1175
- versions[pkgName] = pkgData.version;
1176
- }
1177
- }
1178
- );
1179
- }
1180
- if (lockData.dependencies && Object.keys(versions).length === 0) {
1181
- let extractVersions = function(deps, prefix = "") {
1182
- Object.entries(deps).forEach(([name, data]) => {
1183
- const fullName = prefix ? `${prefix}/${name}` : name;
1184
- if (data.version) {
1185
- versions[fullName] = data.version;
1186
- }
1187
- if (data.dependencies) {
1188
- extractVersions(data.dependencies, fullName);
1189
- }
1190
- });
1191
- };
1192
- extractVersions(lockData.dependencies);
1193
- }
1194
- return versions;
1195
- } catch (error) {
1196
- const message = error instanceof Error ? error.message : String(error);
1197
- console.warn(`Warning: Could not parse package-lock.json: ${message}`);
1198
- return {};
1199
- }
1200
- }
1201
- };
1202
- var PnpmLockfileAdapter = class {
1203
- constructor() {
1204
- this.name = "pnpm";
1205
- this.supportedVersions = ["v5", "v6", "v9"];
1206
- }
1207
- detect(projectPath) {
1208
- const lockfilePath = path.join(projectPath, "pnpm-lock.yaml");
1209
- return fs3.existsSync(lockfilePath) ? lockfilePath : null;
1210
- }
1211
- parse(lockFilePath) {
1212
- try {
1213
- const content = fs3.readFileSync(lockFilePath, "utf8");
1214
- const lockData = yaml.load(content);
1215
- const versions = {};
1216
- if (lockData.importers) {
1217
- const rootImporter = lockData.importers["."];
1218
- if (rootImporter) {
1219
- if (rootImporter.dependencies) {
1220
- for (const [name, data] of Object.entries(
1221
- rootImporter.dependencies
1222
- )) {
1223
- if (typeof data === "object" && data !== null && "version" in data) {
1224
- versions[name] = data.version;
1225
- }
1226
- }
1227
- }
1228
- if (rootImporter.devDependencies) {
1229
- for (const [name, data] of Object.entries(
1230
- rootImporter.devDependencies
1231
- )) {
1232
- if (typeof data === "object" && data !== null && "version" in data) {
1233
- versions[name] = data.version;
1234
- }
1235
- }
1236
- }
1237
- }
1238
- }
1239
- if (lockData.packages && Object.keys(versions).length === 0) {
1240
- Object.keys(lockData.packages).forEach((key) => {
1241
- const match = key.match(/\/(.+?)\/(\d+\.\d+\.\d+.*?)(?:_|$)/);
1242
- if (match) {
1243
- const [, pkgName, version] = match;
1244
- versions[pkgName] = version;
1245
- }
1246
- });
1247
- }
1248
- if (lockData.dependencies && Object.keys(versions).length === 0) {
1249
- Object.entries(lockData.dependencies).forEach(
1250
- ([name, versionSpec]) => {
1251
- if (typeof versionSpec === "string" && !versionSpec.startsWith("link:")) {
1252
- versions[name] = versionSpec;
1253
- } else if (typeof versionSpec === "object" && versionSpec.version) {
1254
- versions[name] = versionSpec.version;
1255
- }
1256
- }
1257
- );
1258
- }
1259
- return versions;
1260
- } catch (error) {
1261
- const message = error instanceof Error ? error.message : String(error);
1262
- console.warn(`Warning: Could not parse pnpm-lock.yaml: ${message}`);
1263
- return {};
1264
- }
1265
- }
1266
- };
1267
- var YarnLockfileAdapter = class {
1268
- constructor() {
1269
- this.name = "yarn";
1270
- this.supportedVersions = ["v1", "v2+"];
1271
- }
1272
- detect(projectPath) {
1273
- const lockfilePath = path.join(projectPath, "yarn.lock");
1274
- return fs3.existsSync(lockfilePath) ? lockfilePath : null;
1275
- }
1276
- parse(lockFilePath) {
1277
- try {
1278
- const content = fs3.readFileSync(lockFilePath, "utf8");
1279
- const parsed = lockfile.parse(content);
1280
- if (parsed.type !== "success") {
1281
- console.warn("Warning: Failed to parse yarn.lock");
1282
- return {};
1283
- }
1284
- const versions = {};
1285
- Object.entries(parsed.object).forEach(([key, value]) => {
1286
- let pkgName = key;
1287
- if (key.startsWith("@")) {
1288
- const match = key.match(/^(@[^@]+\/[^@]+)@/);
1289
- if (match) {
1290
- pkgName = match[1];
1291
- }
1292
- } else {
1293
- const match = key.match(/^([^@]+)@/);
1294
- if (match) {
1295
- pkgName = match[1];
1296
- }
1297
- }
1298
- if (value.version && (!versions[pkgName] || value.version)) {
1299
- versions[pkgName] = value.version;
1300
- }
1301
- });
1302
- return versions;
1303
- } catch (error) {
1304
- const message = error instanceof Error ? error.message : String(error);
1305
- console.warn(`Warning: Could not parse yarn.lock: ${message}`);
1306
- return {};
1307
- }
1308
- }
1309
- };
1310
-
1311
- // src/lock-parser/index.ts
1312
- var LOCKFILE_ADAPTERS = [
1313
- new NpmLockfileAdapter(),
1314
- new YarnLockfileAdapter(),
1315
- new PnpmLockfileAdapter()
1316
- ];
1317
- function findAndParseLockfile(projectPath) {
1318
- for (const adapter of LOCKFILE_ADAPTERS) {
1319
- const lockfilePath = adapter.detect(projectPath);
1320
- if (lockfilePath) {
1321
- const versions = adapter.parse(lockfilePath);
1322
- return {
1323
- versions,
1324
- lockfileType: adapter.name,
1325
- lockfilePath,
1326
- supportedVersions: adapter.supportedVersions
1327
- };
1328
- }
1329
- }
1330
- throw new Error("No supported lockfile found");
1331
- }
1332
-
1333
- // src/commands/scan.ts
1334
- function registerScanCommand(program2) {
1335
- program2.command("scan").description("Scan and analyze local files").argument(
1336
- "[pattern]",
1337
- "Glob pattern for files to analyze (defaults to current directory recursively)",
1338
- "**/*.{tsx,jsx,ts,js}"
1339
- ).option("--ignore <pattern>", "Glob pattern for files to ignore", [
1340
- "**/node_modules/**",
1341
- "**/dist/**",
1342
- "**/build/**"
1343
- ]).option("--allow-packages <pattern>", "Pattern for what packages to scan", [
1344
- "**"
1345
- ]).option(
1346
- "--ignore-packages <pattern>",
1347
- "Pattern for what packages to ignore",
1348
- []
1349
- ).option("--no-summary", "Hide summary", "table").option(
1350
- "--components [mode]",
1351
- "Show components table/chart (table, chart)",
1352
- "table"
1353
- ).option("--no-components", "Do not show components").option(
1354
- "--packages [mode]",
1355
- "Show packages table/chart (table, chart)",
1356
- "table"
1357
- ).option("--no-packages", "Do not show packages").option(
1358
- "--patterns [mode]",
1359
- "Show patterns table/chart (table, chart)",
1360
- "table"
1361
- ).option("--no-patterns", "Do not show patterns").action(async (pattern, options) => {
1362
- const normalizedOptions = normalizeOptions(options);
1363
- await executeScan(pattern, normalizedOptions);
1364
- });
1365
- }
1366
- function normalizeArray(value) {
1367
- if (!value) {
1368
- return [];
1369
- }
1370
- return Array.isArray(value) ? value : [value];
1371
- }
1372
- function normalizeOptions(options) {
1373
- return {
1374
- verbose: options.verbose || false,
1375
- summary: options.summary === false || options.summary === "false" ? false : "log",
1376
- noSummary: options.noSummary || false,
1377
- components: options.components || "table",
1378
- packages: options.packages || "table",
1379
- patterns: options.patterns || "table",
1380
- ignore: normalizeArray(options.ignore),
1381
- allowPackages: normalizeArray(options.allowPackages),
1382
- ignorePackages: normalizeArray(options.ignorePackages)
1383
- };
1384
- }
1385
- async function executeScan(pattern, options) {
1386
- const startTime = Date.now();
1387
- const spinner = ora("Parsing lockfile...").start();
1388
- try {
1389
- const lockfileResult = findAndParseLockfile(process.cwd());
1390
- const allPackages = Object.keys(lockfileResult.versions);
1391
- const filteredPackages = allPackages.filter((pkg) => {
1392
- const allowed = options.allowPackages.some((p) => minimatch(pkg, p));
1393
- const ignored = options.ignorePackages.some((p) => minimatch(pkg, p));
1394
- return allowed && !ignored;
1395
- });
1396
- const filteredVersions = {};
1397
- for (const pkg of filteredPackages) {
1398
- filteredVersions[pkg] = lockfileResult.versions[pkg];
1399
- }
1400
- spinner.succeed(
1401
- `Found ${lockfileResult.lockfileType} lockfile (supports: ${lockfileResult.supportedVersions.join(", ")}) - ${filteredPackages.length}/${allPackages.length} packages`
1402
- );
1403
- spinner.start("Finding files...");
1404
- const files = await findFiles(pattern, options.ignore);
1405
- if (files.length === 0) {
1406
- spinner.fail(`No files found matching pattern: ${pattern}`);
1407
- return;
1408
- }
1409
- spinner.succeed(chalk5.green(` Found ${files.length} files`));
1410
- spinner.start("Analyzing files...");
1411
- const reports = [];
1412
- for (let i = 0; i < files.length; i++) {
1413
- const file = files[i];
1414
- if (!options.verbose) {
1415
- spinner.text = `Analyzing files... (${i + 1}/${files.length})`;
1416
- }
1417
- const report = parseFile(file);
1418
- if (report) {
1419
- reports.push(report);
1420
- }
1421
- }
1422
- const elapsedTime = (Date.now() - startTime) / 1e3;
1423
- const aggregated = aggregateReports(reports, filteredVersions);
1424
- if (options.packages && !options.noPackages) {
1425
- printPackages(aggregated, options.packages);
1426
- }
1427
- if (options.components && !options.noComponents) {
1428
- printComponents(aggregated, options.components);
1429
- }
1430
- if (options.patterns && !options.noPatterns) {
1431
- printPatterns(aggregated, options.patterns);
1432
- }
1433
- if (options.summary && !options.noSummary) {
1434
- printSummary(aggregated);
1435
- }
1436
- console.log("");
1437
- spinner.succeed(
1438
- chalk5.green.bold(
1439
- ` Analysis complete! Analyzed ${reports.length} files in ${formatDuration(elapsedTime)}
1440
- `
1441
- )
1442
- );
1443
- } catch (error) {
1444
- spinner.fail(chalk5.red("Analysis failed: " + error.message));
1445
- console.error(error);
1446
- process.exit(1);
1447
- }
1448
- }
1449
-
1450
- // package.json
1451
- var package_default = {
1452
- version: "1.1.1"};
1453
-
1454
- // src/cli.ts
1455
- var program = new Command();
1456
- program.name("hermex").description("Analyze React component usage patterns in your codebase").version(package_default.version);
1457
- registerScanCommand(program);
1458
- program.parse(process.argv);
1459
-
1460
- export { program };
1461
- //# sourceMappingURL=cli.js.map
1462
- //# sourceMappingURL=cli.js.map