cos-ui
cos-ui 의 루트에는 eslintrc.js 가 없고, 각 패키지의 루트에 eslintrc 가 존재했다. 그래서인지 다른 곳에서 사용하던 방식으로는 오류가 났다.
// lint-staged.config.js
module.exports = {
'*.{js,jsx,ts,tsx}': 'eslint --fix',
};
이유를 생각해보았을 때, eslint 를 실행하는 곳 ( /cos-ui ) 에 eslint 관련 설정 파일이 없어 생기는 문제라고 생각했다.
찾아보니 두 가지 방법이 있었다.
- 각 패키지별로 eslintrc 설정파일을 찾아 실행하도록
- 공통된 eslintrc 를 만들어서 사용
1번 방법은 다소 번거롭지만 lint-staged 에서 경로별로 설정하여 사용한다.
module.exports = {
'packages/react/**/*.+(js|jsx|ts|tsx)': 'eslint --fix',
'packages/playground-react/**/*.+(js|jsx|ts|tsx)': 'eslint --fix',
'packages/primitives/**/*.+(js|jsx|ts|tsx)': 'eslint --fix',
};
eslint 만 하면 그래도 적은 편이지만, tsc 까지 고려한다면 2배로 늘어난다.
module.exports = {
'packages/react/**/*.+(js|jsx|ts|tsx)': 'eslint --fix',
'packages/react/**/*.+(ts|tsx)': () => 'tsc -p packages/react/tsconfig.json --noEmit',
'packages/playground-react/**/*.+(js|jsx|ts|tsx)': 'eslint --fix',
'packages/playground-react/**/*.+(ts|tsx)': () => 'tsc -p packages/playground-react/tsconfig.json --noEmit',
'packages/primitives/**/*.+(js|jsx|ts|tsx)': 'eslint --fix',
'packages/primitives/**/*.+(ts|tsx)': () => 'tsc -p packages/primitives/tsconfig.json --noEmit',
};
결론적으로는 2번 방법을 택했다.
패키지별로 eslint 설정파일을 다룬다면 A 패키지에서 결정난 룰을 B 패키지에서도 수동으로 추가해야하는 번거로움이 있기에 eslint 는 공통된 설정 파일로 관리하는게 맞다고 생각하여 분리하는 과정을 거쳤다.
eslint 가 parserOption 으로 tsconfig.json 를 찾기 때문에 각 패키지의 공통된 tsconfig 속성들을 비교하고 분리해주어 cos-ui 루트에 tsconfig.json 을 생성하였고, 각 패키지는 해당 tsconfig.json 을 extends 하여 사용하도록 변경했다.
여기서 핵심은 린팅을 제대로 해주기 위해서는 eslint 에서 각 tsconfig.json 을 찾아갈 수 있도록 import resolver 설정을 각각의 tsconfig.json 로 설정해주어야 했다.
각 패키지의 tsconfig 관련 파일들이 많이 줄었다.
// packages/react/tsconfig.json
// 이전
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "dist",
"target": "ESNext",
"module": "commonjs",
"moduleResolution": "Node",
"declaration": true,
"declarationMap": true,
"isolatedModules": true,
"jsx": "react-jsx",
"allowJs": true,
"strict": true,
"noImplicitAny": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["node_modules"],
"extends": "./tsconfig.path.json"
}
// + tsconfig.path.json, tsconfig.eslint.json...
// 이후
// 베이스 tsconfig.json
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"target": "ESNext",
"module": "commonjs",
"moduleResolution": "node",
"isolatedModules": true,
"allowJs": true,
"strict": true,
"noImplicitAny": false,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"exclude": [
"packages/**/dist/**",
"lint-staged.config.js",
"packages/**/*.config.js"
]
}
// packages/react/tsconfig.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "dist",
"declaration": true,
"declarationMap": true,
"jsx": "react-jsx",
"paths": {
"@components/*": ["src/components/*"],
"@styles/*": ["src/styles/*"]
}
},
"include": ["src"]
}
// eslintrc.js
{
...,
overrides: [
{
files: ['packages/playground-react/**/*.ts?(x)'],
settings: {
'import/resolver': {
typescript: {
project: './packages/playground-react/tsconfig.json',
},
},
},
},
{
files: ['packages/primitives/**/*.ts?(x)'],
settings: {
'import/resolver': {
typescript: {
project: './packages/primitives/tsconfig.json',
},
},
},
},
{
files: ['packages/react/**/*.ts?(x)'],
settings: {
'import/resolver': {
typescript: {
project: './packages/react/tsconfig.json',
},
},
},
},
]
}
Type check 는 tsconfig 들이 각각 다른 설정을 가질 확률이 높기 때문에 별도의 스크립트를 작성하여 주었다.
module.exports = {
'*.{js,jsx,ts,tsx}': ['eslint --fix'],
'packages/react/**/*.+(ts|tsx)': () =>
'tsc -p packages/react/tsconfig.json --noEmit',
'packages/playground-react/**/*.+(ts|tsx)': () =>
'tsc -p packages/playground-react/tsconfig.json --noEmit',
'packages/primitives/**/*.+(ts|tsx)': () =>
'tsc -p packages/primitives/tsconfig.json --noEmit',
};
lint-staged 가 바뀐 부분만 eslint 를 체크해주니 type check 도 바뀐 파일만 할까? 궁금했다.
A 파일에서 선언한 B 타입을 수정하고, B 타입을 사용하는 C 파일이 타입체크에 걸리는지 테스트해보았다.
결론은 수정된 파일만 확인하는게 아니라 타입체크를 제대로 해주는 것을 볼 수 있었다.
prettier write 까지 추가하고 마무리했다.
module.exports = {
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
'packages/react/**/*.+(ts|tsx)': () =>
'tsc -p packages/react/tsconfig.json --noEmit',
'packages/playground-react/**/*.+(ts|tsx)': () =>
'tsc -p packages/playground-react/tsconfig.json --noEmit',
'packages/primitives/**/*.+(ts|tsx)': () =>
'tsc -p packages/primitives/tsconfig.json --noEmit',
};
참고