@el-j/magic-helix-plugins 4.0.0-beta.2 → 4.0.0-beta.4

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.
Files changed (120) hide show
  1. package/dist/architecture/codeowners.md +123 -0
  2. package/dist/architecture/monorepo.md +146 -0
  3. package/dist/architecture/nx.md +122 -0
  4. package/dist/architecture/turborepo.md +114 -0
  5. package/dist/ci/github-actions.md +268 -0
  6. package/dist/ci/gitlab-ci.md +330 -0
  7. package/dist/containers/docker-multistage.md +120 -0
  8. package/dist/containers/kubernetes-deploy.md +210 -0
  9. package/dist/cpp/index.cjs +79 -0
  10. package/dist/cpp/index.mjs +209 -0
  11. package/dist/csharp/index.cjs +2 -2
  12. package/dist/csharp/{index.js → index.mjs} +17 -11
  13. package/dist/csharp/templates/framework-aspnetcore.md +205 -0
  14. package/dist/csharp/templates/framework-blazor.md +271 -0
  15. package/dist/csharp/templates/lang-csharp.md +162 -0
  16. package/dist/devops/docker-compose.md +111 -0
  17. package/dist/devops/docker-dockerfile.md +94 -0
  18. package/dist/devops/github-actions.md +160 -0
  19. package/dist/devops/gitlab-ci.md +210 -0
  20. package/dist/generic/lang-typescript.md +57 -0
  21. package/dist/generic/state-redux.md +21 -0
  22. package/dist/generic/state-rxjs.md +6 -0
  23. package/dist/generic/style-mui.md +23 -0
  24. package/dist/generic/style-tailwind.md +76 -0
  25. package/dist/generic/test-cypress.md +21 -0
  26. package/dist/generic/test-jest.md +20 -0
  27. package/dist/generic/test-playwright.md +21 -0
  28. package/dist/generic/test-vitest.md +131 -0
  29. package/dist/go/index.cjs +3 -3
  30. package/dist/go/{index.js → index.mjs} +18 -15
  31. package/dist/go/templates/lang-go.md +571 -0
  32. package/dist/index.cjs +1 -1
  33. package/dist/index.mjs +24 -0
  34. package/dist/java/index.cjs +2 -2
  35. package/dist/java/{index.js → index.mjs} +25 -19
  36. package/dist/java/templates/build-gradle.md +102 -0
  37. package/dist/java/templates/build-maven.md +86 -0
  38. package/dist/java/templates/framework-spring-boot.md +179 -0
  39. package/dist/java/templates/lang-java.md +78 -0
  40. package/dist/java/templates/lang-kotlin.md +88 -0
  41. package/dist/meta/magic-helix-meta.md +213 -0
  42. package/dist/meta/meta-debug.md +459 -0
  43. package/dist/meta/meta-implement.md +450 -0
  44. package/dist/meta/meta-roadmap.md +265 -0
  45. package/dist/nodejs/templates/angular-core.md +19 -0
  46. package/dist/nodejs/templates/lang-typescript.md +57 -0
  47. package/dist/nodejs/templates/nestjs-core.md +7 -0
  48. package/dist/nodejs/templates/react-core.md +677 -0
  49. package/dist/nodejs/templates/react-zustand.md +7 -0
  50. package/dist/nodejs/templates/state-redux.md +21 -0
  51. package/dist/nodejs/templates/state-rxjs.md +6 -0
  52. package/dist/nodejs/templates/style-primevue.md +6 -0
  53. package/dist/nodejs/templates/style-quasar.md +22 -0
  54. package/dist/nodejs/templates/style-tailwind.md +76 -0
  55. package/dist/nodejs/templates/test-cypress.md +21 -0
  56. package/dist/nodejs/templates/test-jest.md +20 -0
  57. package/dist/nodejs/templates/test-playwright.md +21 -0
  58. package/dist/nodejs/templates/test-vitest.md +131 -0
  59. package/dist/nodejs/templates/vue-core.md +108 -0
  60. package/dist/nodejs/templates/vue-pinia.md +5 -0
  61. package/dist/patterns/architecture/clean-architecture.md +469 -0
  62. package/dist/patterns/architecture/dependency-injection.md +517 -0
  63. package/dist/patterns/architecture/domain-driven-design.md +621 -0
  64. package/dist/patterns/architecture/layered-architecture.md +382 -0
  65. package/dist/patterns/architecture/repository-pattern.md +408 -0
  66. package/dist/patterns/domain-expertise/nextjs-rules.md +115 -0
  67. package/dist/patterns/domain-expertise/react-patterns.md +181 -0
  68. package/dist/patterns/domain-expertise/server-components.md +212 -0
  69. package/dist/patterns/domain-expertise/shadcn-ui.md +52 -0
  70. package/dist/patterns/domain-expertise/tailwind-patterns.md +52 -0
  71. package/dist/patterns/environment/container-awareness.md +17 -0
  72. package/dist/patterns/environment/ide-features.md +17 -0
  73. package/dist/patterns/environment/os-commands.md +17 -0
  74. package/dist/patterns/organization/heading-hierarchy.md +103 -0
  75. package/dist/patterns/organization/sequential-workflows.md +102 -0
  76. package/dist/patterns/organization/xml-rule-groups.md +64 -0
  77. package/dist/patterns/reasoning/agent-loop.md +151 -0
  78. package/dist/patterns/reasoning/confirmation-gates.md +141 -0
  79. package/dist/patterns/reasoning/dependency-analysis.md +132 -0
  80. package/dist/patterns/reasoning/one-tool-per-iteration.md +152 -0
  81. package/dist/patterns/reasoning/preview-before-action.md +194 -0
  82. package/dist/patterns/reasoning/reflection-checkpoints.md +166 -0
  83. package/dist/patterns/reasoning/result-verification.md +157 -0
  84. package/dist/patterns/reasoning/subtask-breakdown.md +131 -0
  85. package/dist/patterns/reasoning/thinking-tags.md +100 -0
  86. package/dist/patterns/role-definition/capability-declarations.md +72 -0
  87. package/dist/patterns/role-definition/expert-identity.md +45 -0
  88. package/dist/patterns/role-definition/scope-boundaries.md +61 -0
  89. package/dist/patterns/safety/code-safety-rules.md +17 -0
  90. package/dist/patterns/safety/credential-handling.md +17 -0
  91. package/dist/patterns/safety/destructive-warnings.md +17 -0
  92. package/dist/patterns/safety/refusal-messages.md +17 -0
  93. package/dist/patterns/tone/adaptive-tone.md +17 -0
  94. package/dist/patterns/tone/concise-communication.md +17 -0
  95. package/dist/patterns/tone/forbidden-phrases.md +17 -0
  96. package/dist/patterns/tool-guidelines/function-schemas.md +143 -0
  97. package/dist/patterns/tool-guidelines/parameter-examples.md +137 -0
  98. package/dist/patterns/tool-guidelines/usage-policies.md +105 -0
  99. package/dist/php/index.cjs +2 -2
  100. package/dist/php/{index.js → index.mjs} +12 -6
  101. package/dist/php/templates/framework-laravel.md +112 -0
  102. package/dist/php/templates/lang-php.md +94 -0
  103. package/dist/python/index.cjs +4 -4
  104. package/dist/python/{index.js → index.mjs} +10 -7
  105. package/dist/python/templates/lang-python.md +508 -0
  106. package/dist/ruby/index.cjs +2 -2
  107. package/dist/ruby/{index.js → index.mjs} +16 -10
  108. package/dist/ruby/templates/framework-rails.md +309 -0
  109. package/dist/ruby/templates/framework-sinatra.md +227 -0
  110. package/dist/ruby/templates/lang-ruby.md +216 -0
  111. package/dist/rust/index.cjs +3 -3
  112. package/dist/rust/{index.js → index.mjs} +24 -18
  113. package/dist/rust/templates/lang-rust.md +89 -0
  114. package/dist/swift/index.cjs +32 -0
  115. package/dist/swift/index.mjs +112 -0
  116. package/dist/swift/templates/framework-vapor.md +352 -0
  117. package/dist/swift/templates/lang-swift.md +291 -0
  118. package/package.json +31 -21
  119. package/dist/index.js +0 -20
  120. /package/dist/nodejs/{index.js → index.mjs} +0 -0
@@ -0,0 +1,408 @@
1
+ # Repository Pattern
2
+
3
+ ## Purpose
4
+ Abstract data access logic, providing a collection-like interface for domain objects. Decouples business logic from data persistence.
5
+
6
+ ## Core Concept
7
+
8
+ **Repository = Collection of Domain Objects**
9
+
10
+ Think of a repository as an in-memory collection (like a List or Set) that persists to a database.
11
+
12
+ ```typescript
13
+ // ✅ Repository interface acts like a collection
14
+ interface IProductRepository {
15
+ // Collection-like operations
16
+ getById(id: string): Promise<Product | null>;
17
+ getAll(): Promise<Product[]>;
18
+ add(product: Product): Promise<void>;
19
+ update(product: Product): Promise<void>;
20
+ remove(id: string): Promise<void>;
21
+
22
+ // Domain-specific queries
23
+ findByCategory(category: string): Promise<Product[]>;
24
+ findInPriceRange(min: number, max: number): Promise<Product[]>;
25
+ }
26
+ ```
27
+
28
+ ## Implementation
29
+
30
+ ### 1. Define Repository Interface (in domain/use case layer)
31
+
32
+ ```typescript
33
+ // ✅ Interface lives in domain layer (no implementation details)
34
+ export interface IUserRepository {
35
+ findById(id: string): Promise<User | null>;
36
+ findByEmail(email: string): Promise<User | null>;
37
+ findAll(filters?: UserFilters): Promise<User[]>;
38
+ save(user: User): Promise<void>;
39
+ delete(id: string): Promise<void>;
40
+
41
+ // Domain-specific queries
42
+ findActiveUsers(): Promise<User[]>;
43
+ findUsersByRole(role: string): Promise<User[]>;
44
+ }
45
+
46
+ export interface UserFilters {
47
+ role?: string;
48
+ isActive?: boolean;
49
+ registeredAfter?: Date;
50
+ }
51
+ ```
52
+
53
+ ### 2. Implement Repository (in infrastructure layer)
54
+
55
+ ```typescript
56
+ // ✅ Implementation with actual database
57
+ export class PostgresUserRepository implements IUserRepository {
58
+ constructor(private db: Database) {}
59
+
60
+ async findById(id: string): Promise<User | null> {
61
+ const row = await this.db.queryOne(
62
+ 'SELECT * FROM users WHERE id = $1',
63
+ [id]
64
+ );
65
+
66
+ return row ? this.toDomain(row) : null;
67
+ }
68
+
69
+ async findByEmail(email: string): Promise<User | null> {
70
+ const row = await this.db.queryOne(
71
+ 'SELECT * FROM users WHERE email = $1',
72
+ [email]
73
+ );
74
+
75
+ return row ? this.toDomain(row) : null;
76
+ }
77
+
78
+ async save(user: User): Promise<void> {
79
+ // Handle both insert and update (upsert)
80
+ await this.db.query(
81
+ `INSERT INTO users (id, email, password, role, created_at)
82
+ VALUES ($1, $2, $3, $4, $5)
83
+ ON CONFLICT (id) DO UPDATE SET
84
+ email = $2,
85
+ password = $3,
86
+ role = $4`,
87
+ [user.id, user.email, user.password, user.role, user.createdAt]
88
+ );
89
+ }
90
+
91
+ async delete(id: string): Promise<void> {
92
+ await this.db.query('DELETE FROM users WHERE id = $1', [id]);
93
+ }
94
+
95
+ async findActiveUsers(): Promise<User[]> {
96
+ const rows = await this.db.query(
97
+ 'SELECT * FROM users WHERE is_active = true ORDER BY created_at DESC'
98
+ );
99
+
100
+ return rows.map(row => this.toDomain(row));
101
+ }
102
+
103
+ async findUsersByRole(role: string): Promise<User[]> {
104
+ const rows = await this.db.query(
105
+ 'SELECT * FROM users WHERE role = $1',
106
+ [role]
107
+ );
108
+
109
+ return rows.map(row => this.toDomain(row));
110
+ }
111
+
112
+ // Map database row to domain object
113
+ private toDomain(row: any): User {
114
+ return new User(
115
+ row.id,
116
+ row.email,
117
+ row.password,
118
+ row.role,
119
+ row.created_at,
120
+ row.is_active
121
+ );
122
+ }
123
+
124
+ // Map domain object to database row
125
+ private toRow(user: User): any {
126
+ return {
127
+ id: user.id,
128
+ email: user.email,
129
+ password: user.password,
130
+ role: user.role,
131
+ created_at: user.createdAt,
132
+ is_active: user.isActive,
133
+ };
134
+ }
135
+ }
136
+ ```
137
+
138
+ ### 3. Use Repository in Service/Use Case
139
+
140
+ ```typescript
141
+ // ✅ Service depends on interface, not implementation
142
+ export class UserService {
143
+ constructor(private userRepository: IUserRepository) {}
144
+
145
+ async registerUser(email: string, password: string): Promise<User> {
146
+ // Check if user exists
147
+ const existing = await this.userRepository.findByEmail(email);
148
+ if (existing) {
149
+ throw new Error('User already exists');
150
+ }
151
+
152
+ // Create domain object
153
+ const user = User.create(email, password);
154
+
155
+ // Persist
156
+ await this.userRepository.save(user);
157
+
158
+ return user;
159
+ }
160
+
161
+ async getActiveAdmins(): Promise<User[]> {
162
+ const admins = await this.userRepository.findUsersByRole('admin');
163
+ return admins.filter(user => user.isActive);
164
+ }
165
+ }
166
+ ```
167
+
168
+ ## Multiple Implementations
169
+
170
+ ### In-Memory Repository (for testing)
171
+
172
+ ```typescript
173
+ // ✅ In-memory implementation for fast tests
174
+ export class InMemoryUserRepository implements IUserRepository {
175
+ private users: Map<string, User> = new Map();
176
+
177
+ async findById(id: string): Promise<User | null> {
178
+ return this.users.get(id) || null;
179
+ }
180
+
181
+ async findByEmail(email: string): Promise<User | null> {
182
+ return Array.from(this.users.values())
183
+ .find(user => user.email === email) || null;
184
+ }
185
+
186
+ async save(user: User): Promise<void> {
187
+ this.users.set(user.id, user);
188
+ }
189
+
190
+ async delete(id: string): Promise<void> {
191
+ this.users.delete(id);
192
+ }
193
+
194
+ async findActiveUsers(): Promise<User[]> {
195
+ return Array.from(this.users.values())
196
+ .filter(user => user.isActive);
197
+ }
198
+
199
+ async findUsersByRole(role: string): Promise<User[]> {
200
+ return Array.from(this.users.values())
201
+ .filter(user => user.role === role);
202
+ }
203
+
204
+ // Test helper
205
+ clear(): void {
206
+ this.users.clear();
207
+ }
208
+ }
209
+ ```
210
+
211
+ ### Redis Repository (for caching)
212
+
213
+ ```typescript
214
+ // ✅ Redis implementation for caching layer
215
+ export class RedisUserRepository implements IUserRepository {
216
+ constructor(
217
+ private redis: Redis,
218
+ private fallbackRepo: IUserRepository // Delegate to DB if cache miss
219
+ ) {}
220
+
221
+ async findById(id: string): Promise<User | null> {
222
+ // Try cache first
223
+ const cached = await this.redis.get(`user:${id}`);
224
+ if (cached) {
225
+ return JSON.parse(cached);
226
+ }
227
+
228
+ // Fall back to database
229
+ const user = await this.fallbackRepo.findById(id);
230
+ if (user) {
231
+ await this.redis.setex(`user:${id}`, 3600, JSON.stringify(user));
232
+ }
233
+
234
+ return user;
235
+ }
236
+
237
+ async save(user: User): Promise<void> {
238
+ // Save to database
239
+ await this.fallbackRepo.save(user);
240
+
241
+ // Update cache
242
+ await this.redis.setex(`user:${user.id}`, 3600, JSON.stringify(user));
243
+ }
244
+
245
+ async delete(id: string): Promise<void> {
246
+ await this.fallbackRepo.delete(id);
247
+ await this.redis.del(`user:${id}`);
248
+ }
249
+
250
+ // Other methods...
251
+ }
252
+ ```
253
+
254
+ ## Specification Pattern (Advanced)
255
+
256
+ For complex queries, use specification pattern:
257
+
258
+ ```typescript
259
+ // ✅ Specification for complex filtering
260
+ export interface Specification<T> {
261
+ isSatisfiedBy(item: T): boolean;
262
+ toSql(): { where: string; params: any[] }; // Optional for DB queries
263
+ }
264
+
265
+ export class ActiveUserSpec implements Specification<User> {
266
+ isSatisfiedBy(user: User): boolean {
267
+ return user.isActive;
268
+ }
269
+
270
+ toSql() {
271
+ return { where: 'is_active = true', params: [] };
272
+ }
273
+ }
274
+
275
+ export class RoleSpec implements Specification<User> {
276
+ constructor(private role: string) {}
277
+
278
+ isSatisfiedBy(user: User): boolean {
279
+ return user.role === this.role;
280
+ }
281
+
282
+ toSql() {
283
+ return { where: 'role = $1', params: [this.role] };
284
+ }
285
+ }
286
+
287
+ // ✅ Combine specifications
288
+ export class AndSpec<T> implements Specification<T> {
289
+ constructor(private specs: Specification<T>[]) {}
290
+
291
+ isSatisfiedBy(item: T): boolean {
292
+ return this.specs.every(spec => spec.isSatisfiedBy(item));
293
+ }
294
+
295
+ toSql() {
296
+ const sqlParts = this.specs.map(s => s.toSql());
297
+ const where = sqlParts.map((s, i) => `(${s.where})`).join(' AND ');
298
+ const params = sqlParts.flatMap(s => s.params);
299
+ return { where, params };
300
+ }
301
+ }
302
+
303
+ // ✅ Repository accepts specifications
304
+ interface IUserRepository {
305
+ find(spec: Specification<User>): Promise<User[]>;
306
+ }
307
+
308
+ // Usage
309
+ const activeAdmins = await userRepo.find(
310
+ new AndSpec([
311
+ new ActiveUserSpec(),
312
+ new RoleSpec('admin')
313
+ ])
314
+ );
315
+ ```
316
+
317
+ ## Testing Strategy
318
+
319
+ ```typescript
320
+ // ✅ Test service with in-memory repository
321
+ describe('UserService', () => {
322
+ let service: UserService;
323
+ let repo: InMemoryUserRepository;
324
+
325
+ beforeEach(() => {
326
+ repo = new InMemoryUserRepository();
327
+ service = new UserService(repo);
328
+ });
329
+
330
+ it('registers new user', async () => {
331
+ const user = await service.registerUser('test@example.com', 'password');
332
+
333
+ expect(user.email).toBe('test@example.com');
334
+
335
+ // Verify persisted
336
+ const found = await repo.findById(user.id);
337
+ expect(found).toBeTruthy();
338
+ });
339
+
340
+ it('throws if user already exists', async () => {
341
+ await service.registerUser('test@example.com', 'password');
342
+
343
+ await expect(
344
+ service.registerUser('test@example.com', 'password')
345
+ ).rejects.toThrow('User already exists');
346
+ });
347
+ });
348
+
349
+ // ✅ Test repository with real database (integration test)
350
+ describe('PostgresUserRepository', () => {
351
+ let repo: PostgresUserRepository;
352
+ let db: Database;
353
+
354
+ beforeEach(async () => {
355
+ db = await createTestDatabase();
356
+ repo = new PostgresUserRepository(db);
357
+ });
358
+
359
+ afterEach(async () => {
360
+ await db.close();
361
+ });
362
+
363
+ it('saves and retrieves user', async () => {
364
+ const user = new User('1', 'test@example.com', 'hash', 'user');
365
+
366
+ await repo.save(user);
367
+ const found = await repo.findById('1');
368
+
369
+ expect(found?.email).toBe('test@example.com');
370
+ });
371
+ });
372
+ ```
373
+
374
+ ## File Organization
375
+
376
+ ```
377
+ src/
378
+ ├── domain/
379
+ │ ├── models/
380
+ │ │ └── User.ts
381
+ │ └── repositories/ # Interfaces only
382
+ │ ├── IUserRepository.ts
383
+ │ └── IOrderRepository.ts
384
+
385
+ ├── application/
386
+ │ └── services/
387
+ │ └── UserService.ts # Uses IUserRepository
388
+
389
+ └── infrastructure/
390
+ └── repositories/ # Implementations
391
+ ├── PostgresUserRepository.ts
392
+ ├── InMemoryUserRepository.ts
393
+ └── RedisUserRepository.ts
394
+ ```
395
+
396
+ ## Rules Summary
397
+
398
+ - **ALWAYS** define repository interfaces in domain layer
399
+ - **ALWAYS** implement repositories in infrastructure layer
400
+ - **ALWAYS** use collection-like method names (add, remove, getById)
401
+ - **ALWAYS** return domain objects, not database rows
402
+ - **ALWAYS** make repositories responsible for data mapping only
403
+ - **NEVER** put business logic in repositories
404
+ - **NEVER** return database-specific types (like ORM entities)
405
+ - **PREFER** one repository per aggregate root
406
+ - **PREFER** repository methods that work with domain objects, not primitive types
407
+ - **CONSIDER** using in-memory implementation for testing
408
+ - **CONSIDER** specification pattern for complex queries
@@ -0,0 +1,115 @@
1
+ # Next.js Rules Pattern
2
+
3
+ ## Purpose
4
+ Enforce Next.js-specific best practices and constraints. From **v0** pattern.
5
+
6
+ ## Template
7
+
8
+ ```markdown
9
+ ## Next.js {VERSION} Guidelines
10
+
11
+ ### App Router (Next.js 13+)
12
+ - {RULE_ABOUT_SERVER_COMPONENTS}
13
+ - {RULE_ABOUT_CLIENT_COMPONENTS}
14
+ - {RULE_ABOUT_FILE_CONVENTIONS}
15
+ - {RULE_ABOUT_ROUTING}
16
+
17
+ ### Data Fetching
18
+ - {RULE_ABOUT_ASYNC_COMPONENTS}
19
+ - {RULE_ABOUT_CACHING}
20
+ - {RULE_ABOUT_REVALIDATION}
21
+
22
+ ### Performance
23
+ - {RULE_ABOUT_IMAGES}
24
+ - {RULE_ABOUT_FONTS}
25
+ - {RULE_ABOUT_BUNDLING}
26
+ ```
27
+
28
+ ## Examples
29
+
30
+ ### v0 (Next.js 14 App Router)
31
+ ```markdown
32
+ ## Next.js 14 App Router Guidelines
33
+
34
+ ### Server vs Client Components
35
+ - **Default to Server Components**: All components in app/ are Server Components unless marked with 'use client'
36
+ - **Use 'use client' only when**:
37
+ - Component uses React hooks (useState, useEffect, useContext)
38
+ - Component uses browser APIs (window, document, localStorage)
39
+ - Component needs event handlers (onClick, onSubmit, etc.)
40
+ - Component uses third-party libraries that depend on browser features
41
+
42
+ **Example**:
43
+ ```typescript
44
+ // ✅ Server Component (default) - good for static content
45
+ export default function ProductList({ products }: Props) {
46
+ return <div>{products.map(p => <ProductCard key={p.id} {...p} />)}</div>;
47
+ }
48
+
49
+ // ✅ Client Component - needed for interactivity
50
+ 'use client';
51
+ export default function SearchBar() {
52
+ const [query, setQuery] = useState('');
53
+ return <input value={query} onChange={e => setQuery(e.target.value)} />;
54
+ }
55
+ ```
56
+
57
+ ### File Conventions
58
+ - **page.tsx**: Route segment UI (exported as default)
59
+ - **layout.tsx**: Shared UI across route segments
60
+ - **loading.tsx**: Loading UI (Suspense boundary)
61
+ - **error.tsx**: Error UI (Error boundary)
62
+ - **not-found.tsx**: 404 UI
63
+ - **route.ts**: API endpoint (GET, POST, etc.)
64
+
65
+ **Route Groups**: Use (folder) to organize without affecting URL structure
66
+
67
+ ### Data Fetching
68
+ - **Async Server Components**: Fetch data directly in component
69
+ ```typescript
70
+ export default async function Page() {
71
+ const data = await fetch('https://api.example.com/data').then(r => r.json());
72
+ return <div>{data.title}</div>;
73
+ }
74
+ ```
75
+ - **Caching**: Fetch requests are automatically cached
76
+ - **Revalidation**: Use `revalidate` or `revalidatePath` for on-demand updates
77
+
78
+ ### Performance
79
+ - **Image Optimization**: Always use `next/image` component
80
+ ```typescript
81
+ import Image from 'next/image';
82
+ <Image src="/photo.jpg" width={500} height={300} alt="Photo" />
83
+ ```
84
+ - **Font Optimization**: Use `next/font` for self-hosted fonts
85
+ ```typescript
86
+ import { Inter } from 'next/font/google';
87
+ const inter = Inter({ subsets: ['latin'] });
88
+ ```
89
+ - **Dynamic Imports**: Code-split heavy components
90
+ ```typescript
91
+ const HeavyComponent = dynamic(() => import('./HeavyComponent'), { ssr: false });
92
+ ```
93
+
94
+ ### Metadata
95
+ - Export `metadata` object or `generateMetadata` function for SEO
96
+ ```typescript
97
+ export const metadata = {
98
+ title: 'Page Title',
99
+ description: 'Page description',
100
+ };
101
+ ```
102
+ ```
103
+
104
+ ## Variables
105
+ - `{VERSION}`: Next.js version (13, 14, 15)
106
+ - `{RULE_ABOUT_X}`: Specific guideline for feature X
107
+
108
+ ## Best Practices
109
+ 1. Specify Next.js version (APIs change between versions)
110
+ 2. Distinguish Pages Router vs App Router
111
+ 3. Highlight breaking changes from previous versions
112
+ 4. Show code examples (good ✅ vs bad ❌)
113
+ 5. Link to official docs for deep dives
114
+ 6. Update when new Next.js versions release
115
+ 7. Benefits: Version-appropriate code, avoids deprecated patterns, follows framework conventions
@@ -0,0 +1,181 @@
1
+ # React Patterns Pattern
2
+
3
+ ## Purpose
4
+ Enforce React best practices and common patterns. From **v0** and **same.new**.
5
+
6
+ ## Template
7
+
8
+ ```markdown
9
+ ## React Best Practices
10
+
11
+ ### Component Structure
12
+ - {RULE_ABOUT_FUNCTIONAL_VS_CLASS}
13
+ - {RULE_ABOUT_HOOKS_USAGE}
14
+ - {RULE_ABOUT_COMPOSITION}
15
+
16
+ ### State Management
17
+ - {RULE_ABOUT_LOCAL_STATE}
18
+ - {RULE_ABOUT_PROP_DRILLING}
19
+ - {RULE_ABOUT_CONTEXT}
20
+
21
+ ### Performance
22
+ - {RULE_ABOUT_MEMOIZATION}
23
+ - {RULE_ABOUT_KEYS}
24
+ - {RULE_ABOUT_EFFECTS}
25
+ ```
26
+
27
+ ## Examples
28
+
29
+ ### v0 (React 18+ Patterns)
30
+ ```markdown
31
+ ## React 18+ Best Practices
32
+
33
+ ### Component Structure
34
+ - **Use Functional Components**: No class components (deprecated pattern)
35
+ ```typescript
36
+ // ✅ Functional component with TypeScript
37
+ interface ButtonProps {
38
+ label: string;
39
+ onClick: () => void;
40
+ }
41
+
42
+ export const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
43
+ return <button onClick={onClick}>{label}</button>;
44
+ };
45
+
46
+ // ❌ Class component (outdated)
47
+ class Button extends React.Component { /* ... */ }
48
+ ```
49
+
50
+ - **Extract Custom Hooks**: Reuse stateful logic
51
+ ```typescript
52
+ // ✅ Custom hook for form state
53
+ function useFormField(initialValue: string) {
54
+ const [value, setValue] = useState(initialValue);
55
+ const [error, setError] = useState<string | null>(null);
56
+
57
+ const validate = () => {
58
+ if (!value) setError('Required');
59
+ else setError(null);
60
+ };
61
+
62
+ return { value, setValue, error, validate };
63
+ }
64
+
65
+ // Usage in component
66
+ function SignupForm() {
67
+ const email = useFormField('');
68
+ const password = useFormField('');
69
+ // ...
70
+ }
71
+ ```
72
+
73
+ - **Prefer Composition over Inheritance**
74
+ ```typescript
75
+ // ✅ Composition with children prop
76
+ function Card({ children }: { children: React.ReactNode }) {
77
+ return <div className="card">{children}</div>;
78
+ }
79
+
80
+ function ProductCard() {
81
+ return (
82
+ <Card>
83
+ <h2>Product Name</h2>
84
+ <p>Description</p>
85
+ </Card>
86
+ );
87
+ }
88
+ ```
89
+
90
+ ### State Management
91
+ - **Start with Local State**: Use useState for component-specific state
92
+ - **Lift State Up**: Move state to common parent when multiple children need it
93
+ - **Use Context for Global State**: Avoid prop drilling for deeply nested components
94
+ ```typescript
95
+ // ✅ Context for theme (accessed by many components)
96
+ const ThemeContext = createContext<'light' | 'dark'>('light');
97
+
98
+ function ThemeProvider({ children }: { children: React.ReactNode }) {
99
+ const [theme, setTheme] = useState<'light' | 'dark'>('light');
100
+ return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
101
+ }
102
+ ```
103
+
104
+ - **Consider Zustand/Jotai**: For complex client-side state (alternative to Redux)
105
+
106
+ ### Performance
107
+ - **Memoize Expensive Calculations**: Use useMemo
108
+ ```typescript
109
+ const sortedItems = useMemo(() =>
110
+ items.sort((a, b) => a.price - b.price),
111
+ [items]
112
+ );
113
+ ```
114
+
115
+ - **Memoize Callbacks**: Use useCallback for props passed to children
116
+ ```typescript
117
+ const handleClick = useCallback(() => {
118
+ console.log('Clicked');
119
+ }, []); // Empty deps = never recreated
120
+ ```
121
+
122
+ - **Always Provide Keys**: For lists (use stable IDs, not indexes)
123
+ ```typescript
124
+ // ✅ Stable unique key
125
+ {products.map(p => <ProductCard key={p.id} {...p} />)}
126
+
127
+ // ❌ Index as key (causes bugs when list changes)
128
+ {products.map((p, i) => <ProductCard key={i} {...p} />)}
129
+ ```
130
+
131
+ - **Avoid Side Effects in Render**: Use useEffect for side effects
132
+ ```typescript
133
+ // ❌ Side effect in render (runs every render)
134
+ function Component() {
135
+ document.title = 'New Title'; // Bad!
136
+ return <div>Content</div>;
137
+ }
138
+
139
+ // ✅ Side effect in useEffect (runs only when needed)
140
+ function Component() {
141
+ useEffect(() => {
142
+ document.title = 'New Title';
143
+ }, []);
144
+ return <div>Content</div>;
145
+ }
146
+ ```
147
+
148
+ ### TypeScript Integration
149
+ - **Define Prop Interfaces**: Explicit types for all components
150
+ - **Use React.FC sparingly**: Prefer explicit return types
151
+ ```typescript
152
+ // ✅ Explicit props and return type
153
+ interface Props {
154
+ name: string;
155
+ age: number;
156
+ }
157
+
158
+ function UserCard({ name, age }: Props): JSX.Element {
159
+ return <div>{name}, {age}</div>;
160
+ }
161
+ ```
162
+
163
+ - **Type Event Handlers**: Use React's event types
164
+ ```typescript
165
+ function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
166
+ console.log(e.target.value);
167
+ }
168
+ ```
169
+ ```
170
+
171
+ ## Variables
172
+ - `{RULE_ABOUT_X}`: Specific React guideline
173
+
174
+ ## Best Practices
175
+ 1. Cover React fundamentals (components, hooks, effects)
176
+ 2. Show TypeScript integration examples
177
+ 3. Include good/bad code comparisons
178
+ 4. Address common mistakes (useEffect dependencies, key props)
179
+ 5. Mention state management options (Context, Zustand, Redux)
180
+ 6. Update for React version features (18+: Suspense, Concurrent Rendering)
181
+ 7. Benefits: Consistent React patterns, avoids common pitfalls, TypeScript-safe code