hermex 0.0.1-alpha.0 → 1.0.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.
Files changed (4) hide show
  1. package/README.md +4 -21
  2. package/dist/cli.js +1163 -2550
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +18 -20
package/dist/cli.js CHANGED
@@ -3,2694 +3,1307 @@
3
3
 
4
4
  var commander = require('commander');
5
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');
6
+ var ora = require('ora');
7
+ var chalk = require('chalk');
10
8
  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');
9
+ var fs = require('fs');
10
+ var path = require('path');
11
+ var Table = require('cli-table3');
16
12
 
17
13
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
14
 
19
- var chalk8__default = /*#__PURE__*/_interopDefault(chalk8);
20
- var ora6__default = /*#__PURE__*/_interopDefault(ora6);
15
+ var ora__default = /*#__PURE__*/_interopDefault(ora);
16
+ var chalk__default = /*#__PURE__*/_interopDefault(chalk);
17
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
21
18
  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);
19
+ var Table__default = /*#__PURE__*/_interopDefault(Table);
28
20
 
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);
21
+ // src/swc-parser/core/state.ts
22
+ function createState() {
23
+ const usagePatterns = {
24
+ directImports: /* @__PURE__ */ new Set(),
25
+ namedImports: /* @__PURE__ */ new Set(),
26
+ namespaceImports: /* @__PURE__ */ new Set(),
27
+ defaultImports: /* @__PURE__ */ new Set(),
28
+ aliasedImports: /* @__PURE__ */ new Map(),
29
+ variableAssignments: /* @__PURE__ */ new Map(),
30
+ componentMappings: /* @__PURE__ */ new Set(),
31
+ lazyImports: /* @__PURE__ */ new Set(),
32
+ dynamicImports: /* @__PURE__ */ new Set(),
33
+ conditionalUsage: /* @__PURE__ */ new Set(),
34
+ arrayMappings: /* @__PURE__ */ new Set(),
35
+ objectMappings: /* @__PURE__ */ new Set(),
36
+ hocUsage: /* @__PURE__ */ new Set(),
37
+ renderProps: /* @__PURE__ */ new Set(),
38
+ contextUsage: /* @__PURE__ */ new Set(),
39
+ forwardedRefs: /* @__PURE__ */ new Set(),
40
+ memoizedComponents: /* @__PURE__ */ new Set(),
41
+ portalUsage: /* @__PURE__ */ new Set(),
42
+ jsxUsage: /* @__PURE__ */ new Map(),
43
+ destructuredUsage: /* @__PURE__ */ new Set(),
44
+ propsAnalysis: /* @__PURE__ */ new Map()
45
+ };
46
+ return {
47
+ usagePatterns,
48
+ componentNames: /* @__PURE__ */ new Set(),
49
+ allIdentifiers: /* @__PURE__ */ new Set()
50
+ };
51
+ }
52
+
53
+ // src/swc-parser/patterns/imports.ts
54
+ function analyzeImportDeclaration(node, state) {
55
+ const source = node.source.value;
56
+ console.log(`\u{1F4E6} Found import: ${source}`);
57
+ for (const spec of node.specifiers) {
58
+ switch (spec.type) {
59
+ case "ImportDefaultSpecifier":
60
+ analyzeDefaultImport(spec, source, node, state);
120
61
  break;
121
- case "ClassDeclaration":
122
- this.analyzeClassDeclaration(node);
62
+ case "ImportNamespaceSpecifier":
63
+ analyzeNamespaceImport(spec, source, node, state);
123
64
  break;
124
- default:
125
- this.visitChildren(node);
65
+ case "ImportSpecifier":
66
+ analyzeNamedImport(spec, source, node, state);
126
67
  break;
127
68
  }
128
69
  }
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
- }
70
+ }
71
+ function analyzeDefaultImport(spec, source, node, state) {
72
+ var _a;
73
+ const name = spec.local.value;
74
+ state.usagePatterns.defaultImports.add({
75
+ name,
76
+ source,
77
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0
78
+ });
79
+ state.componentNames.add(name);
80
+ }
81
+ function analyzeNamespaceImport(spec, source, node, state) {
82
+ var _a;
83
+ const name = spec.local.value;
84
+ state.usagePatterns.namespaceImports.add({
85
+ name,
86
+ source,
87
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0
88
+ });
89
+ state.allIdentifiers.add(name);
90
+ }
91
+ function analyzeNamedImport(spec, source, node, state) {
92
+ var _a, _b;
93
+ const importedName = spec.imported ? spec.imported.value : spec.local.value;
94
+ const localName = spec.local.value;
95
+ state.usagePatterns.namedImports.add({
96
+ name: importedName,
97
+ source,
98
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0
99
+ });
100
+ if (importedName !== localName) {
101
+ state.usagePatterns.aliasedImports.set(localName, {
102
+ imported: importedName,
103
+ local: localName,
104
+ source,
105
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
182
106
  });
183
107
  }
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
- }
108
+ state.componentNames.add(localName);
109
+ }
110
+
111
+ // src/swc-parser/utils/jsx-helpers.ts
112
+ function getJSXElementName(nameNode) {
113
+ if (!nameNode) return "";
114
+ switch (nameNode.type) {
115
+ case "Identifier":
116
+ return nameNode.value;
117
+ case "JSXMemberExpression":
118
+ return `${getJSXElementName(nameNode.object)}.${nameNode.property.value}`;
119
+ default:
120
+ return "";
121
+ }
122
+ }
123
+ function isMemberExpressionComponent(nameNode, state) {
124
+ if ((nameNode == null ? void 0 : nameNode.type) === "JSXMemberExpression") {
125
+ const objectName = getJSXElementName(nameNode.object);
126
+ return state.allIdentifiers.has(objectName);
127
+ }
128
+ return false;
129
+ }
130
+ function extractJSXProps(attributes) {
131
+ if (!attributes) return [];
132
+ return attributes.map((attr) => {
133
+ var _a, _b, _c;
134
+ if (attr.type === "JSXAttribute") {
135
+ return {
136
+ name: ((_a = attr.name) == null ? void 0 : _a.value) || ((_c = (_b = attr.name) == null ? void 0 : _b.name) == null ? void 0 : _c.value),
137
+ value: extractJSXAttributeValue(attr.value)
138
+ };
201
139
  }
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);
140
+ if (attr.type === "SpreadElement") {
141
+ return {
142
+ name: "...",
143
+ value: "[spread]",
144
+ isSpread: true
145
+ };
204
146
  }
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}`);
147
+ return null;
148
+ }).filter(Boolean);
149
+ }
150
+ function extractJSXAttributeValue(value) {
151
+ if (!value) return true;
152
+ switch (value.type) {
153
+ case "StringLiteral":
154
+ return value.value;
155
+ case "JSXExpressionContainer":
156
+ return extractExpressionValue(value.expression);
157
+ default:
158
+ return "[complex]";
159
+ }
160
+ }
161
+ function extractExpressionValue(expr) {
162
+ if (!expr) return "[unknown]";
163
+ switch (expr.type) {
164
+ case "StringLiteral":
165
+ case "NumericLiteral":
166
+ case "BooleanLiteral":
167
+ return expr.value;
168
+ case "Identifier":
169
+ return `{${expr.value}}`;
170
+ case "ArrowFunctionExpression":
171
+ case "FunctionExpression":
172
+ return "[function]";
173
+ case "ObjectExpression":
174
+ return "[object]";
175
+ case "ArrayExpression":
176
+ return "[array]";
177
+ default:
178
+ return "[expression]";
179
+ }
180
+ }
181
+ function getUsageContext(parent) {
182
+ if (!parent) return "direct";
183
+ switch (parent.type) {
184
+ case "ConditionalExpression":
185
+ return "conditional";
186
+ case "ArrayExpression":
187
+ return "array";
188
+ case "ObjectExpression":
189
+ return "object";
190
+ case "CallExpression":
191
+ return "hoc";
192
+ case "VariableDeclarator":
193
+ return "variable";
194
+ default:
195
+ return "jsx";
196
+ }
197
+ }
198
+
199
+ // src/swc-parser/patterns/props.ts
200
+ function analyzePropsInDetail(attributes, componentName, state) {
201
+ var _a, _b, _c;
202
+ const analysis = {
203
+ namedProps: [],
204
+ hasSpread: false,
205
+ hasComplexProps: false,
206
+ hasEventHandlers: false,
207
+ propDetails: []
208
+ };
209
+ if (!attributes) return analysis;
210
+ for (const attr of attributes) {
211
+ if (attr.type === "JSXAttribute") {
212
+ const propName = ((_a = attr.name) == null ? void 0 : _a.value) || ((_c = (_b = attr.name) == null ? void 0 : _b.name) == null ? void 0 : _c.value);
213
+ if (propName) {
214
+ analysis.namedProps.push(propName);
215
+ const propDetail = {
216
+ name: propName,
217
+ type: getPropType(attr.value),
218
+ isEventHandler: propName.startsWith("on"),
219
+ isComplex: isComplexProp(attr.value)
220
+ };
221
+ if (propDetail.isEventHandler) {
222
+ analysis.hasEventHandlers = true;
223
+ }
224
+ if (propDetail.isComplex) {
225
+ analysis.hasComplexProps = true;
220
226
  }
227
+ analysis.propDetails.push(propDetail);
221
228
  }
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
229
+ } else if (attr.type === "SpreadElement") {
230
+ analysis.hasSpread = true;
231
+ analysis.propDetails.push({
232
+ name: "...",
233
+ type: "spread",
234
+ isSpread: true,
235
+ isComplex: true,
236
+ isEventHandler: false,
237
+ warning: "Spread props cannot be statically analyzed"
231
238
  });
232
- console.log(`\u26A1 Found dynamic import: ${source}`);
239
+ analysis.hasComplexProps = true;
233
240
  }
234
241
  }
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);
242
+ state.usagePatterns.propsAnalysis.set(componentName, analysis);
243
+ return analysis;
244
+ }
245
+ function getPropType(value) {
246
+ if (!value) return "boolean";
247
+ switch (value.type) {
248
+ case "StringLiteral":
249
+ return "string";
250
+ case "JSXExpressionContainer": {
251
+ const expr = value.expression;
252
+ if (!expr) return "unknown";
253
+ switch (expr.type) {
254
+ case "NumericLiteral":
255
+ return "number";
256
+ case "BooleanLiteral":
257
+ return "boolean";
258
+ case "StringLiteral":
259
+ return "string";
260
+ case "ArrowFunctionExpression":
261
+ case "FunctionExpression":
262
+ return "function";
263
+ case "ObjectExpression":
264
+ return "object";
265
+ case "ArrayExpression":
266
+ return "array";
267
+ case "Identifier":
268
+ return "variable";
269
+ default:
270
+ return "expression";
255
271
  }
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
272
+ }
273
+ default:
274
+ return "unknown";
275
+ }
276
+ }
277
+ function isComplexProp(value) {
278
+ if (!value) return false;
279
+ if (value.type === "JSXExpressionContainer") {
280
+ const expr = value.expression;
281
+ if (!expr) return false;
282
+ return expr.type === "ObjectExpression" || expr.type === "ArrayExpression" || expr.type === "CallExpression" || expr.type === "ConditionalExpression";
283
+ }
284
+ return false;
285
+ }
286
+
287
+ // src/swc-parser/patterns/jsx.ts
288
+ function analyzeJSXElement(node, state) {
289
+ if (node.opening) {
290
+ analyzeJSXOpeningElement(node.opening, state, node);
291
+ }
292
+ }
293
+ function analyzeJSXOpeningElement(node, state, parent) {
294
+ var _a;
295
+ const elementName = getJSXElementName(node.name);
296
+ if (!state.componentNames.has(elementName) && !isMemberExpressionComponent(node.name, state)) {
297
+ return;
298
+ }
299
+ const propsAnalysis = analyzePropsInDetail(
300
+ node.attributes,
301
+ elementName,
302
+ state
303
+ );
304
+ const usage = {
305
+ component: elementName,
306
+ props: extractJSXProps(node.attributes).map((p) => p.name),
307
+ propsAnalysis,
308
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0,
309
+ context: getUsageContext(parent)
310
+ };
311
+ if (!state.usagePatterns.jsxUsage.has(elementName)) {
312
+ state.usagePatterns.jsxUsage.set(elementName, usage);
313
+ }
314
+ console.log(`\u{1F3A8} JSX Usage: <${elementName}>`);
315
+ }
316
+
317
+ // src/swc-parser/utils/matchers.ts
318
+ function isKnownComponent(name, state) {
319
+ return state.componentNames.has(name) || state.allIdentifiers.has(name);
320
+ }
321
+
322
+ // src/swc-parser/patterns/variables.ts
323
+ function analyzeVariableDeclaration(node, state) {
324
+ var _a, _b, _c;
325
+ if (!node.declarations) return;
326
+ for (const decl of node.declarations) {
327
+ if (((_a = decl.id) == null ? void 0 : _a.type) === "Identifier") {
328
+ const varName = decl.id.value;
329
+ if (decl.init) {
330
+ const assignment = extractAssignmentInfo(decl.init);
331
+ if (assignment && isKnownComponent(assignment, state)) {
332
+ state.usagePatterns.variableAssignments.set(varName, {
333
+ assignment,
334
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
270
335
  });
271
- this.componentNames.add(propName);
272
- console.log(`\u{1F527} Destructuring: ${propName} from ${init.value}`);
336
+ state.componentNames.add(varName);
337
+ console.log(`\u{1F4DD} Variable assignment: ${varName} = ${assignment}`);
273
338
  }
274
339
  }
275
- });
276
- }
277
- analyzeJSXElement(node, parent) {
278
- if (node.opening) {
279
- this.analyzeJSXOpeningElement(node.opening, node);
280
340
  }
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
341
+ if (((_c = decl.id) == null ? void 0 : _c.type) === "ObjectPattern") {
342
+ analyzeDestructuringPattern(decl.id, decl.init, state);
343
+ }
344
+ }
345
+ }
346
+ function analyzeDestructuringPattern(pattern, init, state) {
347
+ var _a, _b;
348
+ if (!pattern.properties) return;
349
+ for (const prop of pattern.properties) {
350
+ if (prop.type === "AssignmentPatternProperty" && ((_a = prop.key) == null ? void 0 : _a.type) === "Identifier") {
351
+ const propName = prop.key.value;
352
+ if ((init == null ? void 0 : init.type) === "Identifier" && state.allIdentifiers.has(init.value)) {
353
+ state.usagePatterns.destructuredUsage.add({
354
+ property: propName,
355
+ source: init.value,
356
+ line: ((_b = pattern.span) == null ? void 0 : _b.start) || 0
305
357
  });
358
+ state.componentNames.add(propName);
359
+ console.log(`\u{1F527} Destructuring: ${propName} from ${init.value}`);
306
360
  }
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
361
  }
316
362
  }
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
- }
363
+ }
364
+ function extractAssignmentInfo(node) {
365
+ switch (node.type) {
366
+ case "Identifier":
367
+ return node.value;
368
+ case "MemberExpression":
369
+ return `${extractAssignmentInfo(node.object)}.${node.property.value}`;
370
+ case "ConditionalExpression":
371
+ return `${extractAssignmentInfo(node.consequent)} | ${extractAssignmentInfo(node.alternate)}`;
372
+ default:
373
+ return null;
374
+ }
375
+ }
376
+
377
+ // src/swc-parser/patterns/conditionals.ts
378
+ function analyzeConditionalExpression(node, state) {
379
+ var _a, _b, _c;
380
+ const consequent = ((_a = node.consequent) == null ? void 0 : _a.type) === "Identifier" ? node.consequent.value : null;
381
+ const alternate = ((_b = node.alternate) == null ? void 0 : _b.type) === "Identifier" ? node.alternate.value : null;
382
+ if (consequent && state.componentNames.has(consequent) || alternate && state.componentNames.has(alternate)) {
383
+ state.usagePatterns.conditionalUsage.add({
384
+ consequent: consequent || "",
385
+ alternate: alternate || "",
386
+ line: ((_c = node.span) == null ? void 0 : _c.start) || 0
387
+ });
388
+ console.log("\u{1F500} Conditional component usage found");
327
389
  }
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);
390
+ }
391
+
392
+ // src/swc-parser/patterns/collections.ts
393
+ function analyzeArrayExpression(node, state) {
394
+ var _a, _b, _c;
395
+ const hasComponents = (_a = node.elements) == null ? void 0 : _a.some((elem) => {
396
+ if ((elem == null ? void 0 : elem.type) === "Identifier") {
397
+ return state.componentNames.has(elem.value);
332
398
  }
333
399
  return false;
400
+ });
401
+ if (hasComponents) {
402
+ state.usagePatterns.arrayMappings.add({
403
+ components: (_b = node.elements) == null ? void 0 : _b.map((elem) => elem == null ? void 0 : elem.value).filter(Boolean),
404
+ line: ((_c = node.span) == null ? void 0 : _c.start) || 0
405
+ });
406
+ console.log("\u{1F4CB} Array with components found");
334
407
  }
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") {
408
+ }
409
+ function analyzeObjectExpression(node, state) {
410
+ var _a, _b;
411
+ const componentProps = (_a = node.properties) == null ? void 0 : _a.filter((prop) => {
412
+ var _a2;
413
+ if (prop.type === "KeyValueProperty" && ((_a2 = prop.value) == null ? void 0 : _a2.type) === "Identifier") {
414
+ return state.componentNames.has(prop.value.value);
415
+ }
416
+ return false;
417
+ });
418
+ if ((componentProps == null ? void 0 : componentProps.length) > 0) {
419
+ state.usagePatterns.objectMappings.add({
420
+ mappings: componentProps.map((prop) => {
421
+ var _a2, _b2;
345
422
  return {
346
- name: "...",
347
- value: "[spread]",
348
- isSpread: true
423
+ key: ((_a2 = prop.key) == null ? void 0 : _a2.value) || "[computed]",
424
+ component: (_b2 = prop.value) == null ? void 0 : _b2.value
349
425
  };
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
- }
426
+ }),
427
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
428
+ });
429
+ console.log("\u{1F5FA}\uFE0F Object mapping with components found");
384
430
  }
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"
431
+ }
432
+
433
+ // src/swc-parser/patterns/lazy-dynamic.ts
434
+ function analyzeLazyImport(node, state) {
435
+ var _a, _b, _c, _d, _e, _f;
436
+ const arg = (_a = node.arguments) == null ? void 0 : _a[0];
437
+ if ((arg == null ? void 0 : arg.type) === "ArrowFunctionExpression" && ((_b = arg.body) == null ? void 0 : _b.type) === "CallExpression") {
438
+ const importCall = arg.body;
439
+ if (((_c = importCall.callee) == null ? void 0 : _c.type) === "Import") {
440
+ const source = (_e = (_d = importCall.arguments) == null ? void 0 : _d[0]) == null ? void 0 : _e.value;
441
+ if (source) {
442
+ state.usagePatterns.lazyImports.add({
443
+ source,
444
+ line: ((_f = node.span) == null ? void 0 : _f.start) || 0
422
445
  });
423
- analysis.hasComplexProps = true;
446
+ console.log(`\u{1F504} Found lazy import: ${source}`);
424
447
  }
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
448
  }
458
449
  }
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;
450
+ }
451
+ function analyzeDynamicImport(node, state) {
452
+ var _a, _b, _c;
453
+ const source = (_b = (_a = node.arguments) == null ? void 0 : _a[0]) == null ? void 0 : _b.value;
454
+ if (source) {
455
+ state.usagePatterns.dynamicImports.add({
456
+ source,
457
+ line: ((_c = node.span) == null ? void 0 : _c.start) || 0
458
+ });
459
+ console.log(`\u26A1 Found dynamic import: ${source}`);
467
460
  }
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;
461
+ }
462
+
463
+ // src/swc-parser/patterns/advanced.ts
464
+ function analyzeHOCUsage(node, state) {
465
+ var _a, _b, _c, _d;
466
+ state.usagePatterns.hocUsage.add({
467
+ function: ((_a = node.callee) == null ? void 0 : _a.value) || "[unknown]",
468
+ component: ((_c = (_b = node.arguments) == null ? void 0 : _b[0]) == null ? void 0 : _c.value) || "[unknown]",
469
+ line: ((_d = node.span) == null ? void 0 : _d.start) || 0
470
+ });
471
+ console.log(`\u{1F381} HOC usage found`);
472
+ }
473
+ function analyzeMemoUsage(node, state) {
474
+ var _a, _b;
475
+ const component = (_a = node.arguments) == null ? void 0 : _a[0];
476
+ if ((component == null ? void 0 : component.type) === "Identifier" && state.componentNames.has(component.value)) {
477
+ state.usagePatterns.memoizedComponents.add({
478
+ component: component.value,
479
+ line: ((_b = node.span) == null ? void 0 : _b.start) || 0
475
480
  });
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`);
481
+ console.log(`\u{1F9E0} Memoized component: ${component.value}`);
482
+ }
483
+ }
484
+ function analyzeForwardRefUsage(node, state) {
485
+ var _a;
486
+ state.usagePatterns.forwardedRefs.add({
487
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0
488
+ });
489
+ console.log("\u2197\uFE0F ForwardRef usage found");
490
+ }
491
+ function analyzePortalUsage(node, state) {
492
+ var _a;
493
+ state.usagePatterns.portalUsage.add({
494
+ line: ((_a = node.span) == null ? void 0 : _a.start) || 0
495
+ });
496
+ console.log("\u{1F300} Portal usage found");
497
+ }
498
+ function analyzeMemberExpression(node, state) {
499
+ var _a, _b;
500
+ if (((_a = node.object) == null ? void 0 : _a.type) === "Identifier" && state.allIdentifiers.has(node.object.value)) {
501
+ const namespaceName = node.object.value;
502
+ const propertyName = (_b = node.property) == null ? void 0 : _b.value;
503
+ if (propertyName) {
504
+ state.componentNames.add(propertyName);
505
+ console.log(`\u{1F517} Namespace access: ${namespaceName}.${propertyName}`);
482
506
  }
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);
507
+ }
508
+ }
509
+ function isHOCPattern(node, state) {
510
+ var _a, _b;
511
+ return ((_a = node.callee) == null ? void 0 : _a.type) === "Identifier" && ((_b = node.arguments) == null ? void 0 : _b.some(
512
+ (arg) => arg.type === "Identifier" && state.componentNames.has(arg.value)
513
+ ));
514
+ }
515
+
516
+ // src/swc-parser/core/visitor.ts
517
+ function visitNode(node, state, context = {}) {
518
+ if (!node) return;
519
+ switch (node.type) {
520
+ case "Module":
521
+ if (node.body) {
522
+ for (const item of node.body) {
523
+ if (item.type === "ImportDeclaration") {
524
+ visitNode(item, state, context);
525
+ }
526
+ }
527
+ for (const item of node.body) {
528
+ if (item.type !== "ImportDeclaration") {
529
+ visitNode(item, state, { ...context, parent: node });
530
+ }
531
+ }
491
532
  }
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);
533
+ break;
534
+ case "ImportDeclaration":
535
+ analyzeImportDeclaration(node, state);
536
+ break;
537
+ case "CallExpression":
538
+ analyzeCallExpression(node, state, context);
539
+ break;
540
+ case "VariableDeclaration":
541
+ analyzeVariableDeclaration(node, state);
542
+ visitChildren(node, state, context);
543
+ break;
544
+ case "JSXElement":
545
+ case "JSXFragment":
546
+ analyzeJSXElement(node, state);
547
+ visitChildren(node, state, context);
548
+ break;
549
+ case "JSXOpeningElement":
550
+ analyzeJSXOpeningElement(node, state, context.parent);
551
+ break;
552
+ case "ArrayExpression":
553
+ analyzeArrayExpression(node, state);
554
+ visitChildren(node, state, context);
555
+ break;
556
+ case "ObjectExpression":
557
+ analyzeObjectExpression(node, state);
558
+ visitChildren(node, state, context);
559
+ break;
560
+ case "MemberExpression":
561
+ analyzeMemberExpression(node, state);
562
+ visitChildren(node, state, context);
563
+ break;
564
+ case "ConditionalExpression":
565
+ analyzeConditionalExpression(node, state);
566
+ visitChildren(node, state, context);
567
+ break;
568
+ case "FunctionDeclaration":
569
+ case "ClassDeclaration":
570
+ case "ExpressionStatement":
571
+ case "ReturnStatement":
572
+ case "VariableDeclarator":
573
+ case "ArrowFunctionExpression":
574
+ case "FunctionExpression":
575
+ visitChildren(node, state, { ...context, parent: node });
576
+ break;
577
+ default:
578
+ visitChildren(node, state, context);
579
+ break;
508
580
  }
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);
581
+ }
582
+ function analyzeCallExpression(node, state, context) {
583
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
584
+ 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") {
585
+ analyzeLazyImport(node, state);
522
586
  }
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
- });
587
+ if (((_f = node.callee) == null ? void 0 : _f.type) === "Import") {
588
+ analyzeDynamicImport(node, state);
549
589
  }
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
- });
590
+ if (isHOCPattern(node, state)) {
591
+ analyzeHOCUsage(node, state);
557
592
  }
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}`);
593
+ if (((_h = (_g = node.callee) == null ? void 0 : _g.object) == null ? void 0 : _h.value) === "React") {
594
+ if (((_j = (_i = node.callee) == null ? void 0 : _i.property) == null ? void 0 : _j.value) === "memo") {
595
+ analyzeMemoUsage(node, state);
596
+ } else if (((_l = (_k = node.callee) == null ? void 0 : _k.property) == null ? void 0 : _l.value) === "forwardRef") {
597
+ analyzeForwardRefUsage(node, state);
567
598
  }
568
599
  }
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`);
600
+ 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") {
601
+ analyzePortalUsage(node, state);
575
602
  }
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}`);
603
+ visitChildren(node, state, context);
604
+ }
605
+ function visitChildren(node, state, context) {
606
+ if (!node) return;
607
+ for (const key in node) {
608
+ const value = node[key];
609
+ if (Array.isArray(value)) {
610
+ for (const item of value) {
611
+ if (item && typeof item === "object") {
612
+ visitNode(item, state, { ...context, parent: node });
613
+ }
591
614
  }
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;
615
+ } else if (value && typeof value === "object" && value.type) {
616
+ visitNode(value, state, { ...context, parent: node });
605
617
  }
606
618
  }
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
- }
619
+ }
620
+
621
+ // src/swc-parser/core/report.ts
622
+ function generateReport(state) {
623
+ const report = {
624
+ summary: {
625
+ totalImports: state.usagePatterns.defaultImports.size + state.usagePatterns.namedImports.size + state.usagePatterns.namespaceImports.size,
626
+ totalComponents: state.componentNames.size,
627
+ totalUsagePatterns: calculateTotalPatterns(state)
628
+ },
629
+ patterns: {
630
+ imports: {
631
+ default: Array.from(state.usagePatterns.defaultImports),
632
+ named: Array.from(state.usagePatterns.namedImports),
633
+ namespace: Array.from(state.usagePatterns.namespaceImports),
634
+ aliased: Array.from(state.usagePatterns.aliasedImports.values())
635
+ },
636
+ usage: {
637
+ jsx: Array.from(state.usagePatterns.jsxUsage.values()),
638
+ variables: Array.from(
639
+ state.usagePatterns.variableAssignments.entries()
640
+ ).map(([key, value]) => ({
641
+ variable: key,
642
+ assignment: value.assignment
643
+ })),
644
+ destructuring: Array.from(state.usagePatterns.destructuredUsage),
645
+ conditional: Array.from(state.usagePatterns.conditionalUsage),
646
+ arrays: Array.from(state.usagePatterns.arrayMappings),
647
+ objects: Array.from(state.usagePatterns.objectMappings)
648
+ },
649
+ advanced: {
650
+ lazy: Array.from(state.usagePatterns.lazyImports),
651
+ dynamic: Array.from(state.usagePatterns.dynamicImports),
652
+ hoc: Array.from(state.usagePatterns.hocUsage),
653
+ memo: Array.from(state.usagePatterns.memoizedComponents),
654
+ forwardRef: Array.from(state.usagePatterns.forwardedRefs),
655
+ portal: Array.from(state.usagePatterns.portalUsage)
656
+ },
657
+ props: Array.from(state.usagePatterns.propsAnalysis.entries()).map(
658
+ ([component, analysis]) => ({
659
+ component,
660
+ analysis
661
+ })
662
+ )
663
+ },
664
+ components: Array.from(state.componentNames).sort()
665
+ };
666
+ return report;
667
+ }
668
+ function calculateTotalPatterns(state) {
669
+ let sum = 0;
670
+ const patterns = state.usagePatterns;
671
+ for (const key in patterns) {
672
+ const pattern = patterns[key];
673
+ if (pattern instanceof Set) {
674
+ sum += pattern.size;
675
+ } else if (pattern instanceof Map) {
676
+ sum += pattern.size;
677
+ }
678
+ }
679
+ return sum;
680
+ }
681
+
682
+ // src/swc-parser/index.ts
683
+ function parseCode(code, options = {}) {
684
+ const state = createState();
685
+ const ast = core.parseSync(code, {
686
+ syntax: "typescript",
687
+ tsx: true,
688
+ decorators: true,
689
+ dynamicImport: true
690
+ });
691
+ visitNode(ast, state);
692
+ const report = generateReport(state);
693
+ if (options.libraryName) {
694
+ return filterReportByLibrary(report, options.libraryName);
621
695
  }
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
696
+ return report;
697
+ }
698
+ function parseFile(filePath, options = {}) {
699
+ console.log(`
700
+ \u{1F4C1} Analyzing: ${filePath}`);
701
+ try {
702
+ const code = fs__default.default.readFileSync(filePath, "utf8");
703
+ return parseCode(code, options);
704
+ } catch (error) {
705
+ console.error(`\u274C Error parsing ${filePath}:`, error.message);
706
+ return null;
707
+ }
708
+ }
709
+ function filterReportByLibrary(report, libraryName) {
710
+ const isFromLibrary = (source) => source.startsWith(libraryName) || source.includes(libraryName);
711
+ return {
712
+ ...report,
713
+ patterns: {
714
+ imports: {
715
+ default: report.patterns.imports.default.filter(
716
+ (imp) => isFromLibrary(imp.source)
717
+ ),
718
+ named: report.patterns.imports.named.filter(
719
+ (imp) => isFromLibrary(imp.source)
720
+ ),
721
+ namespace: report.patterns.imports.namespace.filter(
722
+ (imp) => isFromLibrary(imp.source)
723
+ ),
724
+ aliased: report.patterns.imports.aliased.filter(
725
+ (imp) => isFromLibrary(imp.source)
641
726
  )
642
727
  },
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
- )
728
+ usage: report.patterns.usage,
729
+ advanced: {
730
+ lazy: report.patterns.advanced.lazy.filter(
731
+ (imp) => isFromLibrary(imp.source)
732
+ ),
733
+ dynamic: report.patterns.advanced.dynamic.filter(
734
+ (imp) => isFromLibrary(imp.source)
735
+ ),
736
+ hoc: report.patterns.advanced.hoc,
737
+ memo: report.patterns.advanced.memo,
738
+ forwardRef: report.patterns.advanced.forwardRef,
739
+ portal: report.patterns.advanced.portal
692
740
  },
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
- });
741
+ props: report.patterns.props
725
742
  }
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
- });
743
+ };
744
+ }
745
+
746
+ // src/utils/aggregator.ts
747
+ function aggregateReports(reports) {
748
+ const componentUsageMap = /* @__PURE__ */ new Map();
749
+ let totalImports = 0;
750
+ let totalUsagePatterns = 0;
751
+ const patternCountMap = /* @__PURE__ */ new Map();
752
+ for (const report of reports) {
753
+ totalImports += report.summary.totalImports;
754
+ totalUsagePatterns += report.summary.totalUsagePatterns;
755
+ for (const jsx of report.patterns.usage.jsx) {
756
+ const key = jsx.component;
757
+ const existing = componentUsageMap.get(key);
758
+ if (existing) {
759
+ existing.count++;
760
+ } else {
761
+ const source = findComponentSource(jsx.component, report);
762
+ componentUsageMap.set(key, {
763
+ name: jsx.component,
764
+ source,
765
+ count: 1,
766
+ files: /* @__PURE__ */ new Set()
767
+ });
768
+ }
733
769
  }
734
- if (report.patterns.imports.aliased.length > 0) {
770
+ countPatterns(report, patternCountMap);
771
+ }
772
+ const topComponents = Array.from(componentUsageMap.values()).sort(
773
+ (a, b) => b.count - a.count
774
+ );
775
+ const allComponents = Array.from(componentUsageMap.keys()).sort();
776
+ const patternCounts = Array.from(patternCountMap.entries()).map(([type, count]) => ({
777
+ patternType: type,
778
+ displayName: getPatternDisplayName(type),
779
+ count
780
+ })).sort((a, b) => b.count - a.count);
781
+ return {
782
+ filesAnalyzed: reports.length,
783
+ totalImports,
784
+ totalComponents: componentUsageMap.size,
785
+ totalUsagePatterns,
786
+ patternCounts,
787
+ componentUsage: componentUsageMap,
788
+ topComponents,
789
+ allComponents,
790
+ reports
791
+ };
792
+ }
793
+ function findComponentSource(componentName, report) {
794
+ const namedImport = report.patterns.imports.named.find(
795
+ (imp) => imp.name === componentName
796
+ );
797
+ if (namedImport) return namedImport.source;
798
+ const defaultImport = report.patterns.imports.default.find(
799
+ (imp) => imp.name === componentName
800
+ );
801
+ if (defaultImport) return defaultImport.source;
802
+ const aliasedImport = report.patterns.imports.aliased.find(
803
+ (imp) => imp.local === componentName
804
+ );
805
+ if (aliasedImport) return aliasedImport.source;
806
+ return "unknown";
807
+ }
808
+ function countPatterns(report, patternMap) {
809
+ increment(
810
+ patternMap,
811
+ "imports.default",
812
+ report.patterns.imports.default.length
813
+ );
814
+ increment(patternMap, "imports.named", report.patterns.imports.named.length);
815
+ increment(
816
+ patternMap,
817
+ "imports.namespace",
818
+ report.patterns.imports.namespace.length
819
+ );
820
+ increment(
821
+ patternMap,
822
+ "imports.aliased",
823
+ report.patterns.imports.aliased.length
824
+ );
825
+ increment(patternMap, "usage.jsx", report.patterns.usage.jsx.length);
826
+ increment(
827
+ patternMap,
828
+ "usage.variables",
829
+ report.patterns.usage.variables.length
830
+ );
831
+ increment(
832
+ patternMap,
833
+ "usage.destructuring",
834
+ report.patterns.usage.destructuring.length
835
+ );
836
+ increment(
837
+ patternMap,
838
+ "usage.conditional",
839
+ report.patterns.usage.conditional.length
840
+ );
841
+ increment(patternMap, "usage.arrays", report.patterns.usage.arrays.length);
842
+ increment(patternMap, "usage.objects", report.patterns.usage.objects.length);
843
+ increment(patternMap, "advanced.lazy", report.patterns.advanced.lazy.length);
844
+ increment(
845
+ patternMap,
846
+ "advanced.dynamic",
847
+ report.patterns.advanced.dynamic.length
848
+ );
849
+ increment(patternMap, "advanced.hoc", report.patterns.advanced.hoc.length);
850
+ increment(patternMap, "advanced.memo", report.patterns.advanced.memo.length);
851
+ increment(
852
+ patternMap,
853
+ "advanced.forwardRef",
854
+ report.patterns.advanced.forwardRef.length
855
+ );
856
+ increment(
857
+ patternMap,
858
+ "advanced.portal",
859
+ report.patterns.advanced.portal.length
860
+ );
861
+ }
862
+ function increment(map, key, value) {
863
+ map.set(key, (map.get(key) || 0) + value);
864
+ }
865
+ function getPatternDisplayName(patternType) {
866
+ const displayNames = {
867
+ "imports.default": "Default Imports",
868
+ "imports.named": "Named Imports",
869
+ "imports.namespace": "Namespace Imports",
870
+ "imports.aliased": "Aliased Imports",
871
+ "usage.jsx": "JSX Usage",
872
+ "usage.variables": "Variable Assignments",
873
+ "usage.destructuring": "Destructuring",
874
+ "usage.conditional": "Conditional Usage",
875
+ "usage.arrays": "Array Mappings",
876
+ "usage.objects": "Object Mappings",
877
+ "advanced.lazy": "Lazy Loading",
878
+ "advanced.dynamic": "Dynamic Imports",
879
+ "advanced.hoc": "Higher-Order Components",
880
+ "advanced.memo": "Memoized Components",
881
+ "advanced.forwardRef": "Forward Refs",
882
+ "advanced.portal": "Portal Usage"
883
+ };
884
+ return displayNames[patternType] || patternType;
885
+ }
886
+ function printVerbose(filePath, report) {
887
+ const relativePath = path__default.default.relative(process.cwd(), filePath);
888
+ console.log(chalk__default.default.gray(`[VERBOSE] Analyzing: ${relativePath}`));
889
+ for (const jsx of report.patterns.usage.jsx) {
890
+ console.log(chalk__default.default.gray(`[VERBOSE] Found JSX Usage: <${jsx.component}>`));
891
+ }
892
+ for (const imp of report.patterns.imports.named) {
893
+ console.log(
894
+ chalk__default.default.gray(`[VERBOSE] Found import: ${imp.name} from ${imp.source}`)
895
+ );
896
+ }
897
+ for (const imp of report.patterns.imports.default) {
898
+ console.log(
899
+ chalk__default.default.gray(
900
+ `[VERBOSE] Found default import: ${imp.name} from ${imp.source}`
901
+ )
902
+ );
903
+ }
904
+ for (const imp of report.patterns.imports.namespace) {
905
+ console.log(
906
+ chalk__default.default.gray(
907
+ `[VERBOSE] Found namespace import: ${imp.name} from ${imp.source}`
908
+ )
909
+ );
910
+ }
911
+ for (const imp of report.patterns.imports.aliased) {
912
+ console.log(
913
+ chalk__default.default.gray(
914
+ `[VERBOSE] Found aliased import: ${imp.imported} as ${imp.local} from ${imp.source}`
915
+ )
916
+ );
917
+ }
918
+ for (const obj of report.patterns.usage.objects) {
919
+ for (const mapping of obj.mappings) {
735
920
  console.log(
736
- ` Aliased Imports (${report.patterns.imports.aliased.length}):`
921
+ chalk__default.default.gray(
922
+ `[VERBOSE] Found Object mapping with Component: ${mapping.component}`
923
+ )
737
924
  );
738
- report.patterns.imports.aliased.forEach((imp) => {
739
- console.log(
740
- ` - ${imp.alias} (originally ${imp.original}) from "${imp.source}"`
741
- );
742
- });
743
925
  }
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:`);
926
+ }
927
+ for (const cond of report.patterns.usage.conditional) {
1059
928
  console.log(
1060
- ` Overall Score: ${complexity.score}/${complexity.maxPossible}`
929
+ chalk__default.default.gray(`[VERBOSE] Found Conditional usage: ${cond.consequent}`)
1061
930
  );
931
+ }
932
+ for (const arr of report.patterns.usage.arrays) {
1062
933
  console.log(
1063
- ` Complexity Level: ${complexity.level} (${complexity.percentage}%)`
934
+ chalk__default.default.gray(
935
+ `[VERBOSE] Found Array mapping with components: ${arr.components.join(", ")}`
936
+ )
1064
937
  );
938
+ }
939
+ for (const variable of report.patterns.usage.variables) {
1065
940
  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
941
+ chalk__default.default.gray(
942
+ `[VERBOSE] Found Variable assignment: ${variable.variable} = ${variable.assignment}`
943
+ )
1071
944
  );
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)}%`);
945
+ }
946
+ for (const destructure of report.patterns.usage.destructuring) {
1111
947
  console.log(
1112
- ` Usage Intensity: ${this.getUsageIntensity(complexity.score)}`
948
+ chalk__default.default.gray(
949
+ `[VERBOSE] Found Destructuring: ${destructure.property} from ${destructure.source}`
950
+ )
1113
951
  );
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
952
  }
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}";
953
+ for (const lazy of report.patterns.advanced.lazy) {
954
+ console.log(chalk__default.default.gray(`[VERBOSE] Found Lazy import: ${lazy.source}`));
1234
955
  }
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
956
+ for (const dynamic of report.patterns.advanced.dynamic) {
957
+ console.log(
958
+ chalk__default.default.gray(`[VERBOSE] Found Dynamic import: ${dynamic.source}`)
1325
959
  );
1326
- spinner.succeed(
1327
- chalk8__default.default.green(
1328
- `Analysis complete! Analyzed ${aggregatedReport.metadata.filesAnalyzed} files`
1329
- )
960
+ }
961
+ for (const hoc of report.patterns.advanced.hoc) {
962
+ console.log(
963
+ chalk__default.default.gray(`[VERBOSE] Found HOC: ${hoc.function}(${hoc.component})`)
1330
964
  );
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
965
  }
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) {
966
+ for (const memo of report.patterns.advanced.memo) {
1357
967
  console.log(
1358
- ` Files with Errors: ${chalk8__default.default.red(report.metadata.filesWithErrors)}`
968
+ chalk__default.default.gray(`[VERBOSE] Found Memoized component: ${memo.component}`)
1359
969
  );
1360
970
  }
971
+ if (report.patterns.advanced.forwardRef.length > 0) {
972
+ console.log(chalk__default.default.gray(`[VERBOSE] Found Forward Ref usage`));
973
+ }
974
+ if (report.patterns.advanced.portal.length > 0) {
975
+ console.log(chalk__default.default.gray(`[VERBOSE] Found Portal usage`));
976
+ }
977
+ console.log(chalk__default.default.gray(`[VERBOSE] ${"\u2500".repeat(80)}`));
978
+ }
979
+ function printSummary(aggregated, elapsedTimeSeconds) {
1361
980
  console.log(
1362
- ` Total Components: ${chalk8__default.default.yellow(report.aggregated.allComponents.length)}`
981
+ chalk__default.default.green(
982
+ `[SUMMARY] Analysis completed successfully in ${elapsedTimeSeconds.toFixed(1)}s`
983
+ )
1363
984
  );
1364
985
  console.log(
1365
- ` Total Imports: ${chalk8__default.default.yellow(report.aggregated.totalImports)}`
986
+ chalk__default.default.green(
987
+ `[SUMMARY] Files analyzed: ${aggregated.filesAnalyzed.toLocaleString()}`
988
+ )
1366
989
  );
1367
990
  console.log(
1368
- ` Total Usage Patterns: ${chalk8__default.default.yellow(report.aggregated.totalUsagePatterns)}`
991
+ chalk__default.default.green(
992
+ `[SUMMARY] Total imports: ${aggregated.totalImports.toLocaleString()}`
993
+ )
1369
994
  );
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;
995
+ console.log(
996
+ chalk__default.default.green(
997
+ `[SUMMARY] Total components: ${aggregated.totalComponents.toLocaleString()}`
998
+ )
999
+ );
1000
+ }
1001
+ function printDetails(aggregated) {
1002
+ console.log(
1003
+ chalk__default.default.cyan(
1004
+ `[DETAILS] Total usage patterns: ${aggregated.totalUsagePatterns.toLocaleString()}`
1005
+ )
1006
+ );
1007
+ for (const pattern of aggregated.patternCounts) {
1008
+ if (pattern.count > 0) {
1391
1009
  console.log(
1392
- ` Average Complexity: ${chalk8__default.default.yellow(avgComplexity.toFixed(2))}`
1010
+ chalk__default.default.cyan(
1011
+ `[DETAILS] ${pattern.displayName}: ${pattern.count.toLocaleString()}`
1012
+ )
1393
1013
  );
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
1014
  }
1402
1015
  }
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
- }
1016
+ }
1017
+ function renderBarChart(data, options = {}) {
1018
+ const {
1019
+ maxWidth = 50,
1020
+ showValues = true,
1021
+ barChar = "\u2588",
1022
+ emptyChar = "\u2591"
1023
+ } = options;
1024
+ if (data.length === 0) {
1025
+ console.log(chalk__default.default.gray(" No data to display"));
1026
+ return;
1027
+ }
1028
+ const maxValue = Math.max(...data.map((d) => d.value));
1029
+ if (maxValue === 0) {
1030
+ console.log(chalk__default.default.gray(" All values are zero"));
1031
+ return;
1032
+ }
1033
+ const maxLabelLength = Math.max(...data.map((d) => d.label.length));
1034
+ for (const item of data) {
1035
+ const percentage = item.value / maxValue;
1036
+ const barLength = Math.round(percentage * maxWidth);
1037
+ const emptyLength = maxWidth - barLength;
1038
+ const paddedLabel = item.label.padEnd(maxLabelLength, " ");
1039
+ const bar = chalk__default.default.green(barChar.repeat(barLength)) + chalk__default.default.gray(emptyChar.repeat(emptyLength));
1040
+ const valueStr = showValues ? ` ${item.value.toLocaleString()}` : "";
1041
+ console.log(`${paddedLabel} ${bar}${valueStr}
1042
+ `);
1415
1043
  }
1416
- console.log("\n" + chalk8__default.default.bold.blue("\u2550".repeat(80)) + "\n");
1417
1044
  }
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);
1045
+
1046
+ // src/utils/print-top-components.ts
1047
+ function printTopComponents(aggregated, mode, topN = 10) {
1048
+ const topComponents = aggregated.topComponents.slice(0, topN);
1049
+ if (mode === "log") {
1050
+ printTopComponentsLog(topComponents);
1051
+ } else if (mode === "table") {
1052
+ printTopComponentsTable(topComponents);
1053
+ } else if (mode === "chart") {
1054
+ printTopComponentsChart(topComponents);
1482
1055
  }
1483
1056
  }
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);
1057
+ function printTopComponentsLog(components) {
1058
+ console.log(chalk__default.default.yellow.bold(`
1059
+ \u{1F3C6} Top Components
1060
+ `));
1061
+ if (components.length === 0) {
1062
+ console.log(chalk__default.default.gray(" No components found"));
1063
+ return;
1064
+ }
1065
+ components.forEach((comp, idx) => {
1066
+ const rank = idx + 1;
1067
+ const emoji = rank === 1 ? "\u{1F947}" : rank === 2 ? "\u{1F948}" : rank === 3 ? "\u{1F949}" : " ";
1068
+ const sourceStr = comp.source !== "unknown" ? ` from ${comp.source}` : "";
1492
1069
  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
- }
1070
+ chalk__default.default.yellow(
1071
+ `[TOP-COMPONENTS] ${emoji} ${rank}. ${comp.name}${sourceStr}: ${comp.count} uses`
1072
+ )
1529
1073
  );
1530
1074
  });
1531
1075
  }
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();
1076
+ function printTopComponentsTable(components) {
1077
+ console.log(chalk__default.default.yellow(`[TOP-COMPONENTS] TABLE`));
1078
+ if (components.length === 0) {
1079
+ console.log(chalk__default.default.gray(" No components found"));
1080
+ return;
1081
+ }
1082
+ const table = new Table__default.default({
1083
+ head: ["Rank", "Component", "Source", "Count"],
1084
+ style: {
1085
+ head: ["cyan"],
1086
+ border: ["gray"]
1599
1087
  }
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
1088
  });
1625
- return files;
1089
+ components.forEach((comp, idx) => {
1090
+ const rank = idx + 1;
1091
+ const emoji = rank === 1 ? "\u{1F947}" : rank === 2 ? "\u{1F948}" : rank === 3 ? "\u{1F949}" : `${rank}.`;
1092
+ table.push([emoji, comp.name, comp.source, comp.count.toString()]);
1093
+ });
1094
+ console.log(table.toString());
1626
1095
  }
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;
1096
+ function printTopComponentsChart(components) {
1097
+ console.log(chalk__default.default.yellow(`[TOP-COMPONENTS] CHART`));
1098
+ if (components.length === 0) {
1099
+ console.log(chalk__default.default.gray(" No components found"));
1100
+ return;
1101
+ }
1102
+ const data = components.map((comp) => ({
1103
+ label: comp.name,
1104
+ value: comp.count
1105
+ }));
1106
+ renderBarChart(data, { maxWidth: 50 });
1638
1107
  }
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
- };
1108
+ function printComponentsUsage(aggregated, mode) {
1109
+ const components = aggregated.topComponents;
1110
+ if (mode === "table") {
1111
+ printComponentsUsageTable(components);
1112
+ } else if (mode === "chart") {
1113
+ printComponentsUsageChart(components);
1684
1114
  }
1685
1115
  }
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}`));
1116
+ function printComponentsUsageTable(components) {
1117
+ console.log(chalk__default.default.magenta.bold(`
1118
+ \u269B\uFE0F Components Usage
1119
+ `));
1120
+ if (components.length === 0) {
1121
+ console.log(chalk__default.default.gray(" No components found"));
1122
+ return;
1739
1123
  }
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);
1124
+ const table = new Table__default.default({
1125
+ head: ["Component", "Source", "Version", "Count"],
1126
+ style: {
1127
+ head: ["cyan"],
1128
+ border: ["gray"]
1770
1129
  }
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
- }
1130
+ });
1131
+ components.forEach((comp) => {
1132
+ table.push([comp.name, comp.source, "0.0.0", comp.count.toString()]);
1133
+ });
1134
+ console.log(table.toString());
1777
1135
  }
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
- }
1136
+ function printComponentsUsageChart(components) {
1137
+ console.log(chalk__default.default.magenta.bold(`
1138
+ \u269B\uFE0F Components Usage
1139
+ `));
1140
+ if (components.length === 0) {
1141
+ console.log(chalk__default.default.gray(" No components found"));
1142
+ return;
1143
+ }
1144
+ const data = components.map((comp) => ({
1145
+ label: comp.name,
1146
+ value: comp.count
1147
+ }));
1148
+ renderBarChart(data, { maxWidth: 50 });
1810
1149
  }
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 {};
1150
+ function printPatterns(aggregated, mode) {
1151
+ const patterns = aggregated.patternCounts.filter((p) => p.count > 0);
1152
+ if (mode === "table") {
1153
+ printPatternsTable(patterns);
1154
+ } else if (mode === "chart") {
1155
+ printPatternsChart(patterns);
1864
1156
  }
1865
1157
  }
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
- }
1158
+ function printPatternsTable(patterns) {
1159
+ console.log(chalk__default.default.blue.bold(`
1160
+ \u{1F50D} Code Patterns
1161
+ `));
1162
+ if (patterns.length === 0) {
1163
+ console.log(chalk__default.default.gray(" No patterns found"));
1164
+ return;
1887
1165
  }
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");
1166
+ const table = new Table__default.default({
1167
+ head: ["Pattern", "Count"],
1168
+ style: {
1169
+ head: ["cyan"],
1170
+ border: ["gray"]
1171
+ }
1172
+ });
1173
+ patterns.forEach((pattern) => {
1174
+ table.push([pattern.displayName, pattern.count.toString()]);
1175
+ });
1176
+ console.log(table.toString());
1897
1177
  }
1898
- function readJsonFile(filePath) {
1899
- const content = readFile(filePath);
1900
- return JSON.parse(content);
1178
+ function printPatternsChart(patterns) {
1179
+ console.log(chalk__default.default.blue(`[PATTERNS] CHART`));
1180
+ if (patterns.length === 0) {
1181
+ console.log(chalk__default.default.gray(" No patterns found"));
1182
+ return;
1183
+ }
1184
+ const data = patterns.map((pattern) => ({
1185
+ label: pattern.displayName,
1186
+ value: pattern.count
1187
+ }));
1188
+ renderBarChart(data, { maxWidth: 50 });
1901
1189
  }
1902
1190
 
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
- }
1191
+ // src/commands/scan.ts
1192
+ function registerScanCommand(program2) {
1193
+ program2.command("scan").description("Scan and analyze local files").argument(
1194
+ "[pattern]",
1195
+ "Glob pattern for files to analyze (defaults to current directory recursively)",
1196
+ "**/*.{tsx,jsx,ts,js}"
1197
+ ).option(
1198
+ "--verbose",
1199
+ "Show detailed file-by-file analysis with every pattern found",
1200
+ false
1201
+ ).option("--summary [mode]", "Show summary stats (log, false)", "log").option("--details", "Show detailed pattern counts").option(
1202
+ "--top-components [mode]",
1203
+ "Show top components (log, table, chart)",
1204
+ "log"
1205
+ ).option(
1206
+ "--components-usage [mode]",
1207
+ "Show components table/chart (table, chart)",
1208
+ "table"
1209
+ ).option(
1210
+ "--patterns [mode]",
1211
+ "Show patterns table/chart (table, chart)",
1212
+ "table"
1213
+ ).action(async (pattern, options) => {
1214
+ const normalizedOptions = normalizeOptions(options);
1215
+ await executeScan(pattern, normalizedOptions);
1216
+ });
2062
1217
  }
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
- );
1218
+ function normalizeOptions(options) {
2092
1219
  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
1220
+ verbose: options.verbose || false,
1221
+ summary: options.summary === false || options.summary === "false" ? false : "log",
1222
+ details: options.details || false,
1223
+ topComponents: options.topComponents || "log",
1224
+ componentsUsage: options.componentsUsage || "table",
1225
+ patterns: options.patterns || "table",
1226
+ output: options.output
2104
1227
  };
2105
1228
  }
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();
1229
+ async function executeScan(pattern, options) {
1230
+ const startTime = Date.now();
1231
+ const spinner = ora__default.default("Finding files...").start();
2177
1232
  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
1233
+ const files = await glob.glob(pattern, {
1234
+ ignore: ["node_modules/**", "dist/**", "build/**", ".git/**"],
1235
+ absolute: true
2211
1236
  });
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
1237
  if (files.length === 0) {
2334
- spinner.fail(chalk8__default.default.red("No files found"));
1238
+ spinner.fail(chalk__default.default.red(`No files found matching pattern: ${pattern}`));
2335
1239
  return;
2336
1240
  }
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) {
1241
+ spinner.succeed(chalk__default.default.green(`Found ${files.length} files`));
1242
+ spinner.start("Analyzing files...");
1243
+ const reports = [];
1244
+ for (let i = 0; i < files.length; i++) {
1245
+ const file = files[i];
1246
+ if (!options.verbose) {
1247
+ spinner.text = `Analyzing files... (${i + 1}/${files.length})`;
2354
1248
  }
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
1249
  try {
2391
- const report = analyzer.analyzeFile(file);
1250
+ const report = parseFile(file);
2392
1251
  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
- });
1252
+ reports.push(report);
1253
+ if (options.verbose) {
1254
+ spinner.stop();
1255
+ printVerbose(file, report);
1256
+ spinner.start();
1257
+ }
2405
1258
  }
2406
1259
  } catch (error) {
1260
+ spinner.stop();
1261
+ console.error(chalk__default.default.red(`Error analyzing ${file}: ${error.message}`));
1262
+ spinner.start();
2407
1263
  }
2408
1264
  }
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]));
1265
+ spinner.succeed(
1266
+ chalk__default.default.green(`Analysis complete! Analyzed ${reports.length} files`)
1267
+ );
1268
+ const elapsedTime = (Date.now() - startTime) / 1e3;
1269
+ const aggregated = aggregateReports(reports);
1270
+ console.log("");
1271
+ if (options.summary) {
1272
+ printSummary(aggregated, elapsedTime);
2418
1273
  }
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)}`);
1274
+ if (options.details) {
2425
1275
  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
- }
1276
+ printDetails(aggregated);
2479
1277
  }
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;
1278
+ if (options.topComponents) {
1279
+ console.log("");
1280
+ printTopComponents(aggregated, options.topComponents);
2525
1281
  }
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
- }
1282
+ if (options.componentsUsage) {
1283
+ console.log("");
1284
+ printComponentsUsage(aggregated, options.componentsUsage);
2572
1285
  }
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
- });
1286
+ if (options.patterns) {
1287
+ console.log("");
1288
+ printPatterns(aggregated, options.patterns);
2656
1289
  }
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
1290
  } catch (error) {
2663
- spinner.fail(chalk8__default.default.red("Table generation failed: " + error.message));
1291
+ spinner.fail(chalk__default.default.red("Analysis failed: " + error.message));
1292
+ console.error(error);
1293
+ process.exit(1);
2664
1294
  }
2665
1295
  }
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
- }
1296
+
1297
+ // package.json
1298
+ var package_default = {
1299
+ version: "1.0.0-beta.1"};
1300
+
1301
+ // src/cli.ts
1302
+ var program = new commander.Command();
1303
+ program.name("hermex").description("Analyze React component usage patterns in your codebase").version(package_default.version);
1304
+ registerScanCommand(program);
2691
1305
  program.parse(process.argv);
2692
- if (process.argv.length < 3) {
2693
- program.help();
2694
- }
1306
+
1307
+ exports.program = program;
2695
1308
  //# sourceMappingURL=cli.js.map
2696
1309
  //# sourceMappingURL=cli.js.map