react-code-smell-detector 1.4.1 → 1.4.2
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 +140 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +24 -0
- package/dist/bundleAnalyzer.d.ts +25 -0
- package/dist/bundleAnalyzer.d.ts.map +1 -0
- package/dist/bundleAnalyzer.js +375 -0
- package/dist/cli.js +42 -0
- package/dist/customRules.d.ts +31 -0
- package/dist/customRules.d.ts.map +1 -0
- package/dist/customRules.js +289 -0
- package/dist/detectors/complexity.d.ts +0 -4
- package/dist/detectors/complexity.d.ts.map +1 -1
- package/dist/detectors/complexity.js +1 -1
- package/dist/detectors/deadCode.d.ts +0 -7
- package/dist/detectors/deadCode.d.ts.map +1 -1
- package/dist/detectors/deadCode.js +0 -24
- package/dist/detectors/index.d.ts +2 -2
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +2 -2
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +0 -7
- package/dist/graphGenerator.d.ts +34 -0
- package/dist/graphGenerator.d.ts.map +1 -0
- package/dist/graphGenerator.js +320 -0
- package/dist/reporter.js +2 -0
- package/dist/types/index.d.ts +6 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +8 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,6 +11,8 @@ import { startWatch } from './watcher.js';
|
|
|
11
11
|
import { getAllModifiedFiles, filterReactFiles, getGitInfo } from './git.js';
|
|
12
12
|
import { initializeBaseline, recordBaseline, formatTrendReport } from './baseline.js';
|
|
13
13
|
import { sendWebhookNotification, getWebhookConfig } from './webhooks.js';
|
|
14
|
+
import { generateDependencyGraphHTML } from './graphGenerator.js';
|
|
15
|
+
import { generateBundleReport } from './bundleAnalyzer.js';
|
|
14
16
|
import fs from 'fs/promises';
|
|
15
17
|
const program = new Command();
|
|
16
18
|
program
|
|
@@ -37,6 +39,10 @@ program
|
|
|
37
39
|
.option('--discord <url>', 'Discord webhook URL for notifications')
|
|
38
40
|
.option('--webhook <url>', 'Generic webhook URL for notifications')
|
|
39
41
|
.option('--webhook-threshold <number>', 'Only notify if smells exceed this threshold', parseInt)
|
|
42
|
+
.option('--graph', 'Generate dependency graph visualization', false)
|
|
43
|
+
.option('--graph-format <format>', 'Graph output format: svg, html', 'html')
|
|
44
|
+
.option('--bundle', 'Analyze bundle size impact per component', false)
|
|
45
|
+
.option('--rules <file>', 'Custom rules configuration file')
|
|
40
46
|
.action(async (directory, options) => {
|
|
41
47
|
const rootDir = path.resolve(process.cwd(), directory);
|
|
42
48
|
// Check if directory exists
|
|
@@ -60,6 +66,18 @@ program
|
|
|
60
66
|
process.exit(1);
|
|
61
67
|
}
|
|
62
68
|
}
|
|
69
|
+
// Load custom rules if specified
|
|
70
|
+
let customRules;
|
|
71
|
+
if (options.rules) {
|
|
72
|
+
try {
|
|
73
|
+
const rulesPath = path.resolve(process.cwd(), options.rules);
|
|
74
|
+
const rulesContent = await fs.readFile(rulesPath, 'utf-8');
|
|
75
|
+
customRules = JSON.parse(rulesContent);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.warn(chalk.yellow(`Warning: Could not load custom rules: ${error.message}`));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
63
81
|
// Build config from options
|
|
64
82
|
const config = {
|
|
65
83
|
...DEFAULT_CONFIG,
|
|
@@ -67,6 +85,10 @@ program
|
|
|
67
85
|
...(options.maxEffects && { maxUseEffectsPerComponent: options.maxEffects }),
|
|
68
86
|
...(options.maxProps && { maxPropsCount: options.maxProps }),
|
|
69
87
|
...(options.maxLines && { maxComponentLines: options.maxLines }),
|
|
88
|
+
...(options.graph && { generateDependencyGraph: true }),
|
|
89
|
+
...(options.graphFormat && { graphOutputFormat: options.graphFormat }),
|
|
90
|
+
...(options.bundle && { analyzeBundleSize: true }),
|
|
91
|
+
...(customRules && { customRules }),
|
|
70
92
|
};
|
|
71
93
|
const include = options.include?.split(',').map((p) => p.trim()) || ['**/*.tsx', '**/*.jsx'];
|
|
72
94
|
const exclude = options.exclude?.split(',').map((p) => p.trim()) || ['**/node_modules/**', '**/dist/**'];
|
|
@@ -188,6 +210,26 @@ program
|
|
|
188
210
|
console.log(chalk.green('✓ Notification sent to webhook'));
|
|
189
211
|
}
|
|
190
212
|
}
|
|
213
|
+
// Generate dependency graph if requested
|
|
214
|
+
if (options.graph) {
|
|
215
|
+
const graph = global._dependencyGraph;
|
|
216
|
+
if (graph) {
|
|
217
|
+
const graphHTML = generateDependencyGraphHTML(graph, path.basename(rootDir), graph.circularDependencies.length);
|
|
218
|
+
const graphPath = path.resolve(process.cwd(), 'dependency-graph.html');
|
|
219
|
+
await fs.writeFile(graphPath, graphHTML, 'utf-8');
|
|
220
|
+
console.log(chalk.green(`✓ Dependency graph written to ${graphPath}`));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Generate bundle analysis if requested
|
|
224
|
+
if (options.bundle) {
|
|
225
|
+
const bundleAnalysis = global._bundleAnalysis;
|
|
226
|
+
if (bundleAnalysis) {
|
|
227
|
+
const bundleHTML = generateBundleReport(bundleAnalysis, path.basename(rootDir));
|
|
228
|
+
const bundlePath = path.resolve(process.cwd(), 'bundle-analysis.html');
|
|
229
|
+
await fs.writeFile(bundlePath, bundleHTML, 'utf-8');
|
|
230
|
+
console.log(chalk.green(`✓ Bundle analysis written to ${bundlePath}`));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
191
233
|
// CI/CD exit code handling
|
|
192
234
|
const { smellsBySeverity } = result.summary;
|
|
193
235
|
let shouldFail = false;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CodeSmell } from './types/index.js';
|
|
2
|
+
export interface CustomRule {
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
severity: 'error' | 'warning' | 'info';
|
|
6
|
+
pattern: string | RegExp;
|
|
7
|
+
patternType: 'regex' | 'ast' | 'text';
|
|
8
|
+
message?: string;
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface CustomRulesConfig {
|
|
12
|
+
rules: CustomRule[];
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse custom rules from configuration
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseCustomRules(config: any): CustomRule[];
|
|
19
|
+
/**
|
|
20
|
+
* Detect violations of custom rules
|
|
21
|
+
*/
|
|
22
|
+
export declare function detectCustomRuleViolations(component: any, filePath: string, sourceCode: string, customRules: CustomRule[]): CodeSmell[];
|
|
23
|
+
/**
|
|
24
|
+
* Example custom rules configuration
|
|
25
|
+
*/
|
|
26
|
+
export declare const EXAMPLE_CUSTOM_RULES: CustomRulesConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Generate custom rules documentation
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateRulesDocumentation(): string;
|
|
31
|
+
//# sourceMappingURL=customRules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customRules.d.ts","sourceRoot":"","sources":["../src/customRules.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAK7C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,GAAG,UAAU,EAAE,CAY1D;AAsBD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,UAAU,EAAE,GACxB,SAAS,EAAE,CA2Bb;AAuDD;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,iBAoClC,CAAC;AAEF;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CA8InD"}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import _traverse from '@babel/traverse';
|
|
2
|
+
const traverse = typeof _traverse === 'function' ? _traverse : _traverse.default;
|
|
3
|
+
/**
|
|
4
|
+
* Parse custom rules from configuration
|
|
5
|
+
*/
|
|
6
|
+
export function parseCustomRules(config) {
|
|
7
|
+
if (!config.customRules || typeof config.customRules !== 'object') {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const rulesArray = Array.isArray(config.customRules)
|
|
11
|
+
? config.customRules
|
|
12
|
+
: [config.customRules];
|
|
13
|
+
return rulesArray
|
|
14
|
+
.map((rule) => normalizeRule(rule))
|
|
15
|
+
.filter((rule) => rule !== null);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Normalize and validate a custom rule
|
|
19
|
+
*/
|
|
20
|
+
function normalizeRule(rule) {
|
|
21
|
+
if (!rule.name || !rule.pattern) {
|
|
22
|
+
console.warn('Invalid custom rule: missing name or pattern');
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
name: rule.name,
|
|
27
|
+
description: rule.description || '',
|
|
28
|
+
severity: rule.severity || 'warning',
|
|
29
|
+
pattern: rule.pattern,
|
|
30
|
+
patternType: rule.patternType || 'text',
|
|
31
|
+
message: rule.message || `Custom rule violation: ${rule.name}`,
|
|
32
|
+
enabled: rule.enabled !== false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Detect violations of custom rules
|
|
37
|
+
*/
|
|
38
|
+
export function detectCustomRuleViolations(component, filePath, sourceCode, customRules) {
|
|
39
|
+
const smells = [];
|
|
40
|
+
for (const rule of customRules) {
|
|
41
|
+
if (!rule.enabled)
|
|
42
|
+
continue;
|
|
43
|
+
const violations = detectRuleViolation(component, sourceCode, rule);
|
|
44
|
+
violations.forEach(violation => {
|
|
45
|
+
smells.push({
|
|
46
|
+
type: 'custom-rule',
|
|
47
|
+
severity: rule.severity,
|
|
48
|
+
message: violation.message,
|
|
49
|
+
file: filePath,
|
|
50
|
+
line: violation.line,
|
|
51
|
+
column: violation.column,
|
|
52
|
+
suggestion: `Follow custom rule: ${rule.name}. ${rule.description || ''}`,
|
|
53
|
+
codeSnippet: violation.snippet,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return smells;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Detect a single rule violation
|
|
61
|
+
*/
|
|
62
|
+
function detectRuleViolation(component, sourceCode, rule) {
|
|
63
|
+
const violations = [];
|
|
64
|
+
if (rule.patternType === 'regex' || rule.patternType === 'text') {
|
|
65
|
+
// Text/regex matching
|
|
66
|
+
const pattern = new RegExp(rule.pattern, 'gm');
|
|
67
|
+
const lines = sourceCode.split('\n');
|
|
68
|
+
for (let i = 0; i < lines.length; i++) {
|
|
69
|
+
const line = lines[i];
|
|
70
|
+
let match;
|
|
71
|
+
if (typeof rule.pattern === 'string') {
|
|
72
|
+
pattern.lastIndex = 0;
|
|
73
|
+
match = pattern.exec(line);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
match = rule.pattern.exec(line);
|
|
77
|
+
}
|
|
78
|
+
if (match) {
|
|
79
|
+
violations.push({
|
|
80
|
+
line: i + 1,
|
|
81
|
+
column: match.index,
|
|
82
|
+
message: rule.message || `Matches custom pattern: ${rule.pattern}`,
|
|
83
|
+
snippet: line,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (rule.patternType === 'ast') {
|
|
89
|
+
// AST-based matching
|
|
90
|
+
const patternStr = typeof rule.pattern === 'string' ? rule.pattern : String(rule.pattern);
|
|
91
|
+
const handlers = {};
|
|
92
|
+
handlers[patternStr] = (path) => {
|
|
93
|
+
violations.push({
|
|
94
|
+
line: path.node.loc?.start.line || 1,
|
|
95
|
+
column: path.node.loc?.start.column || 0,
|
|
96
|
+
message: rule.message || `Violates AST rule: ${patternStr}`,
|
|
97
|
+
snippet: sourceCode.split('\n')[path.node.loc?.start.line - 1] || '',
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
component.path.traverse(handlers);
|
|
101
|
+
}
|
|
102
|
+
return violations;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Example custom rules configuration
|
|
106
|
+
*/
|
|
107
|
+
export const EXAMPLE_CUSTOM_RULES = {
|
|
108
|
+
enabled: true,
|
|
109
|
+
rules: [
|
|
110
|
+
{
|
|
111
|
+
name: 'no-hardcoded-strings',
|
|
112
|
+
description: 'Flags hardcoded strings in components (should use i18n)',
|
|
113
|
+
severity: 'warning',
|
|
114
|
+
pattern: '"(hello|world|test|demo|todo)"',
|
|
115
|
+
patternType: 'regex',
|
|
116
|
+
enabled: false,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'no-px-margins',
|
|
120
|
+
description: 'Disallow px units for margins (use rem or em)',
|
|
121
|
+
severity: 'info',
|
|
122
|
+
pattern: 'margin[^:]*:\\s*\\d+px',
|
|
123
|
+
patternType: 'regex',
|
|
124
|
+
enabled: false,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'max-inline-styles',
|
|
128
|
+
description: 'Disallow multiple inline style props in JSX',
|
|
129
|
+
severity: 'warning',
|
|
130
|
+
pattern: 'style={{[^}]*[,;][^}]*}}.*style={{',
|
|
131
|
+
patternType: 'regex',
|
|
132
|
+
enabled: false,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'require-display-name',
|
|
136
|
+
description: 'All components must have a display name',
|
|
137
|
+
severity: 'info',
|
|
138
|
+
pattern: 'displayName',
|
|
139
|
+
patternType: 'text',
|
|
140
|
+
enabled: false,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Generate custom rules documentation
|
|
146
|
+
*/
|
|
147
|
+
export function generateRulesDocumentation() {
|
|
148
|
+
return `# Custom Rules Configuration
|
|
149
|
+
|
|
150
|
+
## Overview
|
|
151
|
+
Custom rules allow you to define project-specific code quality standards. Add custom rules to your \`.smellrc.json\` configuration.
|
|
152
|
+
|
|
153
|
+
## Configuration
|
|
154
|
+
|
|
155
|
+
### Basic Example
|
|
156
|
+
|
|
157
|
+
\`\`\`json
|
|
158
|
+
{
|
|
159
|
+
"customRules": [
|
|
160
|
+
{
|
|
161
|
+
"name": "no-console-logs",
|
|
162
|
+
"description": "Disallow console.log in production code",
|
|
163
|
+
"severity": "warning",
|
|
164
|
+
"pattern": "console\\\\.(log|debug|info)",
|
|
165
|
+
"patternType": "regex",
|
|
166
|
+
"enabled": true
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
\`\`\`
|
|
171
|
+
|
|
172
|
+
## Rule Properties
|
|
173
|
+
|
|
174
|
+
| Property | Type | Required | Default | Description |
|
|
175
|
+
|----------|------|----------|---------|-------------|
|
|
176
|
+
| name | string | ✓ | - | Unique rule identifier |
|
|
177
|
+
| description | string | | "" | Human-readable description |
|
|
178
|
+
| severity | string | | "warning" | One of: error, warning, info |
|
|
179
|
+
| pattern | string | ✓ | - | Regex pattern or AST node type |
|
|
180
|
+
| patternType | string | | "text" | One of: regex, text, ast |
|
|
181
|
+
| message | string | | Rule pattern | Custom error message |
|
|
182
|
+
| enabled | boolean | | true | Enable/disable rule |
|
|
183
|
+
|
|
184
|
+
## Pattern Types
|
|
185
|
+
|
|
186
|
+
### 1. Regex Pattern
|
|
187
|
+
Match against source code using regular expressions.
|
|
188
|
+
|
|
189
|
+
\`\`\`json
|
|
190
|
+
{
|
|
191
|
+
"name": "no-hardcoded-urls",
|
|
192
|
+
"pattern": "https?://[^\\\"']+",
|
|
193
|
+
"patternType": "regex",
|
|
194
|
+
"severity": "warning"
|
|
195
|
+
}
|
|
196
|
+
\`\`\`
|
|
197
|
+
|
|
198
|
+
### 2. Text Pattern
|
|
199
|
+
Simple string matching (case-sensitive).
|
|
200
|
+
|
|
201
|
+
\`\`\`json
|
|
202
|
+
{
|
|
203
|
+
"name": "no-debugger",
|
|
204
|
+
"pattern": "debugger",
|
|
205
|
+
"patternType": "text",
|
|
206
|
+
"severity": "error"
|
|
207
|
+
}
|
|
208
|
+
\`\`\`
|
|
209
|
+
|
|
210
|
+
### 3. AST Pattern
|
|
211
|
+
Match against Babel AST node types.
|
|
212
|
+
|
|
213
|
+
\`\`\`json
|
|
214
|
+
{
|
|
215
|
+
"name": "no-nested-functions",
|
|
216
|
+
"pattern": "FunctionExpression",
|
|
217
|
+
"patternType": "ast",
|
|
218
|
+
"severity": "info"
|
|
219
|
+
}
|
|
220
|
+
\`\`\`
|
|
221
|
+
|
|
222
|
+
## Real-World Examples
|
|
223
|
+
|
|
224
|
+
### Prevent hardcoded strings for i18n
|
|
225
|
+
\`\`\`json
|
|
226
|
+
{
|
|
227
|
+
"name": "require-i18n",
|
|
228
|
+
"description": "All strings must be internationalized",
|
|
229
|
+
"pattern": "['\\\"]([A-Z][a-z]+\\\\s*){2,}['\\\"]",
|
|
230
|
+
"patternType": "regex",
|
|
231
|
+
"severity": "warning",
|
|
232
|
+
"message": "Hardcoded text found. Use i18n instead."
|
|
233
|
+
}
|
|
234
|
+
\`\`\`
|
|
235
|
+
|
|
236
|
+
### Enforce accessibility attributes
|
|
237
|
+
\`\`\`json
|
|
238
|
+
{
|
|
239
|
+
"name": "missing-aria-label",
|
|
240
|
+
"description": "Interactive elements need aria-label",
|
|
241
|
+
"pattern": "<(button|input|select)(?!.*aria-label)",
|
|
242
|
+
"patternType": "regex",
|
|
243
|
+
"severity": "error"
|
|
244
|
+
}
|
|
245
|
+
\`\`\`
|
|
246
|
+
|
|
247
|
+
### No logger calls without context
|
|
248
|
+
\`\`\`json
|
|
249
|
+
{
|
|
250
|
+
"name": "logger-needs-context",
|
|
251
|
+
"description": "Logger calls must include context",
|
|
252
|
+
"pattern": "logger\\\\.(log|warn)\\\\(['\\\"](?!\\\\[)",
|
|
253
|
+
"patternType": "regex",
|
|
254
|
+
"severity": "warning",
|
|
255
|
+
"message": "Logger call missing context. Use: logger.log('[Component]', ...)"
|
|
256
|
+
}
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
### Maximum nesting depth for JSX
|
|
260
|
+
\`\`\`json
|
|
261
|
+
{
|
|
262
|
+
"name": "jsx-nesting-limit",
|
|
263
|
+
"description": "Limit JSX nesting to improve readability",
|
|
264
|
+
"pattern": "<.*>.*<.*>.*<.*>.*<.*>.*<",
|
|
265
|
+
"patternType": "regex",
|
|
266
|
+
"severity": "info"
|
|
267
|
+
}
|
|
268
|
+
\`\`\`
|
|
269
|
+
|
|
270
|
+
## Using Custom Rules in CLI
|
|
271
|
+
|
|
272
|
+
\`\`\`bash
|
|
273
|
+
# Custom rules are automatically applied with config file
|
|
274
|
+
react-smell ./src -c .smellrc.json
|
|
275
|
+
|
|
276
|
+
# Or create default config with examples
|
|
277
|
+
react-smell init
|
|
278
|
+
\`\`\`
|
|
279
|
+
|
|
280
|
+
## Tips
|
|
281
|
+
|
|
282
|
+
1. **Start simple** - Begin with basic regex patterns
|
|
283
|
+
2. **Test patterns** - Use online regex testers before adding to config
|
|
284
|
+
3. **Use specific patterns** - Avoid overly broad patterns that match unintended code
|
|
285
|
+
4. **Document rules** - Always include clear descriptions
|
|
286
|
+
5. **Set appropriate severity** - Use 'error' for critical rules, 'info' for suggestions
|
|
287
|
+
6. **Enable progressively** - Start with disabled rules, enable after team agreement
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
@@ -10,8 +10,4 @@ export interface ComplexityMetrics {
|
|
|
10
10
|
* Detect code complexity issues in a component
|
|
11
11
|
*/
|
|
12
12
|
export declare function detectComplexity(component: ParsedComponent, filePath: string, sourceCode: string, config: DetectorConfig): CodeSmell[];
|
|
13
|
-
/**
|
|
14
|
-
* Calculate complexity metrics for a component
|
|
15
|
-
*/
|
|
16
|
-
export declare function calculateComplexityMetrics(component: ParsedComponent): ComplexityMetrics;
|
|
17
13
|
//# sourceMappingURL=complexity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"complexity.d.ts","sourceRoot":"","sources":["../../src/detectors/complexity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GACrB,SAAS,EAAE,CAqCb
|
|
1
|
+
{"version":3,"file":"complexity.d.ts","sourceRoot":"","sources":["../../src/detectors/complexity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GACrB,SAAS,EAAE,CAqCb"}
|
|
@@ -38,7 +38,7 @@ export function detectComplexity(component, filePath, sourceCode, config) {
|
|
|
38
38
|
/**
|
|
39
39
|
* Calculate complexity metrics for a component
|
|
40
40
|
*/
|
|
41
|
-
|
|
41
|
+
function calculateComplexityMetrics(component) {
|
|
42
42
|
let cyclomaticComplexity = 1;
|
|
43
43
|
let cognitiveComplexity = 0;
|
|
44
44
|
component.path.traverse({
|
|
@@ -4,11 +4,4 @@ import { CodeSmell, DetectorConfig } from '../types/index.js';
|
|
|
4
4
|
* Detects potentially dead code: unused variables, imports, and functions
|
|
5
5
|
*/
|
|
6
6
|
export declare function detectDeadCode(component: ParsedComponent, filePath: string, sourceCode: string, config?: DetectorConfig): CodeSmell[];
|
|
7
|
-
/**
|
|
8
|
-
* Detects unused imports at the file level
|
|
9
|
-
*/
|
|
10
|
-
export declare function detectUnusedImports(imports: Map<string, {
|
|
11
|
-
source: string;
|
|
12
|
-
line: number;
|
|
13
|
-
}>, usedInFile: Set<string>, filePath: string, sourceCode: string): CodeSmell[];
|
|
14
7
|
//# sourceMappingURL=deadCode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deadCode.d.ts","sourceRoot":"","sources":["../../src/detectors/deadCode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAkB,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAkB,MAAM,mBAAmB,CAAC;AAE9E;;GAEG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,cAA+B,GACtC,SAAS,EAAE,CAsHb
|
|
1
|
+
{"version":3,"file":"deadCode.d.ts","sourceRoot":"","sources":["../../src/detectors/deadCode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAkB,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAkB,MAAM,mBAAmB,CAAC;AAE9E;;GAEG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,cAA+B,GACtC,SAAS,EAAE,CAsHb"}
|
|
@@ -115,27 +115,3 @@ export function detectDeadCode(component, filePath, sourceCode, config = DEFAULT
|
|
|
115
115
|
});
|
|
116
116
|
return smells;
|
|
117
117
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Detects unused imports at the file level
|
|
120
|
-
*/
|
|
121
|
-
export function detectUnusedImports(imports, usedInFile, filePath, sourceCode) {
|
|
122
|
-
const smells = [];
|
|
123
|
-
imports.forEach((info, name) => {
|
|
124
|
-
// Skip React imports as they might be used implicitly
|
|
125
|
-
if (name === 'React' || info.source === 'react')
|
|
126
|
-
return;
|
|
127
|
-
if (!usedInFile.has(name)) {
|
|
128
|
-
smells.push({
|
|
129
|
-
type: 'dead-code',
|
|
130
|
-
severity: 'info',
|
|
131
|
-
message: `Unused import "${name}" from "${info.source}"`,
|
|
132
|
-
file: filePath,
|
|
133
|
-
line: info.line,
|
|
134
|
-
column: 0,
|
|
135
|
-
suggestion: `Remove the unused import: import { ${name} } from '${info.source}'`,
|
|
136
|
-
codeSnippet: getCodeSnippet(sourceCode, info.line),
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
return smells;
|
|
141
|
-
}
|
|
@@ -6,7 +6,7 @@ export { detectMissingKeys } from './missingKey.js';
|
|
|
6
6
|
export { detectHooksRulesViolations } from './hooksRules.js';
|
|
7
7
|
export { detectDependencyArrayIssues } from './dependencyArray.js';
|
|
8
8
|
export { detectNestedTernaries } from './nestedTernary.js';
|
|
9
|
-
export { detectDeadCode
|
|
9
|
+
export { detectDeadCode } from './deadCode.js';
|
|
10
10
|
export { detectMagicValues } from './magicValues.js';
|
|
11
11
|
export { detectNextjsIssues } from './nextjs.js';
|
|
12
12
|
export { detectReactNativeIssues } from './reactNative.js';
|
|
@@ -16,7 +16,7 @@ export { detectTypescriptIssues } from './typescript.js';
|
|
|
16
16
|
export { detectDebugStatements } from './debug.js';
|
|
17
17
|
export { detectSecurityIssues } from './security.js';
|
|
18
18
|
export { detectAccessibilityIssues } from './accessibility.js';
|
|
19
|
-
export { detectComplexity
|
|
19
|
+
export { detectComplexity } from './complexity.js';
|
|
20
20
|
export { detectMemoryLeaks } from './memoryLeak.js';
|
|
21
21
|
export { detectImportIssues, analyzeImports } from './imports.js';
|
|
22
22
|
export { detectUnusedCode } from './unusedCode.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/detectors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/detectors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/detectors/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { detectMissingKeys } from './missingKey.js';
|
|
|
6
6
|
export { detectHooksRulesViolations } from './hooksRules.js';
|
|
7
7
|
export { detectDependencyArrayIssues } from './dependencyArray.js';
|
|
8
8
|
export { detectNestedTernaries } from './nestedTernary.js';
|
|
9
|
-
export { detectDeadCode
|
|
9
|
+
export { detectDeadCode } from './deadCode.js';
|
|
10
10
|
export { detectMagicValues } from './magicValues.js';
|
|
11
11
|
// Framework-specific detectors
|
|
12
12
|
export { detectNextjsIssues } from './nextjs.js';
|
|
@@ -19,7 +19,7 @@ export { detectDebugStatements } from './debug.js';
|
|
|
19
19
|
export { detectSecurityIssues } from './security.js';
|
|
20
20
|
export { detectAccessibilityIssues } from './accessibility.js';
|
|
21
21
|
// Complexity, Memory Leaks, Imports
|
|
22
|
-
export { detectComplexity
|
|
22
|
+
export { detectComplexity } from './complexity.js';
|
|
23
23
|
export { detectMemoryLeaks } from './memoryLeak.js';
|
|
24
24
|
export { detectImportIssues, analyzeImports } from './imports.js';
|
|
25
25
|
export { detectUnusedCode } from './unusedCode.js';
|
package/dist/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAyEnD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAcpE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAU,GAAG,MAAM,EAAE,CAcpF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAW7D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAG1D"}
|
package/dist/git.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface DependencyNode {
|
|
2
|
+
id: string;
|
|
3
|
+
file: string;
|
|
4
|
+
type: 'component' | 'file';
|
|
5
|
+
imports: string[];
|
|
6
|
+
importedBy: string[];
|
|
7
|
+
size?: number;
|
|
8
|
+
isCircular?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface DependencyGraph {
|
|
11
|
+
nodes: Map<string, DependencyNode>;
|
|
12
|
+
edges: Array<{
|
|
13
|
+
from: string;
|
|
14
|
+
to: string;
|
|
15
|
+
circular: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
circularDependencies: string[][];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build dependency graph from parsed components
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildDependencyGraph(files: Array<{
|
|
23
|
+
file: string;
|
|
24
|
+
imports: string[];
|
|
25
|
+
}>, rootDir: string): DependencyGraph;
|
|
26
|
+
/**
|
|
27
|
+
* Generate SVG representation of dependency graph
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateDependencyGraph(graph: DependencyGraph, width?: number, height?: number): string;
|
|
30
|
+
/**
|
|
31
|
+
* Generate HTML report with dependency graph
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateDependencyGraphHTML(graph: DependencyGraph, projectName: string, circularCount?: number): string;
|
|
34
|
+
//# sourceMappingURL=graphGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphGenerator.d.ts","sourceRoot":"","sources":["../src/graphGenerator.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACnC,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC9D,oBAAoB,EAAE,MAAM,EAAE,EAAE,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EACjD,OAAO,EAAE,MAAM,GACd,eAAe,CA2CjB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,SAAO,EAAE,MAAM,SAAM,GAAG,MAAM,CA4DlG;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,EACnB,aAAa,SAAI,GAChB,MAAM,CA6HR"}
|