react-anti-pattern-sniffer 0.1.0
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/.snifferrc.json +29 -0
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/bin/react-sniff.js +3 -0
- package/dist/src/cli/arg-parser.d.ts +10 -0
- package/dist/src/cli/arg-parser.d.ts.map +1 -0
- package/dist/src/cli/arg-parser.js +81 -0
- package/dist/src/cli/arg-parser.js.map +1 -0
- package/dist/src/cli/config-loader.d.ts +11 -0
- package/dist/src/cli/config-loader.d.ts.map +1 -0
- package/dist/src/cli/config-loader.js +140 -0
- package/dist/src/cli/config-loader.js.map +1 -0
- package/dist/src/cli/help.d.ts +3 -0
- package/dist/src/cli/help.d.ts.map +1 -0
- package/dist/src/cli/help.js +59 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +107 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/core/file-discoverer.d.ts +8 -0
- package/dist/src/core/file-discoverer.d.ts.map +1 -0
- package/dist/src/core/file-discoverer.js +151 -0
- package/dist/src/core/file-discoverer.js.map +1 -0
- package/dist/src/core/orchestrator.d.ts +13 -0
- package/dist/src/core/orchestrator.d.ts.map +1 -0
- package/dist/src/core/orchestrator.js +176 -0
- package/dist/src/core/orchestrator.js.map +1 -0
- package/dist/src/core/sniffer-ignore.d.ts +25 -0
- package/dist/src/core/sniffer-ignore.d.ts.map +1 -0
- package/dist/src/core/sniffer-ignore.js +91 -0
- package/dist/src/core/sniffer-ignore.js.map +1 -0
- package/dist/src/core/sniffer-registry.d.ts +8 -0
- package/dist/src/core/sniffer-registry.d.ts.map +1 -0
- package/dist/src/core/sniffer-registry.js +64 -0
- package/dist/src/core/sniffer-registry.js.map +1 -0
- package/dist/src/core/worker-pool.d.ts +27 -0
- package/dist/src/core/worker-pool.d.ts.map +1 -0
- package/dist/src/core/worker-pool.js +176 -0
- package/dist/src/core/worker-pool.js.map +1 -0
- package/dist/src/core/worker-runner.d.ts +2 -0
- package/dist/src/core/worker-runner.d.ts.map +1 -0
- package/dist/src/core/worker-runner.js +52 -0
- package/dist/src/core/worker-runner.js.map +1 -0
- package/dist/src/output/formatter.d.ts +3 -0
- package/dist/src/output/formatter.d.ts.map +1 -0
- package/dist/src/output/formatter.js +13 -0
- package/dist/src/output/formatter.js.map +1 -0
- package/dist/src/output/json-renderer.d.ts +3 -0
- package/dist/src/output/json-renderer.d.ts.map +1 -0
- package/dist/src/output/json-renderer.js +49 -0
- package/dist/src/output/json-renderer.js.map +1 -0
- package/dist/src/output/markdown-renderer.d.ts +3 -0
- package/dist/src/output/markdown-renderer.d.ts.map +1 -0
- package/dist/src/output/markdown-renderer.js +70 -0
- package/dist/src/output/markdown-renderer.js.map +1 -0
- package/dist/src/plugins/plugin-loader.d.ts +7 -0
- package/dist/src/plugins/plugin-loader.d.ts.map +1 -0
- package/dist/src/plugins/plugin-loader.js +47 -0
- package/dist/src/plugins/plugin-loader.js.map +1 -0
- package/dist/src/plugins/plugin-sandbox.d.ts +3 -0
- package/dist/src/plugins/plugin-sandbox.d.ts.map +1 -0
- package/dist/src/plugins/plugin-sandbox.js +105 -0
- package/dist/src/plugins/plugin-sandbox.js.map +1 -0
- package/dist/src/plugins/plugin-validator.d.ts +14 -0
- package/dist/src/plugins/plugin-validator.d.ts.map +1 -0
- package/dist/src/plugins/plugin-validator.js +92 -0
- package/dist/src/plugins/plugin-validator.js.map +1 -0
- package/dist/src/sniffers/god-hook-sniffer.d.ts +12 -0
- package/dist/src/sniffers/god-hook-sniffer.d.ts.map +1 -0
- package/dist/src/sniffers/god-hook-sniffer.js +109 -0
- package/dist/src/sniffers/god-hook-sniffer.js.map +1 -0
- package/dist/src/sniffers/prop-drilling-sniffer.d.ts +5 -0
- package/dist/src/sniffers/prop-drilling-sniffer.d.ts.map +1 -0
- package/dist/src/sniffers/prop-drilling-sniffer.js +145 -0
- package/dist/src/sniffers/prop-drilling-sniffer.js.map +1 -0
- package/dist/src/sniffers/prop-explosion-sniffer.d.ts +4 -0
- package/dist/src/sniffers/prop-explosion-sniffer.d.ts.map +1 -0
- package/dist/src/sniffers/prop-explosion-sniffer.js +134 -0
- package/dist/src/sniffers/prop-explosion-sniffer.js.map +1 -0
- package/dist/src/sniffers/sniffer-interface.d.ts +88 -0
- package/dist/src/sniffers/sniffer-interface.d.ts.map +1 -0
- package/dist/src/sniffers/sniffer-interface.js +18 -0
- package/dist/src/sniffers/sniffer-interface.js.map +1 -0
- package/dist/src/tui/interactive-viewer.d.ts +7 -0
- package/dist/src/tui/interactive-viewer.d.ts.map +1 -0
- package/dist/src/tui/interactive-viewer.js +453 -0
- package/dist/src/tui/interactive-viewer.js.map +1 -0
- package/dist/src/utils/logger.d.ts +11 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +90 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/regex-helpers.d.ts +53 -0
- package/dist/src/utils/regex-helpers.d.ts.map +1 -0
- package/dist/src/utils/regex-helpers.js +275 -0
- package/dist/src/utils/regex-helpers.js.map +1 -0
- package/package.json +40 -0
package/.snifferrc.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["**/*.{jsx,tsx}"],
|
|
3
|
+
"exclude": ["node_modules", "dist", "build", "**/*.test.*", "**/*.spec.*"],
|
|
4
|
+
"parallel": true,
|
|
5
|
+
"maxWorkers": 4,
|
|
6
|
+
"timeoutMs": 30000,
|
|
7
|
+
"outputFormat": "markdown",
|
|
8
|
+
"outputPath": null,
|
|
9
|
+
"sniffers": {
|
|
10
|
+
"prop-explosion": {
|
|
11
|
+
"enabled": true,
|
|
12
|
+
"threshold": 7,
|
|
13
|
+
"severity": "warning"
|
|
14
|
+
},
|
|
15
|
+
"god-hook": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"maxUseState": 4,
|
|
18
|
+
"maxUseEffect": 3,
|
|
19
|
+
"maxTotalHooks": 10,
|
|
20
|
+
"severity": "warning"
|
|
21
|
+
},
|
|
22
|
+
"prop-drilling": {
|
|
23
|
+
"enabled": true,
|
|
24
|
+
"severity": "warning",
|
|
25
|
+
"whitelistedProps": ["className", "style", "children", "key", "ref", "id", "data-testid"]
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"plugins": []
|
|
29
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 react-anti-pattern-sniffer contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# react-anti-pattern-sniffer
|
|
2
|
+
|
|
3
|
+
Zero-dependency CLI tool that detects React anti-patterns in your codebase using regex-based heuristics and parallel worker threads.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Zero runtime dependencies** — uses only Node.js built-ins
|
|
8
|
+
- **Three built-in sniffers**: prop explosion, god hook, prop drilling
|
|
9
|
+
- **Interactive TUI** — browse results, copy as AI prompt, ignore components
|
|
10
|
+
- **Batch mode** — focus on the first N issues at a time
|
|
11
|
+
- **Parallel execution** via `worker_threads` for fast analysis
|
|
12
|
+
- **Plugin system** — register custom sniffers with security validation
|
|
13
|
+
- **Markdown & JSON output** — human-readable reports or structured data
|
|
14
|
+
- **`.snifferignore`** — persistently ignore specific components
|
|
15
|
+
- **Git hook friendly** — exit codes for CI/CD and husky integration
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install --save-dev react-anti-pattern-sniffer
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Scan current directory
|
|
27
|
+
npx react-sniff
|
|
28
|
+
|
|
29
|
+
# Short alias
|
|
30
|
+
npx ras
|
|
31
|
+
|
|
32
|
+
# Interactive mode — browse results in a TUI
|
|
33
|
+
ras -i
|
|
34
|
+
|
|
35
|
+
# Batch mode — show only the first 10 issues
|
|
36
|
+
ras -b 10
|
|
37
|
+
|
|
38
|
+
# Interactive + batch — browse first 20 issues in TUI
|
|
39
|
+
ras -i -b 20
|
|
40
|
+
|
|
41
|
+
# Output JSON report
|
|
42
|
+
react-sniff --format json --output report.json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## CLI Options
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Usage: react-sniff [options] [dir]
|
|
49
|
+
|
|
50
|
+
Options:
|
|
51
|
+
-d, --dir <path> Target directory (default: cwd)
|
|
52
|
+
-c, --config <path> Config file path (default: .snifferrc.json)
|
|
53
|
+
-s, --sniffers <list> Comma-separated sniffers to run
|
|
54
|
+
-f, --format <type> Output format: markdown | json
|
|
55
|
+
-o, --output <path> Write report to file
|
|
56
|
+
-w, --workers <n> Worker thread count (default: 4)
|
|
57
|
+
-i, --interactive Launch interactive TUI to browse results
|
|
58
|
+
-b, --batch <n> Limit output to the first N issues (default: 10)
|
|
59
|
+
--parallel Enable parallel execution (default)
|
|
60
|
+
--no-parallel Run sequentially
|
|
61
|
+
--verbose Debug output
|
|
62
|
+
-q, --quiet Exit code only, no output
|
|
63
|
+
-h, --help Show help
|
|
64
|
+
-v, --version Show version
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Interactive Mode
|
|
68
|
+
|
|
69
|
+
Launch with `ras -i` to get a keyboard-driven TUI for browsing results, organized by code smell:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
React Anti-Pattern Sniffer │ 12/42 issues shown • 85 files scanned
|
|
73
|
+
|
|
74
|
+
▸ ▼ Prop Explosion (6 issues)
|
|
75
|
+
● src/components/UserProfile.tsx:15 UserProfile — has 10 props
|
|
76
|
+
● src/components/Dashboard.tsx:42 Dashboard — has 8 props
|
|
77
|
+
● src/pages/Settings.tsx:23 SettingsForm — has 9 props
|
|
78
|
+
|
|
79
|
+
▶ God Hook (3 issues)
|
|
80
|
+
|
|
81
|
+
▶ Prop Drilling (3 issues)
|
|
82
|
+
|
|
83
|
+
[c]opy as prompt [a]ll as md [x] ignore [f]ilter [d]etails [q]uit
|
|
84
|
+
↑/↓ navigate ←/→ or enter collapse/expand tab next group
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Keyboard Controls
|
|
88
|
+
|
|
89
|
+
| Key | Action |
|
|
90
|
+
|-----|--------|
|
|
91
|
+
| `↑`/`↓` or `j`/`k` | Navigate between items |
|
|
92
|
+
| `Enter`/`→`/`←` | Expand/collapse smell groups |
|
|
93
|
+
| `Tab` | Jump to next group |
|
|
94
|
+
| `d` | Toggle details (show suggestion + metadata) |
|
|
95
|
+
| `c` | Copy current issue as AI prompt (markdown to clipboard) |
|
|
96
|
+
| `a` | Copy all visible issues as markdown |
|
|
97
|
+
| `x` | Ignore component (adds to `.snifferignore`) |
|
|
98
|
+
| `f` | Cycle filter by smell type |
|
|
99
|
+
| `p` | Print current issue markdown (clipboard fallback) |
|
|
100
|
+
| `q` | Quit |
|
|
101
|
+
|
|
102
|
+
## Batch Mode
|
|
103
|
+
|
|
104
|
+
Use `-b` to limit output to the first N issues. Works in both interactive and non-interactive mode:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Show first 5 issues as markdown
|
|
108
|
+
ras -b 5
|
|
109
|
+
|
|
110
|
+
# Browse first 20 issues in TUI
|
|
111
|
+
ras -i -b 20
|
|
112
|
+
|
|
113
|
+
# Only god-hook issues, first 3
|
|
114
|
+
ras -b 3 -s god-hook
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Sniffers
|
|
118
|
+
|
|
119
|
+
### Prop Explosion
|
|
120
|
+
|
|
121
|
+
Detects components with too many props, suggesting decomposition.
|
|
122
|
+
|
|
123
|
+
```jsx
|
|
124
|
+
// ⚠ Flagged: 10 props exceeds threshold of 7
|
|
125
|
+
const UserProfile = ({ firstName, lastName, email, phone, avatar, address, role, permissions, isActive, onUpdate }) => { ... }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Fix:** Group related props into objects, use Context, or split the component.
|
|
129
|
+
|
|
130
|
+
### God Hook
|
|
131
|
+
|
|
132
|
+
Detects custom hooks with excessive state, effects, or responsibilities.
|
|
133
|
+
|
|
134
|
+
```jsx
|
|
135
|
+
// ⚠ Flagged: 6 useState, 4 useEffect
|
|
136
|
+
function useUserDashboard(userId) {
|
|
137
|
+
const [user, setUser] = useState(null);
|
|
138
|
+
const [posts, setPosts] = useState([]);
|
|
139
|
+
// ...6 more useState, 4 useEffect
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Fix:** Split into focused sub-hooks (`useUser`, `usePosts`, `useNotifications`).
|
|
144
|
+
|
|
145
|
+
### Prop Drilling
|
|
146
|
+
|
|
147
|
+
Detects props that are received but only forwarded to children without local use.
|
|
148
|
+
|
|
149
|
+
```jsx
|
|
150
|
+
// ⚠ Flagged: theme, locale passed through without use
|
|
151
|
+
const Wrapper = ({ theme, locale, children }) => (
|
|
152
|
+
<Header theme={theme} locale={locale} />
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Fix:** Use React Context or component composition.
|
|
157
|
+
|
|
158
|
+
## `.snifferignore`
|
|
159
|
+
|
|
160
|
+
Create a `.snifferignore` file in your project root to persistently skip specific components or files. This file is automatically updated when you press `x` in interactive mode.
|
|
161
|
+
|
|
162
|
+
```gitignore
|
|
163
|
+
# Ignore a specific component for a specific sniffer
|
|
164
|
+
src/components/UserProfile.tsx:UserProfile # prop-explosion
|
|
165
|
+
|
|
166
|
+
# Ignore all issues in a file
|
|
167
|
+
src/legacy/OldDashboard.tsx
|
|
168
|
+
|
|
169
|
+
# Ignore a hook
|
|
170
|
+
src/hooks/useMonolith.ts:useMonolith # god-hook
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Format: `<file-path>:<ComponentName> # <sniffer-name>`
|
|
174
|
+
|
|
175
|
+
- `ComponentName` and `# sniffer-name` are optional
|
|
176
|
+
- Lines starting with `#` are comments
|
|
177
|
+
|
|
178
|
+
## Configuration
|
|
179
|
+
|
|
180
|
+
Create `.snifferrc.json` in your project root:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"include": ["**/*.{jsx,tsx}"],
|
|
185
|
+
"exclude": ["node_modules", "dist", "build", "**/*.test.*"],
|
|
186
|
+
"parallel": true,
|
|
187
|
+
"maxWorkers": 4,
|
|
188
|
+
"timeoutMs": 30000,
|
|
189
|
+
"outputFormat": "markdown",
|
|
190
|
+
"sniffers": {
|
|
191
|
+
"prop-explosion": {
|
|
192
|
+
"enabled": true,
|
|
193
|
+
"threshold": 7,
|
|
194
|
+
"severity": "warning"
|
|
195
|
+
},
|
|
196
|
+
"god-hook": {
|
|
197
|
+
"enabled": true,
|
|
198
|
+
"maxUseState": 4,
|
|
199
|
+
"maxUseEffect": 3,
|
|
200
|
+
"maxTotalHooks": 10,
|
|
201
|
+
"severity": "warning"
|
|
202
|
+
},
|
|
203
|
+
"prop-drilling": {
|
|
204
|
+
"enabled": true,
|
|
205
|
+
"severity": "warning",
|
|
206
|
+
"whitelistedProps": ["className", "style", "children", "key", "ref", "id"]
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"plugins": []
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Git Hooks (Husky)
|
|
214
|
+
|
|
215
|
+
Add to `.husky/pre-commit`:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
npx react-sniff --quiet
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Exit codes:
|
|
222
|
+
- `0` — no issues found
|
|
223
|
+
- `1` — anti-patterns detected
|
|
224
|
+
- `2` — configuration or runtime error
|
|
225
|
+
|
|
226
|
+
## Custom Plugins
|
|
227
|
+
|
|
228
|
+
Create a sniffer module:
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
// my-sniffer.js
|
|
232
|
+
module.exports = {
|
|
233
|
+
name: 'no-inline-styles',
|
|
234
|
+
description: 'Detects inline style objects in JSX',
|
|
235
|
+
meta: {
|
|
236
|
+
name: 'no-inline-styles',
|
|
237
|
+
description: 'Flags inline style={{}} usage',
|
|
238
|
+
category: 'custom',
|
|
239
|
+
severity: 'info',
|
|
240
|
+
defaultConfig: {},
|
|
241
|
+
},
|
|
242
|
+
detect(fileContent, filePath, config) {
|
|
243
|
+
const detections = [];
|
|
244
|
+
// Your detection logic here
|
|
245
|
+
// Return array of Detection objects:
|
|
246
|
+
// { snifferName, filePath, line, column, message, severity, suggestion }
|
|
247
|
+
return detections;
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Register in `.snifferrc.json`:
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"plugins": [
|
|
257
|
+
{ "path": "./my-sniffer.js" }
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Plugin Security
|
|
263
|
+
|
|
264
|
+
Plugins are validated before execution:
|
|
265
|
+
1. **Schema check** — must export `name`, `description`, `meta`, `detect`
|
|
266
|
+
2. **Security scan** — warns on `eval()`, `Function()`, `child_process`, etc.
|
|
267
|
+
3. **Smoke test** — must return an array on empty input
|
|
268
|
+
4. **Worker isolation** — runs in separate V8 isolate with memory limits
|
|
269
|
+
5. **Timeout** — killed after `timeoutMs` (default 30s)
|
|
270
|
+
|
|
271
|
+
## Programmatic API
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
const { orchestrate } = require('react-anti-pattern-sniffer');
|
|
275
|
+
|
|
276
|
+
const result = await orchestrate(config, targetDir);
|
|
277
|
+
console.log(result.output); // formatted report
|
|
278
|
+
console.log(result.issueCount); // number of issues
|
|
279
|
+
console.log(result.fileCount); // files scanned
|
|
280
|
+
console.log(result.grouped); // Map<string, SnifferResult[]>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Requirements
|
|
284
|
+
|
|
285
|
+
- Node.js >= 20.0.0
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ParsedArgs {
|
|
2
|
+
flags: Record<string, string | boolean>;
|
|
3
|
+
positionals: string[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Parse CLI arguments with zero dependencies.
|
|
7
|
+
* Supports: --key=value, --key value, --boolean, --no-x (negation), -short aliases
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
10
|
+
//# sourceMappingURL=arg-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-parser.d.ts","sourceRoot":"","sources":["../../../src/cli/arg-parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IACxC,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAgBD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAyDpD"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseArgs = parseArgs;
|
|
4
|
+
const SHORT_ALIASES = {
|
|
5
|
+
'-c': '--config',
|
|
6
|
+
'-d': '--dir',
|
|
7
|
+
'-f': '--format',
|
|
8
|
+
'-o': '--output',
|
|
9
|
+
'-s': '--sniffers',
|
|
10
|
+
'-w': '--workers',
|
|
11
|
+
'-h': '--help',
|
|
12
|
+
'-v': '--version',
|
|
13
|
+
'-q': '--quiet',
|
|
14
|
+
'-i': '--interactive',
|
|
15
|
+
'-b': '--batch',
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Parse CLI arguments with zero dependencies.
|
|
19
|
+
* Supports: --key=value, --key value, --boolean, --no-x (negation), -short aliases
|
|
20
|
+
*/
|
|
21
|
+
function parseArgs(argv) {
|
|
22
|
+
const flags = {};
|
|
23
|
+
const positionals = [];
|
|
24
|
+
let i = 0;
|
|
25
|
+
while (i < argv.length) {
|
|
26
|
+
let token = argv[i];
|
|
27
|
+
// Expand short aliases
|
|
28
|
+
if (SHORT_ALIASES[token]) {
|
|
29
|
+
token = SHORT_ALIASES[token];
|
|
30
|
+
}
|
|
31
|
+
if (token.startsWith('--no-')) {
|
|
32
|
+
// Negation: --no-parallel => parallel = false
|
|
33
|
+
const key = token.substring(5);
|
|
34
|
+
flags[key] = false;
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
else if (token.startsWith('--')) {
|
|
38
|
+
const eqIndex = token.indexOf('=');
|
|
39
|
+
if (eqIndex !== -1) {
|
|
40
|
+
// --key=value
|
|
41
|
+
const key = token.substring(2, eqIndex);
|
|
42
|
+
const value = token.substring(eqIndex + 1);
|
|
43
|
+
flags[key] = value;
|
|
44
|
+
i++;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const key = token.substring(2);
|
|
48
|
+
const next = argv[i + 1];
|
|
49
|
+
// If next token exists and doesn't start with -, it's the value
|
|
50
|
+
if (next !== undefined && !next.startsWith('-')) {
|
|
51
|
+
flags[key] = next;
|
|
52
|
+
i += 2;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Boolean flag
|
|
56
|
+
flags[key] = true;
|
|
57
|
+
i++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (token.startsWith('-') && token.length === 2) {
|
|
62
|
+
// Single-char flag not in aliases
|
|
63
|
+
const key = token.substring(1);
|
|
64
|
+
const next = argv[i + 1];
|
|
65
|
+
if (next !== undefined && !next.startsWith('-')) {
|
|
66
|
+
flags[key] = next;
|
|
67
|
+
i += 2;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
flags[key] = true;
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
positionals.push(token);
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { flags, positionals };
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=arg-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-parser.js","sourceRoot":"","sources":["../../../src/cli/arg-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SnifferConfig } from '../sniffers/sniffer-interface.js';
|
|
2
|
+
declare const DEFAULT_CONFIG: SnifferConfig;
|
|
3
|
+
/**
|
|
4
|
+
* Load configuration by merging:
|
|
5
|
+
* 1. Built-in defaults
|
|
6
|
+
* 2. Config file (.snifferrc.json or --config path)
|
|
7
|
+
* 3. CLI flags
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadConfig(flags: Record<string, string | boolean>): SnifferConfig;
|
|
10
|
+
export { DEFAULT_CONFIG };
|
|
11
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/cli/config-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,QAAA,MAAM,cAAc,EAAE,aAkBrB,CAAC;AAoDF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,aAAa,CA6EjF;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_CONFIG = void 0;
|
|
4
|
+
exports.loadConfig = loadConfig;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
include: ['**/*.{jsx,tsx}'],
|
|
9
|
+
exclude: ['node_modules', 'dist', 'build', '**/*.test.*', '**/*.spec.*'],
|
|
10
|
+
parallel: true,
|
|
11
|
+
maxWorkers: 4,
|
|
12
|
+
timeoutMs: 30000,
|
|
13
|
+
outputFormat: 'markdown',
|
|
14
|
+
outputPath: null,
|
|
15
|
+
sniffers: {
|
|
16
|
+
'prop-explosion': { enabled: true, threshold: 7, severity: 'warning' },
|
|
17
|
+
'god-hook': { enabled: true, maxUseState: 4, maxUseEffect: 3, maxTotalHooks: 10, severity: 'warning' },
|
|
18
|
+
'prop-drilling': {
|
|
19
|
+
enabled: true,
|
|
20
|
+
severity: 'warning',
|
|
21
|
+
whitelistedProps: ['className', 'style', 'children', 'key', 'ref', 'id', 'data-testid'],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
plugins: [],
|
|
25
|
+
};
|
|
26
|
+
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
27
|
+
/**
|
|
28
|
+
* Deep merge two objects. Source values override target values.
|
|
29
|
+
* Arrays are replaced, not merged.
|
|
30
|
+
*/
|
|
31
|
+
function deepMerge(target, source) {
|
|
32
|
+
const result = { ...target };
|
|
33
|
+
for (const key of Object.keys(source)) {
|
|
34
|
+
const sourceVal = source[key];
|
|
35
|
+
const targetVal = target[key];
|
|
36
|
+
if (sourceVal !== null &&
|
|
37
|
+
typeof sourceVal === 'object' &&
|
|
38
|
+
!Array.isArray(sourceVal) &&
|
|
39
|
+
targetVal !== null &&
|
|
40
|
+
typeof targetVal === 'object' &&
|
|
41
|
+
!Array.isArray(targetVal)) {
|
|
42
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
result[key] = sourceVal;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Find a config file by searching common names in the given directory.
|
|
52
|
+
*/
|
|
53
|
+
function findConfigFile(dir) {
|
|
54
|
+
const candidates = [
|
|
55
|
+
'.snifferrc.json',
|
|
56
|
+
'.snifferrc.js',
|
|
57
|
+
'sniffer.config.json',
|
|
58
|
+
'sniffer.config.js',
|
|
59
|
+
];
|
|
60
|
+
for (const name of candidates) {
|
|
61
|
+
const fullPath = (0, node_path_1.join)(dir, name);
|
|
62
|
+
if ((0, node_fs_1.existsSync)(fullPath))
|
|
63
|
+
return fullPath;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Load configuration by merging:
|
|
69
|
+
* 1. Built-in defaults
|
|
70
|
+
* 2. Config file (.snifferrc.json or --config path)
|
|
71
|
+
* 3. CLI flags
|
|
72
|
+
*/
|
|
73
|
+
function loadConfig(flags) {
|
|
74
|
+
let fileConfig = {};
|
|
75
|
+
// Determine config file path
|
|
76
|
+
const configPath = typeof flags.config === 'string'
|
|
77
|
+
? (0, node_path_1.resolve)(flags.config)
|
|
78
|
+
: findConfigFile(process.cwd());
|
|
79
|
+
if (configPath) {
|
|
80
|
+
if (!(0, node_fs_1.existsSync)(configPath)) {
|
|
81
|
+
if (flags.config) {
|
|
82
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
try {
|
|
87
|
+
const raw = (0, node_fs_1.readFileSync)(configPath, 'utf8');
|
|
88
|
+
fileConfig = JSON.parse(raw);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
throw new Error(`Failed to parse config file ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Merge defaults + file config
|
|
96
|
+
const merged = deepMerge(DEFAULT_CONFIG, fileConfig);
|
|
97
|
+
// Apply CLI flag overrides
|
|
98
|
+
if (typeof flags.dir === 'string') {
|
|
99
|
+
// Dir is handled at orchestrator level, not in config
|
|
100
|
+
}
|
|
101
|
+
if (typeof flags.format === 'string') {
|
|
102
|
+
if (flags.format !== 'markdown' && flags.format !== 'json') {
|
|
103
|
+
throw new Error(`Invalid format "${flags.format}". Supported: markdown, json`);
|
|
104
|
+
}
|
|
105
|
+
merged.outputFormat = flags.format;
|
|
106
|
+
}
|
|
107
|
+
if (typeof flags.output === 'string') {
|
|
108
|
+
merged.outputPath = flags.output;
|
|
109
|
+
}
|
|
110
|
+
if (typeof flags.workers === 'string') {
|
|
111
|
+
const n = parseInt(flags.workers, 10);
|
|
112
|
+
if (isNaN(n) || n < 1) {
|
|
113
|
+
throw new Error(`Invalid workers count "${flags.workers}". Must be a positive integer.`);
|
|
114
|
+
}
|
|
115
|
+
merged.maxWorkers = n;
|
|
116
|
+
}
|
|
117
|
+
if (flags.parallel === false || flags.parallel === 'false') {
|
|
118
|
+
merged.parallel = false;
|
|
119
|
+
}
|
|
120
|
+
else if (flags.parallel === true || flags.parallel === 'true') {
|
|
121
|
+
merged.parallel = true;
|
|
122
|
+
}
|
|
123
|
+
if (typeof flags.sniffers === 'string') {
|
|
124
|
+
const requested = flags.sniffers.split(',').map(s => s.trim());
|
|
125
|
+
// Disable all, then enable only requested
|
|
126
|
+
for (const key of Object.keys(merged.sniffers)) {
|
|
127
|
+
merged.sniffers[key].enabled = false;
|
|
128
|
+
}
|
|
129
|
+
for (const name of requested) {
|
|
130
|
+
if (merged.sniffers[name]) {
|
|
131
|
+
merged.sniffers[name].enabled = true;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw new Error(`Unknown sniffer "${name}". Available: ${Object.keys(merged.sniffers).join(', ')}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return merged;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../../src/cli/config-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAGA,wBAAgB,SAAS,IAAI,IAAI,CA0ChC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAQnC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printHelp = printHelp;
|
|
4
|
+
exports.printVersion = printVersion;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
function printHelp() {
|
|
8
|
+
const help = `
|
|
9
|
+
Usage: react-sniff [options] [dir]
|
|
10
|
+
|
|
11
|
+
Detect React anti-patterns in your codebase.
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
-d, --dir <path> Target directory to scan (default: cwd)
|
|
15
|
+
-c, --config <path> Path to config file (default: .snifferrc.json)
|
|
16
|
+
-s, --sniffers <list> Comma-separated list of sniffers to run
|
|
17
|
+
-f, --format <type> Output format: markdown | json (default: markdown)
|
|
18
|
+
-o, --output <path> Write report to file instead of stdout
|
|
19
|
+
-w, --workers <n> Number of worker threads (default: 4)
|
|
20
|
+
-i, --interactive Launch interactive TUI to browse results
|
|
21
|
+
-b, --batch <n> Show first N issues in interactive mode (default: 10)
|
|
22
|
+
--parallel Enable parallel execution (default: true)
|
|
23
|
+
--no-parallel Disable parallel execution
|
|
24
|
+
--verbose Show debug output
|
|
25
|
+
-q, --quiet Suppress all output (exit code only)
|
|
26
|
+
-h, --help Show this help message
|
|
27
|
+
-v, --version Show version number
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
react-sniff Scan current directory
|
|
31
|
+
react-sniff src/ Scan specific directory
|
|
32
|
+
ras -i Interactive mode
|
|
33
|
+
ras -i -b 20 Interactive, show first 20 issues
|
|
34
|
+
react-sniff --sniffers prop-explosion Run only prop explosion sniffer
|
|
35
|
+
react-sniff --format json -o report.json Output JSON report to file
|
|
36
|
+
ras --no-parallel --verbose Run sequentially with debug output
|
|
37
|
+
|
|
38
|
+
Sniffers:
|
|
39
|
+
prop-explosion Detects components with too many props
|
|
40
|
+
god-hook Detects custom hooks that do too much
|
|
41
|
+
prop-drilling Detects props passed through without being used
|
|
42
|
+
|
|
43
|
+
Configuration:
|
|
44
|
+
Create a .snifferrc.json in your project root to customize thresholds.
|
|
45
|
+
See https://github.com/your-repo/react-anti-pattern-sniffer for details.
|
|
46
|
+
`.trim();
|
|
47
|
+
console.log(help);
|
|
48
|
+
}
|
|
49
|
+
function printVersion() {
|
|
50
|
+
try {
|
|
51
|
+
const pkgPath = (0, node_path_1.join)(__dirname, '..', '..', '..', 'package.json');
|
|
52
|
+
const pkg = JSON.parse((0, node_fs_1.readFileSync)(pkgPath, 'utf8'));
|
|
53
|
+
console.log(`react-anti-pattern-sniffer v${pkg.version}`);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
console.log('react-anti-pattern-sniffer (unknown version)');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=help.js.map
|