Kuidas ma korda tõin projektis, kus oli palju otsekäsi (tslint, prettier jne.)

Ja jälle tere. Olen kohal Omelnički Sergei. Täna jagan teiega üht oma peavaludest, nimelt - mida teha, kui projekti kirjutavad mitmed eri tasemete programmid, kasutades Angulari rakendust.

Kuidas ma korda tõin projektis, kus oli palju otsekäsi (tslint, prettier jne.)

Nii on juhtunud, et olen pikka aega töötanud ainult oma meeskonnaga, kus me olime juba kaua kokku leppinud vormindamisreeglites, kommentaarides, taande ja nii edasi. Me harjusime nendega ja elasime õnnelikult. Rõõmustades avaldasin isegi artikli Habr alusel meie koodistiil. Seetõttu kasutasime mingit maagilist lahendust, et kasutada ainult tslint'i pre-commit'is.

Ja siis kasvasime suureks. Ilmus uus projekt päritud koodiga ja sellele lisandusid uued arendajad, kokku neli head noormeest. Ja siis läks kõik valesti.

Kuidas ma korda tõin projektis, kus oli palju otsekäsi (tslint, prettier jne.)

Ma arvan, et paljud teavad, et töö pärinud koodiga ei ole meeldiv. Minu mälestustes olen saanud ühe projekti, millest olin vaimustuses, kõik muu... Nii et, millest ma rääkisin?) Ah jah.

Ausalt öeldes jätab projekti arhitektuur soovida, ja kommentaarid ja tüübistamine olid vaid unistus. Ühel hetkel olin ma mures, et meie dokumentatsioon vormindamise reeglites ei toimi, kommentaare ei kirjutata, tüüp - mis see on?). Sellega oli midagi ette võtta.

Need, kellel on kiusatus kohe kõik sammud teada saada:

  • Me jagasime tslint'i pehmete reegliteks (pre-commit jaoks) ja karmide reegliteks (ide jaoks, et meeles pidada, et arendajad on unustanud teha)

  • Seadisime pre-commit'ile automaatse parandusvõime karmide tslint'i reeglite jaoks

  • Kirjutasime reeglid prettier'i jaoks

  • Tantsisime bubi ümber, et käivitada ng lint koos lint-staged'iga

Esimene samm - jagage ja valitsege

Kui mul tuli idee karmistada linte, mõtlesin, et me läheme hulluks. Kood on päritud. Sellega tuleb tegeleda, ja sellises mahus võib kinni jääda. Tehti otsus luua teine linte ide jaoks, mis ärritaks silmi ja sundiks kirjutama jsdoc meetodite ja omaduste jaoks, kirjutama liideseid või kurikuulus onPush jne.

Nii et meie juures on kaks tslint'i faili:

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": "Kasutage cloneDeep (lodash) objekti kopeerimiseks"
      }
    ],
    "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
  }
}

Failis src/tslint me oleme asendanud tavapärase tslint ide-ga

src/tslint.json

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

И поправил запуск нашего линтера в скритах package.json

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

Pärast seda hakkasime saama rõhutatult asju, mida tuleb parandada.

Teine etapp — paranda paar punkti

Kuidas ma korda tõin projektis, kus oli palju otsekäsi (tslint, prettier jne.)

Tslint'il on reeglid, millel on fiksaator. Nii et kasutame seda.

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

Siin käivitame rangete linneerimise reeglid automaatse fikseerimise võimalustega ja ütleme, et see käsk ei naasaks vigu (siin on meie eesmärk siiski teha automaatparandust).

Kolmas etapp — kirjuta ilusasti

Kui igaüks kirjutab oma moodi, väsyb see lõpuks. Koodi tuleb kirjutada nii, et näeks välja nagu ühe inimese töö. Selleks lisasin prettier'i järgmiste seadistustega:

.prettierr.yaml

printWidth: 200     # Maximalne märkide arv reas
tabWidth: 2         # Tabi tühikute arv
singleQuote: true   # Kasutada ühekordseid jutumärke
trailingComma: all  # Kasutada komasid, kus võimalik
arrowParens: always # Noolefunktsioonid näivad (x) => x
overrides:
  - files: "*.ts"   # Kontrollida *.ts failide
    options:
      parser: typescript  # Keel *.ts failides

Ja lisasin käsu: prettier --write --config .prettierr.yaml

Neljas etapp — Kuidas sa käsid seda kõike käivitada?

Vaatame nüüd lähemalt, kuidas kõike seda käivitada. Selleks, et see kõik töötaks, peame alla laadima järgmised teegid:

npm i -D prettier lint-staged husky

Husky abil seome meie käskude käivitamise git'i hookiga — pre-commit. lint-staged käivitab meie käsud sõltuvalt muudatustest failides (samuti lisades need failid meie käskudesse).

Tahaksin kohe tõstatada probleemi, millega ma silmitsi seisime. Meie projektis kasutame ng lint'i. Kui kasutame seda lint-staged'iga, lisatakse meie käsule muudetud failid. Ng lint'il on selle jaoks võti --files, kuid nagu ma aru sain, ei näe ta mappe ning tal on vaja iga faili jaoks sellele võtmele tugi lisada. Selleks tuli mul luua fail:

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

Selle faili käivitamiseks peame edastama projekti nime. See asub failis angular.json veerg project. Minu puhul on see partner-account ja partner-account-e2e. Mul on vaja esimest.

Tulen tagasi seadistuse juurde. Meie package.json näeb nüüd välja nii:

  "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"
    ]
  },

Pange tähele lint-staged --relative. Parameeter --relative on on kohustuslik. Nüüd, kui me commiti teeme, käivitatakse meil lint-staged. See valib omakorda failid ja käivitab nende põhjal käsud.

Kahjuks ei tühista see koodi ülevaatust, kuid see on muutunud palju puhtamaks. Märgin, et ma olen harvemini pidanud meelde tuletama arendajatele ligipääsu modifikaatoreid, meetodite ja omaduste kirjeldusi, ning nende looming on kirjutatud ühtses stiilis (noh, peaaegu 😀).

P.S. — Aitäh piltide eest meie PM-ile.

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster