Linting
Reglas de ESLint y Prettier - formato consistente, flat config, integración con husky/lint-staged, CI/CDFrom its SKILL.md
npx -y skills add 14BryanEspinoza/agent-stack --skill lintingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.7 KB, ~3.7k tokens by cl100k_base, as published. Nobody here has run it
Linting — ESLint & Prettier
1. Filosofía
- Consistencia sobre preferencias — El linter y formatter eliminan discusiones de estilo. Lo importante es que el código sea uniforme, no qué estilo se elija.
- Formato automatizado — Prettier formatea solo, sin configuración por equipo. ESLint se enfoca en calidad de código, no en estilo.
- Flat config (ESLint 9+) — Desde ESLint v9, el formato legacy (
.eslintrc) está deprecado. Usareslint.config.jscon flat config. - Pre-commit hooks —
lint-staged+huskygarantizan que solo pase código linted y formateado. Sin atajos. - CI como cortafuegos — El CI también corre lint. Si pasa local pero falla en CI, hay un desajuste de configuración.
2. Versiones Mínimas
| Tecnología | Versión Mínima |
|---|---|
| Node.js | 22+ |
| ESLint | 9+ |
| Prettier | 3+ |
| @eslint/js | 9+ |
| typescript-eslint | 8+ |
| husky | 9+ |
| lint-staged | 15+ |
3. Instalación
# ESLint + Prettier + integración
pnpm add -D eslint prettier eslint-config-prettier
# Husky + lint-staged (pre-commit hooks)
pnpm add -D husky lint-staged
# TypeScript (opcional)
pnpm add -D typescript-eslint @eslint/js typescript
Inicializar Husky
pnpm exec husky init
# Crea .husky/pre-commit — luego agregar: pnpm exec lint-staged
4. ESLint — Flat Config (eslint.config.js)
ESLint 9+ usa flat config por defecto. No crear
.eslintrc.*.
Configuración básica (JavaScript/ESM)
// eslint.config.js
import js from "@eslint/js";
export default [
js.configs.recommended,
{
rules: {
"no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
"no-console": ["warn", { allow: ["warn", "error"] }],
"no-debugger": "error",
"no-duplicate-imports": "error",
"prefer-const": "error",
"no-var": "error",
eqeqeq: ["error", "always"],
curly: ["error", "all"],
"no-throw-literal": "error",
"prefer-template": "warn",
},
},
{
ignores: [
"dist/",
"build/",
"node_modules/",
".git/",
"coverage/",
"*.config.*",
],
},
];
TypeScript
// eslint.config.js
import js from "@eslint/js";
import tseslint from "typescript-eslint";
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
{
rules: {
"@typescript-eslint/no-unused-vars": [
"warn",
{ argsIgnorePattern: "^_" },
],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/consistent-type-imports": [
"error",
{ prefer: "type-imports" },
],
},
},
{
ignores: [
"dist/",
"build/",
"node_modules/",
".git/",
"coverage/",
"*.config.*",
"*.d.ts",
],
},
);
React + JSX
// eslint.config.js
import js from "@eslint/js";
import reactPlugin from "eslint-plugin-react";
import reactHooksPlugin from "eslint-plugin-react-hooks";
export default [
js.configs.recommended,
{
plugins: {
react: reactPlugin,
"react-hooks": reactHooksPlugin,
},
settings: {
react: { version: "detect" },
},
rules: {
...reactPlugin.configs.recommended.rules,
...reactHooksPlugin.configs.recommended.rules,
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off",
"react/prop-types": "off",
"react/jsx-no-target-blank": "error",
"react/self-closing-comp": "error",
},
},
{
ignores: ["dist/", "build/", "node_modules/", ".git/", "coverage/"],
},
];
Reglas recomendadas por categoría
Posibles errores
{
rules: {
"no-console": ["warn", { allow: ["warn", "error"] }],
"no-debugger": "error",
"no-duplicate-imports": "error",
"no-constant-binary-expression": "error",
"no-promise-executor-return": "error",
"no-self-compare": "error",
"no-template-curly-in-string": "error",
"no-unmodified-loop-condition": "error",
"no-unreachable-loop": "error",
"require-atomic-updates": "error",
},
}
Buenas prácticas
{
rules: {
"array-callback-return": "error",
"block-scoped-var": "error",
curly: ["error", "all"],
"default-case-last": "error",
eqeqeq: ["error", "always"],
"no-eval": "error",
"no-extend-native": "error",
"no-implied-eval": "error",
"no-iterator": "error",
"no-lone-blocks": "error",
"no-new-wrappers": "error",
"no-proto": "error",
"no-return-assign": "error",
"no-script-url": "error",
"no-throw-literal": "error",
"no-unused-expressions": "error",
"no-useless-concat": "error",
"prefer-regex-literals": "error",
"require-await": "warn",
yoda: "error",
},
}
Estilo
{
rules: {
"consistent-return": "error",
"no-lonely-if": "error",
"no-multi-assign": "error",
"no-nested-ternary": "error",
"no-unneeded-ternary": "error",
"one-var": ["error", "never"],
"prefer-const": "error",
"prefer-destructuring": "warn",
"prefer-object-spread": "error",
"prefer-template": "warn",
"sort-imports": ["warn", { ignoreDeclarationSort: true }],
},
}
5. Prettier
.prettierrc
{
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 80,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf",
"quoteProps": "as-needed"
}
Alternativa en eslint.config.js
import prettierConfig from "eslint-config-prettier";
export default [
// ... otras configs
prettierConfig, // Siempre al final para desactivar reglas conflictivas
];
Ignorar archivos (.prettierignore)
# .prettierignore
dist/
build/
node_modules/
.git/
coverage/
*.min.js
*.map
*.lock
*.svg
*.png
*.jpg
*.ico
CHANGELOG.md
Scripts en package.json
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check ."
}
}
6. Integración ESLint + Prettier
eslint-config-prettier (recomendado)
Desactiva todas las reglas de ESLint que entran en conflicto con Prettier. Se coloca al final del array en flat config.
// eslint.config.js
import js from "@eslint/js";
import prettierConfig from "eslint-config-prettier";
export default [
js.configs.recommended,
// ... otros plugins y reglas
prettierConfig, // último — desactiva reglas de estilo conflictivas
];
eslint-plugin-prettier (no recomendado)
Ejecuta Prettier como regla de ESLint. Tiene problemas de rendimiento y puede duplicar errores. Preferir eslint-config-prettier + ejecutar Prettier por separado.
// ❌ No recomendado
import eslintPluginPrettier from "eslint-plugin-prettier";
export default [
{
plugins: { prettier: eslintPluginPrettier },
rules: {
"prettier/prettier": "error",
},
},
];
Flujo recomendado
1. ESLint detecta errores de calidad (no estilo)
2. Prettier formatea automáticamente (estilo)
3. eslint-config-prettier evita conflictos entre ambos
4. lint-staged ejecuta ambos en pre-commit
7. Husky + lint-staged (Pre-commit Hooks)
Instalación
pnpm add -D husky lint-staged
pnpm exec husky init
Configurar pre-commit hook
# .husky/pre-commit
pnpm exec lint-staged
Configuración en package.json
{
"lint-staged": {
"*.{js,ts,tsx,mjs,cjs}": ["eslint --fix", "prettier --write"],
"*.{json,md,css,html,yaml,yml}": ["prettier --write"]
}
}
Sin husky (git hooks nativos)
# .git/hooks/pre-commit
#!/bin/sh
pnpm exec lint-staged
8. CI/CD
Pipeline básico
name: Lint & Format
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm format:check
Con múltiples versiones de Node
jobs:
lint:
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm lint
9. ESLint en Editores
VS Code (settings.json)
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"eslint.validate": [
"javascript",
"typescript",
"javascriptreact",
"typescriptreact"
]
}
Neovim / Vim (lazy.nvim)
{
"nvimtools/none-ls.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
opts = function()
local null_ls = require("null-ls")
return {
sources = {
null_ls.builtins.formatting.prettier,
null_ls.builtins.diagnostics.eslint,
null_ls.builtins.code_actions.eslint,
},
}
end,
}
10. Linting para Otros Archivos
Markdown
pnpm add -D eslint-plugin-markdown
// eslint.config.js
import markdownPlugin from "eslint-plugin-markdown";
export default [
...markdownPlugin.configs.recommended,
{
files: ["**/*.md"],
processor: "markdown/markdown",
},
];
JSON
pnpm add -D prettier-plugin-packagejson
{
"plugins": ["prettier-plugin-packagejson"]
}
CSS / SCSS
pnpm add -D stylelint stylelint-config-standard
// stylelint.config.js
export default {
extends: ["stylelint-config-standard"],
rules: {
"selector-class-pattern": null,
},
};
11. Troubleshooting
ESLint no encuentra la config
# Verificar que eslint.config.js existe en la raíz
ls eslint.config.js
# Verificar que no hay .eslintrc.* (legacy)
ls .eslintrc* 2>/dev/null || echo "No legacy config"
# ESLint 9+ ignora .eslintrc si existe eslint.config.js
rm .eslintrc* 2>/dev/null; echo "Legacy configs removed"
Conflicto entre ESLint y Prettier
# eslint-config-prettier desactiva reglas conflictivas
# Asegúrate de que prettierConfig está AL FINAL del array flat config
lint-staged no ejecuta
# Verificar hook
cat .husky/pre-commit
# Asegurar que husky está instalado
pnpm exec husky
# Ejecutar manualmente
pnpm exec lint-staged --verbose
ESLint lento
# Ignorar node_modules y dist
# Usar --cache
pnpm lint --cache
# En flat config, los ignores en objeto tienen más prioridad
Error: "ESLint couldn't find the config"
# Asegurar que eslint.config.js exporta un array
node -e "import('./eslint.config.js').then(m => console.log(Array.isArray(m.default)))"
12. Prohibiciones
- ❌ NO usar
.eslintrc.*legacy en proyectos nuevos (usar flat configeslint.config.js) - ❌ NO desactivar reglas sin comentario explicativo (
// eslint-disable-next-line reason) - ❌ NO committear
console.log— usarno-console: warny revisar antes de commit - ❌ NO usar
eslint-plugin-prettier— preferireslint-config-prettier+ Prettier separado - ❌ NO ignorar warnings de lint en PRs (configurar
max-warnings: 0en CI) - ❌ NO saltar hooks de pre-commit (
git commit --no-verifysolo en emergencias) - ❌ NO mezclar flat config con legacy config en el mismo proyecto
- ❌ NO usar
// eslint-disable-next-linesin especificar la regla exacta - ❌ NO tener reglas de estilo en ESLint que Prettier ya maneja
- ❌ NO poner archivos autogenerados en lint (ignorar
dist/,build/,.next/) - ❌ NO usar
@babel/eslint-parsersi usas TypeScript (usar@typescript-eslint/parser) - ❌ NO instalar ESLint globalmente (usar la versión local del proyecto)
13. Referencias
Nota: Para CI/CD, ver Deploy Nota: Para scripts en package.json, ver Package Manager Nota: Para control de versiones y hooks, ver Git Nota: Para JavaScript/TypeScript, ver JavaScript
Última actualización: 2026-07
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.