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,193 @@
1
+ import * as t from '@babel/types';
2
+ /**
3
+ * Detect state management anti-patterns (Redux, Zustand, MobX, etc.)
4
+ */
5
+ export function detectStateManagementIssues(component, filePath, sourceCode, config) {
6
+ if (!config.checkStateManagement)
7
+ return [];
8
+ const smells = [];
9
+ let useSelectorCount = 0;
10
+ let useStoreCount = 0;
11
+ const selectorLocations = [];
12
+ component.path.traverse({
13
+ CallExpression(path) {
14
+ const node = path.node;
15
+ const { callee } = node;
16
+ // Detect Redux useSelector
17
+ if (t.isIdentifier(callee) && callee.name === 'useSelector') {
18
+ useSelectorCount++;
19
+ const loc = node.loc;
20
+ selectorLocations.push({ line: loc?.start.line || 0, column: loc?.start.column || 0 });
21
+ // Check if selector is defined inline (anti-pattern)
22
+ if (node.arguments.length > 0 && t.isArrowFunctionExpression(node.arguments[0])) {
23
+ smells.push({
24
+ type: 'redux-in-render',
25
+ severity: 'warning',
26
+ message: `Inline selector in useSelector in "${component.name}" - will cause re-renders`,
27
+ file: filePath,
28
+ line: loc?.start.line || 0,
29
+ column: loc?.start.column || 0,
30
+ suggestion: 'Define selectors outside the component or use createSelector for memoization.',
31
+ });
32
+ }
33
+ }
34
+ // Detect Zustand useStore
35
+ if (t.isIdentifier(callee) && (callee.name === 'useStore' || callee.name.endsWith('Store'))) {
36
+ useStoreCount++;
37
+ const loc = node.loc;
38
+ // Check if selecting entire store (anti-pattern)
39
+ if (node.arguments.length === 0) {
40
+ smells.push({
41
+ type: 'excessive-redux-selectors',
42
+ severity: 'warning',
43
+ message: `Using entire store without selector in "${component.name}"`,
44
+ file: filePath,
45
+ line: loc?.start.line || 0,
46
+ column: loc?.start.column || 0,
47
+ suggestion: 'Use a selector function to subscribe only to needed state slices.',
48
+ });
49
+ }
50
+ }
51
+ // Detect useDispatch in render
52
+ if (t.isIdentifier(callee) && callee.name === 'useDispatch') {
53
+ // Check if dispatch is being called directly in render body
54
+ let currentPath = path.parentPath;
55
+ let inCallback = false;
56
+ while (currentPath) {
57
+ if (t.isArrowFunctionExpression(currentPath.node) ||
58
+ t.isFunctionExpression(currentPath.node)) {
59
+ inCallback = true;
60
+ break;
61
+ }
62
+ if (t.isJSXAttribute(currentPath.node)) {
63
+ inCallback = true;
64
+ break;
65
+ }
66
+ currentPath = currentPath.parentPath;
67
+ }
68
+ }
69
+ },
70
+ // Detect derived state stored in state (anti-pattern)
71
+ VariableDeclarator(path) {
72
+ const node = path.node;
73
+ // Check for useState with computed value from props or other state
74
+ if (t.isCallExpression(node.init) &&
75
+ t.isIdentifier(node.init.callee) &&
76
+ node.init.callee.name === 'useState') {
77
+ const args = node.init.arguments;
78
+ if (args.length > 0) {
79
+ // Check if initial value is derived from props
80
+ const initValue = args[0];
81
+ if (t.isIdentifier(initValue) && initValue.name.startsWith('props')) {
82
+ const loc = node.loc;
83
+ smells.push({
84
+ type: 'derived-state-in-state',
85
+ severity: 'warning',
86
+ message: `State initialized from props in "${component.name}" - may cause sync issues`,
87
+ file: filePath,
88
+ line: loc?.start.line || 0,
89
+ column: loc?.start.column || 0,
90
+ suggestion: 'Derive values directly from props instead of copying to state, or use a key to reset.',
91
+ });
92
+ }
93
+ }
94
+ }
95
+ },
96
+ });
97
+ // Check for too many selectors (component doing too much)
98
+ if (useSelectorCount > 3) {
99
+ const firstLoc = selectorLocations[0] || { line: component.path.node.loc?.start.line || 0, column: 0 };
100
+ smells.push({
101
+ type: 'excessive-redux-selectors',
102
+ severity: 'warning',
103
+ message: `Component "${component.name}" has ${useSelectorCount} useSelector calls - consider splitting`,
104
+ file: filePath,
105
+ line: firstLoc.line,
106
+ column: firstLoc.column,
107
+ suggestion: 'Create a combined selector or split into smaller connected components.',
108
+ });
109
+ }
110
+ // Detect state sync anti-patterns
111
+ detectStateSyncAntiPatterns(component, filePath, smells);
112
+ return smells;
113
+ }
114
+ /**
115
+ * Detect state synchronization anti-patterns
116
+ */
117
+ function detectStateSyncAntiPatterns(component, filePath, smells) {
118
+ // Check useEffect that sets state based on other state (state sync)
119
+ for (const effect of component.hooks.useEffect) {
120
+ if (!t.isArrowFunctionExpression(effect.arguments[0]) &&
121
+ !t.isFunctionExpression(effect.arguments[0])) {
122
+ continue;
123
+ }
124
+ const callback = effect.arguments[0];
125
+ let setsState = false;
126
+ let dependsOnState = false;
127
+ // Simple check: look for setState calls in the effect
128
+ if (t.isBlockStatement(callback.body)) {
129
+ const bodyStr = JSON.stringify(callback.body);
130
+ setsState = bodyStr.includes('"callee":{"name":"set') ||
131
+ bodyStr.includes('setState');
132
+ }
133
+ // Check dependency array for state variables
134
+ if (effect.arguments.length > 1 && t.isArrayExpression(effect.arguments[1])) {
135
+ const deps = effect.arguments[1].elements;
136
+ dependsOnState = deps.length > 0;
137
+ }
138
+ if (setsState && dependsOnState) {
139
+ const loc = effect.loc;
140
+ smells.push({
141
+ type: 'state-sync-anti-pattern',
142
+ severity: 'warning',
143
+ message: `useEffect syncs state from dependencies in "${component.name}" - potential infinite loop or unnecessary re-renders`,
144
+ file: filePath,
145
+ line: loc?.start.line || 0,
146
+ column: 0,
147
+ suggestion: 'Consider deriving the value during render instead of syncing in useEffect.',
148
+ });
149
+ }
150
+ }
151
+ }
152
+ /**
153
+ * Detect file-level state management patterns
154
+ */
155
+ export function detectStateManagementPatterns(sourceCode, filePath, config) {
156
+ if (!config.checkStateManagement)
157
+ return [];
158
+ const smells = [];
159
+ const lines = sourceCode.split('\n');
160
+ lines.forEach((line, index) => {
161
+ // Check for dispatch in JSX (anti-pattern - should be in handler)
162
+ if (line.includes('onClick={') && line.includes('dispatch(')) {
163
+ smells.push({
164
+ type: 'redux-in-render',
165
+ severity: 'info',
166
+ message: 'Inline dispatch in JSX - consider extracting to handler function',
167
+ file: filePath,
168
+ line: index + 1,
169
+ column: 0,
170
+ suggestion: 'Extract dispatch calls to named handler functions for better readability and debugging.',
171
+ codeSnippet: line.trim(),
172
+ });
173
+ }
174
+ // Check for createSelector without memoization
175
+ if (line.includes('useSelector(state =>') && !line.includes('createSelector')) {
176
+ // Check if it's a complex selector (has multiple property accesses)
177
+ const accessCount = (line.match(/\./g) || []).length;
178
+ if (accessCount > 2) {
179
+ smells.push({
180
+ type: 'redux-in-render',
181
+ severity: 'info',
182
+ message: 'Complex inline selector without createSelector',
183
+ file: filePath,
184
+ line: index + 1,
185
+ column: 0,
186
+ suggestion: 'Use createSelector from reselect for complex derived data to enable memoization.',
187
+ codeSnippet: line.trim(),
188
+ });
189
+ }
190
+ }
191
+ });
192
+ return smells;
193
+ }
@@ -0,0 +1,15 @@
1
+ import { ParsedComponent } from '../parser/index.js';
2
+ import { CodeSmell, DetectorConfig } from '../types/index.js';
3
+ /**
4
+ * Detect components that are likely hard to test or should have tests
5
+ */
6
+ export declare function detectTestingGaps(component: ParsedComponent, filePath: string, sourceCode: string, config: DetectorConfig): CodeSmell[];
7
+ /**
8
+ * Generate test file suggestions for components
9
+ */
10
+ export declare function generateTestSuggestions(component: ParsedComponent, filePath: string, sourceCode: string, config: DetectorConfig): string[];
11
+ /**
12
+ * Check if a test file exists for a component
13
+ */
14
+ export declare function hasTestFile(filePath: string): boolean;
15
+ //# sourceMappingURL=testingGaps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testingGaps.d.ts","sourceRoot":"","sources":["../../src/detectors/testingGaps.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE9D;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GACrB,SAAS,EAAE,CAgDb;AAsHD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GACrB,MAAM,EAAE,CAiCV;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUrD"}
@@ -0,0 +1,182 @@
1
+ import * as t from '@babel/types';
2
+ /**
3
+ * Detect components that are likely hard to test or should have tests
4
+ */
5
+ export function detectTestingGaps(component, filePath, sourceCode, config) {
6
+ if (!config.checkTestingGaps)
7
+ return [];
8
+ const smells = [];
9
+ // Calculate testability metrics
10
+ const metrics = calculateTestabilityMetrics(component, sourceCode);
11
+ // Flag complex components without apparent tests
12
+ if (metrics.complexityScore > config.testComplexityThreshold) {
13
+ smells.push({
14
+ type: 'complex-untestable',
15
+ severity: 'info',
16
+ message: `Component "${component.name}" has high complexity (${metrics.complexityScore}) - ensure adequate test coverage`,
17
+ file: filePath,
18
+ line: component.path.node.loc?.start.line || 0,
19
+ column: 0,
20
+ suggestion: `This component has ${metrics.hooks} hooks, ${metrics.effects} effects, and ${metrics.conditionals} conditionals. Consider splitting or ensuring thorough test coverage.`,
21
+ });
22
+ }
23
+ // Flag components with many side effects
24
+ if (metrics.sideEffects > 3) {
25
+ smells.push({
26
+ type: 'side-effect-heavy',
27
+ severity: 'warning',
28
+ message: `Component "${component.name}" has ${metrics.sideEffects} side effects - difficult to test`,
29
+ file: filePath,
30
+ line: component.path.node.loc?.start.line || 0,
31
+ column: 0,
32
+ suggestion: 'Extract side effects into custom hooks or separate functions for easier testing and mocking.',
33
+ });
34
+ }
35
+ // Flag tightly coupled components
36
+ if (metrics.externalDependencies > 5) {
37
+ smells.push({
38
+ type: 'tightly-coupled',
39
+ severity: 'info',
40
+ message: `Component "${component.name}" has ${metrics.externalDependencies} external dependencies`,
41
+ file: filePath,
42
+ line: component.path.node.loc?.start.line || 0,
43
+ column: 0,
44
+ suggestion: 'Consider dependency injection or using a container pattern to make testing easier.',
45
+ });
46
+ }
47
+ return smells;
48
+ }
49
+ /**
50
+ * Calculate testability metrics for a component
51
+ */
52
+ function calculateTestabilityMetrics(component, sourceCode) {
53
+ let conditionals = 0;
54
+ let sideEffects = 0;
55
+ let externalDependencies = 0;
56
+ let callbacks = 0;
57
+ component.path.traverse({
58
+ // Count conditionals
59
+ IfStatement() {
60
+ conditionals++;
61
+ },
62
+ ConditionalExpression() {
63
+ conditionals++;
64
+ },
65
+ SwitchStatement() {
66
+ conditionals++;
67
+ },
68
+ LogicalExpression(path) {
69
+ const node = path.node;
70
+ if (node.operator === '&&' || node.operator === '||') {
71
+ conditionals++;
72
+ }
73
+ },
74
+ // Count callbacks/event handlers
75
+ ArrowFunctionExpression() {
76
+ callbacks++;
77
+ },
78
+ FunctionExpression() {
79
+ callbacks++;
80
+ },
81
+ // Count side effects
82
+ CallExpression(path) {
83
+ const node = path.node;
84
+ const { callee } = node;
85
+ if (t.isIdentifier(callee)) {
86
+ // Side effect indicators
87
+ const sideEffectFns = [
88
+ 'fetch', 'axios', 'console', 'localStorage', 'sessionStorage',
89
+ 'setInterval', 'setTimeout', 'addEventListener', 'removeEventListener',
90
+ 'dispatch', 'navigate', 'push', 'replace',
91
+ ];
92
+ if (sideEffectFns.some(fn => callee.name.includes(fn))) {
93
+ sideEffects++;
94
+ }
95
+ // External dependencies (hooks that likely need mocking)
96
+ const externalHooks = [
97
+ 'useQuery', 'useMutation', 'useSWR', 'useRouter', 'useNavigate',
98
+ 'useLocation', 'useParams', 'useSelector', 'useDispatch',
99
+ 'useAuth', 'useUser', 'useApi',
100
+ ];
101
+ if (externalHooks.some(hook => callee.name === hook || callee.name.includes(hook))) {
102
+ externalDependencies++;
103
+ }
104
+ }
105
+ // Member expression side effects (e.g., router.push, api.get)
106
+ if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
107
+ const methodName = callee.property.name;
108
+ const sideEffectMethods = ['push', 'replace', 'get', 'post', 'put', 'delete', 'fetch'];
109
+ if (sideEffectMethods.includes(methodName)) {
110
+ sideEffects++;
111
+ }
112
+ }
113
+ },
114
+ });
115
+ const hooks = component.hooks.useState.length +
116
+ component.hooks.useEffect.length +
117
+ component.hooks.useMemo.length +
118
+ component.hooks.useCallback.length +
119
+ component.hooks.useRef.length;
120
+ const effects = component.hooks.useEffect.length;
121
+ // Calculate overall complexity score
122
+ const complexityScore = hooks * 1 +
123
+ effects * 2 +
124
+ conditionals * 1.5 +
125
+ sideEffects * 2 +
126
+ externalDependencies * 1.5 +
127
+ callbacks * 0.5;
128
+ return {
129
+ complexityScore: Math.round(complexityScore),
130
+ hooks,
131
+ effects,
132
+ conditionals,
133
+ sideEffects,
134
+ externalDependencies,
135
+ callbacks,
136
+ };
137
+ }
138
+ /**
139
+ * Generate test file suggestions for components
140
+ */
141
+ export function generateTestSuggestions(component, filePath, sourceCode, config) {
142
+ const suggestions = [];
143
+ const metrics = calculateTestabilityMetrics(component, sourceCode);
144
+ // Basic render test
145
+ suggestions.push(`it('should render ${component.name} without crashing', () => {
146
+ render(<${component.name} />);
147
+ });`);
148
+ // Props tests if component has props
149
+ if (component.path.node.type === 'FunctionDeclaration' ||
150
+ (component.path.node.type === 'VariableDeclarator' &&
151
+ t.isArrowFunctionExpression(component.path.node.init))) {
152
+ suggestions.push(`it('should pass props correctly', () => {
153
+ // TODO: Add prop tests for ${component.name}
154
+ });`);
155
+ }
156
+ // Effect tests
157
+ if (metrics.effects > 0) {
158
+ suggestions.push(`it('should handle side effects in ${component.name}', async () => {
159
+ // TODO: Test useEffect behavior
160
+ });`);
161
+ }
162
+ // Interaction tests
163
+ if (metrics.callbacks > 0) {
164
+ suggestions.push(`it('should handle user interactions', async () => {
165
+ // TODO: Add interaction tests for ${component.name}
166
+ });`);
167
+ }
168
+ return suggestions;
169
+ }
170
+ /**
171
+ * Check if a test file exists for a component
172
+ */
173
+ export function hasTestFile(filePath) {
174
+ const testPatterns = [
175
+ filePath.replace(/\.(tsx?|jsx?)$/, '.test.$1'),
176
+ filePath.replace(/\.(tsx?|jsx?)$/, '.spec.$1'),
177
+ filePath.replace(/\/([^/]+)\.(tsx?|jsx?)$/, '/__tests__/$1.$2'),
178
+ ];
179
+ // Note: This is a simple pattern check. In real usage,
180
+ // you'd want to actually check the file system
181
+ return false; // Placeholder - actual implementation would check fs
182
+ }
@@ -0,0 +1,37 @@
1
+ import { AnalysisResult, CodeSmell } from './types/index.js';
2
+ export interface DocGeneratorOptions {
3
+ format: 'markdown' | 'html' | 'json';
4
+ outputDir?: string;
5
+ includeSmells?: boolean;
6
+ includeMetrics?: boolean;
7
+ groupByFolder?: boolean;
8
+ }
9
+ export interface ComponentDoc {
10
+ name: string;
11
+ file: string;
12
+ relativePath: string;
13
+ lineCount: number;
14
+ props: string[];
15
+ hooks: {
16
+ useState: number;
17
+ useEffect: number;
18
+ useMemo: number;
19
+ useCallback: number;
20
+ useRef: number;
21
+ custom: string[];
22
+ };
23
+ smells: CodeSmell[];
24
+ metrics: {
25
+ complexity: string;
26
+ maintainability: string;
27
+ };
28
+ }
29
+ /**
30
+ * Generate documentation from component analysis
31
+ */
32
+ export declare function generateComponentDocs(result: AnalysisResult, rootDir: string, options?: DocGeneratorOptions): Promise<string>;
33
+ /**
34
+ * Write documentation to file
35
+ */
36
+ export declare function writeComponentDocs(result: AnalysisResult, rootDir: string, options: DocGeneratorOptions): Promise<string>;
37
+ //# sourceMappingURL=docGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docGenerator.d.ts","sourceRoot":"","sources":["../src/docGenerator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAA+B,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE1F,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IACF,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,mBAA4C,GACpD,OAAO,CAAC,MAAM,CAAC,CAYjB;AAqTD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAYjB"}