ABONAMENTE VIDEO REDACȚIA
RO
EN
Numărul 162
NOU
Numărul 161
Numărul 160 Numărul 159 Numărul 158 Numărul 157 Numărul 156 Numărul 155 Numărul 154 Numărul 153 Numărul 152 Numărul 151 Numărul 150 Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 162
Abonamente

Reguli ESLint personalizate pentru guvernanța codului organizațional

Emilian Pașcalău
Software Architect @ msg systems Romania



PROGRAMARE

În companii, menținerea calității codului între proiecte și echipe reprezintă o provocare fundamentală care necesită un efort semnificativ. Costul tiparelor inconsistente, al erorilor subtile, al codului inutil și al stilurilor arhitecturale divergente crește rapid. Deși documentația, revizuirile de cod și respectarea "celor mai bune practici" ajută la atenuarea acestor probleme, ele se bazează în mare măsură pe atenția umană și sunt adesea primele care se erodează sub presiunea livrării.

O soluție mai sustenabilă este automatizarea prin instrumente adecvate care impun verificări consecvente. ESLint [1] este un astfel de instrument — un linter, termen care, în informatică, se referă la un instrument de analiză statică a codului utilizat pentru identificarea și semnalarea erorilor de programare, bugurilor, problemelor de stil și construcțiilor suspecte [2]. "ES" din ESLint provine de la ECMAScript [3], standardul la care JavaScript se conformează. Deși există și alte instrumente cu scopuri similare, ESLint a devenit standardul de facto pentru lintingul JavaScript.

Deși JavaScript este centrul principal de atenție al ESLint, acesta suportă și alte limbaje și tehnologii, precum TypeScript, limbaje de markup și chiar Python, prin intermediul pluginurilor. În acest ghid, vom explica conceptele principale din spatele ESLint și ecosistemului său. Totuși, cititorii ar trebui să aibă o minimă familiaritate cu JavaScript/TypeScript și Node.js (necesar pentru rularea ESLint) pentru a înțelege sintaxa și a interpreta erorile pe care ESLint le identifică.

Unul dintre principalele avantaje ale ESLint este extensibilitatea. Pe lângă funcționalitatea implicită, ESLint poate fi îmbunătățit prin adăugarea de funcționalități suplimentare — fie prin instalarea pluginurilor existente, fie prin crearea unora proprii. În secțiunile următoare ale acestui articol, ne vom concentra pe cea de-a doua opțiune: construirea unui plugin personalizat de la zero.

Un alt aspect important este că aceste configurații ESLint pot fi partajate, ceea ce le face ușor de reutilizat ca seturi predefinite și de aplicat consecvent în mai multe depozite de cod. Există deja multe astfel de seturi disponibile, inclusiv cele oferite de ESLint sau de furnizori cunoscuți, precum Airbnb [4].

Având în vedere evoluția rapidă a toolurilor, materialul prezentat aici se bazează pe ESLint și pe documentația sa oficială [10], disponibilă la momentul redactării (decembrie 2025).

Instalare și configurare ESLint

Înainte de a trece la crearea regulilor și pluginurilor personalizate pentru ESLint, este la fel de important să înțelegem elementele de bază — cum să instalăm, configurăm și utilizăm ESLint pentru scenarii standard. Această secțiune se va concentra pe furnizarea acestor cunoștințe fundamentale.

Pentru a instala și rula ESLint, vei avea nevoie de Node.js [5] și npm [6]. Node.js oferă mediul de execuție pentru JavaScript, iar npm (Node Package Manager) este managerul de pachete implicit inclus în Node.js. Unii dezvoltatori preferă Yarn [7] ca alternativă, ceea ce este o alegere validă. Totuși, pentru simplitate și consistență, acest articol va folosi opțiunea implicită: npm.

În funcție de tehnologiile utilizate în proiect, ESLint poate fi configurat în moduri diferite, folosind diverse pluginuri. Poți instala ESLint global, la fel ca orice alt pachet npm, sau local, în cadrul proiectului. Recomandăm instalarea locală deoarece această abordare menține configurația ESLint legată de proiect, ceea ce o face mai ușor de întreținut, de depanat și de aplicat consecvent în diferite medii. Instalarea locală oferă, de asemenea, flexibilitate, permițând diferitelor proiecte să utilizeze versiuni și configurații ESLint distincte, fără conflicte.

mkdir eslint_test_project
cd eslint_test_project
npm init -y
npm init @eslint/config@latest

După finalizarea asistentului de configurare, folderul proiectului ar trebui să conțină un fișier de configurare numit eslint.config.mjs (dacă ai selectat JavaScript) sau eslint.config.mts (dacă ai selectat TypeScript ca limbaj de configurare). Mai jos este un exemplu al conținutului fișierului. Observă că configurația extinde deja js/recommended, ceea ce înseamnă că regulile implicite ESLint sunt activate în mod prestabilit.

import js from "@eslint/js";
import globals from "globals";
import { defineConfig } from "eslint/config";

export default defineConfig([
  { files: ["**/*.{js,mjs,cjs}"], 
  plugins: { js }, 
  extends: ["js/recommended"], 
  languageOptions: { globals: 
     {...globals.browser, ...globals.node} },
     rules:{…} 
  },
]);

Pentru configurații și combinații mai avansate, consultă documentația ESLint [8], care oferă un ghid cuprinzător. Nu vom intra în detalii suplimentare aici, așa că cititorii care au nevoie de informații mai aprofundate sunt încurajați să consulte documentația oficială.

Conform secțiunii Rules Reference [9] din documentația oficială, toate regulile marcate cu bifă sunt activate implicit.

Dacă dorești să adaugi sau să modifici reguli predefinite, poți face acest lucru specificând numele regulii în secțiunea rules (așa cum este prezentat în fragmentul de cod anterior) și furnizând opțiunile de configurare corespunzătoare pentru acea regulă. De reținut că regulile de bază ale ESLint — adică regulile livrate împreună cu ESLint — nu fac parte din API-ul public [10].

... 'no-console':"error" ...,

Reguli ESLint

În această secțiune, examinăm caracteristicile definitorii ale unei reguli ESLint. Din cauza limitărilor de spațiu, discuția nu este exhaustivă; pentru o prezentare cuprinzătoare, consultați [10]. Cu toate acestea, introducem o prezentare vizuală a acestor caracteristici, în principal prin diagrame de clase UML. După cunoștințele noastre, aceste modele constituie o contribuție originală care clarifică structura și comportamentul regulilor ESLint, simplificând astfel înțelegerea și crearea regulilor ESLint personalizate.

Regula

La prima vedere, fișierul sursă al unei reguli ESLint pare simplu. Totuși, așa cum este ilustrat în Figura 1, complexitatea crește rapid. Modulul exportă un obiect care include un descriptor meta și o funcție create(context), structură împărtășită atât de regulile de bază, cât și de cele personalizate.

Metamodelul regulii (Figura 1) este o sinteză a proprietăților esențiale care definesc o regulă. Metamodelul vizual este bazat pe documentația oficială [10] și pe codul ESLint. Atributul meta al unei reguli include, de obicei:

  1. type — având una dintre valorile problem, suggestion sau layout.

  2. docs — metadate de documentație, cu un scurt description și un url către documentația completă. Atributul docs.recommended este un boolean folosit doar pentru regulile de bază (core), indicând includerea în eslint:recommended.

  3. fixable — opțional; se specifică doar dacă regula oferă o corectare automată. Valorile permise sunt code sau whitespace.

  4. hasSuggestions — opțional; se setează doar dacă regula oferă sugestii (prin context.report({ suggest: [...] })).

  5. messages — o mapare (dicționar) de identificatori de mesaje la mesaje (Strings). Acestea sunt referite prin context.report({ messageId: ... }) pentru a afișa errors sau warnings, în funcție de configurație.

  6. schema — o descriere în format JSON Schema [11] a opțiunilor unei reguli, folosită de ESLint pentru a valida configurarea acesteia.

Procesul

Așa cum este ilustrat în Figura 2, procesul de linting cuprinde mai multe etape. Acesta începe prin rezolvarea și potrivirea fișierelor conform configurației ESLint și regulilor de ignorare. În continuare, ESLint selectează un parser adecvat în funcție de tipul fișierului (de exemplu, JavaScript, TypeScript, JSX/markup). În mod implicit, ESLint utilizează Espree [12].

Pentru a permite analiza statică, ESLint are nevoie de o reprezentare structurată, nu de text brut: parserul transformă codul sursă într-un Arbore de Sintaxă Abstractă (AST) (Figura 3). AST-ul este un model intermediar, structurat sub formă de arbore, compus din noduri pe care instrumente precum ESLint le folosesc pentru a analiza codul. Tabelul 1 prezintă, alăturat, instrucțiunea sursă console.log('hello') și reprezentarea sa AST în format JSON. Apelul către console este descompus în noduri specifice—ExpressionStatement, CallExpression, MemberExpression, Identifier, arguments etc.—ale căror semantici sunt relevante pentru compilatoare, linters și alte unelte.

După ce AST-ul este construit, ESLint creează informațiile despre scope (variabile, referințe, definiții) pentru a susține regulile care depind de legături. Apoi, ESLint parcurge AST-ul și invocă vizitatorii fiecărei reguli obținuți din create(context). Atunci când este detectată o încălcare, regula apelează context.report(...) pentru a înregistra o problemă (eroare sau avertisment, în funcție de configurație). Dacă sunt disponibile și activate corecții (de exemplu, prin --fix), ESLint aplică modificările și, în final, formatează rezultatele utilizând formatterul selectat.

Fig. 2. Procesul

Fig. 3 Code 2 AST

Codul: console.log('hello');

Arborele de Sintaxă Abstractă (AST):

{
  "type": "ExpressionStatement",
  "expression": {
    "type": "CallExpression",
    "callee": {
      "type": "MemberExpression",
      "object": {
        "type": "Identifier",
        "name": "console",
      },
      "property": {
        "type": "Identifier",
        "name": "log",
      },
      "computed": false,
      "optional": false,
    },
    "arguments": [
      {
        "type": "Literal",
        "value": "hello",
        "raw": "'hello'",
      }
    ],
    "optional": false,
  }
}

Exemplu

Deoarece am discutat deja aspectele esențiale pentru crearea unei reguli, mai jos este un exemplu concret care impune utilizarea unui logger în locul consolei standard.

export default {
   meta: {
     type: "problem",
     docs: {
        description: "Prefer company logger instead 
              of console.*",
        url: "https://example.com/docs/rules/
              prefer-logger-over-console"
        },
        fixable: "code",
        schema: [],
        messages: {
            avoidConsole: "Use '{{logger}}
            .{{method}}' instead of console
            .{{method}}.",
        }
    },
    create(context) {
        return {
           "CallExpression[callee.object
             .name='console']"(node) {
               const property = node.callee.property;
               const method = property && property
                              .name;
                context.report({
                    node,
                    messageId: «avoidConsole»,
                    data: {logger: 'logger', method},
                });
            }
        };
    }
};

Proiectul complet este disponibil la: https://github.com/epascalau/custom-eslint-rule

Concluzii

Scopul lucrării prezentate aici nu a fost niciodată realizarea unui articol exhaustiv despre subiectul ESLint. Totuși, am explicat conceptele necesare pentru a crea reguli personalizate și, prin intermediul acestora, pentru a impune stiluri arhitecturale, linii directoare organizaționale și specifice proiectelor etc.

Referințe

  1. ESLint Homepage

  2. Lint (software))

  3. Ecma, E. (1999). 262: Ecmascript language specification. ECMA (European Association for Standardizing Information and Communication Systems), Pub-ECMA: Adr

  4. AirBnb ESLint Preset

  5. Node.js Homepage

  6. npm Homepage

  7. yarn Homepage

  8. ESLint Configuration Files Section

  9. ESLint Rules Reference Section

  10. ESLint Rule Structure Section

  11. JSON Schema Homepage

  12. Espree Parser Homepage

NUMĂRUL 159 - Industria Automotive

Sponsori

  • BT Code Crafters
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • GlobalLogic
  • BMW TechWorks Romania