Hur jag organiserade projektet, dÀr skogen av fria hÀnder (instÀllningar för tslint, prettier, etc.)

Och Äterigen, hej. HÀr Àr vi Serhij Omelnytskyj. Idag kommer jag att dela med mig av en av mina huvudvÀrkare, nÀmligen - vad man ska göra nÀr ett projekt skrivs av mÄnga programmerare pÄ olika nivÄer, med exempel pÄ en Angular-applikation.

Hur jag organiserade projektet, dÀr skogen av fria hÀnder (instÀllningar för tslint, prettier, etc.)

Det har blivit sÄ, att jag under lÄng tid endast har arbetat med mitt team, dÀr vi lÀnge har enats om regler för formatering, kommentering, indrag och sÄ vidare. Vi har blivit vana vid dem och levt i harmoni och lycka. Av glÀdje publicerade jag till och med en artikel pÄ Habr om vÄr kodstandard. SÄ vi anvÀnde nÄgot magiskt, bara tslint vid pre-commit.

Och hÀr vÀxte vi. En ny projektt med Àrvd kod dök upp, och till det en ny grupp utvecklare - fyra duktiga killar. Och hÀr började det gÄ snett.

Hur jag organiserade projektet, dÀr skogen av fria hÀnder (instÀllningar för tslint, prettier, etc.)

Jag tror mĂ„nga vet att det inte Ă€r roligt att arbeta med Ă€rvd kod. Av det jag minns har jag bara fĂ„tt ett projekt som jag tyckte om, resten... SĂ„ vad pratade jag om?) Åh ja.

Ärligt talat, arkitekturen i projektet lĂ€mnade mycket kvar att önska, och kommentarer och typning var nĂ€stan en dröm. Vid nĂ„got tillfĂ€lle kĂ€nde jag mig nedslagen av att vĂ„r dokumentation om formateringsreglerna inte fungerade, kommentarer skrivs inte, typ - vad Ă€r detta?). HĂ€r var det dags att göra nĂ„got.

För den som inte kan vÀnta med att fÄ veta alla steg i förvÀg:

  • Vi delade upp tslint i mjuka regler (för pre-commit) och strikta regler (för ide, för att pĂ„minna om vad utvecklarna glömde göra)

  • Vi lade till automatisk fixering för möjliga regler frĂ„n den strikta tslint vid pre-commit

  • Vi skrev regler för prettier

  • Vi dansade med trumman för att fĂ„ igĂ„ng ng lint med lint-staged

Steg ett - dela och hÀrska

NÀr jag fick idén att skÀrpa linterns regler tÀnkte jag att vi skulle hÀnga oss. Koden Àr ju Àrvd. Man behöver förstÄ den, och i sÄdan omfattning kan man fastna. Beslutet fattades att skapa en andra linter för ide, som skulle irritera ögonen och fÄ folk att skriva jsdoc för metoder och egenskaper, skriva grÀnssnitt eller det olyckliga onPush, och sÄ vidare.

SÄ i roten lÄg det tvÄ tslint-filer:

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": "AnvÀnd cloneDeep (lodash) för att kopiera objekt"
      }
    ],
    "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
  }
}

I filen src/tslint Vi har ersatt standard tslint med ide

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`

Efter det började vi fÄ problem med understrukna saker som behövde rÀttas.

Steg tvĂ„ — fixa ett par punkter

Hur jag organiserade projektet, dÀr skogen av fria hÀnder (instÀllningar för tslint, prettier, etc.)

Tslint har regler för har fixer. SÄ lÄt oss anvÀnda det.

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

HÀr kör vi hÄrda linter-regler med automatisk korrigering för tillgÀngliga parametrar och sÀger att det hÀr kommandot inte ska returnera nÄgra fel (vÄr mÄlsÀttning Àr trots allt att göra automatiska korrigeringar).

Steg tre — skriv vackert

NÀr varje person skriver pÄ sitt eget sÀtt blir det tröttande i slutÀndan. Koden bör skrivas sÄ att det verkar som om en enda person gör det. För detta har jag lagt till prettier med följande instÀllningar:

.prettierr.yaml

printWidth: 200     # Maximalt antal tecken per rad
tabWidth: 2         # mellanslag i tab
singleQuote: true   # AnvÀnda enkla citattecken
trailingComma: all  # AnvÀnda komman dÀr det Àr möjligt
arrowParens: always # Pilfunktioner ser ut som (x) => x
overrides:
  - files: "*.ts"   # Kontroll av *.ts-filer
    options:
      parser: typescript  # SprÄk i *.ts-filer

Och jag lade till kommandot: prettier --write --config .prettierr.yaml

Steg fyra — Och hur tĂ€nker du köra allt detta?

LÄt oss nu gÄ igenom hur man kör allt detta detaljerat. För att det ska fungera behöver vi ladda ner följande bibliotek:

npm i -D prettier lint-staged husky

Med husky kopplar vi körningen av vĂ„ra kommandon till git-hooks — pre-commit. lint-staged kommer att köra vĂ„ra kommandon beroende pĂ„ Ă€ndrade filer (och ocksĂ„ inkludera dessa filer i vĂ„ra kommandon).

Jag skulle ocksÄ vilja belysa ett problem som jag stötte pÄ. I vÄrt projekt anvÀnder vi ng lint. NÀr vi anvÀnder det tillsammans med lint-staged, lÀggs de Àndrade filerna till i vÄrt kommando. Ng lint har en flagga för detta --files, men som jag förstod det, ser den inte ett paket av filer och den behöver den hÀr flaggan lÀggas till för varje fil. För detta var jag tvungen att skapa en fil:

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

För att köra den hÀr filen mÄste vi ange projektets namn. Det finns i filen angular.json i projektattributet. I mitt fall Àr det partner-account och partner-account-e2e. Jag behöver den första.

Tillbaka till instÀllningen. VÄr package.json ser nu ut sÄ hÀr:

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

Observera lint-staged --relative. Parametern --relative Àr obligatorisk. Nu körs lint-staged. Den plockar ut filerna och kör beroende pÄ dem en lista med kommandon.

TyvĂ€rr upphĂ€ver detta inte kodgranskningen, men den har blivit mycket renare. Jag mĂ€rker att jag numera sĂ€llan pĂ„minner utvecklarna om Ă„tkomstmodifierare, metodbeskrivningar och attribut, och deras kreativitet har skrivits i en enhetlig stil (nĂ€stan 😀 ).

P.S. — Tack för bilderna till vĂ„r PM.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster