Como eu coloco as coisas em ordem em um projeto em que há uma floresta de mãos diretas (configurações tslint, mais bonitas, etc.)

Olá novamente. Em contato Omelnitsky Sergey . Hoje vou compartilhar com vocês uma das minhas dores de cabeça, a saber, o que fazer quando muitos programadores multiníveis escrevem um projeto usando o exemplo de um aplicativo angular.



Aconteceu que, durante muito tempo, trabalhei apenas com minha equipe, onde há muito tempo tínhamos concordado com as regras de formatação, comentário, recuo etc. Eu me acostumei com eles e vivi feliz juntos. Para comemorar, eu até publiquei um artigo sobre Habr no nosso estilo de código . Portanto, de algo mágico, usamos apenas tslint no pré-commit.


E então nós crescemos. Um novo projeto com um código herdado apareceu, e para ele, além de novos desenvolvedores, na quantidade de 4 bons colegas. E mesmo aqui, não foi conforme o planejado.



Eu acho que muitas pessoas sabem que trabalhar com código legado não é alto. Na minha memória, recebi apenas um projeto do qual fiquei encantado e o resto ... Então, do que estou falando?) Ah, sim.


Francamente falando, a arquitetura do projeto deixou muito a desejar, e nós apenas sonhávamos em comentar e digitar. Em algum momento, fiquei triste pelo fato de nosso documento não funcionar de acordo com as regras de design, os comentários não terem sido escritos, tipo - o que é isso? Aqui era necessário fazer algo com isso.


Para quem não pode esperar para descobrir todas as etapas de uma só vez:
  • Dividimos o tslint em regras simples (para pré-confirmação) e regras rígidas (para ide, para que ele nos lembre o que os desenvolvedores se esqueceram de fazer)


  • Aguarde a correção automática de pré-confirmação de possíveis regras do tslint rígido


  • Escreveu regras para as mais bonitas


  • Dançou com um pandeiro para executar fiapos com fiapos



Etapa 1 - Dividir e conquistar


Quando tive a ideia de reforçar as regras do linter, pensei que nos enforcaríamos. O código é herdado. Você precisa entendê-lo e, nesse volume, você pode cavar. Decidiu-se criar um segundo linter para ide, o que seria uma dor de cabeça e força para escrever jsdoc para métodos e sv-c, interfaces de gravação ou onPush, etc.


Então, na raiz, começamos a colocar 2 arquivos 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 } } 

No src/tslint substituímos o tslint padrão por ide


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

E corrigimos o lançamento do nosso linter nos scripts do package.json


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

Depois disso, começamos a nos pendurar nas coisas sublinhadas que precisam ser corrigidas.


Etapa dois - corrija alguns pontos



Tslint tem regras com o has fixer . Então, vamos usá-lo.


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

Aqui, executamos as regras do linter rígido com correção automática dos parâmetros disponíveis e dizemos que este comando não retorna erros (aqui nosso objetivo ainda é fazer a correção automática).


Etapa três - escreva lindamente


Quando todo mundo escreve à sua maneira, acaba por se cansar. O código deve ser escrito para que isso seja feito por uma pessoa. Para isso, estraguei tudo, com as seguintes configurações:


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

E ele adicionou o comando: prettier --write --config .prettierr.yaml


Etapa quatro - e como você comanda tudo para executar?


Vamos agora dar uma olhada em como começar tudo isso. Para que isso funcione, precisamos fazer o download do seguinte:


 npm i -D prettier lint-staged husky 

Com o husky, penduraremos o lançamento de nossos comandos em um gancho do git - pre-commit. O lint-staged executará comandos para nós, dependendo dos arquivos alterados (também substitua esses arquivos nos comandos).


Eu também gostaria de descrever imediatamente o problema que encontrei. Em nosso projeto, usamos ng lint. Quando o usamos em conjunto com os arquivos modificados pelo lint, são adicionados ao nosso comando. Ng lint tem a chave --files para isso, mas, pelo que entendi, ele não vê muitos arquivos e precisa adicionar essa chave a cada arquivo. Para fazer isso, tive que criar um arquivo:


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 

Para executar este arquivo, devemos passar o nome do projeto. Ele está localizado no arquivo angular.json na propriedade do projeto. No meu caso, isso é partner-account e partner-account-e2e . Eu preciso de um primeiro.


Voltar para a configuração. Nosso package.json agora fica assim:


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

Preste atenção aos relativos lint-staged --relative . O parâmetro --relative é --relative lá. Agora, quando nos comprometemos, o lint-staged lançado. Ele, por sua vez, seleciona arquivos e inicia uma lista de comandos, dependendo deles.


Infelizmente, isso não cancela a revisão do código, mas ficou muito mais limpo. Observo que com menos frequência comecei a lembrar aos desenvolvedores sobre modificadores de acesso, uma descrição dos métodos e sv, e seu trabalho começou a ser escrito no mesmo estilo (bem, quase: D).


PS - Obrigado pelas fotos para o nosso PM.

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


All Articles