@mohamed_fadl/reactlens 1.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -0
- package/dist/analyzer/componentAnalyzer.d.ts +49 -0
- package/dist/analyzer/componentAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/componentAnalyzer.js +156 -0
- package/dist/analyzer/componentAnalyzer.js.map +1 -0
- package/dist/analyzer/dependencyAnalyzer.d.ts +31 -0
- package/dist/analyzer/dependencyAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/dependencyAnalyzer.js +86 -0
- package/dist/analyzer/dependencyAnalyzer.js.map +1 -0
- package/dist/analyzer/insightEngine.d.ts +44 -0
- package/dist/analyzer/insightEngine.d.ts.map +1 -0
- package/dist/analyzer/insightEngine.js +153 -0
- package/dist/analyzer/insightEngine.js.map +1 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +148 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/reporters/terminalReporter.d.ts +26 -0
- package/dist/reporters/terminalReporter.d.ts.map +1 -0
- package/dist/reporters/terminalReporter.js +84 -0
- package/dist/reporters/terminalReporter.js.map +1 -0
- package/dist/scanners/fileScanner.d.ts +31 -0
- package/dist/scanners/fileScanner.d.ts.map +1 -0
- package/dist/scanners/fileScanner.js +78 -0
- package/dist/scanners/fileScanner.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# ReactLens: Advanced Architectural Analysis Engine
|
|
2
|
+
|
|
3
|
+
ReactLens is a high-performance, deterministic tool designed for deep architectural auditing of React and Next.js applications. It leverages static AST analysis and graph theory to extract actionable intelligence from complex codebases.
|
|
4
|
+
|
|
5
|
+
## System Prerequisites
|
|
6
|
+
|
|
7
|
+
To use the visual graph generation features (`graph` command), you must have **Graphviz** installed on your system:
|
|
8
|
+
|
|
9
|
+
- **Windows:** `winget install graphviz`
|
|
10
|
+
- **macOS:** `brew install graphviz`
|
|
11
|
+
- **Linux:** `sudo apt install graphviz`
|
|
12
|
+
|
|
13
|
+
*Note: The core analysis engine (`analyze`) works without Graphviz.*
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g react-lens
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage & Commands
|
|
22
|
+
|
|
23
|
+
### 1. Project Analysis
|
|
24
|
+
Analyze your project's architectural health, complexity, and dependencies.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
react-lens analyze [path] [options]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Options:**
|
|
31
|
+
- `-j, --json [file]` : Output report in JSON format. If no file is provided, it prints to stdout.
|
|
32
|
+
- `-s, --silent` : Suppress the visual terminal report (ideal for piping JSON).
|
|
33
|
+
- `--fail-under <score>` : Exit with code 1 if the Architectural Score is below the threshold.
|
|
34
|
+
|
|
35
|
+
### 2. Dependency Graph
|
|
36
|
+
Generate a visual representation of your project's module relationships.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
react-lens graph [path] --output <file.svg|file.dot>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Logical Flow and Architecture
|
|
43
|
+
|
|
44
|
+
```mermaid
|
|
45
|
+
graph TD
|
|
46
|
+
A[Project Root] --> B[FileScanner]
|
|
47
|
+
B --> C{Framework Detection}
|
|
48
|
+
C -->|Next.js| D[App Router Analysis]
|
|
49
|
+
C -->|React/Vite| E[Basic Component Analysis]
|
|
50
|
+
C -->|Node CLI| F[CLI Tool Analysis]
|
|
51
|
+
|
|
52
|
+
B --> G[Babel AST Parser]
|
|
53
|
+
G --> H[ComponentAnalyzer]
|
|
54
|
+
H --> I[Extraction: Props/Hooks/Client-Directives]
|
|
55
|
+
H --> J[Prop Drilling Detection]
|
|
56
|
+
|
|
57
|
+
B --> K[Madge Dependency Engine]
|
|
58
|
+
K --> L[Graph Theory Resolution]
|
|
59
|
+
L --> M[Cycle & Zombie Detection]
|
|
60
|
+
|
|
61
|
+
I --> N[Insight Engine]
|
|
62
|
+
J --> N
|
|
63
|
+
M --> N
|
|
64
|
+
N --> O[Mathematical Scoring Model]
|
|
65
|
+
O --> P[Terminal/JSON/Silent Reporting]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Technical Implementation & Algorithms
|
|
69
|
+
|
|
70
|
+
### 1. Semantic AST Traversal & Prop Drilling
|
|
71
|
+
ReactLens utilizes the `@babel/parser` for Abstract Syntax Tree (AST) generation. The `ComponentAnalyzer` implements:
|
|
72
|
+
- **Functional Components:** Identified via function signatures returning JSX.
|
|
73
|
+
- **Prop Drilling Heuristic:** Detects properties passed down to children without local usage within the component body.
|
|
74
|
+
- **Hook Signatures:** Detected through the `use` prefix heuristic.
|
|
75
|
+
|
|
76
|
+
### 2. Dependency Graph Theory
|
|
77
|
+
The `DependencyAnalyzer` constructs a Directed Acyclic Graph (DAG) using **Madge**.
|
|
78
|
+
- **Cycle Detection:** Utilizes DFS to identify circular imports.
|
|
79
|
+
- **Zombie Analysis:** Identifies "Zombie Components" (unreachable nodes) by excluding test files and entry points.
|
|
80
|
+
|
|
81
|
+
## Mathematical Scoring Model (Weighted)
|
|
82
|
+
|
|
83
|
+
The Architectural Integrity Score ($S$) is calculated using weighted health sections:
|
|
84
|
+
|
|
85
|
+
$$S = \text{round}(0.4 \cdot S_{comp} + 0.4 \cdot S_{coup} + 0.2 \cdot S_{zom})$$
|
|
86
|
+
|
|
87
|
+
- **Complexity ($S_{comp}$):** Penalties for large components ($>300$ lines), high prop counts, and detected prop drilling.
|
|
88
|
+
- **Coupling ($S_{coup}$):** Penalties for circular dependencies (15% per cycle).
|
|
89
|
+
- **Zombies ($S_{zom}$):** Penalties for unused modules (2% per instance).
|
|
90
|
+
|
|
91
|
+
## Strategic Roadmap
|
|
92
|
+
|
|
93
|
+
- **v1.0 - v1.2: Advanced Intelligence (Current)**
|
|
94
|
+
AST Analysis, Weighted Scoring, Prop Drilling Detection, JSON Stdout, and Dedicated Graph CLI.
|
|
95
|
+
- **v1.5: HTML Interactive Dashboard**
|
|
96
|
+
Visual exploration of the architecture with real-time filtering.
|
|
97
|
+
- **v1.8: Direct Refactoring AI Integration**
|
|
98
|
+
Automated code transformation suggestions.
|
|
99
|
+
- **v2.0: Multi-Framework Meta-Analysis**
|
|
100
|
+
Unified quality standards for Vue, Svelte, and Angular.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
Technical Reference - ReactLens Engineering Team.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview ComponentAnalyzer
|
|
3
|
+
* Engine responsible for analyzing JS/TS file contents.
|
|
4
|
+
* Uses Babel AST to discover React components and calculate size/complexity.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Represents analysis metrics for a single component
|
|
8
|
+
*/
|
|
9
|
+
export interface ComponentMetrics {
|
|
10
|
+
name: string;
|
|
11
|
+
lineCount: number;
|
|
12
|
+
type: 'functional' | 'class';
|
|
13
|
+
isLarge: boolean;
|
|
14
|
+
propCount: number;
|
|
15
|
+
props: string[];
|
|
16
|
+
hookCount: number;
|
|
17
|
+
hooks: string[];
|
|
18
|
+
isClientComponent: boolean;
|
|
19
|
+
drilledProps: string[];
|
|
20
|
+
}
|
|
21
|
+
export declare class ComponentAnalyzer {
|
|
22
|
+
private readonly SIZE_THRESHOLD;
|
|
23
|
+
/**
|
|
24
|
+
* Analyzes a single file and extracts component information
|
|
25
|
+
* @param filePath Path of the file to analyze
|
|
26
|
+
*/
|
|
27
|
+
analyzeFile(filePath: string): Promise<ComponentMetrics[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Extracts hook names used within a component
|
|
30
|
+
* @param componentPath Babel path of the component
|
|
31
|
+
*/
|
|
32
|
+
private extractHooks;
|
|
33
|
+
/**
|
|
34
|
+
* Extracts prop names from function parameters
|
|
35
|
+
* @param params Babel AST function parameters
|
|
36
|
+
*/
|
|
37
|
+
private extractProps;
|
|
38
|
+
/**
|
|
39
|
+
* Simple rule to identify component name (starts with uppercase)
|
|
40
|
+
* @param name Name of the identifier
|
|
41
|
+
*/
|
|
42
|
+
private isComponentName;
|
|
43
|
+
/**
|
|
44
|
+
* Identifies props that are passed down to children without being used locally
|
|
45
|
+
* Heuristic for V1.2
|
|
46
|
+
*/
|
|
47
|
+
private detectPropDrilling;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=componentAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"componentAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/componentAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAO;IAEtC;;;OAGG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAgEhE;;;OAGG;IACH,OAAO,CAAC,YAAY;IAqBpB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAoBpB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAKvB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAyB3B"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview ComponentAnalyzer
|
|
3
|
+
* Engine responsible for analyzing JS/TS file contents.
|
|
4
|
+
* Uses Babel AST to discover React components and calculate size/complexity.
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from 'fs';
|
|
7
|
+
import * as parser from '@babel/parser';
|
|
8
|
+
import _traverse from '@babel/traverse';
|
|
9
|
+
const traverse = _traverse.default || _traverse;
|
|
10
|
+
export class ComponentAnalyzer {
|
|
11
|
+
SIZE_THRESHOLD = 300;
|
|
12
|
+
/**
|
|
13
|
+
* Analyzes a single file and extracts component information
|
|
14
|
+
* @param filePath Path of the file to analyze
|
|
15
|
+
*/
|
|
16
|
+
async analyzeFile(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
const code = await fs.readFile(filePath, 'utf-8');
|
|
19
|
+
const ast = parser.parse(code, {
|
|
20
|
+
sourceType: 'module',
|
|
21
|
+
plugins: ['typescript', 'jsx'],
|
|
22
|
+
});
|
|
23
|
+
const components = [];
|
|
24
|
+
const hasUseClient = ast.program.directives.some(d => d.value.value === 'use client');
|
|
25
|
+
traverse(ast, {
|
|
26
|
+
// Detect function declarations (Functional Components)
|
|
27
|
+
FunctionDeclaration: (path) => {
|
|
28
|
+
const name = path.node.id?.name;
|
|
29
|
+
if (this.isComponentName(name)) {
|
|
30
|
+
const props = this.extractProps(path.node.params);
|
|
31
|
+
const hooks = this.extractHooks(path);
|
|
32
|
+
components.push({
|
|
33
|
+
name,
|
|
34
|
+
lineCount: path.node.loc.end.line - path.node.loc.start.line + 1,
|
|
35
|
+
type: 'functional',
|
|
36
|
+
isLarge: (path.node.loc.end.line - path.node.loc.start.line + 1) > this.SIZE_THRESHOLD,
|
|
37
|
+
propCount: props.length,
|
|
38
|
+
props,
|
|
39
|
+
hookCount: hooks.length,
|
|
40
|
+
hooks,
|
|
41
|
+
isClientComponent: hasUseClient,
|
|
42
|
+
drilledProps: this.detectPropDrilling(path, props)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
// Detect variable-assigned functions (Arrow Functions)
|
|
47
|
+
VariableDeclarator: (path) => {
|
|
48
|
+
if (path.node.id.type === 'Identifier') {
|
|
49
|
+
const name = path.node.id.name;
|
|
50
|
+
const init = path.node.init;
|
|
51
|
+
if (this.isComponentName(name) &&
|
|
52
|
+
(init?.type === 'ArrowFunctionExpression' || init?.type === 'FunctionExpression')) {
|
|
53
|
+
const props = this.extractProps(init.params);
|
|
54
|
+
const hooks = this.extractHooks(path.get('init'));
|
|
55
|
+
components.push({
|
|
56
|
+
name,
|
|
57
|
+
lineCount: init.loc.end.line - init.loc.start.line + 1,
|
|
58
|
+
type: 'functional',
|
|
59
|
+
isLarge: (init.loc.end.line - init.loc.start.line + 1) > this.SIZE_THRESHOLD,
|
|
60
|
+
propCount: props.length,
|
|
61
|
+
props,
|
|
62
|
+
hookCount: hooks.length,
|
|
63
|
+
hooks,
|
|
64
|
+
isClientComponent: hasUseClient,
|
|
65
|
+
drilledProps: this.detectPropDrilling(path.get('init'), props)
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return components;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Extracts hook names used within a component
|
|
79
|
+
* @param componentPath Babel path of the component
|
|
80
|
+
*/
|
|
81
|
+
extractHooks(componentPath) {
|
|
82
|
+
const hooks = [];
|
|
83
|
+
componentPath.traverse({
|
|
84
|
+
CallExpression(path) {
|
|
85
|
+
const callee = path.node.callee;
|
|
86
|
+
let hookName = null;
|
|
87
|
+
if (callee.type === 'Identifier' && callee.name.startsWith('use')) {
|
|
88
|
+
hookName = callee.name;
|
|
89
|
+
}
|
|
90
|
+
else if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && callee.property.name.startsWith('use')) {
|
|
91
|
+
hookName = callee.property.name;
|
|
92
|
+
}
|
|
93
|
+
if (hookName) {
|
|
94
|
+
hooks.push(hookName);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return hooks;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Extracts prop names from function parameters
|
|
102
|
+
* @param params Babel AST function parameters
|
|
103
|
+
*/
|
|
104
|
+
extractProps(params) {
|
|
105
|
+
if (params.length === 0)
|
|
106
|
+
return [];
|
|
107
|
+
const firstParam = params[0];
|
|
108
|
+
// Case 1: Destructured props ({ name, age })
|
|
109
|
+
if (firstParam.type === 'ObjectPattern') {
|
|
110
|
+
return firstParam.properties
|
|
111
|
+
.filter((p) => p.type === 'ObjectProperty' && p.key.type === 'Identifier')
|
|
112
|
+
.map((p) => p.key.name);
|
|
113
|
+
}
|
|
114
|
+
// Case 2: Single props object (props)
|
|
115
|
+
if (firstParam.type === 'Identifier') {
|
|
116
|
+
return [firstParam.name];
|
|
117
|
+
}
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Simple rule to identify component name (starts with uppercase)
|
|
122
|
+
* @param name Name of the identifier
|
|
123
|
+
*/
|
|
124
|
+
isComponentName(name) {
|
|
125
|
+
if (!name)
|
|
126
|
+
return false;
|
|
127
|
+
return /^[A-Z]/.test(name);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Identifies props that are passed down to children without being used locally
|
|
131
|
+
* Heuristic for V1.2
|
|
132
|
+
*/
|
|
133
|
+
detectPropDrilling(componentPath, props) {
|
|
134
|
+
const drilled = [];
|
|
135
|
+
const usedLocally = new Set();
|
|
136
|
+
props.forEach(prop => {
|
|
137
|
+
// Find all usages of this prop identifier
|
|
138
|
+
componentPath.traverse({
|
|
139
|
+
Identifier(path) {
|
|
140
|
+
if (path.node.name === prop) {
|
|
141
|
+
// Check if it's a JSXAttribute value (passing it down)
|
|
142
|
+
const isPassingDown = path.findParent((p) => p.isJSXAttribute());
|
|
143
|
+
if (!isPassingDown) {
|
|
144
|
+
usedLocally.add(prop);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
if (!usedLocally.has(prop)) {
|
|
150
|
+
drilled.push(prop);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return drilled;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=componentAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"componentAnalyzer.js","sourceRoot":"","sources":["../../src/analyzer/componentAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DependencyAnalyzer
|
|
3
|
+
* Responsible for analyzing file relationships and structural issues.
|
|
4
|
+
* Uses Madge library to identify circular dependencies, unused modules, and coupling.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Represents dependency metrics for the project
|
|
8
|
+
*/
|
|
9
|
+
export interface DependencyMetrics {
|
|
10
|
+
circular: string[][];
|
|
11
|
+
totalModules: number;
|
|
12
|
+
zombieComponents: string[];
|
|
13
|
+
couplingMap: Record<string, number>;
|
|
14
|
+
internalModules: string[];
|
|
15
|
+
externalModules: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare class DependencyAnalyzer {
|
|
18
|
+
private madgeInstance;
|
|
19
|
+
/**
|
|
20
|
+
* Analyzes the project and detects circular dependencies, zombies, and coupling
|
|
21
|
+
* @param rootPath Project root path
|
|
22
|
+
* @param entryPoint Optional entry point to help identify unused files (e.g., src/cli/index.ts)
|
|
23
|
+
*/
|
|
24
|
+
analyze(rootPath: string, entryPoint?: string): Promise<DependencyMetrics>;
|
|
25
|
+
/**
|
|
26
|
+
* Exports the dependency graph to a visual format
|
|
27
|
+
* @param outputPath Path where the graph will be saved
|
|
28
|
+
*/
|
|
29
|
+
exportGraph(outputPath: string): Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=dependencyAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencyAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/dependencyAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,aAAa,CAAa;IAElC;;;;OAIG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA2DhF;;;OAGG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAYvD"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DependencyAnalyzer
|
|
3
|
+
* Responsible for analyzing file relationships and structural issues.
|
|
4
|
+
* Uses Madge library to identify circular dependencies, unused modules, and coupling.
|
|
5
|
+
*/
|
|
6
|
+
import madge from 'madge';
|
|
7
|
+
export class DependencyAnalyzer {
|
|
8
|
+
madgeInstance = null;
|
|
9
|
+
/**
|
|
10
|
+
* Analyzes the project and detects circular dependencies, zombies, and coupling
|
|
11
|
+
* @param rootPath Project root path
|
|
12
|
+
* @param entryPoint Optional entry point to help identify unused files (e.g., src/cli/index.ts)
|
|
13
|
+
*/
|
|
14
|
+
async analyze(rootPath, entryPoint) {
|
|
15
|
+
try {
|
|
16
|
+
this.madgeInstance = await madge(rootPath, {
|
|
17
|
+
fileExtensions: ['js', 'jsx', 'ts', 'tsx'],
|
|
18
|
+
excludeRegExp: [/node_modules/, /dist/, /.next/, /__tests__/, /\.test\./, /\.spec\./, /vitest\.config/, /jest\.config/],
|
|
19
|
+
});
|
|
20
|
+
const circular = this.madgeInstance.circular();
|
|
21
|
+
const obj = this.madgeInstance.obj();
|
|
22
|
+
const totalModules = Object.keys(obj).length;
|
|
23
|
+
// Calculate coupling and modules
|
|
24
|
+
const couplingMap = {};
|
|
25
|
+
const internalModules = Object.keys(obj);
|
|
26
|
+
const externalSet = new Set();
|
|
27
|
+
internalModules.forEach(module => {
|
|
28
|
+
couplingMap[module] = 0;
|
|
29
|
+
});
|
|
30
|
+
Object.values(obj).forEach((deps) => {
|
|
31
|
+
deps.forEach((dep) => {
|
|
32
|
+
// If it's not in our internal modules, it might be an external lib
|
|
33
|
+
// Note: Madge with default config might not list all npm packages unless configured.
|
|
34
|
+
// But we can infer external ones if they don't match our file paths or are explicitly listed.
|
|
35
|
+
if (couplingMap[dep] !== undefined) {
|
|
36
|
+
couplingMap[dep]++;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
externalSet.add(dep);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
const zombieComponents = internalModules.filter(module => {
|
|
44
|
+
if (entryPoint && module.includes(entryPoint))
|
|
45
|
+
return false;
|
|
46
|
+
return couplingMap[module] === 0;
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
circular,
|
|
50
|
+
totalModules,
|
|
51
|
+
zombieComponents,
|
|
52
|
+
couplingMap,
|
|
53
|
+
internalModules,
|
|
54
|
+
externalModules: Array.from(externalSet)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error('Error during dependency analysis:', error);
|
|
59
|
+
return {
|
|
60
|
+
circular: [],
|
|
61
|
+
totalModules: 0,
|
|
62
|
+
zombieComponents: [],
|
|
63
|
+
couplingMap: {},
|
|
64
|
+
internalModules: [],
|
|
65
|
+
externalModules: []
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Exports the dependency graph to a visual format
|
|
71
|
+
* @param outputPath Path where the graph will be saved
|
|
72
|
+
*/
|
|
73
|
+
async exportGraph(outputPath) {
|
|
74
|
+
if (!this.madgeInstance)
|
|
75
|
+
throw new Error('You must run analyze() before exporting a graph.');
|
|
76
|
+
const ext = outputPath.split('.').pop()?.toLowerCase();
|
|
77
|
+
if (ext === 'svg') {
|
|
78
|
+
return await this.madgeInstance.image(outputPath);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Default to DOT format which is robust
|
|
82
|
+
return await this.madgeInstance.dot(outputPath);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=dependencyAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencyAnalyzer.js","sourceRoot":"","sources":["../../src/analyzer/dependencyAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview InsightEngine
|
|
3
|
+
* Responsible for generating actionable recommendations and calculating advanced scores.
|
|
4
|
+
* Transforms raw metrics into architectural insights.
|
|
5
|
+
*/
|
|
6
|
+
import { ComponentMetrics } from '../analyzer/componentAnalyzer.js';
|
|
7
|
+
import { DependencyMetrics } from '../analyzer/dependencyAnalyzer.js';
|
|
8
|
+
export interface Recommendation {
|
|
9
|
+
level: 'info' | 'warning' | 'error';
|
|
10
|
+
target: string;
|
|
11
|
+
message: string;
|
|
12
|
+
suggestion: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ArchitectureReport {
|
|
15
|
+
score: number;
|
|
16
|
+
breakdown: {
|
|
17
|
+
complexity: number;
|
|
18
|
+
coupling: number;
|
|
19
|
+
zombies: number;
|
|
20
|
+
};
|
|
21
|
+
recommendations: Recommendation[];
|
|
22
|
+
}
|
|
23
|
+
export declare class InsightEngine {
|
|
24
|
+
private readonly TRESHOLDS;
|
|
25
|
+
/**
|
|
26
|
+
* Generates a comprehensive architectural insight report
|
|
27
|
+
* @param componentMetrics List of metrics for all components
|
|
28
|
+
* @param dependencyMetrics Results from dependency analysis
|
|
29
|
+
*/
|
|
30
|
+
generateReport(componentMetrics: ComponentMetrics[], dependencyMetrics: DependencyMetrics): ArchitectureReport;
|
|
31
|
+
/**
|
|
32
|
+
* Removes duplicate recommendations for the same target and message
|
|
33
|
+
*/
|
|
34
|
+
private deduplicateRecommendations;
|
|
35
|
+
/**
|
|
36
|
+
* Advanced Scoring v2 - Weighs different architectural issues
|
|
37
|
+
*/
|
|
38
|
+
private calculateAdvancedScore;
|
|
39
|
+
/**
|
|
40
|
+
* Checks for Next.js specific boundary violations or optimizations
|
|
41
|
+
*/
|
|
42
|
+
private checkNextJsBoundaries;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=insightEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insightEngine.d.ts","sourceRoot":"","sources":["../../src/analyzer/insightEngine.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE;QACT,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,eAAe,EAAE,cAAc,EAAE,CAAC;CACnC;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAIxB;IAEF;;;;OAIG;IACH,cAAc,CACZ,gBAAgB,EAAE,gBAAgB,EAAE,EACpC,iBAAiB,EAAE,iBAAiB,GACnC,kBAAkB;IAqFrB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAUlC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiC9B;;OAEG;IACH,OAAO,CAAC,qBAAqB;CAkB9B"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview InsightEngine
|
|
3
|
+
* Responsible for generating actionable recommendations and calculating advanced scores.
|
|
4
|
+
* Transforms raw metrics into architectural insights.
|
|
5
|
+
*/
|
|
6
|
+
export class InsightEngine {
|
|
7
|
+
TRESHOLDS = {
|
|
8
|
+
LARGE_COMPONENT: 300,
|
|
9
|
+
HIGH_PROP_COUNT: 5,
|
|
10
|
+
HIGH_HOOK_COUNT: 4,
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Generates a comprehensive architectural insight report
|
|
14
|
+
* @param componentMetrics List of metrics for all components
|
|
15
|
+
* @param dependencyMetrics Results from dependency analysis
|
|
16
|
+
*/
|
|
17
|
+
generateReport(componentMetrics, dependencyMetrics) {
|
|
18
|
+
const recommendations = [];
|
|
19
|
+
// Analyze components for recommendations
|
|
20
|
+
componentMetrics.forEach(c => {
|
|
21
|
+
if (c.isLarge) {
|
|
22
|
+
recommendations.push({
|
|
23
|
+
level: 'warning',
|
|
24
|
+
target: c.name,
|
|
25
|
+
message: `Oversized component detected (${c.lineCount} lines).`,
|
|
26
|
+
suggestion: 'Break this component into smaller, more focused sub-components.'
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (c.propCount > this.TRESHOLDS.HIGH_PROP_COUNT) {
|
|
30
|
+
recommendations.push({
|
|
31
|
+
level: 'info',
|
|
32
|
+
target: c.name,
|
|
33
|
+
message: `High prop count (${c.propCount} props).`,
|
|
34
|
+
suggestion: 'Consider grouping related props or using a React Context.'
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (c.hookCount > this.TRESHOLDS.HIGH_HOOK_COUNT) {
|
|
38
|
+
recommendations.push({
|
|
39
|
+
level: 'info',
|
|
40
|
+
target: c.name,
|
|
41
|
+
message: `Complexity warning: High hook count (${c.hookCount} hooks).`,
|
|
42
|
+
suggestion: 'Extract some logic into a Custom Hook to simplify the component.'
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (c.drilledProps.length > 0) {
|
|
46
|
+
recommendations.push({
|
|
47
|
+
level: 'info',
|
|
48
|
+
target: c.name,
|
|
49
|
+
message: `Potential Prop Drilling: ${c.drilledProps.join(', ')} passed down without local usage.`,
|
|
50
|
+
suggestion: 'Consider using React Context or a State Management library for deeply nested data.'
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (c.isClientComponent) {
|
|
54
|
+
recommendations.push({
|
|
55
|
+
level: 'info',
|
|
56
|
+
target: c.name,
|
|
57
|
+
message: 'Client-side component identified.',
|
|
58
|
+
suggestion: 'Ensure heavy logic is moved to server components if possible to improve performance.'
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// Analyze dependencies for recommendations
|
|
63
|
+
dependencyMetrics.circular.forEach((cycle, i) => {
|
|
64
|
+
recommendations.push({
|
|
65
|
+
level: 'error',
|
|
66
|
+
target: `Cycle ${i + 1}`,
|
|
67
|
+
message: `Circular dependency detected: ${cycle.join(' -> ')}`,
|
|
68
|
+
suggestion: 'Create a shared/core module to break the dependency cycle.'
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
dependencyMetrics.zombieComponents.forEach(z => {
|
|
72
|
+
recommendations.push({
|
|
73
|
+
level: 'warning',
|
|
74
|
+
target: z,
|
|
75
|
+
message: 'Unused module (Zombie Component).',
|
|
76
|
+
suggestion: 'Safely remove this file if it is no longer needed.'
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
// Next.js Boundary Checks
|
|
80
|
+
this.checkNextJsBoundaries(componentMetrics, dependencyMetrics, recommendations);
|
|
81
|
+
// Deduplicate recommendations
|
|
82
|
+
const uniqueRecommendations = this.deduplicateRecommendations(recommendations);
|
|
83
|
+
const { score, breakdown } = this.calculateAdvancedScore(componentMetrics, dependencyMetrics);
|
|
84
|
+
return {
|
|
85
|
+
score,
|
|
86
|
+
breakdown,
|
|
87
|
+
recommendations: uniqueRecommendations
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Removes duplicate recommendations for the same target and message
|
|
92
|
+
*/
|
|
93
|
+
deduplicateRecommendations(recs) {
|
|
94
|
+
const seen = new Set();
|
|
95
|
+
return recs.filter(r => {
|
|
96
|
+
const key = `${r.level}-${r.target}-${r.message}`;
|
|
97
|
+
if (seen.has(key))
|
|
98
|
+
return false;
|
|
99
|
+
seen.add(key);
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Advanced Scoring v2 - Weighs different architectural issues
|
|
105
|
+
*/
|
|
106
|
+
calculateAdvancedScore(components, deps) {
|
|
107
|
+
let complexityPenalty = 0;
|
|
108
|
+
let couplingPenalty = 0;
|
|
109
|
+
let zombiePenalty = 0;
|
|
110
|
+
// Complexity penalties
|
|
111
|
+
components.forEach(c => {
|
|
112
|
+
if (c.isLarge)
|
|
113
|
+
complexityPenalty += 5;
|
|
114
|
+
if (c.propCount > 7)
|
|
115
|
+
complexityPenalty += 3;
|
|
116
|
+
if (c.hookCount > 6)
|
|
117
|
+
complexityPenalty += 3;
|
|
118
|
+
if (c.drilledProps.length > 0)
|
|
119
|
+
complexityPenalty += (c.drilledProps.length * 2);
|
|
120
|
+
});
|
|
121
|
+
// Coupling/Graph penalties
|
|
122
|
+
couplingPenalty += (deps.circular.length * 15);
|
|
123
|
+
// Zombie penalties
|
|
124
|
+
zombiePenalty += (deps.zombieComponents.length * 2);
|
|
125
|
+
const scores = {
|
|
126
|
+
complexity: Math.max(0, 100 - complexityPenalty),
|
|
127
|
+
coupling: Math.max(0, 100 - couplingPenalty),
|
|
128
|
+
zombies: Math.max(0, 100 - zombiePenalty),
|
|
129
|
+
};
|
|
130
|
+
const totalScore = Math.round((scores.complexity * 0.4) + (scores.coupling * 0.4) + (scores.zombies * 0.2));
|
|
131
|
+
return {
|
|
132
|
+
score: totalScore,
|
|
133
|
+
breakdown: scores
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Checks for Next.js specific boundary violations or optimizations
|
|
138
|
+
*/
|
|
139
|
+
checkNextJsBoundaries(components, deps, recommendations) {
|
|
140
|
+
const clientComponents = components.filter(c => c.isClientComponent);
|
|
141
|
+
const serverComponents = components.filter(c => !c.isClientComponent);
|
|
142
|
+
// Insight: Balance check
|
|
143
|
+
if (clientComponents.length > serverComponents.length && components.length > 5) {
|
|
144
|
+
recommendations.push({
|
|
145
|
+
level: 'info',
|
|
146
|
+
target: 'Project Structure',
|
|
147
|
+
message: 'High ratio of Client Components detected.',
|
|
148
|
+
suggestion: 'In Next.js, try to keep the majority of your components as Server Components for better performance.'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=insightEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insightEngine.js","sourceRoot":"","sources":["../../src/analyzer/insightEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview CLI Entry Point
|
|
4
|
+
* Central entry point for running the tool via command line.
|
|
5
|
+
* Sets up commands and delegates execution to specific analyzers.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Main bootstrap function to initialize CLI commands
|
|
9
|
+
*/
|
|
10
|
+
export declare function bootstrap(): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAkBH;;GAEG;AACH,wBAAsB,SAAS,kBA6H9B"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview CLI Entry Point
|
|
4
|
+
* Central entry point for running the tool via command line.
|
|
5
|
+
* Sets up commands and delegates execution to specific analyzers.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { FileScanner } from '../scanners/fileScanner.js';
|
|
10
|
+
import { ComponentAnalyzer } from '../analyzer/componentAnalyzer.js';
|
|
11
|
+
import { DependencyAnalyzer } from '../analyzer/dependencyAnalyzer.js';
|
|
12
|
+
import { TerminalReporter } from '../reporters/terminalReporter.js';
|
|
13
|
+
import { InsightEngine } from '../analyzer/insightEngine.js';
|
|
14
|
+
// MetaAuditScanner is now imported dynamically in the audit command
|
|
15
|
+
// to ensure it can be safely stripped from the production NPM build.
|
|
16
|
+
import { promises as fs } from 'fs';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
const program = new Command();
|
|
20
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
/**
|
|
22
|
+
* Main bootstrap function to initialize CLI commands
|
|
23
|
+
*/
|
|
24
|
+
export async function bootstrap() {
|
|
25
|
+
program
|
|
26
|
+
.name('react-lens')
|
|
27
|
+
.description('CLI tool for analyzing React/Next project architecture')
|
|
28
|
+
.version('1.0.0');
|
|
29
|
+
program
|
|
30
|
+
.command('analyze')
|
|
31
|
+
.description('Analyze the project architecture')
|
|
32
|
+
.argument('[path]', 'path to the project', '.')
|
|
33
|
+
.option('-j, --json [file]', 'output report in JSON format (optional path to save to file)')
|
|
34
|
+
.option('-s, --silent', 'suppress terminal report (best for piping JSON)')
|
|
35
|
+
.option('-g, --graph <file>', 'output dependency graph to file (dot/svg)')
|
|
36
|
+
.option('--fail-under <score>', 'exit with error if score is below this value')
|
|
37
|
+
.action(async (projectPath, options) => {
|
|
38
|
+
const isSilent = options.silent || (options.json === true);
|
|
39
|
+
if (!isSilent) {
|
|
40
|
+
console.log(chalk.cyan('Starting architectural analysis...'));
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
// 1. File Scanning
|
|
44
|
+
const scanner = new FileScanner();
|
|
45
|
+
const projectInfo = await scanner.scan(projectPath);
|
|
46
|
+
if (projectInfo.files.length === 0) {
|
|
47
|
+
console.log(chalk.yellow('Warning: No relevant files found in common directories.'));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// 2. Component Analysis (AST)
|
|
51
|
+
const componentAnalyzer = new ComponentAnalyzer();
|
|
52
|
+
let allComponentMetrics = [];
|
|
53
|
+
for (const file of projectInfo.files) {
|
|
54
|
+
const metrics = await componentAnalyzer.analyzeFile(file);
|
|
55
|
+
allComponentMetrics = [...allComponentMetrics, ...metrics];
|
|
56
|
+
}
|
|
57
|
+
// 3. Dependency Analysis
|
|
58
|
+
const dependencyAnalyzer = new DependencyAnalyzer();
|
|
59
|
+
const dependencyMetrics = await dependencyAnalyzer.analyze(projectInfo.rootPath, 'index.ts');
|
|
60
|
+
// 4. Intelligence Insight Engine
|
|
61
|
+
const insightEngine = new InsightEngine();
|
|
62
|
+
const insightReport = insightEngine.generateReport(allComponentMetrics, dependencyMetrics);
|
|
63
|
+
// 5. Reporting
|
|
64
|
+
if (!isSilent) {
|
|
65
|
+
const reporter = new TerminalReporter();
|
|
66
|
+
reporter.report(projectInfo, allComponentMetrics, dependencyMetrics, insightReport);
|
|
67
|
+
}
|
|
68
|
+
// 6. JSON Export / Stdout
|
|
69
|
+
if (options.json) {
|
|
70
|
+
const fullReport = {
|
|
71
|
+
project: projectInfo,
|
|
72
|
+
components: allComponentMetrics,
|
|
73
|
+
dependencies: dependencyMetrics,
|
|
74
|
+
insights: insightReport,
|
|
75
|
+
timestamp: new Date().toISOString()
|
|
76
|
+
};
|
|
77
|
+
const jsonString = JSON.stringify(fullReport, null, 2);
|
|
78
|
+
if (typeof options.json === 'string') {
|
|
79
|
+
await fs.writeFile(options.json, jsonString);
|
|
80
|
+
if (!isSilent) {
|
|
81
|
+
console.log(chalk.green(`\nReport saved to: ${options.json}`));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// Direct stdout for piping
|
|
86
|
+
console.log(jsonString);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// 7. Graph Export
|
|
90
|
+
if (options.graph) {
|
|
91
|
+
try {
|
|
92
|
+
await dependencyAnalyzer.exportGraph(options.graph);
|
|
93
|
+
console.log(chalk.green(`\nDependency graph exported to: ${options.graph}`));
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
console.warn(chalk.yellow(`\nWarning: Could not export graph. Ensure Graphviz is installed for SVG/Images.`), err);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// 8. CI/CD Guard
|
|
100
|
+
if (options.failUnder) {
|
|
101
|
+
const threshold = parseInt(options.failUnder, 10);
|
|
102
|
+
if (insightReport.score < threshold) {
|
|
103
|
+
console.error(chalk.red(`\nBuild Failed: Architecture score ${insightReport.score}% is below threshold ${threshold}%`));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error(chalk.red('\nAnalysis failed:'), error);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
program
|
|
114
|
+
.command('audit')
|
|
115
|
+
.description('Run internal security audit (Development only)')
|
|
116
|
+
.option('--self', 'run audit on react-lens itself')
|
|
117
|
+
.action(async (options) => {
|
|
118
|
+
if (options.self) {
|
|
119
|
+
try {
|
|
120
|
+
// Dynamic import allows this module to be stripped from NPM
|
|
121
|
+
// @ts-ignore
|
|
122
|
+
const { MetaAuditScanner } = await import('../audit/scanner.js');
|
|
123
|
+
const projectRoot = path.join(__dirname, '../../');
|
|
124
|
+
const scanner = new MetaAuditScanner(projectRoot);
|
|
125
|
+
const passed = await scanner.runAudit();
|
|
126
|
+
if (!passed)
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error(chalk.red('\n[ERROR] Security Audit module is only available in Development/Source mode.'));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
console.log(chalk.yellow('Please use --self to audit the tool itself.'));
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
program.parse(process.argv);
|
|
139
|
+
}
|
|
140
|
+
// Start only if file is executed directly
|
|
141
|
+
const isMain = process.argv[1] === fileURLToPath(import.meta.url) || process.argv[1]?.endsWith('index.ts');
|
|
142
|
+
if (isMain) {
|
|
143
|
+
bootstrap().catch((err) => {
|
|
144
|
+
console.error(chalk.red('Fatal Error during execution:'), err);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TerminalReporter
|
|
3
|
+
* Responsible for formatting and displaying results to the user in the CLI.
|
|
4
|
+
* Uses Chalk for aesthetic styling and informative colors.
|
|
5
|
+
*/
|
|
6
|
+
import { ProjectInfo } from '../scanners/fileScanner.js';
|
|
7
|
+
import { ComponentMetrics } from '../analyzer/componentAnalyzer.js';
|
|
8
|
+
import { DependencyMetrics } from '../analyzer/dependencyAnalyzer.js';
|
|
9
|
+
import { ArchitectureReport } from '../analyzer/insightEngine.js';
|
|
10
|
+
export declare class TerminalReporter {
|
|
11
|
+
/**
|
|
12
|
+
* Displays the final report in a formatted and attractive way
|
|
13
|
+
* @param projectInfo Basic scanning information
|
|
14
|
+
* @param componentsMetrics Component analysis results
|
|
15
|
+
* @param dependencyMetrics Dependency analysis results
|
|
16
|
+
* @param insightReport Advanced insights and scoring
|
|
17
|
+
*/
|
|
18
|
+
report(projectInfo: ProjectInfo, componentsMetrics: ComponentMetrics[], dependencyMetrics: DependencyMetrics, insightReport: ArchitectureReport): void;
|
|
19
|
+
private getLevelColor;
|
|
20
|
+
/**
|
|
21
|
+
* Determines color based on score value
|
|
22
|
+
* @param score Numerical score from 0-100
|
|
23
|
+
*/
|
|
24
|
+
private getScoreColor;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=terminalReporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminalReporter.d.ts","sourceRoot":"","sources":["../../src/reporters/terminalReporter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAkB,MAAM,8BAA8B,CAAC;AAElF,qBAAa,gBAAgB;IAC3B;;;;;;OAMG;IACH,MAAM,CACJ,WAAW,EAAE,WAAW,EACxB,iBAAiB,EAAE,gBAAgB,EAAE,EACrC,iBAAiB,EAAE,iBAAiB,EACpC,aAAa,EAAE,kBAAkB;IA4DnC,OAAO,CAAC,aAAa;IAMrB;;;OAGG;IACH,OAAO,CAAC,aAAa;CAKtB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TerminalReporter
|
|
3
|
+
* Responsible for formatting and displaying results to the user in the CLI.
|
|
4
|
+
* Uses Chalk for aesthetic styling and informative colors.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
export class TerminalReporter {
|
|
8
|
+
/**
|
|
9
|
+
* Displays the final report in a formatted and attractive way
|
|
10
|
+
* @param projectInfo Basic scanning information
|
|
11
|
+
* @param componentsMetrics Component analysis results
|
|
12
|
+
* @param dependencyMetrics Dependency analysis results
|
|
13
|
+
* @param insightReport Advanced insights and scoring
|
|
14
|
+
*/
|
|
15
|
+
report(projectInfo, componentsMetrics, dependencyMetrics, insightReport) {
|
|
16
|
+
const border = '━'.repeat(45);
|
|
17
|
+
const subBorder = '─'.repeat(45);
|
|
18
|
+
console.log('\n' + chalk.cyan(border));
|
|
19
|
+
console.log(chalk.cyan.bold(' ReactLens Architecture Report'));
|
|
20
|
+
console.log(chalk.cyan(border) + '\n');
|
|
21
|
+
// Stats
|
|
22
|
+
const typeLabel = projectInfo.type === 'node-cli' ? 'Node CLI Tool' : projectInfo.type.toUpperCase();
|
|
23
|
+
console.log(chalk.bold('General Statistics'));
|
|
24
|
+
console.log(`${chalk.dim('• Project Type:')} ${chalk.green(typeLabel)}`);
|
|
25
|
+
console.log(`${chalk.dim('• Modules:')} ${chalk.blue(dependencyMetrics.internalModules.length)} internal / ${chalk.blue(dependencyMetrics.externalModules.length)} external`);
|
|
26
|
+
console.log(`${chalk.dim('• Components:')} ${chalk.blue(componentsMetrics.length)}`);
|
|
27
|
+
// Score Breakdown
|
|
28
|
+
console.log('\n' + chalk.bold('Health Breakdown'));
|
|
29
|
+
console.log(`${chalk.dim('• Complexity:')} ${this.getScoreColor(insightReport.breakdown.complexity)(insightReport.breakdown.complexity + '%')}`);
|
|
30
|
+
console.log(`${chalk.dim('• Coupling:')} ${this.getScoreColor(insightReport.breakdown.coupling)(insightReport.breakdown.coupling + '%')}`);
|
|
31
|
+
console.log(`${chalk.dim('• Zombies:')} ${this.getScoreColor(insightReport.breakdown.zombies)(insightReport.breakdown.zombies + '%')}`);
|
|
32
|
+
console.log(chalk.dim(subBorder));
|
|
33
|
+
// Insights
|
|
34
|
+
if (insightReport.recommendations.length > 0) {
|
|
35
|
+
console.log('\n' + chalk.bold('Actionable Insights'));
|
|
36
|
+
const sortedRecs = [...insightReport.recommendations].sort((a, b) => {
|
|
37
|
+
const order = { error: 0, warning: 1, info: 2 };
|
|
38
|
+
return order[a.level] - order[b.level];
|
|
39
|
+
});
|
|
40
|
+
sortedRecs.forEach(rec => {
|
|
41
|
+
let label = '[INFO]';
|
|
42
|
+
let color = chalk.blue;
|
|
43
|
+
if (rec.level === 'error') {
|
|
44
|
+
label = '[ERROR]';
|
|
45
|
+
color = chalk.red.bold;
|
|
46
|
+
}
|
|
47
|
+
else if (rec.level === 'warning') {
|
|
48
|
+
label = '[WARN]';
|
|
49
|
+
color = chalk.yellow.bold;
|
|
50
|
+
}
|
|
51
|
+
console.log(`\n${color(label)} ${chalk.bold(rec.target)}`);
|
|
52
|
+
console.log(` ${chalk.white(rec.message)}`);
|
|
53
|
+
console.log(` ${chalk.dim('↳')} ${chalk.green(rec.suggestion)}`);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log('\n' + chalk.green('[OK] Architecture is healthy. No issues found.'));
|
|
58
|
+
}
|
|
59
|
+
// Final Score
|
|
60
|
+
console.log('\n' + chalk.dim(subBorder));
|
|
61
|
+
const scoreColor = this.getScoreColor(insightReport.score);
|
|
62
|
+
console.log(`${chalk.bold('Overall Architecture Score:')} ${scoreColor.bold(insightReport.score + '%')}`);
|
|
63
|
+
console.log(chalk.cyan(border) + '\n');
|
|
64
|
+
}
|
|
65
|
+
getLevelColor(level) {
|
|
66
|
+
if (level === 'error')
|
|
67
|
+
return chalk.red;
|
|
68
|
+
if (level === 'warning')
|
|
69
|
+
return chalk.yellow;
|
|
70
|
+
return chalk.cyan;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Determines color based on score value
|
|
74
|
+
* @param score Numerical score from 0-100
|
|
75
|
+
*/
|
|
76
|
+
getScoreColor(score) {
|
|
77
|
+
if (score > 80)
|
|
78
|
+
return chalk.green;
|
|
79
|
+
if (score > 50)
|
|
80
|
+
return chalk.yellow;
|
|
81
|
+
return chalk.red;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=terminalReporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminalReporter.js","sourceRoot":"","sources":["../../src/reporters/terminalReporter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,MAAM,OAAO,gBAAgB;IAC3B;;;;;;OAMG;IACH,MAAM,CACJ,WAAwB,EACxB,iBAAqC,EACrC,iBAAoC,EACpC,aAAiC;QAEjC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEjC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC,CAAC;QAChE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;QAEvC,QAAQ;QACR,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACrG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACzE,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,MAAM,CAAC,eAAe,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACnL,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEvF,kBAAkB;QAClB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QACjJ,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7I,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QAE3I,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QAElC,WAAW;QACX,IAAI,aAAa,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAEtD,MAAM,UAAU,GAAG,CAAC,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBAClE,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;gBAChD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;YAEH,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;gBACvB,IAAI,KAAK,GAAG,QAAQ,CAAC;gBACrB,IAAI,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC;gBAEvB,IAAI,GAAG,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;oBAC1B,KAAK,GAAG,SAAS,CAAC;oBAClB,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;gBACzB,CAAC;qBAAM,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBACnC,KAAK,GAAG,QAAQ,CAAC;oBACjB,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;gBAC5B,CAAC;gBAED,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC3D,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC7C,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACpE,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC,CAAC;QACpF,CAAC;QAED,cAAc;QACd,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3D,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1G,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACzC,CAAC;IAEO,aAAa,CAAC,KAA8B;QAClD,IAAI,KAAK,KAAK,OAAO;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC;QACxC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC,MAAM,CAAC;QAC7C,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED;;;OAGG;IACK,aAAa,CAAC,KAAa;QACjC,IAAI,KAAK,GAAG,EAAE;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC;QACnC,IAAI,KAAK,GAAG,EAAE;YAAE,OAAO,KAAK,CAAC,MAAM,CAAC;QACpC,OAAO,KAAK,CAAC,GAAG,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview FileScanner
|
|
3
|
+
* Responsible for discovering project files and identifying the framework (React, Next, Vue).
|
|
4
|
+
* Supports automatic detection of JavaScript and TypeScript files.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Represents discovered project information
|
|
8
|
+
*/
|
|
9
|
+
export interface ProjectInfo {
|
|
10
|
+
type: 'react' | 'next' | 'vue' | 'node-cli' | 'unknown';
|
|
11
|
+
rootPath: string;
|
|
12
|
+
files: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare class FileScanner {
|
|
15
|
+
/**
|
|
16
|
+
* Starts project scanning from a given path
|
|
17
|
+
* @param targetPath The path where search begins
|
|
18
|
+
*/
|
|
19
|
+
scan(targetPath: string): Promise<ProjectInfo>;
|
|
20
|
+
/**
|
|
21
|
+
* Scans package.json to determine the framework used
|
|
22
|
+
* @param rootPath Project root path
|
|
23
|
+
*/
|
|
24
|
+
private detectProjectType;
|
|
25
|
+
/**
|
|
26
|
+
* Uses fast-glob to search for supported file extensions (js, ts, jsx, tsx)
|
|
27
|
+
* @param rootPath Search root path
|
|
28
|
+
*/
|
|
29
|
+
private discoverFiles;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=fileScanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileScanner.d.ts","sourceRoot":"","sources":["../../src/scanners/fileScanner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,qBAAa,WAAW;IACtB;;;OAGG;IACG,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAYpD;;;OAGG;YACW,iBAAiB;IAqB/B;;;OAGG;YACW,aAAa;CA0B5B"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview FileScanner
|
|
3
|
+
* Responsible for discovering project files and identifying the framework (React, Next, Vue).
|
|
4
|
+
* Supports automatic detection of JavaScript and TypeScript files.
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fg from 'fast-glob';
|
|
9
|
+
export class FileScanner {
|
|
10
|
+
/**
|
|
11
|
+
* Starts project scanning from a given path
|
|
12
|
+
* @param targetPath The path where search begins
|
|
13
|
+
*/
|
|
14
|
+
async scan(targetPath) {
|
|
15
|
+
const rootPath = path.resolve(targetPath);
|
|
16
|
+
const projectType = await this.detectProjectType(rootPath);
|
|
17
|
+
const files = await this.discoverFiles(rootPath);
|
|
18
|
+
return {
|
|
19
|
+
type: projectType,
|
|
20
|
+
rootPath,
|
|
21
|
+
files
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Scans package.json to determine the framework used
|
|
26
|
+
* @param rootPath Project root path
|
|
27
|
+
*/
|
|
28
|
+
async detectProjectType(rootPath) {
|
|
29
|
+
try {
|
|
30
|
+
const packageJsonPath = path.join(rootPath, 'package.json');
|
|
31
|
+
const content = await fs.readFile(packageJsonPath, 'utf-8');
|
|
32
|
+
const pkg = JSON.parse(content);
|
|
33
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
34
|
+
if (deps['next'])
|
|
35
|
+
return 'next';
|
|
36
|
+
if (deps['vue'])
|
|
37
|
+
return 'vue';
|
|
38
|
+
if (deps['react'])
|
|
39
|
+
return 'react';
|
|
40
|
+
// Detection of CLI Tools
|
|
41
|
+
if (pkg.bin)
|
|
42
|
+
return 'node-cli';
|
|
43
|
+
return 'unknown';
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return 'unknown';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Uses fast-glob to search for supported file extensions (js, ts, jsx, tsx)
|
|
51
|
+
* @param rootPath Search root path
|
|
52
|
+
*/
|
|
53
|
+
async discoverFiles(rootPath) {
|
|
54
|
+
const patterns = [
|
|
55
|
+
'src/**/*.{js,jsx,ts,tsx}',
|
|
56
|
+
'app/**/*.{js,jsx,ts,tsx}', // Support for Next.js App Router
|
|
57
|
+
'components/**/*.{js,jsx,ts,tsx}',
|
|
58
|
+
'pages/**/*.{js,jsx,ts,tsx}'
|
|
59
|
+
];
|
|
60
|
+
const files = await fg(patterns, {
|
|
61
|
+
cwd: rootPath,
|
|
62
|
+
absolute: true,
|
|
63
|
+
ignore: [
|
|
64
|
+
'**/node_modules/**',
|
|
65
|
+
'**/dist/**',
|
|
66
|
+
'**/.next/**',
|
|
67
|
+
'**/__tests__/**',
|
|
68
|
+
'**/*.test.{js,ts,jsx,tsx}',
|
|
69
|
+
'**/*.spec.{js,ts,jsx,tsx}',
|
|
70
|
+
'vitest.config.{js,ts,mjs}',
|
|
71
|
+
'jest.config.{js,ts}',
|
|
72
|
+
'tsconfig.json'
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
return files;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=fileScanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileScanner.js","sourceRoot":"","sources":["../../src/scanners/fileScanner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,WAAW,CAAC;AAW3B,MAAM,OAAO,WAAW;IACtB;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,UAAkB;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAEjD,OAAO;YACL,IAAI,EAAE,WAAW;YACjB,QAAQ;YACR,KAAK;SACN,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,iBAAiB,CAAC,QAAgB;QAC9C,IAAI,CAAC;YACH,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;YAC5D,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;YAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAEhC,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;YAE7D,IAAI,IAAI,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAC;YAChC,IAAI,IAAI,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC9B,IAAI,IAAI,CAAC,OAAO,CAAC;gBAAE,OAAO,OAAO,CAAC;YAElC,yBAAyB;YACzB,IAAI,GAAG,CAAC,GAAG;gBAAE,OAAO,UAAU,CAAC;YAE/B,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,aAAa,CAAC,QAAgB;QAC1C,MAAM,QAAQ,GAAG;YACf,0BAA0B;YAC1B,0BAA0B,EAAE,iCAAiC;YAC7D,iCAAiC;YACjC,4BAA4B;SAC7B,CAAC;QAEF,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC/B,GAAG,EAAE,QAAQ;YACb,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE;gBACN,oBAAoB;gBACpB,YAAY;gBACZ,aAAa;gBACb,iBAAiB;gBACjB,2BAA2B;gBAC3B,2BAA2B;gBAC3B,2BAA2B;gBAC3B,qBAAqB;gBACrB,eAAe;aAChB;SACF,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mohamed_fadl/reactlens",
|
|
3
|
+
"version": "1.2.0-beta.1",
|
|
4
|
+
"description": "Intelligent architectural analysis tool for React and Next.js applications.",
|
|
5
|
+
"main": "./dist/cli/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"reactlens": "./dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"react",
|
|
11
|
+
"nextjs",
|
|
12
|
+
"architecture",
|
|
13
|
+
"analysis",
|
|
14
|
+
"dependencies",
|
|
15
|
+
"circular-dependencies",
|
|
16
|
+
"zombie-components",
|
|
17
|
+
"refactoring"
|
|
18
|
+
],
|
|
19
|
+
"author": "ReactLens Team",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/react-lens/reactlens.git"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/react-lens/reactlens/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/react-lens/reactlens#readme",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "tsx src/cli/index.ts analyze",
|
|
32
|
+
"build": "tsc",
|
|
33
|
+
"audit:self": "tsx src/cli/index.ts audit --self",
|
|
34
|
+
"prepublishOnly": "npm run audit:self && npm run build && npm test",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"coverage": "vitest run --coverage"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/babel__traverse": "^7.28.0",
|
|
41
|
+
"@types/madge": "^5.0.3",
|
|
42
|
+
"@types/node": "^25.5.0",
|
|
43
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
44
|
+
"eslint-plugin-security": "^4.0.0",
|
|
45
|
+
"tsx": "^4.21.0",
|
|
46
|
+
"typescript": "^5.9.3",
|
|
47
|
+
"vitest": "^4.1.0"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@babel/parser": "^7.29.0",
|
|
51
|
+
"@babel/traverse": "^7.29.0",
|
|
52
|
+
"chalk": "^5.6.2",
|
|
53
|
+
"commander": "^14.0.3",
|
|
54
|
+
"fast-glob": "^3.3.3",
|
|
55
|
+
"madge": "^8.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|