Üdvözlök mindenkit újra. Itt van . Ma megosztom Önökkel az egyik fejfájásomat, nevezetesen, mit kell tenni, amikor sok különböző szintű programozó ír egy projektet, például egy angular alkalmazás esetében.

Ez így alakult, hogy hosszú ideig csak a saját csapatommal dolgoztam, ahol már régóta megegyeztünk a megjelenítési, kommentálási és behúzási szabályokban, stb. Hozzászoktunk ehhez, és boldogan éltünk. Az örömömben még írtam egy cikket a Habr-ra a . Ezért valami varázslatos okból csak a tslint-t használtuk pre-commit előtt.
És itt egyszer csak megnőttünk. Megjelent egy új projekt örökölt kóddal, és ehhez négy új fejlesztő is csatlakozott. És itt valami nem ment a tervek szerint.

Sokan tudják, hogy az örökölt kóddal való munka nem egy élvezet. Amennyire emlékszem, csak egy projekt volt, amire fel voltam dobva, a többi… Szóval miről is beszélek? Ja, igen.
Őszintén szólva a projekt architektúrája hagyott kívánnivalót maga után, és a kommentárok és a típusok, nos, azokat csak álmodtuk. Egy pillanatban elkeseredtem attól, hogy a dokumentációnk a bemutatási szabályokról nem működik, nem írják a kommentárokat, a típus - mi is ez?). Ezzel valamit kezdeni kellett.
Aki alig várja, hogy az összes lépést azonnal tudja:
A tslint-et lágy és kemény szabályokra osztottuk (pre-commit-ra) és (ide-ra, hogy emlékeztesse a fejlesztőket, hogy mit felejtettek el csinálni)
Az előzetes kötelező auto-fixálásokat alkalmaztuk a kemény tslint szabályaiból
Szabályokat írtunk a prettier-hez
Táncoltunk a bűbájjal, hogy elindítsuk az ng lint-et a lint-staged-dal
Első lépés - oszd meg és uralkodj
Amikor eszembe jutott a linterszabályok szigorítása, azt gondoltam, hogy meg fogunk őrülni. A kód örökölt volt. Ezt meg kell érteni, és ekkora terjedelemben el lehet veszni. Döntés született, hogy egy második lintert készítünk az ide-nek, amely folyamatosan figyelmeztetne a szemem előtt, és rávenne, hogy jsdoc-ot írjak a módszerekhez és attribútumokhoz, interfészeket vagy a balszerencsés onPush-t stb.
Tehát a gyökérben két tslint fájl kezdete volt:
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": "Használj cloneDeep-et (lodash) az objektum másolásához"
}
],
"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
}
}A fájlban src/tslint A szokásos tslint-t a ide-re cseréltük.
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`Ezek után a kiemelt dolgoktól kezdünk el akadozni, amelyeket ki kell javítani.
Második lépés — néhány dolgot ki kell javítani

A tslint-nek vannak szabályai a has fixer. Tehát használjuk őket.
tslint --project tslint.ide_only.json --fix --forceItt futtatjuk a szigorú linter szabályait, automatikusan kijavítva a lehetséges paramétereket, és azt mondjuk, hogy ez a parancs ne adjon vissza hibákat (a célunk végül is a hibák automatikus javítása).
Harmadik lépés — írj szépen
Amikor mindenki a saját stílusában ír, az végül is fárasztó. A kódot úgy kell írni, hogy úgy érezzük, mintha egy ember csinálná. Ehhez csatlakoztattam a prettiert az alábbi beállításokkal:
.prettierr.yaml
printWidth: 200 # Maximális karakterek száma egy sorban
tabWidth: 2 # Tabok száma
singleQuote: true # Használjunk egyszeres idézőjeleket
trailingComma: all # Használjunk vesszőket, ahol lehetséges
arrowParens: always # A nyíl f-ák így néznek ki: (x) => x
overrides:
- files: "*.ts" # Ellenőrzés *.ts fájloknál
options:
parser: typescript # A nyelv a *.ts fájlokbanÉs hozzáadtam a parancsot: prettier --write --config .prettierr.yaml
Negyedik lépés — És hogyan parancsolod, hogy mindezt futtassuk?
Nézzük most részletesebben, hogyan futtassuk mindezt. Ahhoz, hogy minden jól működjön, le kell töltenünk a következő könyvtárakat:
npm i -D prettier lint-staged huskyA husky segítségével a parancsainkat a git hurokra felfüggesztjük — pre-commit. A lint-staged a parancsainkat az módosított fájlok alapján futtatja (alapján behelyettesíti ezeket a fájlokat a parancsainkba).
Szeretném még azonnal felvázolni a problémát, amivel szembesültem. A projektünkben használjuk az ng lint-et. Amikor ezt a lint-staged-del kombinálva használjuk, akkor a parancsunkhoz hozzáadódnak a módosított fájlok. Az ng lint-nek van erre egy kulcsa --files, de ahogy megértettem, nem látja a fájlok halmazát, és minden fájlhoz hozzá kell adni ezt a kulcsot. Ezért létre kellett hoznom egy fájlt:
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 $DESTINATIONSEnnek a fájlnak a futtatásához meg kell adnunk a projekt nevét. Ez a fájlban található. angular.json a project tulajdonságban. Az én esetemben ez partner-account és partner-account-e2e. Az elsőra van szükségem.
Visszatérek a beállításhoz. A package.json fájlunk most így néz ki:
"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"
]
},Kérjük, vegye figyelembe lint-staged --relative. A paraméter --relative ez kötelező. Most, amikor elkötelezünk, elindítjuk a lint-staged. Ez pedig kiválasztja a fájlokat, és attól függően futtatja a parancsok listáját.
Sajnos ez nem zárja ki a kódellenőrzést, de sokkal tisztábbá vált. Megjegyzem, hogy ritkábban emlékeztetem a fejlesztőket a hozzáférési módosítókra, a metódusok és tulajdonságok leírására, és az ő kreativitásuk egy egységes stílusban íródott (nos, szinte 😀 ).
P.S. — Köszönet a képekért a PM-nek.
Forrás: habr.com
