@hyeon/linter 11.0.1-dev.2.41 → 11.0.1-dev.3.43
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 +81 -45
- package/biome.jsonc +79 -0
- package/package.json +16 -28
- package/src/index.mjs +23 -0
- package/src/oxlint.d.ts +0 -8
- package/src/oxlint.mjs +0 -38
- package/templates/oxfmt.base.jsonc +0 -23
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @hyeon/linter
|
|
2
2
|
|
|
3
3
|
```bash
|
|
4
|
-
npm install --save-dev @hyeon/linter
|
|
4
|
+
npm install --save-dev @hyeon/linter @biomejs/biome
|
|
5
5
|
```
|
|
6
6
|
|
|
7
7
|
> AI 사용자용: 설치/셋업 가이드는 [`install.md`](./install.md), 사용 예시는 [`USAGE_EXAMPLES.md`](./USAGE_EXAMPLES.md)를 먼저 확인하세요.
|
|
@@ -9,72 +9,108 @@ npm install --save-dev @hyeon/linter oxlint
|
|
|
9
9
|
## 지원 정책
|
|
10
10
|
|
|
11
11
|
- TypeScript 5.9+ 전용
|
|
12
|
-
-
|
|
12
|
+
- Biome 기반 (lint + format + import organize 단일 도구)
|
|
13
13
|
- ESLint/Prettier 레거시 경로는 v11에서 완전 제거됨
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## 왜 Biome인가
|
|
16
16
|
|
|
17
|
-
###
|
|
17
|
+
### 도구별 속도 벤치마크
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
import { defineConfig } from "oxlint";
|
|
21
|
-
import { hansanghyeon, recommended, react, typescript } from "@hyeon/linter";
|
|
19
|
+
> 출처: [oxc-project/bench-linter](https://github.com/oxc-project/bench-linter) (VS Code 코드베이스, M2 Max 12코어)
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
});
|
|
29
|
-
```
|
|
21
|
+
| 도구 | Lint 시간 | vs ESLint | Format 시간 | vs Prettier |
|
|
22
|
+
|---|---|---|---|---|
|
|
23
|
+
| **ESLint + Prettier** | 31s | 1x | 1x | 1x |
|
|
24
|
+
| **oxlint + oxfmt** | 139ms | **62x** 빠름 | **30x** 빠름 |
|
|
25
|
+
| **Biome** | 377ms | **~20x** 빠름 | **10x** 빠름 |
|
|
30
26
|
|
|
31
|
-
|
|
27
|
+
- oxlint이 Biome보다 ~2.5x 빠르지만, **둘 다 1초 미만**이라 개발자 피드백 루프에 체감 차이 없음
|
|
28
|
+
- 셋 다 **Rust 기반 멀티스레드(rayon)** — 파일 수 증가 시 코어 수에 비례해 스케일
|
|
29
|
+
- ESLint만 **싱글스레드(Node.js)** — 파일 수에 선형 비례해 느려짐
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
import { defineConfig } from "oxlint";
|
|
35
|
-
import { recommended, typescript } from "@hyeon/linter";
|
|
31
|
+
### 룰 커버리지
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
| 도구 | 총 룰 수 | type-aware | auto-fix | 플러그인 생태계 |
|
|
34
|
+
|---|---|---|---|---|
|
|
35
|
+
| **ESLint** | 700+ (+4000 플러그인) | ✅ 완전 | ✅ 우수 | 4000+ |
|
|
36
|
+
| **Biome v2** | 423+ | ✅ v2 추가 | ✅ 양호 | 성장 중 |
|
|
37
|
+
| **oxlint** | ~300 | ✅ tsgolint 43룰 | ⚠️ 제한적 | 최소 |
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
### import DX 커버리지 — Biome 선택 이유
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
| 기능 | ESLint 레거시 | oxlint+oxfmt | Biome |
|
|
42
|
+
|---|:---:|:---:|:---:|
|
|
43
|
+
| import 그룹 정렬 | ✅ | ✅ | ✅ |
|
|
44
|
+
| specifier 정렬 `{ a, B, c }` | ✅ | ❌ | ✅ |
|
|
45
|
+
| 중복 import 병합 | ✅ | ❌ | ✅ 자동 merge |
|
|
46
|
+
| import 최상단 강제 | ✅ | ❌ | ✅ chunk 기반 |
|
|
47
|
+
| import 뒤 빈 줄 | ✅ | ❌ | ✅ `:BLANK_LINE:` |
|
|
48
|
+
| consistent-type-imports | ✅ | ❌ | ✅ `useImportType` |
|
|
49
|
+
| 그룹 간 빈 줄 | ✅ | ⚠️ bool만 | ✅ `:BLANK_LINE:` |
|
|
50
|
+
| side-effect import 처리 | 수동 | 무시 | ✅ auto chunk |
|
|
51
|
+
| custom group (glob) | ❌ | 일부 | ✅ glob + `!` 예외 |
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
**oxlint+oxfmt은 속도 최강이지만 import DX 격차가 일상 코딩에 매번 영향을 줌. Biome은 기존 ESLint 경험의 대부분을 보존하면서 충분히 빠름.**
|
|
54
|
+
|
|
55
|
+
### 요약 매트릭스
|
|
56
|
+
|
|
57
|
+
| | 속도 | 룰 커버리지 | import DX | 단일 도구 | auto-fix |
|
|
58
|
+
|---|:---:|:---:|:---:|:---:|:---:|
|
|
59
|
+
| **oxlint+oxfmt** | ⭐⭐⭐ | ⭐⭐ | ⭐ | ❌ 2개 | ⚠️ |
|
|
60
|
+
| **Biome** | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ✅ 1개 | ✅ |
|
|
61
|
+
| **ESLint+Prettier** | ⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ❌ 2개+ | ✅ |
|
|
62
|
+
|
|
63
|
+
## 사용 가이드
|
|
64
|
+
|
|
65
|
+
### 린트 + 포맷 (`biome.json`)
|
|
66
|
+
|
|
67
|
+
```jsonc
|
|
68
|
+
{
|
|
69
|
+
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
|
|
70
|
+
"linter": {
|
|
71
|
+
"enabled": true,
|
|
72
|
+
"rules": { "recommended": true }
|
|
73
|
+
},
|
|
74
|
+
"formatter": {
|
|
75
|
+
"enabled": true,
|
|
76
|
+
"indentStyle": "space",
|
|
77
|
+
"indentWidth": 2,
|
|
78
|
+
"lineWidth": 120
|
|
79
|
+
},
|
|
80
|
+
"assist": {
|
|
81
|
+
"actions": {
|
|
82
|
+
"source": {
|
|
83
|
+
"organizeImports": "on"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
50
89
|
|
|
51
|
-
|
|
52
|
-
|
|
90
|
+
### 실행
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# 린트 + 포맷 + import 정리 한 번에
|
|
94
|
+
npx @biomejs/biome check --write .
|
|
53
95
|
|
|
54
|
-
#
|
|
55
|
-
npx
|
|
96
|
+
# CI (검사만, 수정 없음)
|
|
97
|
+
npx @biomejs/biome ci .
|
|
56
98
|
```
|
|
57
99
|
|
|
58
100
|
## v10 → v11 마이그레이션
|
|
59
101
|
|
|
60
|
-
v11은 **Breaking Change**입니다. ESLint/Prettier
|
|
102
|
+
v11은 **Breaking Change**입니다. ESLint/Prettier를 Biome으로 완전 교체합니다.
|
|
61
103
|
|
|
62
104
|
### 제거된 항목
|
|
63
105
|
|
|
64
|
-
-
|
|
65
|
-
- `@hyeon/linter/legacy`
|
|
66
|
-
- `@hyeon/linter/recommended` (ESLint base)
|
|
67
|
-
- `@hyeon/linter/typescript` (typescript-eslint)
|
|
68
|
-
- `@hyeon/linter/react` (eslint-plugin-react)
|
|
69
|
-
- `@hyeon/linter/prettier` (eslint-plugin-prettier)
|
|
70
|
-
- `@hyeon/linter/hansanghyeon` (ESLint custom)
|
|
106
|
+
- ESLint 설정 export (`./recommended`, `./typescript`, `./react`, `./prettier`, `./hansanghyeon`)
|
|
71
107
|
- `eslint`, `prettier` peerDependencies
|
|
72
|
-
-
|
|
108
|
+
- 모든 ESLint/Prettier 플러그인 의존성
|
|
73
109
|
|
|
74
110
|
### 마이그레이션 방법
|
|
75
111
|
|
|
76
112
|
1. `eslint.config.mjs` 삭제
|
|
77
|
-
2. `
|
|
78
|
-
3.
|
|
79
|
-
4. `
|
|
80
|
-
5. lint 스크립트를 `
|
|
113
|
+
2. `biome.json` 생성 (위 예시 참고)
|
|
114
|
+
3. `eslint`, `prettier` 및 관련 플러그인 devDependencies 제거
|
|
115
|
+
4. `@biomejs/biome` 설치
|
|
116
|
+
5. lint 스크립트를 `biome check` 또는 `biome ci` 로 변경
|
package/biome.jsonc
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.4.11/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": true
|
|
7
|
+
},
|
|
8
|
+
"linter": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"rules": {
|
|
11
|
+
"recommended": true,
|
|
12
|
+
"suspicious": {
|
|
13
|
+
"noExplicitAny": "off"
|
|
14
|
+
},
|
|
15
|
+
"style": {
|
|
16
|
+
"useImportType": {
|
|
17
|
+
"level": "error",
|
|
18
|
+
"options": { "style": "inlineType" }
|
|
19
|
+
},
|
|
20
|
+
"noUnusedTemplateLiteral": "off"
|
|
21
|
+
},
|
|
22
|
+
"correctness": {
|
|
23
|
+
"noUnusedVariables": "error",
|
|
24
|
+
"noUnusedImports": "error"
|
|
25
|
+
},
|
|
26
|
+
"nursery": {}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"formatter": {
|
|
30
|
+
"enabled": true,
|
|
31
|
+
"indentStyle": "space",
|
|
32
|
+
"indentWidth": 2,
|
|
33
|
+
"lineWidth": 120,
|
|
34
|
+
"lineEnding": "lf"
|
|
35
|
+
},
|
|
36
|
+
"javascript": {
|
|
37
|
+
"formatter": {
|
|
38
|
+
"quoteStyle": "single",
|
|
39
|
+
"jsxQuoteStyle": "double",
|
|
40
|
+
"trailingCommas": "all",
|
|
41
|
+
"semicolons": "asNeeded",
|
|
42
|
+
"arrowParentheses": "always"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"assist": {
|
|
46
|
+
"actions": {
|
|
47
|
+
"source": {
|
|
48
|
+
"organizeImports": {
|
|
49
|
+
"level": "on",
|
|
50
|
+
"options": {
|
|
51
|
+
"groups": [
|
|
52
|
+
":URL:",
|
|
53
|
+
[":BUN:", ":NODE:"],
|
|
54
|
+
":BLANK_LINE:",
|
|
55
|
+
":PACKAGE:",
|
|
56
|
+
":BLANK_LINE:",
|
|
57
|
+
":ALIAS:",
|
|
58
|
+
":BLANK_LINE:",
|
|
59
|
+
":PATH:"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"overrides": [
|
|
67
|
+
{
|
|
68
|
+
"includes": ["*.json", "*.jsonc"],
|
|
69
|
+
"formatter": {
|
|
70
|
+
"indentWidth": 2
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"includes": ["test/src/fixtures/**", "test/src/examples/**"],
|
|
75
|
+
"linter": { "enabled": false },
|
|
76
|
+
"assist": { "enabled": false }
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
package/package.json
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyeon/linter",
|
|
3
|
-
"version": "11.0.1-dev.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "./src/
|
|
3
|
+
"version": "11.0.1-dev.3.43",
|
|
4
|
+
"description": "Biome-based lint, format, and import organize configuration",
|
|
5
|
+
"main": "./src/index.mjs",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"lint": "
|
|
8
|
-
"lint:
|
|
9
|
-
"lint:oxlint:syntax": "oxlint --config oxlint.config.ts src test/src",
|
|
10
|
-
"format:write": "npx --no-install oxfmt --config ./.oxfmtrc.jsonc ./templates/oxfmt.base.jsonc",
|
|
11
|
-
"format:check": "npx --no-install oxfmt --config ./.oxfmtrc.jsonc --check ./templates/oxfmt.base.jsonc",
|
|
8
|
+
"lint": "biome ci .",
|
|
9
|
+
"lint:fix": "biome check --write .",
|
|
12
10
|
"test": "npm run lint && npm --prefix test run test:all"
|
|
13
11
|
},
|
|
14
12
|
"repository": {
|
|
15
13
|
"type": "git",
|
|
16
|
-
"url": "git+https://git.hyeon.pro/
|
|
14
|
+
"url": "git+https://git.hyeon.pro/space/tools/linter.git"
|
|
17
15
|
},
|
|
18
16
|
"keywords": [
|
|
19
|
-
"
|
|
20
|
-
"oxfmt",
|
|
21
|
-
"oxc",
|
|
17
|
+
"biome",
|
|
22
18
|
"linter",
|
|
23
19
|
"formatter",
|
|
24
20
|
"hyeon",
|
|
@@ -31,36 +27,28 @@
|
|
|
31
27
|
"author": "hyeon",
|
|
32
28
|
"license": "MIT",
|
|
33
29
|
"bugs": {
|
|
34
|
-
"url": "https://git.hyeon.pro/
|
|
30
|
+
"url": "https://git.hyeon.pro/space/tools/linter/issues"
|
|
35
31
|
},
|
|
36
|
-
"homepage": "https://git.hyeon.pro/
|
|
32
|
+
"homepage": "https://git.hyeon.pro/space/tools/linter#readme",
|
|
37
33
|
"dependencies": {
|
|
38
34
|
"typescript": "^5.9.3"
|
|
39
35
|
},
|
|
40
36
|
"peerDependencies": {
|
|
41
|
-
"
|
|
37
|
+
"@biomejs/biome": ">=2.0.0"
|
|
42
38
|
},
|
|
43
39
|
"devDependencies": {
|
|
44
|
-
"
|
|
45
|
-
"oxlint": "^1.52.0",
|
|
46
|
-
"oxlint-tsgolint": "^0.16.0"
|
|
40
|
+
"@biomejs/biome": "^2.4.11"
|
|
47
41
|
},
|
|
48
42
|
"exports": {
|
|
49
43
|
".": {
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"default": "./src/oxlint.mjs"
|
|
44
|
+
"import": "./src/index.mjs",
|
|
45
|
+
"default": "./src/index.mjs"
|
|
53
46
|
},
|
|
54
|
-
"./
|
|
55
|
-
"types": "./src/oxlint.d.ts",
|
|
56
|
-
"import": "./src/oxlint.mjs",
|
|
57
|
-
"default": "./src/oxlint.mjs"
|
|
58
|
-
},
|
|
59
|
-
"./oxfmt-template": "./templates/oxfmt.base.jsonc",
|
|
47
|
+
"./biome-config": "./biome.jsonc",
|
|
60
48
|
"./package.json": "./package.json"
|
|
61
49
|
},
|
|
62
50
|
"files": [
|
|
63
51
|
"src",
|
|
64
|
-
"
|
|
52
|
+
"biome.jsonc"
|
|
65
53
|
]
|
|
66
54
|
}
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @hyeon/linter Biome 설정을 반환합니다.
|
|
9
|
+
* biome.jsonc를 파싱하여 JSON 객체로 제공합니다.
|
|
10
|
+
*/
|
|
11
|
+
export function getConfig() {
|
|
12
|
+
const configPath = join(__dirname, '..', 'biome.jsonc')
|
|
13
|
+
const raw = readFileSync(configPath, 'utf8')
|
|
14
|
+
|
|
15
|
+
// JSONC 파싱 (주석/trailing comma 제거)
|
|
16
|
+
const stripped = raw
|
|
17
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
18
|
+
.replace(/(^|[^:])\/\/.*$/gm, '$1')
|
|
19
|
+
.replace(/,\s*([\]}])/g, '$1')
|
|
20
|
+
return JSON.parse(stripped)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default getConfig
|
package/src/oxlint.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { OxlintConfig } from "oxlint";
|
|
2
|
-
|
|
3
|
-
type OxlintConfigSnippet = Omit<OxlintConfig, "extends" | "$schema">;
|
|
4
|
-
|
|
5
|
-
export declare const recommended: OxlintConfigSnippet;
|
|
6
|
-
export declare const typescript: OxlintConfigSnippet;
|
|
7
|
-
export declare const react: OxlintConfigSnippet;
|
|
8
|
-
export declare const hansanghyeon: OxlintConfigSnippet;
|
package/src/oxlint.mjs
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const fullRepoIgnorePatterns = [
|
|
2
|
-
"**/node_modules/**",
|
|
3
|
-
"**/dist/**",
|
|
4
|
-
"**/.turbo/**",
|
|
5
|
-
"**/coverage/**",
|
|
6
|
-
];
|
|
7
|
-
|
|
8
|
-
export const recommended = {
|
|
9
|
-
ignorePatterns: fullRepoIgnorePatterns,
|
|
10
|
-
categories: {
|
|
11
|
-
correctness: "warn",
|
|
12
|
-
suspicious: "warn",
|
|
13
|
-
pedantic: "off",
|
|
14
|
-
perf: "off",
|
|
15
|
-
style: "off",
|
|
16
|
-
restriction: "off",
|
|
17
|
-
nursery: "off",
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const typescript = {
|
|
22
|
-
plugins: ["typescript"],
|
|
23
|
-
rules: {
|
|
24
|
-
"typescript/no-explicit-any": "off",
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const react = {
|
|
29
|
-
plugins: ["react"],
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export const hansanghyeon = {
|
|
33
|
-
rules: {
|
|
34
|
-
"no-empty-pattern": "off",
|
|
35
|
-
camelcase: "off",
|
|
36
|
-
"no-multi-spaces": "off",
|
|
37
|
-
},
|
|
38
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"printWidth": 120,
|
|
3
|
-
"tabWidth": 2,
|
|
4
|
-
"useTabs": false,
|
|
5
|
-
"semi": false,
|
|
6
|
-
"singleQuote": true,
|
|
7
|
-
"jsxSingleQuote": false,
|
|
8
|
-
"trailingComma": "all",
|
|
9
|
-
"sortImports": {
|
|
10
|
-
"newlinesBetween": false,
|
|
11
|
-
"internalPattern": ["~/", "@/"],
|
|
12
|
-
"groups": [
|
|
13
|
-
["value-builtin", "value-external", "value-internal", "value-subpath"],
|
|
14
|
-
["type-builtin", "type-external", "type-internal", "type-subpath"],
|
|
15
|
-
["value-parent", "value-sibling", "value-index"],
|
|
16
|
-
["type-parent", "type-sibling", "type-index"],
|
|
17
|
-
"unknown",
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
"sortTailwindcss": {
|
|
21
|
-
"functions": ["clsx", "cn"],
|
|
22
|
-
},
|
|
23
|
-
}
|