Tere jälle. Siin on . Täna jagan teiega ühte oma peavalu — mis teha, kui projekti kirjutavad erineva tasemega programmeerijad Angulari rakenduse näitel.

Nii on olnud, et pikka aega töötasin ainult oma tiimiga, kus olime juba ammu kokku leppinud vormindamisreeglites, kommentaarides, tosundamisest jne. Olime nendega harjunud ja elasime õnnelikult. Ühe rõõmsa hetkega avaldasin isegi artikli Habbri kohta meie koodistiilist. . Seetõttu kasutasime mingeid maagilisi asju ainult tslinti eelkohustuses.
Ja siis kasvasime. Ilmus uus projekt pärandkoodiga, millele lisandus neli head arendajat. Ja hakkasime asju valesti ajama.

Ma arvan, et paljud teavad, et pärandkoodiga töötamine ei ole tore. Minu mäletamise järgi sain vaid ühe projekti, mis tõeliselt meeldis, kõik muu... Nii, millest ma rääkisin? Ah jah.
Aus tõtt, projekti arhitektuur jättis soovida, samuti ei olnud meil kommentaare ega tüübi määratlemist. Mingil hetkel tundsin ma pettumust, et meie dokumentatsioon vormistamise reeglite järgi ei tööta, kommentaare ei kirjutata, ja tüüp — mis see on?). Sellega tuli midagi ette võtta.
Neile, kes ei jõua ära oodata kõiki samme korraga:
Oleme jaganud tslint pehme ja ranged reeglid (pre-commit jaoks) ja ranged reeglid (ide jaoks, et meenutada, mida arendajad unustasid teha)
Seadsime pre-commitile automaatfikseerimise võimalike ranged tslint reeglite jaoks
Kirjutasime reeglid prettier'i jaoks
Tantsisime, et käivitada ng lint lint-staged'i abil
Esimene samm — jagada ja valitseda
Kui mul tuli mõte karmistada linter'i reegleid, tundsin, et me uppume. Kood on ju pärand, millega tuleb tegeleda, ja sellises mahus võib see väga keeruliseks osutuda. Otsustati luua teine linter ide jaoks, mis häiriks silmi ja sundiks kirjutama jsdoc'i meetodite ja omaduste jaoks, kirjutama liideseid või kurikuulsat onPush jne.
Nii et meil on põhja all 2 tslin 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), et objekti kopeerida"
}
],
"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 meie asendasime tavalise 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 tegelema allpool olevate aspektidega, mida on vaja parandada.
Teine samm — paranda paar asja

tslint'il on reeglid, mis omavad fikseerijat. Nii et võtame selle kasutusele.
tslint --project tslint.ide_only.json --fix --forceSiin käivitame rangete lintimise reeglid automaatsete parandusvõimalustega ja ütleme, et see käsk ei tagastaks vigu (meie eesmärk on loomulikult automaatne parandamine).
Kolmas samm — kirjuta kenasti
Kuna igaüks kirjutab omamoodi, väsitab see lõpuks. Kood tuleb kirjutada nii, et näiliselt teeb seda üks inimene. Selleks lisasin prettier'i, järgnevate seadistustega:
.prettierr.yaml
printWidth: 200 # Rida maksimaalne tähemärkide arv
tabWidth: 2 # Tabi tühikute arv
singleQuote: true # Kasutada ühetähelisi jutumärke
trailingComma: all # Kasutada komasid, kus võimalik
arrowParens: всегда # Noolefunktsioonid on kujul (x) => x
overrides:
- files: "*.ts" # Kontrolli faile *.ts
options:
parser: typescript # Keel failides *.tsJa lisasin käsu: prettier --write --config .prettierr.yaml
Neljas samm — Kuidas sa selle kõik käivitad?
Vaatame nüüd lähemalt, kuidas me need asi käima saame. Selleks, et kõik toimiks, peame alla laadima järgmised teegid:
npm i -D prettier lint-staged huskyHusky abil ühendame meie käskude käivitamise git-kongiga — pre-commit. lint-staged käivitab meie käsud sõltuvalt muudetud failidest (samuti lisatakse need failid meie käskudesse).
Tahaksin ka kohe kirjeldada probleemi, millega ma silmitsi seisin. Meie projektis kasutame ng lint. Kui kasutame seda koos lint-staged'iga, siis lisatakse meie käsule muudetud failid. Ng lint'il on selle jaoks lipp --files, kuid, nagu ma aru sain, ei näe ta failipaketti ja vajab selle lipu lisamist iga fail voor. Selle jaoks pidin ma looma faili:
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 $DESTINATIONSSelle faili käivitamiseks peame edastama projekti nime. See asub failis angular.json omandis project. Minu puhul on see partner-account ja partner-account-e2e. Mul on vaja 1.
Tulge tagasi seadistamise juurde. Meie package.json näeb nüüd välja selline:
"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 kohustuslik. Nüüd käivitame me kommitamise ajal lint-staged. See valib omakorda failid ja käivitab sõltuvalt neist käskude nimekirja.
Kahjuks ei vabasta see koodikontrollist, kuid see on muutunud palju selgemaks. Tahan märkida, et ma räägin arendajatele sagedamini juurdepääsusmodifikaatoritest, meetodite ja omaduste kirjeldamisest, ja nende looming on kirjutatud ühtses stiilis (noh, peaaegu 😀 ).
P.S. — Aitäh meie PM-le piltide eest.
Allikas: habr.com
