๋ค์ ์๋ ํ์ธ์. ์ฐ๋ฝํ๋ค . ์ค๋์ ์ ๊ฐ ๊ณจ์น ์ํ ๋ฌธ์ ์ค ํ๋, ์ฆ Angular ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ฅผ ์ฌ์ฉํ์ฌ ๋ง์ ๋ค๋จ๊ณ ํ๋ก๊ทธ๋๋จธ๊ฐ ํ๋ก์ ํธ๋ฅผ ์์ฑํ ๋ ์ด๋ป๊ฒ ํด์ผ ํ๋์ง์ ๋ํด ๊ณต์ ํ๊ฒ ์ต๋๋ค.

๊ทธ๋์ ๋๋ ์ค๋ซ๋์ ์์, ์ฃผ์ ๋ฌ๊ธฐ, ๋ค์ฌ์ฐ๊ธฐ ๋ฑ์ ๊ท์น์ ๋ํด ์ค๋ซ๋์ ํฉ์ํด ์จ ์ฐ๋ฆฌ ํ๊ณผ๋ง ์ผํ์ต๋๋ค. ๊ทธ๋ค์ ๊ทธ๋ค์๊ฒ ์ต์ํด์ก๊ณ ์ฐํธ์ ์ด๊ณ ํ๋ณตํ๊ฒ ์ด์์ต๋๋ค. ์ด๋ฅผ ์ถํํ๊ธฐ ์ํด Habr์ ๊ธฐ์ฌ๋ฅผ ๊ฒ์ฌํ๊ธฐ๋ ํ์ต๋๋ค. . ๋ฐ๋ผ์ ๋ง๋ฒ๊ฐ์ ์ผ์ ํ๊ธฐ ์ํด ์ฐ๋ฆฌ๋ ์ฌ์ ์ปค๋ฐ์์๋ง tslint๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ๋ ์ฑ์ฅํ์ต๋๋ค. ๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ ์๋ก์ด ํ๋ก์ ํธ๊ฐ ๋ฑ์ฅํ๊ณ , ๊ทธ ์ธ์๋ 4๋ช ์ ์ข์ ๋๋ฃ๋ค์ด ์๋ก์ด ๊ฐ๋ฐ์๋ก ํฉ๋ฅํ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ผ์ด ๊ณํ๋๋ก ์งํ๋์ง ์์์ต๋๋ค.

๋ง์ ์ฌ๋๋ค์ด ๋ ๊ฑฐ์ ์ฝ๋๋ก ์์ ํ๋ ๊ฒ์ด ์ฌ๋ฏธ์๋ค๋ ๊ฒ์ ์๊ณ ์๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ ๊ธฐ์ต์ผ๋ก๋ ๊ธฐ๋ปค๋ ํ๋ก์ ํธ๊ฐ ๋ฑ ํ๋ ์์๋๋ฐ ๋๋จธ์ง๋... ๊ทธ๋ผ ๋ฌด์จ ๋ง์ ํ๋ ๊ฑธ๊น์?) ์ ๊ทธ๋ ๊ตฐ์.
์์งํ ๋งํด์ ํ๋ก์ ํธ์ ์ํคํ ์ฒ๋ ์์ฌ์ด ์ ์ด ๋ง์๊ณ ์ฐ๋ฆฌ๋ ๋๊ธ๊ณผ ํ์ดํ๋ง์ ๊ฟ๊ฟจ์ต๋๋ค. ์ด๋ ์์ ์์ ๋๋ ๋์์ธ ๊ท์น์ ๋ฐ๋ฅธ ๋ฌธ์๊ฐ ์๋ํ์ง ์๊ณ ๋๊ธ์ด ์์ฑ๋์ง ์์๊ธฐ ๋๋ฌธ์ ์ฐ์ธํด์ก์ต๋๋ค. "์ด๊ฒ ๋ญ์ฃ ?). ์ด์ ๋ํด ๋ญ๊ฐ ์กฐ์น๋ฅผ ์ทจํด์ผ ํ์ต๋๋ค.
๋ชจ๋ ๋จ๊ณ๋ฅผ ํ ๋ฒ์ ๋ฐฐ์ฐ๊ณ ์ถ์ ๋ถ๋ค์ ์ํด:
์ฐ๋ฆฌ๋ tslint๋ฅผ ์ํํธ ๊ท์น(์ฌ์ ์ปค๋ฐ์ฉ)๊ณผ ํ๋ ๊ท์น(๊ฐ๋ฐ์๊ฐ ์์ด๋ฒ๋ฆฐ ์์ ์ ์๊ธฐ์ํค๊ธฐ ์ํด)์ผ๋ก ๋๋์์ต๋๋ค.
ํ๋ tslint์์ ์ฌ์ ์ปค๋ฐ๊น์ง ๊ฐ๋ฅํ ๊ท์น์ ์๋ ์์ ์ ์ถ๊ฐํ์ต๋๋ค.
๋ ์๋ป์ง๊ธฐ ์ํ ๊ท์น์ ์ผ์ต๋๋ค
์ฐ๋ฆฌ๋ ํฌ๋ฒ๋ฆฐ๊ณผ ํจ๊ป ์ถค์ ์ถ๋ฉฐ lint-staged๋ก ng lint๋ฅผ ๋ฐ์ฌํ์ต๋๋ค.
XNUMX๋จ๊ณ - ๋ถ์ด๊ณผ ์ ๋ณต
๋ฆฐํฐ์ ๊ท์น์ ๊ฐํํ์๋ ์๊ฐ์ด ๋ค์์ ๋ ๋๋ ์ฐ๋ฆฌ๊ฐ ๋ชฉ์ ๋งค์ผ๊ฒ ๋ค๊ณ ์๊ฐํ์ต๋๋ค. ์ฝ๋๋ ๋ ๊ฑฐ์์ ๋๋ค. ๋น์ ์ ๊ทธ๊ฒ์ ์ดํดํด์ผํ์ง๋ง ๊ทธ๋ฌํ ๋ณผ๋ฅจ์์๋ ๋น์ ์์ ์ ๋ฌป์ ์ ์์ต๋๋ค. ide์ ๋ํ ๋ ๋ฒ์งธ linter๋ฅผ ๋ง๋ค๊ธฐ๋ก ๊ฒฐ์ ํ๋๋ฐ, ์ด๋ ๋์ ๊ฑฐ์ฌ๋ฆฌ๊ณ ๋ฉ์๋ ๋ฐ ์์ฑ์ ๋ํด jsdoc๋ฅผ ์์ฑํ๊ณ , ์ธํฐํ์ด์ค ๋๋ ๋ถํํ onPush ๋ฑ์ ์์ฑํ๋๋ก ๊ฐ์ํฉ๋๋ค.
๊ทธ๋์ ๋ฃจํธ์์ ์ฐ๋ฆฌ๋ 2๊ฐ์ 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
}
}ํ์ผ์์ src/tslint ์ฐ๋ฆฌ๋ ํ์ค tslint๋ฅผ 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 ์คํฌ๋ฆฝํธ์์ linter์ ์์์ ์์ ํ์ต๋๋ค.
ng lint --tslint-config ./tslint.json --fix`๊ทธ ์ดํ๋ก ์ฐ๋ฆฌ๋ ๋ฐ๋ก์ก์์ผ ํ ์ ์ ๊ฐ์กฐํ๋ ์ผ์ ๊ทธ๋ง๋๊ธฐ ์์ํ์ต๋๋ค.
XNUMX๋จ๊ณ - ๋ช ๊ฐ์ง ๋ฌธ์ ์์

tslint์๋ ๋ค์๊ณผ ๊ฐ์ ๊ท์น์ด ์์ต๋๋ค. has fixer. ๊ทธ๋ผ ์ฌ์ฉํด ๋ด
์๋ค.
tslint --project tslint.ide_only.json --fix --force์ฌ๊ธฐ์๋ ์ฌ์ฉ ๊ฐ๋ฅํ ๋งค๊ฐ๋ณ์๋ฅผ ์๋ ์์ ํ๋ ํ๋ ๋ฆฐํฐ ๊ท์น์ ์คํํ๊ณ ์ด ๋ช ๋ น์ ์ค๋ฅ๋ฅผ ๋ฐํํ์ง ์๋๋ก ์ง์ํฉ๋๋ค(์ฌ๊ธฐ์ ์ฐ๋ฆฌ์ ๋ชฉํ๋ ๊ฒฐ๊ตญ ์๋ ์์ ์ ์ํํ๋ ๊ฒ์ ๋๋ค).
XNUMX๋จ๊ณ - ์๋ฆ๋ต๊ฒ ์ฐ๊ธฐ
๋ค๋ค ์๊ธฐ ๋ฐฉ์๋๋ก ๊ธ์ ์ฐ๋ค ๋ณด๋ฉด ๊ฒฐ๊ตญ ์ง๋ฃจํด์ง๊ฑฐ๋ ์. ์ฝ๋๋ ๋ง์น ํ ์ฌ๋์ด ํ๋ ๊ฒ์ฒ๋ผ ์์ฑ๋์ด์ผ ํฉ๋๋ค. ์ด๋ฅผ ์ํด ๋ค์ ์ค์ ์ผ๋ก Pretier๋ฅผ ์ค์นํ์ต๋๋ค.
.prettierr.yaml
printWidth: 200 # ะะฐะบัะธะผะฐะปัะฝะพะต ะบะพะป-ะฒะพ ัะธะผะฒะพะปะพะฒ ะฒ ัััะพะบะต
tabWidth: 2 # ะัะพะฑะตะปะพะฒ ะฒ ะขะฐะฑะต
singleQuote: true # ะัะฟะพะปัะทะพะฒะฐัั ะพะดะธะฝะฐัะฝัะต ะบะฐะฒััะบะธ
trailingComma: all # ะัะฟะพะปัะทะพะฒะฐัั ะทะฐะฟัััะต ะณะดะต ะฒะพะทะผะพะถะฝะพ
arrowParens: always # ะกััะตะปะพัะฝัะต ั-ะธะธ ะฒัะณะปัะดัั (x) => x
overrides:
- files: "*.ts" # ะัะพะฒะตัะบะฐ ัะฐะนะปะพะฒ *.ts
options:
parser: typescript # ะฏะทัะบ ะฒ ัะฐะนะปะฐั
*.ts๊ทธ๋ฆฌ๊ณ ๋ค์ ๋ช
๋ น์ ์ถ๊ฐํ์ต๋๋ค. prettier --write --config .prettierr.yaml
XNUMX๋จ๊ณ - ์ด ๋ชจ๋ ๊ฒ์ ์ด๋ป๊ฒ ์์ํ ์์ ์ ๋๊น?
์ด์ ์ด ๋ชจ๋ ๊ฒ์ ์์ํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ด๊ฒ์ด ์๋ํ๋ ค๋ฉด ๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ค์ด๋ก๋ํด์ผ ํฉ๋๋ค.
npm i -D prettier lint-staged huskyHusky๋ฅผ ์ฌ์ฉํ์ฌ git ํํฌ์์ ๋ช ๋ น ์คํ์ ์ค๋จํฉ๋๋ค(์ฌ์ ์ปค๋ฐ). lint-staged๋ ๋ณ๊ฒฝ๋ ํ์ผ์ ๋ฐ๋ผ ๋ช ๋ น์ ์คํํฉ๋๋ค(๋ํ ์ด ํ์ผ์ ๋ช ๋ น์ผ๋ก ๋์ฒดํฉ๋๋ค).
์ ๊ฐ ์ง๋ฉดํ ๋ฌธ์ ๋ฅผ ์ฆ์ ์ค๋ช
ํ๊ณ ์ถ์ต๋๋ค. ์ฐ๋ฆฌ ํ๋ก์ ํธ์์๋ ng lint๋ฅผ ์ฌ์ฉํฉ๋๋ค. lint-staged์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์์ ๋ ํ์ผ์ด ๋ช
๋ น์ ์ถ๊ฐ๋ฉ๋๋ค. ng lint์ ์ด์ ๋ํ ํค๊ฐ ์์ต๋๋ค --files, ๊ทธ๋ฌ๋ ๋ด๊ฐ ์ดํดํ ๋ฐ์ ๋ฐ๋ฅด๋ฉด ๊ทธ๋ ๋ง์ ํ์ผ์ ๋ณผ ์ ์์ผ๋ฉฐ ๊ฐ ํ์ผ์ ์ด ํค๋ฅผ ์ถ๊ฐํด์ผ ํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ ค๋ฉด ํ์ผ์ ๋ง๋ค์ด์ผ ํ์ต๋๋ค.
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์ด ํ์ผ์ ์คํํ๋ ค๋ฉด ํ๋ก์ ํธ ์ด๋ฆ์ ์ ๋ฌํด์ผ ํฉ๋๋ค. ํ์ผ์ ์์ด์ angular.json ํ๋ก์ ํธ ์์ฑ์์. ๋ด ๊ฒฝ์ฐ์๋ ๊ทธ๋ ์ง partner-account ะธ partner-account-e2e. ์ฒซ ๋ฒ์งธ๊ฐ ํ์ํด์.
์ค์ ์ผ๋ก ๋์๊ฐ ๋ณด๊ฒ ์ต๋๋ค. package.json์ ์ด์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
"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"
]
},์ฃผ์๋ฅผ ๊ธฐ์ธ์ด์ญ์์ค. lint-staged --relative. ๋ชจ์ --relative ๊ฑฐ๊ธฐ์ ํ์ํฉ๋๋ค. ์ด์ ์ปค๋ฐํ๋ฉด ์์๋ฉ๋๋ค. lint-staged. ๊ทธ๋ฐ ๋ค์ ํ์ผ์ ์ ํํ๊ณ ํด๋น ํ์ผ์ ๋ฐ๋ผ ๋ช
๋ น ๋ชฉ๋ก์ ์คํํฉ๋๋ค.
์์ฝ๊ฒ๋ ์ฝ๋ ๊ฒํ ๊ฐ ์ทจ์๋์ง๋ ์์ง๋ง ํจ์ฌ ๊น๋ํด์ก์ต๋๋ค. ๊ฐ๋ฐ์์๊ฒ ์ก์ธ์ค ํ์ ์, ๋ฉ์๋ ๋ฐ ์์ฑ ์ค๋ช ์ ๋ํด ๋ ์์ฃผ ์๊ธฐ์ํค๊ธฐ ์์ํ๊ณ ๊ทธ๋ค์ ์์ ์ด ๋์ผํ ์คํ์ผ๋ก ์์ฑ๋๊ธฐ ์์ํ๋ค๋ ์ ์ ์ฃผ๋ชฉํ๊ฒ ์ต๋๋ค(๊ฑฐ์ ๐).
์ถ์ - PM์๊ฒ ์ฌ์ง์ ๋ณด๋ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
์ถ์ฒ : habr.com
