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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Rust Violation Examples
|
|
2
|
+
|
|
3
|
+
Extended violation patterns for Rust reviews. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
## Unwrap Abuse
|
|
6
|
+
|
|
7
|
+
### Unwrap in Library Code
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
// VIOLATION: Panics on None — caller has no way to handle failure
|
|
11
|
+
pub fn get_username(users: &HashMap<u64, String>, id: u64) -> &str {
|
|
12
|
+
users.get(&id).unwrap() // Panics if id not found
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// VIOLATION: Unwrap on parse without context
|
|
16
|
+
let port: u16 = std::env::var("PORT").unwrap().parse().unwrap();
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Expect Without Useful Message
|
|
20
|
+
|
|
21
|
+
```rust
|
|
22
|
+
// VIOLATION: Message doesn't help diagnose the problem
|
|
23
|
+
let config = load_config().expect("failed");
|
|
24
|
+
|
|
25
|
+
// CORRECT: Actionable message
|
|
26
|
+
let config = load_config().expect("failed to load config from config.toml — does file exist?");
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Unnecessary Cloning
|
|
32
|
+
|
|
33
|
+
### Clone to Satisfy Borrow Checker
|
|
34
|
+
|
|
35
|
+
```rust
|
|
36
|
+
// VIOLATION: Cloning to work around borrow issues
|
|
37
|
+
fn process_items(items: &Vec<Item>) {
|
|
38
|
+
let cloned = items.clone(); // Entire Vec cloned
|
|
39
|
+
for item in &cloned {
|
|
40
|
+
println!("{}", item.name);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// CORRECT: Just borrow
|
|
45
|
+
fn process_items(items: &[Item]) {
|
|
46
|
+
for item in items {
|
|
47
|
+
println!("{}", item.name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Clone in Hot Loop
|
|
53
|
+
|
|
54
|
+
```rust
|
|
55
|
+
// VIOLATION: Allocating on every iteration
|
|
56
|
+
for record in &records {
|
|
57
|
+
let key = record.id.clone(); // String allocation per iteration
|
|
58
|
+
map.insert(key, record);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// CORRECT: Borrow or use references
|
|
62
|
+
for record in &records {
|
|
63
|
+
map.insert(&record.id, record);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Stringly-Typed APIs
|
|
70
|
+
|
|
71
|
+
### String Where Enum Belongs
|
|
72
|
+
|
|
73
|
+
```rust
|
|
74
|
+
// VIOLATION: Any typo compiles and fails at runtime
|
|
75
|
+
fn set_status(status: &str) {
|
|
76
|
+
match status {
|
|
77
|
+
"active" => { /* ... */ }
|
|
78
|
+
"inactive" => { /* ... */ }
|
|
79
|
+
_ => panic!("unknown status"), // Runtime failure
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// CORRECT: Compiler enforces valid values
|
|
84
|
+
enum Status { Active, Inactive }
|
|
85
|
+
|
|
86
|
+
fn set_status(status: Status) {
|
|
87
|
+
match status {
|
|
88
|
+
Status::Active => { /* ... */ }
|
|
89
|
+
Status::Inactive => { /* ... */ }
|
|
90
|
+
} // Exhaustive — no default needed
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Unsafe Without Justification
|
|
97
|
+
|
|
98
|
+
### Bare Unsafe Block
|
|
99
|
+
|
|
100
|
+
```rust
|
|
101
|
+
// VIOLATION: No safety comment explaining invariants
|
|
102
|
+
unsafe {
|
|
103
|
+
let ptr = data.as_ptr();
|
|
104
|
+
std::ptr::copy_nonoverlapping(ptr, dest, len);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// CORRECT: Document why this is safe
|
|
108
|
+
// SAFETY: `data` is guaranteed to be valid for `len` bytes because
|
|
109
|
+
// it was allocated by `Vec::with_capacity(len)` and filled by `read_exact`.
|
|
110
|
+
// `dest` is a valid pointer from `alloc::alloc(layout)` with matching size.
|
|
111
|
+
unsafe {
|
|
112
|
+
std::ptr::copy_nonoverlapping(data.as_ptr(), dest, len);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Unnecessary Unsafe
|
|
117
|
+
|
|
118
|
+
```rust
|
|
119
|
+
// VIOLATION: Using unsafe when safe alternative exists
|
|
120
|
+
unsafe fn get_element(slice: &[u8], index: usize) -> u8 {
|
|
121
|
+
*slice.get_unchecked(index)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// CORRECT: Safe indexing with bounds check
|
|
125
|
+
fn get_element(slice: &[u8], index: usize) -> Option<u8> {
|
|
126
|
+
slice.get(index).copied()
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Ignoring Results
|
|
133
|
+
|
|
134
|
+
### Discarding Write Errors
|
|
135
|
+
|
|
136
|
+
```rust
|
|
137
|
+
// VIOLATION: Write failure silently ignored
|
|
138
|
+
let _ = file.write_all(data);
|
|
139
|
+
let _ = file.flush();
|
|
140
|
+
|
|
141
|
+
// CORRECT: Propagate errors
|
|
142
|
+
file.write_all(data)?;
|
|
143
|
+
file.flush()?;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Ignoring Lock Poisoning
|
|
147
|
+
|
|
148
|
+
```rust
|
|
149
|
+
// VIOLATION: Silently ignoring poisoned mutex
|
|
150
|
+
let guard = mutex.lock().unwrap_or_else(|e| e.into_inner());
|
|
151
|
+
|
|
152
|
+
// CORRECT: Handle or propagate the poison
|
|
153
|
+
let guard = mutex.lock().map_err(|_| AppError::LockPoisoned)?;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Concurrency Violations
|
|
159
|
+
|
|
160
|
+
### Shared Mutable State Without Synchronization
|
|
161
|
+
|
|
162
|
+
```rust
|
|
163
|
+
// VIOLATION: Data race potential — no synchronization
|
|
164
|
+
static mut COUNTER: u64 = 0;
|
|
165
|
+
|
|
166
|
+
fn increment() {
|
|
167
|
+
unsafe { COUNTER += 1; } // Undefined behavior under concurrency
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// CORRECT: Use atomic or mutex
|
|
171
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
172
|
+
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
|
173
|
+
|
|
174
|
+
fn increment() {
|
|
175
|
+
COUNTER.fetch_add(1, Ordering::Relaxed);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Blocking in Async Context
|
|
180
|
+
|
|
181
|
+
```rust
|
|
182
|
+
// VIOLATION: Blocks the async runtime thread
|
|
183
|
+
async fn read_file(path: &str) -> Result<String, io::Error> {
|
|
184
|
+
std::fs::read_to_string(path) // Blocking call in async fn
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// CORRECT: Use async file I/O or spawn_blocking
|
|
188
|
+
async fn read_file(path: &str) -> Result<String, io::Error> {
|
|
189
|
+
tokio::fs::read_to_string(path).await
|
|
190
|
+
}
|
|
191
|
+
```
|
|
@@ -137,7 +137,7 @@ const isUndefined = (value: unknown): value is undefined =>
|
|
|
137
137
|
const isNullish = (value: unknown): value is null | undefined =>
|
|
138
138
|
value === null || value === undefined;
|
|
139
139
|
|
|
140
|
-
const isFunction = (value: unknown): value is
|
|
140
|
+
const isFunction = (value: unknown): value is (...args: unknown[]) => unknown =>
|
|
141
141
|
typeof value === 'function';
|
|
142
142
|
|
|
143
143
|
const isObject = (value: unknown): value is object =>
|
|
@@ -760,7 +760,7 @@ function debounce<T extends (...args: any[]) => any>(
|
|
|
760
760
|
fn: T,
|
|
761
761
|
delayMs: number
|
|
762
762
|
): (...args: Parameters<T>) => void {
|
|
763
|
-
let timeoutId:
|
|
763
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
764
764
|
|
|
765
765
|
return (...args: Parameters<T>) => {
|
|
766
766
|
if (timeoutId) clearTimeout(timeoutId);
|
|
@@ -774,7 +774,7 @@ function throttle<T extends (...args: any[]) => any>(
|
|
|
774
774
|
limitMs: number
|
|
775
775
|
): (...args: Parameters<T>) => void {
|
|
776
776
|
let lastRun = 0;
|
|
777
|
-
let timeoutId:
|
|
777
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
778
778
|
|
|
779
779
|
return (...args: Parameters<T>) => {
|
|
780
780
|
const now = Date.now();
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: react
|
|
3
|
-
description: This skill should be used when the user works with React components (.tsx/.jsx), asks about "hooks", "state management", "context providers", "memo optimization", "useEffect", or discusses component composition and rendering performance. Provides patterns for hooks, state, effects, memoization, and React-specific architecture.
|
|
4
|
-
user-invocable: false
|
|
5
|
-
allowed-tools: Read, Grep, Glob
|
|
6
|
-
activation:
|
|
7
|
-
file-patterns:
|
|
8
|
-
- "**/*.tsx"
|
|
9
|
-
- "**/*.jsx"
|
|
10
|
-
exclude:
|
|
11
|
-
- "node_modules/**"
|
|
12
|
-
- "**/*.test.*"
|
|
13
|
-
- "**/*.spec.*"
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
# React Patterns
|
|
17
|
-
|
|
18
|
-
Reference for React-specific patterns, component design, hooks, and performance optimization.
|
|
19
|
-
|
|
20
|
-
## Iron Law
|
|
21
|
-
|
|
22
|
-
> **COMPOSITION OVER PROPS**
|
|
23
|
-
>
|
|
24
|
-
> Use children and compound components, not prop drilling. If a component has >5 props,
|
|
25
|
-
> it's doing too much. Split it. If you're passing data through 3+ levels, use context
|
|
26
|
-
> or composition. Props are for configuration, not data plumbing.
|
|
27
|
-
|
|
28
|
-
## When This Skill Activates
|
|
29
|
-
|
|
30
|
-
- Working with React codebases
|
|
31
|
-
- Creating components and hooks
|
|
32
|
-
- Managing state and side effects
|
|
33
|
-
- Optimizing render performance
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## Component Patterns
|
|
38
|
-
|
|
39
|
-
### Functional Component Structure
|
|
40
|
-
|
|
41
|
-
```tsx
|
|
42
|
-
export function UserCard({ user, className }: UserCardProps) {
|
|
43
|
-
const [isExpanded, setIsExpanded] = useState(false); // 1. Hooks first
|
|
44
|
-
const displayName = user.firstName + ' ' + user.lastName; // 2. Derived state
|
|
45
|
-
const handleToggle = () => setIsExpanded((prev) => !prev); // 3. Handlers
|
|
46
|
-
return ( // 4. Render
|
|
47
|
-
<div className={cn('user-card', className)}>
|
|
48
|
-
<h3>{displayName}</h3>
|
|
49
|
-
{isExpanded && <UserDetails user={user} />}
|
|
50
|
-
<button onClick={handleToggle}>{isExpanded ? 'Collapse' : 'Expand'}</button>
|
|
51
|
-
</div>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Composition Over Props
|
|
57
|
-
|
|
58
|
-
```tsx
|
|
59
|
-
function Card({ children }: { children: React.ReactNode }) {
|
|
60
|
-
return <div className="card">{children}</div>;
|
|
61
|
-
}
|
|
62
|
-
Card.Header = ({ children }) => <div className="card-header">{children}</div>;
|
|
63
|
-
Card.Body = ({ children }) => <div className="card-body">{children}</div>;
|
|
64
|
-
|
|
65
|
-
// Usage - flexible, not rigid props
|
|
66
|
-
<Card>
|
|
67
|
-
<Card.Header><h2>Title</h2></Card.Header>
|
|
68
|
-
<Card.Body><p>Content</p></Card.Body>
|
|
69
|
-
</Card>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Hook Patterns
|
|
75
|
-
|
|
76
|
-
```tsx
|
|
77
|
-
function useLocalStorage<T>(key: string, initialValue: T) {
|
|
78
|
-
const [value, setValue] = useState<T>(() => {
|
|
79
|
-
const stored = localStorage.getItem(key);
|
|
80
|
-
return stored ? JSON.parse(stored) : initialValue;
|
|
81
|
-
});
|
|
82
|
-
useEffect(() => localStorage.setItem(key, JSON.stringify(value)), [key, value]);
|
|
83
|
-
return [value, setValue] as const;
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## State Management
|
|
90
|
-
|
|
91
|
-
```tsx
|
|
92
|
-
const AuthContext = createContext<AuthContextValue | null>(null);
|
|
93
|
-
|
|
94
|
-
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
95
|
-
const [user, setUser] = useState<User | null>(null);
|
|
96
|
-
const login = async (creds: Credentials) => setUser(await authApi.login(creds));
|
|
97
|
-
const logout = () => { authApi.logout(); setUser(null); };
|
|
98
|
-
return <AuthContext.Provider value={{ user, login, logout }}>{children}</AuthContext.Provider>;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function useAuth() {
|
|
102
|
-
const ctx = useContext(AuthContext);
|
|
103
|
-
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
|
|
104
|
-
return ctx;
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## Performance
|
|
111
|
-
|
|
112
|
-
```tsx
|
|
113
|
-
function UserList({ users, filter }: { users: User[]; filter: string }) {
|
|
114
|
-
const filtered = useMemo(() => users.filter((u) => u.name.includes(filter)), [users, filter]);
|
|
115
|
-
const onClick = useCallback(() => console.log('Clicked'), []);
|
|
116
|
-
return <ul>{filtered.map((u) => <MemoItem key={u.id} user={u} onClick={onClick} />)}</ul>;
|
|
117
|
-
}
|
|
118
|
-
const MemoItem = memo(({ user }: { user: User }) => <li>{user.name}</li>);
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Async Parallelization
|
|
124
|
-
|
|
125
|
-
```tsx
|
|
126
|
-
// CORRECT: Independent fetches run in parallel
|
|
127
|
-
async function loadDashboard(userId: string) {
|
|
128
|
-
const [user, orders, preferences] = await Promise.all([
|
|
129
|
-
fetchUser(userId),
|
|
130
|
-
fetchOrders(userId),
|
|
131
|
-
fetchPreferences(userId),
|
|
132
|
-
]);
|
|
133
|
-
return { user, orders, preferences };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// VIOLATION: Sequential fetches (3x slower)
|
|
137
|
-
async function loadDashboardSlow(userId: string) {
|
|
138
|
-
const user = await fetchUser(userId);
|
|
139
|
-
const orders = await fetchOrders(userId);
|
|
140
|
-
const preferences = await fetchPreferences(userId);
|
|
141
|
-
return { user, orders, preferences };
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## Bundle Size
|
|
148
|
-
|
|
149
|
-
```tsx
|
|
150
|
-
// CORRECT: Direct imports (tree-shakable)
|
|
151
|
-
import { Button } from '@/components/Button';
|
|
152
|
-
import { Card } from '@/components/Card';
|
|
153
|
-
|
|
154
|
-
// VIOLATION: Barrel imports (imports entire library)
|
|
155
|
-
import { Button, Card } from '@/components';
|
|
156
|
-
|
|
157
|
-
// CORRECT: Dynamic import for heavy components
|
|
158
|
-
const Chart = lazy(() => import('./Chart'));
|
|
159
|
-
const Editor = lazy(() => import('./Editor'));
|
|
160
|
-
|
|
161
|
-
function Dashboard() {
|
|
162
|
-
return (
|
|
163
|
-
<Suspense fallback={<Skeleton />}>
|
|
164
|
-
{showChart && <Chart data={data} />}
|
|
165
|
-
</Suspense>
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## Re-render Optimization
|
|
173
|
-
|
|
174
|
-
```tsx
|
|
175
|
-
// CORRECT: Primitive deps (stable references)
|
|
176
|
-
useEffect(() => {
|
|
177
|
-
fetchData(userId, isActive);
|
|
178
|
-
}, [userId, isActive]); // primitives don't cause unnecessary runs
|
|
179
|
-
|
|
180
|
-
// VIOLATION: Object/array deps (new reference every render)
|
|
181
|
-
useEffect(() => {
|
|
182
|
-
fetchData(options);
|
|
183
|
-
}, [options]); // { page: 1 } !== { page: 1 }
|
|
184
|
-
|
|
185
|
-
// CORRECT: Stable callback with useCallback
|
|
186
|
-
const handleClick = useCallback((id: string) => {
|
|
187
|
-
setSelected(id);
|
|
188
|
-
}, []); // no deps = stable reference
|
|
189
|
-
|
|
190
|
-
// VIOLATION: Inline function (new reference every render)
|
|
191
|
-
<List onItemClick={(id) => setSelected(id)} />
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
## Image Optimization
|
|
197
|
-
|
|
198
|
-
```tsx
|
|
199
|
-
// CORRECT: Optimized image with all attributes
|
|
200
|
-
<img
|
|
201
|
-
src={url}
|
|
202
|
-
alt={description}
|
|
203
|
-
width={400}
|
|
204
|
-
height={300}
|
|
205
|
-
loading="lazy"
|
|
206
|
-
decoding="async"
|
|
207
|
-
style={{ aspectRatio: '4/3' }}
|
|
208
|
-
/>
|
|
209
|
-
|
|
210
|
-
// VIOLATION: Unoptimized image
|
|
211
|
-
<img src={url} /> // No dimensions, no lazy loading, layout shift
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
## Data Structure Performance
|
|
217
|
-
|
|
218
|
-
```tsx
|
|
219
|
-
// CORRECT: Set for O(1) membership checks
|
|
220
|
-
const selectedIds = new Set(selected);
|
|
221
|
-
const isSelected = (id: string) => selectedIds.has(id);
|
|
222
|
-
|
|
223
|
-
// VIOLATION: Array.includes is O(n)
|
|
224
|
-
const isSelected = (id: string) => selected.includes(id);
|
|
225
|
-
|
|
226
|
-
// CORRECT: Map for key-value lookups
|
|
227
|
-
const usersById = new Map(users.map(u => [u.id, u]));
|
|
228
|
-
const getUser = (id: string) => usersById.get(id);
|
|
229
|
-
|
|
230
|
-
// VIOLATION: Array.find is O(n)
|
|
231
|
-
const getUser = (id: string) => users.find(u => u.id === id);
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
|
|
236
|
-
## Anti-Patterns
|
|
237
|
-
|
|
238
|
-
```tsx
|
|
239
|
-
// BAD: Derived state in useState | GOOD: useMemo
|
|
240
|
-
const filtered = useMemo(() => items.filter(i => i.active), [items]);
|
|
241
|
-
|
|
242
|
-
// BAD: Missing dependency | GOOD: Include all deps
|
|
243
|
-
useEffect(() => { fetchData(userId); }, [userId]);
|
|
244
|
-
|
|
245
|
-
// BAD: State update in render | GOOD: Use effect
|
|
246
|
-
useEffect(() => { setState(value); }, [value]);
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
## Extended References
|
|
252
|
-
|
|
253
|
-
- `references/patterns.md` - Render props, reducers, virtualization, lazy loading
|
|
254
|
-
- `references/hooks.md` - useQuery, useDebouncedValue, usePrevious, useClickOutside
|
|
255
|
-
- `references/forms.md` - Controlled forms, validation hooks, multi-step forms
|
|
256
|
-
- `references/error-handling.md` - Error boundaries, async error handling
|
|
257
|
-
|
|
258
|
-
---
|
|
259
|
-
|
|
260
|
-
## Checklist
|
|
261
|
-
|
|
262
|
-
- [ ] Hooks at top level only
|
|
263
|
-
- [ ] All useEffect deps included
|
|
264
|
-
- [ ] useCallback for handlers passed to children
|
|
265
|
-
- [ ] useMemo for expensive computations
|
|
266
|
-
- [ ] Context at appropriate level
|
|
267
|
-
- [ ] Error boundaries around risky components
|
|
268
|
-
- [ ] Keys on list items (not index)
|
|
269
|
-
- [ ] Loading/error states handled
|
|
270
|
-
- [ ] Accessibility (aria-*, role)
|
|
271
|
-
- [ ] Independent fetches parallelized with Promise.all
|
|
272
|
-
- [ ] No barrel imports (direct imports for tree-shaking)
|
|
273
|
-
- [ ] Large components lazy-loaded
|
|
274
|
-
- [ ] Object/array deps avoided in useEffect (use primitives)
|
|
275
|
-
- [ ] Set/Map used for lookups instead of Array.includes/find
|
|
276
|
-
- [ ] Images have dimensions, lazy loading, and aspect-ratio
|