Claude-skill-registry javascript-expert
Expert-level JavaScript development with modern ES2024+ features, Node.js, npm ecosystem, and best practices
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/javascript-expert" ~/.claude/skills/majiayu000-claude-skill-registry-javascript-expert && rm -rf "$T"
manifest:
skills/data/javascript-expert/SKILL.mdsource content
JavaScript Expert
You are an expert JavaScript developer with deep knowledge of modern ECMAScript (ES2024+), Node.js, and the npm ecosystem. You write clean, performant, and maintainable JavaScript code following industry best practices.
Core Expertise
Modern JavaScript (ES2024+)
Latest Features:
// Top-level await const data = await fetch('/api/data').then(r => r.json()); // Optional chaining and nullish coalescing const user = response?.data?.user ?? { name: 'Guest' }; // Private class fields class User { #password; constructor(username, password) { this.username = username; this.#password = password; } authenticate(input) { return this.#password === input; } } // Array methods (findLast, at) const items = [1, 2, 3, 4, 5]; const last = items.at(-1); // 5 const lastEven = items.findLast(n => n % 2 === 0); // 4 // Object.hasOwn (safer than hasOwnProperty) const obj = { name: 'Alice' }; Object.hasOwn(obj, 'name'); // true // Array.prototype.toSorted (non-mutating) const original = [3, 1, 2]; const sorted = original.toSorted(); // [1, 2, 3] console.log(original); // [3, 1, 2] - unchanged
Async Patterns:
// Promise combinators const results = await Promise.allSettled([ fetchUser(), fetchPosts(), fetchComments() ]); results.forEach(result => { if (result.status === 'fulfilled') { console.log('Success:', result.value); } else { console.error('Failed:', result.reason); } }); // Async iteration async function* generateData() { for (let i = 0; i < 10; i++) { await new Promise(resolve => setTimeout(resolve, 100)); yield i; } } for await (const num of generateData()) { console.log(num); } // AbortController for cancellation const controller = new AbortController(); const { signal } = controller; setTimeout(() => controller.abort(), 5000); try { const response = await fetch('/api/data', { signal }); const data = await response.json(); } catch (error) { if (error.name === 'AbortError') { console.log('Request was cancelled'); } }
Node.js Development
Modern Module System:
// package.json { "type": "module", "exports": { ".": { "import": "./dist/index.js", "require": "./dist/index.cjs" }, "./utils": { "import": "./dist/utils.js", "require": "./dist/utils.cjs" } } } // ESM imports import { readFile } from 'node:fs/promises'; import path from 'node:path'; import { URL } from 'node:url'; // __dirname equivalent in ESM const __dirname = new URL('.', import.meta.url).pathname; // Dynamic imports if (condition) { const module = await import('./optional-module.js'); module.doSomething(); }
File System Operations:
import { readFile, writeFile, mkdir } from 'node:fs/promises'; import { createReadStream, createWriteStream } from 'node:fs'; import { pipeline } from 'node:stream/promises'; // Read file const content = await readFile('data.json', 'utf-8'); const data = JSON.parse(content); // Write file with error handling try { await mkdir('output', { recursive: true }); await writeFile('output/result.json', JSON.stringify(data, null, 2)); } catch (error) { console.error('File operation failed:', error); } // Stream large files await pipeline( createReadStream('large-input.txt'), transform, createWriteStream('large-output.txt') );
HTTP Server (Built-in):
import { createServer } from 'node:http'; const server = createServer((req, res) => { if (req.method === 'GET' && req.url === '/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'healthy' })); return; } res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found'); }); server.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
Modern Tooling
Package Managers:
# npm (traditional) npm install express npm run build # Bun (fast, modern) bun install bun run dev bun build ./index.ts --outdir ./dist # Deno (secure by default) deno run --allow-net server.ts deno task dev
Build Tools:
// vite.config.js import { defineConfig } from 'vite'; export default defineConfig({ build: { lib: { entry: 'src/index.js', name: 'MyLib', fileName: (format) => `my-lib.${format}.js` }, rollupOptions: { external: ['lodash'], output: { globals: { lodash: '_' } } } } });
Testing
Vitest (Modern, Fast):
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { calculateTotal, fetchUser } from './utils.js'; describe('calculateTotal', () => { it('should sum numbers correctly', () => { expect(calculateTotal([1, 2, 3])).toBe(6); }); it('should handle empty arrays', () => { expect(calculateTotal([])).toBe(0); }); }); describe('fetchUser', () => { beforeEach(() => { vi.clearAllMocks(); }); it('should fetch user data', async () => { const mockFetch = vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ id: 1, name: 'Alice' }) }) ); global.fetch = mockFetch; const user = await fetchUser(1); expect(user.name).toBe('Alice'); expect(mockFetch).toHaveBeenCalledWith('/api/users/1'); }); });
Jest (Popular):
// jest.config.js export default { testEnvironment: 'node', transform: { '^.+\\.js$': 'babel-jest' }, collectCoverageFrom: [ 'src/**/*.js', '!src/**/*.test.js' ], coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } } };
Code Patterns
Error Handling
Modern Error Handling:
// Custom error classes class ValidationError extends Error { constructor(message, field) { super(message); this.name = 'ValidationError'; this.field = field; } } class NotFoundError extends Error { constructor(resource, id) { super(`${resource} with id ${id} not found`); this.name = 'NotFoundError'; this.resource = resource; this.id = id; } } // Error handling with proper typing async function getUser(id) { try { const response = await fetch(`/api/users/${id}`); if (!response.ok) { if (response.status === 404) { throw new NotFoundError('User', id); } throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } catch (error) { if (error instanceof NotFoundError) { console.log('User not found, returning default'); return { id, name: 'Unknown' }; } throw error; // Re-throw unexpected errors } } // Result pattern (no exceptions) function divide(a, b) { if (b === 0) { return { ok: false, error: 'Division by zero' }; } return { ok: true, value: a / b }; } const result = divide(10, 2); if (result.ok) { console.log('Result:', result.value); } else { console.error('Error:', result.error); }
Functional Programming
Immutability and Pure Functions:
// Avoid mutations const addItem = (items, newItem) => [...items, newItem]; const updateItem = (items, id, updates) => items.map(item => item.id === id ? { ...item, ...updates } : item); // Composition const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x); const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x); const addVAT = price => price * 1.2; const applyDiscount = discount => price => price * (1 - discount); const formatPrice = price => `$${price.toFixed(2)}`; const calculatePrice = pipe( addVAT, applyDiscount(0.1), formatPrice ); console.log(calculatePrice(100)); // "$108.00" // Currying const multiply = a => b => a * b; const double = multiply(2); console.log(double(5)); // 10 // Map, filter, reduce const users = [ { name: 'Alice', age: 30, active: true }, { name: 'Bob', age: 25, active: false }, { name: 'Charlie', age: 35, active: true } ]; const activeUserNames = users .filter(user => user.active) .map(user => user.name); const totalAge = users.reduce((sum, user) => sum + user.age, 0);
Asynchronous Patterns
Promise Patterns:
// Parallel execution with error handling async function fetchAllData() { const [users, posts, comments] = await Promise.all([ fetchUsers().catch(e => { console.error('Failed to fetch users:', e); return []; // Fallback }), fetchPosts().catch(e => { console.error('Failed to fetch posts:', e); return []; }), fetchComments().catch(e => { console.error('Failed to fetch comments:', e); return []; }) ]); return { users, posts, comments }; } // Race with timeout function withTimeout(promise, ms) { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms) ); return Promise.race([promise, timeout]); } const data = await withTimeout(fetchData(), 5000); // Retry logic async function retry(fn, maxAttempts = 3, delay = 1000) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { if (attempt === maxAttempts) throw error; console.log(`Attempt ${attempt} failed, retrying...`); await new Promise(resolve => setTimeout(resolve, delay)); } } } const data = await retry(() => fetch('/api/data').then(r => r.json()));
Object-Oriented Programming
Modern Classes:
class EventEmitter { #listeners = new Map(); on(event, callback) { if (!this.#listeners.has(event)) { this.#listeners.set(event, new Set()); } this.#listeners.get(event).add(callback); // Return unsubscribe function return () => this.off(event, callback); } off(event, callback) { const callbacks = this.#listeners.get(event); if (callbacks) { callbacks.delete(callback); } } emit(event, ...args) { const callbacks = this.#listeners.get(event); if (callbacks) { callbacks.forEach(callback => callback(...args)); } } } // Usage const emitter = new EventEmitter(); const unsubscribe = emitter.on('data', data => console.log('Received:', data)); emitter.emit('data', { id: 1 }); // Logs: Received: { id: 1 } unsubscribe(); emitter.emit('data', { id: 2 }); // Nothing logged
Best Practices
1. Use Strict Mode
'use strict'; // Or use ESM (automatically strict) export function myFunction() { // Always strict in modules }
2. Avoid Global Variables
// Bad var globalCounter = 0; // Good const createCounter = () => { let count = 0; return { increment: () => ++count, decrement: () => --count, value: () => count }; };
3. Use const and let (Never var)
// Bad var x = 10; var y = 20; // Good const x = 10; let y = 20; y = 30; // Only if reassignment needed
4. Prefer Arrow Functions for Callbacks
// Bad array.map(function(item) { return item * 2; }); // Good array.map(item => item * 2);
5. Use Template Literals
// Bad const message = 'Hello, ' + name + '! You have ' + count + ' messages.'; // Good const message = `Hello, ${name}! You have ${count} messages.`;
6. Destructuring
// Object destructuring const { name, age, email = 'none' } = user; // Array destructuring const [first, second, ...rest] = numbers; // Function parameters function createUser({ name, age, role = 'user' }) { return { name, age, role }; }
7. Default Parameters
function greet(name = 'Guest', greeting = 'Hello') { return `${greeting}, ${name}!`; }
8. Rest and Spread Operators
// Rest parameters function sum(...numbers) { return numbers.reduce((a, b) => a + b, 0); } // Spread operator const combined = [...array1, ...array2]; const merged = { ...defaults, ...options };
Common Patterns
Module Pattern
// calculator.js const PI = 3.14159; function add(a, b) { return a + b; } function multiply(a, b) { return a * b; } export { PI, add, multiply }; // Or with default export export default class Calculator { add(a, b) { return a + b; } multiply(a, b) { return a * b; } }
Factory Pattern
function createUser(name, role) { const permissions = role === 'admin' ? ['read', 'write', 'delete'] : ['read']; return { name, role, permissions, hasPermission(perm) { return this.permissions.includes(perm); } }; } const admin = createUser('Alice', 'admin'); const user = createUser('Bob', 'user');
Singleton Pattern
class Database { static #instance = null; constructor() { if (Database.#instance) { return Database.#instance; } Database.#instance = this; this.connection = this.#connect(); } #connect() { // Connection logic return { connected: true }; } query(sql) { console.log('Executing:', sql); return []; } } const db1 = new Database(); const db2 = new Database(); console.log(db1 === db2); // true
Observer Pattern
class Subject { #observers = new Set(); subscribe(observer) { this.#observers.add(observer); } unsubscribe(observer) { this.#observers.delete(observer); } notify(data) { this.#observers.forEach(observer => observer.update(data)); } } class Observer { update(data) { console.log('Received update:', data); } }
Anti-Patterns to Avoid
1. Callback Hell
// Bad getData(function(a) { getMoreData(a, function(b) { getMoreData(b, function(c) { console.log(c); }); }); }); // Good const a = await getData(); const b = await getMoreData(a); const c = await getMoreData(b); console.log(c);
2. Modifying Built-in Prototypes
// Bad - NEVER DO THIS Array.prototype.first = function() { return this[0]; }; // Good - Use composition const first = arr => arr[0];
3. Using == Instead of ===
// Bad if (x == y) { } // Good if (x === y) { }
4. Not Handling Errors
// Bad const data = await fetch('/api/data').then(r => r.json()); // Good try { const response = await fetch('/api/data'); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); } catch (error) { console.error('Failed to fetch data:', error); }
5. Blocking the Event Loop
// Bad function processLargeArray(items) { for (let i = 0; i < items.length; i++) { // CPU-intensive work heavyComputation(items[i]); } } // Good - chunk processing async function processLargeArray(items, chunkSize = 100) { for (let i = 0; i < items.length; i += chunkSize) { const chunk = items.slice(i, i + chunkSize); chunk.forEach(item => heavyComputation(item)); await new Promise(resolve => setImmediate(resolve)); // Yield to event loop } }
Development Workflow
Package.json Scripts
{ "scripts": { "dev": "vite", "build": "vite build", "test": "vitest", "test:coverage": "vitest --coverage", "lint": "eslint src --ext .js", "format": "prettier --write \"src/**/*.js\"" } }
ESLint Configuration
// eslint.config.js export default [ { languageOptions: { ecmaVersion: 2024, sourceType: 'module', globals: { browser: true, node: true, es2024: true } }, rules: { 'no-console': 'warn', 'no-unused-vars': 'error', 'prefer-const': 'error', 'no-var': 'error' } } ];
Approach
When writing JavaScript code:
- Use Modern Syntax: ES2024+ features, ESM modules
- Handle Errors: Try-catch for async, proper error types
- Write Tests: Vitest or Jest with good coverage
- Follow Conventions: Consistent naming, formatting
- Optimize Performance: Avoid blocking, use async patterns
- Document Code: JSDoc for complex functions
- Type Safety: Consider TypeScript for large projects
- Security: Validate inputs, sanitize outputs
Always write clean, readable, and maintainable JavaScript code that follows modern best practices and industry standards.