red64-cli 0.1.0 → 0.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/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +5 -0
- package/dist/cli/parseArgs.js.map +1 -1
- package/dist/components/init/CompleteStep.d.ts.map +1 -1
- package/dist/components/init/CompleteStep.js +2 -2
- package/dist/components/init/CompleteStep.js.map +1 -1
- package/dist/components/init/TestCheckStep.d.ts +16 -0
- package/dist/components/init/TestCheckStep.d.ts.map +1 -0
- package/dist/components/init/TestCheckStep.js +120 -0
- package/dist/components/init/TestCheckStep.js.map +1 -0
- package/dist/components/init/index.d.ts +1 -0
- package/dist/components/init/index.d.ts.map +1 -1
- package/dist/components/init/index.js +1 -0
- package/dist/components/init/index.js.map +1 -1
- package/dist/components/init/types.d.ts +9 -0
- package/dist/components/init/types.d.ts.map +1 -1
- package/dist/components/screens/InitScreen.d.ts.map +1 -1
- package/dist/components/screens/InitScreen.js +69 -6
- package/dist/components/screens/InitScreen.js.map +1 -1
- package/dist/components/screens/StartScreen.d.ts.map +1 -1
- package/dist/components/screens/StartScreen.js +89 -3
- package/dist/components/screens/StartScreen.js.map +1 -1
- package/dist/services/ConfigService.d.ts +1 -0
- package/dist/services/ConfigService.d.ts.map +1 -1
- package/dist/services/ConfigService.js.map +1 -1
- package/dist/services/ProjectDetector.d.ts +28 -0
- package/dist/services/ProjectDetector.d.ts.map +1 -0
- package/dist/services/ProjectDetector.js +236 -0
- package/dist/services/ProjectDetector.js.map +1 -0
- package/dist/services/TestRunner.d.ts +46 -0
- package/dist/services/TestRunner.d.ts.map +1 -0
- package/dist/services/TestRunner.js +85 -0
- package/dist/services/TestRunner.js.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +2 -0
- package/dist/services/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/framework/agents/claude/.claude/agents/red64/spec-impl.md +131 -2
- package/framework/agents/claude/.claude/commands/red64/spec-impl.md +24 -0
- package/framework/agents/codex/.codex/agents/red64/spec-impl.md +131 -2
- package/framework/agents/codex/.codex/commands/red64/spec-impl.md +24 -0
- package/framework/stacks/generic/feedback.md +80 -0
- package/framework/stacks/nextjs/accessibility.md +437 -0
- package/framework/stacks/nextjs/api.md +431 -0
- package/framework/stacks/nextjs/coding-style.md +282 -0
- package/framework/stacks/nextjs/commenting.md +226 -0
- package/framework/stacks/nextjs/components.md +411 -0
- package/framework/stacks/nextjs/conventions.md +333 -0
- package/framework/stacks/nextjs/css.md +310 -0
- package/framework/stacks/nextjs/error-handling.md +442 -0
- package/framework/stacks/nextjs/feedback.md +124 -0
- package/framework/stacks/nextjs/migrations.md +332 -0
- package/framework/stacks/nextjs/models.md +362 -0
- package/framework/stacks/nextjs/queries.md +410 -0
- package/framework/stacks/nextjs/responsive.md +338 -0
- package/framework/stacks/nextjs/tech-stack.md +177 -0
- package/framework/stacks/nextjs/test-writing.md +475 -0
- package/framework/stacks/nextjs/validation.md +467 -0
- package/framework/stacks/python/api.md +468 -0
- package/framework/stacks/python/authentication.md +342 -0
- package/framework/stacks/python/code-quality.md +283 -0
- package/framework/stacks/python/code-refactoring.md +315 -0
- package/framework/stacks/python/coding-style.md +462 -0
- package/framework/stacks/python/conventions.md +399 -0
- package/framework/stacks/python/error-handling.md +512 -0
- package/framework/stacks/python/feedback.md +92 -0
- package/framework/stacks/python/implement-ai-llm.md +468 -0
- package/framework/stacks/python/migrations.md +388 -0
- package/framework/stacks/python/models.md +399 -0
- package/framework/stacks/python/python.md +232 -0
- package/framework/stacks/python/queries.md +451 -0
- package/framework/stacks/python/structure.md +245 -58
- package/framework/stacks/python/tech.md +92 -35
- package/framework/stacks/python/testing.md +380 -0
- package/framework/stacks/python/validation.md +471 -0
- package/framework/stacks/rails/authentication.md +176 -0
- package/framework/stacks/rails/code-quality.md +287 -0
- package/framework/stacks/rails/code-refactoring.md +299 -0
- package/framework/stacks/rails/feedback.md +130 -0
- package/framework/stacks/rails/implement-ai-llm-with-rubyllm.md +342 -0
- package/framework/stacks/rails/rails.md +301 -0
- package/framework/stacks/rails/rails8-best-practices.md +498 -0
- package/framework/stacks/rails/rails8-css.md +573 -0
- package/framework/stacks/rails/structure.md +140 -0
- package/framework/stacks/rails/tech.md +108 -0
- package/framework/stacks/react/code-quality.md +521 -0
- package/framework/stacks/react/components.md +625 -0
- package/framework/stacks/react/data-fetching.md +586 -0
- package/framework/stacks/react/feedback.md +110 -0
- package/framework/stacks/react/forms.md +694 -0
- package/framework/stacks/react/performance.md +640 -0
- package/framework/stacks/react/product.md +22 -9
- package/framework/stacks/react/state-management.md +472 -0
- package/framework/stacks/react/structure.md +351 -44
- package/framework/stacks/react/tech.md +219 -30
- package/framework/stacks/react/testing.md +690 -0
- package/package.json +1 -1
- package/framework/stacks/node/product.md +0 -27
- package/framework/stacks/node/structure.md +0 -82
- package/framework/stacks/node/tech.md +0 -63
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Technology Stack
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
Server-rendered Rails application with progressive enhancement via Hotwire. Database-backed infrastructure for queues, caching, and real-time features (Solid Stack).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Technologies
|
|
10
|
+
|
|
11
|
+
- **Language**: Ruby 3.4.7
|
|
12
|
+
- **Framework**: Rails 8.1 (`config.load_defaults 8.1`)
|
|
13
|
+
- **Runtime**: Puma web server
|
|
14
|
+
- **Database**: SQLite3 (development and production-ready)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Key Libraries
|
|
19
|
+
|
|
20
|
+
### Frontend
|
|
21
|
+
- **Hotwire (Turbo + Stimulus)**: SPA-like interactivity without heavy JavaScript
|
|
22
|
+
- **Propshaft**: Modern asset pipeline
|
|
23
|
+
- **Importmap**: ESM-based JavaScript, no bundler
|
|
24
|
+
|
|
25
|
+
### Backend
|
|
26
|
+
- **Solid Queue**: Database-backed job processing
|
|
27
|
+
- **Solid Cache**: Database-backed caching
|
|
28
|
+
- **Solid Cable**: Database-backed Action Cable
|
|
29
|
+
- **Jbuilder**: JSON API responses
|
|
30
|
+
- **Active Storage**: File uploads with image processing
|
|
31
|
+
|
|
32
|
+
### Deployment
|
|
33
|
+
- **Kamal**: Docker-based deployment
|
|
34
|
+
- **Thruster**: HTTP caching/compression for Puma
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Development Standards
|
|
39
|
+
|
|
40
|
+
### Code Quality
|
|
41
|
+
- **RuboCop Rails Omakase**: Default Rails styling conventions
|
|
42
|
+
- Run: `bundle exec rubocop`
|
|
43
|
+
|
|
44
|
+
### Security
|
|
45
|
+
- **Brakeman**: Static security analysis (`bundle exec brakeman`)
|
|
46
|
+
- **bundler-audit**: Gem vulnerability scanning
|
|
47
|
+
- Credentials via `bin/rails credentials:edit`
|
|
48
|
+
|
|
49
|
+
### Testing
|
|
50
|
+
- **Minitest**: Default Rails testing framework
|
|
51
|
+
- **Capybara + Selenium**: System/integration tests
|
|
52
|
+
- Tests in `test/` directory with parallel execution
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Development Environment
|
|
57
|
+
|
|
58
|
+
### Required Tools
|
|
59
|
+
- Ruby 3.4.7 (see `.ruby-version`)
|
|
60
|
+
- SQLite3 2.1+
|
|
61
|
+
- Node.js (for asset compilation tooling)
|
|
62
|
+
|
|
63
|
+
### Common Commands
|
|
64
|
+
```bash
|
|
65
|
+
# Dev server
|
|
66
|
+
bin/rails server
|
|
67
|
+
|
|
68
|
+
# Console
|
|
69
|
+
bin/rails console
|
|
70
|
+
|
|
71
|
+
# Tests
|
|
72
|
+
bin/rails test
|
|
73
|
+
bin/rails test:system
|
|
74
|
+
|
|
75
|
+
# Database
|
|
76
|
+
bin/rails db:migrate
|
|
77
|
+
bin/rails db:seed
|
|
78
|
+
|
|
79
|
+
# Code quality
|
|
80
|
+
bundle exec rubocop
|
|
81
|
+
bundle exec brakeman
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Key Technical Decisions
|
|
87
|
+
|
|
88
|
+
| Decision | Rationale |
|
|
89
|
+
|----------|-----------|
|
|
90
|
+
| **Hotwire over SPA** | Simpler architecture, server-rendered with progressive enhancement |
|
|
91
|
+
| **SQLite for all** | Solid Stack enables production SQLite; simpler ops, fewer dependencies |
|
|
92
|
+
| **Importmap over bundler** | Native ESM support, no build step for JavaScript |
|
|
93
|
+
| **Solid Queue over Sidekiq** | No Redis dependency, database-backed consistency |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## AI Integration (Planned)
|
|
98
|
+
|
|
99
|
+
MediaPulse will integrate with AI services for:
|
|
100
|
+
- Content analysis and summarization
|
|
101
|
+
- Idea generation from source material
|
|
102
|
+
- Multi-platform content adaptation
|
|
103
|
+
|
|
104
|
+
Specific AI providers and patterns will be documented as implementation progresses.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
_Document standards and patterns, not every dependency. See `rails.md` for detailed Rails conventions._
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
# Code Quality Standards
|
|
2
|
+
|
|
3
|
+
Code quality conventions, linting, formatting, and type checking for React applications.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **Automate everything**: No manual enforcement; tools catch issues automatically
|
|
10
|
+
- **Fail fast**: Catch errors at write-time (IDE), commit-time (hooks), or CI
|
|
11
|
+
- **Single source of truth**: One tool per concern, no conflicting configurations
|
|
12
|
+
- **Developer experience**: Fast feedback, clear error messages, auto-fix when possible
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Tool Stack
|
|
17
|
+
|
|
18
|
+
| Tool | Purpose | Runs |
|
|
19
|
+
|------|---------|------|
|
|
20
|
+
| **TypeScript** | Static type checking | IDE, pre-commit, CI |
|
|
21
|
+
| **ESLint** | Code linting, React rules | IDE, pre-commit, CI |
|
|
22
|
+
| **Prettier** | Code formatting | IDE (on save), pre-commit |
|
|
23
|
+
| **Husky** | Git hooks | Pre-commit |
|
|
24
|
+
| **lint-staged** | Run checks on staged files only | Pre-commit |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## TypeScript Configuration
|
|
29
|
+
|
|
30
|
+
### Strict Mode (Required)
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
// tsconfig.json
|
|
34
|
+
{
|
|
35
|
+
"compilerOptions": {
|
|
36
|
+
"target": "ES2022",
|
|
37
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
38
|
+
"module": "ESNext",
|
|
39
|
+
"moduleResolution": "bundler",
|
|
40
|
+
|
|
41
|
+
// Strict type checking (all required)
|
|
42
|
+
"strict": true,
|
|
43
|
+
"noUncheckedIndexedAccess": true,
|
|
44
|
+
"noImplicitOverride": true,
|
|
45
|
+
"noUnusedLocals": true,
|
|
46
|
+
"noUnusedParameters": true,
|
|
47
|
+
"exactOptionalPropertyTypes": true,
|
|
48
|
+
"noFallthroughCasesInSwitch": true,
|
|
49
|
+
"forceConsistentCasingInFileNames": true,
|
|
50
|
+
|
|
51
|
+
// React
|
|
52
|
+
"jsx": "react-jsx",
|
|
53
|
+
|
|
54
|
+
// Module resolution
|
|
55
|
+
"resolveJsonModule": true,
|
|
56
|
+
"isolatedModules": true,
|
|
57
|
+
"esModuleInterop": true,
|
|
58
|
+
"skipLibCheck": true,
|
|
59
|
+
|
|
60
|
+
// Path aliases
|
|
61
|
+
"baseUrl": ".",
|
|
62
|
+
"paths": {
|
|
63
|
+
"@/*": ["./src/*"]
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"include": ["src"],
|
|
67
|
+
"exclude": ["node_modules"]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Key Compiler Options Explained
|
|
72
|
+
|
|
73
|
+
| Option | Effect |
|
|
74
|
+
|--------|--------|
|
|
75
|
+
| `strict` | Enables all strict checks (required) |
|
|
76
|
+
| `noUncheckedIndexedAccess` | `arr[0]` returns `T \| undefined` |
|
|
77
|
+
| `exactOptionalPropertyTypes` | `{ x?: string }` means `string \| undefined`, not `string \| undefined \| null` |
|
|
78
|
+
| `noImplicitOverride` | Must use `override` keyword for overridden methods |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## ESLint Configuration
|
|
83
|
+
|
|
84
|
+
### Flat Config (ESLint 9+)
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// eslint.config.js
|
|
88
|
+
import js from '@eslint/js';
|
|
89
|
+
import typescript from '@typescript-eslint/eslint-plugin';
|
|
90
|
+
import tsParser from '@typescript-eslint/parser';
|
|
91
|
+
import react from 'eslint-plugin-react';
|
|
92
|
+
import reactHooks from 'eslint-plugin-react-hooks';
|
|
93
|
+
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
|
94
|
+
|
|
95
|
+
export default [
|
|
96
|
+
// Base JS rules
|
|
97
|
+
js.configs.recommended,
|
|
98
|
+
|
|
99
|
+
// TypeScript files
|
|
100
|
+
{
|
|
101
|
+
files: ['**/*.{ts,tsx}'],
|
|
102
|
+
languageOptions: {
|
|
103
|
+
parser: tsParser,
|
|
104
|
+
parserOptions: {
|
|
105
|
+
project: './tsconfig.json',
|
|
106
|
+
ecmaFeatures: { jsx: true },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
plugins: {
|
|
110
|
+
'@typescript-eslint': typescript,
|
|
111
|
+
'react': react,
|
|
112
|
+
'react-hooks': reactHooks,
|
|
113
|
+
'jsx-a11y': jsxA11y,
|
|
114
|
+
},
|
|
115
|
+
settings: {
|
|
116
|
+
react: { version: 'detect' },
|
|
117
|
+
},
|
|
118
|
+
rules: {
|
|
119
|
+
// TypeScript
|
|
120
|
+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
121
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
122
|
+
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
|
|
123
|
+
'@typescript-eslint/no-non-null-assertion': 'warn',
|
|
124
|
+
'@typescript-eslint/prefer-nullish-coalescing': 'error',
|
|
125
|
+
'@typescript-eslint/prefer-optional-chain': 'error',
|
|
126
|
+
|
|
127
|
+
// React
|
|
128
|
+
'react/react-in-jsx-scope': 'off', // Not needed in React 17+
|
|
129
|
+
'react/prop-types': 'off', // Using TypeScript
|
|
130
|
+
'react/jsx-no-target-blank': 'error',
|
|
131
|
+
'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
|
|
132
|
+
'react/self-closing-comp': 'error',
|
|
133
|
+
|
|
134
|
+
// React Hooks
|
|
135
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
136
|
+
'react-hooks/exhaustive-deps': 'warn',
|
|
137
|
+
|
|
138
|
+
// Accessibility
|
|
139
|
+
'jsx-a11y/alt-text': 'error',
|
|
140
|
+
'jsx-a11y/anchor-has-content': 'error',
|
|
141
|
+
'jsx-a11y/click-events-have-key-events': 'warn',
|
|
142
|
+
'jsx-a11y/no-static-element-interactions': 'warn',
|
|
143
|
+
|
|
144
|
+
// General
|
|
145
|
+
'no-console': ['warn', { allow: ['warn', 'error'] }],
|
|
146
|
+
'prefer-const': 'error',
|
|
147
|
+
'no-var': 'error',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// Test files (relaxed rules)
|
|
152
|
+
{
|
|
153
|
+
files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
|
|
154
|
+
rules: {
|
|
155
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
156
|
+
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// Ignore patterns
|
|
161
|
+
{
|
|
162
|
+
ignores: ['dist/**', 'node_modules/**', 'coverage/**', '*.config.js'],
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Prettier Configuration
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
// .prettierrc
|
|
173
|
+
{
|
|
174
|
+
"semi": true,
|
|
175
|
+
"singleQuote": true,
|
|
176
|
+
"tabWidth": 2,
|
|
177
|
+
"trailingComma": "es5",
|
|
178
|
+
"printWidth": 100,
|
|
179
|
+
"bracketSpacing": true,
|
|
180
|
+
"bracketSameLine": false,
|
|
181
|
+
"arrowParens": "always",
|
|
182
|
+
"endOfLine": "lf",
|
|
183
|
+
"plugins": ["prettier-plugin-tailwindcss"]
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
// .prettierignore
|
|
189
|
+
dist
|
|
190
|
+
node_modules
|
|
191
|
+
coverage
|
|
192
|
+
pnpm-lock.yaml
|
|
193
|
+
*.md
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Important**: Let Prettier handle formatting; disable ESLint formatting rules to avoid conflicts.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Pre-commit Hooks
|
|
201
|
+
|
|
202
|
+
### Husky Setup
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Install
|
|
206
|
+
pnpm add -D husky lint-staged
|
|
207
|
+
|
|
208
|
+
# Initialize Husky
|
|
209
|
+
pnpm exec husky init
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Pre-commit Hook
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# .husky/pre-commit
|
|
216
|
+
pnpm lint-staged
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### lint-staged Configuration
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
// package.json
|
|
223
|
+
{
|
|
224
|
+
"lint-staged": {
|
|
225
|
+
"*.{ts,tsx}": [
|
|
226
|
+
"eslint --fix",
|
|
227
|
+
"prettier --write"
|
|
228
|
+
],
|
|
229
|
+
"*.{json,md,yml,yaml}": [
|
|
230
|
+
"prettier --write"
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Behavior**: Only staged files are checked, keeping commits fast.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## VS Code Settings
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
// .vscode/settings.json
|
|
244
|
+
{
|
|
245
|
+
// Format on save
|
|
246
|
+
"editor.formatOnSave": true,
|
|
247
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
248
|
+
|
|
249
|
+
// ESLint
|
|
250
|
+
"eslint.validate": ["javascript", "typescript", "javascriptreact", "typescriptreact"],
|
|
251
|
+
"editor.codeActionsOnSave": {
|
|
252
|
+
"source.fixAll.eslint": "explicit"
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
// TypeScript
|
|
256
|
+
"typescript.preferences.importModuleSpecifier": "non-relative",
|
|
257
|
+
"typescript.suggest.autoImports": true,
|
|
258
|
+
"typescript.updateImportsOnFileMove.enabled": "always",
|
|
259
|
+
|
|
260
|
+
// Tailwind CSS
|
|
261
|
+
"tailwindCSS.experimental.classRegex": [
|
|
262
|
+
["cn\\(([^)]*)\\)", "'([^']*)'"]
|
|
263
|
+
],
|
|
264
|
+
|
|
265
|
+
// File associations
|
|
266
|
+
"files.associations": {
|
|
267
|
+
"*.css": "tailwindcss"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Recommended Extensions
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
// .vscode/extensions.json
|
|
276
|
+
{
|
|
277
|
+
"recommendations": [
|
|
278
|
+
"dbaeumer.vscode-eslint",
|
|
279
|
+
"esbenp.prettier-vscode",
|
|
280
|
+
"bradlc.vscode-tailwindcss",
|
|
281
|
+
"usernamehw.errorlens",
|
|
282
|
+
"yoavbls.pretty-ts-errors"
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Type Safety Patterns
|
|
290
|
+
|
|
291
|
+
### Avoid `any`
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// BAD
|
|
295
|
+
function parseData(data: any) {
|
|
296
|
+
return data.value;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// GOOD - Use unknown and validate
|
|
300
|
+
function parseData(data: unknown): string {
|
|
301
|
+
if (typeof data === 'object' && data !== null && 'value' in data) {
|
|
302
|
+
return String(data.value);
|
|
303
|
+
}
|
|
304
|
+
throw new Error('Invalid data');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// BETTER - Use Zod for runtime validation
|
|
308
|
+
import { z } from 'zod';
|
|
309
|
+
|
|
310
|
+
const DataSchema = z.object({ value: z.string() });
|
|
311
|
+
|
|
312
|
+
function parseData(data: unknown) {
|
|
313
|
+
return DataSchema.parse(data).value;
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Type-safe Event Handlers
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// BAD
|
|
321
|
+
const handleChange = (e: any) => {
|
|
322
|
+
setValue(e.target.value);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// GOOD
|
|
326
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
327
|
+
setValue(e.target.value);
|
|
328
|
+
};
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Discriminated Unions for State
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
// BAD
|
|
335
|
+
interface State {
|
|
336
|
+
loading: boolean;
|
|
337
|
+
data?: User[];
|
|
338
|
+
error?: string;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// GOOD - State is always valid
|
|
342
|
+
type State =
|
|
343
|
+
| { status: 'idle' }
|
|
344
|
+
| { status: 'loading' }
|
|
345
|
+
| { status: 'success'; data: User[] }
|
|
346
|
+
| { status: 'error'; error: string };
|
|
347
|
+
|
|
348
|
+
// Usage with exhaustive checking
|
|
349
|
+
function render(state: State) {
|
|
350
|
+
switch (state.status) {
|
|
351
|
+
case 'idle':
|
|
352
|
+
return <Idle />;
|
|
353
|
+
case 'loading':
|
|
354
|
+
return <Loading />;
|
|
355
|
+
case 'success':
|
|
356
|
+
return <UserList data={state.data} />;
|
|
357
|
+
case 'error':
|
|
358
|
+
return <Error message={state.error} />;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Interface vs Type
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Use interface for object shapes (extendable, better errors)
|
|
367
|
+
interface User {
|
|
368
|
+
id: number;
|
|
369
|
+
name: string;
|
|
370
|
+
email: string;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
interface AdminUser extends User {
|
|
374
|
+
permissions: string[];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Use type for unions, intersections, primitives
|
|
378
|
+
type Status = 'idle' | 'loading' | 'success' | 'error';
|
|
379
|
+
type ID = string | number;
|
|
380
|
+
type UserWithStatus = User & { status: Status };
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Import Organization
|
|
386
|
+
|
|
387
|
+
ESLint can auto-sort imports. Add this rule:
|
|
388
|
+
|
|
389
|
+
```javascript
|
|
390
|
+
// eslint.config.js
|
|
391
|
+
{
|
|
392
|
+
rules: {
|
|
393
|
+
'import/order': [
|
|
394
|
+
'error',
|
|
395
|
+
{
|
|
396
|
+
groups: [
|
|
397
|
+
'builtin',
|
|
398
|
+
'external',
|
|
399
|
+
'internal',
|
|
400
|
+
['parent', 'sibling'],
|
|
401
|
+
'index',
|
|
402
|
+
'type',
|
|
403
|
+
],
|
|
404
|
+
'newlines-between': 'always',
|
|
405
|
+
alphabetize: { order: 'asc', caseInsensitive: true },
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
},
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Result:
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
// Builtin
|
|
416
|
+
import { useState, useEffect } from 'react';
|
|
417
|
+
|
|
418
|
+
// External
|
|
419
|
+
import { useQuery } from '@tanstack/react-query';
|
|
420
|
+
import { z } from 'zod';
|
|
421
|
+
|
|
422
|
+
// Internal (absolute imports)
|
|
423
|
+
import { Button } from '@/components/ui';
|
|
424
|
+
import { useAuth } from '@/hooks/useAuth';
|
|
425
|
+
|
|
426
|
+
// Relative
|
|
427
|
+
import { UserCard } from './UserCard';
|
|
428
|
+
|
|
429
|
+
// Types
|
|
430
|
+
import type { User } from '@/types';
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Quality Commands
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
# Full quality check (CI pipeline)
|
|
439
|
+
pnpm typecheck # TypeScript
|
|
440
|
+
pnpm lint # ESLint
|
|
441
|
+
pnpm format:check # Prettier check (no write)
|
|
442
|
+
|
|
443
|
+
# Development workflow
|
|
444
|
+
pnpm lint:fix # Auto-fix ESLint issues
|
|
445
|
+
pnpm format # Format all files
|
|
446
|
+
|
|
447
|
+
# Single file check
|
|
448
|
+
pnpm exec eslint src/features/users/UserList.tsx
|
|
449
|
+
pnpm exec tsc --noEmit
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Package.json Scripts
|
|
453
|
+
|
|
454
|
+
```json
|
|
455
|
+
{
|
|
456
|
+
"scripts": {
|
|
457
|
+
"typecheck": "tsc --noEmit",
|
|
458
|
+
"lint": "eslint .",
|
|
459
|
+
"lint:fix": "eslint . --fix",
|
|
460
|
+
"format": "prettier --write .",
|
|
461
|
+
"format:check": "prettier --check .",
|
|
462
|
+
"quality": "pnpm typecheck && pnpm lint && pnpm format:check"
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## CI Pipeline
|
|
470
|
+
|
|
471
|
+
```yaml
|
|
472
|
+
# .github/workflows/ci.yml
|
|
473
|
+
name: CI
|
|
474
|
+
|
|
475
|
+
on: [push, pull_request]
|
|
476
|
+
|
|
477
|
+
jobs:
|
|
478
|
+
quality:
|
|
479
|
+
runs-on: ubuntu-latest
|
|
480
|
+
steps:
|
|
481
|
+
- uses: actions/checkout@v4
|
|
482
|
+
- uses: pnpm/action-setup@v2
|
|
483
|
+
- uses: actions/setup-node@v4
|
|
484
|
+
with:
|
|
485
|
+
node-version: 20
|
|
486
|
+
cache: 'pnpm'
|
|
487
|
+
|
|
488
|
+
- run: pnpm install --frozen-lockfile
|
|
489
|
+
|
|
490
|
+
- name: Type Check
|
|
491
|
+
run: pnpm typecheck
|
|
492
|
+
|
|
493
|
+
- name: Lint
|
|
494
|
+
run: pnpm lint
|
|
495
|
+
|
|
496
|
+
- name: Format Check
|
|
497
|
+
run: pnpm format:check
|
|
498
|
+
|
|
499
|
+
- name: Test
|
|
500
|
+
run: pnpm test:run
|
|
501
|
+
|
|
502
|
+
- name: Build
|
|
503
|
+
run: pnpm build
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## Anti-Patterns
|
|
509
|
+
|
|
510
|
+
| Anti-Pattern | Problem | Correct Approach |
|
|
511
|
+
|--------------|---------|------------------|
|
|
512
|
+
| Disabling strict mode | Hides bugs | Keep strict mode, fix types |
|
|
513
|
+
| `// @ts-ignore` everywhere | Ignores type errors | Fix the types or use `@ts-expect-error` with comment |
|
|
514
|
+
| `as` type assertions | Unsafe casting | Validate with Zod, use type guards |
|
|
515
|
+
| No pre-commit hooks | Bad code reaches repo | Always use Husky + lint-staged |
|
|
516
|
+
| ESLint and Prettier conflicts | Inconsistent formatting | Let Prettier handle formatting |
|
|
517
|
+
| Manual import sorting | Inconsistent imports | Use ESLint import/order |
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
_Quality is automated. If a human has to remember a rule, it will be forgotten._
|