Skip to content

Commit 2ba9e23

Browse files
committed
chore(lint): migrate to oxlint and oxfmt
Move linting to Oxlint and formatting to Oxfmt, keeping a small residual ESLint for what Oxlint cannot do (import-x/order, Markdown, package.json). - Add .oxlintrc.json with correctness category + curated rules from all categories, plus .oxlintrc.react.json for react/react-x rules - Add .oxfmtrc.json for formatting with oxfmt - Keep eslint.config.mjs for import-x/order, Markdown, and package.json linting (documented in CONTRIBUTING.md) - Run TypeScript 7 (tsc) side-by-side with TypeScript 6 (API for typescript-eslint) - Cap oxlint/oxfmt thread count at 8 (scripts/run-oxc.mjs) - Fix 5 no-misused-spread and require-array-sort-compare findings - Guard against oxlint config regressions with test/unit/oxlintConfig.test.ts Benchmarks: CI lint job -80% (26.3s -> 5.2s), format:check -91% (3.22s -> 0.30s), typecheck -83% (5.43s -> 0.95s). Fixes OOM on default heap.
1 parent c51f472 commit 2ba9e23

47 files changed

Lines changed: 1674 additions & 448 deletions

Some content is hidden

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

.oxfmtrc.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
3+
"printWidth": 80,
4+
// Disabled to avoid churning the key order of every package.json in the repo.
5+
"sortPackageJson": false,
6+
"ignorePatterns": [
7+
"/dist/",
8+
"/node_modules/",
9+
"/out/",
10+
"/.vscode-test/",
11+
"/coverage/",
12+
"*.vsix",
13+
"flake.lock",
14+
"pnpm-lock.yaml",
15+
"/storybook-static/",
16+
"**/__golden__/",
17+
".storybook/themes/generated/"
18+
],
19+
"overrides": [
20+
{
21+
"files": ["*.jsonc"],
22+
"options": {
23+
"trailingComma": "none"
24+
}
25+
}
26+
]
27+
}

.oxlintrc.json

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
{
2+
"$schema": "./node_modules/oxlint/configuration_schema.json",
3+
"extends": [".oxlintrc.react.json"],
4+
"plugins": ["unicorn"],
5+
"categories": {
6+
"correctness": "error"
7+
},
8+
"options": {
9+
"typeAware": true
10+
},
11+
"env": {
12+
"builtin": true
13+
},
14+
"ignorePatterns": [
15+
"out/**",
16+
"dist/**",
17+
"**/*.d.ts",
18+
"**/vite.config*.ts",
19+
".vscode-test/**",
20+
"test/fixtures/scripts/**",
21+
"storybook-static/**"
22+
],
23+
"overrides": [
24+
{
25+
"files": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
26+
"plugins": ["typescript", "import"],
27+
"rules": {
28+
"no-case-declarations": "error",
29+
"no-empty": "error",
30+
"no-fallthrough": "error",
31+
"no-prototype-builtins": "error",
32+
"no-regex-spaces": "error",
33+
"preserve-caught-error": "error",
34+
"no-var": "error",
35+
"prefer-const": "error",
36+
"prefer-rest-params": "error",
37+
"prefer-spread": "error",
38+
"no-array-constructor": "error",
39+
"no-unused-expressions": "error",
40+
"no-unused-vars": [
41+
"error",
42+
{
43+
"varsIgnorePattern": "^_",
44+
"argsIgnorePattern": "^_"
45+
}
46+
],
47+
"no-throw-literal": "error",
48+
"require-await": "off",
49+
"typescript/await-thenable": "error",
50+
"typescript/ban-ts-comment": "error",
51+
"typescript/no-array-delete": "error",
52+
"typescript/no-base-to-string": "error",
53+
"typescript/no-duplicate-enum-values": "error",
54+
"typescript/no-duplicate-type-constituents": "error",
55+
"typescript/no-empty-object-type": "error",
56+
"typescript/no-explicit-any": "error",
57+
"typescript/no-extra-non-null-assertion": "error",
58+
"typescript/no-floating-promises": "error",
59+
"typescript/no-for-in-array": "error",
60+
"typescript/no-implied-eval": "error",
61+
"typescript/no-misused-new": "error",
62+
"typescript/no-misused-promises": "error",
63+
"typescript/no-namespace": "error",
64+
"typescript/no-non-null-asserted-optional-chain": "error",
65+
"typescript/no-redundant-type-constituents": "error",
66+
"typescript/no-require-imports": "error",
67+
"typescript/no-this-alias": "error",
68+
"typescript/no-unnecessary-type-assertion": "error",
69+
"typescript/no-unnecessary-type-constraint": "error",
70+
"typescript/no-unsafe-argument": "error",
71+
"typescript/no-unsafe-assignment": "error",
72+
"typescript/no-unsafe-call": "error",
73+
"typescript/no-unsafe-declaration-merging": "error",
74+
"typescript/no-unsafe-enum-comparison": "error",
75+
"typescript/no-unsafe-function-type": "error",
76+
"typescript/no-unsafe-member-access": "error",
77+
"typescript/no-unsafe-return": "error",
78+
"typescript/no-unsafe-unary-minus": "error",
79+
"typescript/no-wrapper-object-types": "error",
80+
"typescript/only-throw-error": "error",
81+
"typescript/prefer-as-const": "error",
82+
"typescript/prefer-namespace-keyword": "error",
83+
"typescript/prefer-promise-reject-errors": "error",
84+
"typescript/require-await": "error",
85+
"typescript/restrict-plus-operands": "error",
86+
"typescript/restrict-template-expressions": "error",
87+
"typescript/triple-slash-reference": "error",
88+
"typescript/unbound-method": "error",
89+
"no-empty-function": "error",
90+
"typescript/adjacent-overload-signatures": "error",
91+
"typescript/array-type": [
92+
"error",
93+
{
94+
"default": "array-simple"
95+
}
96+
],
97+
"typescript/ban-tslint-comment": "error",
98+
"typescript/class-literal-property-style": "error",
99+
"typescript/consistent-generic-constructors": "error",
100+
"typescript/consistent-indexed-object-style": "error",
101+
"typescript/consistent-type-assertions": "error",
102+
"typescript/consistent-type-definitions": "error",
103+
"typescript/dot-notation": [
104+
"error",
105+
{
106+
"allowIndexSignaturePropertyAccess": true
107+
}
108+
],
109+
"typescript/no-confusing-non-null-assertion": "error",
110+
"typescript/no-inferrable-types": "error",
111+
"typescript/non-nullable-type-assertion-style": "error",
112+
"typescript/prefer-find": "error",
113+
"typescript/prefer-for-of": "error",
114+
"typescript/prefer-function-type": "error",
115+
"typescript/prefer-includes": "error",
116+
"typescript/prefer-nullish-coalescing": [
117+
"error",
118+
{
119+
"ignorePrimitives": {
120+
"string": true
121+
}
122+
}
123+
],
124+
"typescript/prefer-regexp-exec": "error",
125+
"typescript/prefer-string-starts-ends-with": "error",
126+
"eqeqeq": "error",
127+
"no-console": "error",
128+
"no-duplicate-imports": "off",
129+
"import/no-duplicates": [
130+
"error",
131+
{
132+
"prefer-inline": true
133+
}
134+
],
135+
"typescript/consistent-type-imports": "error",
136+
"typescript/switch-exhaustiveness-check": [
137+
"error",
138+
{
139+
"considerDefaultExhaustiveForUnions": true
140+
}
141+
],
142+
"typescript/no-non-null-assertion": "error"
143+
}
144+
},
145+
{
146+
"files": [
147+
"test/**/*.{ts,tsx}",
148+
"**/*.test.{ts,tsx}",
149+
"**/*.spec.{ts,tsx}"
150+
],
151+
"plugins": ["typescript"],
152+
"rules": {
153+
"no-empty-function": "off",
154+
"typescript/consistent-type-imports": [
155+
"error",
156+
{
157+
"disallowTypeAnnotations": false
158+
}
159+
],
160+
"typescript/unbound-method": "off",
161+
"typescript/no-non-null-assertion": "off",
162+
"typescript/no-unsafe-assignment": "off",
163+
"typescript/no-unsafe-call": "off",
164+
"typescript/no-unsafe-return": "off"
165+
}
166+
},
167+
{
168+
"files": [
169+
"esbuild.mjs",
170+
"scripts/*.mjs",
171+
".storybook/themes/*.{mjs,cjs}"
172+
],
173+
"env": {
174+
"node": true
175+
}
176+
},
177+
{
178+
"files": ["packages/*/src/**/*.ts", "packages/*/src/**/*.tsx"],
179+
"env": {
180+
"browser": true
181+
},
182+
"rules": {
183+
"no-restricted-imports": [
184+
"error",
185+
{
186+
"patterns": [
187+
{
188+
"group": ["@repo/mocks", "@repo/mocks/*"],
189+
"message": "@repo/mocks is for tests and stories only. Do not import it from runtime code."
190+
},
191+
{
192+
"group": ["@repo/storybook-utils", "@repo/storybook-utils/*"],
193+
"message": "@repo/storybook-utils is for stories only. Do not import it from runtime code."
194+
}
195+
]
196+
}
197+
]
198+
}
199+
},
200+
{
201+
"files": ["packages/ui/**/*.{ts,tsx}"],
202+
"rules": {
203+
"no-restricted-imports": [
204+
"error",
205+
{
206+
"patterns": [
207+
{
208+
"group": ["@repo/*"],
209+
"message": "packages/ui must not import other workspace packages."
210+
},
211+
{
212+
"group": ["../../../**"],
213+
"message": "packages/ui must not reach outside itself via relative paths."
214+
}
215+
]
216+
}
217+
]
218+
}
219+
},
220+
{
221+
"files": ["packages/**/*.stories.ts", "packages/**/*.stories.tsx"],
222+
"rules": {
223+
"no-restricted-imports": "off"
224+
}
225+
},
226+
{
227+
"files": [
228+
"**/*.ts",
229+
"**/*.tsx",
230+
"**/*.mts",
231+
"**/*.cts",
232+
"**/*.js",
233+
"**/*.mjs",
234+
"**/*.cjs"
235+
],
236+
"jsPlugins": [
237+
{
238+
"name": "eslint-js",
239+
"specifier": "oxlint-plugin-eslint"
240+
}
241+
],
242+
"rules": {
243+
"eslint-js/no-restricted-syntax": [
244+
"error",
245+
{
246+
"selector": "CallExpression[callee.property.name='executeCommand'][arguments.0.value='setContext'][arguments.length>=3]",
247+
"message": "Do not use executeCommand('setContext', ...) directly. Use the ContextManager class instead."
248+
},
249+
{
250+
"selector": "CallExpression[callee.property.name='registerCommand'][arguments.0.value=/^coder\\./][arguments.length>=2]",
251+
"message": "Do not use registerCommand('coder.*', ...) directly. Use the CommandManager class instead."
252+
},
253+
{
254+
"selector": "MemberExpression[property.name='remoteAuthority'][object.property.name='env'][object.object.name='vscode']",
255+
"message": "env.remoteAuthority is a proposed API (resolvers) and throws through our own vscode module. Read it via vscodeProposed.env.remoteAuthority."
256+
},
257+
{
258+
"selector": "JSXExpressionContainer > LogicalExpression[operator='&&'][left.property.name='length']",
259+
"message": "Numeric '.length' as a JSX '&&' operand renders 0 when empty. Use '.length > 0' or a ternary."
260+
},
261+
{
262+
"selector": "JSXExpressionContainer > LogicalExpression[operator='&&'][left.type='CallExpression'][left.callee.property.name='indexOf']",
263+
"message": "Numeric 'indexOf()' as a JSX '&&' operand renders 0 for a match at index 0. Compare explicitly."
264+
}
265+
],
266+
"eslint-js/no-useless-assignment": "error"
267+
}
268+
},
269+
{
270+
"files": ["src/core/contextManager.ts", "src/core/commandManager.ts"],
271+
"rules": {
272+
"eslint-js/no-restricted-syntax": "off"
273+
}
274+
},
275+
{
276+
"files": [
277+
"**/*.stories.ts",
278+
"**/*.stories.tsx",
279+
"**/*.story.ts",
280+
"**/*.story.tsx"
281+
],
282+
"jsPlugins": [
283+
{
284+
"name": "storybook",
285+
"specifier": "./node_modules/eslint-plugin-storybook/dist/index.js"
286+
}
287+
],
288+
"rules": {
289+
"storybook/await-interactions": "error",
290+
"storybook/context-in-play-function": "error",
291+
"storybook/default-exports": "error",
292+
"storybook/hierarchy-separator": "error",
293+
"storybook/no-redundant-story-name": "error",
294+
"storybook/no-renderer-packages": "error",
295+
"storybook/prefer-pascal-case": "error",
296+
"storybook/story-exports": "error",
297+
"storybook/use-storybook-expect": "error",
298+
"storybook/use-storybook-testing-library": "error"
299+
}
300+
},
301+
{
302+
"files": [".storybook/main.ts"],
303+
"jsPlugins": [
304+
{
305+
"name": "storybook",
306+
"specifier": "./node_modules/eslint-plugin-storybook/dist/index.js"
307+
}
308+
],
309+
"rules": {
310+
"storybook/no-uninstalled-addons": "error"
311+
}
312+
}
313+
]
314+
}

0 commit comments

Comments
 (0)