metacoding 1.5.1 → 2.0.1

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 (95) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +108 -514
  3. package/lib/cli.d.ts.map +1 -1
  4. package/lib/cli.js +18 -19
  5. package/lib/cli.js.map +1 -1
  6. package/lib/commands/init.d.ts +8 -14
  7. package/lib/commands/init.d.ts.map +1 -1
  8. package/lib/commands/init.js +105 -387
  9. package/lib/commands/init.js.map +1 -1
  10. package/lib/commands/update.d.ts +9 -9
  11. package/lib/commands/update.d.ts.map +1 -1
  12. package/lib/commands/update.js +141 -320
  13. package/lib/commands/update.js.map +1 -1
  14. package/lib/services/backup.d.ts +1 -1
  15. package/lib/services/backup.d.ts.map +1 -1
  16. package/lib/services/backup.js +10 -6
  17. package/lib/services/backup.js.map +1 -1
  18. package/lib/services/filesystem.d.ts.map +1 -1
  19. package/lib/services/filesystem.js +11 -5
  20. package/lib/services/filesystem.js.map +1 -1
  21. package/lib/services/gitignore-manager.js +5 -5
  22. package/lib/services/gitignore-manager.js.map +1 -1
  23. package/lib/services/project-detector.d.ts +9 -8
  24. package/lib/services/project-detector.d.ts.map +1 -1
  25. package/lib/services/project-detector.js +79 -197
  26. package/lib/services/project-detector.js.map +1 -1
  27. package/lib/services/skill-manager.d.ts +23 -0
  28. package/lib/services/skill-manager.d.ts.map +1 -0
  29. package/lib/services/skill-manager.js +212 -0
  30. package/lib/services/skill-manager.js.map +1 -0
  31. package/lib/types/index.d.ts +5 -15
  32. package/lib/types/index.d.ts.map +1 -1
  33. package/package.json +9 -17
  34. package/skills/metacoding-workflow/SKILL.md +52 -0
  35. package/skills/metacoding-workflow/agents/openai.yaml +4 -0
  36. package/skills/metacoding-workflow/assets/templates/changelog-entry.md +6 -0
  37. package/skills/metacoding-workflow/assets/templates/project-context.md +18 -0
  38. package/skills/metacoding-workflow/assets/templates/repeated-task-checklist.md +8 -0
  39. package/skills/metacoding-workflow/assets/templates/task-entry.md +9 -0
  40. package/skills/metacoding-workflow/assets/templates/test-plan.md +8 -0
  41. package/skills/metacoding-workflow/references/javascript.md +7 -0
  42. package/skills/metacoding-workflow/references/node.md +7 -0
  43. package/skills/metacoding-workflow/references/platform-adaptation.md +37 -0
  44. package/skills/metacoding-workflow/references/python.md +7 -0
  45. package/skills/metacoding-workflow/references/react.md +7 -0
  46. package/skills/metacoding-workflow/references/repository-organization.md +84 -0
  47. package/skills/metacoding-workflow/references/typescript.md +7 -0
  48. package/skills/metacoding-workflow/references/workflow-rules.md +54 -0
  49. package/skills/vendor-templates/claude-agent.md.template +41 -0
  50. package/lib/services/assistant-adapter.d.ts +0 -18
  51. package/lib/services/assistant-adapter.d.ts.map +0 -1
  52. package/lib/services/assistant-adapter.js +0 -246
  53. package/lib/services/assistant-adapter.js.map +0 -1
  54. package/lib/services/cursor.d.ts +0 -47
  55. package/lib/services/cursor.d.ts.map +0 -1
  56. package/lib/services/cursor.js +0 -314
  57. package/lib/services/cursor.js.map +0 -1
  58. package/lib/services/template-manager.d.ts +0 -23
  59. package/lib/services/template-manager.d.ts.map +0 -1
  60. package/lib/services/template-manager.js +0 -374
  61. package/lib/services/template-manager.js.map +0 -1
  62. package/lib/services/vscode.d.ts +0 -10
  63. package/lib/services/vscode.d.ts.map +0 -1
  64. package/lib/services/vscode.js +0 -108
  65. package/lib/services/vscode.js.map +0 -1
  66. package/templates/assistants/AGENTS.md +0 -203
  67. package/templates/assistants/CLAUDE.md +0 -156
  68. package/templates/assistants/GEMINI.md +0 -193
  69. package/templates/general/code-review.instructions.md +0 -265
  70. package/templates/general/copilot-instructions.md +0 -427
  71. package/templates/general/docs-update.instructions.md +0 -275
  72. package/templates/general/release.instructions.md +0 -242
  73. package/templates/general/template.json +0 -9
  74. package/templates/general/test-runner.instructions.md +0 -188
  75. package/templates/javascript/javascript.coding.instructions.md +0 -500
  76. package/templates/javascript/javascript.docs.instructions.md +0 -563
  77. package/templates/javascript/javascript.testing.instructions.md +0 -686
  78. package/templates/javascript/template.json +0 -36
  79. package/templates/node/nodejs.coding.instructions.md +0 -249
  80. package/templates/node/nodejs.docs.instructions.md +0 -261
  81. package/templates/node/nodejs.testing.instructions.md +0 -373
  82. package/templates/node/template.json +0 -23
  83. package/templates/python/python.coding.instructions.md +0 -338
  84. package/templates/python/python.docs.instructions.md +0 -1178
  85. package/templates/python/python.testing.instructions.md +0 -1073
  86. package/templates/python/template.json +0 -75
  87. package/templates/react/react.coding.instructions.md +0 -694
  88. package/templates/react/react.docs.instructions.md +0 -451
  89. package/templates/react/react.testing.instructions.md +0 -192
  90. package/templates/react/template.json +0 -14
  91. package/templates/react/test-runner.instructions.md +0 -135
  92. package/templates/typescript/template.json +0 -16
  93. package/templates/typescript/typescript.coding.instructions.md +0 -368
  94. package/templates/typescript/typescript.docs.instructions.md +0 -760
  95. package/templates/typescript/typescript.testing.instructions.md +0 -739
@@ -1,694 +0,0 @@
1
- ---
2
- description: 'React/Frontend-specific coding standards and best practices'
3
- applyTo: '**/*.{tsx,jsx,ts,js}'
4
- ---
5
-
6
- # React/Frontend Coding Standards and Best Practices
7
-
8
- ## Language and Framework Preferences
9
-
10
- - **Primary Language:** TypeScript with React 18+ for all React projects
11
- - **Build Tool:** Vite for development and build tooling
12
- - **Code Style:** Prettier with ESLint for consistent formatting and linting
13
- - **State Management:** Context API for simple state, Zustand/Redux Toolkit for complex state
14
- - **Target Compatibility:** Modern browsers (ES2020+), Node.js 18+
15
-
16
- ## Code Quality Guidelines
17
-
18
- - **Component Design:** Small, focused components with single responsibilities
19
- - **Functions:** Prefer functional components with hooks over class components
20
- - **Performance:** Use React.memo, useMemo, and useCallback judiciously
21
- - **Error Handling:** Implement error boundaries and proper error handling
22
- - **Accessibility:** Follow WCAG guidelines and use semantic HTML
23
- - **Type Safety:** Strict TypeScript configuration with comprehensive type coverage
24
-
25
- ## Naming Conventions
26
-
27
- - **Files:** PascalCase for components (e.g., `UserProfile.tsx`), camelCase for utilities (e.g., `apiHelpers.ts`)
28
- - **Components:** PascalCase (e.g., `UserCard`, `NavigationMenu`)
29
- - **Functions/Hooks:** camelCase (e.g., `useUserData`, `handleSubmit`)
30
- - **Variables:** camelCase (e.g., `userData`, `isLoading`)
31
- - **Constants:** SCREAMING_SNAKE_CASE (e.g., `API_ENDPOINTS`, `DEFAULT_TIMEOUT`)
32
- - **Interfaces/Types:** PascalCase with descriptive names (e.g., `UserData`, `ApiResponse`)
33
- - **CSS Classes:** kebab-case (e.g., `user-card`, `navigation-menu`)
34
-
35
- ## Code Organization
36
-
37
- - **Feature-Based Structure:** Organize by features rather than file types
38
- - **Component Co-location:** Keep related files (component, styles, tests) together
39
- - **Barrel Exports:** Use index.ts files for clean imports
40
- - **Separation of Concerns:** Separate business logic from presentation logic
41
-
42
- ### Recommended Project Structure
43
-
44
- ```
45
- src/
46
- components/ # Reusable UI components
47
- ui/ # Basic UI primitives (Button, Input, Modal)
48
- layout/ # Layout components (Header, Sidebar, Footer)
49
- features/ # Feature-specific components and logic
50
- auth/ # Authentication feature
51
- dashboard/ # Dashboard feature
52
- hooks/ # Custom React hooks
53
- services/ # API calls and external services
54
- utils/ # Utility functions
55
- types/ # TypeScript type definitions
56
- stores/ # State management (Context, Zustand, etc.)
57
- assets/ # Static assets (images, icons, fonts)
58
- ```
59
-
60
- ## React-Specific Best Practices
61
-
62
- ### Component Design Patterns
63
-
64
- ```tsx
65
- // Good: Functional component with TypeScript
66
- interface UserCardProps {
67
- user: User;
68
- onEdit?: (user: User) => void;
69
- className?: string;
70
- }
71
-
72
- export const UserCard: React.FC<UserCardProps> = ({
73
- user,
74
- onEdit,
75
- className,
76
- }) => {
77
- const handleEditClick = useCallback(() => {
78
- onEdit?.(user);
79
- }, [user, onEdit]);
80
-
81
- return (
82
- <div className={`user-card ${className || ''}`}>
83
- <h3>{user.name}</h3>
84
- <p>{user.email}</p>
85
- {onEdit && <button onClick={handleEditClick}>Edit</button>}
86
- </div>
87
- );
88
- };
89
- ```
90
-
91
- ### Custom Hooks
92
-
93
- ```tsx
94
- // Good: Custom hook for data fetching
95
- interface UseUserDataResult {
96
- user: User | null;
97
- loading: boolean;
98
- error: string | null;
99
- refetch: () => void;
100
- }
101
-
102
- export const useUserData = (userId: string): UseUserDataResult => {
103
- const [user, setUser] = useState<User | null>(null);
104
- const [loading, setLoading] = useState(true);
105
- const [error, setError] = useState<string | null>(null);
106
-
107
- const fetchUser = useCallback(async () => {
108
- try {
109
- setLoading(true);
110
- setError(null);
111
- const userData = await userService.getUser(userId);
112
- setUser(userData);
113
- } catch (err) {
114
- setError(err instanceof Error ? err.message : 'Unknown error');
115
- } finally {
116
- setLoading(false);
117
- }
118
- }, [userId]);
119
-
120
- useEffect(() => {
121
- fetchUser();
122
- }, [fetchUser]);
123
-
124
- return { user, loading, error, refetch: fetchUser };
125
- };
126
- ```
127
-
128
- ### State Management Patterns
129
-
130
- ```tsx
131
- // Good: Context for global state
132
- interface AppContextType {
133
- user: User | null;
134
- theme: 'light' | 'dark';
135
- setUser: (user: User | null) => void;
136
- setTheme: (theme: 'light' | 'dark') => void;
137
- }
138
-
139
- const AppContext = createContext<AppContextType | undefined>(undefined);
140
-
141
- export const useAppContext = (): AppContextType => {
142
- const context = useContext(AppContext);
143
- if (!context) {
144
- throw new Error('useAppContext must be used within AppProvider');
145
- }
146
- return context;
147
- };
148
-
149
- export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
150
- children,
151
- }) => {
152
- const [user, setUser] = useState<User | null>(null);
153
- const [theme, setTheme] = useState<'light' | 'dark'>('light');
154
-
155
- const value = useMemo(
156
- () => ({
157
- user,
158
- theme,
159
- setUser,
160
- setTheme,
161
- }),
162
- [user, theme]
163
- );
164
-
165
- return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
166
- };
167
- ```
168
-
169
- ## Performance Optimization
170
-
171
- ### Component Optimization
172
-
173
- ```tsx
174
- // Good: Memoized component to prevent unnecessary re-renders
175
- interface UserListProps {
176
- users: User[];
177
- onUserSelect: (user: User) => void;
178
- }
179
-
180
- export const UserList = React.memo<UserListProps>(({ users, onUserSelect }) => {
181
- return (
182
- <div className="user-list">
183
- {users.map((user) => (
184
- <UserCard key={user.id} user={user} onEdit={onUserSelect} />
185
- ))}
186
- </div>
187
- );
188
- });
189
-
190
- UserList.displayName = 'UserList';
191
- ```
192
-
193
- ### Optimized Callbacks and Values
194
-
195
- ```tsx
196
- // Good: Memoized callbacks and computed values
197
- export const Dashboard: React.FC = () => {
198
- const [filter, setFilter] = useState('');
199
- const [users, setUsers] = useState<User[]>([]);
200
-
201
- // Memoize expensive computations
202
- const filteredUsers = useMemo(() => {
203
- return users.filter((user) =>
204
- user.name.toLowerCase().includes(filter.toLowerCase())
205
- );
206
- }, [users, filter]);
207
-
208
- // Memoize callbacks to prevent child re-renders
209
- const handleUserSelect = useCallback((user: User) => {
210
- console.log('Selected user:', user);
211
- }, []);
212
-
213
- const handleFilterChange = useCallback(
214
- (event: React.ChangeEvent<HTMLInputElement>) => {
215
- setFilter(event.target.value);
216
- },
217
- []
218
- );
219
-
220
- return (
221
- <div>
222
- <input
223
- value={filter}
224
- onChange={handleFilterChange}
225
- placeholder="Filter users..."
226
- />
227
- <UserList users={filteredUsers} onUserSelect={handleUserSelect} />
228
- </div>
229
- );
230
- };
231
- ```
232
-
233
- ## Error Handling and Validation
234
-
235
- ### Error Boundaries
236
-
237
- ```tsx
238
- interface ErrorBoundaryState {
239
- hasError: boolean;
240
- error?: Error;
241
- }
242
-
243
- class ErrorBoundary extends React.Component<
244
- React.PropsWithChildren<{}>,
245
- ErrorBoundaryState
246
- > {
247
- constructor(props: React.PropsWithChildren<{}>) {
248
- super(props);
249
- this.state = { hasError: false };
250
- }
251
-
252
- static getDerivedStateFromError(error: Error): ErrorBoundaryState {
253
- return { hasError: true, error };
254
- }
255
-
256
- componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
257
- console.error('Error caught by boundary:', error, errorInfo);
258
- }
259
-
260
- render() {
261
- if (this.state.hasError) {
262
- return (
263
- <div className="error-boundary">
264
- <h2>Something went wrong</h2>
265
- <p>{this.state.error?.message}</p>
266
- </div>
267
- );
268
- }
269
-
270
- return this.props.children;
271
- }
272
- }
273
- ```
274
-
275
- ### Form Validation
276
-
277
- ```tsx
278
- // Good: Form with validation using react-hook-form
279
- import { useForm } from 'react-hook-form';
280
- import { zodResolver } from '@hookform/resolvers/zod';
281
- import { z } from 'zod';
282
-
283
- const userSchema = z.object({
284
- name: z.string().min(2, 'Name must be at least 2 characters'),
285
- email: z.string().email('Invalid email address'),
286
- age: z.number().min(18, 'Must be at least 18 years old'),
287
- });
288
-
289
- type UserFormData = z.infer<typeof userSchema>;
290
-
291
- export const UserForm: React.FC = () => {
292
- const {
293
- register,
294
- handleSubmit,
295
- formState: { errors, isSubmitting },
296
- } = useForm<UserFormData>({
297
- resolver: zodResolver(userSchema),
298
- });
299
-
300
- const onSubmit = async (data: UserFormData) => {
301
- try {
302
- await userService.createUser(data);
303
- } catch (error) {
304
- console.error('Failed to create user:', error);
305
- }
306
- };
307
-
308
- return (
309
- <form onSubmit={handleSubmit(onSubmit)}>
310
- <div>
311
- <input {...register('name')} placeholder="Name" />
312
- {errors.name && <p className="error">{errors.name.message}</p>}
313
- </div>
314
-
315
- <div>
316
- <input {...register('email')} placeholder="Email" />
317
- {errors.email && <p className="error">{errors.email.message}</p>}
318
- </div>
319
-
320
- <div>
321
- <input
322
- {...register('age', { valueAsNumber: true })}
323
- type="number"
324
- placeholder="Age"
325
- />
326
- {errors.age && <p className="error">{errors.age.message}</p>}
327
- </div>
328
-
329
- <button type="submit" disabled={isSubmitting}>
330
- {isSubmitting ? 'Creating...' : 'Create User'}
331
- </button>
332
- </form>
333
- );
334
- };
335
- ```
336
-
337
- ## Testing Standards
338
-
339
- ### Component Testing
340
-
341
- ```tsx
342
- // Good: Component testing with React Testing Library
343
- import { render, screen, fireEvent } from '@testing-library/react';
344
- import { UserCard } from './UserCard';
345
-
346
- const mockUser: User = {
347
- id: '1',
348
- name: 'John Doe',
349
- email: 'john@example.com',
350
- };
351
-
352
- describe('UserCard', () => {
353
- it('renders user information correctly', () => {
354
- render(<UserCard user={mockUser} />);
355
-
356
- expect(screen.getByText('John Doe')).toBeInTheDocument();
357
- expect(screen.getByText('john@example.com')).toBeInTheDocument();
358
- });
359
-
360
- it('calls onEdit when edit button is clicked', () => {
361
- const mockOnEdit = jest.fn();
362
- render(<UserCard user={mockUser} onEdit={mockOnEdit} />);
363
-
364
- const editButton = screen.getByText('Edit');
365
- fireEvent.click(editButton);
366
-
367
- expect(mockOnEdit).toHaveBeenCalledWith(mockUser);
368
- });
369
-
370
- it('does not render edit button when onEdit is not provided', () => {
371
- render(<UserCard user={mockUser} />);
372
-
373
- expect(screen.queryByText('Edit')).not.toBeInTheDocument();
374
- });
375
- });
376
- ```
377
-
378
- ### Hook Testing
379
-
380
- ```tsx
381
- // Good: Custom hook testing
382
- import { renderHook, waitFor } from '@testing-library/react';
383
- import { useUserData } from './useUserData';
384
-
385
- // Mock the service
386
- jest.mock('../services/userService');
387
-
388
- describe('useUserData', () => {
389
- beforeEach(() => {
390
- jest.clearAllMocks();
391
- });
392
-
393
- it('fetches user data successfully', async () => {
394
- const mockUser = { id: '1', name: 'John Doe' };
395
- (userService.getUser as jest.Mock).mockResolvedValue(mockUser);
396
-
397
- const { result } = renderHook(() => useUserData('1'));
398
-
399
- expect(result.current.loading).toBe(true);
400
- expect(result.current.user).toBe(null);
401
-
402
- await waitFor(() => {
403
- expect(result.current.loading).toBe(false);
404
- });
405
-
406
- expect(result.current.user).toEqual(mockUser);
407
- expect(result.current.error).toBe(null);
408
- });
409
-
410
- it('handles error states correctly', async () => {
411
- const errorMessage = 'User not found';
412
- (userService.getUser as jest.Mock).mockRejectedValue(
413
- new Error(errorMessage)
414
- );
415
-
416
- const { result } = renderHook(() => useUserData('1'));
417
-
418
- await waitFor(() => {
419
- expect(result.current.loading).toBe(false);
420
- });
421
-
422
- expect(result.current.user).toBe(null);
423
- expect(result.current.error).toBe(errorMessage);
424
- });
425
- });
426
- ```
427
-
428
- ## Accessibility Standards
429
-
430
- ### Semantic HTML and ARIA
431
-
432
- ```tsx
433
- // Good: Accessible form component
434
- export const AccessibleForm: React.FC = () => {
435
- const [fieldError, setFieldError] = useState<string>('');
436
-
437
- return (
438
- <form role="form" aria-label="User registration form">
439
- <fieldset>
440
- <legend>Personal Information</legend>
441
-
442
- <div className="form-group">
443
- <label htmlFor="username">Username:</label>
444
- <input
445
- id="username"
446
- type="text"
447
- aria-required="true"
448
- aria-describedby={fieldError ? 'username-error' : undefined}
449
- aria-invalid={fieldError ? 'true' : 'false'}
450
- />
451
- {fieldError && (
452
- <div
453
- id="username-error"
454
- role="alert"
455
- aria-live="polite"
456
- className="error-message"
457
- >
458
- {fieldError}
459
- </div>
460
- )}
461
- </div>
462
- </fieldset>
463
-
464
- <button type="submit" aria-describedby="submit-help">
465
- Register
466
- </button>
467
- <div id="submit-help" className="help-text">
468
- Click to create your account
469
- </div>
470
- </form>
471
- );
472
- };
473
- ```
474
-
475
- ### Keyboard Navigation
476
-
477
- ```tsx
478
- // Good: Keyboard accessible dropdown
479
- export const Dropdown: React.FC<DropdownProps> = ({ options, onSelect }) => {
480
- const [isOpen, setIsOpen] = useState(false);
481
- const [activeIndex, setActiveIndex] = useState(-1);
482
-
483
- const handleKeyDown = (event: React.KeyboardEvent) => {
484
- switch (event.key) {
485
- case 'Enter':
486
- case ' ':
487
- event.preventDefault();
488
- if (activeIndex >= 0) {
489
- onSelect(options[activeIndex]);
490
- }
491
- setIsOpen(!isOpen);
492
- break;
493
- case 'ArrowDown':
494
- event.preventDefault();
495
- setActiveIndex((prev) => (prev < options.length - 1 ? prev + 1 : 0));
496
- break;
497
- case 'ArrowUp':
498
- event.preventDefault();
499
- setActiveIndex((prev) => (prev > 0 ? prev - 1 : options.length - 1));
500
- break;
501
- case 'Escape':
502
- setIsOpen(false);
503
- setActiveIndex(-1);
504
- break;
505
- }
506
- };
507
-
508
- return (
509
- <div className="dropdown" onKeyDown={handleKeyDown}>
510
- <button
511
- aria-haspopup="listbox"
512
- aria-expanded={isOpen}
513
- onClick={() => setIsOpen(!isOpen)}
514
- >
515
- Select option
516
- </button>
517
- {isOpen && (
518
- <ul role="listbox" className="dropdown-menu">
519
- {options.map((option, index) => (
520
- <li
521
- key={option.id}
522
- role="option"
523
- aria-selected={index === activeIndex}
524
- className={index === activeIndex ? 'active' : ''}
525
- onClick={() => onSelect(option)}
526
- >
527
- {option.label}
528
- </li>
529
- ))}
530
- </ul>
531
- )}
532
- </div>
533
- );
534
- };
535
- ```
536
-
537
- ## Styling and CSS Standards
538
-
539
- ### CSS-in-JS with Styled Components
540
-
541
- ```tsx
542
- import styled from 'styled-components';
543
-
544
- // Good: Styled component with theme support
545
- const Button = styled.button<{ variant?: 'primary' | 'secondary' }>`
546
- padding: ${({ theme }) => theme.spacing.md};
547
- border: none;
548
- border-radius: ${({ theme }) => theme.borderRadius.sm};
549
- font-weight: 500;
550
- cursor: pointer;
551
- transition: all 0.2s ease;
552
-
553
- ${({ variant = 'primary', theme }) => {
554
- switch (variant) {
555
- case 'primary':
556
- return `
557
- background-color: ${theme.colors.primary};
558
- color: ${theme.colors.white};
559
-
560
- &:hover {
561
- background-color: ${theme.colors.primaryDark};
562
- }
563
- `;
564
- case 'secondary':
565
- return `
566
- background-color: ${theme.colors.secondary};
567
- color: ${theme.colors.text};
568
-
569
- &:hover {
570
- background-color: ${theme.colors.secondaryDark};
571
- }
572
- `;
573
- }
574
- }}
575
-
576
- &:disabled {
577
- opacity: 0.6;
578
- cursor: not-allowed;
579
- }
580
- `;
581
- ```
582
-
583
- ### CSS Modules
584
-
585
- ```tsx
586
- // UserCard.module.css
587
- .userCard {
588
- border: 1px solid #e0e0e0;
589
- border-radius: 8px;
590
- padding: 16px;
591
- margin-bottom: 16px;
592
- }
593
-
594
- .userCard:hover {
595
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
596
- }
597
-
598
- .userInfo {
599
- margin-bottom: 12px;
600
- }
601
-
602
- .userName {
603
- font-size: 1.2em;
604
- font-weight: bold;
605
- margin-bottom: 4px;
606
- }
607
-
608
- // UserCard.tsx
609
- import styles from './UserCard.module.css';
610
-
611
- export const UserCard: React.FC<UserCardProps> = ({ user }) => {
612
- return (
613
- <div className={styles.userCard}>
614
- <div className={styles.userInfo}>
615
- <h3 className={styles.userName}>{user.name}</h3>
616
- <p>{user.email}</p>
617
- </div>
618
- </div>
619
- );
620
- };
621
- ```
622
-
623
- ## Common Anti-Patterns to Avoid
624
-
625
- - **Inline Styles for Complex Styling:** Use CSS-in-JS or CSS modules instead
626
- - **Prop Drilling:** Use Context or state management for deeply nested props
627
- - **Mutating Props:** Always treat props as read-only
628
- - **Missing Keys in Lists:** Always provide unique keys for list items
629
- - **Not Cleaning Up Effects:** Remove event listeners and cancel async operations
630
- - **Using Index as Key:** Use stable, unique identifiers for keys
631
- - **Overusing useEffect:** Consider if the effect is really necessary
632
- - **Not Memoizing Expensive Calculations:** Use useMemo for expensive computations
633
- - **Creating Objects/Functions in Render:** This causes unnecessary re-renders
634
- - **Ignoring Accessibility:** Always consider screen readers and keyboard navigation
635
-
636
- ## Security Considerations
637
-
638
- ### XSS Prevention
639
-
640
- ```tsx
641
- // Good: Safe rendering of user content
642
- import DOMPurify from 'dompurify';
643
-
644
- interface SafeHtmlProps {
645
- content: string;
646
- }
647
-
648
- export const SafeHtml: React.FC<SafeHtmlProps> = ({ content }) => {
649
- const sanitizedContent = useMemo(() => {
650
- return DOMPurify.sanitize(content);
651
- }, [content]);
652
-
653
- return <div dangerouslySetInnerHTML={{ __html: sanitizedContent }} />;
654
- };
655
-
656
- // Good: Escape user input in URL parameters
657
- const SearchResults: React.FC = () => {
658
- const query = useSearchParams().get('q') || '';
659
- const encodedQuery = encodeURIComponent(query);
660
-
661
- return (
662
- <div>
663
- <h2>Results for: {query}</h2>
664
- <a href={`/search/advanced?query=${encodedQuery}`}>Advanced search</a>
665
- </div>
666
- );
667
- };
668
- ```
669
-
670
- ### Environment Variables
671
-
672
- ```tsx
673
- // Good: Environment configuration
674
- interface Config {
675
- apiUrl: string;
676
- environment: 'development' | 'staging' | 'production';
677
- enableAnalytics: boolean;
678
- }
679
-
680
- const config: Config = {
681
- apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000',
682
- environment:
683
- (import.meta.env.VITE_ENVIRONMENT as Config['environment']) ||
684
- 'development',
685
- enableAnalytics: import.meta.env.VITE_ENABLE_ANALYTICS === 'true',
686
- };
687
-
688
- // Validate required environment variables
689
- if (!config.apiUrl) {
690
- throw new Error('VITE_API_URL environment variable is required');
691
- }
692
-
693
- export default config;
694
- ```