devflow-kit 1.1.0 → 1.2.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/CHANGELOG.md +39 -0
- package/README.md +23 -6
- package/dist/plugins.js +67 -3
- package/package.json +2 -1
- package/plugins/devflow-accessibility/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-ambient/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +1 -1
- package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +4 -0
- package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-code-review/.claude-plugin/plugin.json +1 -4
- package/plugins/devflow-code-review/agents/reviewer.md +8 -0
- package/plugins/devflow-code-review/commands/code-review-teams.md +11 -1
- package/plugins/devflow-code-review/commands/code-review.md +12 -2
- package/plugins/devflow-core-skills/.claude-plugin/plugin.json +2 -6
- package/plugins/devflow-debug/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-go/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-go/skills/go/SKILL.md +187 -0
- package/plugins/devflow-go/skills/go/references/concurrency.md +312 -0
- package/plugins/devflow-go/skills/go/references/detection.md +129 -0
- package/plugins/devflow-go/skills/go/references/patterns.md +232 -0
- package/plugins/devflow-go/skills/go/references/violations.md +205 -0
- package/plugins/devflow-implement/.claude-plugin/plugin.json +1 -3
- package/plugins/devflow-implement/agents/coder.md +11 -6
- package/plugins/devflow-java/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-java/skills/java/SKILL.md +183 -0
- package/plugins/devflow-java/skills/java/references/detection.md +120 -0
- package/plugins/devflow-java/skills/java/references/modern-java.md +270 -0
- package/plugins/devflow-java/skills/java/references/patterns.md +235 -0
- package/plugins/devflow-java/skills/java/references/violations.md +213 -0
- package/plugins/devflow-python/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-python/skills/python/SKILL.md +188 -0
- package/plugins/devflow-python/skills/python/references/async.md +220 -0
- package/plugins/devflow-python/skills/python/references/detection.md +128 -0
- package/plugins/devflow-python/skills/python/references/patterns.md +226 -0
- package/plugins/devflow-python/skills/python/references/violations.md +204 -0
- package/plugins/devflow-react/.claude-plugin/plugin.json +15 -0
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/SKILL.md +1 -1
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/references/patterns.md +3 -3
- package/plugins/devflow-resolve/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-rust/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-rust/skills/rust/SKILL.md +193 -0
- package/plugins/devflow-rust/skills/rust/references/detection.md +131 -0
- package/plugins/devflow-rust/skills/rust/references/ownership.md +242 -0
- package/plugins/devflow-rust/skills/rust/references/patterns.md +210 -0
- package/plugins/devflow-rust/skills/rust/references/violations.md +191 -0
- package/plugins/devflow-self-review/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-specify/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-typescript/.claude-plugin/plugin.json +15 -0
- package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
- package/shared/agents/coder.md +11 -6
- package/shared/agents/reviewer.md +8 -0
- package/shared/skills/ambient-router/SKILL.md +1 -1
- package/shared/skills/ambient-router/references/skill-catalog.md +4 -0
- package/shared/skills/go/SKILL.md +187 -0
- package/shared/skills/go/references/concurrency.md +312 -0
- package/shared/skills/go/references/detection.md +129 -0
- package/shared/skills/go/references/patterns.md +232 -0
- package/shared/skills/go/references/violations.md +205 -0
- package/shared/skills/java/SKILL.md +183 -0
- package/shared/skills/java/references/detection.md +120 -0
- package/shared/skills/java/references/modern-java.md +270 -0
- package/shared/skills/java/references/patterns.md +235 -0
- package/shared/skills/java/references/violations.md +213 -0
- package/shared/skills/python/SKILL.md +188 -0
- package/shared/skills/python/references/async.md +220 -0
- package/shared/skills/python/references/detection.md +128 -0
- package/shared/skills/python/references/patterns.md +226 -0
- package/shared/skills/python/references/violations.md +204 -0
- package/shared/skills/react/SKILL.md +1 -1
- package/shared/skills/react/references/patterns.md +3 -3
- package/shared/skills/rust/SKILL.md +193 -0
- package/shared/skills/rust/references/detection.md +131 -0
- package/shared/skills/rust/references/ownership.md +242 -0
- package/shared/skills/rust/references/patterns.md +210 -0
- package/shared/skills/rust/references/violations.md +191 -0
- package/shared/skills/typescript/references/patterns.md +3 -3
- package/plugins/devflow-code-review/skills/react/SKILL.md +0 -276
- package/plugins/devflow-code-review/skills/react/references/patterns.md +0 -1331
- package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +0 -453
- package/plugins/devflow-core-skills/skills/react/references/violations.md +0 -565
- package/plugins/devflow-implement/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-implement/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-implement/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-implement/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-implement/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-implement/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-implement/skills/frontend-design/references/violations.md +0 -453
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-react}/skills/react/references/violations.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/SKILL.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/violations.md +0 -0
|
@@ -1,565 +0,0 @@
|
|
|
1
|
-
# React Violation Examples
|
|
2
|
-
|
|
3
|
-
Extended violation patterns for React reviews. Reference from main SKILL.md.
|
|
4
|
-
|
|
5
|
-
## Vercel Performance Violations
|
|
6
|
-
|
|
7
|
-
### Sequential Fetches
|
|
8
|
-
|
|
9
|
-
```tsx
|
|
10
|
-
// VIOLATION: Sequential fetches (waterfall)
|
|
11
|
-
async function loadDashboard(userId: string) {
|
|
12
|
-
const user = await fetchUser(userId);
|
|
13
|
-
const orders = await fetchOrders(userId); // Waits for user
|
|
14
|
-
const prefs = await fetchPreferences(userId); // Waits for orders
|
|
15
|
-
return { user, orders, prefs };
|
|
16
|
-
}
|
|
17
|
-
// Total time: T(user) + T(orders) + T(prefs)
|
|
18
|
-
// Should be: max(T(user), T(orders), T(prefs))
|
|
19
|
-
|
|
20
|
-
// VIOLATION: Sequential in useEffect
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
async function load() {
|
|
23
|
-
setUser(await fetchUser(id));
|
|
24
|
-
setOrders(await fetchOrders(id)); // Sequential
|
|
25
|
-
setStats(await fetchStats(id)); // Sequential
|
|
26
|
-
}
|
|
27
|
-
load();
|
|
28
|
-
}, [id]);
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### Barrel Import Anti-Patterns
|
|
32
|
-
|
|
33
|
-
```tsx
|
|
34
|
-
// VIOLATION: Barrel import pulls entire library
|
|
35
|
-
import { Button } from '@/components';
|
|
36
|
-
import { formatDate, formatCurrency, formatNumber } from '@/utils';
|
|
37
|
-
import { IconHome, IconUser } from '@/icons';
|
|
38
|
-
|
|
39
|
-
// VIOLATION: Named imports from large packages
|
|
40
|
-
import { debounce } from 'lodash'; // Imports entire lodash
|
|
41
|
-
import { Button, Input, Select } from 'antd'; // Large bundle
|
|
42
|
-
|
|
43
|
-
// VIOLATION: Not lazy loading heavy dependencies
|
|
44
|
-
import Editor from '@monaco-editor/react'; // 2MB+ bundle
|
|
45
|
-
import { Chart } from 'chart.js'; // Heavy charting library
|
|
46
|
-
|
|
47
|
-
function Dashboard() {
|
|
48
|
-
const [showChart, setShowChart] = useState(false);
|
|
49
|
-
return (
|
|
50
|
-
<div>
|
|
51
|
-
{showChart && <Chart />} {/* Loaded even when not shown */}
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### Re-render Causing Patterns
|
|
58
|
-
|
|
59
|
-
```tsx
|
|
60
|
-
// VIOLATION: Object literal in deps (new ref every render)
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
fetchData(config);
|
|
63
|
-
}, [{ page: 1, limit: 10 }]); // Always "changed"
|
|
64
|
-
|
|
65
|
-
// VIOLATION: Array in deps
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
updateFilters(filters);
|
|
68
|
-
}, [[filter1, filter2, filter3]]); // New array every render
|
|
69
|
-
|
|
70
|
-
// VIOLATION: Inline callback to memoized child
|
|
71
|
-
const MemoChild = memo(Child);
|
|
72
|
-
|
|
73
|
-
function Parent() {
|
|
74
|
-
return (
|
|
75
|
-
<MemoChild
|
|
76
|
-
onClick={() => console.log('clicked')} // New fn = re-render
|
|
77
|
-
style={{ color: 'blue' }} // New object = re-render
|
|
78
|
-
/>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// VIOLATION: Computed value in render without memo
|
|
83
|
-
function FilteredList({ items, filter }: Props) {
|
|
84
|
-
const filtered = items.filter(i => i.includes(filter)); // Every render
|
|
85
|
-
return <List items={filtered} />;
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Unoptimized Images
|
|
90
|
-
|
|
91
|
-
```tsx
|
|
92
|
-
// VIOLATION: No dimensions (causes layout shift)
|
|
93
|
-
<img src={url} alt={alt} />
|
|
94
|
-
|
|
95
|
-
// VIOLATION: No lazy loading (loads all images immediately)
|
|
96
|
-
{images.map(img => <img src={img.src} alt={img.alt} />)}
|
|
97
|
-
|
|
98
|
-
// VIOLATION: No aspect ratio (jumps on load)
|
|
99
|
-
<img
|
|
100
|
-
src={url}
|
|
101
|
-
width={400}
|
|
102
|
-
height={300}
|
|
103
|
-
// Missing: style={{ aspectRatio: '4/3' }}
|
|
104
|
-
/>
|
|
105
|
-
|
|
106
|
-
// VIOLATION: Eager loading below fold
|
|
107
|
-
<img
|
|
108
|
-
src={heroImage}
|
|
109
|
-
alt="Hero"
|
|
110
|
-
loading="eager" // Should be lazy for non-critical images
|
|
111
|
-
/>
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Inefficient Data Structures
|
|
115
|
-
|
|
116
|
-
```tsx
|
|
117
|
-
// VIOLATION: Array.includes for frequent checks (O(n))
|
|
118
|
-
function SelectableList({ items, selected }: Props) {
|
|
119
|
-
return (
|
|
120
|
-
<ul>
|
|
121
|
-
{items.map(item => (
|
|
122
|
-
<li className={selected.includes(item.id) ? 'selected' : ''}>
|
|
123
|
-
{/* O(n) check on every item render */}
|
|
124
|
-
{item.name}
|
|
125
|
-
</li>
|
|
126
|
-
))}
|
|
127
|
-
</ul>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// VIOLATION: Array.find for lookups (O(n))
|
|
132
|
-
function UserDisplay({ userId, users }: Props) {
|
|
133
|
-
const user = users.find(u => u.id === userId); // O(n) every render
|
|
134
|
-
return <span>{user?.name}</span>;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// VIOLATION: Filter + map in render (double iteration)
|
|
138
|
-
function ActiveUsers({ users }: Props) {
|
|
139
|
-
return (
|
|
140
|
-
<ul>
|
|
141
|
-
{users
|
|
142
|
-
.filter(u => u.active)
|
|
143
|
-
.map(u => <li key={u.id}>{u.name}</li>)
|
|
144
|
-
}
|
|
145
|
-
</ul>
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// VIOLATION: Recreating Set every render
|
|
150
|
-
function Tags({ tags, selected }: Props) {
|
|
151
|
-
const selectedSet = new Set(selected); // Created every render
|
|
152
|
-
return tags.map(tag => (
|
|
153
|
-
<Tag selected={selectedSet.has(tag)} />
|
|
154
|
-
));
|
|
155
|
-
}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## Component Violations
|
|
161
|
-
|
|
162
|
-
### Prop Drilling
|
|
163
|
-
|
|
164
|
-
```tsx
|
|
165
|
-
// VIOLATION: Passing data through multiple intermediate components
|
|
166
|
-
function App() {
|
|
167
|
-
const [user, setUser] = useState<User | null>(null);
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<Layout user={user}>
|
|
171
|
-
<Sidebar user={user}>
|
|
172
|
-
<Navigation user={user}>
|
|
173
|
-
<UserMenu user={user} /> {/* Props drilled through 3 levels */}
|
|
174
|
-
</Navigation>
|
|
175
|
-
</Sidebar>
|
|
176
|
-
</Layout>
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Rigid Component Structure
|
|
182
|
-
|
|
183
|
-
```tsx
|
|
184
|
-
// VIOLATION: Too many props, no composition
|
|
185
|
-
function Card({
|
|
186
|
-
title,
|
|
187
|
-
subtitle,
|
|
188
|
-
content,
|
|
189
|
-
footer,
|
|
190
|
-
headerIcon,
|
|
191
|
-
showCloseButton,
|
|
192
|
-
onClose,
|
|
193
|
-
variant,
|
|
194
|
-
size,
|
|
195
|
-
className
|
|
196
|
-
}: CardProps) {
|
|
197
|
-
return (
|
|
198
|
-
<div className={className}>
|
|
199
|
-
<header>
|
|
200
|
-
{headerIcon && <Icon name={headerIcon} />}
|
|
201
|
-
<h2>{title}</h2>
|
|
202
|
-
<p>{subtitle}</p>
|
|
203
|
-
{showCloseButton && <button onClick={onClose}>X</button>}
|
|
204
|
-
</header>
|
|
205
|
-
<div>{content}</div>
|
|
206
|
-
<footer>{footer}</footer>
|
|
207
|
-
</div>
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Direct State Mutation
|
|
213
|
-
|
|
214
|
-
```tsx
|
|
215
|
-
// VIOLATION: Mutating state directly
|
|
216
|
-
function UserList() {
|
|
217
|
-
const [users, setUsers] = useState<User[]>([]);
|
|
218
|
-
|
|
219
|
-
const updateUser = (index: number, name: string) => {
|
|
220
|
-
users[index].name = name; // BAD: Direct mutation
|
|
221
|
-
setUsers(users); // Won't trigger re-render
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### Missing Keys
|
|
227
|
-
|
|
228
|
-
```tsx
|
|
229
|
-
// VIOLATION: Index as key causes reconciliation issues
|
|
230
|
-
{items.map((item, index) => (
|
|
231
|
-
<Item key={index} {...item} /> // Index key breaks reordering
|
|
232
|
-
))}
|
|
233
|
-
|
|
234
|
-
// VIOLATION: Missing key entirely
|
|
235
|
-
{items.map((item) => (
|
|
236
|
-
<Item {...item} /> // React warning, poor performance
|
|
237
|
-
))}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Hooks Violations
|
|
243
|
-
|
|
244
|
-
### Missing Dependencies
|
|
245
|
-
|
|
246
|
-
```tsx
|
|
247
|
-
// VIOLATION: Missing dependency causes stale closure
|
|
248
|
-
function SearchResults({ query }: { query: string }) {
|
|
249
|
-
const [results, setResults] = useState<Result[]>([]);
|
|
250
|
-
|
|
251
|
-
useEffect(() => {
|
|
252
|
-
fetchResults(query).then(setResults);
|
|
253
|
-
}, []); // BAD: Missing 'query' dependency
|
|
254
|
-
|
|
255
|
-
return <ResultsList results={results} />;
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Conditional Hooks
|
|
260
|
-
|
|
261
|
-
```tsx
|
|
262
|
-
// VIOLATION: Hook called conditionally
|
|
263
|
-
function UserProfile({ user }: { user: User | null }) {
|
|
264
|
-
if (!user) {
|
|
265
|
-
return <LoginPrompt />;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// BAD: Hook called after conditional return
|
|
269
|
-
const [isEditing, setIsEditing] = useState(false);
|
|
270
|
-
|
|
271
|
-
return <ProfileEditor user={user} isEditing={isEditing} />;
|
|
272
|
-
}
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### Stale Closure in Callbacks
|
|
276
|
-
|
|
277
|
-
```tsx
|
|
278
|
-
// VIOLATION: Callback captures stale state
|
|
279
|
-
function Counter() {
|
|
280
|
-
const [count, setCount] = useState(0);
|
|
281
|
-
|
|
282
|
-
useEffect(() => {
|
|
283
|
-
const interval = setInterval(() => {
|
|
284
|
-
setCount(count + 1); // BAD: Always references initial count (0)
|
|
285
|
-
}, 1000);
|
|
286
|
-
|
|
287
|
-
return () => clearInterval(interval);
|
|
288
|
-
}, []); // Missing count dependency
|
|
289
|
-
|
|
290
|
-
return <span>{count}</span>;
|
|
291
|
-
}
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Effects Without Cleanup
|
|
295
|
-
|
|
296
|
-
```tsx
|
|
297
|
-
// VIOLATION: Event listener never removed
|
|
298
|
-
function WindowSize() {
|
|
299
|
-
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
300
|
-
|
|
301
|
-
useEffect(() => {
|
|
302
|
-
const handleResize = () => {
|
|
303
|
-
setSize({ width: window.innerWidth, height: window.innerHeight });
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
window.addEventListener('resize', handleResize);
|
|
307
|
-
// BAD: Missing cleanup - memory leak
|
|
308
|
-
}, []);
|
|
309
|
-
|
|
310
|
-
return <span>{size.width} x {size.height}</span>;
|
|
311
|
-
}
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### Derived State in useState
|
|
315
|
-
|
|
316
|
-
```tsx
|
|
317
|
-
// VIOLATION: Storing computed value in state
|
|
318
|
-
function ProductList({ products, filter }: Props) {
|
|
319
|
-
const [filteredProducts, setFilteredProducts] = useState<Product[]>([]);
|
|
320
|
-
|
|
321
|
-
useEffect(() => {
|
|
322
|
-
setFilteredProducts(products.filter(p => p.category === filter));
|
|
323
|
-
}, [products, filter]); // BAD: Unnecessary state and effect
|
|
324
|
-
|
|
325
|
-
return <List items={filteredProducts} />;
|
|
326
|
-
}
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
---
|
|
330
|
-
|
|
331
|
-
## Forms Violations
|
|
332
|
-
|
|
333
|
-
### Uncontrolled to Controlled Switch
|
|
334
|
-
|
|
335
|
-
```tsx
|
|
336
|
-
// VIOLATION: Switching from uncontrolled to controlled
|
|
337
|
-
function SearchInput() {
|
|
338
|
-
const [value, setValue] = useState<string>(); // undefined initially
|
|
339
|
-
|
|
340
|
-
return (
|
|
341
|
-
<input
|
|
342
|
-
value={value} // BAD: undefined -> string causes warning
|
|
343
|
-
onChange={(e) => setValue(e.target.value)}
|
|
344
|
-
/>
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### Missing Form Validation
|
|
350
|
-
|
|
351
|
-
```tsx
|
|
352
|
-
// VIOLATION: No validation, direct submission
|
|
353
|
-
function LoginForm({ onSubmit }: Props) {
|
|
354
|
-
const [email, setEmail] = useState('');
|
|
355
|
-
const [password, setPassword] = useState('');
|
|
356
|
-
|
|
357
|
-
const handleSubmit = (e: React.FormEvent) => {
|
|
358
|
-
e.preventDefault();
|
|
359
|
-
onSubmit({ email, password }); // BAD: No validation
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
return (
|
|
363
|
-
<form onSubmit={handleSubmit}>
|
|
364
|
-
<input value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
365
|
-
<input value={password} onChange={(e) => setPassword(e.target.value)} />
|
|
366
|
-
<button type="submit">Login</button>
|
|
367
|
-
</form>
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Missing Accessibility Attributes
|
|
373
|
-
|
|
374
|
-
```tsx
|
|
375
|
-
// VIOLATION: Form inputs without proper accessibility
|
|
376
|
-
function ContactForm() {
|
|
377
|
-
return (
|
|
378
|
-
<form>
|
|
379
|
-
{/* BAD: No labels, no aria attributes, no error announcements */}
|
|
380
|
-
<input placeholder="Email" />
|
|
381
|
-
{error && <span style={{ color: 'red' }}>{error}</span>}
|
|
382
|
-
<button>Submit</button>
|
|
383
|
-
</form>
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Form State Not Reset After Submit
|
|
389
|
-
|
|
390
|
-
```tsx
|
|
391
|
-
// VIOLATION: Form keeps stale data after successful submit
|
|
392
|
-
function CommentForm({ onSubmit }: Props) {
|
|
393
|
-
const [comment, setComment] = useState('');
|
|
394
|
-
|
|
395
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
396
|
-
e.preventDefault();
|
|
397
|
-
await onSubmit(comment);
|
|
398
|
-
// BAD: Missing state reset after successful submit
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
return (
|
|
402
|
-
<form onSubmit={handleSubmit}>
|
|
403
|
-
<textarea value={comment} onChange={(e) => setComment(e.target.value)} />
|
|
404
|
-
<button type="submit">Post</button>
|
|
405
|
-
</form>
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
---
|
|
411
|
-
|
|
412
|
-
## Error Handling Violations
|
|
413
|
-
|
|
414
|
-
### Missing Error Boundaries
|
|
415
|
-
|
|
416
|
-
```tsx
|
|
417
|
-
// VIOLATION: No error boundary around risky component
|
|
418
|
-
function App() {
|
|
419
|
-
return (
|
|
420
|
-
<div>
|
|
421
|
-
<Header />
|
|
422
|
-
<main>
|
|
423
|
-
<UserProfile userId={userId} /> {/* If this crashes, entire app crashes */}
|
|
424
|
-
<OrderHistory userId={userId} />
|
|
425
|
-
</main>
|
|
426
|
-
<Footer />
|
|
427
|
-
</div>
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
### Swallowed Errors
|
|
433
|
-
|
|
434
|
-
```tsx
|
|
435
|
-
// VIOLATION: Error caught but not handled
|
|
436
|
-
function DataLoader({ url }: { url: string }) {
|
|
437
|
-
const [data, setData] = useState(null);
|
|
438
|
-
|
|
439
|
-
useEffect(() => {
|
|
440
|
-
fetch(url)
|
|
441
|
-
.then(res => res.json())
|
|
442
|
-
.then(setData)
|
|
443
|
-
.catch(() => {}); // BAD: Silently swallows error
|
|
444
|
-
}, [url]);
|
|
445
|
-
|
|
446
|
-
return data ? <DataDisplay data={data} /> : <Spinner />;
|
|
447
|
-
}
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
### No Loading/Error States
|
|
451
|
-
|
|
452
|
-
```tsx
|
|
453
|
-
// VIOLATION: Only handles success case
|
|
454
|
-
function UserProfile({ userId }: { userId: string }) {
|
|
455
|
-
const [user, setUser] = useState<User | null>(null);
|
|
456
|
-
|
|
457
|
-
useEffect(() => {
|
|
458
|
-
fetchUser(userId).then(setUser);
|
|
459
|
-
}, [userId]);
|
|
460
|
-
|
|
461
|
-
// BAD: No loading spinner, no error handling
|
|
462
|
-
return user ? <Profile user={user} /> : null;
|
|
463
|
-
}
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
### Error State Not Cleared
|
|
467
|
-
|
|
468
|
-
```tsx
|
|
469
|
-
// VIOLATION: Error persists after retry
|
|
470
|
-
function DataFetcher({ url }: { url: string }) {
|
|
471
|
-
const [data, setData] = useState(null);
|
|
472
|
-
const [error, setError] = useState<Error | null>(null);
|
|
473
|
-
|
|
474
|
-
const fetchData = async () => {
|
|
475
|
-
try {
|
|
476
|
-
const result = await fetch(url).then(r => r.json());
|
|
477
|
-
setData(result);
|
|
478
|
-
// BAD: Error state not cleared on success
|
|
479
|
-
} catch (e) {
|
|
480
|
-
setError(e as Error);
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
return (
|
|
485
|
-
<div>
|
|
486
|
-
{error && <ErrorMessage error={error} />}
|
|
487
|
-
<button onClick={fetchData}>Retry</button>
|
|
488
|
-
</div>
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
---
|
|
494
|
-
|
|
495
|
-
## Performance Violations
|
|
496
|
-
|
|
497
|
-
### Inline Objects in JSX
|
|
498
|
-
|
|
499
|
-
```tsx
|
|
500
|
-
// VIOLATION: New reference every render
|
|
501
|
-
<Component
|
|
502
|
-
options={{ show: true, animate: false }} // New object each render
|
|
503
|
-
items={[1, 2, 3]} // New array each render
|
|
504
|
-
/>
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
### Inline Arrow Functions in JSX
|
|
508
|
-
|
|
509
|
-
```tsx
|
|
510
|
-
// VIOLATION: New function every render
|
|
511
|
-
<Button onClick={() => handleClick(item.id)} />
|
|
512
|
-
|
|
513
|
-
// Also creates new function each render
|
|
514
|
-
{items.map(item => (
|
|
515
|
-
<Item
|
|
516
|
-
key={item.id}
|
|
517
|
-
onClick={() => onSelect(item)} // New function per item per render
|
|
518
|
-
/>
|
|
519
|
-
))}
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### Missing useMemo for Expensive Computations
|
|
523
|
-
|
|
524
|
-
```tsx
|
|
525
|
-
// VIOLATION: Recalculates on every render
|
|
526
|
-
function Dashboard({ data }: { data: DataPoint[] }) {
|
|
527
|
-
const stats = computeExpensiveStats(data); // Runs every render
|
|
528
|
-
const chartData = transformForChart(data); // Also runs every render
|
|
529
|
-
|
|
530
|
-
return (
|
|
531
|
-
<div>
|
|
532
|
-
<Stats data={stats} />
|
|
533
|
-
<Chart data={chartData} />
|
|
534
|
-
</div>
|
|
535
|
-
);
|
|
536
|
-
}
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
### State Updates in Render
|
|
540
|
-
|
|
541
|
-
```tsx
|
|
542
|
-
// VIOLATION: Causes infinite loop
|
|
543
|
-
function Sync({ value }: { value: string }) {
|
|
544
|
-
const [state, setState] = useState(value);
|
|
545
|
-
|
|
546
|
-
if (value !== state) {
|
|
547
|
-
setState(value); // BAD: State update during render
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return <span>{state}</span>;
|
|
551
|
-
}
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
### Unthrottled Event Handlers
|
|
555
|
-
|
|
556
|
-
```tsx
|
|
557
|
-
// VIOLATION: Fires on every scroll pixel
|
|
558
|
-
function ParallaxEffect() {
|
|
559
|
-
useEffect(() => {
|
|
560
|
-
window.addEventListener('scroll', () => {
|
|
561
|
-
updateParallax(); // 60+ calls per second
|
|
562
|
-
});
|
|
563
|
-
}, []);
|
|
564
|
-
}
|
|
565
|
-
```
|