Hola de nuevo En contacto Omelnitsky Sergey . Hoy compartiré con ustedes uno de mis dolores de cabeza, a saber, qué hacer cuando muchos programadores multinivel escriben un proyecto utilizando el ejemplo de una aplicación angular.

Dio la casualidad de que durante mucho tiempo trabajé solo con mi equipo, donde habíamos acordado durante mucho tiempo las reglas para formatear, comentar, sangrar, etc. Me acostumbré a ellos y viví juntos felizmente. Para celebrarlo, incluso publiqué un artículo sobre Habr sobre nuestro estilo de código . Por lo tanto, de algo mágico, usamos solo tslint en el pre-commit.
Y luego crecimos. Apareció un nuevo proyecto con un código heredado y, además de los nuevos desarrolladores, por la cantidad de 4 buenos compañeros. E incluso aquí no fue según el plan.

Creo que mucha gente sabe que trabajar con código heredado no es alto. En mi memoria, recibí solo un proyecto del que estaba encantado, y el resto ... Entonces, ¿de qué estoy hablando?) Oh, sí.
Hablando francamente, la arquitectura en el proyecto dejó mucho que desear, y solo soñamos con comentar y escribir. En algún momento, me entristeció el hecho de que nuestro documento no funcionaba de acuerdo con las reglas de diseño, los comentarios no estaban escritos, escriba: ¿qué es esto?). Aquí era necesario hacer algo con esto.
Para aquellos que no pueden esperar para conocer todos los pasos a la vez:Dividimos tslint en reglas suaves (para pre-commit) y reglas duras (para ide, para que nos recuerde lo que los desarrolladores olvidaron hacer)
Espere la autofijación previa al compromiso de posibles reglas de tslint duro
Reglas escritas para más bonitas
Bailó con una pandereta para correr pelusa con pelusa puesta en escena
Paso uno: divide y vencerás
Cuando tuve la idea de ajustar las reglas de la peluquería, pensé que nos colgaríamos. El código es heredado. Necesitas entenderlo, y en tal volumen puedes cavar. Se decidió crear un segundo linter para ide, lo que sería una monstruosidad y una fuerza para escribir jsdoc para métodos y sv-c, escribir interfaces o onPush desafortunado, etc.
Entonces, en la raíz, comenzamos a colocar 2 archivos 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 } }
En el src/tslint
reemplazamos el tslint estándar con ide
src / tslint.json { "extends": "../tslint.ide_only.json", "rules": { "directive-selector": [ true, "attribute", "app", "camelCase" ], "component-selector": [ true, "element", "app", "kebab-case" ] } }
Y solucionó el lanzamiento de nuestro linter en los scripts de package.json
ng lint --tslint-config ./tslint.json --fix`
Después de lo cual comenzamos a colgarnos de las cosas subrayadas que deben corregirse.
Paso dos: arregla un par de puntos

Tslint tiene reglas con has fixer
. Así que usémoslo.
tslint --project tslint.ide_only.json --fix --force
Aquí ejecutamos las reglas del linter duro con autofijación de los parámetros disponibles y decimos que este comando no devuelve errores (aquí nuestro objetivo sigue siendo la autocorrección).
Paso tres: escribe bellamente
Cuando todos escriben a su manera, finalmente se cansa. El código debe estar escrito de manera que parezca que lo hace una persona. Para esto, atornillé más bonito, con la siguiente configuración:
.prettierr.yaml printWidth: 200 # - tabWidth: 2 # singleQuote: true # trailingComma: all # arrowParens: always # - (x) => x overrides: - files: "*.ts" # *.ts options: parser: typescript # *.ts
Y agregó el comando: prettier --write --config .prettierr.yaml
Paso cuatro - ¿Y cómo ordenas que se ejecute todo?
Ahora echemos un vistazo más de cerca a cómo comenzar todo esto. Para que esto funcione, necesitamos descargar lo siguiente:
npm i -D prettier lint-staged husky
Con Husky, colgaremos el lanzamiento de nuestros comandos en un gancho de git: pre-commit. lint-staged ejecutará comandos para nosotros en función de los archivos modificados (también los sustituiremos en los comandos).
También me gustaría describir de inmediato el problema que encontré. En nuestro proyecto usamos ng lint. Cuando lo usamos junto con lint-staged, los archivos modificados se agregan a nuestro comando. Ng lint tiene la clave --files
para esto, pero según tengo entendido, no ve un montón de archivos, y necesita agregar esta clave a cada archivo. Para hacer esto, tuve que crear un archivo:
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 ejecutar este archivo, debemos pasar el nombre del proyecto. Se encuentra en el archivo angular.json
en la propiedad del proyecto. En mi caso, esto es partner-account
y partner-account-e2e
. Necesito un 1er.
Volver a la configuración. Nuestro package.json ahora se ve así:
"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 atención a la lint-staged --relative
. El parámetro --relative
se --relative
allí. Ahora, cuando nos comprometemos, lint-staged
lanza lint-staged
. Él, a su vez, selecciona archivos e inicia una lista de comandos dependiendo de ellos.
Desafortunadamente, esto no cancela la revisión del código, pero se ha vuelto mucho más limpia. Noto que con menos frecuencia comencé a recordarles a los desarrolladores sobre modificadores de acceso, una descripción de los métodos y sv, y su trabajo comenzó a escribirse en el mismo estilo (bueno, casi: D).
PD: Gracias por las fotos a nuestro primer ministro.