Skip to content

Commit 58d31f8

Browse files
Migrate from ESLint to Oxlint for faster linting (#653)
* Migrate ESLint to Oxlint with @nkzw/oxlint-config Replace ESLint 8 + @typescript-eslint with oxlint + oxlint-tsgolint for type-aware linting. Uses @nkzw/oxlint-config as the shared base config with conservative rule overrides to preserve the original lint surface (no new violations requiring codebase changes). - Add: oxlint, oxlint-tsgolint, @nkzw/oxlint-config and required JS plugins - Remove: eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser, eslint-config-prettier - Create oxlint.config.ts with calibrated overrides that disable @nkzw rules beyond the original ESLint scope (perfectionist sorting, unicorn stylistic rules, react-hooks-js extras, etc.) - Disable unused-imports/no-unused-imports due to @typescript-eslint compat crash in oxlint's JS plugin runner on TSMappedType - Update package.json lint scripts: eslint → oxlint --type-aware - Update .lintstagedrc.json: eslint --fix → oxlint --fix - Update CI workflow job name - Delete .eslintrc.js and .eslintignore https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Simplify oxlint config: drop @nkzw/oxlint-config and all JS plugins Use oxlint + oxlint-tsgolint with standard defaults only. No third-party plugin layer needed — oxlint's built-in rules cover the correctness and TypeScript-aware checks we care about. - Remove: @nkzw/oxlint-config, @nkzw/eslint-plugin, eslint-plugin-no-only-tests, eslint-plugin-perfectionist, eslint-plugin-react-hooks, eslint-plugin-unused-imports - Simplify oxlint.config.ts to just ignorePatterns + typescript plugin + the three custom rules from original ESLint (no-console, no-var-requires off, unbound-method off) Result: pnpm lint exits 0 (194 warnings from tsgolint type-aware defaults, 0 errors) https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Add --max-warnings 0; fix/suppress all warnings for clean lint - Remove dead overrides (no-var-requires, unbound-method had 0 violations) - Disable no-redundant-type-constituents and no-duplicate-type-constituents: systematic false positives from neo4j-driver types using `any` internally, causing cascading type issues in unions across ~40 files - Add --max-warnings 0 to lint script for strict mode going forward - Fix no-unused-vars: 15 catch bindings → `catch { }` (unused params) - Fix no-unused-expressions: convert ternary-as-statement to if/else, fix comma-assignment syntax, add missing `return`, remove stray `1;` - Add file-level oxlint-disable for no-misused-spread in test file (intentional plain-object spreads of class instances for comparison) - Fix require-array-sort-compare: .sort() → .sort((a, b) => a.localeCompare(b)) - Add inline disable for restrict-template-expressions and no-meaningless-void-operator (type-cascade false positive / preserve behavior) Result: pnpm lint exits 0 with 0 warnings and 0 errors https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Move no-misused-spread to config-level off, remove file disable comment https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Restore unbound-method: off for Playwright mount fixture false positives The `mount` parameter in Playwright e2e tests is a test fixture destructured from the test context, not a class method — oxlint incorrectly flags it as an unbound method reference. This restores the suppression that was mistakenly removed in the previous commit. https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Replace vscode-eslint recommendation with oxc-vscode; clean up tsconfig.node - Swap dbaeumer.vscode-eslint for oxc.oxc-vscode in .vscode/extensions.json - Remove .eslintrc.js from tsconfig.node.json include (file no longer exists) https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Remove unnecessary playwright/index.tsx from oxlint ignorePatterns The file produces no lint violations so it does not need to be ignored. https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Remove unnecessary ignore for simple-match.js; fix no-unused-vars Prefix unused fixture variables with _ instead of suppressing the file in oxlint ignorePatterns. https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Restore original variable names in simple-match.js, suppress lint via comment Use /* eslint-disable no-unused-vars */ (matching the pattern in simple-match.ts) to avoid invalidating variable names that are part of the textmate snapshot. https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Revert simple-match.js to original, add textmate fixtures to oxlint ignore The textmate fixture files are test data for snapshot-based syntax highlighting tests. They intentionally contain unused variables and other patterns that trigger lint rules. Ignoring the entire fixtures directory is more robust than suppressing individual rules per file. https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Match lint-staged flags to pnpm lint: --type-aware, --max-warnings 0, *.js Align the pre-commit hook with the full lint script by adding --type-aware and --max-warnings 0 to the oxlint call, and extending coverage to *.js files so the same rules apply on commit as in CI. https://claude.ai/code/session_01JZoXnsCz9YCM7SyHxfqPEH * Fix oxfmt formatting in oxlint.config.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 330688c commit 58d31f8

28 files changed

Lines changed: 354 additions & 318 deletions

File tree

.eslintignore

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

.eslintrc.js

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

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
(git diff-index --name-status HEAD | grep "cypher.json" && (echo "Run pnpm build and commit an up to date cypher.json file please" && exit 1)) || echo "\n\nTextMate grammar is up to date"
2929
3030
lint-and-format:
31-
name: Run eslint and check formatting
31+
name: Run oxlint and check formatting
3232
needs: build
3333
runs-on: ubuntu-latest
3434
steps:

.lintstagedrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2-
"*.ts": ["oxfmt", "eslint --fix --max-warnings 0"],
2+
"*.ts": ["oxfmt", "oxlint --fix --type-aware --max-warnings 0"],
3+
"*.js": ["oxlint --fix --type-aware --max-warnings 0"],
34
"*.json": ["oxfmt"]
45
}

.vscode/extensions.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"recommendations": [
33
"vitest.explorer",
44
"oxc.oxc-vscode",
5-
"dbaeumer.vscode-eslint",
65
"bradlc.vscode-tailwindcss"
76
]
87
}

oxlint.config.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { defineConfig } from 'oxlint';
2+
3+
export default defineConfig({
4+
ignorePatterns: [
5+
'semanticAnalysis.js',
6+
'vendor/**',
7+
'**/fixtures/textmate/**',
8+
],
9+
plugins: ['typescript'],
10+
rules: {
11+
'no-console': ['error', { allow: ['warn', 'error'] }],
12+
// False positive: Playwright's `mount` fixture is destructured from the
13+
// test context object, not from a class, so it is not an unbound method.
14+
'@typescript-eslint/unbound-method': 'off',
15+
// These rules produce systematic false positives because neo4j-driver
16+
// types use `any` internally, causing cascading type issues in unions.
17+
'@typescript-eslint/no-redundant-type-constituents': 'off',
18+
'@typescript-eslint/no-duplicate-type-constituents': 'off',
19+
'@typescript-eslint/no-misused-spread': 'off',
20+
},
21+
});

package.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"test": "pnpm --recursive --parallel test",
2323
"test:e2e": "pnpm --recursive test:e2e",
2424
"test:formattingIntegrity": "tsx ./packages/language-support/src/tests/formatting/verification/verificationCheck.ts",
25-
"lint": "eslint . --ext .ts,.tsx,.js,.mts",
25+
"lint": "oxlint --type-aware --max-warnings 0",
2626
"lint-fix": "pnpm lint --fix",
2727
"format": "oxfmt",
2828
"format:check": "oxfmt --check",
@@ -31,16 +31,14 @@
3131
"devDependencies": {
3232
"@changesets/cli": "^2.28.1",
3333
"@types/node": "^24.10.1",
34-
"@typescript-eslint/eslint-plugin": "^7.8.0",
35-
"@typescript-eslint/parser": "^7.0.0",
3634
"concurrently": "^8.2.1",
3735
"cross-env": "^7.0.3",
3836
"esbuild": "^0.27.1",
39-
"eslint": "^8.32.0",
40-
"eslint-config-prettier": "^8.6.0",
4137
"husky": "^8.0.0",
4238
"lint-staged": "^16.1.2",
4339
"oxfmt": "^0.35.0",
40+
"oxlint": "^1.50.0",
41+
"oxlint-tsgolint": "^0.14.2",
4442
"rimraf": "^6.0.1",
4543
"semver": "^7.6.1",
4644
"ts-node": "10.9.1",

packages/language-server/src/server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class SymbolFetcher {
9797
}),
9898
);
9999
}
100-
} catch (e) {
100+
} catch {
101101
//eslint-disable-next-line
102102
console.log('Symbol table calculation failed');
103103
}
@@ -245,6 +245,7 @@ connection.onNotification(
245245
}) => {
246246
neo4jSchemaPoller.events.once(
247247
'schemaFetched',
248+
// oxlint-disable-next-line typescript-eslint/no-meaningless-void-operator
248249
void symbolFetcher.queueSymbolJob(
249250
params.query,
250251
params.uri,

packages/language-support/src/autocompletion/schemaBasedCompletions.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export function getShortPathCompletions(
5757
return [];
5858
}
5959
cnfTree = convertToCNF(treeWithRewrittenAnys);
60-
} catch (e) {
60+
} catch {
6161
return [];
6262
}
6363
const { inLabels, outLabels } = walkCNFTree(
@@ -119,7 +119,7 @@ export function getPathCompletions(
119119
return [];
120120
}
121121
cnfTree = convertToCNF(treeWithRewrittenAnys);
122-
} catch (e) {
122+
} catch {
123123
return [];
124124
}
125125
const { inLabels: inRelTypes, outLabels: outRelTypes } = walkCNFTree(
@@ -393,7 +393,7 @@ export function completeNodeLabel(
393393
return [];
394394
}
395395
cnfTree = convertToCNF(treeWithRewrittenAnys);
396-
} catch (e) {
396+
} catch {
397397
return allLabelCompletions(dbSchema);
398398
}
399399
let allIncomingLabels = new Set<string>();
@@ -490,7 +490,7 @@ export function completeRelationshipType(
490490
return [];
491491
}
492492
cnfTree = convertToCNF(treeWithRewrittenAnys);
493-
} catch (e) {
493+
} catch {
494494
return [];
495495
}
496496
let allIncomingLabels = new Set<string>();

packages/language-support/src/syntaxValidation/completionCoreErrors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export function completionCoreErrormessage(
125125

126126
const keywordCandidates = tokenCandidates
127127
.filter((v) => keywordNames.has(v))
128-
.sort();
128+
.sort((a, b) => a.localeCompare(b));
129129
const nonKeywordCandidates = tokenCandidates.filter(
130130
(v) => !keywordNames.has(v),
131131
);

0 commit comments

Comments
 (0)