Skip to content

Commit bc70642

Browse files
authored
Upgrade ESLint to v9 with flat config (#707)
- Migrate from legacy `.eslintrc.json` to ESLint v9 flat config (`eslint.config.mjs`) - Upgrade ESLint from v8.57 to v9.39 - Upgrade typescript-eslint packages to v8.52 - Switch from `eslint-plugin-import` to `eslint-plugin-import-x` (actively maintained fork) - Replace `eslint-plugin-md` with `@eslint/markdown` - Fix all ESLint errors introduced by stricter v9 rules - Extract error utilities into dedicated `src/error/` module
1 parent 391c6cc commit bc70642

Some content is hidden

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

48 files changed

+1881
-3854
lines changed

.eslintignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

.eslintrc.json

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

eslint.config.mjs

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// @ts-check
2+
import eslint from "@eslint/js";
3+
import { defineConfig } from "eslint/config";
4+
import markdown from "@eslint/markdown";
5+
import tseslint from "typescript-eslint";
6+
import prettierConfig from "eslint-config-prettier";
7+
import prettierPlugin from "eslint-plugin-prettier";
8+
import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
9+
import { flatConfigs as importXFlatConfigs } from "eslint-plugin-import-x";
10+
import packageJson from "eslint-plugin-package-json";
11+
import globals from "globals";
12+
13+
export default defineConfig(
14+
// Global ignores
15+
{
16+
ignores: [
17+
"out/**",
18+
"dist/**",
19+
"**/*.d.ts",
20+
"vitest.config.ts",
21+
".vscode-test/**",
22+
],
23+
},
24+
25+
// Base ESLint recommended rules (for JS/TS files only)
26+
{
27+
files: ["**/*.ts", "**/*.js", "**/*.mjs"],
28+
...eslint.configs.recommended,
29+
},
30+
31+
// TypeScript configuration with type-checked rules
32+
{
33+
files: ["**/*.ts"],
34+
extends: [
35+
...tseslint.configs.recommendedTypeChecked,
36+
...tseslint.configs.stylisticTypeChecked,
37+
importXFlatConfigs.typescript,
38+
],
39+
languageOptions: {
40+
parserOptions: {
41+
projectService: true,
42+
tsconfigRootDir: import.meta.dirname,
43+
},
44+
},
45+
plugins: {
46+
prettier: prettierPlugin,
47+
},
48+
settings: {
49+
"import-x/resolver-next": [
50+
createTypeScriptImportResolver({ project: "./tsconfig.json" }),
51+
],
52+
"import-x/internal-regex": "^@/",
53+
},
54+
rules: {
55+
// Prettier integration
56+
"prettier/prettier": "error",
57+
58+
// Core ESLint rules
59+
curly: "error",
60+
eqeqeq: "error",
61+
"no-throw-literal": "error",
62+
"no-console": "error",
63+
64+
// TypeScript rules (extending/overriding presets)
65+
"require-await": "off",
66+
"@typescript-eslint/require-await": "error",
67+
"@typescript-eslint/consistent-type-imports": "error",
68+
"@typescript-eslint/switch-exhaustiveness-check": [
69+
"error",
70+
{ considerDefaultExhaustiveForUnions: true },
71+
],
72+
"@typescript-eslint/no-unused-vars": [
73+
"error",
74+
{ varsIgnorePattern: "^_" },
75+
],
76+
"@typescript-eslint/array-type": ["error", { default: "array-simple" }],
77+
"@typescript-eslint/prefer-nullish-coalescing": [
78+
"error",
79+
// Allow || for strings where empty string should be treated as falsy
80+
{ ignorePrimitives: { string: true } },
81+
],
82+
"@typescript-eslint/dot-notation": [
83+
"error",
84+
// Allow bracket notation for index signatures (e.g., Record<string, T>)
85+
{ allowIndexSignaturePropertyAccess: true },
86+
],
87+
88+
// Import rules
89+
"import-x/order": [
90+
"error",
91+
{
92+
groups: [
93+
["builtin", "external"],
94+
"internal",
95+
"parent",
96+
["sibling", "index"],
97+
"type",
98+
],
99+
pathGroups: [
100+
{ pattern: "@/**", group: "internal", position: "before" },
101+
],
102+
pathGroupsExcludedImportTypes: ["builtin", "external"],
103+
"newlines-between": "always",
104+
alphabetize: { order: "asc", caseInsensitive: true },
105+
sortTypesGroup: true,
106+
},
107+
],
108+
"no-duplicate-imports": "off",
109+
"import-x/no-duplicates": ["error", { "prefer-inline": true }],
110+
"import-x/no-unresolved": ["error", { ignore: ["vscode"] }],
111+
112+
// Custom AST selector rule
113+
"no-restricted-syntax": [
114+
"error",
115+
{
116+
selector:
117+
"CallExpression[callee.property.name='executeCommand'][arguments.0.value='setContext'][arguments.length>=3]",
118+
message:
119+
"Do not use executeCommand('setContext', ...) directly. Use the ContextManager class instead.",
120+
},
121+
],
122+
},
123+
},
124+
125+
// Test files - use test tsconfig and relax some rules
126+
{
127+
files: ["test/**/*.ts", "**/*.test.ts", "**/*.spec.ts"],
128+
settings: {
129+
"import-x/resolver-next": [
130+
createTypeScriptImportResolver({ project: "test/tsconfig.json" }),
131+
],
132+
},
133+
rules: {
134+
// Allow type annotations in tests (e.g., for vi.fn<SomeType>())
135+
"@typescript-eslint/consistent-type-imports": [
136+
"error",
137+
{
138+
disallowTypeAnnotations: false,
139+
},
140+
],
141+
// vitest mocks trigger false positives for unbound-method
142+
"@typescript-eslint/unbound-method": "off",
143+
// Empty callbacks are common in test stubs
144+
"@typescript-eslint/no-empty-function": "off",
145+
// Test mocks often have loose typing - relax unsafe rules
146+
"@typescript-eslint/no-unsafe-assignment": "off",
147+
"@typescript-eslint/no-unsafe-call": "off",
148+
"@typescript-eslint/no-unsafe-return": "off",
149+
},
150+
},
151+
152+
// Disable no-restricted-syntax for contextManager
153+
{
154+
files: ["src/core/contextManager.ts"],
155+
rules: {
156+
"no-restricted-syntax": "off",
157+
},
158+
},
159+
160+
// Webpack config - CommonJS with Node globals
161+
{
162+
files: ["webpack.config.js"],
163+
languageOptions: {
164+
globals: {
165+
...globals.node,
166+
},
167+
},
168+
},
169+
170+
// Package.json linting
171+
packageJson.configs.recommended,
172+
173+
// Markdown linting with GitHub-flavored admonitions allowed
174+
...markdown.configs.recommended,
175+
{
176+
files: ["**/*.md"],
177+
rules: {
178+
"markdown/no-missing-label-refs": [
179+
"error",
180+
{
181+
allowLabels: ["!NOTE", "!TIP", "!IMPORTANT", "!WARNING", "!CAUTION"],
182+
},
183+
],
184+
},
185+
},
186+
187+
// Prettier must be last to override other formatting rules
188+
prettierConfig,
189+
);

package.json

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"scripts": {
2121
"build": "webpack",
2222
"fmt": "prettier --write .",
23-
"lint": "eslint . --ext ts,md,json",
23+
"lint": "eslint .",
2424
"lint:fix": "yarn lint --fix",
2525
"package": "webpack --mode production --devtool hidden-source-map",
2626
"package:prerelease": "npx vsce package --pre-release",
@@ -418,35 +418,37 @@
418418
"zod": "^4.3.2"
419419
},
420420
"devDependencies": {
421-
"@types/eventsource": "^3.0.0",
422-
"@types/node": "^22.14.1",
421+
"@eslint/js": "^9.39.2",
422+
"@eslint/markdown": "^7.5.1",
423+
"@types/node": "^18",
423424
"@types/proper-lockfile": "^4.1.4",
424425
"@types/semver": "^7.7.1",
425426
"@types/ua-parser-js": "0.7.39",
426427
"@types/vscode": "^1.73.0",
427428
"@types/ws": "^8.18.1",
428-
"@typescript-eslint/eslint-plugin": "^8.49.0",
429-
"@typescript-eslint/parser": "^8.50.1",
429+
"@typescript-eslint/eslint-plugin": "^8.52.0",
430+
"@typescript-eslint/parser": "^8.52.0",
430431
"@vitest/coverage-v8": "^4.0.16",
431432
"@vscode/test-cli": "^0.0.12",
433+
"@vscode/test-electron": "^2.5.2",
432434
"@vscode/vsce": "^3.7.1",
433-
"bufferutil": "^4.0.9",
435+
"bufferutil": "^4.1.0",
434436
"coder": "https://github.com/coder/coder#main",
435437
"dayjs": "^1.11.19",
436-
"electron": "^39.2.6",
437-
"eslint": "^8.57.1",
438+
"electron": "^39.2.7",
439+
"eslint": "^9.39.2",
438440
"eslint-config-prettier": "^10.1.8",
439441
"eslint-import-resolver-typescript": "^4.4.4",
440-
"eslint-plugin-import": "^2.32.0",
441-
"eslint-plugin-md": "^1.0.19",
442-
"eslint-plugin-package-json": "^0.85.0",
442+
"eslint-plugin-import-x": "^4.16.1",
443+
"eslint-plugin-package-json": "^0.88.1",
443444
"eslint-plugin-prettier": "^5.5.4",
445+
"globals": "^16.5.0",
444446
"jsonc-eslint-parser": "^2.4.2",
445-
"markdown-eslint-parser": "^1.2.1",
446447
"memfs": "^4.51.1",
447448
"prettier": "^3.7.4",
448449
"ts-loader": "^9.5.4",
449450
"typescript": "^5.9.3",
451+
"typescript-eslint": "^8.52.0",
450452
"utf-8-validate": "^6.0.6",
451453
"vitest": "^4.0.16",
452454
"webpack": "^5.104.1",

src/api/agentMetadataHelper.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import {
88
} from "./api-helper";
99
import { type CoderApi } from "./coderApi";
1010

11-
export type AgentMetadataWatcher = {
11+
export interface AgentMetadataWatcher {
1212
onChange: vscode.EventEmitter<null>["event"];
1313
dispose: () => void;
1414
metadata?: AgentMetadataEvent[];
1515
error?: unknown;
16-
};
16+
}
1717

1818
/**
1919
* Opens a websocket connection to watch metadata for a given workspace agent.

0 commit comments

Comments
 (0)