This skill provides JavaScript coding standards and is automatically loaded for JavaScript projects. It includes modern ES2025 patterns, async handling, and recommended tooling.
| Element | Convention | Example |
|---------|------------|---------|
| Variables/Functions | camelCase | getUserById, isActive |
| Classes | PascalCase | UserService, ApiClient |
| Constants | UPPER_SNAKE_CASE | MAX_RETRY_COUNT |
| Private | Prefix with _ or # | _internalMethod, #privateField |
| Files | kebab-case or camelCase | user-service.js, userService.js |
| Event Handlers | Prefix with handle | handleClick, handleSubmit |
myproject/
├── src/
│ ├── index.js # Entry point
│ ├── config.js # Settings, env vars
│ ├── models/
│ │ └── user.js # Domain models
│ ├── services/
│ │ └── user-service.js # Business logic
│ ├── routes/
│ │ └── user-routes.js # API routes
│ └── utils/
│ └── helpers.js # Utility functions
├── tests/
│ ├── services/
│ │ └── user-service.test.js
│ └── setup.js
├── package.json
└── README.md
// math.js - Named exports
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// app.js - Import
import { add, multiply } from './math.js';
// Default export
export default class UserService {
// ...
}
// Import default
import UserService from './user-service.js';
package.json for ES Modules:
{
"type": "module",
"exports": {
".": "./src/index.js",
"./utils": "./src/utils/index.js"
}
}
// Always use async/await for async operations
async function fetchUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Fetch failed:', error);
throw error;
}
}
// Parallel execution with Promise.all
async function fetchMultipleUsers(userIds) {
const users = await Promise.all(
userIds.map(id => fetchUserData(id))
);
return users;
}
// Race with timeout
async function fetchWithTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
),
]);
}
// Get all results, including failures
async function fetchAllSettled(urls) {
const results = await Promise.allSettled(
urls.map(url => fetch(url).then(r => r.json()))
);
return results;
}
// Prefer const over let
const users = [];
// Use nullish coalescing and optional chaining
const name = user?.profile?.name ?? 'Anonymous';
// Prefer template literals
const message = `Hello, ${user.name}!`;
// Use destructuring
const { id, name, email } = user;
function processUser({ id, name }) { }
// Prefer array methods over loops
const activeUsers = users.filter(u => u.isActive);
const userNames = users.map(u => u.name);
const totalAge = users.reduce((sum, u) => sum + u.age, 0);
// Use Object.freeze for immutable constants
const CONFIG = Object.freeze({
apiUrl: 'https://api.example.com',
maxRetries: 3,
});
// Private class fields with #
class UserService {
#apiClient;
constructor(apiClient) {
this.#apiClient = apiClient;
}
async getUser(id) {
return this.#apiClient.get(`/users/${id}`);
}
}
import express from 'express';
const app = express();
app.use(express.json());
// Routes
app.get('/users', (req, res) => {
res.json([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
]);
});
app.post('/users', (req, res) => {
const { name } = req.body;
res.status(201).json({ id: 3, name });
});
app.get('/users/:id', (req, res) => {
const { id } = req.params;
res.json({ id: parseInt(id), name: 'User' });
});
// Error handling middleware (must be last)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal Server Error' });
});
app.listen(3000, () => {
console.log('Server running on :3000');
});
Middleware Pattern:
// Logging middleware
const logger = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
};
// Authentication middleware
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Missing token' });
}
// Verify token...
req.user = decodedUser;
next();
};
app.use(logger);
app.use('/api/protected', authenticateToken);
import Fastify from 'fastify';
const fastify = Fastify({ logger: true });
fastify.get('/users/:id', async (request, reply) => {
const { id } = request.params;
return { id: parseInt(id), name: 'User' };
});
fastify.post('/users', async (request, reply) => {
const { name } = request.body;
reply.code(201);
return { id: 1, name };
});
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err;
console.log(`Server listening at ${address}`);
});
// Custom error classes
class AppError extends Error {
constructor(message, statusCode = 500) {
super(message);
this.name = 'AppError';
this.statusCode = statusCode;
}
}
class NotFoundError extends AppError {
constructor(resource) {
super(`${resource} not found`, 404);
this.name = 'NotFoundError';
}
}
// Result pattern for explicit error handling
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.value);
} else {
console.error(result.error);
}
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { add, multiply } from './math.js';
describe('Math utils', () => {
it('should add numbers', () => {
expect(add(2, 3)).toBe(5);
});
it('should multiply numbers', () => {
expect(multiply(2, 3)).toBe(6);
});
});
// Integration test example
describe('API Endpoints', () => {
let server;
beforeEach(() => {
server = createServer();
});
afterEach(() => {
server.close();
});
it('GET /users returns user list', async () => {
const response = await fetch('http://localhost:3000/users');
const data = await response.json();
expect(response.ok).toBe(true);
expect(Array.isArray(data)).toBe(true);
});
});
// config.js
const config = {
development: {
port: 3000,
database: 'mongodb://localhost:27017/mydb',
logLevel: 'debug',
},
production: {
port: process.env.PORT || 8080,
database: process.env.DATABASE_URL,
logLevel: 'info',
},
};
const env = process.env.NODE_ENV || 'development';
export default config[env];
// BAD - redundant comment
// Get the user from database
const user = repository.getUser(userId);
// GOOD - self-explanatory code, no comment needed
const user = repository.getUser(userId);
// GOOD - comment explains WHY (not obvious)
// Rate limit: API allows max 1000 requests/min
await rateLimiter.acquire();
| Tool | Purpose |
|------|---------|
| pnpm or bun | Package manager (faster than npm) |
| eslint | Linting |
| prettier | Code formatting |
| vitest or jest | Testing framework |
| node --watch | Development with auto-reload |
| dotenv | Environment variable management |
{
"name": "@org/myapp",
"version": "1.0.0",
"type": "module",
"main": "src/index.js",
"exports": {
".": "./src/index.js",
"./utils": "./src/utils/index.js"
},
"engines": {
"node": ">=22.0.0"
},
"scripts": {
"dev": "node --watch src/index.js",
"start": "node src/index.js",
"test": "vitest",
"lint": "eslint ."
}
}
"type": "module" in package.jsonnpm audit regularlySearch 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