@3o3/eslint-config 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -0
- package/package.json +50 -0
- package/src/all.js +23 -0
- package/src/base.js +115 -0
- package/src/docs.js +75 -0
- package/src/index.js +49 -0
- package/src/react.js +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# @3o3/eslint-config
|
|
2
|
+
|
|
3
|
+
3o3 공유 ESLint 설정(ESLint 9 flat config). **사용처에 따라 레이어를 골라** 쓴다.
|
|
4
|
+
|
|
5
|
+
## 설치
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add -D @3o3/eslint-config eslint typescript
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`eslint` 와 `typescript` 는 peerDependency, 나머지 플러그인(typescript-eslint,
|
|
12
|
+
eslint-plugin-react, react-hooks, import, @emotion, jsdoc, tsdoc, prettier)은 이
|
|
13
|
+
패키지의 dependency 라 따로 설치할 필요가 없다.
|
|
14
|
+
|
|
15
|
+
## 레이어
|
|
16
|
+
|
|
17
|
+
| 레이어 | 내용 | 언제 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `base` | js + ts 공통(`@eslint/js` + `typescript-eslint` + `import`) + 3o3 하우스 규칙 | 모든 TS 코드(토대) |
|
|
20
|
+
| `react` | `base` 위 React 델타(`eslint-plugin-react` + `react-hooks` + `@emotion`) | React 패키지/앱 |
|
|
21
|
+
| `docs` | 공개 API 문서 완전성(`jsdoc` + `tsdoc`) — `src/**` 글롭, 내부는 파일 지시어로 opt-out | 공개 API 문서 강제 |
|
|
22
|
+
| `all` | `base + react + prettier` 번들 | "전부 켜기" |
|
|
23
|
+
| `prettier` | `eslint-config-prettier`(포매팅 충돌 규칙 off) | 항상 **마지막**에 스프레드 |
|
|
24
|
+
|
|
25
|
+
`react` 는 `base` 위에 얹는 누적 델타다. 각 영역의 3o3 규칙을 이미 포함한다.
|
|
26
|
+
전역 무시(dist/esm/비 ts·tsx)와 프로젝트 고유 규칙은 소비하는 쪽 config 에서 직접 둔다.
|
|
27
|
+
|
|
28
|
+
## 사용법
|
|
29
|
+
|
|
30
|
+
루트에 `eslint.config.mjs` 하나를 두는 **단일 설정**을 권장한다(이 모노레포도 동일).
|
|
31
|
+
`defineConfig` + 문자열 `extends` 스타일(ESLint 가 미는 방식)을 기본으로 쓴다.
|
|
32
|
+
default export 는 `configs` 맵을 가진 플러그인 형태(typescript-eslint /
|
|
33
|
+
eslint-plugin-react 와 동일)라 `extends: ['@3o3/<layer>']` 로 참조된다.
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
// eslint.config.mjs (React 포함 레포)
|
|
37
|
+
import { defineConfig } from 'eslint/config'
|
|
38
|
+
import threeo3 from '@3o3/eslint-config'
|
|
39
|
+
|
|
40
|
+
export default defineConfig([
|
|
41
|
+
// 전역 무시는 소비하는 쪽에서 직접 둔다(빌드 산출물 + 비 ts·tsx)
|
|
42
|
+
{ ignores: ['**/dist/**', '**/esm/**', '**/*.{js,cjs,mjs,jsx,json}'] },
|
|
43
|
+
{ plugins: { '@3o3': threeo3 }, extends: ['@3o3/base', '@3o3/react'] },
|
|
44
|
+
...threeo3.docs(), // 공개 API 문서 검사(선택)
|
|
45
|
+
threeo3.prettier, // 항상 마지막
|
|
46
|
+
])
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
// 플레인 TS 레포(React 없음) — react 레이어만 빼면 된다
|
|
51
|
+
export default defineConfig([
|
|
52
|
+
{ ignores: ['**/dist/**', '**/esm/**', '**/*.{js,cjs,mjs,jsx,json}'] },
|
|
53
|
+
{ plugins: { '@3o3': threeo3 }, extends: ['@3o3/base'] },
|
|
54
|
+
threeo3.prettier,
|
|
55
|
+
])
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
프로젝트 고유 규칙은 마지막(프리티어 앞)에 flat-config 객체로 직접 얹는다:
|
|
59
|
+
```js
|
|
60
|
+
{ files: ['**/*.{ts,tsx}'], rules: { 'no-console': 'error' } },
|
|
61
|
+
{ files: ['scripts/**'], rules: { '@typescript-eslint/no-explicit-any': 'off' } },
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 배열 스프레드 스타일 (named export)
|
|
65
|
+
|
|
66
|
+
`extends` 대신 배열을 직접 펼쳐도 된다.
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import { base, react, docs, prettier } from '@3o3/eslint-config'
|
|
70
|
+
|
|
71
|
+
export default [
|
|
72
|
+
{ ignores: ['**/dist/**', '**/esm/**', '**/*.{js,cjs,mjs,jsx,json}'] },
|
|
73
|
+
...base,
|
|
74
|
+
...react,
|
|
75
|
+
...docs(),
|
|
76
|
+
prettier,
|
|
77
|
+
]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## docs 레이어 — 공개 API 문서 강제 (글롭 + 지시어 opt-out)
|
|
81
|
+
|
|
82
|
+
`docs()` 는 `src/**` 의 ts/tsx 에서 **export 된 함수·상수·인터페이스에 문서 블록을
|
|
83
|
+
요구**한다(`jsdoc/require-jsdoc`). 즉 **"문서 필수가 기본값"**.
|
|
84
|
+
|
|
85
|
+
내부 전용 모듈(배럴로 공개 안 하는 헬퍼)은 파일 **맨 위 한 줄**로 제외한다:
|
|
86
|
+
```ts
|
|
87
|
+
/* eslint-disable jsdoc/require-jsdoc -- 비공개(내부) 모듈: 공개 API 아님 */
|
|
88
|
+
```
|
|
89
|
+
- `jsdoc/require-jsdoc` 하나만 끄면 docs 강제가 통째로 꺼진다(base/react 등은 그대로).
|
|
90
|
+
- `tsdoc/syntax` 는 같이 적지 말 것 — 그 파일에 TSDoc 주석이 없으면 "unused directive"
|
|
91
|
+
경고가 난다.
|
|
92
|
+
|
|
93
|
+
`files` 글롭은 옵션으로 바꿀 수 있다: `docs({ files: ['packages/**/src/**/*.{ts,tsx}'] })`.
|
|
94
|
+
기본은 `['**/src/**/*.{ts,tsx}']`(test/spec·stories·d.ts·`_internal` 자동 제외).
|
|
95
|
+
|
|
96
|
+
### 왜 이 방식인가 (과거 re-export fs 스캔 대비)
|
|
97
|
+
|
|
98
|
+
- **에디터 즉시 반영** — 정적 글롭이라 배럴을 바꿔도 ESLint 서버 재시작이 필요 없다.
|
|
99
|
+
(스캔 방식은 공개 목록을 config 로드 때 한 번 계산·캐시해서, 배럴 변경이 에디터에
|
|
100
|
+
반영되려면 재시작이 필요했다.)
|
|
101
|
+
- **모르게 빠지지 않음** — 기본이 "문서 필수"라, 새 공개 파일은 자동으로 강제 대상.
|
|
102
|
+
내부 제외는 코드에 지시어로 **명시**되어 코드리뷰에서 보인다.
|
|
103
|
+
- **런타임 fs 작업 없음** — docs 설정이 순수 정적.
|
|
104
|
+
|
|
105
|
+
## 설계 메모
|
|
106
|
+
|
|
107
|
+
- **타입 인지 규칙 미사용**: `recommended`(not type-checked)만 켠다 →
|
|
108
|
+
`parserOptions.project` 가 필요 없어 어떤 레포에서도 그대로 동작한다.
|
|
109
|
+
- **무시·프로젝트 규칙은 소비처 config 에서**: base/react 는 순수 룰 레이어라 무시
|
|
110
|
+
규칙을 갖지 않는다. 전역 무시(dist/esm 등)와 프로젝트 고유 규칙은 소비하는 쪽
|
|
111
|
+
`eslint.config` 에서 직접 둔다.
|
|
112
|
+
- **공개/내부 구분은 위치·지시어로** : 재export 그래프(동적)가 아니라 `src/**` 글롭 +
|
|
113
|
+
파일 지시어(정적)로 판정 → 예측 가능하고 에디터에 실시간.
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@3o3/eslint-config",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "3o3 공유 ESLint 설정 — 사용처에 따라 레이어(base/react/docs/all)를 골라 쓴다.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./src/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.js",
|
|
10
|
+
"./base": "./src/base.js",
|
|
11
|
+
"./react": "./src/react.js",
|
|
12
|
+
"./docs": "./src/docs.js",
|
|
13
|
+
"./all": "./src/all.js",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.jobis.co/jobisnvillains/3o3_libraries",
|
|
22
|
+
"directory": "configs/eslint-config"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@emotion/eslint-plugin": "^11.12.0",
|
|
27
|
+
"@eslint/js": "^9.39.4",
|
|
28
|
+
"eslint-config-prettier": "^10.1.8",
|
|
29
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
30
|
+
"eslint-plugin-import": "^2.32.0",
|
|
31
|
+
"eslint-plugin-jsdoc": "^63.0.2",
|
|
32
|
+
"eslint-plugin-react": "^7.37.5",
|
|
33
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
34
|
+
"eslint-plugin-tsdoc": "^0.5.2",
|
|
35
|
+
"globals": "^15.15.0",
|
|
36
|
+
"typescript-eslint": "^8.60.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"eslint": ">=9",
|
|
40
|
+
"typescript": ">=5"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"typescript": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/all.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import base from './base.js'
|
|
2
|
+
import reactLayer from './react.js'
|
|
3
|
+
import prettier from 'eslint-config-prettier'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* all 레이어 — 배터리 포함 번들.
|
|
7
|
+
*
|
|
8
|
+
* base + react 를 켜고 마지막에 eslint-config-prettier 로 포매팅 충돌 규칙을
|
|
9
|
+
* 끈다(반드시 마지막). "전부 켜고 싶다" 일 때 쓴다. 플레인 TS 라이브러리는
|
|
10
|
+
* react 규칙이 불필요하므로 base 만 권장.
|
|
11
|
+
*
|
|
12
|
+
* docs 레이어는 `packageDir`/`rootDir` 이 필요한 팩토리라 번들에 포함하지 않는다 —
|
|
13
|
+
* 필요하면 `docs({ packageDir })` 를 직접 스프레드한다.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```js
|
|
17
|
+
* import { all } from '@3o3/eslint-config'
|
|
18
|
+
* export default all
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
const all = [...base, ...reactLayer, prettier]
|
|
22
|
+
|
|
23
|
+
export default all
|
package/src/base.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import tseslint from 'typescript-eslint'
|
|
3
|
+
import importPlugin from 'eslint-plugin-import'
|
|
4
|
+
import globals from 'globals'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* base 레이어 — js + ts 공통.
|
|
8
|
+
*
|
|
9
|
+
* 프레임워크에 무관한 3o3 공통 규칙의 단일 출처다.
|
|
10
|
+
* · @eslint/js + typescript-eslint(recommended) + eslint-plugin-import
|
|
11
|
+
* · 3o3 하우스 규칙(naming-convention / member-ordering / eqeqeq / curly 등)
|
|
12
|
+
*
|
|
13
|
+
* 타입 인지(type-aware) 규칙은 켜지 않는다 → `parserOptions.project` 가 필요 없어
|
|
14
|
+
* 어떤 레포에서도 그대로 동작한다(이식성).
|
|
15
|
+
*
|
|
16
|
+
* 모든 `@3o3/eslint-config` 소비의 토대. react 레이어는 이 위에 스프레드한다.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```js
|
|
20
|
+
* import { base } from '@3o3/eslint-config'
|
|
21
|
+
* export default [...base]
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
const base = tseslint.config({
|
|
25
|
+
files: ['**/*.{ts,tsx}'],
|
|
26
|
+
extends: [
|
|
27
|
+
js.configs.recommended,
|
|
28
|
+
...tseslint.configs.recommended,
|
|
29
|
+
importPlugin.flatConfigs.recommended,
|
|
30
|
+
importPlugin.flatConfigs.typescript,
|
|
31
|
+
],
|
|
32
|
+
languageOptions: {
|
|
33
|
+
globals: {
|
|
34
|
+
...globals.node,
|
|
35
|
+
...globals.browser,
|
|
36
|
+
...globals.jest,
|
|
37
|
+
...globals.es2021,
|
|
38
|
+
},
|
|
39
|
+
parserOptions: {
|
|
40
|
+
ecmaFeatures: { jsx: true },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
settings: {
|
|
44
|
+
'import/resolver': { typescript: {} },
|
|
45
|
+
},
|
|
46
|
+
rules: {
|
|
47
|
+
'no-implicit-coercion': 'error',
|
|
48
|
+
'no-warning-comments': [
|
|
49
|
+
'warn',
|
|
50
|
+
{
|
|
51
|
+
terms: ['TODO', 'FIXME', 'XXX', 'BUG'],
|
|
52
|
+
location: 'anywhere',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
curly: ['error', 'all'],
|
|
56
|
+
eqeqeq: ['error', 'always', { null: 'ignore' }],
|
|
57
|
+
|
|
58
|
+
// eslint-plugin-import recommended preset에서 신규 적용 — 기존 config는 import 규칙을 켜지 않았으므로 동작 보존 위해 off
|
|
59
|
+
// (import/export는 TS type re-export에 대한 오탐, 실제 중복 export는 tsc typecheck가 잡음)
|
|
60
|
+
'import/export': 'off',
|
|
61
|
+
'import/no-duplicates': 'off',
|
|
62
|
+
|
|
63
|
+
// Hoisting을 전략적으로 사용한 경우가 많아서
|
|
64
|
+
'@typescript-eslint/no-use-before-define': 'off',
|
|
65
|
+
// 모델 정의 부분에서 class와 interface를 합치기 위해 사용하는 용법도 잡고 있어서
|
|
66
|
+
// (v8에서 no-empty-interface는 no-empty-object-type으로 통합됨)
|
|
67
|
+
'@typescript-eslint/no-empty-object-type': 'off',
|
|
68
|
+
// 모델 정의 부분에서 파라미터 프로퍼티를 잘 쓰고 있어서
|
|
69
|
+
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
70
|
+
// (v8에서 no-var-requires는 no-require-imports로 통합됨, 기존 warn 의도 보존)
|
|
71
|
+
'@typescript-eslint/no-require-imports': 'warn',
|
|
72
|
+
'@typescript-eslint/no-non-null-asserted-optional-chain': 'warn',
|
|
73
|
+
// v5 recommended에는 있었으나 v8 recommended에서 빠진 규칙. 기존 동작(warn) 보존.
|
|
74
|
+
// (소스에 남아있는 eslint-disable @typescript-eslint/no-non-null-assertion 주석들이 이를 전제로 함)
|
|
75
|
+
'@typescript-eslint/no-non-null-assertion': 'warn',
|
|
76
|
+
'@typescript-eslint/no-inferrable-types': 'warn',
|
|
77
|
+
'@typescript-eslint/no-empty-function': 'off',
|
|
78
|
+
'@typescript-eslint/naming-convention': [
|
|
79
|
+
'error',
|
|
80
|
+
{
|
|
81
|
+
format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
|
|
82
|
+
selector: 'variable',
|
|
83
|
+
leadingUnderscore: 'allow',
|
|
84
|
+
},
|
|
85
|
+
{ format: ['camelCase', 'PascalCase'], selector: 'function' },
|
|
86
|
+
{ format: ['PascalCase'], selector: 'interface' },
|
|
87
|
+
{ format: ['PascalCase'], selector: 'typeAlias' },
|
|
88
|
+
],
|
|
89
|
+
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
90
|
+
'@typescript-eslint/array-type': ['error', { default: 'array-simple' }],
|
|
91
|
+
'@typescript-eslint/no-unused-vars': [
|
|
92
|
+
'error',
|
|
93
|
+
// v8에서 caughtErrors 기본값이 'none' → 'all'로 바뀌어 catch (e) 바인딩까지 잡는다.
|
|
94
|
+
// 기존(v5) 동작 보존을 위해 caughtErrors를 'none'으로 복원.
|
|
95
|
+
{ ignoreRestSiblings: true, caughtErrors: 'none' },
|
|
96
|
+
],
|
|
97
|
+
'@typescript-eslint/member-ordering': [
|
|
98
|
+
'error',
|
|
99
|
+
{
|
|
100
|
+
default: [
|
|
101
|
+
'public-static-field',
|
|
102
|
+
'private-static-field',
|
|
103
|
+
'public-instance-field',
|
|
104
|
+
'private-instance-field',
|
|
105
|
+
'public-constructor',
|
|
106
|
+
'private-constructor',
|
|
107
|
+
'public-instance-method',
|
|
108
|
+
'private-instance-method',
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
export default base
|
package/src/docs.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import jsdoc from 'eslint-plugin-jsdoc'
|
|
2
|
+
import tsdoc from 'eslint-plugin-tsdoc'
|
|
3
|
+
|
|
4
|
+
const disabledJsdocRules = {
|
|
5
|
+
// ── 타입 그룹: 주석에 타입을 요구/검증/삭제하는 룰. TS에선 전부 OFF ──
|
|
6
|
+
'jsdoc/require-param-type': 'off',
|
|
7
|
+
'jsdoc/require-returns-type': 'off',
|
|
8
|
+
'jsdoc/require-property-type': 'off',
|
|
9
|
+
'jsdoc/no-defaults': 'off', // 기본값도 코드에서. fixer가 내용 삭제
|
|
10
|
+
'jsdoc/no-undefined-types': 'off', // TS에서 오탐 많음
|
|
11
|
+
'jsdoc/valid-types': 'off',
|
|
12
|
+
'jsdoc/check-types': 'off',
|
|
13
|
+
'jsdoc/type-formatting': 'off',
|
|
14
|
+
'jsdoc/reject-function-type': 'off',
|
|
15
|
+
// ── 태그 이름 검증은 tsdoc/syntax가 단독 담당 ──
|
|
16
|
+
'jsdoc/check-tag-names': 'off',
|
|
17
|
+
// 완전한 문장을 위해 작성하는건대 한글 하고는 잘 안 맞음
|
|
18
|
+
'jsdoc/require-description-complete-sentence': 'off',
|
|
19
|
+
// ── 과함: 파일마다 @file 헤더 강제 ──
|
|
20
|
+
'jsdoc/require-file-overview': 'off',
|
|
21
|
+
// ── 작동에 옵션이 필수인데 정책이 없어 "검사 0건 + 설정오류만" 출력하는 룰 ──
|
|
22
|
+
'jsdoc/no-missing-syntax': 'off', // tsdoc 따름
|
|
23
|
+
'jsdoc/no-restricted-syntax': 'off', // tsdoc 따름
|
|
24
|
+
'jsdoc/require-tags': 'off', // tags 옵션 필요
|
|
25
|
+
'jsdoc/match-name': 'off', // match 옵션 필요
|
|
26
|
+
'jsdoc/match-description': 'off',
|
|
27
|
+
'jsdoc/text-escaping': 'off', // escapeHTML/escapeMarkdown 옵션 필요
|
|
28
|
+
// ── ESLint 9에서 작동 불가: 플러그인이 직접 "이 룰 끄고 processor 쓰라"고 출력 ──
|
|
29
|
+
'jsdoc/check-examples': 'off',
|
|
30
|
+
'jsdoc/require-template': 'off',
|
|
31
|
+
'jsdoc/require-example': 'off',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function docs({ files = ['**/src/**/*.{ts,tsx}'] } = {}) {
|
|
35
|
+
return [
|
|
36
|
+
{
|
|
37
|
+
name: '@3o3/eslint-config/docs',
|
|
38
|
+
files,
|
|
39
|
+
ignores: [
|
|
40
|
+
'**/*.{test,spec}.{ts,tsx}',
|
|
41
|
+
'**/*.stories.{ts,tsx}',
|
|
42
|
+
'**/*.d.ts',
|
|
43
|
+
'**/_internal/**',
|
|
44
|
+
],
|
|
45
|
+
plugins: { jsdoc, tsdoc },
|
|
46
|
+
settings: {
|
|
47
|
+
jsdoc: {
|
|
48
|
+
mode: 'typescript',
|
|
49
|
+
// TSDoc 표준은 제네릭 문서화에 @typeParam을 쓴다(@template 아님).
|
|
50
|
+
tagNamePreference: { template: 'typeParam' },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
rules: {
|
|
54
|
+
...disabledJsdocRules,
|
|
55
|
+
'jsdoc/informative-docs': 'off',
|
|
56
|
+
|
|
57
|
+
'jsdoc/require-jsdoc': [
|
|
58
|
+
'error',
|
|
59
|
+
{
|
|
60
|
+
publicOnly: true,
|
|
61
|
+
enableFixer: false,
|
|
62
|
+
require: { FunctionDeclaration: false },
|
|
63
|
+
contexts: [
|
|
64
|
+
'ExportNamedDeclaration > FunctionDeclaration', // export function
|
|
65
|
+
'ExportNamedDeclaration > VariableDeclaration > VariableDeclarator > ArrowFunctionExpression', // export const () =>
|
|
66
|
+
'ExportNamedDeclaration > TSInterfaceDeclaration', // export interface
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default docs
|
package/src/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import base from './base.js'
|
|
2
|
+
import reactLayer from './react.js'
|
|
3
|
+
import all from './all.js'
|
|
4
|
+
import { docs } from './docs.js'
|
|
5
|
+
import eslintConfigPrettier from 'eslint-config-prettier'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `@3o3/eslint-config` — 사용처별 레이어를 골라 쓰는 공유 ESLint 설정(ESLint 9 flat).
|
|
9
|
+
*
|
|
10
|
+
* 두 가지 소비 방식을 모두 지원한다.
|
|
11
|
+
*
|
|
12
|
+
* 1) 배열 스프레드(named export):
|
|
13
|
+
* ```js
|
|
14
|
+
* import { base, react } from '@3o3/eslint-config'
|
|
15
|
+
* export default [...base, ...react]
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* 2) ESLint 가 권장하는 `extends` 스타일(default export + `defineConfig`):
|
|
19
|
+
* ```js
|
|
20
|
+
* import { defineConfig } from 'eslint/config'
|
|
21
|
+
* import threeo3 from '@3o3/eslint-config'
|
|
22
|
+
* export default defineConfig([
|
|
23
|
+
* { plugins: { '@3o3': threeo3 }, extends: ['@3o3/base', '@3o3/react'] },
|
|
24
|
+
* ])
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* default export 는 `configs` 맵을 가진 플러그인 형태(typescript-eslint /
|
|
28
|
+
* eslint-plugin-react 와 동일한 패턴)라 `extends: ['@3o3/<layer>']` 문자열로
|
|
29
|
+
* 참조된다. `<layer>` 는 base / react / all.
|
|
30
|
+
*/
|
|
31
|
+
const plugin = {
|
|
32
|
+
meta: { name: '@3o3/eslint-config' },
|
|
33
|
+
// `extends: ['@3o3/<layer>']` 문자열 참조용(typescript-eslint / react 패턴).
|
|
34
|
+
configs: {
|
|
35
|
+
base,
|
|
36
|
+
react: reactLayer,
|
|
37
|
+
all,
|
|
38
|
+
},
|
|
39
|
+
docs,
|
|
40
|
+
prettier: eslintConfigPrettier,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default plugin
|
|
44
|
+
|
|
45
|
+
// 배열 스프레드용 named export (기존 방식 유지)
|
|
46
|
+
export { base, all }
|
|
47
|
+
export { reactLayer as react }
|
|
48
|
+
export { docs }
|
|
49
|
+
export { eslintConfigPrettier as prettier }
|
package/src/react.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import tseslint from 'typescript-eslint'
|
|
2
|
+
import react from 'eslint-plugin-react'
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
|
+
import * as emotionPlugin from '@emotion/eslint-plugin'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* react 레이어 — base 위에 얹는 React 델타.
|
|
8
|
+
*
|
|
9
|
+
* eslint-plugin-react(flat recommended) + react-hooks(rules-of-hooks / exhaustive-deps)
|
|
10
|
+
* + @emotion 을 포함한다. base 를 먼저 스프레드한 뒤 이 레이어를 스프레드한다.
|
|
11
|
+
*
|
|
12
|
+
* react-hooks v7 의 flat preset(recommended-latest)은 React Compiler 시대의
|
|
13
|
+
* 16개 규칙 슈퍼셋이라 기존 동작과 크게 달라진다. 따라서 preset을 쓰지 않고
|
|
14
|
+
* 플러그인만 등록한 뒤 기존에 켜져 있던 두 규칙만 명시적으로 활성화한다.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```js
|
|
18
|
+
* import { base, react } from '@3o3/eslint-config'
|
|
19
|
+
* export default [...base, ...react]
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
const reactLayer = tseslint.config({
|
|
23
|
+
files: ['**/*.{ts,tsx}'],
|
|
24
|
+
extends: [react.configs.flat.recommended],
|
|
25
|
+
languageOptions: {
|
|
26
|
+
parserOptions: {
|
|
27
|
+
ecmaFeatures: { jsx: true },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
settings: {
|
|
31
|
+
react: { version: 'detect' },
|
|
32
|
+
},
|
|
33
|
+
plugins: {
|
|
34
|
+
// @emotion/eslint-plugin 11.x는 flat preset이 없어 수동으로 등록한다.
|
|
35
|
+
'@emotion': emotionPlugin,
|
|
36
|
+
'react-hooks': reactHooks,
|
|
37
|
+
},
|
|
38
|
+
rules: {
|
|
39
|
+
'@emotion/pkg-renaming': 'error',
|
|
40
|
+
|
|
41
|
+
'react/prop-types': 'off',
|
|
42
|
+
// React.memo, React.forwardRef에서 사용하는 경우도 막고 있어서
|
|
43
|
+
'react/display-name': 'off',
|
|
44
|
+
// 기존 v4 recommended preset에서 켜져 있던 규칙 (수동 등록 시 명시 필요)
|
|
45
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
46
|
+
'react-hooks/exhaustive-deps': 'error',
|
|
47
|
+
'react/react-in-jsx-scope': 'off',
|
|
48
|
+
'react/no-unknown-property': ['error', { ignore: ['css'] }],
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
export default reactLayer
|