linters
- Testing
- Automatizacion y Build
- Sistemas
- Formatters
- prettier vs biome
- eslint
- oxidation
- husky
- Husky info web
- Husky
- pre comits+
- Biome
- Biome, toolchain of the web
-
[Curso Biome JS Prettier y Eslint todo en 1 - YouTube](https://youtu.be/ObInDSchTOM)
Linters y Herramientas de Calidad de Código
Introducción a Linters
Los linters son herramientas esenciales en el desarrollo moderno que analizan el código para identificar problemas potenciales, errores de sintaxis, y violaciones de convenciones de estilo.
ESLint
Configuración y Uso
ESLint es el linter más popular para JavaScript/TypeScript, altamente configurable y extensible.
Configuración básica:
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'eslint:recommended',
'@typescript-eslint/recommended'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
'no-unused-vars': 'error',
'prefer-const': 'warn',
'no-console': 'warn'
}
};
Scripts de package.json:
{
"scripts": {
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix"
}
}
Características Principales
- Plugins para frameworks específicos (React, Vue, Angular)
- Parsers para TypeScript y nuevas características de JavaScript
- Reglas configurables con diferentes niveles de severidad
- Integración con editores de código y sistemas de CI/CD
- Auto-fix para problemas corregibles automáticamente
Biome
Herramienta Unificada
Biome es una herramienta moderna que combina funcionalidades de linter y formateador en una sola herramienta escrita en Rust.
Configuración básica (biome.json):
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "single"
}
}
}
Ventajas de Biome
- Rendimiento extremadamente rápido gracias a estar escrito en Rust
- Baterías incluidas - no requiere configuración compleja
- Zero-config para empezar rápidamente
- Compatibilidad con reglas de ESLint y Prettier
- Menos dependencias en el proyecto
Formatters
Prettier vs Biome
Prettier:
// .prettierrc
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false
}
Comparación:
- Prettier: Especializado en formateo, ampliamente adoptado
- Biome: Enfoque unificado (linter + formatter), mejor rendimiento
- Configuración: Prettier requiere configuración separada, Biome es más integrado
Oxc (Oxidation Compiler)
Compilador JavaScript en Rust
Oxc es un conjunto de herramientas de alto rendimiento para JavaScript/TypeScript escrito en Rust.
Características:
- Parser ultra-rápido para JavaScript/TypeScript
- Linter con reglas personalizables
- Transformer para operaciones de código
- Minimizer para optimización de bundles
- Compatibilidad con el ecosistema JavaScript existente
Uso potencial:
// Integración futura con herramientas existentes
const { OxcCompiler } = require('@oxc/compiler');
Husky para Git Hooks
Automatización de Pre-commits
Husky permite ejecutar scripts automáticamente en hooks de Git, asegurando calidad antes de los commits.
Configuración:
// package.json
{
"scripts": {
"prepare": "husky install",
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,html,css,scss}": [
"prettier --write"
]
}
}
Setup:
# Instalación y configuración
npm install husky lint-staged --save-dev
npx husky init
npm run prepare
Flujo de Trabajo Integrado
Configuración Completa
Ejemplo de setup completo:
// package.json
{
"scripts": {
"dev": "next dev",
"build": "next build",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"prepare": "husky install"
},
"devDependencies": {
"eslint": "^8.0.0",
"prettier": "^3.0.0",
"husky": "^8.0.0",
"lint-staged": "^13.0.0"
}
}
Beneficios del Setup
- Calidad consistente del código en todo el equipo
- Detección temprana de problemas y errores
- Formato uniforme automáticamente
- Integración continua sin sorpresas
- Mejor experiencia de desarrollo
Estrategias de Implementación
Para Proyectos Nuevos
- Biome como herramienta unificada para máxima velocidad
- Husky para hooks de pre-commit automáticos
- Configuración zero-config cuando sea posible
Para Proyectos Existentes
- ESLint + Prettier para compatibilidad
- Migración gradual a herramientas más modernas
- Configuración incremental de reglas
Reglas Recomendadas
- Errores críticos: Bloqueantes para el build
- Advertencias: Para mejoras de código
- Auto-fix: Siempre que sea posible
- Exclusiones estratégicas: Para código legacy o casos específicos
Linters - Temas Avanzados y Configuraciones Especializadas
Configuraciones Específicas por Framework
React ESLint Configuration
// .eslintrc.react.js
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'eslint:recommended',
'@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended'
],
plugins: ['react', 'react-hooks', 'jsx-a11y'],
rules: {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
'jsx-a11y/anchor-is-valid': 'warn'
},
settings: {
react: {
version: 'detect'
}
}
};
Vue ESLint Setup
// .eslintrc.vue.js
module.exports = {
extends: [
'plugin:vue/vue3-essential',
'@vue/typescript/recommended'
],
rules: {
'vue/multi-word-component-names': 'warn',
'vue/no-unused-vars': 'error',
'vue/require-default-prop': 'error'
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020
}
};
Configuraciones para Entornos Específicos
Node.js/Backend Configuration
// .eslintrc.node.js
module.exports = {
env: {
node: true,
es2021: true
},
extends: [
'eslint:recommended',
'@typescript-eslint/recommended'
],
rules: {
'no-console': 'off',
'no-process-exit': 'error',
'global-require': 'warn',
'no-sync': 'warn',
'handle-callback-err': 'error'
},
overrides: [
{
files: ['**/*.test.js', '**/*.spec.js'],
env: { jest: true }
}
]
};
Configuración para Testing (Jest)
// .eslintrc.jest.js
module.exports = {
env: {
'jest/globals': true
},
plugins: ['jest'],
extends: ['plugin:jest/recommended'],
rules: {
'jest/no-disabled-tests': 'warn',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'warn',
'jest/valid-expect': 'error'
}
};
Reglas Personalizadas y Plugins
Custom ESLint Rules
// eslint-custom-rules.js
module.exports = {
rules: {
'no-relative-imports': {
create: function(context) {
return {
ImportDeclaration(node) {
if (node.source.value.startsWith('.')) {
context.report({
node,
message: 'Use absolute imports instead of relative imports'
});
}
}
};
}
}
}
};
Plugin de Seguridad
// eslint-security.config.js
module.exports = {
plugins: ['security'],
extends: ['plugin:security/recommended'],
rules: {
'security/detect-object-injection': 'warn',
'security/detect-eval-with-expression': 'error',
'security/detect-non-literal-require': 'error'
}
};
Configuraciones de Performance
ESLint con Caching
// .eslintrc con cache
{
"cache": true,
"cacheLocation": "node_modules/.cache/eslint/",
"cacheStrategy": "metadata",
"rules": {
"no-unused-vars": "error"
}
}
Biome Performance Optimization
// biome.json con optimizaciones
{
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"performance": {
"noAccumulatingSpread": "error",
"noDelete": "warn"
}
}
},
"files": {
"maxSize": 2097152
}
}
Integración con Editores
Configuración VS Code
// .vscode/settings.json
{
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.biome": "explicit"
},
"eslint.workingDirectories": [
{"mode": "auto"}
],
"biome.lspBin": "./node_modules/@biomejs/biome/bin/biome"
}
Configuración WebStorm
// .idea/inspectionProfiles/Project_Default.xml
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TypeScript" enabled="false" level="ERROR" enabled_by_default="false" />
</profile>
</component>
CI/CD Integration
GitHub Actions Setup
# .github/workflows/lint.yml
name: Lint and Test
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check
- run: npm run test
biome:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: biomejs/setup-biome@v1
with:
version: latest
- run: biome ci .
GitLab CI Configuration
# .gitlab-ci.yml
stages:
- lint
- test
eslint:
stage: lint
image: node:18
script:
- npm ci
- npm run lint
biome:
stage: lint
image: node:18
script:
- npm ci
- npx @biomejs/biome ci .
Monorepo Configurations
ESLint en Monorepo
// packages/eslint-config-custom/index.js
module.exports = {
extends: ['eslint:recommended'],
rules: {
'import/no-relative-parent-imports': 'error'
},
overrides: [
{
files: ['**/src/**/*.{js,ts}'],
rules: {
'no-console': 'error'
}
}
]
};
Biome en Monorepo
// biome.json para monorepo
{
"files": {
"ignore": ["**/node_modules/**", "**/dist/**", "**/build/**"]
},
"formatter": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}
Reglas de Import/Export
ESLint Import Rules
// eslint-imports.config.js
module.exports = {
plugins: ['import'],
rules: {
'import/order': [
'error',
{
'groups': [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index'
],
'newlines-between': 'always'
}
],
'import/no-unresolved': 'error',
'import/no-duplicates': 'error',
'import/no-cycle': 'error'
}
};
Análisis de Bundle y Performance
ESLint Plugin para Bundle Size
// eslint-bundle.config.js
module.exports = {
plugins: ['bundle-size'],
rules: {
'bundle-size/no-large-dependencies': [
'error',
{
maxSize: '100KB',
exclude: ['react', 'react-dom']
}
]
}
};
Configuraciones para Legacy Code
ESLint para Código Legacy
// eslint-legacy.config.js
module.exports = {
rules: {
'no-var': 'off',
'prefer-const': 'off',
'prefer-arrow-callback': 'off'
},
overrides: [
{
files: ['legacy/**/*.js'],
rules: {
'no-unused-vars': 'warn',
'no-console': 'off'
}
}
]
};
Internacionalización y Localización
ESLint para i18n
// eslint-i18n.config.js
module.exports = {
plugins: ['i18n'],
rules: {
'i18n/no-hardcoded-strings': 'error',
'i18n/text-restrictions': 'warn'
}
};
Accessibility Linting
ESLint para Accesibilidad
// eslint-a11y.config.js
module.exports = {
plugins: ['jsx-a11y'],
rules: {
'jsx-a11y/alt-text': 'error',
'jsx-a11y/anchor-is-valid': 'error',
'jsx-a11y/label-has-associated-control': 'error'
}
};
Security-Focused Linting
Reglas de Seguridad
// eslint-security-advanced.config.js
module.exports = {
plugins: ['security', 'no-unsanitized'],
rules: {
'security/detect-possible-timing-attacks': 'error',
'security/detect-buffer-noassert': 'error',
'no-unsanitized/method': 'error',
'no-unsanitized/property': 'error'
}
};
Linters - Configuraciones Avanzadas y Casos de Uso Especializados
Configuraciones para Microfrontends
ESLint Config Multi-proyecto
// eslint.config.microfrontends.js
export default [
// Configuración base para todos los proyectos
{
files: ["**/*.{js,ts,jsx,tsx}"],
rules: {
"no-console": "warn",
"prefer-const": "error"
}
},
// Configuración específica para shared libraries
{
files: ["packages/shared/**/*"],
rules: {
"no-restricted-imports": ["error", {
patterns: [{
group: ["../*", "./*"],
message: "Use imports absolutos en shared libraries"
}]
}]
}
},
// Configuración para aplicaciones host
{
files: ["apps/**/*"],
rules: {
"import/no-relative-packages": "off"
}
}
];
Linters para GraphQL
ESLint para GraphQL Schemas
// eslint-graphql.config.js
module.exports = {
plugins: ['@graphql-eslint'],
rules: {
'@graphql-eslint/known-type-names': 'error',
'@graphql-eslint/no-deprecated': 'warn',
'@graphql-eslint/unique-field-definition-names': 'error'
},
overrides: [
{
files: ['*.graphql'],
parser: '@graphql-eslint/eslint-plugin',
rules: {
'@graphql-eslint/alphabetize': ['error', {
fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition']
}]
}
}
]
};
Linters para CSS/SCSS
Stylelint Configuration
// .stylelintrc.js
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-prettier'
],
rules: {
'color-no-invalid-hex': true,
'font-family-no-duplicate-names': true,
'declaration-block-no-duplicate-properties': true,
'selector-class-pattern': null,
'custom-property-pattern': null
},
ignoreFiles: [
'**/node_modules/**',
'**/dist/**',
'**/coverage/**'
]
};
Stylelint con SCSS
// .stylelintrc.scss.js
module.exports = {
extends: [
'stylelint-config-standard-scss',
'stylelint-config-recommended-scss'
],
rules: {
'scss/dollar-variable-pattern': null,
'scss/selector-no-redundant-nesting-selector': true,
'scss/no-global-function-names': true
}
};
Linters para Markdown
ESLint para Markdown
// eslint-markdown.config.js
module.exports = {
plugins: ['markdown'],
overrides: [
{
files: ['**/*.md'],
processor: 'markdown/markdown',
rules: {
'no-console': 'off',
'no-unused-vars': 'off'
}
},
{
files: ['**/*.md/*.{js,ts}'],
rules: {
'no-console': 'off',
'prefer-const': 'warn'
}
}
]
};
Markdownlint Configuration
// .markdownlint.json
{
"default": true,
"MD013": false,
"MD024": false,
"MD025": false,
"line-length": false,
"no-inline-html": false,
"first-line-heading": false,
"single-title": false
}
Linters para Docker
Hadolint Configuration
# .hadolint.yaml
failure-threshold: warning
ignored:
- DL3008
- DL3018
- DL3059
override:
error:
- DL3003
- DL3006
warning:
- DL3015
- DL3025
trustedRegistries:
- docker.io
- registry.hub.docker.com
Linters para GitHub Actions
Actionlint Setup
# actionlint configuration example
# .github/workflows/ci.yml con linting integrado
name: CI
on: [push, pull_request]
jobs:
lint-workflows:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: rhysd/actionlint@v1
with:
config-file: .actionlint.yaml
Linters para JSON/YAML
ESLint para JSON
// eslint-json.config.js
module.exports = {
plugins: ['json'],
overrides: [
{
files: ['*.json', '*.json5'],
parser: 'jsonc-eslint-parser',
rules: {
'jsonc/sort-keys': 'error'
}
}
]
};
YAML Lint Configuration
# .yamllint.yaml
extends: default
rules:
braces: enable
brackets: enable
colons: enable
commas: enable
comments: disable
comments-indentation: disable
document-start: disable
empty-lines: enable
hyphens: enable
indentation:
level: error
indent-sequences: consistent
key-duplicates: enable
line-length: disable
new-line-at-end-of-file: enable
new-lines: enable
trailing-spaces: enable
Linters para Performance
ESLint Performance Rules
// eslint-performance.config.js
module.exports = {
rules: {
'no-multiple-empty-lines': ['error', { max: 2 }],
'max-depth': ['error', 4],
'complexity': ['error', 10],
'max-params': ['error', 4],
'no-nested-ternary': 'error',
'prefer-template': 'error',
'no-loop-func': 'warn'
}
};
Linters para TypeScript Avanzado
ESLint TypeScript Estricto
// eslint-typescript-strict.config.js
module.exports = {
extends: ['@typescript-eslint/recommended-requiring-type-checking'],
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname
},
rules: {
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unsafe-assignment': 'error',
'@typescript-eslint/no-unsafe-call': 'error',
'@typescript-eslint/no-unsafe-member-access': 'error',
'@typescript-eslint/no-unsafe-return': 'error',
'@typescript-eslint/strict-boolean-expressions': 'error'
}
};
Linters para Code Quality Metrics
ESLint para Métricas de Código
// eslint-metrics.config.js
module.exports = {
rules: {
'max-lines': ['warn', 300],
'max-lines-per-function': ['warn', 50],
'max-statements': ['warn', 15],
'max-nested-callbacks': ['error', 3],
'cognitive-complexity': ['warn', 15]
}
};
Linters para Commit Messages
Commitlint Configuration
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'revert']
],
'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case']],
'header-max-length': [2, 'always', 72]
}
};
Linters para SEO y Meta Tags
ESLint para SEO
// eslint-seo.config.js
module.exports = {
plugins: ['jsx-seo'],
rules: {
'jsx-seo/no-missing-meta-tags': 'error',
'jsx-seo/no-duplicate-meta-tags': 'error',
'jsx-seo/require-meta-description': 'warn',
'jsx-seo/require-open-graph-tags': 'warn'
}
};
Linters para Internationalization
ESLint para i18n
// eslint-i18n-advanced.config.js
module.exports = {
plugins: ['i18n-ally'],
rules: {
'i18n-ally/key-style': ['error', 'nested'],
'i18n-ally/no-duplicate-keys': 'error',
'i18n-ally/no-missing-keys': 'warn',
'i18n-ally/no-unused-keys': 'error'
},
settings: {
'i18n-ally': {
localeDir: './locales',
sourceLanguage: 'en',
supportedLngs: ['en', 'es', 'fr']
}
}
};
Linters para Web Components
ESLint para Custom Elements
// eslint-web-components.config.js
module.exports = {
plugins: ['wc'],
rules: {
'wc/guard-super-call': 'error',
'wc/no-closed-shadow-root': 'error',
'wc/no-constructor-attributes': 'error',
'wc/no-invalid-element-name': 'error',
'wc/no-self-class': 'error'
}
};
Linters para Deno
Deno Lint Configuration
// deno.json
{
"lint": {
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"],
"exclude": ["no-console"]
}
},
"fmt": {
"useTabs": false,
"lineWidth": 100,
"indentWidth": 2,
"singleQuote": true
}
}
Linters para Rust (WASM Projects)
Clippy Configuration
# .cargo/config.toml
[build]
rustflags = ["-D", "warnings"]
[clippy]
all-targets = true
all-features = true
Linters para Python (Full Stack)
Flake8 Configuration
# .flake8
[flake8]
max-line-length = 100
exclude = .git,__pycache__,build,dist
ignore = E203, W503
per-file-ignores =
__init__.py: F401
tests/*: S101
Linters para Shell Scripts
ShellCheck Configuration
# .shellcheckrc
disable=SC1090,SC1091
enable=add-default-case,avoid-nullary-conditions,check-unassigned-uppercase
exclude=SC2154
shell=bash
Linters para SQL
SQLFluff Configuration
# .sqlfluff
[sqlfluff]
dialect = postgres
exclude_rules = L011,L031
[sqlfluff:rules]
comma_style = trailing
allow_scalar = True
single_table_references = consistent
Linters para Terraform
TFLint Configuration
# .tflint.hcl
config {
module = true
force = false
}
rule "terraform_deprecated_interpolation" {
enabled = true
}
rule "terraform_unused_declarations" {
enabled = true
}
rule "terraform_comment_syntax" {
enabled = true
}
Linters para OpenAPI/Swagger
Spectral Configuration
# .spectral.yaml
extends:
- spectral:oas
rules:
info-contact: error
operation-operationId: warn
path-parameters: error
no-script-tags-in-markdown: off
Linters para Jupyter Notebooks
nbQA Configuration
# .nbqa.ini
[nbqa]
addopts = --nbqa-mutate
mypy = --ignore-missing-imports
flake8 = --extend-ignore=E402,F401
Linters para Mobile Development
ESLint para React Native
// eslint-react-native.config.js
module.exports = {
env: {
'react-native/react-native': true
},
plugins: ['react-native'],
rules: {
'react-native/no-unused-styles': 'error',
'react-native/split-platform-components': 'warn',
'react-native/no-inline-styles': 'warn',
'react-native/no-color-literals': 'warn'
}
};
Linters para WebAssembly
ESLint para WASM
// eslint-wasm.config.js
module.exports = {
rules: {
'no-restricted-syntax': [
'error',
{
selector: 'CallExpression[callee.name="WebAssembly.instantiate"]',
message: 'Use async WebAssembly.instantiateStreaming for better performance'
}
]
}
};
¿Te gusta este contenido? Suscríbete vía RSS