@su-record/vibe 2.4.71 → 2.4.74
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/CLAUDE.md +216 -215
- package/README.md +4 -4
- package/agents/research/best-practices-agent.md +13 -13
- package/agents/research/codebase-patterns-agent.md +33 -33
- package/agents/research/framework-docs-agent.md +23 -23
- package/agents/research/security-advisory-agent.md +29 -29
- package/agents/review/architecture-reviewer.md +31 -31
- package/agents/review/complexity-reviewer.md +21 -21
- package/agents/review/data-integrity-reviewer.md +29 -29
- package/agents/review/git-history-reviewer.md +24 -24
- package/agents/review/performance-reviewer.md +29 -29
- package/agents/review/python-reviewer.md +53 -53
- package/agents/review/rails-reviewer.md +40 -40
- package/agents/review/react-reviewer.md +40 -40
- package/agents/review/security-reviewer.md +29 -29
- package/agents/review/simplicity-reviewer.md +24 -24
- package/agents/review/test-coverage-reviewer.md +31 -31
- package/agents/review/typescript-reviewer.md +41 -41
- package/commands/vibe.analyze.md +103 -7
- package/commands/vibe.reason.md +106 -0
- package/commands/vibe.review.md +123 -38
- package/commands/vibe.run.md +286 -221
- package/commands/vibe.spec.md +293 -173
- package/commands/vibe.utils.md +104 -3
- package/commands/vibe.verify.md +179 -86
- package/dist/cli/detect.js +40 -40
- package/dist/cli/detect.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/llm.js +5 -5
- package/dist/cli/llm.js.map +1 -1
- package/dist/cli/setup.js +3 -3
- package/dist/cli/setup.js.map +1 -1
- package/dist/lib/ContextCompressor.js +1 -1
- package/dist/lib/ContextCompressor.js.map +1 -1
- package/dist/lib/gemini-api.js +12 -12
- package/dist/lib/gemini-api.js.map +1 -1
- package/dist/lib/gemini-oauth.js +22 -22
- package/dist/lib/gemini-oauth.js.map +1 -1
- package/dist/lib/gemini-storage.js +3 -3
- package/dist/lib/gemini-storage.js.map +1 -1
- package/dist/lib/gpt-api.js +11 -11
- package/dist/lib/gpt-api.js.map +1 -1
- package/dist/lib/gpt-oauth.js +28 -28
- package/dist/lib/gpt-oauth.js.map +1 -1
- package/dist/lib/gpt-storage.js +3 -3
- package/dist/lib/gpt-storage.js.map +1 -1
- package/dist/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator.js +4 -6
- package/dist/orchestrator/orchestrator.js.map +1 -1
- package/dist/tools/convention/analyzeComplexity.js +3 -3
- package/dist/tools/convention/analyzeComplexity.js.map +1 -1
- package/dist/tools/convention/applyQualityRules.js +1 -1
- package/dist/tools/convention/applyQualityRules.js.map +1 -1
- package/dist/tools/convention/checkCouplingCohesion.js +2 -2
- package/dist/tools/convention/checkCouplingCohesion.js.map +1 -1
- package/dist/tools/convention/suggestImprovements.js +1 -1
- package/dist/tools/convention/suggestImprovements.js.map +1 -1
- package/dist/tools/convention/validateCodeQuality.js +1 -1
- package/dist/tools/convention/validateCodeQuality.js.map +1 -1
- package/dist/tools/memory/autoSaveContext.js +1 -1
- package/dist/tools/memory/autoSaveContext.js.map +1 -1
- package/dist/tools/memory/createMemoryTimeline.js +27 -27
- package/dist/tools/memory/createMemoryTimeline.js.map +1 -1
- package/dist/tools/memory/deleteMemory.js +1 -1
- package/dist/tools/memory/deleteMemory.js.map +1 -1
- package/dist/tools/memory/getMemoryGraph.js +24 -24
- package/dist/tools/memory/getMemoryGraph.js.map +1 -1
- package/dist/tools/memory/getSessionContext.js +36 -36
- package/dist/tools/memory/getSessionContext.js.map +1 -1
- package/dist/tools/memory/linkMemories.js +21 -21
- package/dist/tools/memory/linkMemories.js.map +1 -1
- package/dist/tools/memory/prioritizeMemory.js +1 -1
- package/dist/tools/memory/prioritizeMemory.js.map +1 -1
- package/dist/tools/memory/restoreSessionContext.js +1 -1
- package/dist/tools/memory/restoreSessionContext.js.map +1 -1
- package/dist/tools/memory/searchMemories.js +1 -1
- package/dist/tools/memory/searchMemories.js.map +1 -1
- package/dist/tools/memory/searchMemoriesAdvanced.js +42 -42
- package/dist/tools/memory/searchMemoriesAdvanced.js.map +1 -1
- package/dist/tools/memory/startSession.js +2 -2
- package/dist/tools/memory/startSession.js.map +1 -1
- package/dist/tools/memory/updateMemory.js +1 -1
- package/dist/tools/memory/updateMemory.js.map +1 -1
- package/dist/tools/semantic/analyzeDependencyGraph.js +38 -38
- package/dist/tools/semantic/analyzeDependencyGraph.js.map +1 -1
- package/dist/tools/semantic/findReferences.js +1 -1
- package/dist/tools/semantic/findReferences.js.map +1 -1
- package/dist/tools/semantic/findSymbol.js +1 -1
- package/dist/tools/semantic/findSymbol.js.map +1 -1
- package/dist/tools/time/getCurrentTime.js +1 -1
- package/dist/tools/time/getCurrentTime.js.map +1 -1
- package/dist/tools/ui/previewUiAscii.js +2 -2
- package/dist/tools/ui/previewUiAscii.js.map +1 -1
- package/hooks/hooks.json +11 -2
- package/hooks/scripts/llm-orchestrate.js +1 -1
- package/hooks/scripts/utils.js +31 -6
- package/languages/csharp-unity.md +82 -83
- package/languages/dart-flutter.md +89 -88
- package/languages/go.md +76 -75
- package/languages/java-spring.md +85 -84
- package/languages/kotlin-android.md +64 -63
- package/languages/python-django.md +83 -82
- package/languages/python-fastapi.md +82 -81
- package/languages/rust.md +75 -74
- package/languages/swift-ios.md +73 -72
- package/languages/typescript-electron.md +70 -71
- package/languages/typescript-nextjs.md +93 -92
- package/languages/typescript-node.md +64 -63
- package/languages/typescript-nuxt.md +113 -112
- package/languages/typescript-react-native.md +82 -81
- package/languages/typescript-react.md +76 -75
- package/languages/typescript-tauri.md +74 -75
- package/languages/typescript-vue.md +73 -72
- package/package.json +1 -1
- package/skills/git-worktree.md +25 -25
- package/skills/multi-llm-orchestration.md +4 -6
- package/skills/priority-todos.md +39 -39
- package/skills/vibe-capabilities.md +2 -2
- package/vibe/config.json +2 -2
|
@@ -1,42 +1,41 @@
|
|
|
1
|
-
#
|
|
1
|
+
# TypeScript + Tauri v2 Quality Rules
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Core Principles (inherited from core)
|
|
4
4
|
|
|
5
5
|
```markdown
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
# Core Principles (inherited from core)
|
|
7
|
+
Single Responsibility (SRP)
|
|
8
|
+
No Duplication (DRY)
|
|
9
|
+
Reusability
|
|
10
|
+
Low Complexity
|
|
11
|
+
Function <= 30 lines, JSX <= 50 lines
|
|
12
|
+
Nesting <= 3 levels
|
|
13
|
+
Cyclomatic complexity <= 10
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
## Tauri
|
|
16
|
+
## Tauri Architecture Understanding
|
|
16
17
|
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
│ - 보안 샌드박스 │
|
|
28
|
-
└─────────────────────────────────────────────┘
|
|
18
|
+
```text
|
|
19
|
+
Frontend (TypeScript/React/Vue/Svelte)
|
|
20
|
+
- UI rendering
|
|
21
|
+
- User interaction
|
|
22
|
+
- @tauri-apps/api calls
|
|
23
|
+
|
|
24
|
+
Tauri Core (Rust)
|
|
25
|
+
- System API access
|
|
26
|
+
- File system, network
|
|
27
|
+
- Security sandbox
|
|
29
28
|
```
|
|
30
29
|
|
|
31
|
-
## TypeScript/Tauri
|
|
30
|
+
## TypeScript/Tauri Specific Rules
|
|
32
31
|
|
|
33
|
-
### 1. Tauri Command
|
|
32
|
+
### 1. Tauri Command Type Safety
|
|
34
33
|
|
|
35
34
|
```typescript
|
|
36
|
-
//
|
|
35
|
+
// Bad: Using any
|
|
37
36
|
const result = await invoke('get_data');
|
|
38
37
|
|
|
39
|
-
//
|
|
38
|
+
// Good: Clear type definition
|
|
40
39
|
interface FileInfo {
|
|
41
40
|
path: string;
|
|
42
41
|
size: number;
|
|
@@ -45,7 +44,7 @@ interface FileInfo {
|
|
|
45
44
|
|
|
46
45
|
const fileInfo = await invoke<FileInfo>('get_file_info', { path: '/path/to/file' });
|
|
47
46
|
|
|
48
|
-
//
|
|
47
|
+
// Good: Command response type definition
|
|
49
48
|
interface CommandResponse<T> {
|
|
50
49
|
success: boolean;
|
|
51
50
|
data?: T;
|
|
@@ -61,14 +60,14 @@ async function invokeCommand<T>(cmd: string, args?: Record<string, unknown>): Pr
|
|
|
61
60
|
}
|
|
62
61
|
```
|
|
63
62
|
|
|
64
|
-
### 2. Tauri API
|
|
63
|
+
### 2. Tauri API Usage Patterns
|
|
65
64
|
|
|
66
65
|
```typescript
|
|
67
66
|
import { invoke } from '@tauri-apps/api/core';
|
|
68
67
|
import { open, save } from '@tauri-apps/plugin-dialog';
|
|
69
68
|
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
|
|
70
69
|
|
|
71
|
-
//
|
|
70
|
+
// Good: File dialog + read
|
|
72
71
|
async function openFile(): Promise<string | null> {
|
|
73
72
|
const selected = await open({
|
|
74
73
|
multiple: false,
|
|
@@ -80,7 +79,7 @@ async function openFile(): Promise<string | null> {
|
|
|
80
79
|
return await readTextFile(selected as string);
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
//
|
|
82
|
+
// Good: File save
|
|
84
83
|
async function saveFile(content: string): Promise<void> {
|
|
85
84
|
const path = await save({
|
|
86
85
|
filters: [{ name: 'Text', extensions: ['txt'] }]
|
|
@@ -92,12 +91,12 @@ async function saveFile(content: string): Promise<void> {
|
|
|
92
91
|
}
|
|
93
92
|
```
|
|
94
93
|
|
|
95
|
-
### 3. Event
|
|
94
|
+
### 3. Event System Usage
|
|
96
95
|
|
|
97
96
|
```typescript
|
|
98
97
|
import { listen, emit } from '@tauri-apps/api/event';
|
|
99
98
|
|
|
100
|
-
//
|
|
99
|
+
// Good: Event listener (cleanup required)
|
|
101
100
|
function useBackendEvent<T>(eventName: string, handler: (payload: T) => void) {
|
|
102
101
|
useEffect(() => {
|
|
103
102
|
const unlisten = listen<T>(eventName, (event) => {
|
|
@@ -110,41 +109,41 @@ function useBackendEvent<T>(eventName: string, handler: (payload: T) => void) {
|
|
|
110
109
|
}, [eventName, handler]);
|
|
111
110
|
}
|
|
112
111
|
|
|
113
|
-
//
|
|
112
|
+
// Good: Frontend -> Backend event
|
|
114
113
|
async function notifyBackend(action: string, data: unknown): Promise<void> {
|
|
115
114
|
await emit('frontend-action', { action, data });
|
|
116
115
|
}
|
|
117
116
|
```
|
|
118
117
|
|
|
119
|
-
### 4. Window
|
|
118
|
+
### 4. Window Management
|
|
120
119
|
|
|
121
120
|
```typescript
|
|
122
121
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
|
123
122
|
|
|
124
|
-
//
|
|
123
|
+
// Good: Window control
|
|
125
124
|
async function setupWindow(): Promise<void> {
|
|
126
125
|
const appWindow = getCurrentWindow();
|
|
127
126
|
|
|
128
|
-
//
|
|
127
|
+
// Set window size
|
|
129
128
|
await appWindow.setSize(new LogicalSize(800, 600));
|
|
130
129
|
|
|
131
|
-
//
|
|
130
|
+
// Center window
|
|
132
131
|
await appWindow.center();
|
|
133
132
|
|
|
134
|
-
//
|
|
133
|
+
// Set window title
|
|
135
134
|
await appWindow.setTitle('My Tauri App');
|
|
136
135
|
}
|
|
137
136
|
|
|
138
|
-
//
|
|
137
|
+
// Good: Window event listener
|
|
139
138
|
function useWindowEvents() {
|
|
140
139
|
useEffect(() => {
|
|
141
140
|
const appWindow = getCurrentWindow();
|
|
142
141
|
|
|
143
142
|
const unlistenClose = appWindow.onCloseRequested(async (event) => {
|
|
144
|
-
//
|
|
143
|
+
// Check for unsaved changes
|
|
145
144
|
if (hasUnsavedChanges) {
|
|
146
145
|
event.preventDefault();
|
|
147
|
-
//
|
|
146
|
+
// Show confirmation dialog
|
|
148
147
|
}
|
|
149
148
|
});
|
|
150
149
|
|
|
@@ -155,18 +154,18 @@ function useWindowEvents() {
|
|
|
155
154
|
}
|
|
156
155
|
```
|
|
157
156
|
|
|
158
|
-
### 5. Rust Command
|
|
157
|
+
### 5. Rust Command Definition (Backend)
|
|
159
158
|
|
|
160
159
|
```rust
|
|
161
|
-
// src-tauri/src/main.rs
|
|
160
|
+
// src-tauri/src/main.rs or lib.rs
|
|
162
161
|
|
|
163
|
-
//
|
|
162
|
+
// Good: Command definition
|
|
164
163
|
#[tauri::command]
|
|
165
164
|
fn greet(name: &str) -> String {
|
|
166
165
|
format!("Hello, {}!", name)
|
|
167
166
|
}
|
|
168
167
|
|
|
169
|
-
//
|
|
168
|
+
// Good: Async Command
|
|
170
169
|
#[tauri::command]
|
|
171
170
|
async fn read_file(path: String) -> Result<String, String> {
|
|
172
171
|
tokio::fs::read_to_string(&path)
|
|
@@ -174,13 +173,13 @@ async fn read_file(path: String) -> Result<String, String> {
|
|
|
174
173
|
.map_err(|e| e.to_string())
|
|
175
174
|
}
|
|
176
175
|
|
|
177
|
-
//
|
|
176
|
+
// Good: Using State
|
|
178
177
|
#[tauri::command]
|
|
179
178
|
fn get_count(state: tauri::State<'_, AppState>) -> u32 {
|
|
180
179
|
*state.count.lock().unwrap()
|
|
181
180
|
}
|
|
182
181
|
|
|
183
|
-
// main.rs
|
|
182
|
+
// Register in main.rs
|
|
184
183
|
fn main() {
|
|
185
184
|
tauri::Builder::default()
|
|
186
185
|
.invoke_handler(tauri::generate_handler![greet, read_file, get_count])
|
|
@@ -189,7 +188,7 @@ fn main() {
|
|
|
189
188
|
}
|
|
190
189
|
```
|
|
191
190
|
|
|
192
|
-
### 6.
|
|
191
|
+
### 6. Security Configuration (tauri.conf.json)
|
|
193
192
|
|
|
194
193
|
```json
|
|
195
194
|
{
|
|
@@ -210,10 +209,10 @@ fn main() {
|
|
|
210
209
|
}
|
|
211
210
|
```
|
|
212
211
|
|
|
213
|
-
### 7. Custom Hook
|
|
212
|
+
### 7. Custom Hook Pattern
|
|
214
213
|
|
|
215
214
|
```typescript
|
|
216
|
-
//
|
|
215
|
+
// Good: Tauri Command Hook
|
|
217
216
|
function useTauriCommand<T, A extends Record<string, unknown>>(
|
|
218
217
|
command: string
|
|
219
218
|
) {
|
|
@@ -240,7 +239,7 @@ function useTauriCommand<T, A extends Record<string, unknown>>(
|
|
|
240
239
|
return { data, loading, error, execute };
|
|
241
240
|
}
|
|
242
241
|
|
|
243
|
-
//
|
|
242
|
+
// Usage example
|
|
244
243
|
function FileViewer() {
|
|
245
244
|
const { data: content, loading, error, execute } = useTauriCommand<string>('read_file');
|
|
246
245
|
|
|
@@ -260,24 +259,24 @@ function FileViewer() {
|
|
|
260
259
|
}
|
|
261
260
|
```
|
|
262
261
|
|
|
263
|
-
### 8.
|
|
262
|
+
### 8. Build and Deploy
|
|
264
263
|
|
|
265
264
|
```bash
|
|
266
|
-
#
|
|
265
|
+
# Development mode
|
|
267
266
|
npm run tauri dev
|
|
268
267
|
|
|
269
|
-
#
|
|
268
|
+
# Production build
|
|
270
269
|
npm run tauri build
|
|
271
270
|
|
|
272
|
-
#
|
|
271
|
+
# Specific target
|
|
273
272
|
npm run tauri build -- --target x86_64-pc-windows-msvc
|
|
274
273
|
npm run tauri build -- --target aarch64-apple-darwin
|
|
275
274
|
npm run tauri build -- --target x86_64-unknown-linux-gnu
|
|
276
275
|
```
|
|
277
276
|
|
|
278
|
-
##
|
|
277
|
+
## Recommended Folder Structure
|
|
279
278
|
|
|
280
|
-
```
|
|
279
|
+
```text
|
|
281
280
|
my-tauri-app/
|
|
282
281
|
├── src/ # Frontend
|
|
283
282
|
│ ├── components/
|
|
@@ -295,34 +294,34 @@ my-tauri-app/
|
|
|
295
294
|
└── package.json
|
|
296
295
|
```
|
|
297
296
|
|
|
298
|
-
##
|
|
297
|
+
## Performance Optimization
|
|
299
298
|
|
|
300
299
|
```typescript
|
|
301
|
-
//
|
|
300
|
+
// Good: Large data streaming
|
|
302
301
|
import { Channel } from '@tauri-apps/api/core';
|
|
303
302
|
|
|
304
303
|
async function streamLargeFile(path: string): Promise<void> {
|
|
305
304
|
const channel = new Channel<string>();
|
|
306
305
|
|
|
307
306
|
channel.onmessage = (chunk) => {
|
|
308
|
-
//
|
|
307
|
+
// Process chunk by chunk
|
|
309
308
|
appendToDisplay(chunk);
|
|
310
309
|
};
|
|
311
310
|
|
|
312
311
|
await invoke('stream_file', { path, channel });
|
|
313
312
|
}
|
|
314
313
|
|
|
315
|
-
//
|
|
314
|
+
// Good: Background task
|
|
316
315
|
async function runHeavyTask(): Promise<void> {
|
|
317
|
-
//
|
|
316
|
+
// Process in separate thread in Rust
|
|
318
317
|
await invoke('heavy_computation', { data: largeData });
|
|
319
318
|
}
|
|
320
319
|
```
|
|
321
320
|
|
|
322
|
-
##
|
|
321
|
+
## Debugging
|
|
323
322
|
|
|
324
323
|
```typescript
|
|
325
|
-
//
|
|
324
|
+
// Good: Logging only in development mode
|
|
326
325
|
const isDev = import.meta.env.DEV;
|
|
327
326
|
|
|
328
327
|
function debugLog(message: string, data?: unknown): void {
|
|
@@ -331,14 +330,14 @@ function debugLog(message: string, data?: unknown): void {
|
|
|
331
330
|
}
|
|
332
331
|
}
|
|
333
332
|
|
|
334
|
-
//
|
|
333
|
+
// Good: Check Rust logs (in terminal)
|
|
335
334
|
// RUST_LOG=debug npm run tauri dev
|
|
336
335
|
```
|
|
337
336
|
|
|
338
|
-
##
|
|
337
|
+
## Testing
|
|
339
338
|
|
|
340
339
|
```typescript
|
|
341
|
-
//
|
|
340
|
+
// Good: Command Mock
|
|
342
341
|
import { mockIPC } from '@tauri-apps/api/mocks';
|
|
343
342
|
|
|
344
343
|
beforeAll(() => {
|
|
@@ -355,12 +354,12 @@ test('greet command', async () => {
|
|
|
355
354
|
});
|
|
356
355
|
```
|
|
357
356
|
|
|
358
|
-
##
|
|
357
|
+
## Checklist
|
|
359
358
|
|
|
360
|
-
- [ ]
|
|
361
|
-
- [ ]
|
|
362
|
-
- [ ]
|
|
363
|
-
- [ ] CSP
|
|
364
|
-
- [ ]
|
|
365
|
-
- [ ]
|
|
366
|
-
- [ ]
|
|
359
|
+
- [ ] Define types for all Commands
|
|
360
|
+
- [ ] Handle event listener cleanup
|
|
361
|
+
- [ ] Minimize file access scope (tauri.conf.json)
|
|
362
|
+
- [ ] Verify CSP configuration
|
|
363
|
+
- [ ] Error handling (Rust -> Frontend)
|
|
364
|
+
- [ ] Handle large data streaming
|
|
365
|
+
- [ ] Separate development/production environments
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
#
|
|
1
|
+
# TypeScript + Vue/Nuxt Quality Rules
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Core Principles (inherited from core)
|
|
4
4
|
|
|
5
5
|
```markdown
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
# Core Principles (inherited from core)
|
|
7
|
+
Single Responsibility (SRP)
|
|
8
|
+
No Duplication (DRY)
|
|
9
|
+
Reusability
|
|
10
|
+
Low Complexity
|
|
11
|
+
Function <= 30 lines, Template <= 100 lines
|
|
12
|
+
Nesting <= 3 levels
|
|
13
|
+
Cyclomatic complexity <= 10
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
## Vue 3 + TypeScript
|
|
16
|
+
## Vue 3 + TypeScript Specific Rules
|
|
16
17
|
|
|
17
|
-
### 1. Composition API
|
|
18
|
+
### 1. Use Composition API (Avoid Options API)
|
|
18
19
|
|
|
19
20
|
```typescript
|
|
20
|
-
//
|
|
21
|
+
// Bad: Options API (legacy)
|
|
21
22
|
export default {
|
|
22
23
|
data() {
|
|
23
24
|
return { count: 0 };
|
|
@@ -29,7 +30,7 @@ export default {
|
|
|
29
30
|
}
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
//
|
|
33
|
+
// Good: Composition API + script setup
|
|
33
34
|
<script setup lang="ts">
|
|
34
35
|
import { ref, computed, onMounted } from 'vue';
|
|
35
36
|
|
|
@@ -41,15 +42,15 @@ function increment() {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
onMounted(() => {
|
|
44
|
-
console.log('
|
|
45
|
+
console.log('Component mounted');
|
|
45
46
|
});
|
|
46
47
|
</script>
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
### 2.
|
|
50
|
+
### 2. Type-safe Props/Emits
|
|
50
51
|
|
|
51
52
|
```typescript
|
|
52
|
-
//
|
|
53
|
+
// Good: Props type definition
|
|
53
54
|
interface Props {
|
|
54
55
|
userId: string;
|
|
55
56
|
title?: string;
|
|
@@ -57,10 +58,10 @@ interface Props {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
const props = withDefaults(defineProps<Props>(), {
|
|
60
|
-
title: '
|
|
61
|
+
title: 'Default Title',
|
|
61
62
|
});
|
|
62
63
|
|
|
63
|
-
//
|
|
64
|
+
// Good: Emits type definition
|
|
64
65
|
interface Emits {
|
|
65
66
|
(e: 'update', value: string): void;
|
|
66
67
|
(e: 'delete', id: number): void;
|
|
@@ -69,15 +70,15 @@ interface Emits {
|
|
|
69
70
|
|
|
70
71
|
const emit = defineEmits<Emits>();
|
|
71
72
|
|
|
72
|
-
//
|
|
73
|
-
emit('update', '
|
|
73
|
+
// Usage
|
|
74
|
+
emit('update', 'new value');
|
|
74
75
|
emit('delete', 123);
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
### 3.
|
|
78
|
+
### 3. Separate Logic with Composables
|
|
78
79
|
|
|
79
80
|
```typescript
|
|
80
|
-
//
|
|
81
|
+
// Good: composables/useUser.ts
|
|
81
82
|
import { ref, computed } from 'vue';
|
|
82
83
|
import type { User } from '@/types';
|
|
83
84
|
|
|
@@ -97,7 +98,7 @@ export function useUser(userId: string) {
|
|
|
97
98
|
const response = await api.getUser(userId);
|
|
98
99
|
user.value = response.data;
|
|
99
100
|
} catch (e) {
|
|
100
|
-
error.value = '
|
|
101
|
+
error.value = 'Failed to load user';
|
|
101
102
|
} finally {
|
|
102
103
|
isLoading.value = false;
|
|
103
104
|
}
|
|
@@ -112,7 +113,7 @@ export function useUser(userId: string) {
|
|
|
112
113
|
};
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
//
|
|
116
|
+
// Usage in component
|
|
116
117
|
<script setup lang="ts">
|
|
117
118
|
const { user, isLoading, fetchUser } = useUser(props.userId);
|
|
118
119
|
|
|
@@ -120,10 +121,10 @@ onMounted(fetchUser);
|
|
|
120
121
|
</script>
|
|
121
122
|
```
|
|
122
123
|
|
|
123
|
-
### 4. Pinia
|
|
124
|
+
### 4. Pinia State Management
|
|
124
125
|
|
|
125
126
|
```typescript
|
|
126
|
-
//
|
|
127
|
+
// Good: stores/user.ts
|
|
127
128
|
import { defineStore } from 'pinia';
|
|
128
129
|
import type { User } from '@/types';
|
|
129
130
|
|
|
@@ -162,7 +163,7 @@ export const useUserStore = defineStore('user', {
|
|
|
162
163
|
},
|
|
163
164
|
});
|
|
164
165
|
|
|
165
|
-
// Setup Store
|
|
166
|
+
// Setup Store style (recommended)
|
|
166
167
|
export const useUserStore = defineStore('user', () => {
|
|
167
168
|
const currentUser = ref<User | null>(null);
|
|
168
169
|
const isLoggedIn = computed(() => !!currentUser.value);
|
|
@@ -175,10 +176,10 @@ export const useUserStore = defineStore('user', () => {
|
|
|
175
176
|
});
|
|
176
177
|
```
|
|
177
178
|
|
|
178
|
-
### 5. Nuxt 3
|
|
179
|
+
### 5. Nuxt 3 Specific Rules
|
|
179
180
|
|
|
180
181
|
```typescript
|
|
181
|
-
//
|
|
182
|
+
// Good: Server API Routes (server/api/)
|
|
182
183
|
// server/api/users/[id].get.ts
|
|
183
184
|
export default defineEventHandler(async (event) => {
|
|
184
185
|
const id = getRouterParam(event, 'id');
|
|
@@ -186,7 +187,7 @@ export default defineEventHandler(async (event) => {
|
|
|
186
187
|
if (!id) {
|
|
187
188
|
throw createError({
|
|
188
189
|
statusCode: 400,
|
|
189
|
-
message: 'ID
|
|
190
|
+
message: 'ID is required',
|
|
190
191
|
});
|
|
191
192
|
}
|
|
192
193
|
|
|
@@ -195,28 +196,28 @@ export default defineEventHandler(async (event) => {
|
|
|
195
196
|
if (!user) {
|
|
196
197
|
throw createError({
|
|
197
198
|
statusCode: 404,
|
|
198
|
-
message: '
|
|
199
|
+
message: 'User not found',
|
|
199
200
|
});
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
return user;
|
|
203
204
|
});
|
|
204
205
|
|
|
205
|
-
//
|
|
206
|
+
// Good: useFetch / useAsyncData
|
|
206
207
|
<script setup lang="ts">
|
|
207
|
-
// SSR
|
|
208
|
+
// SSR supported data fetching
|
|
208
209
|
const { data: user, pending, error } = await useFetch<User>(
|
|
209
210
|
`/api/users/${props.userId}`
|
|
210
211
|
);
|
|
211
212
|
|
|
212
|
-
//
|
|
213
|
+
// With caching key
|
|
213
214
|
const { data: posts } = await useAsyncData(
|
|
214
215
|
`user-${props.userId}-posts`,
|
|
215
216
|
() => $fetch(`/api/users/${props.userId}/posts`)
|
|
216
217
|
);
|
|
217
218
|
</script>
|
|
218
219
|
|
|
219
|
-
//
|
|
220
|
+
// Good: Middleware
|
|
220
221
|
// middleware/auth.ts
|
|
221
222
|
export default defineNuxtRouteMiddleware((to, from) => {
|
|
222
223
|
const { isLoggedIn } = useUserStore();
|
|
@@ -227,15 +228,15 @@ export default defineNuxtRouteMiddleware((to, from) => {
|
|
|
227
228
|
});
|
|
228
229
|
```
|
|
229
230
|
|
|
230
|
-
### 6.
|
|
231
|
+
### 6. Component Structure
|
|
231
232
|
|
|
232
233
|
```vue
|
|
233
|
-
<!--
|
|
234
|
+
<!-- Good: Recommended component structure -->
|
|
234
235
|
<script setup lang="ts">
|
|
235
|
-
// 1.
|
|
236
|
+
// 1. Type imports
|
|
236
237
|
import type { User, Item } from '@/types';
|
|
237
238
|
|
|
238
|
-
// 2.
|
|
239
|
+
// 2. Component imports
|
|
239
240
|
import UserAvatar from '@/components/UserAvatar.vue';
|
|
240
241
|
|
|
241
242
|
// 3. Props/Emits
|
|
@@ -272,7 +273,7 @@ async function handleSave() {
|
|
|
272
273
|
|
|
273
274
|
// 8. Lifecycle
|
|
274
275
|
onMounted(() => {
|
|
275
|
-
console.log('
|
|
276
|
+
console.log('Component ready');
|
|
276
277
|
});
|
|
277
278
|
</script>
|
|
278
279
|
|
|
@@ -285,7 +286,7 @@ onMounted(() => {
|
|
|
285
286
|
:disabled="!canSave"
|
|
286
287
|
@click="handleSave"
|
|
287
288
|
>
|
|
288
|
-
|
|
289
|
+
Save
|
|
289
290
|
</button>
|
|
290
291
|
</div>
|
|
291
292
|
</template>
|
|
@@ -298,56 +299,56 @@ onMounted(() => {
|
|
|
298
299
|
</style>
|
|
299
300
|
```
|
|
300
301
|
|
|
301
|
-
##
|
|
302
|
+
## Anti-patterns
|
|
302
303
|
|
|
303
304
|
```typescript
|
|
304
|
-
//
|
|
305
|
+
// Bad: Using v-if and v-for together
|
|
305
306
|
<li v-for="user in users" v-if="user.isActive">
|
|
306
307
|
|
|
307
|
-
//
|
|
308
|
+
// Good: Filter with computed
|
|
308
309
|
const activeUsers = computed(() => users.value.filter(u => u.isActive));
|
|
309
310
|
<li v-for="user in activeUsers">
|
|
310
311
|
|
|
311
|
-
//
|
|
312
|
-
props.user.name = '
|
|
312
|
+
// Bad: Mutating props directly
|
|
313
|
+
props.user.name = 'New Name';
|
|
313
314
|
|
|
314
|
-
//
|
|
315
|
-
emit('update', { ...props.user, name: '
|
|
315
|
+
// Good: Emit to parent
|
|
316
|
+
emit('update', { ...props.user, name: 'New Name' });
|
|
316
317
|
|
|
317
|
-
//
|
|
318
|
+
// Bad: Overusing $refs
|
|
318
319
|
this.$refs.input.focus();
|
|
319
320
|
|
|
320
|
-
//
|
|
321
|
+
// Good: template ref + expose
|
|
321
322
|
const inputRef = ref<HTMLInputElement>();
|
|
322
323
|
defineExpose({ focus: () => inputRef.value?.focus() });
|
|
323
324
|
```
|
|
324
325
|
|
|
325
|
-
##
|
|
326
|
+
## File Structure (Nuxt 3)
|
|
326
327
|
|
|
327
|
-
```
|
|
328
|
+
```text
|
|
328
329
|
project/
|
|
329
330
|
├── components/
|
|
330
|
-
│ ├── ui/ #
|
|
331
|
-
│ ├── features/ #
|
|
332
|
-
│ └── layouts/ #
|
|
333
|
-
├── composables/ # Composition
|
|
334
|
-
├── stores/ # Pinia
|
|
331
|
+
│ ├── ui/ # Base UI components
|
|
332
|
+
│ ├── features/ # Feature-specific components
|
|
333
|
+
│ └── layouts/ # Layout components
|
|
334
|
+
├── composables/ # Composition functions
|
|
335
|
+
├── stores/ # Pinia stores
|
|
335
336
|
├── server/
|
|
336
|
-
│ ├── api/ # API
|
|
337
|
-
│ ├── middleware/ #
|
|
338
|
-
│ └── utils/ #
|
|
339
|
-
├── pages/ #
|
|
340
|
-
├── middleware/ #
|
|
341
|
-
├── types/ # TypeScript
|
|
342
|
-
└── utils/ #
|
|
337
|
+
│ ├── api/ # API routes
|
|
338
|
+
│ ├── middleware/ # Server middleware
|
|
339
|
+
│ └── utils/ # Server utilities
|
|
340
|
+
├── pages/ # File-based routing
|
|
341
|
+
├── middleware/ # Client middleware
|
|
342
|
+
├── types/ # TypeScript types
|
|
343
|
+
└── utils/ # Utility functions
|
|
343
344
|
```
|
|
344
345
|
|
|
345
|
-
##
|
|
346
|
+
## Checklist
|
|
346
347
|
|
|
347
|
-
- [ ] Composition API + `<script setup>`
|
|
348
|
-
- [ ] Props/Emits
|
|
349
|
-
- [ ]
|
|
350
|
-
- [ ] Pinia Setup Store
|
|
351
|
-
- [ ] `any`
|
|
352
|
-
- [ ] v-if/v-for
|
|
353
|
-
- [ ] scoped
|
|
348
|
+
- [ ] Use Composition API + `<script setup>`
|
|
349
|
+
- [ ] Define Props/Emits types
|
|
350
|
+
- [ ] Separate logic with Composables
|
|
351
|
+
- [ ] Use Pinia Setup Store style
|
|
352
|
+
- [ ] No `any` type usage
|
|
353
|
+
- [ ] Separate v-if/v-for
|
|
354
|
+
- [ ] Use scoped styles
|