@3o3/eslint-config
v1.0.0
Published
3o3 공유 ESLint 설정 — 사용처에 따라 레이어(base/react/docs/all)를 골라 쓴다.
Readme
@3o3/eslint-config
3o3 공유 ESLint 설정(ESLint 9 flat config). 사용처에 따라 레이어를 골라 쓴다.
설치
pnpm add -D @3o3/eslint-config eslint typescripteslint 와 typescript 는 peerDependency, 나머지 플러그인(typescript-eslint,
eslint-plugin-react, react-hooks, import, @emotion, jsdoc, tsdoc, prettier)은 이
패키지의 dependency 라 따로 설치할 필요가 없다.
레이어
| 레이어 | 내용 | 언제 |
|---|---|---|
| base | js + ts 공통(@eslint/js + typescript-eslint + import) + 3o3 하우스 규칙 | 모든 TS 코드(토대) |
| react | base 위 React 델타(eslint-plugin-react + react-hooks + @emotion) | React 패키지/앱 |
| docs | 공개 API 문서 완전성(jsdoc + tsdoc) — src/** 글롭, 내부는 파일 지시어로 opt-out | 공개 API 문서 강제 |
| all | base + react + prettier 번들 | "전부 켜기" |
| prettier | eslint-config-prettier(포매팅 충돌 규칙 off) | 항상 마지막에 스프레드 |
react 는 base 위에 얹는 누적 델타다. 각 영역의 3o3 규칙을 이미 포함한다.
전역 무시(dist/esm/비 ts·tsx)와 프로젝트 고유 규칙은 소비하는 쪽 config 에서 직접 둔다.
사용법
루트에 eslint.config.mjs 하나를 두는 단일 설정을 권장한다(이 모노레포도 동일).
defineConfig + 문자열 extends 스타일(ESLint 가 미는 방식)을 기본으로 쓴다.
default export 는 configs 맵을 가진 플러그인 형태(typescript-eslint /
eslint-plugin-react 와 동일)라 extends: ['@3o3/<layer>'] 로 참조된다.
// eslint.config.mjs (React 포함 레포)
import { defineConfig } from 'eslint/config'
import threeo3 from '@3o3/eslint-config'
export default defineConfig([
// 전역 무시는 소비하는 쪽에서 직접 둔다(빌드 산출물 + 비 ts·tsx)
{ ignores: ['**/dist/**', '**/esm/**', '**/*.{js,cjs,mjs,jsx,json}'] },
{ plugins: { '@3o3': threeo3 }, extends: ['@3o3/base', '@3o3/react'] },
...threeo3.docs(), // 공개 API 문서 검사(선택)
threeo3.prettier, // 항상 마지막
])// 플레인 TS 레포(React 없음) — react 레이어만 빼면 된다
export default defineConfig([
{ ignores: ['**/dist/**', '**/esm/**', '**/*.{js,cjs,mjs,jsx,json}'] },
{ plugins: { '@3o3': threeo3 }, extends: ['@3o3/base'] },
threeo3.prettier,
])프로젝트 고유 규칙은 마지막(프리티어 앞)에 flat-config 객체로 직접 얹는다:
{ files: ['**/*.{ts,tsx}'], rules: { 'no-console': 'error' } },
{ files: ['scripts/**'], rules: { '@typescript-eslint/no-explicit-any': 'off' } },배열 스프레드 스타일 (named export)
extends 대신 배열을 직접 펼쳐도 된다.
import { base, react, docs, prettier } from '@3o3/eslint-config'
export default [
{ ignores: ['**/dist/**', '**/esm/**', '**/*.{js,cjs,mjs,jsx,json}'] },
...base,
...react,
...docs(),
prettier,
]docs 레이어 — 공개 API 문서 강제 (글롭 + 지시어 opt-out)
docs() 는 src/** 의 ts/tsx 에서 export 된 함수·상수·인터페이스에 문서 블록을
요구한다(jsdoc/require-jsdoc). 즉 "문서 필수가 기본값".
내부 전용 모듈(배럴로 공개 안 하는 헬퍼)은 파일 맨 위 한 줄로 제외한다:
/* eslint-disable jsdoc/require-jsdoc -- 비공개(내부) 모듈: 공개 API 아님 */jsdoc/require-jsdoc하나만 끄면 docs 강제가 통째로 꺼진다(base/react 등은 그대로).tsdoc/syntax는 같이 적지 말 것 — 그 파일에 TSDoc 주석이 없으면 "unused directive" 경고가 난다.
files 글롭은 옵션으로 바꿀 수 있다: docs({ files: ['packages/**/src/**/*.{ts,tsx}'] }).
기본은 ['**/src/**/*.{ts,tsx}'](test/spec·stories·d.ts·_internal 자동 제외).
왜 이 방식인가 (과거 re-export fs 스캔 대비)
- 에디터 즉시 반영 — 정적 글롭이라 배럴을 바꿔도 ESLint 서버 재시작이 필요 없다. (스캔 방식은 공개 목록을 config 로드 때 한 번 계산·캐시해서, 배럴 변경이 에디터에 반영되려면 재시작이 필요했다.)
- 모르게 빠지지 않음 — 기본이 "문서 필수"라, 새 공개 파일은 자동으로 강제 대상. 내부 제외는 코드에 지시어로 명시되어 코드리뷰에서 보인다.
- 런타임 fs 작업 없음 — docs 설정이 순수 정적.
설계 메모
- 타입 인지 규칙 미사용:
recommended(not type-checked)만 켠다 →parserOptions.project가 필요 없어 어떤 레포에서도 그대로 동작한다. - 무시·프로젝트 규칙은 소비처 config 에서: base/react 는 순수 룰 레이어라 무시
규칙을 갖지 않는다. 전역 무시(dist/esm 등)와 프로젝트 고유 규칙은 소비하는 쪽
eslint.config에서 직접 둔다. - 공개/내부 구분은 위치·지시어로 : 재export 그래프(동적)가 아니라
src/**글롭 + 파일 지시어(정적)로 판정 → 예측 가능하고 에디터에 실시간.
