Kako posložiti stvari u projektu gdje postoji šuma izravnih ruku (tslint, prettier, itd. postavke)

Pozdrav opet. Ostanimo u kontaktu. Omelnicki SergejDanas ću s vama podijeliti jednu od svojih glavobolja: što učiniti kada projekt uključuje više programera različitih razina vještina, koristeći primjer Angular aplikacije.

Kako posložiti stvari u projektu gdje postoji šuma izravnih ruku (tslint, prettier, itd. postavke)

Slučajno sam dugo radio isključivo sa svojim timom, gdje smo se odavno dogovorili o pravilima formatiranja, komentiranja, uvlačenja i tako dalje. Navikli smo se na njih i živjeli sretno do kraja života. U svojoj radosti, čak sam objavio i članak na Habru. naš stil kodaDakle, od svega magičnog, tslint smo koristili samo na pre-commitu.

A onda smo rasli. Pojavio se novi projekt sa starim kodom, zajedno s četiri nova programera. A onda stvari nisu išle po planu.

Kako posložiti stvari u projektu gdje postoji šuma izravnih ruku (tslint, prettier, itd. postavke)

Mislim da mnogi znaju da rad sa starim kodom nije zabavan. Sjećam se samo jednog projekta s kojim sam bio oduševljen, a ostali... Pa o čemu sam pričao?) Oh da.

Iskreno, arhitektura projekta je ostavljala mnogo toga za poželjeti, a komentari i tipovi sustava bili su daleki san. U nekom trenutku me obeshrabrila činjenica da naša dokumentacija o pravilima formatiranja nije radila, komentari se nisu pisali, a tipovi sustava - što je to bilo?). Nešto se moralo poduzeti po tom pitanju.

Za one koji jedva čekaju naučiti sve korake odjednom:

  • Podijelili smo tslint na meka pravila (za pre-commit) i tvrda pravila (za IDE, kako bismo podsjetili programere što su zaboravili učiniti).

  • Dodano automatsko ispravljanje mogućih pravila prije potvrde iz hard tslinta

  • Napisao/la sam pravila za ljepše

  • Plesali smo uz tamburin kako bismo lansirali ng lint s lint-insceniranim

Prvi korak: podijeli pa vladaj

Kad sam došao na ideju da pooštrim pravila za linter, mislio sam da ćemo propasti. Kod je naslijeđen. Morate ga razumjeti, a s toliko koda možete se izgubiti. Donjela se odluka o stvaranju drugog lintera za IDE, onog koji bi bio ruglo i prisilio vas da pišete jsdoc za metode i svojstva, pišete sučelja ili nesretni onPush i tako dalje.

Dakle, u korijenu smo počeli imati 2 tslin datoteke:

tsconfig.json

{
  "rulesDirectory": [
    "node_modules/codelyzer"
  ],
  "rules": {
    "arrow-return-shorthand": true,
    "callable-types": true,
    "class-name": true,
    "comment-format": [
      true,
      "check-space"
    ],
    "curly": true,
    "deprecation": {
      "severity": "warn"
    },
    "eofline": true,
    "forin": true,
    "import-blacklist": [
      true,
      "rxjs/Rx"
    ],
    "import-spacing": true,
    "indent": [
      true,
      "spaces"
    ],
    "interface-over-type-literal": true,
    "label-position": true,
    "max-line-length": [
      true,
      200
    ],
    "member-access": false,
    "member-ordering": [
      true,
      {
        "order": [
          "static-field",
          "instance-field",
          "static-method",
          "instance-method"
        ]
      }
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-super": true,
    "no-empty": false,
    "no-empty-interface": true,
    "no-eval": true,
    "no-inferrable-types": [
      false,
      "ignore-params"
    ],
    "no-duplicate-imports": true,
    "no-misused-new": true,
    "no-non-null-assertion": true,
    "no-redundant-jsdoc": true,
    "no-shadowed-variable": false,
    "no-string-literal": false,
    "no-string-throw": true,
    "no-switch-case-fall-through": true,
    "no-trailing-whitespace": [
      true,
      "ignore-comments",
      "ignore-jsdoc"
    ],
    "no-unnecessary-initializer": true,
    "no-unused-expression": true,
    "no-use-before-declare": false,
    "no-var-keyword": true,
    "object-literal-sort-keys": false,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "prefer-const": true,
    "quotemark": [
      true,
      "single"
    ],
    "radix": false,
    "semicolon": [
      true,
      "always"
    ],
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      }
    ],
    "unified-signatures": true,
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ],
    "directive-selector": [
      true,
      "attribute",
      "app",
      "camelCase"
    ],
    "component-selector": [
      true,
      "element",
      "app",
      "kebab-case"
    ],
    "no-output-on-prefix": false,
    "no-inputs-metadata-property": true,
    "no-outputs-metadata-property": true,
    "no-host-metadata-property": true,
    "no-input-rename": false,
    "no-output-rename": true,
    "use-lifecycle-interface": true,
    "use-pipe-transform-interface": true,
    "component-class-suffix": true,
    "directive-class-suffix": true,
    "no-consecutive-blank-lines": true
  }
}

tslint.ide_only.json

{
  "rulesDirectory": [
    "node_modules/codelyzer"
  ],
  "rules": {
    "completed-docs": [
      true,
      {
        "properties": true,
        "methods": true
      }
    ],
    "no-angle-bracket-type-assertion": true,
    "no-any": true,
    "prefer-output-readonly": true,
    "prefer-on-push-component-change-detection": true,
    "array-type": [
      true,
      "array"
    ],
    "typedef": [
      true,
      "call-signature",
      "arrow-call-signature"
    ],
    "arrow-return-shorthand": true,
    "callable-types": true,
    "class-name": true,
    "comment-format": [
      true,
      "check-space"
    ],
    "curly": true,
    "deprecation": {
      "severity": "warn"
    },
    "eofline": true,
    "forin": true,
    "import-blacklist": [
      true,
      "rxjs/Rx"
    ],
    "import-spacing": true,
    "indent": [
      true,
      "spaces"
    ],
    "interface-over-type-literal": true,
    "label-position": true,
    "max-line-length": [
      true,
      200
    ],
    "member-access": [
      true,
      "check-parameter-property",
      "check-accessor"
    ],
    "member-ordering": [
      true,
      {
        "order": [
          "public-static-field",
          "protected-static-field",
          "private-static-field",
          "public-instance-field",
          "protected-instance-field",
          "private-instance-field",
          "constructor",
          "public-static-method",
          "protected-static-method",
          "private-static-method",
          "public-instance-method",
          "protected-instance-method",
          "private-instance-method"
        ]
      }
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": true,
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-super": true,
    "no-empty": false,
    "no-empty-interface": true,
    "no-duplicate-switch-case": true,
    "no-eval": true,
    "no-inferrable-types": [
      false,
      "ignore-params"
    ],
    "no-duplicate-imports": true,
    "one-variable-per-declaration": true,
    "no-misused-new": true,
    "no-non-null-assertion": true,
    "prefer-template": [
      true,
      "allow-single-concat"
    ],
    "ordered-imports": true,
    "no-redundant-jsdoc": true,
    "no-shadowed-variable": false,
    "no-string-literal": false,
    "no-string-throw": true,
    "no-switch-case-fall-through": true,
    "no-trailing-whitespace": [
      true,
      "ignore-comments",
      "ignore-jsdoc"
    ],
    "ban": [
      true,
      {
        "name": [
          "Object",
          "assign"
        ],
        "message": "Используйте cloneDeep (lodash) для копирования объекта"
      }
    ],
    "max-classes-per-file": [
      true,
      1
    ],
    "cyclomatic-complexity": [
      true,
      6
    ],
    "static-this": true,
    "no-unnecessary-initializer": true,
    "no-unused-expression": true,
    "no-var-keyword": true,
    "object-literal-sort-keys": false,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "prefer-const": true,
    "quotemark": [
      true,
      "single"
    ],
    "radix": false,
    "semicolon": [
      true,
      "always"
    ],
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      }
    ],
    "unified-signatures": true,
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ],
    "directive-selector": [
      true,
      "attribute",
      "app",
      "camelCase"
    ],
    "component-selector": [
      true,
      "element",
      "app",
      "kebab-case"
    ],
    "no-output-on-prefix": false,
    "no-inputs-metadata-property": true,
    "no-outputs-metadata-property": true,
    "no-host-metadata-property": true,
    "no-input-rename": false,
    "no-output-rename": true,
    "use-lifecycle-interface": true,
    "use-pipe-transform-interface": true,
    "component-class-suffix": true,
    "directive-class-suffix": true,
    "no-consecutive-blank-lines": true
  }
}

U spisu src/tslint Standardni tslint zamijenili smo s ide

src/clint.json

{
    "extends": "../tslint.ide_only.json",
    "rules": {
        "directive-selector": [
            true,
            "attribute",
            "app",
            "camelCase"
        ],
        "component-selector": [
            true,
            "element",
            "app",
            "kebab-case"
        ]
    }
}

I popravio sam pokretanje našeg lintera u skriptama package.json.

ng lint --tslint-config ./tslint.json --fix`

Nakon čega smo se počeli vješati o podcrtane stvari koje je trebalo ispraviti.

Drugi korak - popravite nekoliko stvari

Kako posložiti stvari u projektu gdje postoji šuma izravnih ruku (tslint, prettier, itd. postavke)

tslint ima pravila s has fixerPa iskoristimo ga.

tslint --project tslint.ide_only.json --fix --force

Ovdje pokrećemo hard linter pravila s automatskim ispravljanjem dostupnih parametara i kažemo ovoj naredbi da ne vraća greške (naš cilj ovdje je izvršiti automatsko ispravljanje).

Treći korak: pišite lijepo

Kad svatko piše u svom stilu, to s vremenom postane zamorno. Kod treba pisati na način koji ostavlja dojam kao da ga piše jedna osoba. Da bih to postigao, koristio sam Prettier sa sljedećim postavkama:

.prettierr.yaml

printWidth: 200     # Максимальное кол-во символов в строке
tabWidth: 2         # Пробелов в Табе
singleQuote: true   # Использовать одинарные кавычки
trailingComma: all  # Использовать запятые где возможно
arrowParens: always # Стрелочные ф-ии выглядят (x) => x
overrides:
  - files: "*.ts"   # Проверка файлов *.ts
    options:
      parser: typescript  # Язык в файлах *.ts

I dodao je naredbu: prettier --write --config .prettierr.yaml

Četvrti korak: A kako namjeravate sve ovo pokrenuti?

Sada pogledajmo pobliže kako sve ovo pokrenuti. Da bi ovo funkcioniralo, moramo preuzeti sljedeće biblioteke:

npm i -D prettier lint-staged husky

Koristeći husky, povezat ćemo naše naredbe s git hook-om—pre-commit. lint-staged će izvršavati naredbe za nas na temelju promijenjenih datoteka (i također će umetnuti te datoteke u naše naredbe).

Želio bih odmah opisati problem na koji sam naišao. U našem projektu koristimo ng lint. Kada ga koristimo zajedno s lint-staged, modificirane datoteke se dodaju našoj naredbi. ng lint ima prekidač za to. --files, ali koliko ja razumijem, ne može vidjeti hrpu datoteka i svakoj datoteci treba dodati ovaj ključ. Da bih to učinio, morao sam stvoriti datoteku:

lint.sh

#!/bin/bash

PROJECT=$1
shift
SOURCES=$@
DESTINATIONS=""
DELIMITER=""

for src in $SOURCES
do
    DELIMITER=" --files "
    DESTINATIONS="$DESTINATIONS$DELIMITER${src}"
done

ng lint $PROJECT --tslint-config ./tslint.json $DESTINATIONS

Za pokretanje ove datoteke, moramo proslijediti naziv projekta. Nalazi se u datoteci angular.json u svojstvu projekta. U mom slučaju, to je partner-account и partner-account-e2eTrebam prvi.

Vratimo se na postavljanje. Naš package.json sada izgleda ovako:

  "husky": {
    "hooks": {
      "pre-commit": "lint-staged --relative"
    }
  },
  "lint-staged": {
    "*.{ts,js}": [
      "prettier --write --config .prettierr.yaml",
      "tslint --project tslint.ide_only.json --fix --force",
      "sh lint.sh partner-account",
      "git add"
    ],
    "*.{html,scss,css}": [
      "prettier --write --config .prettierr.yaml",
      "git add"
    ]
  },

Napomena lint-staged --relative. Parametar --relative je tamo potreban. Sada, kada izvršimo commit, on se izvršava lint-stagedOn, pak, odabire datoteke i pokreće popis naredbi ovisno o njima.

Nažalost, ovo ne eliminira pregled koda, ali je postao puno čišći. Napominjem da sam postao manje sklon podsjećati programere na modifikatore pristupa, deklaracije metoda i svojstva, a njihov rad je postao dosljedan (pa, gotovo 😀).

PS - Hvala našoj privatnoj poruci za slike.

Izvor: www.habr.com

Kupite pouzdan hosting za stranice s DDoS zaštitom, VPS VDS poslužiteljima 🔥 Kupite pouzdan web hosting sa DDoS zaštitom, VPS VDS servere | ProHoster