Skip to content

Commit 156908c

Browse files
committed
chore(web): migrate from ESLint+Prettier to Biome
- Install @biomejs/[email protected] as unified linter and formatter - Remove ESLint, Prettier and all related plugins (221 packages removed) - Migrate linting rules from ESLint to Biome configuration - Migrate formatting rules from Prettier to Biome configuration - Exclude auto-generated proto files from linting (src/types/proto/**) - Exclude CSS files from Biome (Tailwind syntax not yet supported) - Update package.json scripts: - lint: tsc + biome check - lint:fix: biome check --write - format: biome format --write - Auto-fix import organization across 60+ files - Fix duplicate key in Russian locale (ru.json) - Update CLAUDE.md documentation to reflect Biome usage Benefits: - 10-100x faster linting performance - Simplified toolchain with single configuration file - 221 fewer npm dependencies - Unified linting, formatting, and import organization
1 parent 6411136 commit 156908c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+450
-2358
lines changed

CLAUDE.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,12 @@ pnpm build # Build to web/dist/
6060
pnpm release # Build and copy to server/router/frontend/dist/
6161
```
6262

63-
**Lint:**
63+
**Lint and Format:**
6464
```bash
6565
cd web
66-
pnpm lint # TypeScript check + ESLint
66+
pnpm lint # TypeScript check + Biome lint
67+
pnpm lint:fix # Auto-fix linting issues
68+
pnpm format # Format code with Biome
6769
```
6870

6971
### CLI Flags and Environment Variables
@@ -209,9 +211,10 @@ The frontend uses `nice-grpc-web` to call backend services. Client setup is in `
209211

210212
- **Components:** PascalCase filenames (e.g., `MemoEditor.tsx`)
211213
- **Hooks:** camelCase filenames (e.g., `useMemoList.ts`)
212-
- **Formatting:** Prettier enforced (see `web/.prettierrc.js`)
213-
- **Import Ordering:** Managed by `@trivago/prettier-plugin-sort-imports`
214+
- **Formatting:** Biome enforced (see `web/biome.json`)
215+
- **Import Ordering:** Automatic via Biome's organize imports
214216
- **Styling:** Tailwind utility classes preferred over custom CSS
217+
- **Linting:** Biome replaces ESLint and Prettier for faster, unified tooling
215218

216219
### Commit Messages
217220

@@ -338,6 +341,7 @@ For SQLite (default), all data is stored in the directory specified by `--data`
338341
- `react-router-dom` - Routing
339342
- `mobx` / `mobx-react-lite` - State management
340343
- `tailwindcss` - Styling
344+
- `@biomejs/biome` - Fast linter and formatter (replaces ESLint + Prettier)
341345
- `nice-grpc-web` - gRPC-Web client
342346
- `@radix-ui/*` - Headless UI components
343347
- `react-i18next` - Internationalization

web/.prettierrc.js

Lines changed: 0 additions & 8 deletions
This file was deleted.

web/biome.json

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/2.3.5/schema.json",
3+
"vcs": {
4+
"enabled": true,
5+
"clientKind": "git",
6+
"useIgnoreFile": true
7+
},
8+
"files": {
9+
"includes": [
10+
"**",
11+
"!!**/dist",
12+
"!src/types/proto"
13+
],
14+
"ignoreUnknown": true
15+
},
16+
"formatter": {
17+
"enabled": true,
18+
"formatWithErrors": false,
19+
"indentStyle": "space",
20+
"indentWidth": 2,
21+
"lineEnding": "lf",
22+
"lineWidth": 140,
23+
"attributePosition": "auto",
24+
"bracketSameLine": false,
25+
"bracketSpacing": true,
26+
"expand": "auto",
27+
"useEditorconfig": true
28+
},
29+
"linter": {
30+
"enabled": true,
31+
"rules": {
32+
"recommended": false,
33+
"complexity": {
34+
"noAdjacentSpacesInRegex": "error",
35+
"noExtraBooleanCast": "error",
36+
"noUselessCatch": "error",
37+
"noUselessEscapeInRegex": "error",
38+
"noUselessTypeConstraint": "error"
39+
},
40+
"correctness": {
41+
"noConstAssign": "error",
42+
"noConstantCondition": "error",
43+
"noEmptyCharacterClassInRegex": "error",
44+
"noEmptyPattern": "error",
45+
"noGlobalObjectCalls": "error",
46+
"noInvalidBuiltinInstantiation": "error",
47+
"noInvalidConstructorSuper": "error",
48+
"noNonoctalDecimalEscape": "error",
49+
"noPrecisionLoss": "error",
50+
"noSelfAssign": "error",
51+
"noSetterReturn": "error",
52+
"noSwitchDeclarations": "error",
53+
"noUndeclaredVariables": "error",
54+
"noUnreachable": "error",
55+
"noUnreachableSuper": "error",
56+
"noUnsafeFinally": "error",
57+
"noUnsafeOptionalChaining": "error",
58+
"noUnusedLabels": "error",
59+
"noUnusedPrivateClassMembers": "error",
60+
"noUnusedVariables": "error",
61+
"useIsNan": "error",
62+
"useValidForDirection": "error",
63+
"useValidTypeof": "error",
64+
"useYield": "error"
65+
},
66+
"style": {
67+
"noCommonJs": "error",
68+
"noNamespace": "error",
69+
"useArrayLiterals": "error",
70+
"useAsConstAssertion": "error",
71+
"useBlockStatements": "off"
72+
},
73+
"suspicious": {
74+
"noAsyncPromiseExecutor": "error",
75+
"noCatchAssign": "error",
76+
"noClassAssign": "error",
77+
"noCompareNegZero": "error",
78+
"noConstantBinaryExpressions": "error",
79+
"noControlCharactersInRegex": "error",
80+
"noDebugger": "error",
81+
"noDuplicateCase": "error",
82+
"noDuplicateClassMembers": "error",
83+
"noDuplicateElseIf": "error",
84+
"noDuplicateObjectKeys": "error",
85+
"noDuplicateParameters": "error",
86+
"noEmptyBlockStatements": "off",
87+
"noExplicitAny": "off",
88+
"noExtraNonNullAssertion": "error",
89+
"noFallthroughSwitchClause": "error",
90+
"noFunctionAssign": "error",
91+
"noGlobalAssign": "error",
92+
"noImportAssign": "error",
93+
"noIrregularWhitespace": "error",
94+
"noMisleadingCharacterClass": "error",
95+
"noMisleadingInstantiator": "error",
96+
"noNonNullAssertedOptionalChain": "error",
97+
"noPrototypeBuiltins": "error",
98+
"noRedeclare": "error",
99+
"noShadowRestrictedNames": "error",
100+
"noSparseArray": "error",
101+
"noUnsafeDeclarationMerging": "error",
102+
"noUnsafeNegation": "error",
103+
"noUselessRegexBackrefs": "error",
104+
"noWith": "error",
105+
"useGetterReturn": "error",
106+
"useNamespaceKeyword": "error"
107+
}
108+
},
109+
"includes": [
110+
"**",
111+
"!**/dist/**",
112+
"!**/node_modules/**",
113+
"!src/types/proto/**"
114+
]
115+
},
116+
"javascript": {
117+
"formatter": {
118+
"jsxQuoteStyle": "double",
119+
"quoteProperties": "asNeeded",
120+
"trailingCommas": "all",
121+
"semicolons": "always",
122+
"arrowParentheses": "always",
123+
"bracketSameLine": false,
124+
"quoteStyle": "double",
125+
"attributePosition": "auto",
126+
"bracketSpacing": true
127+
},
128+
"globals": []
129+
},
130+
"css": {
131+
"parser": {
132+
"cssModules": false,
133+
"allowWrongLineComments": true,
134+
"tailwindDirectives": true
135+
}
136+
},
137+
"html": {
138+
"formatter": {
139+
"indentScriptAndStyle": false,
140+
"selfCloseVoidElements": "always"
141+
}
142+
},
143+
"overrides": [
144+
{
145+
"includes": [
146+
"**/*.ts",
147+
"**/*.tsx",
148+
"**/*.mts",
149+
"**/*.cts"
150+
],
151+
"linter": {
152+
"rules": {
153+
"complexity": {
154+
"noArguments": "error"
155+
},
156+
"correctness": {
157+
"noConstAssign": "off",
158+
"noGlobalObjectCalls": "off",
159+
"noInvalidBuiltinInstantiation": "off",
160+
"noInvalidConstructorSuper": "off",
161+
"noSetterReturn": "off",
162+
"noUndeclaredVariables": "off",
163+
"noUnreachable": "off",
164+
"noUnreachableSuper": "off"
165+
},
166+
"style": {
167+
"useConst": "error"
168+
},
169+
"suspicious": {
170+
"noClassAssign": "off",
171+
"noDuplicateClassMembers": "off",
172+
"noDuplicateObjectKeys": "off",
173+
"noDuplicateParameters": "off",
174+
"noFunctionAssign": "off",
175+
"noImportAssign": "off",
176+
"noRedeclare": "off",
177+
"noUnsafeNegation": "off",
178+
"noVar": "error",
179+
"noWith": "off",
180+
"useGetterReturn": "off"
181+
}
182+
}
183+
}
184+
},
185+
{
186+
"includes": [
187+
"src/utils/i18n.ts"
188+
],
189+
"linter": {
190+
"rules": {}
191+
}
192+
}
193+
],
194+
"assist": {
195+
"enabled": true,
196+
"actions": {
197+
"source": {
198+
"organizeImports": "on"
199+
}
200+
}
201+
}
202+
}

web/eslint.config.mjs

Lines changed: 0 additions & 34 deletions
This file was deleted.

web/package.json

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"dev": "vite",
66
"build": "vite build",
77
"release": "vite build --mode release --outDir=../server/router/frontend/dist --emptyOutDir",
8-
"lint": "tsc --noEmit --skipLibCheck && eslint --ext .js,.ts,.tsx, src"
8+
"lint": "tsc --noEmit --skipLibCheck && biome check src",
9+
"lint:fix": "biome check --write src",
10+
"format": "biome format --write src"
911
},
1012
"dependencies": {
1113
"@dnd-kit/core": "^6.3.1",
@@ -63,9 +65,8 @@
6365
"uuid": "^11.1.0"
6466
},
6567
"devDependencies": {
68+
"@biomejs/biome": "2.3.5",
6669
"@bufbuild/protobuf": "^2.8.0",
67-
"@eslint/js": "^9.35.0",
68-
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
6970
"@types/d3": "^7.4.3",
7071
"@types/katex": "^0.16.7",
7172
"@types/leaflet": "^1.9.20",
@@ -79,17 +80,11 @@
7980
"@types/unist": "^3.0.3",
8081
"@types/uuid": "^10.0.0",
8182
"@vitejs/plugin-react": "^4.7.0",
82-
"eslint": "^9.35.0",
83-
"eslint-config-prettier": "^10.1.8",
84-
"eslint-plugin-prettier": "^5.5.4",
85-
"eslint-plugin-react": "^7.37.5",
8683
"long": "^5.3.2",
8784
"nice-grpc-web": "^3.3.8",
88-
"prettier": "^3.6.2",
8985
"terser": "^5.44.0",
9086
"tw-animate-css": "^1.4.0",
9187
"typescript": "^5.9.3",
92-
"typescript-eslint": "^8.45.0",
9388
"vite": "^7.1.5"
9489
},
9590
"pnpm": {

0 commit comments

Comments
 (0)