react-code-smell-detector 1.4.2 → 1.5.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 +227 -22
- package/dist/__tests__/aiRefactoring.test.d.ts +2 -0
- package/dist/__tests__/aiRefactoring.test.d.ts.map +1 -0
- package/dist/__tests__/aiRefactoring.test.js +86 -0
- package/dist/__tests__/analyzer-real.test.d.ts +2 -0
- package/dist/__tests__/analyzer-real.test.d.ts.map +1 -0
- package/dist/__tests__/analyzer-real.test.js +149 -0
- package/dist/__tests__/analyzer.test.d.ts +2 -0
- package/dist/__tests__/analyzer.test.d.ts.map +1 -0
- package/dist/__tests__/analyzer.test.js +173 -0
- package/dist/__tests__/baseline.test.d.ts +2 -0
- package/dist/__tests__/baseline.test.d.ts.map +1 -0
- package/dist/__tests__/baseline.test.js +136 -0
- package/dist/__tests__/bundleAnalyzer.test.d.ts +2 -0
- package/dist/__tests__/bundleAnalyzer.test.d.ts.map +1 -0
- package/dist/__tests__/bundleAnalyzer.test.js +182 -0
- package/dist/__tests__/customRules.test.d.ts +2 -0
- package/dist/__tests__/customRules.test.d.ts.map +1 -0
- package/dist/__tests__/customRules.test.js +283 -0
- package/dist/__tests__/detectors/index.test.d.ts +2 -0
- package/dist/__tests__/detectors/index.test.d.ts.map +1 -0
- package/dist/__tests__/detectors/index.test.js +1012 -0
- package/dist/__tests__/detectors/newDetectors.test.d.ts +2 -0
- package/dist/__tests__/detectors/newDetectors.test.d.ts.map +1 -0
- package/dist/__tests__/detectors/newDetectors.test.js +333 -0
- package/dist/__tests__/docGenerator.test.d.ts +2 -0
- package/dist/__tests__/docGenerator.test.d.ts.map +1 -0
- package/dist/__tests__/docGenerator.test.js +157 -0
- package/dist/__tests__/fixer.test.d.ts +2 -0
- package/dist/__tests__/fixer.test.d.ts.map +1 -0
- package/dist/__tests__/fixer.test.js +193 -0
- package/dist/__tests__/git.test.d.ts +2 -0
- package/dist/__tests__/git.test.d.ts.map +1 -0
- package/dist/__tests__/git.test.js +38 -0
- package/dist/__tests__/graphGenerator.test.d.ts +2 -0
- package/dist/__tests__/graphGenerator.test.d.ts.map +1 -0
- package/dist/__tests__/graphGenerator.test.js +190 -0
- package/dist/__tests__/htmlReporter.test.d.ts +2 -0
- package/dist/__tests__/htmlReporter.test.d.ts.map +1 -0
- package/dist/__tests__/htmlReporter.test.js +258 -0
- package/dist/__tests__/interactiveFixer.test.d.ts +2 -0
- package/dist/__tests__/interactiveFixer.test.d.ts.map +1 -0
- package/dist/__tests__/interactiveFixer.test.js +231 -0
- package/dist/__tests__/parser.test.d.ts +2 -0
- package/dist/__tests__/parser.test.d.ts.map +1 -0
- package/dist/__tests__/parser.test.js +56 -0
- package/dist/__tests__/performanceBudget.test.d.ts +2 -0
- package/dist/__tests__/performanceBudget.test.d.ts.map +1 -0
- package/dist/__tests__/performanceBudget.test.js +242 -0
- package/dist/__tests__/prComments.test.d.ts +2 -0
- package/dist/__tests__/prComments.test.d.ts.map +1 -0
- package/dist/__tests__/prComments.test.js +118 -0
- package/dist/__tests__/reporter.test.d.ts +2 -0
- package/dist/__tests__/reporter.test.d.ts.map +1 -0
- package/dist/__tests__/reporter.test.js +136 -0
- package/dist/__tests__/watcher.test.d.ts +2 -0
- package/dist/__tests__/watcher.test.d.ts.map +1 -0
- package/dist/__tests__/watcher.test.js +161 -0
- package/dist/__tests__/webhooks.test.d.ts +2 -0
- package/dist/__tests__/webhooks.test.d.ts.map +1 -0
- package/dist/__tests__/webhooks.test.js +209 -0
- package/dist/aiRefactoring.d.ts +29 -0
- package/dist/aiRefactoring.d.ts.map +1 -0
- package/dist/aiRefactoring.js +290 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +33 -1
- package/dist/cli.js +123 -1
- package/dist/detectors/contextApi.d.ts +11 -0
- package/dist/detectors/contextApi.d.ts.map +1 -0
- package/dist/detectors/contextApi.js +151 -0
- package/dist/detectors/errorBoundary.d.ts +11 -0
- package/dist/detectors/errorBoundary.d.ts.map +1 -0
- package/dist/detectors/errorBoundary.js +167 -0
- package/dist/detectors/formValidation.d.ts +11 -0
- package/dist/detectors/formValidation.d.ts.map +1 -0
- package/dist/detectors/formValidation.js +193 -0
- package/dist/detectors/index.d.ts +6 -0
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +12 -0
- package/dist/detectors/serverComponents.d.ts +11 -0
- package/dist/detectors/serverComponents.d.ts.map +1 -0
- package/dist/detectors/serverComponents.js +222 -0
- package/dist/detectors/stateManagement.d.ts +11 -0
- package/dist/detectors/stateManagement.d.ts.map +1 -0
- package/dist/detectors/stateManagement.js +193 -0
- package/dist/detectors/testingGaps.d.ts +15 -0
- package/dist/detectors/testingGaps.d.ts.map +1 -0
- package/dist/detectors/testingGaps.js +182 -0
- package/dist/docGenerator.d.ts +37 -0
- package/dist/docGenerator.d.ts.map +1 -0
- package/dist/docGenerator.js +306 -0
- package/dist/guide.d.ts +9 -0
- package/dist/guide.d.ts.map +1 -0
- package/dist/guide.js +922 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/interactiveFixer.d.ts +20 -0
- package/dist/interactiveFixer.d.ts.map +1 -0
- package/dist/interactiveFixer.js +178 -0
- package/dist/performanceBudget.d.ts +54 -0
- package/dist/performanceBudget.d.ts.map +1 -0
- package/dist/performanceBudget.js +218 -0
- package/dist/prComments.d.ts +47 -0
- package/dist/prComments.d.ts.map +1 -0
- package/dist/prComments.js +233 -0
- package/dist/types/index.d.ts +12 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +18 -0
- package/package.json +10 -4
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { parseCustomRules, detectCustomRuleViolations, generateRulesDocumentation, EXAMPLE_CUSTOM_RULES } from '../customRules.js';
|
|
3
|
+
import { parseCode } from '../parser/index.js';
|
|
4
|
+
describe('Custom Rules', () => {
|
|
5
|
+
const getFirstComponent = (code) => {
|
|
6
|
+
const { components } = parseCode(code, 'test.tsx');
|
|
7
|
+
return components[0];
|
|
8
|
+
};
|
|
9
|
+
describe('parseCustomRules', () => {
|
|
10
|
+
it('should return empty array for no custom rules', () => {
|
|
11
|
+
const rules = parseCustomRules({});
|
|
12
|
+
expect(rules).toEqual([]);
|
|
13
|
+
});
|
|
14
|
+
it('should return empty array for undefined custom rules', () => {
|
|
15
|
+
const rules = parseCustomRules({ customRules: undefined });
|
|
16
|
+
expect(rules).toEqual([]);
|
|
17
|
+
});
|
|
18
|
+
it('should parse single rule object', () => {
|
|
19
|
+
const config = {
|
|
20
|
+
customRules: {
|
|
21
|
+
name: 'no-console',
|
|
22
|
+
pattern: 'console.log',
|
|
23
|
+
patternType: 'text',
|
|
24
|
+
severity: 'error',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const rules = parseCustomRules(config);
|
|
28
|
+
expect(rules).toHaveLength(1);
|
|
29
|
+
expect(rules[0].name).toBe('no-console');
|
|
30
|
+
expect(rules[0].severity).toBe('error');
|
|
31
|
+
});
|
|
32
|
+
it('should parse array of rules', () => {
|
|
33
|
+
const config = {
|
|
34
|
+
customRules: [
|
|
35
|
+
{ name: 'rule1', pattern: 'test1', patternType: 'text' },
|
|
36
|
+
{ name: 'rule2', pattern: 'test2', patternType: 'regex' },
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
const rules = parseCustomRules(config);
|
|
40
|
+
expect(rules).toHaveLength(2);
|
|
41
|
+
expect(rules[0].name).toBe('rule1');
|
|
42
|
+
expect(rules[1].name).toBe('rule2');
|
|
43
|
+
});
|
|
44
|
+
it('should filter out invalid rules without name', () => {
|
|
45
|
+
const config = {
|
|
46
|
+
customRules: [
|
|
47
|
+
{ pattern: 'test1' }, // missing name
|
|
48
|
+
{ name: 'rule2', pattern: 'test2' },
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
52
|
+
const rules = parseCustomRules(config);
|
|
53
|
+
expect(rules).toHaveLength(1);
|
|
54
|
+
expect(rules[0].name).toBe('rule2');
|
|
55
|
+
warnSpy.mockRestore();
|
|
56
|
+
});
|
|
57
|
+
it('should filter out invalid rules without pattern', () => {
|
|
58
|
+
const config = {
|
|
59
|
+
customRules: [
|
|
60
|
+
{ name: 'rule1' }, // missing pattern
|
|
61
|
+
{ name: 'rule2', pattern: 'test2' },
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
65
|
+
const rules = parseCustomRules(config);
|
|
66
|
+
expect(rules).toHaveLength(1);
|
|
67
|
+
expect(rules[0].name).toBe('rule2');
|
|
68
|
+
warnSpy.mockRestore();
|
|
69
|
+
});
|
|
70
|
+
it('should set defaults for optional fields', () => {
|
|
71
|
+
const config = {
|
|
72
|
+
customRules: [{ name: 'test-rule', pattern: 'test' }],
|
|
73
|
+
};
|
|
74
|
+
const rules = parseCustomRules(config);
|
|
75
|
+
expect(rules[0].severity).toBe('warning');
|
|
76
|
+
expect(rules[0].patternType).toBe('text');
|
|
77
|
+
expect(rules[0].enabled).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
it('should respect enabled flag', () => {
|
|
80
|
+
const config = {
|
|
81
|
+
customRules: [
|
|
82
|
+
{ name: 'enabled-rule', pattern: 'test', enabled: true },
|
|
83
|
+
{ name: 'disabled-rule', pattern: 'test', enabled: false },
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
const rules = parseCustomRules(config);
|
|
87
|
+
expect(rules).toHaveLength(2);
|
|
88
|
+
expect(rules.find(r => r.name === 'enabled-rule')?.enabled).toBe(true);
|
|
89
|
+
expect(rules.find(r => r.name === 'disabled-rule')?.enabled).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
it('should handle regex pattern type', () => {
|
|
92
|
+
const config = {
|
|
93
|
+
customRules: [
|
|
94
|
+
{ name: 'regex-rule', pattern: '\\bconsole\\b', patternType: 'regex' },
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
const rules = parseCustomRules(config);
|
|
98
|
+
expect(rules[0].patternType).toBe('regex');
|
|
99
|
+
});
|
|
100
|
+
it('should preserve custom message', () => {
|
|
101
|
+
const config = {
|
|
102
|
+
customRules: [
|
|
103
|
+
{
|
|
104
|
+
name: 'test-rule',
|
|
105
|
+
pattern: 'test',
|
|
106
|
+
message: 'Custom error message',
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
const rules = parseCustomRules(config);
|
|
111
|
+
expect(rules[0].message).toBe('Custom error message');
|
|
112
|
+
});
|
|
113
|
+
it('should handle non-object customRules', () => {
|
|
114
|
+
const rules = parseCustomRules({ customRules: 'invalid' });
|
|
115
|
+
expect(rules).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
it('should handle description field', () => {
|
|
118
|
+
const config = {
|
|
119
|
+
customRules: [{
|
|
120
|
+
name: 'test-rule',
|
|
121
|
+
pattern: 'test',
|
|
122
|
+
description: 'This is a test rule',
|
|
123
|
+
}],
|
|
124
|
+
};
|
|
125
|
+
const rules = parseCustomRules(config);
|
|
126
|
+
expect(rules[0].description).toBe('This is a test rule');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('detectCustomRuleViolations', () => {
|
|
130
|
+
it('should detect text pattern violations', () => {
|
|
131
|
+
const code = `
|
|
132
|
+
function TestComponent() {
|
|
133
|
+
const todo = 'hello world';
|
|
134
|
+
return <div>{todo}</div>;
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
const component = getFirstComponent(code);
|
|
138
|
+
const rules = [{
|
|
139
|
+
name: 'no-hello',
|
|
140
|
+
pattern: 'hello',
|
|
141
|
+
patternType: 'text',
|
|
142
|
+
severity: 'warning',
|
|
143
|
+
enabled: true,
|
|
144
|
+
message: 'Found hello',
|
|
145
|
+
}];
|
|
146
|
+
const smells = detectCustomRuleViolations(component, '/test.tsx', code, rules);
|
|
147
|
+
expect(smells.length).toBeGreaterThanOrEqual(1);
|
|
148
|
+
expect(smells[0].message).toBe('Found hello');
|
|
149
|
+
});
|
|
150
|
+
it('should detect regex pattern violations', () => {
|
|
151
|
+
const code = `
|
|
152
|
+
function TestComponent() {
|
|
153
|
+
const value = 12345;
|
|
154
|
+
return <div>{value}</div>;
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
const component = getFirstComponent(code);
|
|
158
|
+
const rules = [{
|
|
159
|
+
name: 'no-numbers',
|
|
160
|
+
pattern: '\\d{5}',
|
|
161
|
+
patternType: 'regex',
|
|
162
|
+
severity: 'error',
|
|
163
|
+
enabled: true,
|
|
164
|
+
message: 'Found 5-digit number',
|
|
165
|
+
}];
|
|
166
|
+
const smells = detectCustomRuleViolations(component, '/test.tsx', code, rules);
|
|
167
|
+
expect(smells.length).toBeGreaterThanOrEqual(1);
|
|
168
|
+
expect(smells[0].severity).toBe('error');
|
|
169
|
+
});
|
|
170
|
+
it('should skip disabled rules', () => {
|
|
171
|
+
const code = `
|
|
172
|
+
function TestComponent() {
|
|
173
|
+
const hello = 'world';
|
|
174
|
+
return <div>{hello}</div>;
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
const component = getFirstComponent(code);
|
|
178
|
+
const rules = [{
|
|
179
|
+
name: 'no-hello',
|
|
180
|
+
pattern: 'hello',
|
|
181
|
+
patternType: 'text',
|
|
182
|
+
severity: 'warning',
|
|
183
|
+
enabled: false,
|
|
184
|
+
message: 'Found hello',
|
|
185
|
+
}];
|
|
186
|
+
const smells = detectCustomRuleViolations(component, '/test.tsx', code, rules);
|
|
187
|
+
expect(smells.length).toBe(0);
|
|
188
|
+
});
|
|
189
|
+
it('should handle AST pattern type', () => {
|
|
190
|
+
const code = `
|
|
191
|
+
function TestComponent() {
|
|
192
|
+
return <div>Test</div>;
|
|
193
|
+
}
|
|
194
|
+
`;
|
|
195
|
+
const component = getFirstComponent(code);
|
|
196
|
+
const rules = [{
|
|
197
|
+
name: 'detect-jsx',
|
|
198
|
+
pattern: 'JSXElement',
|
|
199
|
+
patternType: 'ast',
|
|
200
|
+
severity: 'info',
|
|
201
|
+
enabled: true,
|
|
202
|
+
message: 'Found JSX element',
|
|
203
|
+
}];
|
|
204
|
+
const smells = detectCustomRuleViolations(component, '/test.tsx', code, rules);
|
|
205
|
+
expect(smells.length).toBeGreaterThanOrEqual(1);
|
|
206
|
+
});
|
|
207
|
+
it('should return empty array for no rules', () => {
|
|
208
|
+
const code = `
|
|
209
|
+
function TestComponent() {
|
|
210
|
+
return <div>Test</div>;
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
const component = getFirstComponent(code);
|
|
214
|
+
const smells = detectCustomRuleViolations(component, '/test.tsx', code, []);
|
|
215
|
+
expect(smells).toEqual([]);
|
|
216
|
+
});
|
|
217
|
+
it('should include code snippet in violation', () => {
|
|
218
|
+
const code = `function TestComponent() {
|
|
219
|
+
const badVar = 'test';
|
|
220
|
+
return <div>{badVar}</div>;
|
|
221
|
+
}`;
|
|
222
|
+
const component = getFirstComponent(code);
|
|
223
|
+
const rules = [{
|
|
224
|
+
name: 'no-bad',
|
|
225
|
+
pattern: 'badVar',
|
|
226
|
+
patternType: 'text',
|
|
227
|
+
severity: 'warning',
|
|
228
|
+
enabled: true,
|
|
229
|
+
}];
|
|
230
|
+
const smells = detectCustomRuleViolations(component, '/test.tsx', code, rules);
|
|
231
|
+
expect(smells.some(s => s.codeSnippet?.includes('badVar'))).toBe(true);
|
|
232
|
+
});
|
|
233
|
+
it('should handle RegExp objects', () => {
|
|
234
|
+
const code = `
|
|
235
|
+
function Test() {
|
|
236
|
+
const x = console.debug('test');
|
|
237
|
+
return <div>{x}</div>;
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
const component = getFirstComponent(code);
|
|
241
|
+
const rules = [{
|
|
242
|
+
name: 'no-console-debug',
|
|
243
|
+
pattern: /console\.debug/,
|
|
244
|
+
patternType: 'regex',
|
|
245
|
+
severity: 'warning',
|
|
246
|
+
enabled: true,
|
|
247
|
+
}];
|
|
248
|
+
const smells = detectCustomRuleViolations(component, '/test.tsx', code, rules);
|
|
249
|
+
expect(smells.length).toBeGreaterThanOrEqual(1);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
describe('generateRulesDocumentation', () => {
|
|
253
|
+
it('should return documentation string', () => {
|
|
254
|
+
const docs = generateRulesDocumentation();
|
|
255
|
+
expect(typeof docs).toBe('string');
|
|
256
|
+
expect(docs.length).toBeGreaterThan(0);
|
|
257
|
+
});
|
|
258
|
+
it('should include markdown headers', () => {
|
|
259
|
+
const docs = generateRulesDocumentation();
|
|
260
|
+
expect(docs).toContain('#');
|
|
261
|
+
});
|
|
262
|
+
it('should include custom rules info', () => {
|
|
263
|
+
const docs = generateRulesDocumentation();
|
|
264
|
+
expect(docs).toContain('Custom Rules');
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe('EXAMPLE_CUSTOM_RULES', () => {
|
|
268
|
+
it('should be a valid CustomRulesConfig', () => {
|
|
269
|
+
expect(EXAMPLE_CUSTOM_RULES.enabled).toBe(true);
|
|
270
|
+
expect(Array.isArray(EXAMPLE_CUSTOM_RULES.rules)).toBe(true);
|
|
271
|
+
});
|
|
272
|
+
it('should contain example rules', () => {
|
|
273
|
+
expect(EXAMPLE_CUSTOM_RULES.rules.length).toBeGreaterThan(0);
|
|
274
|
+
});
|
|
275
|
+
it('should have valid rule structure', () => {
|
|
276
|
+
for (const rule of EXAMPLE_CUSTOM_RULES.rules) {
|
|
277
|
+
expect(rule.name).toBeDefined();
|
|
278
|
+
expect(rule.pattern).toBeDefined();
|
|
279
|
+
expect(rule.severity).toBeDefined();
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/detectors/index.test.ts"],"names":[],"mappings":""}
|