hermex 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,2696 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var commander = require('commander');
5
+ var glob = require('glob');
6
+ var chalk8 = require('chalk');
7
+ var ora6 = require('ora');
8
+ var path = require('path');
9
+ var Table2 = require('cli-table3');
10
+ var core = require('@swc/core');
11
+ var fs5 = require('fs');
12
+ var simpleGit = require('simple-git');
13
+ var tmp = require('tmp');
14
+ var yaml = require('js-yaml');
15
+ var lockfile = require('@yarnpkg/lockfile');
16
+
17
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
+
19
+ var chalk8__default = /*#__PURE__*/_interopDefault(chalk8);
20
+ var ora6__default = /*#__PURE__*/_interopDefault(ora6);
21
+ var path__default = /*#__PURE__*/_interopDefault(path);
22
+ var Table2__default = /*#__PURE__*/_interopDefault(Table2);
23
+ var fs5__default = /*#__PURE__*/_interopDefault(fs5);
24
+ var simpleGit__default = /*#__PURE__*/_interopDefault(simpleGit);
25
+ var tmp__default = /*#__PURE__*/_interopDefault(tmp);
26
+ var yaml__default = /*#__PURE__*/_interopDefault(yaml);
27
+ var lockfile__default = /*#__PURE__*/_interopDefault(lockfile);
28
+
29
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
30
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
31
+ }) : x)(function(x) {
32
+ if (typeof require !== "undefined") return require.apply(this, arguments);
33
+ throw Error('Dynamic require of "' + x + '" is not supported');
34
+ });
35
+ var ReactComponentUsageAnalyzer = class {
36
+ constructor(libraryName = "@design-system/foundation") {
37
+ this.libraryName = libraryName;
38
+ this.usagePatterns = {
39
+ directImports: /* @__PURE__ */ new Set(),
40
+ namedImports: /* @__PURE__ */ new Set(),
41
+ namespaceImports: /* @__PURE__ */ new Set(),
42
+ defaultImports: /* @__PURE__ */ new Set(),
43
+ aliasedImports: /* @__PURE__ */ new Map(),
44
+ variableAssignments: /* @__PURE__ */ new Map(),
45
+ componentMappings: /* @__PURE__ */ new Set(),
46
+ lazyImports: /* @__PURE__ */ new Set(),
47
+ dynamicImports: /* @__PURE__ */ new Set(),
48
+ conditionalUsage: /* @__PURE__ */ new Set(),
49
+ arrayMappings: /* @__PURE__ */ new Set(),
50
+ objectMappings: /* @__PURE__ */ new Set(),
51
+ hocUsage: /* @__PURE__ */ new Set(),
52
+ renderProps: /* @__PURE__ */ new Set(),
53
+ contextUsage: /* @__PURE__ */ new Set(),
54
+ forwardedRefs: /* @__PURE__ */ new Set(),
55
+ memoizedComponents: /* @__PURE__ */ new Set(),
56
+ portalUsage: /* @__PURE__ */ new Set(),
57
+ jsxUsage: /* @__PURE__ */ new Map(),
58
+ destructuredUsage: /* @__PURE__ */ new Set(),
59
+ propsAnalysis: /* @__PURE__ */ new Map()
60
+ };
61
+ this.componentNames = /* @__PURE__ */ new Set();
62
+ this.allIdentifiers = /* @__PURE__ */ new Set();
63
+ }
64
+ analyzeFile(filePath) {
65
+ console.log(`
66
+ \u{1F4C1} Analyzing: ${filePath}`);
67
+ try {
68
+ const code = fs5__default.default.readFileSync(filePath, "utf8");
69
+ const ast = core.parseSync(code, {
70
+ syntax: "typescript",
71
+ tsx: true,
72
+ decorators: true,
73
+ dynamicImport: true
74
+ });
75
+ this.visitNode(ast);
76
+ return this.generateReport();
77
+ } catch (error) {
78
+ console.error(`\u274C Error parsing ${filePath}:`, error.message);
79
+ return null;
80
+ }
81
+ }
82
+ visitNode(node, parent = null) {
83
+ if (!node) return;
84
+ switch (node.type) {
85
+ case "Module":
86
+ node.body.forEach((item) => this.visitNode(item, node));
87
+ break;
88
+ case "ImportDeclaration":
89
+ this.analyzeImport(node);
90
+ break;
91
+ case "CallExpression":
92
+ this.analyzeCallExpression(node, parent);
93
+ break;
94
+ case "VariableDeclaration":
95
+ this.analyzeVariableDeclaration(node);
96
+ break;
97
+ case "JSXElement":
98
+ case "JSXFragment":
99
+ this.analyzeJSXElement(node, parent);
100
+ break;
101
+ case "JSXOpeningElement":
102
+ this.analyzeJSXOpeningElement(node, parent);
103
+ break;
104
+ case "ArrayExpression":
105
+ this.analyzeArrayExpression(node, parent);
106
+ break;
107
+ case "ObjectExpression":
108
+ this.analyzeObjectExpression(node, parent);
109
+ break;
110
+ case "MemberExpression":
111
+ this.analyzeMemberExpression(node, parent);
112
+ break;
113
+ case "ConditionalExpression":
114
+ this.analyzeConditionalExpression(node, parent);
115
+ break;
116
+ case "FunctionDeclaration":
117
+ case "FunctionExpression":
118
+ case "ArrowFunctionExpression":
119
+ this.analyzeFunctionDeclaration(node);
120
+ break;
121
+ case "ClassDeclaration":
122
+ this.analyzeClassDeclaration(node);
123
+ break;
124
+ default:
125
+ this.visitChildren(node);
126
+ break;
127
+ }
128
+ }
129
+ visitChildren(node) {
130
+ if (!node || typeof node !== "object") return;
131
+ for (const key in node) {
132
+ const value = node[key];
133
+ if (Array.isArray(value)) {
134
+ value.forEach((child) => this.visitNode(child, node));
135
+ } else if (value && typeof value === "object" && value.type) {
136
+ this.visitNode(value, node);
137
+ }
138
+ }
139
+ }
140
+ analyzeImport(node) {
141
+ const source = node.source.value;
142
+ if (!this.isLibraryImport(source)) return;
143
+ console.log(`\u{1F4E6} Found library import: ${source}`);
144
+ node.specifiers.forEach((spec) => {
145
+ var _a, _b, _c, _d;
146
+ switch (spec.type) {
147
+ case "ImportDefaultSpecifier":
148
+ this.usagePatterns.defaultImports.add({
149
+ name: spec.local.value,
150
+ source,
151
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0
152
+ });
153
+ this.componentNames.add(spec.local.value);
154
+ break;
155
+ case "ImportNamespaceSpecifier":
156
+ this.usagePatterns.namespaceImports.add({
157
+ name: spec.local.value,
158
+ source,
159
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
160
+ });
161
+ this.allIdentifiers.add(spec.local.value);
162
+ break;
163
+ case "ImportSpecifier":
164
+ const importedName = spec.imported ? spec.imported.value : spec.local.value;
165
+ const localName = spec.local.value;
166
+ this.usagePatterns.namedImports.add({
167
+ imported: importedName,
168
+ local: localName,
169
+ source,
170
+ line: ((_c = node.span) == null ? void 0 : _c.start) || 0
171
+ });
172
+ if (importedName !== localName) {
173
+ this.usagePatterns.aliasedImports.set(localName, {
174
+ original: importedName,
175
+ source,
176
+ line: ((_d = node.span) == null ? void 0 : _d.start) || 0
177
+ });
178
+ }
179
+ this.componentNames.add(localName);
180
+ break;
181
+ }
182
+ });
183
+ }
184
+ analyzeCallExpression(node, parent) {
185
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
186
+ 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") {
187
+ this.analyzeLazyImport(node);
188
+ }
189
+ if (((_f = node.callee) == null ? void 0 : _f.type) === "Import") {
190
+ this.analyzeDynamicImport(node);
191
+ }
192
+ if (this.isHOCPattern(node)) {
193
+ this.analyzeHOCUsage(node);
194
+ }
195
+ if (((_h = (_g = node.callee) == null ? void 0 : _g.object) == null ? void 0 : _h.value) === "React") {
196
+ if (((_j = (_i = node.callee) == null ? void 0 : _i.property) == null ? void 0 : _j.value) === "memo") {
197
+ this.analyzeMemoUsage(node);
198
+ } else if (((_l = (_k = node.callee) == null ? void 0 : _k.property) == null ? void 0 : _l.value) === "forwardRef") {
199
+ this.analyzeForwardRefUsage(node);
200
+ }
201
+ }
202
+ if (((_n = (_m = node.callee) == null ? void 0 : _m.property) == null ? void 0 : _n.value) === "createPortal" || ((_o = node.callee) == null ? void 0 : _o.value) === "createPortal") {
203
+ this.analyzePortalUsage(node);
204
+ }
205
+ this.visitChildren(node);
206
+ }
207
+ analyzeLazyImport(node) {
208
+ var _a, _b, _c, _d, _e, _f;
209
+ const arg = (_a = node.arguments) == null ? void 0 : _a[0];
210
+ if ((arg == null ? void 0 : arg.type) === "ArrowFunctionExpression" && ((_b = arg.body) == null ? void 0 : _b.type) === "CallExpression") {
211
+ const importCall = arg.body;
212
+ if (((_c = importCall.callee) == null ? void 0 : _c.type) === "Import") {
213
+ const source = (_e = (_d = importCall.arguments) == null ? void 0 : _d[0]) == null ? void 0 : _e.value;
214
+ if (this.isLibraryImport(source)) {
215
+ this.usagePatterns.lazyImports.add({
216
+ source,
217
+ line: ((_f = node.span) == null ? void 0 : _f.start) || 0
218
+ });
219
+ console.log(`\u{1F504} Found lazy import: ${source}`);
220
+ }
221
+ }
222
+ }
223
+ }
224
+ analyzeDynamicImport(node) {
225
+ var _a, _b, _c;
226
+ const source = (_b = (_a = node.arguments) == null ? void 0 : _a[0]) == null ? void 0 : _b.value;
227
+ if (this.isLibraryImport(source)) {
228
+ this.usagePatterns.dynamicImports.add({
229
+ source,
230
+ line: ((_c = node.span) == null ? void 0 : _c.start) || 0
231
+ });
232
+ console.log(`\u26A1 Found dynamic import: ${source}`);
233
+ }
234
+ }
235
+ analyzeVariableDeclaration(node) {
236
+ var _a;
237
+ (_a = node.declarations) == null ? void 0 : _a.forEach((decl) => {
238
+ var _a2, _b, _c;
239
+ if (((_a2 = decl.id) == null ? void 0 : _a2.type) === "Identifier") {
240
+ const varName = decl.id.value;
241
+ if (decl.init) {
242
+ const assignment = this.extractAssignmentInfo(decl.init);
243
+ if (assignment && this.isLibraryComponent(assignment)) {
244
+ this.usagePatterns.variableAssignments.set(varName, {
245
+ assignment,
246
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
247
+ });
248
+ this.componentNames.add(varName);
249
+ console.log(`\u{1F4DD} Variable assignment: ${varName} = ${assignment}`);
250
+ }
251
+ }
252
+ }
253
+ if (((_c = decl.id) == null ? void 0 : _c.type) === "ObjectPattern") {
254
+ this.analyzeDestructuringPattern(decl.id, decl.init);
255
+ }
256
+ });
257
+ this.visitChildren(node);
258
+ }
259
+ analyzeDestructuringPattern(pattern, init) {
260
+ var _a;
261
+ (_a = pattern.properties) == null ? void 0 : _a.forEach((prop) => {
262
+ var _a2, _b;
263
+ if (prop.type === "AssignmentPatternProperty" && ((_a2 = prop.key) == null ? void 0 : _a2.type) === "Identifier") {
264
+ const propName = prop.key.value;
265
+ if ((init == null ? void 0 : init.type) === "Identifier" && this.allIdentifiers.has(init.value)) {
266
+ this.usagePatterns.destructuredUsage.add({
267
+ property: propName,
268
+ source: init.value,
269
+ line: ((_b = pattern.span) == null ? void 0 : _b.start) || 0
270
+ });
271
+ this.componentNames.add(propName);
272
+ console.log(`\u{1F527} Destructuring: ${propName} from ${init.value}`);
273
+ }
274
+ }
275
+ });
276
+ }
277
+ analyzeJSXElement(node, parent) {
278
+ if (node.opening) {
279
+ this.analyzeJSXOpeningElement(node.opening, node);
280
+ }
281
+ this.visitChildren(node);
282
+ }
283
+ analyzeJSXOpeningElement(node, parent) {
284
+ var _a;
285
+ const elementName = this.getJSXElementName(node.name);
286
+ if (this.componentNames.has(elementName) || this.isMemberExpressionComponent(node.name)) {
287
+ const propsAnalysis = this.analyzePropsInDetail(node.attributes);
288
+ const usage = {
289
+ component: elementName,
290
+ props: this.extractJSXProps(node.attributes),
291
+ propsAnalysis,
292
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0,
293
+ context: this.getUsageContext(parent)
294
+ };
295
+ if (!this.usagePatterns.jsxUsage.has(elementName)) {
296
+ this.usagePatterns.jsxUsage.set(elementName, []);
297
+ }
298
+ this.usagePatterns.jsxUsage.get(elementName).push(usage);
299
+ if (!this.usagePatterns.propsAnalysis.has(elementName)) {
300
+ this.usagePatterns.propsAnalysis.set(elementName, {
301
+ namedProps: /* @__PURE__ */ new Set(),
302
+ spreadProps: 0,
303
+ totalUsages: 0,
304
+ complexProps: 0
305
+ });
306
+ }
307
+ const componentPropsStats = this.usagePatterns.propsAnalysis.get(elementName);
308
+ componentPropsStats.totalUsages++;
309
+ propsAnalysis.namedProps.forEach(
310
+ (prop) => componentPropsStats.namedProps.add(prop)
311
+ );
312
+ if (propsAnalysis.hasSpread) componentPropsStats.spreadProps++;
313
+ if (propsAnalysis.hasComplexProps) componentPropsStats.complexProps++;
314
+ console.log(`\u{1F3A8} JSX Usage: <${elementName}>`);
315
+ }
316
+ }
317
+ getJSXElementName(nameNode) {
318
+ if (!nameNode) return "";
319
+ switch (nameNode.type) {
320
+ case "Identifier":
321
+ return nameNode.value;
322
+ case "JSXMemberExpression":
323
+ return `${this.getJSXElementName(nameNode.object)}.${nameNode.property.value}`;
324
+ default:
325
+ return "";
326
+ }
327
+ }
328
+ isMemberExpressionComponent(nameNode) {
329
+ if ((nameNode == null ? void 0 : nameNode.type) === "JSXMemberExpression") {
330
+ const objectName = this.getJSXElementName(nameNode.object);
331
+ return this.allIdentifiers.has(objectName);
332
+ }
333
+ return false;
334
+ }
335
+ extractJSXProps(attributes) {
336
+ if (!attributes) return [];
337
+ return attributes.map((attr) => {
338
+ var _a, _b, _c;
339
+ if (attr.type === "JSXAttribute") {
340
+ return {
341
+ name: ((_a = attr.name) == null ? void 0 : _a.value) || ((_c = (_b = attr.name) == null ? void 0 : _b.name) == null ? void 0 : _c.value),
342
+ value: this.extractJSXAttributeValue(attr.value)
343
+ };
344
+ } else if (attr.type === "SpreadElement") {
345
+ return {
346
+ name: "...",
347
+ value: "[spread]",
348
+ isSpread: true
349
+ };
350
+ }
351
+ return null;
352
+ }).filter(Boolean);
353
+ }
354
+ extractJSXAttributeValue(value) {
355
+ if (!value) return true;
356
+ switch (value.type) {
357
+ case "StringLiteral":
358
+ return value.value;
359
+ case "JSXExpressionContainer":
360
+ return this.extractExpressionValue(value.expression);
361
+ default:
362
+ return "[complex]";
363
+ }
364
+ }
365
+ extractExpressionValue(expr) {
366
+ if (!expr) return "[unknown]";
367
+ switch (expr.type) {
368
+ case "StringLiteral":
369
+ case "NumericLiteral":
370
+ case "BooleanLiteral":
371
+ return expr.value;
372
+ case "Identifier":
373
+ return `{${expr.value}}`;
374
+ case "ArrowFunctionExpression":
375
+ case "FunctionExpression":
376
+ return "[function]";
377
+ case "ObjectExpression":
378
+ return "[object]";
379
+ case "ArrayExpression":
380
+ return "[array]";
381
+ default:
382
+ return "[expression]";
383
+ }
384
+ }
385
+ analyzePropsInDetail(attributes) {
386
+ const analysis = {
387
+ namedProps: /* @__PURE__ */ new Set(),
388
+ hasSpread: false,
389
+ hasComplexProps: false,
390
+ hasEventHandlers: false,
391
+ propDetails: []
392
+ };
393
+ if (!attributes) return analysis;
394
+ attributes.forEach((attr) => {
395
+ var _a, _b, _c;
396
+ if (attr.type === "JSXAttribute") {
397
+ const propName = ((_a = attr.name) == null ? void 0 : _a.value) || ((_c = (_b = attr.name) == null ? void 0 : _b.name) == null ? void 0 : _c.value);
398
+ if (propName) {
399
+ analysis.namedProps.add(propName);
400
+ const propDetail = {
401
+ name: propName,
402
+ type: this.getPropType(attr.value),
403
+ isEventHandler: propName.startsWith("on"),
404
+ isComplex: this.isComplexProp(attr.value)
405
+ };
406
+ if (propDetail.isEventHandler) {
407
+ analysis.hasEventHandlers = true;
408
+ }
409
+ if (propDetail.isComplex) {
410
+ analysis.hasComplexProps = true;
411
+ }
412
+ analysis.propDetails.push(propDetail);
413
+ }
414
+ } else if (attr.type === "SpreadElement") {
415
+ analysis.hasSpread = true;
416
+ analysis.propDetails.push({
417
+ name: "...",
418
+ type: "spread",
419
+ isSpread: true,
420
+ isComplex: true,
421
+ warning: "Spread props cannot be statically analyzed"
422
+ });
423
+ analysis.hasComplexProps = true;
424
+ }
425
+ });
426
+ return analysis;
427
+ }
428
+ getPropType(value) {
429
+ if (!value) return "boolean";
430
+ switch (value.type) {
431
+ case "StringLiteral":
432
+ return "string";
433
+ case "JSXExpressionContainer":
434
+ const expr = value.expression;
435
+ if (!expr) return "unknown";
436
+ switch (expr.type) {
437
+ case "NumericLiteral":
438
+ return "number";
439
+ case "BooleanLiteral":
440
+ return "boolean";
441
+ case "StringLiteral":
442
+ return "string";
443
+ case "ArrowFunctionExpression":
444
+ case "FunctionExpression":
445
+ return "function";
446
+ case "ObjectExpression":
447
+ return "object";
448
+ case "ArrayExpression":
449
+ return "array";
450
+ case "Identifier":
451
+ return "variable";
452
+ default:
453
+ return "expression";
454
+ }
455
+ default:
456
+ return "unknown";
457
+ }
458
+ }
459
+ isComplexProp(value) {
460
+ if (!value) return false;
461
+ if (value.type === "JSXExpressionContainer") {
462
+ const expr = value.expression;
463
+ if (!expr) return false;
464
+ return expr.type === "ObjectExpression" || expr.type === "ArrayExpression" || expr.type === "CallExpression" || expr.type === "ConditionalExpression";
465
+ }
466
+ return false;
467
+ }
468
+ analyzeArrayExpression(node, parent) {
469
+ var _a, _b, _c;
470
+ const hasComponents = (_a = node.elements) == null ? void 0 : _a.some((elem) => {
471
+ if ((elem == null ? void 0 : elem.type) === "Identifier") {
472
+ return this.componentNames.has(elem.value);
473
+ }
474
+ return false;
475
+ });
476
+ if (hasComponents) {
477
+ this.usagePatterns.arrayMappings.add({
478
+ components: (_b = node.elements) == null ? void 0 : _b.map((elem) => elem == null ? void 0 : elem.value).filter(Boolean),
479
+ line: ((_c = node.span) == null ? void 0 : _c.start) || 0
480
+ });
481
+ console.log(`\u{1F4CB} Array with components found`);
482
+ }
483
+ this.visitChildren(node);
484
+ }
485
+ analyzeObjectExpression(node, parent) {
486
+ var _a, _b;
487
+ const componentProps = (_a = node.properties) == null ? void 0 : _a.filter((prop) => {
488
+ var _a2;
489
+ if (prop.type === "KeyValueProperty" && ((_a2 = prop.value) == null ? void 0 : _a2.type) === "Identifier") {
490
+ return this.componentNames.has(prop.value.value);
491
+ }
492
+ return false;
493
+ });
494
+ if ((componentProps == null ? void 0 : componentProps.length) > 0) {
495
+ this.usagePatterns.objectMappings.add({
496
+ mappings: componentProps.map((prop) => {
497
+ var _a2, _b2;
498
+ return {
499
+ key: ((_a2 = prop.key) == null ? void 0 : _a2.value) || "[computed]",
500
+ component: (_b2 = prop.value) == null ? void 0 : _b2.value
501
+ };
502
+ }),
503
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
504
+ });
505
+ console.log(`\u{1F5FA}\uFE0F Object mapping with components found`);
506
+ }
507
+ this.visitChildren(node);
508
+ }
509
+ analyzeConditionalExpression(node, parent) {
510
+ var _a, _b, _c;
511
+ const consequent = ((_a = node.consequent) == null ? void 0 : _a.type) === "Identifier" ? node.consequent.value : null;
512
+ const alternate = ((_b = node.alternate) == null ? void 0 : _b.type) === "Identifier" ? node.alternate.value : null;
513
+ if (consequent && this.componentNames.has(consequent) || alternate && this.componentNames.has(alternate)) {
514
+ this.usagePatterns.conditionalUsage.add({
515
+ consequent,
516
+ alternate,
517
+ line: ((_c = node.span) == null ? void 0 : _c.start) || 0
518
+ });
519
+ console.log(`\u{1F500} Conditional component usage found`);
520
+ }
521
+ this.visitChildren(node);
522
+ }
523
+ analyzeFunctionDeclaration(node) {
524
+ var _a, _b, _c;
525
+ if (this.isHOCFunction(node)) {
526
+ this.usagePatterns.hocUsage.add({
527
+ name: ((_a = node.identifier) == null ? void 0 : _a.value) || "[anonymous]",
528
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
529
+ });
530
+ console.log(`\u{1F527} HOC function found: ${(_c = node.identifier) == null ? void 0 : _c.value}`);
531
+ }
532
+ this.visitChildren(node);
533
+ }
534
+ analyzeClassDeclaration(node) {
535
+ this.visitChildren(node);
536
+ }
537
+ isHOCPattern(node) {
538
+ var _a, _b;
539
+ return ((_a = node.callee) == null ? void 0 : _a.type) === "Identifier" && ((_b = node.arguments) == null ? void 0 : _b.some(
540
+ (arg) => arg.type === "Identifier" && this.componentNames.has(arg.value)
541
+ ));
542
+ }
543
+ isHOCFunction(node) {
544
+ const params = node.params || [];
545
+ return params.some((param) => {
546
+ var _a;
547
+ return ((_a = param.pat) == null ? void 0 : _a.type) === "Identifier";
548
+ });
549
+ }
550
+ analyzeHOCUsage(node) {
551
+ var _a, _b, _c, _d;
552
+ this.usagePatterns.hocUsage.add({
553
+ function: (_a = node.callee) == null ? void 0 : _a.value,
554
+ component: (_c = (_b = node.arguments) == null ? void 0 : _b[0]) == null ? void 0 : _c.value,
555
+ line: ((_d = node.span) == null ? void 0 : _d.start) || 0
556
+ });
557
+ }
558
+ analyzeMemoUsage(node) {
559
+ var _a, _b;
560
+ const component = (_a = node.arguments) == null ? void 0 : _a[0];
561
+ if ((component == null ? void 0 : component.type) === "Identifier" && this.componentNames.has(component.value)) {
562
+ this.usagePatterns.memoizedComponents.add({
563
+ component: component.value,
564
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
565
+ });
566
+ console.log(`\u{1F9E0} Memoized component: ${component.value}`);
567
+ }
568
+ }
569
+ analyzeForwardRefUsage(node) {
570
+ var _a;
571
+ this.usagePatterns.forwardedRefs.add({
572
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0
573
+ });
574
+ console.log(`\u2197\uFE0F ForwardRef usage found`);
575
+ }
576
+ analyzePortalUsage(node) {
577
+ var _a;
578
+ this.usagePatterns.portalUsage.add({
579
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0
580
+ });
581
+ console.log(`\u{1F300} Portal usage found`);
582
+ }
583
+ analyzeMemberExpression(node, parent) {
584
+ var _a, _b;
585
+ if (((_a = node.object) == null ? void 0 : _a.type) === "Identifier" && this.allIdentifiers.has(node.object.value)) {
586
+ const namespaceName = node.object.value;
587
+ const propertyName = (_b = node.property) == null ? void 0 : _b.value;
588
+ if (propertyName) {
589
+ this.componentNames.add(propertyName);
590
+ console.log(`\u{1F517} Namespace access: ${namespaceName}.${propertyName}`);
591
+ }
592
+ }
593
+ this.visitChildren(node);
594
+ }
595
+ extractAssignmentInfo(node) {
596
+ switch (node.type) {
597
+ case "Identifier":
598
+ return node.value;
599
+ case "MemberExpression":
600
+ return `${this.extractAssignmentInfo(node.object)}.${node.property.value}`;
601
+ case "ConditionalExpression":
602
+ return `${this.extractAssignmentInfo(node.consequent)} | ${this.extractAssignmentInfo(node.alternate)}`;
603
+ default:
604
+ return null;
605
+ }
606
+ }
607
+ getUsageContext(parent) {
608
+ if (!parent) return "unknown";
609
+ switch (parent.type) {
610
+ case "JSXElement":
611
+ return "jsx";
612
+ case "CallExpression":
613
+ return "function-call";
614
+ case "ArrayExpression":
615
+ return "array";
616
+ case "ConditionalExpression":
617
+ return "conditional";
618
+ default:
619
+ return "other";
620
+ }
621
+ }
622
+ isLibraryImport(source) {
623
+ return source && (source.startsWith(this.libraryName) || source.includes(this.libraryName));
624
+ }
625
+ isLibraryComponent(name) {
626
+ return this.componentNames.has(name) || this.allIdentifiers.has(name);
627
+ }
628
+ generateReport() {
629
+ const report = {
630
+ summary: {
631
+ totalImports: this.usagePatterns.defaultImports.size + this.usagePatterns.namedImports.size + this.usagePatterns.namespaceImports.size,
632
+ totalComponents: this.componentNames.size,
633
+ totalUsagePatterns: Object.keys(this.usagePatterns).reduce(
634
+ (sum, key) => {
635
+ const pattern = this.usagePatterns[key];
636
+ if (pattern instanceof Set) return sum + pattern.size;
637
+ if (pattern instanceof Map) return sum + pattern.size;
638
+ return sum;
639
+ },
640
+ 0
641
+ )
642
+ },
643
+ patterns: {
644
+ imports: {
645
+ default: Array.from(this.usagePatterns.defaultImports),
646
+ named: Array.from(this.usagePatterns.namedImports),
647
+ namespace: Array.from(this.usagePatterns.namespaceImports),
648
+ aliased: Array.from(this.usagePatterns.aliasedImports.entries()).map(
649
+ ([key, value]) => ({
650
+ alias: key,
651
+ ...value
652
+ })
653
+ )
654
+ },
655
+ usage: {
656
+ jsx: Array.from(this.usagePatterns.jsxUsage.entries()).map(
657
+ ([component, usages]) => ({
658
+ component,
659
+ count: usages.length,
660
+ usages
661
+ })
662
+ ),
663
+ variables: Array.from(
664
+ this.usagePatterns.variableAssignments.entries()
665
+ ).map(([key, value]) => ({
666
+ variable: key,
667
+ ...value
668
+ })),
669
+ destructuring: Array.from(this.usagePatterns.destructuredUsage),
670
+ conditional: Array.from(this.usagePatterns.conditionalUsage),
671
+ arrays: Array.from(this.usagePatterns.arrayMappings),
672
+ objects: Array.from(this.usagePatterns.objectMappings)
673
+ },
674
+ advanced: {
675
+ lazy: Array.from(this.usagePatterns.lazyImports),
676
+ dynamic: Array.from(this.usagePatterns.dynamicImports),
677
+ hoc: Array.from(this.usagePatterns.hocUsage),
678
+ memo: Array.from(this.usagePatterns.memoizedComponents),
679
+ forwardRef: Array.from(this.usagePatterns.forwardedRefs),
680
+ portal: Array.from(this.usagePatterns.portalUsage)
681
+ },
682
+ props: Array.from(this.usagePatterns.propsAnalysis.entries()).map(
683
+ ([component, stats]) => ({
684
+ component,
685
+ namedProps: Array.from(stats.namedProps),
686
+ spreadPropsCount: stats.spreadProps,
687
+ totalUsages: stats.totalUsages,
688
+ complexPropsCount: stats.complexProps,
689
+ spreadWarning: stats.spreadProps > 0 ? `${stats.spreadProps} usage(s) with spread props - cannot analyze statically` : null
690
+ })
691
+ )
692
+ },
693
+ components: Array.from(this.componentNames).sort()
694
+ };
695
+ return report;
696
+ }
697
+ printReport(report) {
698
+ console.log("\n" + "=".repeat(80));
699
+ console.log("\u{1F4CA} REACT COMPONENT USAGE ANALYSIS REPORT");
700
+ console.log("=".repeat(80));
701
+ console.log(`
702
+ \u{1F4C8} SUMMARY:`);
703
+ console.log(` Total Imports: ${report.summary.totalImports}`);
704
+ console.log(` Components Found: ${report.summary.totalComponents}`);
705
+ console.log(` Usage Patterns: ${report.summary.totalUsagePatterns}`);
706
+ console.log(`
707
+ \u{1F4E6} IMPORT PATTERNS:`);
708
+ if (report.patterns.imports.default.length > 0) {
709
+ console.log(
710
+ ` Default Imports (${report.patterns.imports.default.length}):`
711
+ );
712
+ report.patterns.imports.default.forEach((imp) => {
713
+ console.log(` - ${imp.name} from "${imp.source}"`);
714
+ });
715
+ }
716
+ if (report.patterns.imports.named.length > 0) {
717
+ console.log(
718
+ ` Named Imports (${report.patterns.imports.named.length}):`
719
+ );
720
+ report.patterns.imports.named.forEach((imp) => {
721
+ console.log(
722
+ ` - {${imp.imported}${imp.imported !== imp.local ? ` as ${imp.local}` : ""}} from "${imp.source}"`
723
+ );
724
+ });
725
+ }
726
+ if (report.patterns.imports.namespace.length > 0) {
727
+ console.log(
728
+ ` Namespace Imports (${report.patterns.imports.namespace.length}):`
729
+ );
730
+ report.patterns.imports.namespace.forEach((imp) => {
731
+ console.log(` - * as ${imp.name} from "${imp.source}"`);
732
+ });
733
+ }
734
+ if (report.patterns.imports.aliased.length > 0) {
735
+ console.log(
736
+ ` Aliased Imports (${report.patterns.imports.aliased.length}):`
737
+ );
738
+ report.patterns.imports.aliased.forEach((imp) => {
739
+ console.log(
740
+ ` - ${imp.alias} (originally ${imp.original}) from "${imp.source}"`
741
+ );
742
+ });
743
+ }
744
+ console.log(`
745
+ \u{1F3A8} JSX USAGE PATTERNS:`);
746
+ report.patterns.usage.jsx.forEach((usage) => {
747
+ console.log(
748
+ ` Component: ${usage.component} (used ${usage.count} times)`
749
+ );
750
+ usage.usages.slice(0, 3).forEach((use, idx) => {
751
+ const props = use.props.length > 0 ? ` with ${use.props.length} props` : "";
752
+ console.log(
753
+ ` ${idx + 1}. <${usage.component}${props}> (line ~${use.line})`
754
+ );
755
+ });
756
+ if (usage.usages.length > 3) {
757
+ console.log(` ... and ${usage.usages.length - 3} more`);
758
+ }
759
+ });
760
+ if (report.patterns.usage.variables.length > 0) {
761
+ console.log(`
762
+ \u{1F4DD} VARIABLE ASSIGNMENTS:`);
763
+ report.patterns.usage.variables.forEach((variable) => {
764
+ console.log(` ${variable.variable} = ${variable.assignment}`);
765
+ });
766
+ }
767
+ if (report.patterns.advanced.lazy.length > 0) {
768
+ console.log(`
769
+ \u{1F504} LAZY LOADING:`);
770
+ report.patterns.advanced.lazy.forEach((lazy) => {
771
+ console.log(` - Lazy import from "${lazy.source}"`);
772
+ });
773
+ }
774
+ if (report.patterns.advanced.dynamic.length > 0) {
775
+ console.log(`
776
+ \u26A1 DYNAMIC IMPORTS:`);
777
+ report.patterns.advanced.dynamic.forEach((dynamic) => {
778
+ console.log(` - Dynamic import from "${dynamic.source}"`);
779
+ });
780
+ }
781
+ if (report.patterns.usage.conditional.length > 0) {
782
+ console.log(`
783
+ \u{1F500} CONDITIONAL USAGE:`);
784
+ report.patterns.usage.conditional.forEach((cond) => {
785
+ console.log(
786
+ ` - ${cond.consequent || "null"} ? ${cond.alternate || "null"}`
787
+ );
788
+ });
789
+ }
790
+ console.log(`
791
+ \u{1F9E9} COMPONENTS IDENTIFIED:`);
792
+ report.components.forEach((comp) => {
793
+ console.log(` - ${comp}`);
794
+ });
795
+ console.log("\n" + "=".repeat(80));
796
+ }
797
+ };
798
+ var FocusedUsageAnalyzer = class extends ReactComponentUsageAnalyzer {
799
+ constructor(libraryName = "@design-system/foundation") {
800
+ super(libraryName);
801
+ this.patternMap = /* @__PURE__ */ new Map();
802
+ this.componentFrequency = /* @__PURE__ */ new Map();
803
+ this.usageComplexity = /* @__PURE__ */ new Map();
804
+ }
805
+ analyzePatterns() {
806
+ const patterns = {
807
+ "Direct Import & Usage": {
808
+ weight: 1,
809
+ description: "Simple import and direct JSX usage",
810
+ examples: ['import Button from "lib"; <Button />']
811
+ },
812
+ "Named Import with Alias": {
813
+ weight: 2,
814
+ description: "Named import with renaming",
815
+ examples: ['import { Button as MyButton } from "lib"; <MyButton />']
816
+ },
817
+ "Namespace Import": {
818
+ weight: 2,
819
+ description: "Import entire namespace",
820
+ examples: ['import * as Lib from "lib"; <Lib.Button />']
821
+ },
822
+ "Variable Assignment": {
823
+ weight: 3,
824
+ description: "Assigning components to variables",
825
+ examples: ["const MyButton = Button; <MyButton />"]
826
+ },
827
+ "Conditional Assignment": {
828
+ weight: 4,
829
+ description: "Conditional component selection",
830
+ examples: ["const Comp = condition ? Button : Input; <Comp />"]
831
+ },
832
+ "Object Mapping": {
833
+ weight: 5,
834
+ description: "Components stored in objects",
835
+ examples: ["const map = {btn: Button}; <map.btn />"]
836
+ },
837
+ "Array Mapping": {
838
+ weight: 5,
839
+ description: "Components in arrays",
840
+ examples: ["[Button, Input].map(Comp => <Comp />)"]
841
+ },
842
+ "Dynamic Mapping": {
843
+ weight: 6,
844
+ description: "Runtime component selection",
845
+ examples: ["components[type]"]
846
+ },
847
+ "HOC Wrapping": {
848
+ weight: 7,
849
+ description: "Higher-order component patterns",
850
+ examples: ["withProps(Button)"]
851
+ },
852
+ "Lazy Loading": {
853
+ weight: 6,
854
+ description: "Lazy-loaded components",
855
+ examples: ['lazy(() => import("lib/Button"))']
856
+ },
857
+ "Dynamic Import": {
858
+ weight: 7,
859
+ description: "Runtime dynamic imports",
860
+ examples: ['await import("lib/Button")']
861
+ },
862
+ "Destructuring Usage": {
863
+ weight: 4,
864
+ description: "Destructured from objects",
865
+ examples: ["const {Button} = Foundation; <Button />"]
866
+ },
867
+ "Memoized Components": {
868
+ weight: 5,
869
+ description: "React.memo wrapped components",
870
+ examples: ["memo(Button)"]
871
+ },
872
+ "Forward Ref": {
873
+ weight: 6,
874
+ description: "forwardRef wrapped components",
875
+ examples: ["forwardRef((props, ref) => <Button ref={ref} />)"]
876
+ },
877
+ "Portal Usage": {
878
+ weight: 8,
879
+ description: "Components rendered in portals",
880
+ examples: ["createPortal(<Button />, document.body)"]
881
+ },
882
+ "Context Integration": {
883
+ weight: 7,
884
+ description: "Components from React context",
885
+ examples: ["const {Button} = useContext(ThemeContext)"]
886
+ }
887
+ };
888
+ return patterns;
889
+ }
890
+ classifyUsage(report) {
891
+ const patterns = this.analyzePatterns();
892
+ const foundPatterns = /* @__PURE__ */ new Map();
893
+ if (report.patterns.imports.default.length > 0) {
894
+ foundPatterns.set("Direct Import & Usage", {
895
+ count: report.patterns.imports.default.length,
896
+ complexity: 1,
897
+ examples: report.patterns.imports.default.slice(0, 3)
898
+ });
899
+ }
900
+ if (report.patterns.imports.aliased.length > 0) {
901
+ foundPatterns.set("Named Import with Alias", {
902
+ count: report.patterns.imports.aliased.length,
903
+ complexity: 2,
904
+ examples: report.patterns.imports.aliased.slice(0, 3)
905
+ });
906
+ }
907
+ if (report.patterns.imports.namespace.length > 0) {
908
+ foundPatterns.set("Namespace Import", {
909
+ count: report.patterns.imports.namespace.length,
910
+ complexity: 2,
911
+ examples: report.patterns.imports.namespace.slice(0, 3)
912
+ });
913
+ }
914
+ if (report.patterns.usage.variables.length > 0) {
915
+ foundPatterns.set("Variable Assignment", {
916
+ count: report.patterns.usage.variables.length,
917
+ complexity: 3,
918
+ examples: report.patterns.usage.variables.slice(0, 3)
919
+ });
920
+ }
921
+ if (report.patterns.usage.conditional.length > 0) {
922
+ foundPatterns.set("Conditional Assignment", {
923
+ count: report.patterns.usage.conditional.length,
924
+ complexity: 4,
925
+ examples: report.patterns.usage.conditional.slice(0, 3)
926
+ });
927
+ }
928
+ if (report.patterns.usage.objects.length > 0) {
929
+ foundPatterns.set("Object Mapping", {
930
+ count: report.patterns.usage.objects.length,
931
+ complexity: 5,
932
+ examples: report.patterns.usage.objects.slice(0, 3)
933
+ });
934
+ }
935
+ if (report.patterns.usage.arrays.length > 0) {
936
+ foundPatterns.set("Array Mapping", {
937
+ count: report.patterns.usage.arrays.length,
938
+ complexity: 5,
939
+ examples: report.patterns.usage.arrays.slice(0, 3)
940
+ });
941
+ }
942
+ if (report.patterns.advanced.lazy.length > 0) {
943
+ foundPatterns.set("Lazy Loading", {
944
+ count: report.patterns.advanced.lazy.length,
945
+ complexity: 6,
946
+ examples: report.patterns.advanced.lazy.slice(0, 3)
947
+ });
948
+ }
949
+ if (report.patterns.advanced.dynamic.length > 0) {
950
+ foundPatterns.set("Dynamic Import", {
951
+ count: report.patterns.advanced.dynamic.length,
952
+ complexity: 7,
953
+ examples: report.patterns.advanced.dynamic.slice(0, 3)
954
+ });
955
+ }
956
+ if (report.patterns.usage.destructuring.length > 0) {
957
+ foundPatterns.set("Destructuring Usage", {
958
+ count: report.patterns.usage.destructuring.length,
959
+ complexity: 4,
960
+ examples: report.patterns.usage.destructuring.slice(0, 3)
961
+ });
962
+ }
963
+ if (report.patterns.advanced.memo.length > 0) {
964
+ foundPatterns.set("Memoized Components", {
965
+ count: report.patterns.advanced.memo.length,
966
+ complexity: 5,
967
+ examples: report.patterns.advanced.memo.slice(0, 3)
968
+ });
969
+ }
970
+ if (report.patterns.advanced.forwardRef.length > 0) {
971
+ foundPatterns.set("Forward Ref", {
972
+ count: report.patterns.advanced.forwardRef.length,
973
+ complexity: 6,
974
+ examples: report.patterns.advanced.forwardRef.slice(0, 3)
975
+ });
976
+ }
977
+ if (report.patterns.advanced.portal.length > 0) {
978
+ foundPatterns.set("Portal Usage", {
979
+ count: report.patterns.advanced.portal.length,
980
+ complexity: 8,
981
+ examples: report.patterns.advanced.portal.slice(0, 3)
982
+ });
983
+ }
984
+ return { patterns, foundPatterns };
985
+ }
986
+ generateComplexityScore(foundPatterns) {
987
+ let totalScore = 0;
988
+ let maxPossibleScore = 0;
989
+ foundPatterns.forEach((data, patternName) => {
990
+ const weight = data.complexity;
991
+ const score = weight * data.count;
992
+ totalScore += score;
993
+ maxPossibleScore += weight * 10;
994
+ });
995
+ return {
996
+ score: totalScore,
997
+ maxPossible: maxPossibleScore,
998
+ percentage: Math.round(
999
+ totalScore / Math.max(maxPossibleScore, 1) * 100
1000
+ ),
1001
+ level: this.getComplexityLevel(totalScore)
1002
+ };
1003
+ }
1004
+ getComplexityLevel(score) {
1005
+ if (score <= 10) return "Simple";
1006
+ if (score <= 30) return "Moderate";
1007
+ if (score <= 60) return "Complex";
1008
+ if (score <= 100) return "Very Complex";
1009
+ return "Extremely Complex";
1010
+ }
1011
+ generateRecommendations(foundPatterns, complexity) {
1012
+ const recommendations = [];
1013
+ if (foundPatterns.has("Dynamic Import") || foundPatterns.has("Portal Usage")) {
1014
+ recommendations.push({
1015
+ type: "Performance",
1016
+ priority: "High",
1017
+ message: "Consider code splitting strategies for dynamic imports",
1018
+ action: "Implement lazy loading boundaries"
1019
+ });
1020
+ }
1021
+ if (foundPatterns.has("Object Mapping") || foundPatterns.has("Array Mapping")) {
1022
+ recommendations.push({
1023
+ type: "Maintainability",
1024
+ priority: "Medium",
1025
+ message: "Component mappings can be hard to track",
1026
+ action: "Consider using TypeScript for better type safety"
1027
+ });
1028
+ }
1029
+ if (complexity.level === "Extremely Complex") {
1030
+ recommendations.push({
1031
+ type: "Architecture",
1032
+ priority: "High",
1033
+ message: "High complexity detected in component usage",
1034
+ action: "Consider refactoring to simpler patterns"
1035
+ });
1036
+ }
1037
+ if (foundPatterns.size > 8) {
1038
+ recommendations.push({
1039
+ type: "Consistency",
1040
+ priority: "Medium",
1041
+ message: "Many different usage patterns found",
1042
+ action: "Standardize on 2-3 primary patterns"
1043
+ });
1044
+ }
1045
+ return recommendations;
1046
+ }
1047
+ printFocusedReport(report) {
1048
+ const { patterns, foundPatterns } = this.classifyUsage(report);
1049
+ const complexity = this.generateComplexityScore(foundPatterns);
1050
+ const recommendations = this.generateRecommendations(
1051
+ foundPatterns,
1052
+ complexity
1053
+ );
1054
+ console.log("\n" + "\u{1F3AF}".repeat(40));
1055
+ console.log("\u{1F3AF} FOCUSED COMPONENT USAGE ANALYSIS");
1056
+ console.log("\u{1F3AF}".repeat(40));
1057
+ console.log(`
1058
+ \u{1F4CA} COMPLEXITY ANALYSIS:`);
1059
+ console.log(
1060
+ ` Overall Score: ${complexity.score}/${complexity.maxPossible}`
1061
+ );
1062
+ console.log(
1063
+ ` Complexity Level: ${complexity.level} (${complexity.percentage}%)`
1064
+ );
1065
+ console.log(
1066
+ `
1067
+ \u{1F50D} PATTERNS DETECTED (${foundPatterns.size} of ${Object.keys(patterns).length}):`
1068
+ );
1069
+ const sortedPatterns = Array.from(foundPatterns.entries()).sort(
1070
+ (a, b) => b[1].complexity - a[1].complexity
1071
+ );
1072
+ sortedPatterns.forEach(([patternName, data]) => {
1073
+ const patternInfo = patterns[patternName];
1074
+ console.log(
1075
+ `
1076
+ ${this.getComplexityIcon(data.complexity)} ${patternName}:`
1077
+ );
1078
+ console.log(` Complexity: ${data.complexity}/10`);
1079
+ console.log(` Instances: ${data.count}`);
1080
+ console.log(` Description: ${patternInfo.description}`);
1081
+ if (data.examples.length > 0) {
1082
+ console.log(
1083
+ ` Examples: ${JSON.stringify(data.examples[0], null, " ").slice(0, 100)}...`
1084
+ );
1085
+ }
1086
+ });
1087
+ console.log(`
1088
+ \u{1F4C8} COMPONENT FREQUENCY:`);
1089
+ report.patterns.usage.jsx.forEach((usage) => {
1090
+ console.log(` ${usage.component}: ${usage.count} uses`);
1091
+ });
1092
+ if (recommendations.length > 0) {
1093
+ console.log(`
1094
+ \u{1F4A1} RECOMMENDATIONS:`);
1095
+ recommendations.forEach((rec, idx) => {
1096
+ console.log(` ${idx + 1}. [${rec.priority}] ${rec.type}:`);
1097
+ console.log(` Issue: ${rec.message}`);
1098
+ console.log(` Action: ${rec.action}`);
1099
+ });
1100
+ }
1101
+ console.log(`
1102
+ \u{1F4CB} PATTERN COVERAGE:`);
1103
+ Object.keys(patterns).forEach((patternName) => {
1104
+ const found = foundPatterns.has(patternName);
1105
+ console.log(` ${found ? "\u2705" : "\u274C"} ${patternName}`);
1106
+ });
1107
+ console.log(`
1108
+ \u{1F3AF} USAGE DENSITY:`);
1109
+ const density = foundPatterns.size / Object.keys(patterns).length * 100;
1110
+ console.log(` Pattern Coverage: ${Math.round(density)}%`);
1111
+ console.log(
1112
+ ` Usage Intensity: ${this.getUsageIntensity(complexity.score)}`
1113
+ );
1114
+ return { foundPatterns, complexity, recommendations };
1115
+ }
1116
+ getComplexityIcon(complexity) {
1117
+ if (complexity <= 2) return "\u{1F7E2}";
1118
+ if (complexity <= 4) return "\u{1F7E1}";
1119
+ if (complexity <= 6) return "\u{1F7E0}";
1120
+ return "\u{1F534}";
1121
+ }
1122
+ getUsageIntensity(score) {
1123
+ if (score <= 10) return "Light";
1124
+ if (score <= 30) return "Moderate";
1125
+ if (score <= 60) return "Heavy";
1126
+ return "Intensive";
1127
+ }
1128
+ analyzeMultipleFiles(filePatterns) {
1129
+ const allReports = [];
1130
+ const combinedAnalysis = {
1131
+ totalFiles: 0,
1132
+ totalComponents: /* @__PURE__ */ new Set(),
1133
+ patternFrequency: /* @__PURE__ */ new Map(),
1134
+ complexityDistribution: []
1135
+ };
1136
+ filePatterns.forEach((pattern) => {
1137
+ const files = this.findMatchingFiles(pattern);
1138
+ files.forEach((file) => {
1139
+ console.log(`
1140
+ \u{1F4C1} Analyzing: ${file}`);
1141
+ const report = this.analyzeFile(file);
1142
+ if (report) {
1143
+ allReports.push({ file, report });
1144
+ combinedAnalysis.totalFiles++;
1145
+ report.components.forEach(
1146
+ (comp) => combinedAnalysis.totalComponents.add(comp)
1147
+ );
1148
+ const { foundPatterns, complexity } = this.classifyUsage(report);
1149
+ combinedAnalysis.complexityDistribution.push({
1150
+ file,
1151
+ score: complexity.score,
1152
+ level: complexity.level
1153
+ });
1154
+ foundPatterns.forEach((data, pattern2) => {
1155
+ const current = combinedAnalysis.patternFrequency.get(pattern2) || 0;
1156
+ combinedAnalysis.patternFrequency.set(pattern2, current + 1);
1157
+ });
1158
+ }
1159
+ });
1160
+ });
1161
+ return { allReports, combinedAnalysis };
1162
+ }
1163
+ findMatchingFiles(pattern) {
1164
+ const glob5 = __require("glob");
1165
+ try {
1166
+ return glob5.sync(pattern);
1167
+ } catch (e) {
1168
+ console.warn(`Warning: Could not process pattern ${pattern}`);
1169
+ return [];
1170
+ }
1171
+ }
1172
+ };
1173
+ async function findFiles(pattern, ignorePatterns, maxFiles) {
1174
+ const allFiles = await glob.glob(pattern, {
1175
+ ignore: ignorePatterns || [
1176
+ "**/node_modules/**",
1177
+ "**/dist/**",
1178
+ "**/build/**"
1179
+ ],
1180
+ nodir: true,
1181
+ absolute: true,
1182
+ // Only match React/JS/TS files
1183
+ matchBase: true
1184
+ });
1185
+ const reactFiles = allFiles.filter((file) => {
1186
+ var _a;
1187
+ const ext = (_a = file.split(".").pop()) == null ? void 0 : _a.toLowerCase();
1188
+ return ["tsx", "jsx", "ts", "js"].includes(ext || "");
1189
+ });
1190
+ return maxFiles ? reactFiles.slice(0, maxFiles) : reactFiles;
1191
+ }
1192
+ function generateReportFilename(commandType, extension = "json") {
1193
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1194
+ return `${commandType}-report-${timestamp}.${extension}`;
1195
+ }
1196
+ function addReportMetadata(data, commandType, additionalMetadata = {}) {
1197
+ return {
1198
+ ...data,
1199
+ metadata: {
1200
+ ...data.metadata,
1201
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1202
+ commandType,
1203
+ ...additionalMetadata
1204
+ }
1205
+ };
1206
+ }
1207
+ function saveReport(options) {
1208
+ const { data, commandType, outputPath, format = "both" } = options;
1209
+ if (format === "console") {
1210
+ return "";
1211
+ }
1212
+ const filename = outputPath || generateReportFilename(commandType);
1213
+ const reportsDir = path__default.default.join(process.cwd(), "reports-outputs");
1214
+ if (!fs5__default.default.existsSync(reportsDir)) {
1215
+ fs5__default.default.mkdirSync(reportsDir, { recursive: true });
1216
+ }
1217
+ const finalPath = path__default.default.isAbsolute(filename) ? filename : path__default.default.join(reportsDir, filename);
1218
+ const reportWithMetadata = addReportMetadata(data, commandType);
1219
+ fs5__default.default.writeFileSync(finalPath, JSON.stringify(reportWithMetadata, null, 2));
1220
+ console.log(chalk8__default.default.blue(`
1221
+ \u{1F4C4} JSON report saved to: ${finalPath}`));
1222
+ return finalPath;
1223
+ }
1224
+ function getRankEmoji(rank) {
1225
+ switch (rank) {
1226
+ case 1:
1227
+ return "\u{1F947}";
1228
+ case 2:
1229
+ return "\u{1F948}";
1230
+ case 3:
1231
+ return "\u{1F949}";
1232
+ default:
1233
+ return "\u{1F4CD}";
1234
+ }
1235
+ }
1236
+
1237
+ // src/commands/analyze.ts
1238
+ async function analyzeCommand(pattern, options) {
1239
+ const spinner = ora6__default.default("Finding files to analyze...").start();
1240
+ try {
1241
+ const files = await findFiles(pattern, options.ignore, options.maxFiles);
1242
+ if (files.length === 0) {
1243
+ spinner.fail(chalk8__default.default.red("No files found matching pattern: " + pattern));
1244
+ return;
1245
+ }
1246
+ spinner.succeed(chalk8__default.default.green(`Found ${files.length} files to analyze`));
1247
+ spinner.start("Analyzing files...");
1248
+ const analyzer = options.complexity ? new FocusedUsageAnalyzer(options.library) : new ReactComponentUsageAnalyzer(options.library);
1249
+ const aggregatedReport = {
1250
+ metadata: {
1251
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1252
+ commandType: "analyze",
1253
+ library: options.library,
1254
+ pattern,
1255
+ filesAnalyzed: 0,
1256
+ filesWithErrors: 0,
1257
+ totalFiles: files.length
1258
+ },
1259
+ files: [],
1260
+ aggregated: {
1261
+ allComponents: /* @__PURE__ */ new Set(),
1262
+ totalImports: 0,
1263
+ totalUsagePatterns: 0,
1264
+ patternFrequency: {},
1265
+ componentFrequency: {},
1266
+ fileComplexity: [],
1267
+ errors: []
1268
+ }
1269
+ };
1270
+ for (let i = 0; i < files.length; i++) {
1271
+ const file = files[i];
1272
+ spinner.text = `Analyzing files... (${i + 1}/${files.length}) ${path__default.default.basename(file)}`;
1273
+ try {
1274
+ const report = analyzer.analyzeFile(file);
1275
+ if (report) {
1276
+ aggregatedReport.metadata.filesAnalyzed++;
1277
+ const fileResult = {
1278
+ path: file,
1279
+ components: report.components,
1280
+ summary: report.summary,
1281
+ patterns: report.patterns
1282
+ };
1283
+ if (options.complexity) {
1284
+ const analysis = analyzer.classifyUsage(report);
1285
+ const complexity = analyzer.generateComplexityScore(
1286
+ analysis.foundPatterns
1287
+ );
1288
+ fileResult.complexity = complexity;
1289
+ aggregatedReport.aggregated.fileComplexity.push({
1290
+ file,
1291
+ score: complexity.score,
1292
+ level: complexity.level
1293
+ });
1294
+ }
1295
+ aggregatedReport.files.push(fileResult);
1296
+ report.components.forEach(
1297
+ (comp) => aggregatedReport.aggregated.allComponents.add(comp)
1298
+ );
1299
+ aggregatedReport.aggregated.totalImports += report.summary.totalImports;
1300
+ aggregatedReport.aggregated.totalUsagePatterns += report.summary.totalUsagePatterns;
1301
+ report.patterns.usage.jsx.forEach((usage) => {
1302
+ aggregatedReport.aggregated.componentFrequency[usage.component] = (aggregatedReport.aggregated.componentFrequency[usage.component] || 0) + usage.count;
1303
+ });
1304
+ Object.keys(report.patterns).forEach((category) => {
1305
+ Object.keys(report.patterns[category]).forEach((patternType) => {
1306
+ const pattern2 = report.patterns[category][patternType];
1307
+ const count = Array.isArray(pattern2) ? pattern2.length : 0;
1308
+ if (count > 0) {
1309
+ const key = `${category}.${patternType}`;
1310
+ aggregatedReport.aggregated.patternFrequency[key] = (aggregatedReport.aggregated.patternFrequency[key] || 0) + count;
1311
+ }
1312
+ });
1313
+ });
1314
+ }
1315
+ } catch (error) {
1316
+ aggregatedReport.metadata.filesWithErrors++;
1317
+ aggregatedReport.aggregated.errors.push({
1318
+ file,
1319
+ error: error.message
1320
+ });
1321
+ }
1322
+ }
1323
+ aggregatedReport.aggregated.allComponents = Array.from(
1324
+ aggregatedReport.aggregated.allComponents
1325
+ );
1326
+ spinner.succeed(
1327
+ chalk8__default.default.green(
1328
+ `Analysis complete! Analyzed ${aggregatedReport.metadata.filesAnalyzed} files`
1329
+ )
1330
+ );
1331
+ if (options.format === "json" || options.format === "both") {
1332
+ saveReport({
1333
+ data: aggregatedReport,
1334
+ commandType: "analyze",
1335
+ outputPath: options.output,
1336
+ format: options.format
1337
+ });
1338
+ }
1339
+ if (options.format === "console" || options.format === "both") {
1340
+ printAggregatedReport(aggregatedReport, options);
1341
+ }
1342
+ } catch (error) {
1343
+ spinner.fail(chalk8__default.default.red("Analysis failed: " + error.message));
1344
+ console.error(error);
1345
+ }
1346
+ }
1347
+ function printAggregatedReport(report, options) {
1348
+ console.log("\n" + chalk8__default.default.bold.blue("\u2550".repeat(80)));
1349
+ console.log(chalk8__default.default.bold.blue(" \u{1F4CA} AGGREGATED ANALYSIS REPORT"));
1350
+ console.log(chalk8__default.default.bold.blue("\u2550".repeat(80)));
1351
+ console.log(chalk8__default.default.bold("\n\u{1F4C8} SUMMARY:"));
1352
+ console.log(` Library: ${chalk8__default.default.cyan(report.metadata.library)}`);
1353
+ console.log(
1354
+ ` Files Analyzed: ${chalk8__default.default.green(report.metadata.filesAnalyzed)} / ${report.metadata.totalFiles}`
1355
+ );
1356
+ if (report.metadata.filesWithErrors > 0) {
1357
+ console.log(
1358
+ ` Files with Errors: ${chalk8__default.default.red(report.metadata.filesWithErrors)}`
1359
+ );
1360
+ }
1361
+ console.log(
1362
+ ` Total Components: ${chalk8__default.default.yellow(report.aggregated.allComponents.length)}`
1363
+ );
1364
+ console.log(
1365
+ ` Total Imports: ${chalk8__default.default.yellow(report.aggregated.totalImports)}`
1366
+ );
1367
+ console.log(
1368
+ ` Total Usage Patterns: ${chalk8__default.default.yellow(report.aggregated.totalUsagePatterns)}`
1369
+ );
1370
+ if (!options.summaryOnly) {
1371
+ console.log(chalk8__default.default.bold("\n\u{1F3AF} TOP COMPONENTS:"));
1372
+ const topComponents = Object.entries(report.aggregated.componentFrequency).sort((a, b) => b[1] - a[1]).slice(0, 10);
1373
+ topComponents.forEach(([comp, count], index) => {
1374
+ const rank = index + 1;
1375
+ const emoji = getRankEmoji(rank);
1376
+ console.log(
1377
+ ` ${emoji} ${rank}. ${chalk8__default.default.cyan(comp)}: ${chalk8__default.default.yellow(count)} uses`
1378
+ );
1379
+ });
1380
+ console.log(chalk8__default.default.bold("\n\u{1F50D} PATTERN FREQUENCY:"));
1381
+ const topPatterns = Object.entries(report.aggregated.patternFrequency).sort((a, b) => b[1] - a[1]).slice(0, 10);
1382
+ topPatterns.forEach(([pattern, count], index) => {
1383
+ console.log(` ${index + 1}. ${pattern}: ${chalk8__default.default.yellow(count)}`);
1384
+ });
1385
+ if (report.aggregated.fileComplexity.length > 0) {
1386
+ console.log(chalk8__default.default.bold("\n\u{1F4CA} COMPLEXITY ANALYSIS:"));
1387
+ const avgComplexity = report.aggregated.fileComplexity.reduce(
1388
+ (sum, f) => sum + f.score,
1389
+ 0
1390
+ ) / report.aggregated.fileComplexity.length;
1391
+ console.log(
1392
+ ` Average Complexity: ${chalk8__default.default.yellow(avgComplexity.toFixed(2))}`
1393
+ );
1394
+ const mostComplex = report.aggregated.fileComplexity.sort((a, b) => b.score - a.score).slice(0, 5);
1395
+ console.log(chalk8__default.default.bold("\n Most Complex Files:"));
1396
+ mostComplex.forEach((file, index) => {
1397
+ console.log(
1398
+ ` ${index + 1}. ${path__default.default.basename(file.file)}: ${chalk8__default.default.yellow(file.score)} (${file.level})`
1399
+ );
1400
+ });
1401
+ }
1402
+ }
1403
+ if (report.aggregated.errors.length > 0) {
1404
+ console.log(chalk8__default.default.bold.red("\n\u26A0\uFE0F ERRORS:"));
1405
+ report.aggregated.errors.slice(0, 5).forEach((error) => {
1406
+ console.log(
1407
+ ` ${chalk8__default.default.red("\u2717")} ${path__default.default.basename(error.file)}: ${error.error}`
1408
+ );
1409
+ });
1410
+ if (report.aggregated.errors.length > 5) {
1411
+ console.log(
1412
+ ` ... and ${report.aggregated.errors.length - 5} more errors`
1413
+ );
1414
+ }
1415
+ }
1416
+ console.log("\n" + chalk8__default.default.bold.blue("\u2550".repeat(80)) + "\n");
1417
+ }
1418
+ async function compareCommand(pattern, options) {
1419
+ const spinner = ora6__default.default("Finding files to analyze...").start();
1420
+ try {
1421
+ const files = await findFiles(pattern, [], 1e3);
1422
+ if (files.length === 0) {
1423
+ spinner.fail(chalk8__default.default.red("No files found matching pattern: " + pattern));
1424
+ return;
1425
+ }
1426
+ spinner.succeed(chalk8__default.default.green(`Found ${files.length} files`));
1427
+ const comparisonResults = {
1428
+ metadata: {
1429
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1430
+ commandType: "compare",
1431
+ pattern,
1432
+ filesAnalyzed: files.length,
1433
+ libraries: options.libraries
1434
+ },
1435
+ libraries: []
1436
+ };
1437
+ for (const library of options.libraries) {
1438
+ spinner.start(`Analyzing ${library}...`);
1439
+ const analyzer = new FocusedUsageAnalyzer(library);
1440
+ let componentsFound = 0;
1441
+ let usagePatterns = 0;
1442
+ const components = /* @__PURE__ */ new Set();
1443
+ for (const file of files) {
1444
+ try {
1445
+ const report = analyzer.analyzeFile(file);
1446
+ if (report) {
1447
+ componentsFound += report.summary.totalComponents;
1448
+ usagePatterns += report.summary.totalUsagePatterns;
1449
+ report.components.forEach((comp) => components.add(comp));
1450
+ }
1451
+ } catch (error) {
1452
+ }
1453
+ }
1454
+ const libraryResult = {
1455
+ name: library,
1456
+ componentsFound: components.size,
1457
+ totalUsagePatterns: usagePatterns,
1458
+ topComponents: Array.from(components).slice(0, 10)
1459
+ };
1460
+ comparisonResults.libraries.push(libraryResult);
1461
+ spinner.succeed(
1462
+ chalk8__default.default.green(`${library}: ${components.size} components found`)
1463
+ );
1464
+ }
1465
+ comparisonResults.libraries.sort(
1466
+ (a, b) => b.componentsFound - a.componentsFound
1467
+ );
1468
+ if (options.format === "json" || options.format === "both") {
1469
+ saveReport({
1470
+ data: comparisonResults,
1471
+ commandType: "compare",
1472
+ outputPath: options.output,
1473
+ format: options.format
1474
+ });
1475
+ }
1476
+ if (options.format === "console" || options.format === "both") {
1477
+ printComparisonReport(comparisonResults);
1478
+ }
1479
+ } catch (error) {
1480
+ spinner.fail(chalk8__default.default.red("Comparison failed: " + error.message));
1481
+ console.error(error);
1482
+ }
1483
+ }
1484
+ function printComparisonReport(results) {
1485
+ console.log("\n" + chalk8__default.default.bold.blue("\u2550".repeat(60)));
1486
+ console.log(chalk8__default.default.bold.blue(" \u{1F4CA} LIBRARY COMPARISON REPORT"));
1487
+ console.log(chalk8__default.default.bold.blue("\u2550".repeat(60)));
1488
+ console.log(chalk8__default.default.bold("\n\u{1F3C6} RESULTS:"));
1489
+ results.libraries.forEach((lib, index) => {
1490
+ const rank = index + 1;
1491
+ const emoji = getRankEmoji(rank);
1492
+ console.log(
1493
+ ` ${emoji} ${rank}. ${chalk8__default.default.cyan(lib.name)}: ${chalk8__default.default.yellow(lib.componentsFound)} components, ${chalk8__default.default.yellow(lib.totalUsagePatterns)} usage patterns`
1494
+ );
1495
+ });
1496
+ console.log("\n" + chalk8__default.default.bold.blue("\u2550".repeat(60)) + "\n");
1497
+ }
1498
+ function parseGitHubUrl(url) {
1499
+ if (/^[\w-]+\/[\w-]+$/.test(url)) {
1500
+ const [owner, repo] = url.split("/");
1501
+ return {
1502
+ owner,
1503
+ repo: repo.replace(".git", ""),
1504
+ url: `https://github.com/${owner}/${repo}`,
1505
+ shorthand: url
1506
+ };
1507
+ }
1508
+ const httpsMatch = url.match(/github\.com\/([^/]+)\/([^/.]+)/);
1509
+ if (httpsMatch) {
1510
+ return {
1511
+ owner: httpsMatch[1],
1512
+ repo: httpsMatch[2],
1513
+ url,
1514
+ shorthand: `${httpsMatch[1]}/${httpsMatch[2]}`
1515
+ };
1516
+ }
1517
+ throw new Error(
1518
+ `Invalid GitHub URL: ${url}. Expected format: https://github.com/owner/repo or owner/repo`
1519
+ );
1520
+ }
1521
+ function createTempDir() {
1522
+ return new Promise((resolve, reject) => {
1523
+ tmp__default.default.dir(
1524
+ { unsafeCleanup: true, prefix: "react-analyzer-" },
1525
+ (err, dirPath, cleanup) => {
1526
+ if (err) reject(err);
1527
+ resolve({ path: dirPath, cleanup });
1528
+ }
1529
+ );
1530
+ });
1531
+ }
1532
+ async function cloneRepository(repoUrl, targetDir, options = {}) {
1533
+ const { branch = "main", depth = 1 } = options;
1534
+ const repoInfo = parseGitHubUrl(repoUrl);
1535
+ const localPath = path__default.default.join(targetDir, `${repoInfo.owner}-${repoInfo.repo}`);
1536
+ const git = simpleGit__default.default();
1537
+ const cloneOptions = [
1538
+ "--depth",
1539
+ depth.toString(),
1540
+ "--branch",
1541
+ branch,
1542
+ "--single-branch"
1543
+ ];
1544
+ try {
1545
+ await git.clone(repoInfo.url, localPath, cloneOptions);
1546
+ return {
1547
+ ...repoInfo,
1548
+ localPath,
1549
+ branch,
1550
+ success: true
1551
+ };
1552
+ } catch (error) {
1553
+ if (error instanceof Error && error.message.includes("not found") && branch === "main") {
1554
+ try {
1555
+ const fallbackOptions = [
1556
+ "--depth",
1557
+ "1",
1558
+ "--branch",
1559
+ "master",
1560
+ "--single-branch"
1561
+ ];
1562
+ await git.clone(repoInfo.url, localPath, fallbackOptions);
1563
+ return {
1564
+ ...repoInfo,
1565
+ localPath,
1566
+ branch: "master",
1567
+ success: true
1568
+ };
1569
+ } catch (retryError) {
1570
+ throw retryError;
1571
+ }
1572
+ }
1573
+ throw error;
1574
+ }
1575
+ }
1576
+ async function cloneRepositories(repoUrls, targetDir, options = {}) {
1577
+ const spinner = ora6__default.default("Cloning repositories...").start();
1578
+ const clonedRepos = [];
1579
+ const errors = [];
1580
+ for (const repoUrl of repoUrls) {
1581
+ try {
1582
+ spinner.text = `Cloning ${repoUrl}...`;
1583
+ const repoInfo = await cloneRepository(repoUrl, targetDir, options);
1584
+ clonedRepos.push(repoInfo);
1585
+ spinner.succeed(
1586
+ chalk8__default.default.green(
1587
+ `\u2713 Cloned ${repoInfo.shorthand} (${repoInfo.branch} branch)`
1588
+ )
1589
+ );
1590
+ spinner.start();
1591
+ } catch (error) {
1592
+ const message = error instanceof Error ? error.message : String(error);
1593
+ errors.push({
1594
+ url: repoUrl,
1595
+ error: message
1596
+ });
1597
+ spinner.fail(chalk8__default.default.red(`\u2717 Failed to clone ${repoUrl}: ${message}`));
1598
+ spinner.start();
1599
+ }
1600
+ }
1601
+ spinner.stop();
1602
+ if (clonedRepos.length === 0) {
1603
+ throw new Error("No repositories were successfully cloned");
1604
+ }
1605
+ console.log(
1606
+ chalk8__default.default.green(
1607
+ `
1608
+ Successfully cloned ${clonedRepos.length} of ${repoUrls.length} repositories
1609
+ `
1610
+ )
1611
+ );
1612
+ return { clonedRepos, errors };
1613
+ }
1614
+ async function findFilesInRepo(repo, pattern = "**/*.{tsx,jsx,ts,js}") {
1615
+ if (!repo.localPath) {
1616
+ throw new Error("Repository localPath is not defined");
1617
+ }
1618
+ const normalizedPath = repo.localPath.replace(/\\/g, "/");
1619
+ const searchPattern = `${normalizedPath}/${pattern}`;
1620
+ const files = await glob.glob(searchPattern, {
1621
+ ignore: ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.git/**"],
1622
+ nodir: true,
1623
+ windowsPathsNoEscape: true
1624
+ });
1625
+ return files;
1626
+ }
1627
+ async function findFilesInRepos(repos, pattern = "**/*.{tsx,jsx,ts,js}") {
1628
+ const filesByRepo = {};
1629
+ for (const repo of repos) {
1630
+ const files = await findFilesInRepo(repo, pattern);
1631
+ filesByRepo[repo.shorthand] = {
1632
+ repo,
1633
+ files,
1634
+ count: files.length
1635
+ };
1636
+ }
1637
+ return filesByRepo;
1638
+ }
1639
+ async function getRepoStats(repoPath) {
1640
+ try {
1641
+ const packageJsonPath = path__default.default.join(repoPath, "package.json");
1642
+ let packageJson = null;
1643
+ if (fs5__default.default.existsSync(packageJsonPath)) {
1644
+ packageJson = JSON.parse(fs5__default.default.readFileSync(packageJsonPath, "utf8"));
1645
+ }
1646
+ const normalizedPath = repoPath.replace(/\\/g, "/");
1647
+ const files = await glob.glob(`${normalizedPath}/**/*.{tsx,jsx,ts,js}`, {
1648
+ ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"],
1649
+ nodir: true,
1650
+ windowsPathsNoEscape: true
1651
+ });
1652
+ const fileTypes = {
1653
+ tsx: 0,
1654
+ jsx: 0,
1655
+ ts: 0,
1656
+ js: 0
1657
+ };
1658
+ files.forEach((file) => {
1659
+ const ext = path__default.default.extname(file).slice(1);
1660
+ if (fileTypes.hasOwnProperty(ext)) {
1661
+ fileTypes[ext]++;
1662
+ }
1663
+ });
1664
+ return {
1665
+ packageJson,
1666
+ name: (packageJson == null ? void 0 : packageJson.name) || "unknown",
1667
+ version: (packageJson == null ? void 0 : packageJson.version) || "unknown",
1668
+ dependencies: (packageJson == null ? void 0 : packageJson.dependencies) || {},
1669
+ devDependencies: (packageJson == null ? void 0 : packageJson.devDependencies) || {},
1670
+ totalFiles: files.length,
1671
+ fileTypes
1672
+ };
1673
+ } catch (error) {
1674
+ const message = error instanceof Error ? error.message : String(error);
1675
+ return {
1676
+ error: message,
1677
+ name: "unknown",
1678
+ version: "unknown",
1679
+ dependencies: {},
1680
+ devDependencies: {},
1681
+ totalFiles: 0,
1682
+ fileTypes: { tsx: 0, jsx: 0, ts: 0, js: 0 }
1683
+ };
1684
+ }
1685
+ }
1686
+ function generateCombinedReport(analysisResults) {
1687
+ const combined = {
1688
+ totalComponents: [],
1689
+ totalImports: [],
1690
+ componentsByRepo: {},
1691
+ componentFrequency: {},
1692
+ importFrequency: {},
1693
+ repoSummaries: []
1694
+ };
1695
+ const totalComponentsSet = /* @__PURE__ */ new Set();
1696
+ const totalImportsSet = /* @__PURE__ */ new Set();
1697
+ Object.entries(analysisResults.repositories).forEach(
1698
+ ([repoName, repoData]) => {
1699
+ repoData.analysis.components.forEach(
1700
+ (comp) => totalComponentsSet.add(comp)
1701
+ );
1702
+ Object.keys(repoData.analysis.imports).forEach(
1703
+ (imp) => totalImportsSet.add(imp)
1704
+ );
1705
+ combined.componentsByRepo[repoName] = repoData.analysis.components;
1706
+ Object.entries(repoData.analysis.componentUsage).forEach(
1707
+ ([comp, count]) => {
1708
+ combined.componentFrequency[comp] = (combined.componentFrequency[comp] || 0) + count;
1709
+ }
1710
+ );
1711
+ Object.entries(repoData.analysis.imports).forEach(([imp, data]) => {
1712
+ combined.importFrequency[imp] = (combined.importFrequency[imp] || 0) + data.count;
1713
+ });
1714
+ combined.repoSummaries.push({
1715
+ name: repoName,
1716
+ components: repoData.analysis.components.length,
1717
+ files: repoData.files.length,
1718
+ errors: repoData.analysis.errors.length,
1719
+ topComponents: Object.entries(repoData.analysis.componentUsage).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([comp, count]) => ({ component: comp, uses: count }))
1720
+ });
1721
+ }
1722
+ );
1723
+ combined.totalComponents = Array.from(totalComponentsSet);
1724
+ combined.totalImports = Array.from(totalImportsSet);
1725
+ return combined;
1726
+ }
1727
+ function cleanupTempDir(cleanup, tmpDir, keepRepos = false) {
1728
+ if (!keepRepos && cleanup) {
1729
+ try {
1730
+ cleanup();
1731
+ console.log(chalk8__default.default.gray("\n\u{1F9F9} Cleaned up temporary directory"));
1732
+ } catch (error) {
1733
+ const message = error instanceof Error ? error.message : String(error);
1734
+ console.warn(chalk8__default.default.yellow(`Warning: Failed to cleanup: ${message}`));
1735
+ }
1736
+ } else if (keepRepos && tmpDir) {
1737
+ console.log(chalk8__default.default.blue(`
1738
+ \u{1F4C1} Repositories kept in: ${tmpDir}`));
1739
+ }
1740
+ }
1741
+ function parsePackageLock(lockFilePath) {
1742
+ try {
1743
+ const content = fs5__default.default.readFileSync(lockFilePath, "utf8");
1744
+ const lockData = JSON.parse(content);
1745
+ const versions = {};
1746
+ if (lockData.packages) {
1747
+ Object.entries(lockData.packages).forEach(
1748
+ ([pkgPath, pkgData]) => {
1749
+ if (!pkgPath || pkgPath === "") return;
1750
+ const pkgName = pkgPath.replace(/^node_modules\//, "");
1751
+ if (pkgData.version) {
1752
+ versions[pkgName] = pkgData.version;
1753
+ }
1754
+ }
1755
+ );
1756
+ }
1757
+ if (lockData.dependencies && Object.keys(versions).length === 0) {
1758
+ let extractVersions = function(deps, prefix = "") {
1759
+ Object.entries(deps).forEach(([name, data]) => {
1760
+ const fullName = prefix ? `${prefix}/${name}` : name;
1761
+ if (data.version) {
1762
+ versions[fullName] = data.version;
1763
+ }
1764
+ if (data.dependencies) {
1765
+ extractVersions(data.dependencies, fullName);
1766
+ }
1767
+ });
1768
+ };
1769
+ extractVersions(lockData.dependencies);
1770
+ }
1771
+ return versions;
1772
+ } catch (error) {
1773
+ const message = error instanceof Error ? error.message : String(error);
1774
+ console.warn(`Warning: Could not parse package-lock.json: ${message}`);
1775
+ return {};
1776
+ }
1777
+ }
1778
+ function parseYarnLock(lockFilePath) {
1779
+ try {
1780
+ const content = fs5__default.default.readFileSync(lockFilePath, "utf8");
1781
+ const parsed = lockfile__default.default.parse(content);
1782
+ if (parsed.type !== "success") {
1783
+ console.warn("Warning: Failed to parse yarn.lock");
1784
+ return {};
1785
+ }
1786
+ const versions = {};
1787
+ Object.entries(parsed.object).forEach(([key, value]) => {
1788
+ let pkgName = key;
1789
+ if (key.startsWith("@")) {
1790
+ const match = key.match(/^(@[^@]+\/[^@]+)@/);
1791
+ if (match) {
1792
+ pkgName = match[1];
1793
+ }
1794
+ } else {
1795
+ const match = key.match(/^([^@]+)@/);
1796
+ if (match) {
1797
+ pkgName = match[1];
1798
+ }
1799
+ }
1800
+ if (value.version && (!versions[pkgName] || value.version)) {
1801
+ versions[pkgName] = value.version;
1802
+ }
1803
+ });
1804
+ return versions;
1805
+ } catch (error) {
1806
+ const message = error instanceof Error ? error.message : String(error);
1807
+ console.warn(`Warning: Could not parse yarn.lock: ${message}`);
1808
+ return {};
1809
+ }
1810
+ }
1811
+ function parsePnpmLock(lockFilePath) {
1812
+ try {
1813
+ const content = fs5__default.default.readFileSync(lockFilePath, "utf8");
1814
+ const lockData = yaml__default.default.load(content);
1815
+ const versions = {};
1816
+ if (lockData.importers) {
1817
+ const rootImporter = lockData.importers["."];
1818
+ if (rootImporter) {
1819
+ if (rootImporter.dependencies) {
1820
+ Object.entries(rootImporter.dependencies).forEach(
1821
+ ([name, data]) => {
1822
+ if (typeof data === "object" && data.version) {
1823
+ versions[name] = data.version;
1824
+ }
1825
+ }
1826
+ );
1827
+ }
1828
+ if (rootImporter.devDependencies) {
1829
+ Object.entries(rootImporter.devDependencies).forEach(
1830
+ ([name, data]) => {
1831
+ if (typeof data === "object" && data.version) {
1832
+ versions[name] = data.version;
1833
+ }
1834
+ }
1835
+ );
1836
+ }
1837
+ }
1838
+ }
1839
+ if (lockData.packages && Object.keys(versions).length === 0) {
1840
+ Object.keys(lockData.packages).forEach((key) => {
1841
+ const match = key.match(/\/(.+?)\/(\d+\.\d+\.\d+.*?)(?:_|$)/);
1842
+ if (match) {
1843
+ const [, pkgName, version] = match;
1844
+ versions[pkgName] = version;
1845
+ }
1846
+ });
1847
+ }
1848
+ if (lockData.dependencies && Object.keys(versions).length === 0) {
1849
+ Object.entries(lockData.dependencies).forEach(
1850
+ ([name, versionSpec]) => {
1851
+ if (typeof versionSpec === "string" && !versionSpec.startsWith("link:")) {
1852
+ versions[name] = versionSpec;
1853
+ } else if (typeof versionSpec === "object" && versionSpec.version) {
1854
+ versions[name] = versionSpec.version;
1855
+ }
1856
+ }
1857
+ );
1858
+ }
1859
+ return versions;
1860
+ } catch (error) {
1861
+ const message = error instanceof Error ? error.message : String(error);
1862
+ console.warn(`Warning: Could not parse pnpm-lock.yaml: ${message}`);
1863
+ return {};
1864
+ }
1865
+ }
1866
+ function findAndParseLockfile(projectPath) {
1867
+ const lockfiles = [
1868
+ {
1869
+ name: "package-lock.json",
1870
+ parser: parsePackageLock,
1871
+ type: "npm"
1872
+ },
1873
+ { name: "yarn.lock", parser: parseYarnLock, type: "yarn" },
1874
+ { name: "pnpm-lock.yaml", parser: parsePnpmLock, type: "pnpm" }
1875
+ ];
1876
+ for (const { name, parser, type } of lockfiles) {
1877
+ const lockfilePath = path__default.default.join(projectPath, name);
1878
+ if (fs5__default.default.existsSync(lockfilePath)) {
1879
+ const versions = parser(lockfilePath);
1880
+ return {
1881
+ versions,
1882
+ lockfileType: type,
1883
+ lockfilePath,
1884
+ found: true
1885
+ };
1886
+ }
1887
+ }
1888
+ return {
1889
+ versions: {},
1890
+ lockfileType: null,
1891
+ lockfilePath: null,
1892
+ found: false
1893
+ };
1894
+ }
1895
+ function readFile(filePath) {
1896
+ return fs5__default.default.readFileSync(filePath, "utf8");
1897
+ }
1898
+ function readJsonFile(filePath) {
1899
+ const content = readFile(filePath);
1900
+ return JSON.parse(content);
1901
+ }
1902
+
1903
+ // src/github-analysis.ts
1904
+ async function analyzeGitHubRepositories(repoUrls, analyzer, options = {}) {
1905
+ const {
1906
+ branch = "main",
1907
+ pattern = "**/*.{tsx,jsx,ts,js}",
1908
+ depth = 1,
1909
+ keepRepos = false
1910
+ } = options;
1911
+ console.log(chalk8__default.default.bold.cyan("\n\u{1F50D} GitHub Repository Analysis\n"));
1912
+ console.log(chalk8__default.default.gray(`Repositories to analyze: ${repoUrls.length}`));
1913
+ console.log(chalk8__default.default.gray(`Pattern: ${pattern}`));
1914
+ console.log(chalk8__default.default.gray(`Branch: ${branch}
1915
+ `));
1916
+ const { path: tmpDir, cleanup } = await createTempDir();
1917
+ try {
1918
+ const { clonedRepos, errors: cloneErrors } = await cloneRepositories(
1919
+ repoUrls,
1920
+ tmpDir,
1921
+ { branch, depth }
1922
+ );
1923
+ console.log(chalk8__default.default.bold("\n\u{1F4C1} Finding files in repositories...\n"));
1924
+ const filesByRepo = await findFilesInRepos(clonedRepos, pattern);
1925
+ Object.entries(filesByRepo).forEach(([repoName, data]) => {
1926
+ console.log(` ${chalk8__default.default.cyan(repoName)}: ${data.count} files`);
1927
+ });
1928
+ console.log(chalk8__default.default.bold("\n\u{1F4CA} Gathering repository statistics...\n"));
1929
+ const repoStats = {};
1930
+ const lockfileData = {};
1931
+ for (const repo of clonedRepos) {
1932
+ if (!repo.localPath) continue;
1933
+ const stats = await getRepoStats(repo.localPath);
1934
+ repoStats[repo.shorthand] = stats;
1935
+ const lockfileInfo = findAndParseLockfile(repo.localPath);
1936
+ lockfileData[repo.shorthand] = lockfileInfo;
1937
+ console.log(
1938
+ ` ${chalk8__default.default.cyan(repo.shorthand)}: ${stats.name}@${stats.version} (${stats.totalFiles} files)`
1939
+ );
1940
+ if (lockfileInfo.found) {
1941
+ console.log(
1942
+ chalk8__default.default.gray(
1943
+ ` Lockfile: ${lockfileInfo.lockfileType} (${Object.keys(lockfileInfo.versions).length} packages)`
1944
+ )
1945
+ );
1946
+ }
1947
+ }
1948
+ console.log(chalk8__default.default.bold("\n\u{1F52C} Analyzing repositories...\n"));
1949
+ const results = {};
1950
+ const spinner = ora6__default.default("Analyzing...").start();
1951
+ for (const [repoName, repoData] of Object.entries(filesByRepo)) {
1952
+ spinner.text = `Analyzing ${repoName}...`;
1953
+ const repoResults = {
1954
+ files: repoData.files,
1955
+ stats: repoStats[repoName],
1956
+ lockfile: lockfileData[repoName],
1957
+ analysis: {
1958
+ components: [],
1959
+ imports: {},
1960
+ componentUsage: {},
1961
+ errors: []
1962
+ }
1963
+ };
1964
+ const componentsSet = /* @__PURE__ */ new Set();
1965
+ const componentUsageTemp = /* @__PURE__ */ new Map();
1966
+ const importsTemp = /* @__PURE__ */ new Map();
1967
+ for (const file of repoData.files) {
1968
+ try {
1969
+ const report = analyzer.analyzeFile(file);
1970
+ if (report) {
1971
+ report.components.forEach((comp) => componentsSet.add(comp));
1972
+ report.patterns.usage.jsx.forEach((usage) => {
1973
+ const existing = componentUsageTemp.get(usage.component);
1974
+ if (!existing) {
1975
+ componentUsageTemp.set(usage.component, {
1976
+ count: usage.count,
1977
+ files: /* @__PURE__ */ new Set([file])
1978
+ });
1979
+ } else {
1980
+ existing.count += usage.count;
1981
+ existing.files.add(file);
1982
+ }
1983
+ });
1984
+ [
1985
+ ...report.patterns.imports.default,
1986
+ ...report.patterns.imports.named
1987
+ ].forEach((imp) => {
1988
+ const name = imp.name || imp.local || "";
1989
+ const source = imp.source || imp.from;
1990
+ const existing = importsTemp.get(name);
1991
+ if (!existing) {
1992
+ importsTemp.set(name, {
1993
+ count: 1,
1994
+ files: /* @__PURE__ */ new Set([file]),
1995
+ source
1996
+ });
1997
+ } else {
1998
+ existing.count++;
1999
+ existing.files.add(file);
2000
+ }
2001
+ });
2002
+ }
2003
+ } catch (error) {
2004
+ const message = error instanceof Error ? error.message : String(error);
2005
+ repoResults.analysis.errors.push({
2006
+ file,
2007
+ error: message
2008
+ });
2009
+ }
2010
+ }
2011
+ repoResults.analysis.components = Array.from(componentsSet);
2012
+ componentUsageTemp.forEach((value, key) => {
2013
+ repoResults.analysis.componentUsage[key] = {
2014
+ count: value.count,
2015
+ files: Array.from(value.files)
2016
+ };
2017
+ });
2018
+ importsTemp.forEach((value, key) => {
2019
+ repoResults.analysis.imports[key] = {
2020
+ count: value.count,
2021
+ files: Array.from(value.files),
2022
+ source: value.source
2023
+ };
2024
+ });
2025
+ results[repoName] = repoResults;
2026
+ spinner.succeed(
2027
+ chalk8__default.default.green(
2028
+ `\u2713 Analyzed ${repoName}: ${repoResults.analysis.components.length} components found`
2029
+ )
2030
+ );
2031
+ spinner.start();
2032
+ }
2033
+ spinner.stop();
2034
+ const analysisResults = {
2035
+ repositories: results,
2036
+ metadata: {
2037
+ analyzedAt: (/* @__PURE__ */ new Date()).toISOString(),
2038
+ totalRepositories: clonedRepos.length,
2039
+ failedClones: cloneErrors.length,
2040
+ pattern,
2041
+ branch,
2042
+ repoStats,
2043
+ lockfiles: lockfileData
2044
+ },
2045
+ cloneErrors
2046
+ };
2047
+ cleanupTempDir(cleanup, tmpDir, keepRepos);
2048
+ return analysisResults;
2049
+ } catch (error) {
2050
+ cleanupTempDir(cleanup, tmpDir, false);
2051
+ throw error;
2052
+ }
2053
+ }
2054
+ function loadRepositoriesFromConfig(configPath) {
2055
+ try {
2056
+ const config = readJsonFile(configPath);
2057
+ return config.repositories || [];
2058
+ } catch (error) {
2059
+ const message = error instanceof Error ? error.message : String(error);
2060
+ throw new Error(`Failed to load config file: ${message}`);
2061
+ }
2062
+ }
2063
+ function createGitHubAnalysisReport(analysisResults, libraryName) {
2064
+ const combined = generateCombinedReport(analysisResults);
2065
+ const enhancedComponentFrequency = {};
2066
+ Object.entries(analysisResults.repositories).forEach(
2067
+ ([repoName, repoData]) => {
2068
+ var _a;
2069
+ const lockfileData = repoData.lockfile || {};
2070
+ const version = (_a = lockfileData.versions) == null ? void 0 : _a[libraryName];
2071
+ Object.entries(repoData.analysis.componentUsage).forEach(
2072
+ ([component, usage]) => {
2073
+ const key = version ? `${component} from ${libraryName}@${version}` : `${component} from ${libraryName}`;
2074
+ if (!enhancedComponentFrequency[key]) {
2075
+ enhancedComponentFrequency[key] = {
2076
+ component,
2077
+ library: libraryName,
2078
+ version: version || "unknown",
2079
+ count: 0,
2080
+ repos: []
2081
+ };
2082
+ }
2083
+ enhancedComponentFrequency[key].count += usage.count || 0;
2084
+ enhancedComponentFrequency[key].repos.push({
2085
+ name: repoName,
2086
+ count: usage.count || 0
2087
+ });
2088
+ }
2089
+ );
2090
+ }
2091
+ );
2092
+ return {
2093
+ metadata: {
2094
+ ...analysisResults.metadata,
2095
+ library: libraryName,
2096
+ repositories: Object.keys(analysisResults.repositories)
2097
+ },
2098
+ combined: {
2099
+ ...combined,
2100
+ enhancedComponentFrequency
2101
+ },
2102
+ repositories: analysisResults.repositories,
2103
+ cloneErrors: analysisResults.cloneErrors
2104
+ };
2105
+ }
2106
+ function formatGitHubReport(report, options = {}) {
2107
+ const { metadata, combined, repositories } = report;
2108
+ console.log(chalk8__default.default.bold.cyan("\n" + "=".repeat(80)));
2109
+ console.log(chalk8__default.default.bold.cyan(" \u{1F680} GITHUB REPOSITORIES ANALYSIS REPORT"));
2110
+ console.log(chalk8__default.default.bold.cyan("=".repeat(80) + "\n"));
2111
+ console.log(chalk8__default.default.bold("\u{1F4C8} SUMMARY:"));
2112
+ console.log(chalk8__default.default.gray(` Library: ${chalk8__default.default.cyan(metadata.library)}`));
2113
+ console.log(
2114
+ chalk8__default.default.gray(` Repositories Analyzed: ${metadata.repositories.length}`)
2115
+ );
2116
+ console.log(
2117
+ chalk8__default.default.gray(` Total Components Found: ${combined.totalComponents.length}`)
2118
+ );
2119
+ console.log(
2120
+ chalk8__default.default.gray(` Total Imports Found: ${combined.totalImports.length}`)
2121
+ );
2122
+ const topComponents = Object.entries(combined.componentFrequency).sort((a, b) => b[1] - a[1]).slice(0, 10);
2123
+ if (topComponents.length > 0) {
2124
+ console.log(chalk8__default.default.bold("\n\u{1F3C6} TOP COMPONENTS (Across All Repos):"));
2125
+ topComponents.forEach(([comp, count], idx) => {
2126
+ const rank = idx + 1;
2127
+ const emoji = rank === 1 ? "\u{1F947}" : rank === 2 ? "\u{1F948}" : rank === 3 ? "\u{1F949}" : " ";
2128
+ console.log(
2129
+ ` ${emoji} ${rank}. ${chalk8__default.default.green(comp)}: ${chalk8__default.default.yellow(count)} uses`
2130
+ );
2131
+ });
2132
+ }
2133
+ if (combined.repoSummaries && combined.repoSummaries.length > 0) {
2134
+ console.log(chalk8__default.default.bold("\n\u{1F4E6} REPOSITORY SUMMARIES:\n"));
2135
+ combined.repoSummaries.forEach((summary, idx) => {
2136
+ console.log(` ${chalk8__default.default.bold(idx + 1 + ".")} ${chalk8__default.default.cyan(summary.name)}`);
2137
+ console.log(` Components: ${summary.components}`);
2138
+ console.log(` Files: ${summary.files}`);
2139
+ if (summary.topComponents && summary.topComponents.length > 0) {
2140
+ console.log(` Top Components:`);
2141
+ summary.topComponents.slice(0, 3).forEach((comp) => {
2142
+ console.log(` - ${comp.component}: ${comp.uses} uses`);
2143
+ });
2144
+ }
2145
+ console.log("");
2146
+ });
2147
+ }
2148
+ console.log(chalk8__default.default.bold("\u{1F50D} COMPONENT DISTRIBUTION:"));
2149
+ Object.entries(combined.componentsByRepo).forEach(([repo, components]) => {
2150
+ console.log(
2151
+ ` ${chalk8__default.default.cyan(repo)}: ${components.length} unique components`
2152
+ );
2153
+ });
2154
+ console.log(chalk8__default.default.bold.cyan("\n" + "=".repeat(80)));
2155
+ console.log(chalk8__default.default.bold("\n\u{1F4A1} TIPS:"));
2156
+ console.log(" \u2022 Use --keep-repos to inspect cloned repositories locally");
2157
+ console.log(" \u2022 Use --branch <name> to analyze different branches");
2158
+ console.log(" \u2022 Use --pattern to customize which files to analyze");
2159
+ console.log(" \u2022 Use --config <file> to load repositories from JSON file");
2160
+ console.log(" \u2022 JSON report contains detailed per-repo analysis");
2161
+ console.log(chalk8__default.default.bold("\n\u{1F4DD} CONFIG FILE FORMAT:"));
2162
+ console.log(
2163
+ chalk8__default.default.gray(` {
2164
+ "repositories": [
2165
+ "owner/repo1",
2166
+ "owner/repo2",
2167
+ "https://github.com/owner/repo3"
2168
+ ]
2169
+ }`)
2170
+ );
2171
+ console.log("");
2172
+ }
2173
+
2174
+ // src/commands/github.ts
2175
+ async function githubCommand(repos, options) {
2176
+ const spinner = ora6__default.default("Initializing GitHub analyzer...").start();
2177
+ try {
2178
+ let repoList = repos || [];
2179
+ if (options.config) {
2180
+ spinner.text = `Loading repositories from ${options.config}...`;
2181
+ try {
2182
+ repoList = loadRepositoriesFromConfig(options.config);
2183
+ spinner.succeed(
2184
+ chalk8__default.default.green(`Loaded ${repoList.length} repositories from config`)
2185
+ );
2186
+ spinner.start();
2187
+ } catch (error) {
2188
+ spinner.fail(chalk8__default.default.red(`Failed to load config file: ${error.message}`));
2189
+ process.exit(1);
2190
+ }
2191
+ }
2192
+ if (repoList.length === 0) {
2193
+ spinner.fail(
2194
+ chalk8__default.default.red(
2195
+ "No repositories specified. Use arguments or --config <file>"
2196
+ )
2197
+ );
2198
+ console.log(
2199
+ chalk8__default.default.yellow("\nExample: node cli.js github owner/repo1 owner/repo2")
2200
+ );
2201
+ console.log(chalk8__default.default.yellow("Or: node cli.js github --config repos.json"));
2202
+ process.exit(1);
2203
+ }
2204
+ spinner.succeed(chalk8__default.default.green("GitHub analyzer initialized"));
2205
+ const analyzer = options.complexity ? new FocusedUsageAnalyzer(options.library) : new ReactComponentUsageAnalyzer(options.library);
2206
+ const results = await analyzeGitHubRepositories(repoList, analyzer, {
2207
+ branch: options.branch,
2208
+ pattern: options.pattern,
2209
+ depth: parseInt(options.depth || "1"),
2210
+ keepRepos: options.keepRepos
2211
+ });
2212
+ const fullReport = createGitHubAnalysisReport(results, options.library);
2213
+ fullReport.metadata = {
2214
+ ...fullReport.metadata,
2215
+ commandType: "github",
2216
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2217
+ };
2218
+ if (options.format === "json" || options.format === "both") {
2219
+ saveReport({
2220
+ data: fullReport,
2221
+ commandType: "github",
2222
+ outputPath: options.output,
2223
+ format: options.format
2224
+ });
2225
+ }
2226
+ if (options.format === "console" || options.format === "both") {
2227
+ formatGitHubReport(fullReport, options);
2228
+ }
2229
+ } catch (error) {
2230
+ spinner.fail(chalk8__default.default.red("GitHub analysis failed: " + error.message));
2231
+ console.error(error);
2232
+ process.exit(1);
2233
+ }
2234
+ }
2235
+
2236
+ // src/cli.ts
2237
+ var program = new commander.Command();
2238
+ program.name("react-usage-analyzer").description("Analyze React component usage patterns in your codebase").version("1.0.0");
2239
+ program.command("analyze").description(
2240
+ "Analyze component usage patterns in files matching a glob pattern"
2241
+ ).argument(
2242
+ "<pattern>",
2243
+ 'Glob pattern for files to analyze (e.g., "src/**/*.tsx")'
2244
+ ).option(
2245
+ "-l, --library <name>",
2246
+ "Library name to analyze (e.g., @mui/material)",
2247
+ "@design-system/foundation"
2248
+ ).option(
2249
+ "-o, --output <file>",
2250
+ "Output file path for JSON report (defaults to timestamped filename)"
2251
+ ).option(
2252
+ "-f, --format <type>",
2253
+ "Output format: json, console, or both",
2254
+ "both"
2255
+ ).option("-c, --complexity", "Include complexity analysis", false).option(
2256
+ "-s, --summary-only",
2257
+ "Show only summary, not detailed patterns",
2258
+ false
2259
+ ).option(
2260
+ "--ignore <patterns...>",
2261
+ 'Glob patterns to ignore (e.g., "**/*.test.tsx")',
2262
+ []
2263
+ ).option("--max-files <number>", "Maximum number of files to analyze", "1000").action(async (pattern, options) => {
2264
+ await analyzeCommand(pattern, options);
2265
+ });
2266
+ program.command("compare").description("Compare usage patterns across multiple libraries").argument("<pattern>", "Glob pattern for files to analyze").option("-l, --libraries <names...>", "Library names to compare", [
2267
+ "@mui/material",
2268
+ "antd",
2269
+ "@chakra-ui/react"
2270
+ ]).option(
2271
+ "-o, --output <file>",
2272
+ "Output file path for comparison report (defaults to timestamped filename)"
2273
+ ).option(
2274
+ "-f, --format <type>",
2275
+ "Output format: json, console, or both",
2276
+ "both"
2277
+ ).action(async (pattern, options) => {
2278
+ await compareCommand(pattern, options);
2279
+ });
2280
+ program.command("summary").description("Generate a quick summary of component usage").argument("<pattern>", "Glob pattern for files to analyze").option(
2281
+ "-l, --library <name>",
2282
+ "Library name to analyze",
2283
+ "@design-system/foundation"
2284
+ ).option("--top <number>", "Number of top components to show", "10").action(async (pattern, options) => {
2285
+ await summaryCommand(pattern, options);
2286
+ });
2287
+ program.command("patterns").description("List all detected usage patterns").argument("<pattern>", "Glob pattern for files to analyze").option(
2288
+ "-l, --library <name>",
2289
+ "Library name to analyze",
2290
+ "@design-system/foundation"
2291
+ ).option("--sort <by>", "Sort by: frequency, complexity, or name", "frequency").action(async (pattern, options) => {
2292
+ await patternsCommand(pattern, options);
2293
+ });
2294
+ program.command("stats").description("Show detailed statistics about component usage").argument("<pattern>", "Glob pattern for files to analyze").option(
2295
+ "-l, --library <name>",
2296
+ "Library name to analyze",
2297
+ "@design-system/foundation"
2298
+ ).option("--chart", "Show ASCII charts", false).action(async (pattern, options) => {
2299
+ await statsCommand(pattern, options);
2300
+ });
2301
+ program.command("table").description("Show components and imports in table format").argument("<pattern>", "Glob pattern for files to analyze").option(
2302
+ "-l, --library <name>",
2303
+ "Library name to analyze",
2304
+ "@design-system/foundation"
2305
+ ).option("-s, --sort <by>", "Sort by: uses, name, files, or props", "uses").option("-t, --top <number>", "Number of top items to show", "20").option("--props", "Show props analysis", false).action(async (pattern, options) => {
2306
+ await tableCommand(pattern, options);
2307
+ });
2308
+ program.command("github").description("Analyze GitHub repositories").argument(
2309
+ "[repos...]",
2310
+ "GitHub repository URLs or owner/repo format (or use --config)"
2311
+ ).option(
2312
+ "-l, --library <name>",
2313
+ "Library name to analyze",
2314
+ "@design-system/foundation"
2315
+ ).option("-b, --branch <name>", "Branch to analyze", "main").option(
2316
+ "-p, --pattern <glob>",
2317
+ "File pattern to analyze",
2318
+ "**/*.{tsx,jsx,ts,js}"
2319
+ ).option(
2320
+ "-o, --output <file>",
2321
+ "Output JSON file (defaults to timestamped filename)"
2322
+ ).option(
2323
+ "-f, --format <type>",
2324
+ "Output format: json, console, or both",
2325
+ "both"
2326
+ ).option("--keep-repos", "Keep cloned repositories after analysis", false).option("--depth <number>", "Clone depth", "1").option("-c, --complexity", "Include complexity analysis", false).option("--config <file>", "Path to config file with repository list").action(async (repos, options) => {
2327
+ await githubCommand(repos, options);
2328
+ });
2329
+ async function summaryCommand(pattern, options) {
2330
+ const spinner = ora6__default.default("Generating summary...").start();
2331
+ try {
2332
+ const files = await findFiles2(pattern, [], 1e3);
2333
+ if (files.length === 0) {
2334
+ spinner.fail(chalk8__default.default.red("No files found"));
2335
+ return;
2336
+ }
2337
+ const analyzer = new ReactComponentUsageAnalyzer(options.library);
2338
+ const componentUsage = {};
2339
+ let totalFiles = 0;
2340
+ for (const file of files) {
2341
+ try {
2342
+ const report = analyzer.analyzeFile(file);
2343
+ if (report) {
2344
+ totalFiles++;
2345
+ report.patterns.usage.jsx.forEach((usage) => {
2346
+ if (!componentUsage[usage.component]) {
2347
+ componentUsage[usage.component] = { count: 0, files: /* @__PURE__ */ new Set() };
2348
+ }
2349
+ componentUsage[usage.component].count += usage.count;
2350
+ componentUsage[usage.component].files.add(file);
2351
+ });
2352
+ }
2353
+ } catch (error) {
2354
+ }
2355
+ }
2356
+ spinner.succeed(chalk8__default.default.green("Summary generated"));
2357
+ console.log(chalk8__default.default.bold("\n\u{1F4CA} COMPONENT USAGE SUMMARY\n"));
2358
+ console.log(chalk8__default.default.gray(`Library: ${options.library}`));
2359
+ console.log(chalk8__default.default.gray(`Files analyzed: ${totalFiles}
2360
+ `));
2361
+ const topN = parseInt(options.top);
2362
+ const sorted = Object.entries(componentUsage).map(([comp, data]) => ({
2363
+ comp,
2364
+ count: data.count,
2365
+ files: data.files.size
2366
+ })).sort((a, b) => b.count - a.count).slice(0, topN);
2367
+ console.log(chalk8__default.default.bold(`Top ${topN} Components:`));
2368
+ sorted.forEach((item, index) => {
2369
+ const rank = index + 1;
2370
+ const emoji = rank === 1 ? "\u{1F947}" : rank === 2 ? "\u{1F948}" : rank === 3 ? "\u{1F949}" : " ";
2371
+ console.log(
2372
+ `${emoji} ${rank}. ${chalk8__default.default.cyan(item.comp)}: ${chalk8__default.default.yellow(item.count)} uses in ${chalk8__default.default.green(item.files)} files`
2373
+ );
2374
+ });
2375
+ } catch (error) {
2376
+ spinner.fail(chalk8__default.default.red("Summary failed: " + error.message));
2377
+ }
2378
+ }
2379
+ async function patternsCommand(pattern, options) {
2380
+ const spinner = ora6__default.default("Analyzing patterns...").start();
2381
+ try {
2382
+ const files = await findFiles2(pattern, [], 1e3);
2383
+ if (files.length === 0) {
2384
+ spinner.fail(chalk8__default.default.red("No files found"));
2385
+ return;
2386
+ }
2387
+ const analyzer = new FocusedUsageAnalyzer(options.library);
2388
+ const patternStats = {};
2389
+ for (const file of files) {
2390
+ try {
2391
+ const report = analyzer.analyzeFile(file);
2392
+ if (report) {
2393
+ const analysis = analyzer.classifyUsage(report);
2394
+ analysis.foundPatterns.forEach((data, patternName) => {
2395
+ if (!patternStats[patternName]) {
2396
+ patternStats[patternName] = {
2397
+ count: 0,
2398
+ complexity: data.complexity,
2399
+ files: /* @__PURE__ */ new Set()
2400
+ };
2401
+ }
2402
+ patternStats[patternName].count += data.count;
2403
+ patternStats[patternName].files.add(file);
2404
+ });
2405
+ }
2406
+ } catch (error) {
2407
+ }
2408
+ }
2409
+ spinner.succeed(chalk8__default.default.green("Pattern analysis complete"));
2410
+ console.log(chalk8__default.default.bold("\n\u{1F50D} USAGE PATTERNS DETECTED\n"));
2411
+ let sorted = Object.entries(patternStats);
2412
+ if (options.sort === "complexity") {
2413
+ sorted = sorted.sort((a, b) => b[1].complexity - a[1].complexity);
2414
+ } else if (options.sort === "frequency") {
2415
+ sorted = sorted.sort((a, b) => b[1].count - a[1].count);
2416
+ } else {
2417
+ sorted = sorted.sort((a, b) => a[0].localeCompare(b[0]));
2418
+ }
2419
+ sorted.forEach(([name, stats]) => {
2420
+ const icon = getComplexityIcon(stats.complexity);
2421
+ console.log(`${icon} ${chalk8__default.default.bold(name)}`);
2422
+ console.log(` Complexity: ${stats.complexity}/10`);
2423
+ console.log(` Instances: ${chalk8__default.default.yellow(stats.count)}`);
2424
+ console.log(` Files: ${chalk8__default.default.green(stats.files.size)}`);
2425
+ console.log("");
2426
+ });
2427
+ } catch (error) {
2428
+ spinner.fail(chalk8__default.default.red("Pattern analysis failed: " + error.message));
2429
+ }
2430
+ }
2431
+ async function statsCommand(pattern, options) {
2432
+ const spinner = ora6__default.default("Generating statistics...").start();
2433
+ try {
2434
+ const files = await findFiles2(pattern, [], 1e3);
2435
+ if (files.length === 0) {
2436
+ spinner.fail(chalk8__default.default.red("No files found"));
2437
+ return;
2438
+ }
2439
+ const analyzer = new FocusedUsageAnalyzer(options.library);
2440
+ const stats = {
2441
+ totalFiles: files.length,
2442
+ analyzedFiles: 0,
2443
+ totalComponents: /* @__PURE__ */ new Set(),
2444
+ totalPatterns: 0,
2445
+ complexityDistribution: {
2446
+ Simple: 0,
2447
+ Moderate: 0,
2448
+ Complex: 0,
2449
+ "Very Complex": 0,
2450
+ "Extremely Complex": 0
2451
+ },
2452
+ avgComplexity: 0,
2453
+ topComponents: {},
2454
+ topPatterns: {}
2455
+ };
2456
+ let totalComplexityScore = 0;
2457
+ for (const file of files) {
2458
+ try {
2459
+ const report = analyzer.analyzeFile(file);
2460
+ if (report) {
2461
+ stats.analyzedFiles++;
2462
+ report.components.forEach((comp) => stats.totalComponents.add(comp));
2463
+ stats.totalPatterns += report.summary.totalUsagePatterns;
2464
+ const analysis = analyzer.classifyUsage(report);
2465
+ const complexity = analyzer.generateComplexityScore(
2466
+ analysis.foundPatterns
2467
+ );
2468
+ stats.complexityDistribution[complexity.level]++;
2469
+ totalComplexityScore += complexity.score;
2470
+ report.patterns.usage.jsx.forEach((usage) => {
2471
+ stats.topComponents[usage.component] = (stats.topComponents[usage.component] || 0) + usage.count;
2472
+ });
2473
+ analysis.foundPatterns.forEach((data, patternName) => {
2474
+ stats.topPatterns[patternName] = (stats.topPatterns[patternName] || 0) + data.count;
2475
+ });
2476
+ }
2477
+ } catch (error) {
2478
+ }
2479
+ }
2480
+ stats.avgComplexity = stats.analyzedFiles > 0 ? totalComplexityScore / stats.analyzedFiles : 0;
2481
+ stats.totalComponents = stats.totalComponents.size;
2482
+ spinner.succeed(chalk8__default.default.green("Statistics generated"));
2483
+ console.log(chalk8__default.default.bold("\n\u{1F4C8} DETAILED STATISTICS\n"));
2484
+ console.log(chalk8__default.default.cyan("Overview:"));
2485
+ console.log(` Total Files: ${stats.totalFiles}`);
2486
+ console.log(` Analyzed Files: ${chalk8__default.default.green(stats.analyzedFiles)}`);
2487
+ console.log(` Unique Components: ${chalk8__default.default.yellow(stats.totalComponents)}`);
2488
+ console.log(` Total Patterns: ${chalk8__default.default.yellow(stats.totalPatterns)}`);
2489
+ console.log(
2490
+ ` Average Complexity: ${chalk8__default.default.yellow(stats.avgComplexity.toFixed(2))}`
2491
+ );
2492
+ console.log(chalk8__default.default.cyan("\nComplexity Distribution:"));
2493
+ Object.entries(stats.complexityDistribution).forEach(([level, count]) => {
2494
+ if (count > 0) {
2495
+ const percentage = (count / stats.analyzedFiles * 100).toFixed(1);
2496
+ const bar = options.chart ? createBar(count, stats.analyzedFiles, 30) : "";
2497
+ console.log(
2498
+ ` ${level.padEnd(20)} ${count.toString().padStart(4)} (${percentage}%) ${bar}`
2499
+ );
2500
+ }
2501
+ });
2502
+ console.log(chalk8__default.default.cyan("\nTop 5 Components:"));
2503
+ Object.entries(stats.topComponents).sort((a, b) => b[1] - a[1]).slice(0, 5).forEach(([comp, count], index) => {
2504
+ console.log(
2505
+ ` ${index + 1}. ${comp.padEnd(30)} ${chalk8__default.default.yellow(count)} uses`
2506
+ );
2507
+ });
2508
+ console.log(chalk8__default.default.cyan("\nTop 5 Patterns:"));
2509
+ Object.entries(stats.topPatterns).sort((a, b) => b[1] - a[1]).slice(0, 5).forEach(([pattern2, count], index) => {
2510
+ console.log(
2511
+ ` ${index + 1}. ${pattern2.padEnd(30)} ${chalk8__default.default.yellow(count)} instances`
2512
+ );
2513
+ });
2514
+ } catch (error) {
2515
+ spinner.fail(chalk8__default.default.red("Stats generation failed: " + error.message));
2516
+ }
2517
+ }
2518
+ async function tableCommand(pattern, options) {
2519
+ const spinner = ora6__default.default("Generating table...").start();
2520
+ try {
2521
+ const files = await findFiles2(pattern, [], 1e3);
2522
+ if (files.length === 0) {
2523
+ spinner.fail(chalk8__default.default.red("No files found"));
2524
+ return;
2525
+ }
2526
+ const analyzer = new ReactComponentUsageAnalyzer(options.library);
2527
+ const componentData = {};
2528
+ const importData = {};
2529
+ for (const file of files) {
2530
+ try {
2531
+ const report = analyzer.analyzeFile(file);
2532
+ if (report) {
2533
+ report.patterns.usage.jsx.forEach((usage) => {
2534
+ if (!componentData[usage.component]) {
2535
+ componentData[usage.component] = {
2536
+ uses: 0,
2537
+ files: /* @__PURE__ */ new Set(),
2538
+ props: /* @__PURE__ */ new Set(),
2539
+ spreadProps: 0,
2540
+ propsDetails: []
2541
+ };
2542
+ }
2543
+ componentData[usage.component].uses += usage.count;
2544
+ componentData[usage.component].files.add(file);
2545
+ usage.usages.forEach((u) => {
2546
+ if (u.propsAnalysis) {
2547
+ u.propsAnalysis.namedProps.forEach(
2548
+ (prop) => componentData[usage.component].props.add(prop)
2549
+ );
2550
+ if (u.propsAnalysis.hasSpread) {
2551
+ componentData[usage.component].spreadProps++;
2552
+ }
2553
+ }
2554
+ });
2555
+ });
2556
+ [
2557
+ ...report.patterns.imports.default,
2558
+ ...report.patterns.imports.named
2559
+ ].forEach((imp) => {
2560
+ const name = imp.name || imp.local;
2561
+ if (!importData[name]) {
2562
+ importData[name] = {
2563
+ files: /* @__PURE__ */ new Set(),
2564
+ type: imp.imported ? "named" : "default"
2565
+ };
2566
+ }
2567
+ importData[name].files.add(file);
2568
+ });
2569
+ }
2570
+ } catch (error) {
2571
+ }
2572
+ }
2573
+ spinner.succeed(chalk8__default.default.green("Table generated"));
2574
+ Object.keys(componentData).forEach((key) => {
2575
+ componentData[key].filesCount = componentData[key].files.size;
2576
+ componentData[key].propsCount = componentData[key].props.size;
2577
+ componentData[key].propsList = Array.from(componentData[key].props);
2578
+ });
2579
+ Object.keys(importData).forEach((key) => {
2580
+ importData[key].filesCount = importData[key].files.size;
2581
+ });
2582
+ const sortedComponents = Object.entries(componentData).sort((a, b) => {
2583
+ switch (options.sort) {
2584
+ case "name":
2585
+ return a[0].localeCompare(b[0]);
2586
+ case "files":
2587
+ return b[1].filesCount - a[1].filesCount;
2588
+ case "props":
2589
+ return b[1].propsCount - a[1].propsCount;
2590
+ case "uses":
2591
+ default:
2592
+ return b[1].uses - a[1].uses;
2593
+ }
2594
+ }).slice(0, parseInt(options.top));
2595
+ const sortedImports = Object.entries(importData).sort((a, b) => {
2596
+ if (options.sort === "name") {
2597
+ return a[0].localeCompare(b[0]);
2598
+ }
2599
+ return b[1].filesCount - a[1].filesCount;
2600
+ }).slice(0, parseInt(options.top));
2601
+ console.log(chalk8__default.default.bold("\n\u{1F4CA} COMPONENT USAGE TABLE\n"));
2602
+ const componentTable = new Table2__default.default({
2603
+ head: [
2604
+ chalk8__default.default.cyan("Component"),
2605
+ chalk8__default.default.cyan("Uses"),
2606
+ chalk8__default.default.cyan("Files"),
2607
+ ...options.props ? [chalk8__default.default.cyan("Props"), chalk8__default.default.cyan("Spread")] : []
2608
+ ],
2609
+ colWidths: [30, 10, 10, ...options.props ? [40, 10] : []],
2610
+ style: { head: [], border: [] }
2611
+ });
2612
+ sortedComponents.forEach(([component, data]) => {
2613
+ const row = [
2614
+ component,
2615
+ chalk8__default.default.yellow(data.uses),
2616
+ chalk8__default.default.green(data.filesCount)
2617
+ ];
2618
+ if (options.props) {
2619
+ const propsDisplay = data.propsCount > 0 ? data.propsList.slice(0, 5).join(", ") + (data.propsCount > 5 ? "..." : "") : "-";
2620
+ const spreadDisplay = data.spreadProps > 0 ? chalk8__default.default.red(`\u26A0 ${data.spreadProps}`) : chalk8__default.default.gray("0");
2621
+ row.push(propsDisplay, spreadDisplay);
2622
+ }
2623
+ componentTable.push(row);
2624
+ });
2625
+ console.log(componentTable.toString());
2626
+ console.log(chalk8__default.default.bold("\n\u{1F4E6} IMPORTS TABLE\n"));
2627
+ const importTable = new Table2__default.default({
2628
+ head: [chalk8__default.default.cyan("Import"), chalk8__default.default.cyan("Type"), chalk8__default.default.cyan("Files")],
2629
+ colWidths: [30, 15, 10],
2630
+ style: { head: [], border: [] }
2631
+ });
2632
+ sortedImports.forEach(([name, data]) => {
2633
+ importTable.push([
2634
+ name,
2635
+ data.type === "named" ? chalk8__default.default.blue("named") : chalk8__default.default.green("default"),
2636
+ chalk8__default.default.yellow(data.filesCount)
2637
+ ]);
2638
+ });
2639
+ console.log(importTable.toString());
2640
+ if (options.props) {
2641
+ console.log(chalk8__default.default.bold("\n\u{1F527} PROPS ANALYSIS\n"));
2642
+ sortedComponents.forEach(([component, data]) => {
2643
+ if (data.propsCount > 0 || data.spreadProps > 0) {
2644
+ console.log(chalk8__default.default.cyan(`${component}:`));
2645
+ console.log(` Props: ${data.propsList.join(", ")}`);
2646
+ if (data.spreadProps > 0) {
2647
+ console.log(
2648
+ chalk8__default.default.yellow(
2649
+ ` \u26A0 Warning: ${data.spreadProps} usage(s) with spread props (cannot analyze statically)`
2650
+ )
2651
+ );
2652
+ }
2653
+ console.log("");
2654
+ }
2655
+ });
2656
+ }
2657
+ console.log(chalk8__default.default.bold("\u{1F4C8} SUMMARY"));
2658
+ console.log(` Total Components: ${Object.keys(componentData).length}`);
2659
+ console.log(` Total Imports: ${Object.keys(importData).length}`);
2660
+ console.log(` Files Analyzed: ${files.length}`);
2661
+ console.log(` Sort: ${options.sort}`);
2662
+ } catch (error) {
2663
+ spinner.fail(chalk8__default.default.red("Table generation failed: " + error.message));
2664
+ }
2665
+ }
2666
+ async function findFiles2(pattern, ignorePatterns, maxFiles) {
2667
+ const allFiles = await glob.glob(pattern, {
2668
+ ignore: ["node_modules/**", "dist/**", "build/**", ...ignorePatterns],
2669
+ nodir: true,
2670
+ absolute: false,
2671
+ // Support all React file types
2672
+ matchBase: false
2673
+ });
2674
+ const reactFiles = allFiles.filter((file) => {
2675
+ const ext = path__default.default.extname(file).toLowerCase();
2676
+ return [".tsx", ".jsx", ".ts", ".js"].includes(ext);
2677
+ });
2678
+ return reactFiles.slice(0, maxFiles);
2679
+ }
2680
+ function getComplexityIcon(complexity) {
2681
+ if (complexity <= 2) return chalk8__default.default.green("\u{1F7E2}");
2682
+ if (complexity <= 4) return chalk8__default.default.yellow("\u{1F7E1}");
2683
+ if (complexity <= 6) return chalk8__default.default.hex("#FFA500")("\u{1F7E0}");
2684
+ return chalk8__default.default.red("\u{1F534}");
2685
+ }
2686
+ function createBar(value, max, width) {
2687
+ const filled = Math.round(value / max * width);
2688
+ const empty = width - filled;
2689
+ return chalk8__default.default.green("\u2588".repeat(filled)) + chalk8__default.default.gray("\u2591".repeat(empty));
2690
+ }
2691
+ program.parse(process.argv);
2692
+ if (process.argv.length < 3) {
2693
+ program.help();
2694
+ }
2695
+ //# sourceMappingURL=cli.js.map
2696
+ //# sourceMappingURL=cli.js.map