@codyswann/lisa 1.66.3 → 1.67.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/all/copy-overwrite/.claude/rules/base-rules.md +137 -0
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -10
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +2 -14
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -12
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -12
- package/plugins/src/base/.claude-plugin/plugin.json +0 -9
- package/plugins/src/expo/.claude-plugin/plugin.json +2 -13
- package/plugins/src/rails/.claude-plugin/plugin.json +0 -11
- package/plugins/src/typescript/.claude-plugin/plugin.json +0 -11
- package/plugins/lisa/rules/README.md +0 -240
- package/plugins/lisa-rails/rules/lisa.md +0 -42
- package/plugins/lisa-typescript/rules/lisa.md +0 -37
- package/plugins/src/base/rules/README.md +0 -240
- package/plugins/src/base/rules/coding-philosophy.md +0 -428
- package/plugins/src/base/rules/verfication.md +0 -594
- package/plugins/src/expo/rules/expo-verification.md +0 -261
- package/plugins/src/rails/rules/lisa.md +0 -42
- package/plugins/src/rails/rules/rails-conventions.md +0 -176
- package/plugins/src/typescript/rules/lisa.md +0 -37
- /package/{plugins/lisa → all/copy-overwrite/.claude}/rules/coding-philosophy.md +0 -0
- /package/{plugins/lisa/rules/verfication.md → all/copy-overwrite/.claude/rules/verification.md} +0 -0
- /package/{plugins/lisa-expo → expo/copy-overwrite/.claude}/rules/expo-verification.md +0 -0
- /package/{plugins/lisa-rails → rails/copy-overwrite/.claude}/rules/rails-conventions.md +0 -0
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
# Claude Code Configuration
|
|
2
|
-
|
|
3
|
-
This directory contains Claude Code configuration files that customize AI-assisted development workflows.
|
|
4
|
-
|
|
5
|
-
## Directory Structure
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
.claude/
|
|
9
|
-
├── settings.json # Main Claude Code settings
|
|
10
|
-
├── settings.local.json # Local overrides (gitignored)
|
|
11
|
-
├── settings.local.json.example # Template for local settings
|
|
12
|
-
├── agents/ # Custom agent definitions
|
|
13
|
-
├── hooks/ # Automation hooks
|
|
14
|
-
├── rules/ # Always-on governance rules
|
|
15
|
-
└── skills/ # Skill definitions (colon-namespaced)
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Settings
|
|
19
|
-
|
|
20
|
-
### Environment Variables
|
|
21
|
-
|
|
22
|
-
| Variable | Default | Description |
|
|
23
|
-
|----------|---------|-------------|
|
|
24
|
-
| `BASH_DEFAULT_TIMEOUT_MS` | 1800000 (30 min) | Default timeout for bash commands |
|
|
25
|
-
| `BASH_MAX_TIMEOUT_MS` | 7200000 (2 hours) | Maximum allowed timeout |
|
|
26
|
-
|
|
27
|
-
### Hooks
|
|
28
|
-
|
|
29
|
-
| Event | Hook | Purpose |
|
|
30
|
-
|-------|------|---------|
|
|
31
|
-
| `SessionStart` | `install-pkgs.sh` | Install dependencies when session starts |
|
|
32
|
-
| `PostToolUse` | `format-on-edit.sh` | Auto-format files after Write/Edit operations |
|
|
33
|
-
| `Notification` | `notify-ntfy.sh` | Send notifications via ntfy.sh |
|
|
34
|
-
| `Stop` | `notify-ntfy.sh` | Notify when session ends |
|
|
35
|
-
|
|
36
|
-
## Plugins
|
|
37
|
-
|
|
38
|
-
The `enabledPlugins` section in `settings.json` references Claude Code plugins. These extend Claude Code functionality with additional capabilities.
|
|
39
|
-
|
|
40
|
-
### Plugin Sources
|
|
41
|
-
|
|
42
|
-
| Source | Description | Registration |
|
|
43
|
-
|--------|-------------|--------------|
|
|
44
|
-
| `claude-plugins-official` | Official Anthropic plugins | Built-in, no registration needed |
|
|
45
|
-
| `cc-marketplace` | Community marketplace | Available at [Claude Code Marketplace](https://marketplace.claude.ai) |
|
|
46
|
-
|
|
47
|
-
### Enabled Plugins
|
|
48
|
-
|
|
49
|
-
| Plugin | Source | Purpose |
|
|
50
|
-
|--------|--------|---------|
|
|
51
|
-
| `typescript-lsp` | `claude-plugins-official` | TypeScript language server integration |
|
|
52
|
-
| `safety-net` | `cc-marketplace` | Backup and safety features |
|
|
53
|
-
| `code-simplifier` | `claude-plugins-official` | Code complexity reduction suggestions |
|
|
54
|
-
| `code-review` | `claude-plugins-official` | Automated code review capabilities |
|
|
55
|
-
| `playwright` | `claude-plugins-official` | Playwright test integration |
|
|
56
|
-
|
|
57
|
-
### Installing Plugins
|
|
58
|
-
|
|
59
|
-
1. **Official plugins** are available by default in Claude Code
|
|
60
|
-
2. **Marketplace plugins** can be installed via the Claude Code settings UI
|
|
61
|
-
3. **Third-party plugins** require following their installation instructions
|
|
62
|
-
|
|
63
|
-
### Plugin Availability
|
|
64
|
-
|
|
65
|
-
Not all plugins may be available in all Claude Code installations:
|
|
66
|
-
|
|
67
|
-
- Some plugins require specific Claude Code versions
|
|
68
|
-
- Marketplace plugins require marketplace access
|
|
69
|
-
- Enterprise installations may restrict available plugins
|
|
70
|
-
|
|
71
|
-
If a plugin is not available, Claude Code will ignore it gracefully.
|
|
72
|
-
|
|
73
|
-
## Local Settings Override
|
|
74
|
-
|
|
75
|
-
Create `settings.local.json` to override settings for your local environment:
|
|
76
|
-
|
|
77
|
-
```json
|
|
78
|
-
{
|
|
79
|
-
"env": {
|
|
80
|
-
"CUSTOM_API_KEY": "your-key-here"
|
|
81
|
-
},
|
|
82
|
-
"hooks": {
|
|
83
|
-
"PostToolUse": []
|
|
84
|
-
},
|
|
85
|
-
"enabledPlugins": {
|
|
86
|
-
"playwright": false
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
This file should be:
|
|
92
|
-
- Added to `.gitignore`
|
|
93
|
-
- Never committed to version control
|
|
94
|
-
- Used for machine-specific settings
|
|
95
|
-
|
|
96
|
-
## Agents
|
|
97
|
-
|
|
98
|
-
Custom agent definitions in `agents/` provide specialized AI personas for different tasks:
|
|
99
|
-
|
|
100
|
-
| Agent | Purpose |
|
|
101
|
-
|-------|---------|
|
|
102
|
-
| `agent-architect.md` | Agent file design and optimization |
|
|
103
|
-
| `architecture-specialist.md` | Implementation design, dependency mapping, pattern evaluation |
|
|
104
|
-
| `git-history-analyzer.md` | Git history analysis |
|
|
105
|
-
| `hooks-expert.md` | Claude Code hooks expertise |
|
|
106
|
-
| `implementer.md` | Code implementation with TDD enforcement |
|
|
107
|
-
| `learner.md` | Post-implementation learning and skill creation |
|
|
108
|
-
| `performance-specialist.md` | N+1 queries, algorithmic complexity, memory leaks |
|
|
109
|
-
| `product-specialist.md` | User flows, acceptance criteria, UX validation |
|
|
110
|
-
| `quality-specialist.md` | Code correctness, coding philosophy, test coverage review |
|
|
111
|
-
| `security-specialist.md` | Threat modeling (STRIDE), OWASP Top 10, auth/secrets review |
|
|
112
|
-
| `skill-evaluator.md` | Skill quality assessment |
|
|
113
|
-
| `slash-command-architect.md` | Command design |
|
|
114
|
-
| `test-specialist.md` | Test strategy, test writing, coverage analysis |
|
|
115
|
-
| `web-search-researcher.md` | Web research tasks |
|
|
116
|
-
|
|
117
|
-
## Skills
|
|
118
|
-
|
|
119
|
-
Skills use colon-namespaced directories (e.g., `plan:create/`) and are invoked via `/skill-name` in Claude Code.
|
|
120
|
-
|
|
121
|
-
### Plan Skills
|
|
122
|
-
|
|
123
|
-
| Skill | Purpose |
|
|
124
|
-
|-------|---------|
|
|
125
|
-
| `plan:create` | Create implementation plans from ticket URLs, file paths, or text descriptions |
|
|
126
|
-
| `plan:implement` | Execute an existing plan with an Agent Team |
|
|
127
|
-
| `plan:add-test-coverage` | Increase test coverage to a target threshold |
|
|
128
|
-
| `plan:fix-linter-error` | Fix all violations of specific ESLint rules |
|
|
129
|
-
| `plan:local-code-review` | Review local branch changes against main |
|
|
130
|
-
| `plan:lower-code-complexity` | Reduce cognitive complexity threshold |
|
|
131
|
-
| `plan:reduce-max-lines` | Reduce max file lines threshold |
|
|
132
|
-
| `plan:reduce-max-lines-per-function` | Reduce max function lines threshold |
|
|
133
|
-
|
|
134
|
-
### Git Skills
|
|
135
|
-
|
|
136
|
-
| Skill | Purpose |
|
|
137
|
-
|-------|---------|
|
|
138
|
-
| `git:commit` | Create conventional commits for current changes |
|
|
139
|
-
| `git:commit-and-submit-pr` | Commit changes and create/update a pull request |
|
|
140
|
-
| `git:submit-pr` | Push changes and create or update a pull request |
|
|
141
|
-
| `git:prune` | Prune local branches deleted on remote |
|
|
142
|
-
|
|
143
|
-
### Integration Skills
|
|
144
|
-
|
|
145
|
-
| Skill | Purpose |
|
|
146
|
-
|-------|---------|
|
|
147
|
-
| `jira:create` | Create JIRA epics, stories, and tasks |
|
|
148
|
-
| `jira:verify` | Verify JIRA ticket meets organizational standards |
|
|
149
|
-
| `jira:sync` | Sync plan progress to linked JIRA tickets |
|
|
150
|
-
| `sonarqube:check` | Check SonarQube/SonarCloud quality gate failures |
|
|
151
|
-
| `sonarqube:fix` | Fix SonarQube quality gate failures |
|
|
152
|
-
| `pull-request:review` | Check and implement PR review comments |
|
|
153
|
-
| `security:zap-scan` | Run OWASP ZAP baseline security scan |
|
|
154
|
-
|
|
155
|
-
### Utility Skills
|
|
156
|
-
|
|
157
|
-
| Skill | Purpose |
|
|
158
|
-
|-------|---------|
|
|
159
|
-
| `tasks:load` | Load tasks from project directory into session |
|
|
160
|
-
| `tasks:sync` | Export session tasks to project directory |
|
|
161
|
-
| `skill-creator` | Guide for creating new skills |
|
|
162
|
-
| `agent-design-best-practices` | Best practices for designing agent files |
|
|
163
|
-
| `jsdoc-best-practices` | JSDoc documentation standards |
|
|
164
|
-
|
|
165
|
-
See each skill's `SKILL.md` for detailed documentation.
|
|
166
|
-
|
|
167
|
-
## Customization
|
|
168
|
-
|
|
169
|
-
### Adding Custom Skills
|
|
170
|
-
|
|
171
|
-
Skills contain implementation logic and use hyphen-separated naming:
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
mkdir -p .claude/skills/my-namespace-my-skill
|
|
175
|
-
cat > .claude/skills/my-namespace-my-skill/SKILL.md << 'EOF'
|
|
176
|
-
---
|
|
177
|
-
name: my-namespace-my-skill
|
|
178
|
-
description: "What this skill does"
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
# My Skill
|
|
182
|
-
|
|
183
|
-
Instructions for the skill...
|
|
184
|
-
EOF
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### Adding Custom Commands
|
|
188
|
-
|
|
189
|
-
Commands are user-facing pass-throughs to skills. Directory nesting creates colon-separated names in the UI (e.g., `my-namespace/my-skill.md` becomes `/my-namespace:my-skill`):
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
mkdir -p .claude/commands/my-namespace
|
|
193
|
-
cat > .claude/commands/my-namespace/my-skill.md << 'EOF'
|
|
194
|
-
---
|
|
195
|
-
description: "What this command does"
|
|
196
|
-
allowed-tools: ["Skill"]
|
|
197
|
-
argument-hint: "<arguments>"
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
Use the /my-namespace-my-skill skill to do the thing. $ARGUMENTS
|
|
201
|
-
EOF
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Adding Custom Agents
|
|
205
|
-
|
|
206
|
-
```bash
|
|
207
|
-
cat > .claude/agents/my-agent.md << 'EOF'
|
|
208
|
-
# My Agent
|
|
209
|
-
|
|
210
|
-
## Role
|
|
211
|
-
Specialized for specific tasks...
|
|
212
|
-
|
|
213
|
-
## Capabilities
|
|
214
|
-
- Capability 1
|
|
215
|
-
- Capability 2
|
|
216
|
-
|
|
217
|
-
## Instructions
|
|
218
|
-
How to behave...
|
|
219
|
-
EOF
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## Troubleshooting
|
|
223
|
-
|
|
224
|
-
### Hooks Not Running
|
|
225
|
-
|
|
226
|
-
1. Check file permissions: `chmod +x .claude/hooks/*.sh`
|
|
227
|
-
2. Verify `$CLAUDE_PROJECT_DIR` is set correctly
|
|
228
|
-
3. Check hook timeout settings
|
|
229
|
-
|
|
230
|
-
### Plugins Not Loading
|
|
231
|
-
|
|
232
|
-
1. Verify plugin is installed in your Claude Code installation
|
|
233
|
-
2. Check marketplace access if using marketplace plugins
|
|
234
|
-
3. Review Claude Code logs for plugin errors
|
|
235
|
-
|
|
236
|
-
### Skills Not Found
|
|
237
|
-
|
|
238
|
-
1. Ensure skill directory contains a `SKILL.md` file with correct frontmatter
|
|
239
|
-
2. Restart Claude Code to reload skills
|
|
240
|
-
3. Check for syntax errors in skill definition
|
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
# Coding Philosophy
|
|
2
|
-
|
|
3
|
-
This rule enforces the core coding philosophy: **immutability**, **predictable structure**, **functional transformations**, **test-driven development**, **clean deletion**, and **simplicity**.
|
|
4
|
-
|
|
5
|
-
## Guiding Principles: YAGNI + SOLID + DRY + KISS
|
|
6
|
-
|
|
7
|
-
Follow these software engineering principles, **deferring to Occam's Razor/KISS whenever principles conflict**:
|
|
8
|
-
|
|
9
|
-
### KISS (Keep It Simple, Stupid) - The Tiebreaker
|
|
10
|
-
|
|
11
|
-
When principles conflict, **always choose the simpler solution**.
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
// KISS: Simple direct approach
|
|
15
|
-
const isAdmin = user.role === "admin";
|
|
16
|
-
|
|
17
|
-
// Over-engineered: Abstraction without value
|
|
18
|
-
const isAdmin = RoleChecker.getInstance().checkRole(user, RoleTypes.ADMIN);
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### YAGNI (You Ain't Gonna Need It)
|
|
22
|
-
|
|
23
|
-
Don't build features, abstractions, or flexibility you don't need **right now**.
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
// Correct: Solve today's problem
|
|
27
|
-
const formatDate = (date: Date) => date.toISOString().split("T")[0];
|
|
28
|
-
|
|
29
|
-
// Wrong: Building for hypothetical future needs
|
|
30
|
-
const formatDate = (date: Date, format?: string, locale?: string, timezone?: string) => {
|
|
31
|
-
// 50 lines handling cases that may never be used
|
|
32
|
-
};
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### DRY (Don't Repeat Yourself) - With KISS Constraint
|
|
36
|
-
|
|
37
|
-
Extract duplication only when:
|
|
38
|
-
1. The same logic appears **3+ times**
|
|
39
|
-
2. The abstraction is **simpler** than the duplication
|
|
40
|
-
3. The extracted code has a **clear single purpose**
|
|
41
|
-
|
|
42
|
-
### SOLID Principles - Applied Pragmatically
|
|
43
|
-
|
|
44
|
-
| Principle | Apply When | Skip When |
|
|
45
|
-
| ------------------------- | --------------------------------------------- | --------------------------------------- |
|
|
46
|
-
| **S**ingle Responsibility | Function does 2+ unrelated things | Splitting adds complexity |
|
|
47
|
-
| **O**pen/Closed | Extension points have clear use cases | No foreseeable extensions |
|
|
48
|
-
| **L**iskov Substitution | Using inheritance hierarchies | Using composition (preferred) |
|
|
49
|
-
| **I**nterface Segregation | Consumers need different subsets | Interface is already small |
|
|
50
|
-
| **D**ependency Inversion | Testing requires mocking external services | Direct dependency is simpler |
|
|
51
|
-
|
|
52
|
-
### Decision Framework
|
|
53
|
-
|
|
54
|
-
When unsure, ask in order:
|
|
55
|
-
1. **Do I need this now?** (YAGNI) - If no, don't build it
|
|
56
|
-
2. **Is there a simpler way?** (KISS) - Choose the simpler option
|
|
57
|
-
3. **Am I repeating myself 3+ times?** (DRY) - Extract if the abstraction is simpler
|
|
58
|
-
4. **Does this function do one thing?** (SOLID-SRP) - Split only if clearer
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## Core Principles
|
|
63
|
-
|
|
64
|
-
### 1. Immutability First
|
|
65
|
-
|
|
66
|
-
Never mutate data. Always create new references.
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
// Correct - spread creates new object
|
|
70
|
-
const updated = { ...user, name: "New Name" };
|
|
71
|
-
|
|
72
|
-
// Incorrect - mutation
|
|
73
|
-
user.name = "New Name";
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 2. Function Structure Ordering
|
|
77
|
-
|
|
78
|
-
All functions, hooks, and components follow a strict ordering:
|
|
79
|
-
|
|
80
|
-
```text
|
|
81
|
-
1. Variable definitions and derived state (const, useState, useMemo, useCallback)
|
|
82
|
-
2. Side effects (useEffect, function calls with no return value)
|
|
83
|
-
3. Return statement
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 3. Functional Transformations
|
|
87
|
-
|
|
88
|
-
Use `map`, `filter`, `reduce` instead of imperative loops and mutations.
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
// Correct - functional transformation
|
|
92
|
-
const names = users.map(u => u.name);
|
|
93
|
-
|
|
94
|
-
// Incorrect - imperative mutation
|
|
95
|
-
const names = [];
|
|
96
|
-
users.forEach(u => names.push(u.name));
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### 4. Test-Driven Development (TDD)
|
|
100
|
-
|
|
101
|
-
**Always write failing tests before implementation code.** This is mandatory, not optional.
|
|
102
|
-
|
|
103
|
-
```text
|
|
104
|
-
TDD Cycle:
|
|
105
|
-
1. RED: Write a failing test that defines expected behavior
|
|
106
|
-
2. GREEN: Write the minimum code to make the test pass
|
|
107
|
-
3. REFACTOR: Clean up while keeping tests green
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### 5. Clean Deletion
|
|
111
|
-
|
|
112
|
-
**Delete old code completely.** No deprecation warnings, migration shims, or backward-compatibility layers unless explicitly requested.
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// Correct: Remove the old code entirely
|
|
116
|
-
const calculateScore = (player: Player): number => player.stats.overall;
|
|
117
|
-
|
|
118
|
-
// Wrong: Keeping deprecated versions around
|
|
119
|
-
/** @deprecated Use calculateScore instead */
|
|
120
|
-
const getPlayerScore = (player: Player): number => calculateScore(player);
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
**Clean deletion rules:**
|
|
124
|
-
- When replacing code, delete the old version completely
|
|
125
|
-
- Never create `V2`, `New`, or `Old` suffixed functions/variables
|
|
126
|
-
- Never add `@deprecated` comments - just remove the code
|
|
127
|
-
- Never write migration code unless explicitly asked
|
|
128
|
-
- Trust git history for recovery if needed
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## Quick Reference
|
|
133
|
-
|
|
134
|
-
### Variable Declaration
|
|
135
|
-
|
|
136
|
-
| Pattern | Status | Example |
|
|
137
|
-
| ------- | --------- | ----------------------------- |
|
|
138
|
-
| `const` | Required | `const value = calculate();` |
|
|
139
|
-
| `let` | Forbidden | Use ternary or reduce instead |
|
|
140
|
-
| `var` | Forbidden | Never use |
|
|
141
|
-
|
|
142
|
-
### Array Operations
|
|
143
|
-
|
|
144
|
-
| Instead of | Use |
|
|
145
|
-
| ----------------------- | -------------------------------------------- |
|
|
146
|
-
| `arr.push(item)` | `[...arr, item]` |
|
|
147
|
-
| `arr.pop()` | `arr.slice(0, -1)` |
|
|
148
|
-
| `arr.splice(i, 1)` | `arr.filter((_, idx) => idx !== i)` |
|
|
149
|
-
| `arr.sort()` | `[...arr].sort()` |
|
|
150
|
-
| `arr[i] = value` | `arr.map((v, idx) => idx === i ? value : v)` |
|
|
151
|
-
| `forEach` with mutation | `reduce` or `map` |
|
|
152
|
-
|
|
153
|
-
### Object Operations
|
|
154
|
-
|
|
155
|
-
| Instead of | Use |
|
|
156
|
-
| ------------------------- | ----------------------------- |
|
|
157
|
-
| `obj.key = value` | `{ ...obj, key: value }` |
|
|
158
|
-
| `delete obj.key` | `({ key: _, ...rest } = obj)` |
|
|
159
|
-
| `Object.assign(obj, ...)` | `{ ...obj, ...other }` |
|
|
160
|
-
|
|
161
|
-
### Building Lookup Objects
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
// Correct - reduce with spread
|
|
165
|
-
const lookup = items.reduce(
|
|
166
|
-
(acc, item) => ({ ...acc, [item.id]: item }),
|
|
167
|
-
{} as Record<string, Item>
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
// Incorrect - forEach with Map.set
|
|
171
|
-
const lookup = new Map();
|
|
172
|
-
items.forEach(item => lookup.set(item.id, item));
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Conditional Values
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
// Correct - ternary expression
|
|
179
|
-
const status = isComplete ? "done" : "pending";
|
|
180
|
-
|
|
181
|
-
// Incorrect - let with reassignment
|
|
182
|
-
let status = "pending";
|
|
183
|
-
if (isComplete) {
|
|
184
|
-
status = "done";
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## Hook Structure Example
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
export const usePlayerData = (playerId: string) => {
|
|
194
|
-
// 1. VARIABLES & STATE (first)
|
|
195
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
196
|
-
const { data } = useQuery(GetPlayerDocument, { variables: { playerId } });
|
|
197
|
-
|
|
198
|
-
const playerName = useMemo(() => data?.player?.name ?? "Unknown", [data]);
|
|
199
|
-
|
|
200
|
-
const handleRefresh = useCallback(() => {
|
|
201
|
-
refetch();
|
|
202
|
-
}, [refetch]);
|
|
203
|
-
|
|
204
|
-
// 2. SIDE EFFECTS (second)
|
|
205
|
-
useEffect(() => {
|
|
206
|
-
console.log("Player loaded:", playerName);
|
|
207
|
-
}, [playerName]);
|
|
208
|
-
|
|
209
|
-
// 3. RETURN (last)
|
|
210
|
-
return { playerName, isLoading, handleRefresh };
|
|
211
|
-
};
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## Container Component Example
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
const PlayerCardContainer: React.FC<Props> = ({ playerId }) => {
|
|
218
|
-
// 1. VARIABLES & STATE
|
|
219
|
-
const { data, loading } = useQuery(GetPlayerDocument, { variables: { playerId } });
|
|
220
|
-
const { colors } = useTheme();
|
|
221
|
-
|
|
222
|
-
const formattedStats = useMemo(
|
|
223
|
-
() => data?.stats?.map(s => ({ ...s, display: formatStat(s) })) ?? [],
|
|
224
|
-
[data?.stats]
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
const handlePress = useCallback(() => {
|
|
228
|
-
router.push(`/players/${playerId}`);
|
|
229
|
-
}, [playerId]);
|
|
230
|
-
|
|
231
|
-
// 2. SIDE EFFECTS (none in this example)
|
|
232
|
-
|
|
233
|
-
// 3. RETURN
|
|
234
|
-
return (
|
|
235
|
-
<PlayerCardView
|
|
236
|
-
stats={formattedStats}
|
|
237
|
-
colors={colors}
|
|
238
|
-
loading={loading}
|
|
239
|
-
onPress={handlePress}
|
|
240
|
-
/>
|
|
241
|
-
);
|
|
242
|
-
};
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
## Utility Function Example
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
export const calculateTeamRankings = (
|
|
249
|
-
players: readonly Player[]
|
|
250
|
-
): readonly TeamRanking[] => {
|
|
251
|
-
// 1. VARIABLES & DERIVED VALUES
|
|
252
|
-
const validPlayers = players.filter(p => p.team && p.score != null);
|
|
253
|
-
|
|
254
|
-
const teamScores = validPlayers.reduce(
|
|
255
|
-
(acc, player) => ({
|
|
256
|
-
...acc,
|
|
257
|
-
[player.team.id]: {
|
|
258
|
-
teamId: player.team.id,
|
|
259
|
-
totalScore: (acc[player.team.id]?.totalScore ?? 0) + player.score,
|
|
260
|
-
count: (acc[player.team.id]?.count ?? 0) + 1,
|
|
261
|
-
},
|
|
262
|
-
}),
|
|
263
|
-
{} as Record<string, { teamId: string; totalScore: number; count: number }>
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
const rankings = Object.values(teamScores).map(t => ({
|
|
267
|
-
teamId: t.teamId,
|
|
268
|
-
avgScore: t.totalScore / t.count,
|
|
269
|
-
}));
|
|
270
|
-
|
|
271
|
-
const sorted = [...rankings].sort((a, b) => b.avgScore - a.avgScore);
|
|
272
|
-
|
|
273
|
-
// 2. NO SIDE EFFECTS IN PURE FUNCTIONS
|
|
274
|
-
|
|
275
|
-
// 3. RETURN
|
|
276
|
-
return sorted;
|
|
277
|
-
};
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
---
|
|
281
|
-
|
|
282
|
-
## Anti-Patterns to Avoid
|
|
283
|
-
|
|
284
|
-
### Never use `let` for conditional assignment
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
// Wrong
|
|
288
|
-
let result;
|
|
289
|
-
if (condition) {
|
|
290
|
-
result = valueA;
|
|
291
|
-
} else {
|
|
292
|
-
result = valueB;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Correct
|
|
296
|
-
const result = condition ? valueA : valueB;
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
### Never mutate arrays
|
|
300
|
-
|
|
301
|
-
```typescript
|
|
302
|
-
// Wrong
|
|
303
|
-
const items = [];
|
|
304
|
-
data.forEach(d => items.push(transform(d)));
|
|
305
|
-
|
|
306
|
-
// Correct
|
|
307
|
-
const items = data.map(d => transform(d));
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Never use Map when Record suffices
|
|
311
|
-
|
|
312
|
-
```typescript
|
|
313
|
-
// Wrong
|
|
314
|
-
const lookup = new Map<string, User>();
|
|
315
|
-
users.forEach(u => lookup.set(u.id, u));
|
|
316
|
-
const user = lookup.get(userId);
|
|
317
|
-
|
|
318
|
-
// Correct
|
|
319
|
-
const lookup = users.reduce(
|
|
320
|
-
(acc, u) => ({ ...acc, [u.id]: u }),
|
|
321
|
-
{} as Record<string, User>
|
|
322
|
-
);
|
|
323
|
-
const user = lookup[userId];
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Never sort in place
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
// Wrong - mutates original
|
|
330
|
-
const sorted = items.sort((a, b) => a.value - b.value);
|
|
331
|
-
|
|
332
|
-
// Correct - creates new array
|
|
333
|
-
const sorted = [...items].sort((a, b) => a.value - b.value);
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### Never place useEffect before variable definitions
|
|
337
|
-
|
|
338
|
-
```typescript
|
|
339
|
-
// Wrong
|
|
340
|
-
useEffect(() => {
|
|
341
|
-
/* ... */
|
|
342
|
-
}, [value]);
|
|
343
|
-
const value = useMemo(() => calculate(), [dep]);
|
|
344
|
-
|
|
345
|
-
// Correct
|
|
346
|
-
const value = useMemo(() => calculate(), [dep]);
|
|
347
|
-
useEffect(() => {
|
|
348
|
-
/* ... */
|
|
349
|
-
}, [value]);
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
---
|
|
353
|
-
|
|
354
|
-
## Immutable Patterns Reference
|
|
355
|
-
|
|
356
|
-
### Building Lookup Objects with Reduce
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
const colorMap =
|
|
360
|
-
edges?.reduce(
|
|
361
|
-
(acc, edge) => (edge.color ? { ...acc, [edge.tagId]: edge.color } : acc),
|
|
362
|
-
{} as Record<string, string>
|
|
363
|
-
) ?? {};
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Accumulating Multiple Properties
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
const teamGprAccumulator = validPlayers.reduce(
|
|
370
|
-
(acc, player) => {
|
|
371
|
-
const teamId = player.team?.id;
|
|
372
|
-
if (!teamId) return acc;
|
|
373
|
-
|
|
374
|
-
const existing = acc[teamId];
|
|
375
|
-
return {
|
|
376
|
-
...acc,
|
|
377
|
-
[teamId]: {
|
|
378
|
-
teamId,
|
|
379
|
-
teamName: player.team.name,
|
|
380
|
-
gprSum: (existing?.gprSum ?? 0) + player.gpr,
|
|
381
|
-
playerCount: (existing?.playerCount ?? 0) + 1,
|
|
382
|
-
},
|
|
383
|
-
};
|
|
384
|
-
},
|
|
385
|
-
{} as Record<string, { teamId: string; teamName: string; gprSum: number; playerCount: number }>
|
|
386
|
-
);
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### Nested Object Updates
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
const updated = {
|
|
393
|
-
...state,
|
|
394
|
-
user: {
|
|
395
|
-
...state.user,
|
|
396
|
-
profile: {
|
|
397
|
-
...state.user.profile,
|
|
398
|
-
avatar: newAvatar,
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
};
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
### Conditional Property Addition
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
const result = {
|
|
408
|
-
...baseObj,
|
|
409
|
-
...(condition && { optionalProp: value }),
|
|
410
|
-
};
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
### Ternary Chain for Multiple Conditions
|
|
414
|
-
|
|
415
|
-
```typescript
|
|
416
|
-
const priority = score > 90 ? "high" : score > 70 ? "medium" : "low";
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
### Readonly Types for Function Parameters
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
export const calculateTeamGprRank = (
|
|
423
|
-
leaguePlayers: readonly (PlayerWithScores | null | undefined)[],
|
|
424
|
-
myTeamId: string | null | undefined
|
|
425
|
-
): number | null => {
|
|
426
|
-
// ...
|
|
427
|
-
};
|
|
428
|
-
```
|