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.
Files changed (110) hide show
  1. package/README.md +227 -22
  2. package/dist/__tests__/aiRefactoring.test.d.ts +2 -0
  3. package/dist/__tests__/aiRefactoring.test.d.ts.map +1 -0
  4. package/dist/__tests__/aiRefactoring.test.js +86 -0
  5. package/dist/__tests__/analyzer-real.test.d.ts +2 -0
  6. package/dist/__tests__/analyzer-real.test.d.ts.map +1 -0
  7. package/dist/__tests__/analyzer-real.test.js +149 -0
  8. package/dist/__tests__/analyzer.test.d.ts +2 -0
  9. package/dist/__tests__/analyzer.test.d.ts.map +1 -0
  10. package/dist/__tests__/analyzer.test.js +173 -0
  11. package/dist/__tests__/baseline.test.d.ts +2 -0
  12. package/dist/__tests__/baseline.test.d.ts.map +1 -0
  13. package/dist/__tests__/baseline.test.js +136 -0
  14. package/dist/__tests__/bundleAnalyzer.test.d.ts +2 -0
  15. package/dist/__tests__/bundleAnalyzer.test.d.ts.map +1 -0
  16. package/dist/__tests__/bundleAnalyzer.test.js +182 -0
  17. package/dist/__tests__/customRules.test.d.ts +2 -0
  18. package/dist/__tests__/customRules.test.d.ts.map +1 -0
  19. package/dist/__tests__/customRules.test.js +283 -0
  20. package/dist/__tests__/detectors/index.test.d.ts +2 -0
  21. package/dist/__tests__/detectors/index.test.d.ts.map +1 -0
  22. package/dist/__tests__/detectors/index.test.js +1012 -0
  23. package/dist/__tests__/detectors/newDetectors.test.d.ts +2 -0
  24. package/dist/__tests__/detectors/newDetectors.test.d.ts.map +1 -0
  25. package/dist/__tests__/detectors/newDetectors.test.js +333 -0
  26. package/dist/__tests__/docGenerator.test.d.ts +2 -0
  27. package/dist/__tests__/docGenerator.test.d.ts.map +1 -0
  28. package/dist/__tests__/docGenerator.test.js +157 -0
  29. package/dist/__tests__/fixer.test.d.ts +2 -0
  30. package/dist/__tests__/fixer.test.d.ts.map +1 -0
  31. package/dist/__tests__/fixer.test.js +193 -0
  32. package/dist/__tests__/git.test.d.ts +2 -0
  33. package/dist/__tests__/git.test.d.ts.map +1 -0
  34. package/dist/__tests__/git.test.js +38 -0
  35. package/dist/__tests__/graphGenerator.test.d.ts +2 -0
  36. package/dist/__tests__/graphGenerator.test.d.ts.map +1 -0
  37. package/dist/__tests__/graphGenerator.test.js +190 -0
  38. package/dist/__tests__/htmlReporter.test.d.ts +2 -0
  39. package/dist/__tests__/htmlReporter.test.d.ts.map +1 -0
  40. package/dist/__tests__/htmlReporter.test.js +258 -0
  41. package/dist/__tests__/interactiveFixer.test.d.ts +2 -0
  42. package/dist/__tests__/interactiveFixer.test.d.ts.map +1 -0
  43. package/dist/__tests__/interactiveFixer.test.js +231 -0
  44. package/dist/__tests__/parser.test.d.ts +2 -0
  45. package/dist/__tests__/parser.test.d.ts.map +1 -0
  46. package/dist/__tests__/parser.test.js +56 -0
  47. package/dist/__tests__/performanceBudget.test.d.ts +2 -0
  48. package/dist/__tests__/performanceBudget.test.d.ts.map +1 -0
  49. package/dist/__tests__/performanceBudget.test.js +242 -0
  50. package/dist/__tests__/prComments.test.d.ts +2 -0
  51. package/dist/__tests__/prComments.test.d.ts.map +1 -0
  52. package/dist/__tests__/prComments.test.js +118 -0
  53. package/dist/__tests__/reporter.test.d.ts +2 -0
  54. package/dist/__tests__/reporter.test.d.ts.map +1 -0
  55. package/dist/__tests__/reporter.test.js +136 -0
  56. package/dist/__tests__/watcher.test.d.ts +2 -0
  57. package/dist/__tests__/watcher.test.d.ts.map +1 -0
  58. package/dist/__tests__/watcher.test.js +161 -0
  59. package/dist/__tests__/webhooks.test.d.ts +2 -0
  60. package/dist/__tests__/webhooks.test.d.ts.map +1 -0
  61. package/dist/__tests__/webhooks.test.js +209 -0
  62. package/dist/aiRefactoring.d.ts +29 -0
  63. package/dist/aiRefactoring.d.ts.map +1 -0
  64. package/dist/aiRefactoring.js +290 -0
  65. package/dist/analyzer.d.ts.map +1 -1
  66. package/dist/analyzer.js +33 -1
  67. package/dist/cli.js +123 -1
  68. package/dist/detectors/contextApi.d.ts +11 -0
  69. package/dist/detectors/contextApi.d.ts.map +1 -0
  70. package/dist/detectors/contextApi.js +151 -0
  71. package/dist/detectors/errorBoundary.d.ts +11 -0
  72. package/dist/detectors/errorBoundary.d.ts.map +1 -0
  73. package/dist/detectors/errorBoundary.js +167 -0
  74. package/dist/detectors/formValidation.d.ts +11 -0
  75. package/dist/detectors/formValidation.d.ts.map +1 -0
  76. package/dist/detectors/formValidation.js +193 -0
  77. package/dist/detectors/index.d.ts +6 -0
  78. package/dist/detectors/index.d.ts.map +1 -1
  79. package/dist/detectors/index.js +12 -0
  80. package/dist/detectors/serverComponents.d.ts +11 -0
  81. package/dist/detectors/serverComponents.d.ts.map +1 -0
  82. package/dist/detectors/serverComponents.js +222 -0
  83. package/dist/detectors/stateManagement.d.ts +11 -0
  84. package/dist/detectors/stateManagement.d.ts.map +1 -0
  85. package/dist/detectors/stateManagement.js +193 -0
  86. package/dist/detectors/testingGaps.d.ts +15 -0
  87. package/dist/detectors/testingGaps.d.ts.map +1 -0
  88. package/dist/detectors/testingGaps.js +182 -0
  89. package/dist/docGenerator.d.ts +37 -0
  90. package/dist/docGenerator.d.ts.map +1 -0
  91. package/dist/docGenerator.js +306 -0
  92. package/dist/guide.d.ts +9 -0
  93. package/dist/guide.d.ts.map +1 -0
  94. package/dist/guide.js +922 -0
  95. package/dist/index.d.ts +5 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +5 -0
  98. package/dist/interactiveFixer.d.ts +20 -0
  99. package/dist/interactiveFixer.d.ts.map +1 -0
  100. package/dist/interactiveFixer.js +178 -0
  101. package/dist/performanceBudget.d.ts +54 -0
  102. package/dist/performanceBudget.d.ts.map +1 -0
  103. package/dist/performanceBudget.js +218 -0
  104. package/dist/prComments.d.ts +47 -0
  105. package/dist/prComments.d.ts.map +1 -0
  106. package/dist/prComments.js +233 -0
  107. package/dist/types/index.d.ts +12 -1
  108. package/dist/types/index.d.ts.map +1 -1
  109. package/dist/types/index.js +18 -0
  110. package/package.json +10 -4
@@ -0,0 +1,173 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ // Mock dependencies for analyzer
3
+ vi.mock('fast-glob', () => ({
4
+ default: vi.fn().mockResolvedValue([]),
5
+ }));
6
+ vi.mock('ora', () => ({
7
+ default: () => ({
8
+ start: vi.fn().mockReturnThis(),
9
+ succeed: vi.fn().mockReturnThis(),
10
+ fail: vi.fn().mockReturnThis(),
11
+ stop: vi.fn().mockReturnThis(),
12
+ text: '',
13
+ }),
14
+ }));
15
+ describe('Analyzer', () => {
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ });
19
+ describe('analyzeProject', () => {
20
+ it('should accept project path', () => {
21
+ const projectPath = '/path/to/project';
22
+ expect(typeof projectPath).toBe('string');
23
+ });
24
+ it('should accept options object', () => {
25
+ const options = {
26
+ verbose: true,
27
+ fix: false,
28
+ format: 'json',
29
+ };
30
+ expect(options.verbose).toBe(true);
31
+ expect(options.fix).toBe(false);
32
+ expect(options.format).toBe('json');
33
+ });
34
+ it('should handle default options', () => {
35
+ const defaultOptions = {
36
+ verbose: false,
37
+ fix: false,
38
+ format: 'console',
39
+ include: ['**/*.tsx', '**/*.jsx', '**/*.ts', '**/*.js'],
40
+ exclude: ['node_modules/**', 'dist/**', 'build/**'],
41
+ };
42
+ expect(defaultOptions.include).toContain('**/*.tsx');
43
+ expect(defaultOptions.exclude).toContain('node_modules/**');
44
+ });
45
+ it('should support various output formats', () => {
46
+ const formats = ['json', 'console', 'markdown', 'html', 'sarif'];
47
+ formats.forEach(format => {
48
+ expect(['json', 'console', 'markdown', 'html', 'sarif']).toContain(format);
49
+ });
50
+ });
51
+ });
52
+ describe('File pattern matching', () => {
53
+ it('should match React file extensions', () => {
54
+ const patterns = ['**/*.tsx', '**/*.jsx', '**/*.ts', '**/*.js'];
55
+ const testFiles = [
56
+ 'src/App.tsx',
57
+ 'src/utils/helper.ts',
58
+ 'components/Button.jsx',
59
+ 'lib/index.js',
60
+ ];
61
+ testFiles.forEach(file => {
62
+ const matches = patterns.some(pattern => {
63
+ const ext = pattern.replace('**/*', '');
64
+ return file.endsWith(ext);
65
+ });
66
+ expect(matches).toBe(true);
67
+ });
68
+ });
69
+ it('should exclude node_modules and build dirs', () => {
70
+ const excludePatterns = ['node_modules/**', 'dist/**', 'build/**'];
71
+ const shouldExclude = (file) => {
72
+ return excludePatterns.some(pattern => {
73
+ const dir = pattern.replace('/**', '');
74
+ return file.startsWith(dir + '/') || file.startsWith(dir);
75
+ });
76
+ };
77
+ expect(shouldExclude('node_modules/react/index.js')).toBe(true);
78
+ expect(shouldExclude('dist/bundle.js')).toBe(true);
79
+ expect(shouldExclude('build/output.js')).toBe(true);
80
+ expect(shouldExclude('src/App.tsx')).toBe(false);
81
+ });
82
+ });
83
+ describe('Options validation', () => {
84
+ it('should validate format option', () => {
85
+ const validFormats = ['json', 'console', 'markdown', 'html', 'sarif'];
86
+ const isValidFormat = (format) => validFormats.includes(format);
87
+ expect(isValidFormat('json')).toBe(true);
88
+ expect(isValidFormat('invalid')).toBe(false);
89
+ });
90
+ it('should validate threshold is positive number', () => {
91
+ const isValidThreshold = (threshold) => {
92
+ return typeof threshold === 'number' && threshold >= 0;
93
+ };
94
+ expect(isValidThreshold(10)).toBe(true);
95
+ expect(isValidThreshold(0)).toBe(true);
96
+ expect(isValidThreshold(-1)).toBe(false);
97
+ expect(isValidThreshold('10')).toBe(false);
98
+ });
99
+ it('should validate custom rules path', () => {
100
+ const isValidPath = (path) => {
101
+ return typeof path === 'string' && path.length > 0;
102
+ };
103
+ expect(isValidPath('./rules.json')).toBe(true);
104
+ expect(isValidPath('')).toBe(false);
105
+ expect(isValidPath(null)).toBe(false);
106
+ });
107
+ });
108
+ describe('Analysis results structure', () => {
109
+ it('should have correct result shape', () => {
110
+ const mockResult = {
111
+ smells: [],
112
+ filesAnalyzed: 10,
113
+ totalSmells: 0,
114
+ byType: {},
115
+ bySeverity: { error: 0, warning: 0, info: 0 },
116
+ };
117
+ expect(mockResult).toHaveProperty('smells');
118
+ expect(mockResult).toHaveProperty('filesAnalyzed');
119
+ expect(mockResult).toHaveProperty('totalSmells');
120
+ expect(mockResult).toHaveProperty('byType');
121
+ expect(mockResult).toHaveProperty('bySeverity');
122
+ });
123
+ it('should aggregate smells by type', () => {
124
+ const smells = [
125
+ { type: 'debug-statement' },
126
+ { type: 'debug-statement' },
127
+ { type: 'js-var-usage' },
128
+ ];
129
+ const byType = smells.reduce((acc, s) => {
130
+ acc[s.type] = (acc[s.type] || 0) + 1;
131
+ return acc;
132
+ }, {});
133
+ expect(byType['debug-statement']).toBe(2);
134
+ expect(byType['js-var-usage']).toBe(1);
135
+ });
136
+ it('should aggregate smells by severity', () => {
137
+ const smells = [
138
+ { severity: 'error' },
139
+ { severity: 'warning' },
140
+ { severity: 'warning' },
141
+ { severity: 'info' },
142
+ ];
143
+ const bySeverity = smells.reduce((acc, s) => {
144
+ acc[s.severity] = (acc[s.severity] || 0) + 1;
145
+ return acc;
146
+ }, { error: 0, warning: 0, info: 0 });
147
+ expect(bySeverity.error).toBe(1);
148
+ expect(bySeverity.warning).toBe(2);
149
+ expect(bySeverity.info).toBe(1);
150
+ });
151
+ });
152
+ describe('Performance', () => {
153
+ it('should batch file processing efficiently', () => {
154
+ const files = Array(100).fill(null).map((_, i) => `file${i}.tsx`);
155
+ const batchSize = 10;
156
+ const batches = [];
157
+ for (let i = 0; i < files.length; i += batchSize) {
158
+ batches.push(files.slice(i, i + batchSize));
159
+ }
160
+ expect(batches).toHaveLength(10);
161
+ expect(batches[0]).toHaveLength(10);
162
+ });
163
+ it('should handle concurrent file analysis', async () => {
164
+ const analyzeFile = async (file) => {
165
+ return { file, smells: [] };
166
+ };
167
+ const files = ['a.tsx', 'b.tsx', 'c.tsx'];
168
+ const results = await Promise.all(files.map(analyzeFile));
169
+ expect(results).toHaveLength(3);
170
+ expect(results[0].file).toBe('a.tsx');
171
+ });
172
+ });
173
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=baseline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/baseline.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,136 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { initializeBaseline, recordBaseline, getTrendAnalysis } from '../baseline.js';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import os from 'os';
6
+ describe('Baseline', () => {
7
+ let tempDir;
8
+ beforeEach(() => {
9
+ // Create a temporary directory for tests
10
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'baseline-test-'));
11
+ });
12
+ afterEach(() => {
13
+ // Clean up temporary directory
14
+ try {
15
+ fs.rmSync(tempDir, { recursive: true });
16
+ }
17
+ catch {
18
+ // Ignore cleanup errors
19
+ }
20
+ });
21
+ const createSmell = (type) => ({
22
+ type: type,
23
+ severity: 'warning',
24
+ message: 'Test smell',
25
+ file: '/test.tsx',
26
+ line: 1,
27
+ column: 0,
28
+ suggestion: 'Fix it',
29
+ });
30
+ describe('initializeBaseline', () => {
31
+ it('should create baseline file if it does not exist', () => {
32
+ initializeBaseline(tempDir);
33
+ const baselinePath = path.join(tempDir, '.smellrc-baseline.json');
34
+ expect(fs.existsSync(baselinePath)).toBe(true);
35
+ });
36
+ it('should not overwrite existing baseline file', () => {
37
+ const baselinePath = path.join(tempDir, '.smellrc-baseline.json');
38
+ // Create initial baseline
39
+ initializeBaseline(tempDir);
40
+ // Add a record
41
+ recordBaseline(tempDir, [createSmell('debug-statement')]);
42
+ // Reinitialize
43
+ initializeBaseline(tempDir);
44
+ // Check that record still exists
45
+ const data = JSON.parse(fs.readFileSync(baselinePath, 'utf-8'));
46
+ expect(data.records.length).toBeGreaterThan(0);
47
+ });
48
+ it('should create valid JSON structure', () => {
49
+ initializeBaseline(tempDir);
50
+ const baselinePath = path.join(tempDir, '.smellrc-baseline.json');
51
+ const data = JSON.parse(fs.readFileSync(baselinePath, 'utf-8'));
52
+ expect(data.version).toBe('1.0');
53
+ expect(Array.isArray(data.records)).toBe(true);
54
+ });
55
+ });
56
+ describe('recordBaseline', () => {
57
+ it('should add a record to baseline', () => {
58
+ initializeBaseline(tempDir);
59
+ const smells = [createSmell('debug-statement'), createSmell('js-var-usage')];
60
+ const record = recordBaseline(tempDir, smells);
61
+ expect(record.totalSmells).toBe(2);
62
+ expect(record.byType['debug-statement']).toBe(1);
63
+ expect(record.byType['js-var-usage']).toBe(1);
64
+ });
65
+ it('should include timestamp', () => {
66
+ initializeBaseline(tempDir);
67
+ const record = recordBaseline(tempDir, [createSmell('debug-statement')]);
68
+ expect(record.timestamp).toBeDefined();
69
+ expect(new Date(record.timestamp).getTime()).toBeLessThanOrEqual(Date.now());
70
+ });
71
+ it('should include commit hash when provided', () => {
72
+ initializeBaseline(tempDir);
73
+ const record = recordBaseline(tempDir, [createSmell('debug-statement')], 'abc123');
74
+ expect(record.commit).toBe('abc123');
75
+ });
76
+ it('should keep only last 50 records', () => {
77
+ initializeBaseline(tempDir);
78
+ // Add 55 records
79
+ for (let i = 0; i < 55; i++) {
80
+ recordBaseline(tempDir, [createSmell('debug-statement')]);
81
+ }
82
+ const baselinePath = path.join(tempDir, '.smellrc-baseline.json');
83
+ const data = JSON.parse(fs.readFileSync(baselinePath, 'utf-8'));
84
+ expect(data.records.length).toBe(50);
85
+ });
86
+ it('should limit stored smells to 100', () => {
87
+ initializeBaseline(tempDir);
88
+ // Create 150 smells
89
+ const manySmells = Array(150).fill(null).map(() => createSmell('debug-statement'));
90
+ const record = recordBaseline(tempDir, manySmells);
91
+ expect(record.totalSmells).toBe(150);
92
+ expect(record.smells.length).toBe(100);
93
+ });
94
+ });
95
+ describe('getTrendAnalysis', () => {
96
+ it('should return stable when no baseline exists', () => {
97
+ const trend = getTrendAnalysis(tempDir);
98
+ expect(trend.trend).toBe('stable');
99
+ expect(trend.improved).toBe(0);
100
+ expect(trend.worsened).toBe(0);
101
+ });
102
+ it('should return stable with only one record', () => {
103
+ initializeBaseline(tempDir);
104
+ recordBaseline(tempDir, [createSmell('debug-statement')]);
105
+ const trend = getTrendAnalysis(tempDir);
106
+ expect(trend.trend).toBe('stable');
107
+ });
108
+ it('should detect improving trend', () => {
109
+ initializeBaseline(tempDir);
110
+ // First record: 5 smells
111
+ recordBaseline(tempDir, Array(5).fill(null).map(() => createSmell('debug-statement')));
112
+ // Second record: 3 smells (improved)
113
+ recordBaseline(tempDir, Array(3).fill(null).map(() => createSmell('debug-statement')));
114
+ const trend = getTrendAnalysis(tempDir);
115
+ expect(trend.trend).toBe('improving');
116
+ expect(trend.improved).toBe(2);
117
+ });
118
+ it('should detect worsening trend', () => {
119
+ initializeBaseline(tempDir);
120
+ // First record: 3 smells
121
+ recordBaseline(tempDir, Array(3).fill(null).map(() => createSmell('debug-statement')));
122
+ // Second record: 5 smells (worsened)
123
+ recordBaseline(tempDir, Array(5).fill(null).map(() => createSmell('debug-statement')));
124
+ const trend = getTrendAnalysis(tempDir);
125
+ expect(trend.trend).toBe('worsening');
126
+ expect(trend.worsened).toBe(2);
127
+ });
128
+ it('should return stable when same count', () => {
129
+ initializeBaseline(tempDir);
130
+ recordBaseline(tempDir, Array(3).fill(null).map(() => createSmell('debug-statement')));
131
+ recordBaseline(tempDir, Array(3).fill(null).map(() => createSmell('js-var-usage')));
132
+ const trend = getTrendAnalysis(tempDir);
133
+ expect(trend.trend).toBe('stable');
134
+ });
135
+ });
136
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bundleAnalyzer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundleAnalyzer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bundleAnalyzer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,182 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { analyzeBundleImpact } from '../bundleAnalyzer.js';
3
+ describe('Bundle Analyzer', () => {
4
+ describe('analyzeBundleImpact', () => {
5
+ it('should return empty results for empty components', () => {
6
+ const result = analyzeBundleImpact([], [], '');
7
+ expect(result.components).toEqual([]);
8
+ expect(result.totalEstimatedSize).toBe(0);
9
+ expect(result.averageComponentSize).toBe(0);
10
+ expect(result.largestComponents).toEqual([]);
11
+ });
12
+ it('should analyze component with basic structure', () => {
13
+ const mockComponent = {
14
+ name: 'TestComponent',
15
+ file: '/test/Component.tsx',
16
+ code: 'function TestComponent() { return <div>Test</div>; }',
17
+ path: {
18
+ traverse: (visitor) => {
19
+ // Simulate no exports or imports
20
+ },
21
+ },
22
+ };
23
+ const result = analyzeBundleImpact([mockComponent], [], '');
24
+ expect(result.components.length).toBe(1);
25
+ expect(result.components[0].name).toBe('TestComponent');
26
+ expect(result.totalEstimatedSize).toBeGreaterThan(0);
27
+ });
28
+ it('should detect exports', () => {
29
+ let exportCount = 0;
30
+ const mockComponent = {
31
+ name: 'ExportComponent',
32
+ file: '/test/Export.tsx',
33
+ code: 'export function ExportComponent() { return <div>Export</div>; }',
34
+ path: {
35
+ traverse: (visitor) => {
36
+ if (visitor.ExportNamedDeclaration) {
37
+ visitor.ExportNamedDeclaration();
38
+ exportCount++;
39
+ }
40
+ },
41
+ },
42
+ };
43
+ const result = analyzeBundleImpact([mockComponent], [], '');
44
+ expect(result.components[0].exports).toBeGreaterThanOrEqual(0);
45
+ });
46
+ it('should detect dependencies', () => {
47
+ const mockComponent = {
48
+ name: 'ImportComponent',
49
+ file: '/test/Import.tsx',
50
+ code: 'import React from "react"; function ImportComponent() { return <div>Import</div>; }',
51
+ path: {
52
+ traverse: (visitor) => {
53
+ if (visitor.ImportDeclaration) {
54
+ visitor.ImportDeclaration();
55
+ }
56
+ },
57
+ },
58
+ };
59
+ const result = analyzeBundleImpact([mockComponent], [], '');
60
+ expect(result.components[0].dependencies).toBeGreaterThanOrEqual(0);
61
+ });
62
+ it('should calculate complexity', () => {
63
+ const mockComponent = {
64
+ name: 'ComplexComponent',
65
+ file: '/test/Complex.tsx',
66
+ code: `
67
+ function ComplexComponent() {
68
+ if (true) { return <div>A</div>; }
69
+ const fn = () => {};
70
+ return <div>B</div>;
71
+ }
72
+ `,
73
+ path: {
74
+ traverse: (visitor) => {
75
+ if (visitor.IfStatement)
76
+ visitor.IfStatement();
77
+ if (visitor.ArrowFunctionExpression)
78
+ visitor.ArrowFunctionExpression();
79
+ },
80
+ },
81
+ };
82
+ const result = analyzeBundleImpact([mockComponent], [], '');
83
+ expect(result.components[0].complexity).toBeGreaterThan(0);
84
+ });
85
+ it('should sort components by size', () => {
86
+ const smallComponent = {
87
+ name: 'Small',
88
+ file: '/test/Small.tsx',
89
+ code: 'x',
90
+ path: { traverse: () => { } },
91
+ };
92
+ const largeComponent = {
93
+ name: 'Large',
94
+ file: '/test/Large.tsx',
95
+ code: Array(100).fill('const x = 1;').join('\n'),
96
+ path: { traverse: () => { } },
97
+ };
98
+ const result = analyzeBundleImpact([smallComponent, largeComponent], [], '');
99
+ expect(result.largestComponents[0].name).toBe('Large');
100
+ });
101
+ it('should generate recommendations', () => {
102
+ const hugeComponent = {
103
+ name: 'Huge',
104
+ file: '/test/Huge.tsx',
105
+ code: Array(600).fill('const x = 1;').join('\n'),
106
+ path: {
107
+ traverse: (visitor) => {
108
+ // Simulate lots of imports
109
+ for (let i = 0; i < 20; i++) {
110
+ if (visitor.ImportDeclaration)
111
+ visitor.ImportDeclaration();
112
+ }
113
+ },
114
+ },
115
+ };
116
+ const result = analyzeBundleImpact([hugeComponent], [], '');
117
+ expect(result.recommendations).toBeDefined();
118
+ expect(Array.isArray(result.recommendations)).toBe(true);
119
+ });
120
+ it('should determine impact levels', () => {
121
+ const criticalComponent = {
122
+ name: 'Critical',
123
+ file: '/test/Critical.tsx',
124
+ code: Array(600).fill('const x = 1;').join('\n'),
125
+ path: {
126
+ traverse: (visitor) => {
127
+ // High complexity
128
+ for (let i = 0; i < 30; i++) {
129
+ if (visitor.IfStatement)
130
+ visitor.IfStatement();
131
+ if (visitor.FunctionDeclaration)
132
+ visitor.FunctionDeclaration();
133
+ if (visitor.ArrowFunctionExpression)
134
+ visitor.ArrowFunctionExpression();
135
+ }
136
+ },
137
+ },
138
+ };
139
+ const result = analyzeBundleImpact([criticalComponent], [], '');
140
+ const impact = result.components[0].impact;
141
+ expect(['low', 'medium', 'high', 'critical']).toContain(impact);
142
+ });
143
+ it('should handle component without code property', () => {
144
+ const componentNoCode = {
145
+ name: 'NoCode',
146
+ file: '/test/NoCode.tsx',
147
+ path: { traverse: () => { } },
148
+ };
149
+ const result = analyzeBundleImpact([componentNoCode], [], '');
150
+ expect(result.components.length).toBe(1);
151
+ expect(result.components[0].estimatedSize).toBeGreaterThan(0); // Base overhead
152
+ });
153
+ it('should handle dynamic imports', () => {
154
+ const dynamicComponent = {
155
+ name: 'Dynamic',
156
+ file: '/test/Dynamic.tsx',
157
+ code: 'const Lazy = React.lazy(() => import("./Lazy"));',
158
+ path: {
159
+ traverse: (visitor) => {
160
+ if (visitor.CallExpression) {
161
+ visitor.CallExpression({
162
+ node: { callee: { type: 'Import' } },
163
+ });
164
+ }
165
+ },
166
+ },
167
+ };
168
+ const result = analyzeBundleImpact([dynamicComponent], [], '');
169
+ expect(result.components[0].dependencies).toBeGreaterThanOrEqual(0);
170
+ });
171
+ });
172
+ describe('BundleAnalysisResult structure', () => {
173
+ it('should have correct result structure', () => {
174
+ const result = analyzeBundleImpact([], [], '');
175
+ expect(result).toHaveProperty('components');
176
+ expect(result).toHaveProperty('totalEstimatedSize');
177
+ expect(result).toHaveProperty('averageComponentSize');
178
+ expect(result).toHaveProperty('largestComponents');
179
+ expect(result).toHaveProperty('recommendations');
180
+ });
181
+ });
182
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=customRules.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customRules.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/customRules.test.ts"],"names":[],"mappings":""}