metacoding 1.2.1 → 1.4.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/CHANGELOG.md +33 -0
- package/README.md +4 -0
- package/lib/commands/init.d.ts.map +1 -1
- package/lib/commands/init.js +7 -0
- package/lib/commands/init.js.map +1 -1
- package/lib/services/gitignore-manager.d.ts.map +1 -1
- package/lib/services/gitignore-manager.js +1 -0
- package/lib/services/gitignore-manager.js.map +1 -1
- package/lib/services/project-detector.d.ts +1 -0
- package/lib/services/project-detector.d.ts.map +1 -1
- package/lib/services/project-detector.js +85 -8
- package/lib/services/project-detector.js.map +1 -1
- package/lib/services/template-manager.d.ts.map +1 -1
- package/lib/services/template-manager.js +18 -0
- package/lib/services/template-manager.js.map +1 -1
- package/package.json +1 -1
- package/templates/javascript/javascript.coding.instructions.md +500 -0
- package/templates/javascript/javascript.docs.instructions.md +532 -0
- package/templates/javascript/javascript.testing.instructions.md +686 -0
- package/templates/javascript/template.json +36 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Modern JavaScript coding standards and best practices for browser and Node.js environments'
|
|
3
|
+
applyTo: 'src/**/*.{js,mjs}'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Modern JavaScript Development Guidelines
|
|
7
|
+
|
|
8
|
+
## Language and Framework Preferences
|
|
9
|
+
|
|
10
|
+
- **Primary Language:** Modern JavaScript (ES6+/ES2020+)
|
|
11
|
+
- **Code Style:** Follow project's ESLint/Prettier configuration
|
|
12
|
+
- **Target Compatibility:** Node.js 18+, modern browsers (ES2020+)
|
|
13
|
+
- **Module System:** ES modules (import/export) preferred over CommonJS
|
|
14
|
+
|
|
15
|
+
## Core JavaScript Principles
|
|
16
|
+
|
|
17
|
+
- **Modern Syntax:** Use ES6+ features (arrow functions, destructuring, async/await)
|
|
18
|
+
- **Immutability:** Prefer immutable patterns and functional programming concepts
|
|
19
|
+
- **Async Programming:** Use async/await for asynchronous operations, avoid callback hell
|
|
20
|
+
- **Error Handling:** Implement comprehensive error handling with try/catch blocks
|
|
21
|
+
- **Performance:** Consider performance implications of operations and memory usage
|
|
22
|
+
|
|
23
|
+
## Naming Conventions
|
|
24
|
+
|
|
25
|
+
- **Files:** Use kebab-case for file names (e.g., `user-service.js`, `api-client.js`)
|
|
26
|
+
- **Classes:** PascalCase (e.g., `UserService`, `ApiClient`)
|
|
27
|
+
- **Functions/Methods:** camelCase (e.g., `getUserById`, `validateInput`)
|
|
28
|
+
- **Variables:** camelCase (e.g., `userId`, `isValid`, `userProfile`)
|
|
29
|
+
- **Constants:** SCREAMING_SNAKE_CASE (e.g., `MAX_RETRY_ATTEMPTS`, `API_BASE_URL`)
|
|
30
|
+
- **Private Methods:** Prefix with underscore (e.g., `_validateData`, `_handleError`)
|
|
31
|
+
|
|
32
|
+
## Modern JavaScript Best Practices
|
|
33
|
+
|
|
34
|
+
### ES6+ Features and Patterns
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// ✅ Good: Use const/let instead of var
|
|
38
|
+
const API_URL = 'https://api.example.com';
|
|
39
|
+
let userCount = 0;
|
|
40
|
+
|
|
41
|
+
// ✅ Good: Arrow functions for concise syntax
|
|
42
|
+
const users = data.map((user) => user.name);
|
|
43
|
+
const filteredUsers = users.filter((user) => user.active);
|
|
44
|
+
|
|
45
|
+
// ✅ Good: Destructuring assignment
|
|
46
|
+
const {
|
|
47
|
+
name,
|
|
48
|
+
email,
|
|
49
|
+
address: { city },
|
|
50
|
+
} = user;
|
|
51
|
+
const [first, second, ...rest] = items;
|
|
52
|
+
|
|
53
|
+
// ✅ Good: Template literals
|
|
54
|
+
const message = `Welcome ${user.name}, you have ${count} notifications`;
|
|
55
|
+
|
|
56
|
+
// ✅ Good: Spread operator
|
|
57
|
+
const newUser = { ...existingUser, status: 'active' };
|
|
58
|
+
const mergedArray = [...array1, ...array2];
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Async/Await Patterns
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// ✅ Good: Async/await with proper error handling
|
|
65
|
+
async function fetchUserData(userId) {
|
|
66
|
+
try {
|
|
67
|
+
const response = await fetch(`/api/users/${userId}`);
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
70
|
+
}
|
|
71
|
+
const userData = await response.json();
|
|
72
|
+
return userData;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('Failed to fetch user data:', error);
|
|
75
|
+
throw error; // Re-throw to allow caller to handle
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ✅ Good: Promise.all for concurrent operations
|
|
80
|
+
async function fetchMultipleUsers(userIds) {
|
|
81
|
+
try {
|
|
82
|
+
const userPromises = userIds.map((id) => fetchUserData(id));
|
|
83
|
+
const users = await Promise.all(userPromises);
|
|
84
|
+
return users;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('Failed to fetch multiple users:', error);
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Functional Programming Patterns
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// ✅ Good: Pure functions
|
|
96
|
+
const calculateTotal = (items) =>
|
|
97
|
+
items.reduce((total, item) => total + item.price, 0);
|
|
98
|
+
|
|
99
|
+
// ✅ Good: Higher-order functions
|
|
100
|
+
const withRetry =
|
|
101
|
+
(fn, maxAttempts = 3) =>
|
|
102
|
+
async (...args) => {
|
|
103
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
104
|
+
try {
|
|
105
|
+
return await fn(...args);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (attempt === maxAttempts) throw error;
|
|
108
|
+
await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// ✅ Good: Composition and chaining
|
|
114
|
+
const processUsers = (users) =>
|
|
115
|
+
users
|
|
116
|
+
.filter((user) => user.active)
|
|
117
|
+
.map((user) => ({
|
|
118
|
+
...user,
|
|
119
|
+
displayName: `${user.firstName} ${user.lastName}`,
|
|
120
|
+
}))
|
|
121
|
+
.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Browser-Specific Development
|
|
125
|
+
|
|
126
|
+
### DOM Manipulation Best Practices
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
// ✅ Good: Modern DOM querying
|
|
130
|
+
const userElement = document.querySelector('[data-user-id="123"]');
|
|
131
|
+
const allButtons = document.querySelectorAll('.btn');
|
|
132
|
+
|
|
133
|
+
// ✅ Good: Event delegation
|
|
134
|
+
document.addEventListener('click', (event) => {
|
|
135
|
+
if (event.target.matches('.btn-submit')) {
|
|
136
|
+
handleSubmit(event);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ✅ Good: Modern event handling
|
|
141
|
+
const button = document.getElementById('submit-btn');
|
|
142
|
+
button.addEventListener('click', async (event) => {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
await handleFormSubmission();
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Web APIs and Browser Features
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
// ✅ Good: Fetch API with error handling
|
|
152
|
+
async function apiRequest(url, options = {}) {
|
|
153
|
+
const config = {
|
|
154
|
+
headers: {
|
|
155
|
+
'Content-Type': 'application/json',
|
|
156
|
+
},
|
|
157
|
+
...options,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const response = await fetch(url, config);
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
throw new Error(`API request failed: ${response.status}`);
|
|
164
|
+
}
|
|
165
|
+
return await response.json();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error('API request error:', error);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ✅ Good: Local storage with error handling
|
|
173
|
+
function saveToStorage(key, data) {
|
|
174
|
+
try {
|
|
175
|
+
localStorage.setItem(key, JSON.stringify(data));
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('Failed to save to localStorage:', error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function loadFromStorage(key) {
|
|
182
|
+
try {
|
|
183
|
+
const data = localStorage.getItem(key);
|
|
184
|
+
return data ? JSON.parse(data) : null;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error('Failed to load from localStorage:', error);
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Node.js-Specific Development
|
|
193
|
+
|
|
194
|
+
### Module Patterns
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
// ✅ Good: ES module exports
|
|
198
|
+
export const config = {
|
|
199
|
+
port: process.env.PORT || 3000,
|
|
200
|
+
dbUrl: process.env.DATABASE_URL,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export class UserService {
|
|
204
|
+
constructor(database) {
|
|
205
|
+
this.db = database;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async findById(id) {
|
|
209
|
+
return await this.db.users.findById(id);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export default UserService;
|
|
214
|
+
|
|
215
|
+
// ✅ Good: Named imports
|
|
216
|
+
import { config } from './config.js';
|
|
217
|
+
import UserService from './user-service.js';
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### File System and Path Operations
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
import { promises as fs } from 'fs';
|
|
224
|
+
import path from 'path';
|
|
225
|
+
import { fileURLToPath } from 'url';
|
|
226
|
+
|
|
227
|
+
// ✅ Good: ES module __dirname equivalent
|
|
228
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
229
|
+
const __dirname = path.dirname(__filename);
|
|
230
|
+
|
|
231
|
+
// ✅ Good: Async file operations
|
|
232
|
+
async function readConfigFile(filename) {
|
|
233
|
+
try {
|
|
234
|
+
const configPath = path.join(__dirname, 'config', filename);
|
|
235
|
+
const data = await fs.readFile(configPath, 'utf8');
|
|
236
|
+
return JSON.parse(data);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error(`Failed to read config file ${filename}:`, error);
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Error Handling Patterns
|
|
245
|
+
|
|
246
|
+
### Comprehensive Error Management
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
// ✅ Good: Custom error classes
|
|
250
|
+
class ValidationError extends Error {
|
|
251
|
+
constructor(message, field) {
|
|
252
|
+
super(message);
|
|
253
|
+
this.name = 'ValidationError';
|
|
254
|
+
this.field = field;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
class ApiError extends Error {
|
|
259
|
+
constructor(message, statusCode, originalError) {
|
|
260
|
+
super(message);
|
|
261
|
+
this.name = 'ApiError';
|
|
262
|
+
this.statusCode = statusCode;
|
|
263
|
+
this.originalError = originalError;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ✅ Good: Error boundary patterns
|
|
268
|
+
async function withErrorHandling(operation, context = '') {
|
|
269
|
+
try {
|
|
270
|
+
return await operation();
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(`Error in ${context}:`, error);
|
|
273
|
+
|
|
274
|
+
// Re-throw known errors
|
|
275
|
+
if (error instanceof ValidationError || error instanceof ApiError) {
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Wrap unknown errors
|
|
280
|
+
throw new Error(`Unexpected error in ${context}: ${error.message}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Security Best Practices
|
|
286
|
+
|
|
287
|
+
### Input Validation and Sanitization
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
// ✅ Good: Input validation
|
|
291
|
+
function validateEmail(email) {
|
|
292
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
293
|
+
return emailRegex.test(email);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function sanitizeInput(input) {
|
|
297
|
+
if (typeof input !== 'string') return '';
|
|
298
|
+
return input.trim().replace(/[<>]/g, '');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ✅ Good: Data validation with schema
|
|
302
|
+
function validateUserData(userData) {
|
|
303
|
+
const errors = [];
|
|
304
|
+
|
|
305
|
+
if (!userData.name || typeof userData.name !== 'string') {
|
|
306
|
+
errors.push('Name is required and must be a string');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!validateEmail(userData.email)) {
|
|
310
|
+
errors.push('Valid email address is required');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (errors.length > 0) {
|
|
314
|
+
throw new ValidationError('Validation failed', errors);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
name: sanitizeInput(userData.name),
|
|
319
|
+
email: userData.email.toLowerCase(),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Performance Optimization
|
|
325
|
+
|
|
326
|
+
### Efficient Data Operations
|
|
327
|
+
|
|
328
|
+
```javascript
|
|
329
|
+
// ✅ Good: Efficient array operations
|
|
330
|
+
const processLargeDataset = (items) => {
|
|
331
|
+
// Use for...of for better performance with large arrays
|
|
332
|
+
const results = [];
|
|
333
|
+
for (const item of items) {
|
|
334
|
+
if (item.active) {
|
|
335
|
+
results.push(transformItem(item));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return results;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// ✅ Good: Debouncing for performance
|
|
342
|
+
function debounce(func, wait) {
|
|
343
|
+
let timeout;
|
|
344
|
+
return function executedFunction(...args) {
|
|
345
|
+
const later = () => {
|
|
346
|
+
clearTimeout(timeout);
|
|
347
|
+
func.apply(this, args);
|
|
348
|
+
};
|
|
349
|
+
clearTimeout(timeout);
|
|
350
|
+
timeout = setTimeout(later, wait);
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ✅ Good: Memoization for expensive operations
|
|
355
|
+
function memoize(fn) {
|
|
356
|
+
const cache = new Map();
|
|
357
|
+
return function memoized(...args) {
|
|
358
|
+
const key = JSON.stringify(args);
|
|
359
|
+
if (cache.has(key)) {
|
|
360
|
+
return cache.get(key);
|
|
361
|
+
}
|
|
362
|
+
const result = fn.apply(this, args);
|
|
363
|
+
cache.set(key, result);
|
|
364
|
+
return result;
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Build Tools and Development Workflow
|
|
370
|
+
|
|
371
|
+
### Package.json Best Practices
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
// ✅ Good: Well-structured package.json scripts
|
|
375
|
+
{
|
|
376
|
+
"scripts": {
|
|
377
|
+
"dev": "vite",
|
|
378
|
+
"build": "vite build",
|
|
379
|
+
"preview": "vite preview",
|
|
380
|
+
"test": "jest",
|
|
381
|
+
"test:watch": "jest --watch",
|
|
382
|
+
"test:coverage": "jest --coverage",
|
|
383
|
+
"lint": "eslint src/**/*.js",
|
|
384
|
+
"lint:fix": "eslint src/**/*.js --fix",
|
|
385
|
+
"format": "prettier --write src/**/*.js"
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Environment Configuration
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
// ✅ Good: Environment configuration
|
|
394
|
+
const config = {
|
|
395
|
+
development: {
|
|
396
|
+
apiUrl: 'http://localhost:3000/api',
|
|
397
|
+
debug: true,
|
|
398
|
+
},
|
|
399
|
+
production: {
|
|
400
|
+
apiUrl: 'https://api.production.com',
|
|
401
|
+
debug: false,
|
|
402
|
+
},
|
|
403
|
+
test: {
|
|
404
|
+
apiUrl: 'http://localhost:3001/api',
|
|
405
|
+
debug: false,
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
export default config[process.env.NODE_ENV || 'development'];
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Anti-Patterns to Avoid
|
|
413
|
+
|
|
414
|
+
### Common JavaScript Pitfalls
|
|
415
|
+
|
|
416
|
+
```javascript
|
|
417
|
+
// ❌ Bad: Callback hell
|
|
418
|
+
getData(function (a) {
|
|
419
|
+
getMoreData(a, function (b) {
|
|
420
|
+
getEvenMoreData(b, function (c) {
|
|
421
|
+
// Nested callbacks are hard to maintain
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// ❌ Bad: Modifying function parameters
|
|
427
|
+
function updateUser(user) {
|
|
428
|
+
user.lastUpdated = new Date(); // Mutates input parameter
|
|
429
|
+
return user;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ❌ Bad: Using var and global variables
|
|
433
|
+
var globalCounter = 0; // Avoid var and globals
|
|
434
|
+
function incrementCounter() {
|
|
435
|
+
globalCounter++; // Side effects are hard to track
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// ❌ Bad: Not handling promise rejections
|
|
439
|
+
fetch('/api/data'); // Unhandled promise
|
|
440
|
+
|
|
441
|
+
// ❌ Bad: Mixing async patterns
|
|
442
|
+
async function mixedPatterns() {
|
|
443
|
+
const data = await fetch('/api/data');
|
|
444
|
+
data.then((result) => {
|
|
445
|
+
// Don't mix async/await with .then()
|
|
446
|
+
console.log(result);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Documentation Standards
|
|
452
|
+
|
|
453
|
+
### JSDoc Documentation
|
|
454
|
+
|
|
455
|
+
```javascript
|
|
456
|
+
/**
|
|
457
|
+
* Calculates the total price including tax
|
|
458
|
+
* @param {number} basePrice - The base price before tax
|
|
459
|
+
* @param {number} taxRate - The tax rate as a decimal (e.g., 0.08 for 8%)
|
|
460
|
+
* @returns {number} The total price including tax
|
|
461
|
+
* @throws {Error} When basePrice or taxRate is negative
|
|
462
|
+
* @example
|
|
463
|
+
* const total = calculateTotalPrice(100, 0.08); // Returns 108
|
|
464
|
+
*/
|
|
465
|
+
function calculateTotalPrice(basePrice, taxRate) {
|
|
466
|
+
if (basePrice < 0 || taxRate < 0) {
|
|
467
|
+
throw new Error('Price and tax rate must be non-negative');
|
|
468
|
+
}
|
|
469
|
+
return basePrice * (1 + taxRate);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* User service for managing user data
|
|
474
|
+
* @class
|
|
475
|
+
*/
|
|
476
|
+
class UserService {
|
|
477
|
+
/**
|
|
478
|
+
* Creates a new UserService instance
|
|
479
|
+
* @param {Object} database - Database connection instance
|
|
480
|
+
*/
|
|
481
|
+
constructor(database) {
|
|
482
|
+
this.db = database;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Retrieves a user by ID
|
|
487
|
+
* @param {string} userId - The unique user identifier
|
|
488
|
+
* @returns {Promise<Object|null>} User object or null if not found
|
|
489
|
+
* @async
|
|
490
|
+
*/
|
|
491
|
+
async getUserById(userId) {
|
|
492
|
+
try {
|
|
493
|
+
return await this.db.users.findById(userId);
|
|
494
|
+
} catch (error) {
|
|
495
|
+
console.error(`Failed to fetch user ${userId}:`, error);
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
```
|