JavaScript/ES6+ coding standards following Airbnb guidelines, modern patterns, React best practices, and comprehensive Jest testing. Use for JavaScript projects requiring clean, maintainable, production-ready code with modern tooling.
Quick Navigation: Level 1: Quick Start (5 min) → Level 2: Implementation (30 min) → Level 3: Mastery (Extended)
/**
* User authentication module with secure session handling.
* @module auth
*/
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
/**
* Authenticate user with username and password.
*
* @param {string} username - User's username
* @param {string} password - Plain text password
* @returns {Promise<{success: boolean, token?: string, error?: string}>}
* @throws {ValidationError} If credentials are invalid
*
* @example
* const result = await authenticateUser('alice', 'secret123');
* if (result.success) {
* console.log('Token:', result.token);
* }
*/
export const authenticateUser = async (username, password) => {
// Validate inputs
if (!username || !password) {
throw new ValidationError('Username and password required');
}
try {
// Fetch user from database
const user = await fetchUserFromDb(username);
if (!user) {
return { success: false, error: 'Invalid credentials' };
}
// Verify password
const isValid = await bcrypt.compare(password, user.passwordHash);
if (!isValid) {
return { success: false, error: 'Invalid credentials' };
}
// Generate JWT token
const token = jwt.sign(
{ userId: user.id, username: user.username },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
return { success: true, token };
} catch (error) {
console.error('Authentication error:', error);
throw new AuthenticationError('Authentication failed', error);
}
};
/**
* Custom validation error.
*/
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
/**
* Custom authentication error.
*/
class AuthenticationError extends Error {
constructor(message, cause) {
super(message);
this.name = 'AuthenticationError';
this.cause = cause;
}
}
Airbnb Style Guide + Automation
// ✅ Good: Modern ES6+ syntax
const calculateDiscount = (user, purchaseAmount, promoCode = null) => {
const baseDiscount = getLoyaltyDiscount(user);
const promoDiscount = promoCode ? validatePromoCode(promoCode) : 0;
return Math.min(baseDiscount + promoDiscount, 0.5);
};
// ❌ Bad: Old-style function, var, no default param
function calculateDiscount(user, purchaseAmount, promoCode) {
var baseDiscount = getLoyaltyDiscount(user);
var promoDiscount = promoCode ? validatePromoCode(promoCode) : 0;
return Math.min(baseDiscount + promoDiscount, 0.5);
}
Tool Configuration (see resources/configs/.eslintrc.json):
{
"extends": ["airbnb-base", "prettier"],
"env": {
"node": true,
"es2022": true,
"jest": true
},
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module"
},
"rules": {
"no-console": ["warn", { "allow": ["warn", "error"] }],
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"prefer-const": "error",
"no-var": "error",
"arrow-body-style": ["error", "as-needed"]
}
}
Common Patterns:
// Destructuring
const { name, email } = user;
const [first, ...rest] = items;
// Spread operator
const newUser = { ...user, updatedAt: Date.now() };
const combined = [...array1, ...array2];
// Template literals
const message = `Welcome, ${user.name}!`;
const multiline = `
Line 1
Line 2
`;
// Optional chaining
const city = user?.address?.city;
// Nullish coalescing
const displayName = user.name ?? 'Guest';
// Object shorthand
const createUser = (name, email) => ({ name, email });
Anti-Patterns:
// ❌ Mutating arrays (use immutable methods)
const items = [1, 2, 3];
items.push(4); // Mutation!
// ✅ Immutable approach
const newItems = [...items, 4];
// ❌ Using == instead of ===
if (value == '42') { }
// ✅ Strict equality
if (value === '42') { }
// ❌ Not handling promise rejections
fetch('/api/data'); // Unhandled promise
// ✅ Proper error handling
fetch('/api/data')
.then(response => response.json())
.catch(error => console.error('Fetch failed:', error));
See Also: Advanced Patterns for generators, proxies, decorators
ES6+ Features:
// Classes with private fields
class UserService {
#apiKey; // Private field
constructor(apiKey) {
this.#apiKey = apiKey;
}
async fetchUser(userId) {
const response = await fetch(`/api/users/${userId}`, {
headers: { 'X-API-Key': this.#apiKey }
});
return response.json();
}
}
// Async/Await with error handling
const processData = async (data) => {
try {
const validated = await validateData(data);
const transformed = await transformData(validated);
return await saveData(transformed);
} catch (error) {
console.error('Processing failed:', error);
throw new ProcessingError('Data processing failed', error);
}
};
// Promises with Promise.all/allSettled
const fetchAllUsers = async (userIds) => {
const promises = userIds.map(id => fetchUser(id));
// Fail fast if any request fails
const users = await Promise.all(promises);
// Or continue even if some fail
const results = await Promise.allSettled(promises);
return results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
};
// Generators for lazy evaluation
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// Symbols for unique keys
const SECRET_KEY = Symbol('secretKey');
const user = {
name: 'Alice',
[SECRET_KEY]: 'hidden value'
};
Jest Best Practices:
// users.test.js
/**
* Tests for user service module.
*/
import { describe, test, expect, beforeEach, jest } from '@jest/globals';
import { authenticateUser } from './users.js';
import * as db from './database.js';
// Mock database module
jest.mock('./database.js');
describe('authenticateUser', () => {
beforeEach(() => {
jest.clearAllMocks();
});
test('should authenticate valid credentials', async () => {
// Arrange
const mockUser = {
id: 1,
username: 'testuser',
passwordHash: '$2b$10$...' // bcrypt hash
};
db.fetchUserFromDb.mockResolvedValue(mockUser);
// Act
const result = await authenticateUser('testuser', 'correct_password');
// Assert
expect(result.success).toBe(true);
expect(result.token).toBeDefined();
expect(db.fetchUserFromDb).toHaveBeenCalledWith('testuser');
});
test('should reject invalid password', async () => {
// Arrange
const mockUser = {
id: 1,
username: 'testuser',
passwordHash: '$2b$10$...'
};
db.fetchUserFromDb.mockResolvedValue(mockUser);
// Act
const result = await authenticateUser('testuser', 'wrong_password');
// Assert
expect(result.success).toBe(false);
expect(result.error).toBe('Invalid credentials');
expect(result.token).toBeUndefined();
});
test('should throw error for empty credentials', async () => {
// Assert
await expect(authenticateUser('', 'password'))
.rejects
.toThrow('Username and password required');
});
test.each([
['user1', 'pass1', true],
['user2', 'wrong', false],
['', 'pass', false],
])('should handle %s with %s', async (username, password, expected) => {
const result = await authenticateUser(username, password);
expect(result.success).toBe(expected);
});
});
Test Organization:
tests/
├── unit/ # Unit tests (fast, isolated)
│ ├── utils.test.js
│ └── models.test.js
├── integration/ # Integration tests (DB, API)
│ ├── api.test.js
│ └── database.test.js
└── e2e/ # End-to-end tests (full flow)
└── user-flows.test.js
Coverage Configuration:
// jest.config.json
{
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
},
"collectCoverageFrom": [
"src/**/*.js",
"!src/**/*.test.js",
"!src/**/index.js"
]
}
See Also: Test Template
Modern React Patterns:
// Functional components with hooks
import React, { useState, useEffect, useCallback, useMemo } from 'react';
/**
* User profile component with data fetching.
* @param {Object} props
* @param {string} props.userId - User ID to display
*/
export const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// Memoized fetch function
const fetchUser = useCallback(async () => {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('Failed to fetch user');
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [userId]);
// Effect for data fetching
useEffect(() => {
fetchUser();
}, [fetchUser]);
// Memoized computed value
const displayName = useMemo(() => {
return user ? `${user.firstName} ${user.lastName}` : 'Unknown';
}, [user]);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
if (!user) return null;
return (
<div className="user-profile">
<h2>{displayName}</h2>
<p>{user.email}</p>
</div>
);
};
// Custom hooks for reusable logic
export const useUser = (userId) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
const fetchUser = async () => {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!cancelled) {
setUser(data);
setLoading(false);
}
} catch (error) {
if (!cancelled) {
setLoading(false);
}
}
};
fetchUser();
return () => {
cancelled = true; // Cleanup
};
}, [userId]);
return { user, loading };
};
Error Classes and Patterns:
// Custom error hierarchy
class AppError extends Error {
constructor(message, code, details = {}) {
super(message);
this.name = 'AppError';
this.code = code;
this.details = details;
this.timestamp = new Date().toISOString();
}
}
class ValidationError extends AppError {
constructor(field, message) {
super(
`Validation failed for ${field}: ${message}`,
'VALIDATION_ERROR',
{ field }
);
this.name = 'ValidationError';
}
}
class AuthenticationError extends AppError {
constructor(reason = 'Invalid credentials') {
super(reason, 'AUTH_ERROR', {
timestamp: Date.now()
});
this.name = 'AuthenticationError';
}
}
// Error handling middleware (Express)
export const errorHandler = (err, req, res, next) => {
// Log error
console.error('Error:', {
message: err.message,
code: err.code,
stack: err.stack,
timestamp: new Date().toISOString()
});
// Send appropriate response
if (err instanceof ValidationError) {
return res.status(400).json({
error: 'Validation Error',
message: err.message,
field: err.details.field
});
}
if (err instanceof AuthenticationError) {
return res.status(401).json({
error: 'Authentication Failed',
message: err.message
});
}
// Generic error
res.status(500).json({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'production'
? 'An error occurred'
: err.message
});
};
// Try/catch with proper cleanup
const processFile = async (filePath) => {
const fileHandle = await fs.open(filePath, 'r');
try {
const content = await fileHandle.readFile('utf8');
return JSON.parse(content);
} catch (error) {
throw new ProcessingError('Failed to process file', error);
} finally {
await fileHandle.close(); // Always cleanup
}
};
Modern Async/Await Patterns:
// Parallel execution with Promise.all
const fetchUserData = async (userId) => {
const [profile, orders, preferences] = await Promise.all([
fetchProfile(userId),
fetchOrders(userId),
fetchPreferences(userId)
]);
return { profile, orders, preferences };
};
// Sequential execution with proper error handling
const updateUserProfile = async (userId, updates) => {
try {
const user = await fetchUser(userId);
const validated = await validateUpdates(updates);
const updated = await saveUser(userId, validated);
await invalidateCache(userId);
await sendNotification(userId, 'Profile updated');
return updated;
} catch (error) {
await rollbackChanges(userId);
throw error;
}
};
// Retry logic with exponential backoff
const fetchWithRetry = async (url, maxRetries = 3) => {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fetch(url);
} catch (error) {
if (attempt === maxRetries - 1) throw error;
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
};
// Concurrent with limit
const processBatch = async (items, concurrency = 5) => {
const results = [];
for (let i = 0; i < items.length; i += concurrency) {
const batch = items.slice(i, i + concurrency);
const batchResults = await Promise.all(
batch.map(item => processItem(item))
);
results.push(...batchResults);
}
return results;
};
Input Validation:
// Using validator.js
import validator from 'validator';
const validateUserInput = (data) => {
const errors = {};
// Email validation
if (!data.email || !validator.isEmail(data.email)) {
errors.email = 'Valid email required';
}
// Password strength
if (!data.password || !validator.isStrongPassword(data.password, {
minLength: 8,
minLowercase: 1,
minUppercase: 1,
minNumbers: 1,
minSymbols: 1
})) {
errors.password = 'Password must be at least 8 characters with uppercase, lowercase, number, and symbol';
}
// Username sanitization
if (data.username) {
data.username = validator.escape(data.username);
}
if (Object.keys(errors).length > 0) {
throw new ValidationError('Input validation failed', errors);
}
return data;
};
// SQL injection prevention (parameterized queries)
const getUserByEmail = async (email) => {
// ✅ Good: Parameterized query
const query = 'SELECT * FROM users WHERE email = ?';
return await db.query(query, [email]);
// ❌ Bad: String concatenation (SQL injection!)
// const query = `SELECT * FROM users WHERE email = '${email}'`;
};
// XSS prevention
import DOMPurify from 'isomorphic-dompurify';
const sanitizeHTML = (html) => {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
ALLOWED_ATTR: []
});
};
Secrets Management:
// ❌ Never commit secrets
// const API_KEY = "sk-1234567890abcdef";
// ✅ Use environment variables
import dotenv from 'dotenv';
dotenv.config();
const config = {
apiKey: process.env.API_KEY,
dbUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET
};
// Validate required env vars
const requiredEnvVars = ['API_KEY', 'DATABASE_URL', 'JWT_SECRET'];
for (const varName of requiredEnvVars) {
if (!process.env[varName]) {
throw new Error(`Missing required environment variable: ${varName}`);
}
}
NIST Control Tags:
// @nist ia-2 "User authentication"
// @nist ia-5.1 "Password-based authentication"
export const authenticateUser = async (username, password) => {
// Implementation
};
// @nist ac-3 "Access enforcement"
// @nist ac-6 "Least privilege"
export const checkPermissions = (user, resource, action) => {
// Implementation
};
// @nist sc-8 "Transmission confidentiality"
// @nist sc-13 "Cryptographic protection"
export const encryptData = (data) => {
// Implementation
};
See Also: NIST Implementation Guide
# Setup
npm init -y
npm install --save-dev eslint prettier jest
# Linting & formatting
npx eslint --fix src/
npx prettier --write src/
# Testing
npm test
npm test -- --coverage
npm test -- --watch
# Build & run
npm run build
npm start
# Security audit
npm audit
npm audit fix
// TODO: Add basic example for javascript
// This example demonstrates core functionality
// TODO: Add advanced example for javascript
// This example shows production-ready patterns
// TODO: Add integration example showing how javascript
// works with other systems and services
See examples/javascript/ for complete working examples.
This skill integrates with:
Problem: Not testing edge cases and error conditions leads to production bugs
Solution: Implement comprehensive test coverage including:
Prevention: Enforce minimum code coverage (80%+) in CI/CD pipeline
Problem: Hardcoding values makes applications inflexible and environment-dependent
Solution: Use environment variables and configuration management:
Prevention: Use tools like dotenv, config validators, and secret scanners
Problem: Security vulnerabilities from not following established security patterns
Solution: Follow security guidelines:
Prevention: Use security linters, SAST tools, and regular dependency updates
Best Practices:
This skill has been validated with:
npx skills add williamzujkowski/javascript-coding-standards下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer