Comment je mets les choses en ordre dans un projet où il y a une forêt de mains directes (paramètres tslint, plus joli, etc.)

Bonjour encore. En contact avec Omelnitsky Sergey . Aujourd'hui, je vais partager avec vous l'un de mes maux de tête, à savoir ce qu'il faut faire lorsque de nombreux programmeurs à plusieurs niveaux écrivent un projet en utilisant l'exemple d'une application angulaire.



Il se trouve que pendant longtemps je n'ai travaillé qu'avec mon équipe, où nous nous étions longtemps entendus sur les règles de formatage, de commentaire, d'indentation, etc. Je me suis habitué à eux et j'ai vécu heureux ensemble. Pour fêter ça, j'ai même publié un article sur Habr sur notre style de code . Par conséquent, à partir de quelque chose de magique, nous n'avons utilisé que tslint sur le pré-commit.


Et puis nous avons grandi. Un nouveau projet avec un code hérité est apparu, et pour lui, en plus de nouveaux développeurs, au nombre de 4 bons camarades. Et même ici, cela ne s'est pas passé comme prévu.



Je pense que beaucoup de gens savent que travailler avec du code hérité n'est pas élevé. Dans ma mémoire, je n'ai reçu qu'un seul projet dont j'ai été ravi, et le reste ... Alors de quoi je parle?) Oh oui.


Franchement, l'architecture du projet laissait beaucoup à désirer, et nous ne rêvions que de commenter et de taper. À un moment donné, j'ai été attristé par le fait que notre document ne fonctionnait pas selon les règles de conception, les commentaires n'ont pas été écrits, tapez - qu'est-ce que c'est?). Ici, il fallait faire quelque chose avec ça.


Pour ceux qui ont hâte de découvrir toutes les étapes à la fois:
  • Nous avons divisé tslint en règles souples (pour le pré-commit) et règles strictes (pour ide, afin que cela nous rappelle ce que les développeurs ont oublié de faire)


  • Suspendre l'autofixation pré-validation des règles possibles de hard tslint


  • A écrit des règles pour plus joli


  • Dansé avec un tambourin pour exécuter des peluches avec des peluches



Première étape - Diviser pour mieux régner


Quand j'ai eu l'idée de resserrer les règles du linter, j'ai pensé qu'on allait se pendre. Le code est hérité. Vous devez le comprendre, et dans un tel volume, vous pouvez creuser. Il a été décidé de créer un 2ème linter pour ide, ce qui serait une horreur et forcerait d'écrire jsdoc pour les méthodes et sv-c, d'écrire des interfaces ou de malheureux onPush, etc.


Donc, à la racine, nous avons commencé à déposer 2 fichiers tslin:


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 } } 

Dans le src/tslint nous avons remplacé le tslint standard par ide


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

Et corrigé le lancement de notre linter dans les scripts de package.json


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

Après quoi, nous avons commencé à nous accrocher aux choses soulignées qui doivent être corrigées.


Deuxième étape - fixer quelques points



Tslint a des règles avec has fixer . Alors utilisons-le.


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

Ici, nous exécutons les règles du linter dur avec autofixation des paramètres disponibles et disons que cette commande ne renvoie pas d'erreurs (ici, notre objectif est toujours de faire une auto-correction).


Troisième étape - écrivez magnifiquement


Quand chacun écrit à sa manière, ça finit par se fatiguer. Le code doit être écrit de sorte qu'il semble que cela soit fait par une seule personne. Pour cela, j'ai vissé plus joli, avec les réglages suivants:


.prettierr.yaml
 printWidth: 200 #  -    tabWidth: 2 #    singleQuote: true #    trailingComma: all #     arrowParens: always #  -  (x) => x overrides: - files: "*.ts" #   *.ts options: parser: typescript #    *.ts 

Et il a ajouté la commande: prettier --write --config .prettierr.yaml


Quatrième étape - Et comment ordonnez-vous que tout fonctionne?


Voyons maintenant de plus près comment démarrer tout cela. Pour que cela fonctionne, nous devons télécharger les éléments suivants:


 npm i -D prettier lint-staged husky 

Avec husky, nous allons suspendre le lancement de nos commandes sur un crochet git - pré-commit. lint-staged exécutera des commandes pour nous en fonction des fichiers modifiés (substituez également ces fichiers pour nous dans les commandes).


Je voudrais également souligner immédiatement le problème que j'ai rencontré. Dans notre projet, nous utilisons ng lint. Lorsque nous l'utilisons en conjonction avec lint-staged, des fichiers modifiés sont ajoutés à notre commande. Ng lint a la clé --files pour cela, mais si je comprends bien, il ne voit pas un tas de fichiers, et il doit ajouter cette clé à chaque fichier. Pour ce faire, j'ai dû créer un fichier:


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 

Pour exécuter ce fichier, nous devons transmettre le nom du projet. Il se trouve dans le fichier angular.json dans la propriété du projet. Dans mon cas, il s'agit partner-account partner-account-e2e et du partner-account partner-account-e2e . J'ai besoin d'un 1er.


Retour à la configuration. Notre package.json ressemble maintenant à ceci:


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

Faites attention aux lint-staged --relative . Le paramètre --relative est --relative . Maintenant, lorsque nous nous engageons, le lint-staged lancé. À son tour, il sélectionne les fichiers et démarre une liste de commandes en fonction d'eux.


Malheureusement, cela n'annule pas la révision du code, mais il est devenu beaucoup plus propre. Je note que j'ai moins souvent commencé à rappeler aux développeurs les modificateurs d'accès, une description des méthodes et des sv, et leur travail a commencé à être écrit dans le même style (enfin presque: D).


PS - Merci pour les photos à notre PM.

Source: https://habr.com/ru/post/fr464383/


All Articles