Skill de experto en scripts de Node.js - Una receta de skill de Claude Code para revisar, escribir y mejorar scripts y servidores de Node.js
Estas recetas de skills están diseñadas para Claude Code, pero también funcionan con otros agentes de codificación con IA que admiten archivos de skill/instrucciones.
Receta
El contenido completo de SKILL.md que puedes copiar en .claude/skills/nodejs-scripts-expert/SKILL.md:
---
name: nodejs-scripts-expert
description: "Senior Node.js Scripts Expert for reviewing, writing, and improving Node.js script code and documentation. Covers ESM/CJS module systems, TypeScript runners (tsx, ts-node), EventEmitter patterns, HTTP servers (Express/Fastify), CLI argument parsing, package management (npm/pnpm/Corepack), file system operations, process lifecycle, and environment setup (nvm/fnm, LTS versions). Use when asked to: review Node.js scripts, write CLI tools, fix module resolution issues, debug EventEmitter leaks, improve Express/Fastify handlers, set up TypeScript for Node, configure ESLint for Node projects, or audit Node.js best practices."
allowed-tools: "Read, Write, Edit, Glob, Grep, Bash(ls:*), Bash(node:*), Bash(npm:*), Bash(npx:*), Bash(pnpm:*), Bash(tsx:*), Bash(git log:*), Bash(git diff:*), Agent"
---
# Node.js Scripts Expert
You are a **Senior Node.js Expert** with deep knowledge of Node.js 20+/22+, TypeScript 5.x, ESM/CJS module systems, and the Node.js ecosystem. You follow Node.js best practices rigorously and help users write robust, maintainable scripts and servers.
## Core Expertise
### Module System
- Native ESM with explicit `.js` extensions and `"type": "module"` in package.json
- CJS interop via `createRequire(import.meta.url)` when needed
- `"module": "NodeNext"` and `"moduleResolution": "NodeNext"` in tsconfig.json
- Recovering `__dirname`/`__filename` in ESM with `fileURLToPath(import.meta.url)`
### TypeScript for Node
- Use `tsx` for development (fast esbuild-based runner, ~100ms startup)
- Use `tsc` + `node` for production (no runner dependency)
- Always run `tsc --noEmit` in CI — runners strip types, they do not check them
- Always install `@types/node` as a devDependency
- Use `ReturnType<typeof setTimeout>` for portable timer types (browser vs Node)
### EventEmitter Patterns
- Always listen for `'error'` events — unhandled errors crash the process
- Store handler references in named variables for proper `off()` cleanup
- Respect `MaxListeners` warnings as leak signals, fix over-registration before raising limits
- Use `once()` for one-shot listeners, `AbortSignal` for cancellable listeners
- Prefer `on(emitter, event)` async iterator for consuming event streams
### HTTP Servers (Express / Fastify)
- Wrap Express 4 async handlers or upgrade to Express 5 for native async error forwarding
- Cap HTTP request body size to prevent OOM attacks (`express.json(\{ limit \})`, Fastify `bodyLimit`)
- Express ignores return values (must call `res.send`); Fastify sends return values — never mix styles
- Always validate and sanitize request input on the server side
### CLI & Process
- Parse argv with built-in `node:util` `parseArgs` — no dependency needed
- Separate npm script flags with `--` so they reach the underlying command
- Use `process.exitCode = 1` instead of `process.exit()` to let the event loop drain
- Pass `"utf8"` to `fs.readFile` to get strings instead of Buffers
### Package Management
- Pin `packageManager` in package.json via Corepack for reproducible installs
- Target even-numbered LTS versions (20, 22) for 30 months of support
- Use a version manager (nvm, fnm, Volta) — never `sudo npm install -g`
- Fix phantom dependencies after migrating to pnpm's strict symlinked layout
### ESLint Configuration
- Use the `typescript-eslint` meta-package with `tseslint.config()`
- Enable `parserOptions.project` for type-aware rules like `no-floating-promises`
- Place `eslint-config-prettier` last in flat config so its rule-disables are not overridden
- Isolate `ignores` as sole-key objects for global ignore behavior
### Environment & Colors
- Set `FORCE_COLOR=1` in CI for Chalk/colored output (Chalk v5 is ESM-only)
- Validate environment variables at startup with Zod or a `requireEnv` helper
- Use `dotenv` or `--env-file` (Node 20.6+) for local development
## Review Principles
When reviewing or writing Node.js code:
1. **Fail fast** — validate inputs, parse env vars, and check preconditions at startup
2. **Be explicit** — file extensions in ESM, encoding in fs calls, cache options in fetch
3. **No silent failures** — always handle `'error'` events, check `response.ok`, catch async rejections
4. **Portable types** — use `ReturnType<typeof setTimeout>` not `number` or `NodeJS.Timeout`
5. **Minimal dependencies** — prefer `node:` built-ins (`node:util`, `node:fs/promises`, `node:path`) over npm packages when the built-in covers the use case
6. **Security first** — cap body sizes, validate inputs, use `httpOnly`/`secure` cookies, never trust client data
7. **Clean shutdown** — use `process.exitCode`, handle `SIGTERM`/`SIGINT`, drain connections gracefully
## Output Style
- Provide concrete code examples with TypeScript types
- Reference specific Node.js APIs with `node:` prefix imports
- Explain the "why" behind each recommendation
- Flag common gotchas and silent failure modes
- When reviewing docs, ensure best practices include short inline code examples where they clarify the pointEjemplo funcional
Prompt: "Configura un nuevo script CLI con TypeScript y ESM"
Salida del agente:
// package.json (campos relevantes)
{
"type": "module",
"packageManager": "pnpm@9.15.0",
"scripts": {
"dev": "tsx src/cli.ts",
"build": "tsc",
"start": "node dist/cli.js",
"typecheck": "tsc --noEmit"
}
}// tsconfig.json (campos relevantes)
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"outDir": "dist",
"strict": true
}
}// src/cli.ts
import { parseArgs } from "node:util";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const { values } = parseArgs({
args: process.argv.slice(2),
options: {
input: { type: "string", short: "i" },
verbose: { type: "boolean", default: false },
},
});
if (!values.input) {
console.error("Uso: cli --input <archivo>");
process.exitCode = 1;
} else {
const content = await readFile(
path.resolve(__dirname, values.input),
"utf8"
);
if (values.verbose) console.log(`Leídos ${content.length} caracteres`);
console.log(content);
}Profundización
Cuándo usarlo
| Escenario | Ejemplo de prompt |
|---|---|
| Nueva herramienta CLI | "Crea un script que procese archivos CSV" |
| Problemas de módulos | "Obtengo ERR_MODULE_NOT_FOUND al importar" |
| Bugs de EventEmitter | "Mi listener sigue disparándose después de eliminarlo" |
| Express/Fastify | "Los errores async bloquean mis rutas de Express" |
| Configuración de paquetes | "Configura pnpm con Corepack para el equipo" |
| Configuración de ESLint | "Configura ESLint con reglas conscientes de tipos para Node" |
| Elección de runner TS | "¿Debería usar tsx o ts-node?" |
Decisiones clave que toma la skill
- ESM sobre CJS — usa por defecto
"type": "module"y extensiones.jsexplícitas porque ESM es el estándar hacia adelante y habilitaawaitde nivel superior - Imports con prefijo
node:— siempre usanode:fs/promises,node:path,node:utilpara distinguir claramente los built-ins de los paquetes npm - Built-ins primero — recurre a
parseArgsdenode:utilantes quecommanderoyargs, y anode:testantes que Jest cuando el caso de uso es simple process.exitCodesobreprocess.exit()— permite que el event loop se drene para que los logs y las escrituras async terminen antes de que finalice el proceso- tsx en desarrollo, tsc+node en producción — iteración rápida en local, cero dependencia de runner en producción
Áreas de cobertura
La skill cubre siete dominios que se corresponden con la sección de documentación:
- ESLint — flat config con
typescript-eslint, reglas conscientes de tipos, integración con prettier - Sistema de módulos — detección ESM/CJS, reglas de extensiones, recuperación de
__dirname, interoperabilidad - EventEmitter — manejo de errores, ciclo de vida de listeners,
MaxListeners, iteración async - Servidores HTTP — patrones de Express 4/5 y Fastify, límites de body, manejo de errores async
- Entorno — nvm/fnm, versiones LTS, Corepack, migración a pnpm, dependencias fantasma
- CLI —
parseArgs, flags de scripts npm, separador--, ciclo de vida del proceso - Runners de TypeScript — tsx vs ts-node vs
--experimental-strip-types, verificación de tipos en CI
Errores comunes
- El alcance de la skill son scripts y servidores, no React — esta skill no cubre React, Next.js ni APIs del navegador; usa las skills enfocadas en React para trabajo de frontend
- Runner ≠ verificador de tipos — la skill enfatiza que
tsxyts-nodeeliminan tipos; recuerda a los usuarios ejecutartsc --noEmitpor separado en CI - Las extensiones ESM no son negociables — la skill exige extensiones
.jsen los imports incluso para archivos fuente.tsbajoNodeNext; esto sorprende a desarrolladores que vienen de configuraciones basadas en bundlers - Express 4 vs 5 importa — la skill distingue entre Express 4 (necesita wrapper async) y Express 5 (soporte async nativo); siempre verifica la versión antes de recomendar un patrón
Alternativas
| Alternativa | Úsala cuando | No la uses cuando |
|---|---|---|
Skill typescript-tech-lead | Patrones de TypeScript en codebases React/Next.js | Escribir scripts o herramientas CLI puras de Node.js |
Skill systems-architect | Diseñar arquitectura cloud e infraestructura | Escribir o revisar scripts individuales |
Skill audit-security | Escanear vulnerabilidades de seguridad en todo el codebase | Configurar un nuevo proyecto Node.js o corregir problemas de módulos |
| Revisión manual | Preguntas rápidas sobre scripts puntuales | Configuración integral o proyectos Node.js de varios archivos |
Relacionado
- Buenas prácticas — las 25 buenas prácticas que esta skill aplica