turbo monorepo eslint custom config update

Jung Wish·2024년 2월 22일
0

오늘의_코딩

목록 보기
1/11

오늘 코딩한 내용은 사실 별건 없고, next랑 default로 plugin setting을 나누는 작업을 했다. 근데 히스토리를 좀 정리해두면 좋을 것 같아 기록을 남겨본다.

이미 Monorepo를 생성할 때부터 아래 가이드를 보고 eslint config custom package세팅을 해두긴 했었다. 참고로 링크는 이미 최신화가 되서 예전이랑 다른 내용이다. 초기 turbo example을 보고 세팅했었기 때문에, commit 내용 추적해보면 원래 파일과 같이 세팅되어 있는 것을 찾을 수 있을 것이다.
https://turbo.build/repo/docs/handbook/linting/eslint

원래의 파일(packages/eslint-config-custom/index.js)은 아래와 같았다.

module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint'],
  extends: ['eslint:recommended', 'prettier', 'turbo', 'plugin:@typescript-eslint/recommended'],
  rules: {},
  env: {
    browser: true,
  },
  ignorePatterns: ['node_modules', '.next', '.github', '*.config.js', '*.js'],
};

이것이 구 eslint 버전에서는 잘 동작하고 있긴 했는데...?

프로젝트 도중 yarn(not berry..) -> pnpm 으로 패키지 매니저를 바꾸면서 모듈의 dependencies 구조도 바뀌었다. 그에 따라 유령 모듈 사용이 방지되어 그동안 잘못 참조하고 있었던 모듈들을 정리하게 되었다. 오늘의 코딩과 관련된 내용은 아래와 같다.(사실 이 에피소드는 좀 오래되서 사실 가물가물한데 요인이 아마 2가지 때문이라 예상한다..)

  • eslint-config-custom을 apps/* 프로젝트 devDepencies에 모두 추가해주고 re-install 해주었다.
  • 새롭게 추가된 프로젝트에 eslint 최신판이 깔렸다.
    commit을 하려고 보니..? 왠걸 갑자기 husky pre-commit 단계에서 lint를 하는데 계속
    ESLint couldn't determine the plugin "@typescript-eslint" uniquely. 에러를 뱉어냈다.
    --resolve-plugins-relative-to 옵션을 주면 된다는데...그렇게 줘도 해결되지 않았다. 그래서 eslint-custom을 사용하는 app 마다 @typescript-eslint plugin을 깔아주면 일시적 해결은 되었는데 사용하는 패키지와 앱마다 모든 파서를 다 깔아줄거면 공통 config 모듈을 쓰는 의미가 약간 퇴색되는 것 같았다.

(*유령 모듈 : 실제 app, package dependency에는 선언하지 않았기 때문에 사실 명시되지 않은 모듈을 사용하려고 하면 오류가 나야 정상이지만 root 경로 node_modules에 모두 install 되어 hosting 되다보니 오류가 나지 않는 뭐 그런 현상으로 알고있다.)

그래서 turbo 공식문서 eslint setting을 다시 찾아보았고, 업데이트 된 것을 확인했다.
https://github.com/vercel/turbo/pull/5812
그래서, 가이드 대로 parserOptions에 tsconfigRoot경로와 project를 설정해주었다. project는 여러 프로젝트를 사용하는 monorepo, 그리고 각 app마다 tsconfig를 사용하는 경우 사용할 tsconfig 프로젝트를 지정할 수 있는 속성인 것 같고, tsconfigRoot는 project에 설정한 tsconfig 파일 위치를 custom plugin기준으로 상대경로를 만들어낼 수 있도록 설정해주는 듯 하다.

const { resolve } = require('node:path');
console.log(__dirname); // 이건 그냥 콘솔찍어 보려고 적은것..ㅎㅎ

const project = ['tsconfig.json', 'tsconfig.node.json'].map(f => resolve(process.cwd(), f));
module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: { tsconfigRootDir: __dirname, project },
  extends: ['eslint:recommended', 'prettier', 'eslint-config-turbo', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended'],
  env: {
    browser: true,
    node: true,
  },
  globals: {
    React: true,
    JSX: true,
  },
  ignorePatterns: ['node_modules', 'dist', '.next', '.github', '*.config.js', '*.js'],
  overrides: [{ files: ['*.js?(x)', '*.ts?(x)'] }],
  plugins: ['@typescript-eslint', 'react', 'react-hooks', 'import', 'react-refresh'],
  rules: {
    '@typescript-eslint/no-unused-vars': 'warn',
    '@typescript-eslint/no-explicit-any': 'warn',
    'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
  },
};

이렇게 하니, 해결은 되었고 오늘 신규 app project 세팅을 하면서 eslint를 다시보니 next app은 그동안 eslint-config-custom과 next/core-web-vitals를 확장해서 쓰고 있었는데, 어차피 대체로 모든 next에 같은 설정을 하다보니 모듈 설치도 덜어낼겸 next 전용 플러그인을 분리해야겠다는 생각이 들었다.

const { resolve } = require('node:path');
console.log(__dirname);
const project = ['tsconfig.json', 'tsconfig.node.json'].map(f => resolve(process.cwd(), f));

module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: { tsconfigRootDir: __dirname, project, sourceType: 'module' },
  extends: ['eslint:recommended', 'prettier', 'eslint-config-turbo', 'plugin:@typescript-eslint/recommended', 'next/core-web-vitals'],
  env: {
    browser: true,
    node: true,
  },
  globals: {
    React: true,
    JSX: true,
  },
  ignorePatterns: ['node_modules', 'dist', '.next', '.github', '*.config.js', '*.js'],
  overrides: [{ files: ['*.js?(x)', '*.ts?(x)'] }],
  plugins: ['@typescript-eslint'],
  rules: {
    '@typescript-eslint/no-unused-vars': 'warn',
    '@typescript-eslint/no-explicit-any': 'warn',
  }
};

사실 이 파일도 문서 난독 이슈로 몇가지 시행착오가 있었는데, 그냥 create-next-app으로 생성하면 만들어지는 eslint 파일 설정만 고대로 잘 옮겨오면 금방 작성할 수 있다...ㅋ_ㅋ..

eslint-config-next라는 패키지에는 eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-next 플러그인을 사용하여, 권장되는 rules를 설정하였다고 되어있다. 근데 이 부분을 안읽고 엄한곳을 읽는 바람에.. eslint-plugin-next 단일로 깔고 거기에 없는 rule이 설정된 next config를 extends했더니 계속 플러그인 못찾는다는 에러만 겁나 나서(당연하다.....) 오류가 말하는 플러그인 다 깔아주고 rule을 설정하는 불상사를 겪었다. 굳이 세밀하게 customize할게 아니라면 그냥 eslint-config-next 깔아주고 config extends 해주기만 하면 된다. 🫠

저렇게 설정해두고, next app 내부 .eslintrc.js에는 아래와 같이 custom/next로 설정해주면 되고, 다른 앱(나의 경우는 vite)에는 next 설정을 쓸게 아니니까 ['custom']으로 설정해주었다.

module.exports = {
  root: true,
  extends: ['custom/next'],
  rules: {
    'react/no-unescaped-entities': 'off',
    '@next/next/no-page-custom-font': 'off',
  },
};

오늘의 코딩 결론은 문서를 좀 잘읽자...

profile
Frontend Developer, 올라운더가 되고싶은 잡부 개발자, ISTP, 겉촉속바 인간, 블로그 주제 찾아다니는 사람

0개의 댓글