ESLint para scripts de Node.js
Configura ESLint con flat config (eslint.config.js) para proyectos de scripts de Node.js con soporte de TypeScript, reglas específicas de Node.js y linting con conciencia de tipos.
Receta
Tarjeta de receta de referencia rápida — lista para copiar y pegar.
# Inicializa una nueva configuración de ESLint de forma interactiva
npm init @eslint/config@latest
# Instala el conjunto de herramientas central para un proyecto de script de Node.js con TypeScript
npm install --save-dev \
eslint \
typescript-eslint \
eslint-plugin-n \
globals
# Lint en tus scripts
npx eslint .
# Lint y auto-corregir
npx eslint . --fixCuándo usarlo: Cualquier proyecto de Node.js independiente — CLIs, scripts de construcción, automatización, o paquetes de herramientas de monorepo — que vive fuera de una aplicación Next.js/React.
Ejemplo Funcional
// eslint.config.js
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import nodePlugin from "eslint-plugin-n";
import globals from "globals";
export default tseslint.config(
// Los ignores globales — deben ser la única clave en su propio objeto
{
ignores: ["dist/", "build/", "coverage/", "node_modules/"],
},
// Reglas de JS base
js.configs.recommended,
// Reglas recomendadas del plugin de Node.js (preset de flat config)
nodePlugin.configs["flat/recommended-script"],
// Reglas de TypeScript con verificación de tipos
...tseslint.configs.strictTypeChecked,
...tseslint.configs.stylisticTypeChecked,
{
files: ["**/*.ts", "**/*.mts"],
languageOptions: {
globals: globals.nodeBuiltin,
parserOptions: {
project: "./tsconfig.json",
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
// Aplica async/await sobre cadenas de promesas sin procesar
"promise/prefer-await-to-then": "off",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/return-await": ["error", "always"],
// Reglas del plugin de Node.js
"n/no-missing-import": "off", // typescript-eslint resuelve importaciones
"n/no-unpublished-import": "off",
"n/no-process-exit": "warn",
},
},
);Lo que esto demuestra:
- Flat config con el helper
tseslint.config()para la autoría de configuración con seguridad de tipos strictTypeChecked+stylisticTypeCheckedpara las reglas de TS más rigurosaseslint-plugin-n(el fork mantenido deeslint-plugin-node) que proporciona reglas específicas de Node.js- Linting con conciencia de tipos habilitado vía
parserOptions.project - Ignores globales en un objeto dedicado (requerido por flat config)
Inmersión Profunda
Cómo Funciona
- ESLint 9+ carga
eslint.config.js(flat config) desde la raíz del proyecto de forma predeterminada — no más archivos.eslintrcen cascada. typescript-eslintse envía como un paquete único que re-exporta@typescript-eslint/parser,@typescript-eslint/eslint-plugin, y un helperconfig()que concatena y tipifica objetos de configuración.eslint-plugin-nreemplaza el abandonadoeslint-plugin-node. Agrega reglas específicas de Node.js comon/no-missing-import,n/no-unpublished-bin, yn/no-deprecated-api.- Las reglas con conciencia de tipos (cualquier cosa con sufijo
TypeChecked) requieren que el parser carguetsconfig.jsonvíaparserOptions.project. Sin él, esas reglas no hacen nada silenciosamente. tsconfigRootDir: import.meta.dirnameasegura que la rutaprojectse resuelva relativa al archivo de configuración, no al directorio de trabajo actual.
Variaciones
Configuración solo JavaScript (sin TypeScript):
// eslint.config.js
import js from "@eslint/js";
import nodePlugin from "eslint-plugin-n";
import globals from "globals";
export default [
js.configs.recommended,
nodePlugin.configs["flat/recommended-script"],
{
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: globals.nodeBuiltin,
},
},
];Linting con conciencia de tipos con un tsconfig dedicado:
{
files: ["**/*.ts"],
languageOptions: {
parserOptions: {
project: "./tsconfig.eslint.json",
tsconfigRootDir: import.meta.dirname,
},
},
}Integración con Prettier (desactiva reglas de estilo que entran en conflicto):
npm install --save-dev eslint-config-prettierimport prettier from "eslint-config-prettier";
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
prettier, // debe ser ÚLTIMO — desactiva reglas de estilo conflictivas
);Ignores con alcance para archivos generados:
{
ignores: ["**/*.generated.ts", "src/proto/**"],
}Notas sobre TypeScript
@typescript-eslint/parserse instala transitivamente a través del meta-paquetetypescript-eslint— raramente necesitas importarlo directamente.- Las reglas con conciencia de tipos como
no-floating-promisesyno-misused-promisesson esenciales para scripts de Node.js donde los rechazos silenciosos sin manejar pueden corromper el estado. - Habilita el preset más riguroso con
...tseslint.configs.strictTypeChecked— atrapa errores reales comono-unnecessary-conditionyno-unsafe-argument. - El helper
tseslint.config()proporciona autocompletado y errores de tipo si escribes mal un nombre de regla u opción.
Gotchas
Cosas que te morderán. Cada gotcha incluye qué sale mal, por qué sucede, y la solución.
-
Confusión entre flat config y
.eslintrcheredado — ESLint 9+ usa flat config de forma predeterminada. Si aún tienes un.eslintrc.jsonen el proyecto, ESLint lo ignora silenciosamente una vez queeslint.config.jsexiste. Solución: Elimina todos los archivos heredados cuando migres y verifica connpx eslint --print-config path/to/file.ts. -
Falta
parserOptions.projectdesactiva reglas con conciencia de tipos — Las reglas destrictTypeCheckedostylisticTypeCheckedrequieren que se cargue el programa de TypeScript. Sinproject, lanzan en tiempo de ejecución o pasan silenciosamente. Solución: Siempre estableceparserOptions.projectytsconfigRootDiren el bloque de archivos de TS. -
eslint-plugin-nreporta falsos positivos para alias de ruta de TS — Las reglas comon/no-missing-importno pueden resolver alias de ruta@/utilsdefinidos entsconfig.json. Solución: Desactivan/no-missing-import(yn/no-unpublished-import) cuando uses TypeScript — typescript-eslint ya valida importaciones. -
Archivo de configuración ESM en un proyecto CommonJS — Si
package.jsontiene"type": "commonjs"(o ningún campotype),eslint.config.jscon sintaxisimportfalla al cargar. Solución: Cambia el nombre aeslint.config.mjsO agrega"type": "module"apackage.json. -
Conflictos entre reglas de Prettier/ESLint — Habilitar reglas de ESLint de estilo junto con Prettier produce auto-correcciones en conflicto. Solución: Agrega
eslint-config-prettiercomo el último elemento en el array de configuración para desactivar reglas conflictivas. -
ignoresmezclado con otras claves silenciosamente se convierte en un filtro de archivo — En flat config, un objeto con tantoignorescomorulesse trata como un filtro, no como un ignore global. Solución: Coloca los ignores globales en su propio objeto{ ignores: [...] }sin otras claves.
Alternativas
Otras formas de resolver el mismo problema — y cuándo cada una es la mejor opción.
| Alternativa | Úsalo Cuando | No lo Uses Cuando |
|---|---|---|
| Biome | Quieres una herramienta única basada en Rust rápida para lint + format | Necesitas el ecosistema completo de plugins de ESLint o reglas personalizadas |
| oxlint | Quieres velocidad extrema y estás bien con un subconjunto de reglas | Dependes de reglas con conciencia de tipos (aún no soportadas) |
| Deno lint | Estás ejecutando scripts en Deno, no en Node | Apuntas a Node.js |
standard | Quieres cero configuración, valores predeterminados opinados | Necesitas personalizar cualquier regla |
FAQs
¿Qué reemplazó a eslint-plugin-node?
eslint-plugin-nodeno se mantiene.eslint-plugin-nes el fork mantenido por la comunidad con soporte de flat config.- Proporciona las mismas reglas bajo el prefijo
n/(p. ej.n/no-missing-import).
¿Necesito @typescript-eslint/parser como una instalación separada?
- No. El meta-paquete
typescript-eslintagrupa el parser y el plugin. - Instala
typescript-eslinty usatseslint.config()— cablean ambos juntos.
¿Cómo habilito linting con conciencia de tipos?
- Establece
parserOptions.projecten la ruta de tutsconfig.json. - Establece
parserOptions.tsconfigRootDirenimport.meta.dirname. - Extiende
...tseslint.configs.strictTypeChecked(orecommendedTypeChecked). - Sin
project, las reglas con conciencia de tipos lanzan un error o pasan silenciosamente.
¿Por qué las reglas de strictTypeChecked son mejores para scripts de Node.js?
- Los scripts a menudo tratan con I/O de archivos y procesos secundarios donde los fallos silenciosos corrompen datos.
no-floating-promisesatrapa llamadasawaitolvidadas.no-misused-promisesevita pasar funciones asincrónicas donde se esperan callbacks sincronos.no-unsafe-argumentatrapaanyfiltrándose desde dependencias sin tipo.
Gotcha: ¿Por qué no se carga mi eslint.config.js?
- Verifica
package.jsonpara"type". Si escommonjso falta, cambia el nombre de la configuración aeslint.config.mjsu agrega"type": "module". - ESLint 9+ requiere flat config de forma predeterminada — los archivos
.eslintrcheredados se ignoran cuando existe una flat config. - Ejecuta
npx eslint --print-config somefile.tspara ver qué configuración se carga realmente.
Gotcha: ¿Por qué eslint-plugin-n se queja de mis alias de ruta?
n/no-missing-importno puede resolver alias de ruta de TypeScript como@/lib/foo.- Desactiva
n/no-missing-importyn/no-unpublished-importen proyectos de TypeScript. typescript-eslintya verifica importaciones a través del compilador de TypeScript.
TypeScript: ¿Cómo tipificó mi archivo eslint.config.js?
- Usa el helper
tseslint.config()— proporciona autocompletado completo y errores de tipo. - Alternativamente, agrega
// @ts-checkcon JSDoc@type \{import("eslint").Linter.Config[]\}. - ESLint 9.18+ también soporta
eslint.config.tsde forma nativa.
TypeScript: ¿Cuál es la diferencia entre recommendedTypeChecked y strictTypeChecked?
recommendedTypeCheckedes la línea base segura — atrapa errores sin ser demasiado estricto.strictTypeCheckedagrega reglas más estrictas comono-unnecessary-conditionyprefer-reduce-type-parameter.- Para nuevos proyectos, comienza estricto. Para migraciones, comienza recomendado y aprieta después.
¿Cómo desactivo reglas solo para un directorio específico?
- Agrega un nuevo objeto de configuración con
files: ["scripts/**/*.ts"]y anula reglas dentro de él. - Flat config es sensible al orden: los objetos posteriores anulan los anteriores para archivos coincidentes.
¿Por qué poner Prettier al final del array de configuración?
eslint-config-prettiersolo desactiva reglas de estilo que entran en conflicto con Prettier.- Debe ejecutarse después de cualquier preset que habilite esas reglas, de lo contrario los presets las vuelven a habilitar.
- Siempre colócalo como el último elemento en el array.
¿Puedo ejecutar ESLint en archivos JavaScript y TypeScript en la misma configuración?
- Sí. Usa dos objetos de configuración: uno con
files: ["**/*.js"]y otro confiles: ["**/*.ts"]. - Aplica reglas con conciencia de tipos solo al bloque
.tspara evitar errores en archivos de JS sin tipo.
¿Cómo aplico async/await sobre cadenas de promesas sin procesar?
- Habilita
@typescript-eslint/no-floating-promisespara atrapar promesas sin manejar. - Habilita
@typescript-eslint/no-misused-promisespara desajustes de callback. - Habilita
@typescript-eslint/return-awaitestablecido en"always"para trazas de stack más limpias.
Relacionado
- Patrones de Script Utilitario — I/O de archivos, prompts, spinners, y salida coloreada
- Configuración de ESLint para Next.js — flat config para aplicaciones Next.js
- Plugins de ESLint — agrega plugins de React, a11y, import
- Conceptos Básicos de TypeScript — fundamentos de tsconfig