Linting JavaScript

Edoardo Midali
Edoardo Midali

Il linting è il processo di analisi statica del codice per identificare errori di sintassi, problemi di stile, pattern problematici e potenziali bug prima dell’esecuzione. In JavaScript, dove la flessibilità del linguaggio può portare a codice inconsistente e propenso agli errori, il linting diventa uno strumento essenziale per mantenere qualità e leggibilità del codice.

Perché il Linting è Fondamentale

JavaScript è un linguaggio dinamico e permissivo che consente molte libertà sintattiche. Questa flessibilità, pur essendo una forza, può diventare un problema quando si lavora in team o su progetti di grandi dimensioni. Il linting risolve problemi comuni come variabili non dichiarate, codice morto, inconsistenze stilistiche e pattern che possono causare bug sottili.

Un linter non si limita a controllare la sintassi: analizza il codice per pattern anti-pattern, suggerisce best practices moderne, identifica potenziali problemi di performance e può anche enformare convenzioni di naming e organizzazione del codice. È come avere un mentore esperto che rivede costantemente il tuo codice.

ESLint: Lo Standard de Facto

ESLint è il linter JavaScript più diffuso e potente. A differenza dei suoi predecessori, ESLint è completamente configurabile e estensibile, permettendo di creare regole personalizzate per adattarsi alle esigenze specifiche di ogni progetto o organizzazione.

Installazione e Setup Base

# Installazione globale
npm install -g eslint

# Installazione per progetto (consigliata)
npm install --save-dev eslint

# Inizializzazione configurazione interattiva
npx eslint --init

L’inizializzazione interattiva ti guida attraverso domande sullo stile di codice preferito, framework utilizzati e ambiente di sviluppo, generando automaticamente una configurazione appropriata.

Configurazione Fondamentale

// .eslintrc.js
module.exports = {
  // Ambiente di esecuzione
  env: {
    browser: true,
    es2021: true,
    node: true,
    jest: true,
  },

  // Estensioni di configurazioni predefinite
  extends: [
    "eslint:recommended",
    "@typescript-eslint/recommended",
    "prettier", // Deve essere ultimo per evitare conflitti
  ],

  // Parser per sintassi moderna
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: 2021,
    sourceType: "module",
    ecmaFeatures: {
      jsx: true,
    },
  },

  // Plugin aggiuntivi
  plugins: ["@typescript-eslint", "react", "react-hooks", "import"],

  // Regole personalizzate
  rules: {
    // Errori che bloccano il build
    "no-unused-vars": "error",
    "no-undef": "error",
    "no-console": "warn",

    // Stile e best practices
    "prefer-const": "error",
    "no-var": "error",
    eqeqeq: ["error", "always"],

    // Regole React specifiche
    "react/prop-types": "warn",
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
  },

  // Configurazioni specifiche per file
  overrides: [
    {
      files: ["**/*.test.js", "**/*.spec.js"],
      rules: {
        "no-console": "off", // Permetti console.log nei test
      },
    },
  ],
};

Regole Essenziali e Configurazioni

Categorie di Regole

Possibili Errori: Identificano codice che potrebbe causare errori runtime o comportamenti inaspettati.

// Esempi di regole per possibili errori
{
  'no-unused-vars': 'error',        // Variabili dichiarate ma non usate
  'no-undef': 'error',             // Uso di variabili non dichiarate
  'no-unreachable': 'error',       // Codice irraggiungibile
  'no-constant-condition': 'error', // Condizioni sempre vere/false
  'no-dupe-keys': 'error',         // Chiavi duplicate negli oggetti
  'no-empty': 'warn',              // Blocchi vuoti
  'valid-typeof': 'error'          // Operatori typeof incorretti
}

Best Practices: Enforchano pattern di codice che prevengono bug e migliorano la leggibilità.

{
  'eqeqeq': ['error', 'always'],      // Usa sempre === invece di ==
  'no-eval': 'error',                 // Vieta uso di eval()
  'no-implied-globals': 'error',      // Previeni variabili globali accidentali
  'prefer-const': 'error',            // Usa const quando possibile
  'no-var': 'error',                  // Vieta var, usa let/const
  'curly': ['error', 'all'],          // Require parentesi graffe sempre
  'default-case': 'warn'              // Require default in switch
}

Stile e Formattazione: Mantengono consistenza visuale nel codice.

{
  'indent': ['error', 2],             // Indentazione a 2 spazi
  'quotes': ['error', 'single'],      // Usa virgolette singole
  'semi': ['error', 'always'],        // Richiedi semicoloni
  'comma-dangle': ['error', 'never'], // No virgole finali
  'object-curly-spacing': ['error', 'always'], // Spazi nelle parentesi graffe
  'array-bracket-spacing': ['error', 'never']  // No spazi nelle parentesi quadre
}

Integrazione con Prettier

Prettier è un code formatter che si occupa automaticamente della formattazione, mentre ESLint si concentra sulla qualità del codice. La combinazione dei due è potentissima:

# Installazione
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier

# .prettierrc
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false
}
// .eslintrc.js con integrazione Prettier
module.exports = {
  extends: [
    "eslint:recommended",
    "prettier", // Disabilita regole di stile di ESLint che confliggono
  ],
  plugins: ["prettier"],
  rules: {
    "prettier/prettier": "error", // Tratta errori Prettier come errori ESLint
  },
};

Configurazioni Predefinite Popolari

Airbnb Style Guide

Una delle configurazioni più rispettate della community:

npm install --save-dev eslint-config-airbnb

# .eslintrc.js
module.exports = {
  extends: ['airbnb'],
  rules: {
    // Override specifiche se necessario
    'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }]
  }
};

Standard JS

Configurazione minimalista senza semicoloni:

npm install --save-dev eslint-config-standard

# .eslintrc.js
module.exports = {
  extends: ['standard']
};

Integrazione nel Workflow

Script NPM

{
  "scripts": {
    "lint": "eslint src/",
    "lint:fix": "eslint src/ --fix",
    "lint:watch": "nodemon --exec 'npm run lint' --watch src/",
    "precommit": "npm run lint"
  }
}

Git Hooks con Husky

npm install --save-dev husky lint-staged

# package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "src/**/*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write",
      "git add"
    ]
  }
}

Editor Integration

La maggior parte degli editor moderni supporta ESLint nativamente:

// VS Code settings.json
{
  "eslint.enable": true,
  "eslint.autoFixOnSave": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ]
}

Regole Personalizzate

Per esigenze specifiche, puoi creare regole personalizzate:

// eslint-plugin-custom/rules/no-hardcoded-colors.js
module.exports = {
  meta: {
    type: "suggestion",
    docs: {
      description: "Disallow hardcoded color values",
      category: "Best Practices",
    },
    fixable: null,
    schema: [],
  },

  create(context) {
    const colorRegex = /#[0-9a-fA-F]{3,6}\b/;

    return {
      Literal(node) {
        if (typeof node.value === "string" && colorRegex.test(node.value)) {
          context.report({
            node,
            message:
              "Avoid hardcoded color values. Use CSS variables or theme constants.",
          });
        }
      },
    };
  },
};

// Utilizzo
// .eslintrc.js
module.exports = {
  plugins: ["custom"],
  rules: {
    "custom/no-hardcoded-colors": "warn",
  },
};

Gestione di Progetti Grandi

Configurazioni Multiple

Per progetti complessi, puoi avere configurazioni diverse per diverse parti:

// .eslintrc.js (root)
module.exports = {
  extends: ["eslint:recommended"],
  overrides: [
    {
      files: ["src/components/**/*.js"],
      extends: ["airbnb/react"],
    },
    {
      files: ["src/utils/**/*.js"],
      rules: {
        "no-console": "off",
      },
    },
    {
      files: ["**/*.test.js"],
      env: {
        jest: true,
      },
      rules: {
        "no-unused-expressions": "off",
      },
    },
  ],
};

Performance e Esclusioni

// .eslintignore
node_modules/
dist/
build/
*.min.js
coverage/

// .eslintrc.js
module.exports = {
  ignorePatterns: [
    'src/legacy/**',
    'src/vendor/**'
  ]
};

Best Practices per il Linting

Introduzione Graduale: In progetti esistenti, introduci il linting gradualmente, iniziando con regole di errore base e aggiungendo regole di stile progressivamente.

Consenso del Team: Le regole di linting dovrebbero essere discusse e accettate dal team. Un linter troppo rigido può rallentare lo sviluppo, mentre uno troppo permissivo perde efficacia.

Automazione: Integra il linting nei processi automatici (CI/CD, pre-commit hooks) per garantire che venga sempre eseguito.

Documentazione: Documenta le regole personalizzate e le ragioni dietro scelte specifiche per aiutare nuovi membri del team.

Aggiornamenti Regolari: Mantieni ESLint e le sue configurazioni aggiornate per beneficiare di nuove regole e miglioramenti.

Il linting non è solo uno strumento tecnico, ma un investimento nella qualità a lungo termine del codice. Un setup di linting ben configurato riduce significativamente i bug, migliora la leggibilità del codice e facilita la collaborazione nel team, diventando un moltiplicatore di produttività per l’intero processo di sviluppo.