Skip to content

Commit ed470b2

Browse files
JohnMcLearclaude
andcommitted
chore(eslint): migrate to ESLint 9 flat config
- Replace .eslintrc.cjs with eslint.config.js - Bump eslint-config-prettier 9 → 10 - Use the typescript-eslint meta package + @eslint/js + globals - Drop --ext flag from lint script (inferred from globs in v9) - Fix lint errors: unused imports, inline import() type, unused vars - Downgrade react-hooks/set-state-in-effect to warn (1 instance; legitimate derived-state pattern — follow-up to refactor to key-based reset) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5ff7889 commit ed470b2

10 files changed

Lines changed: 401 additions & 225 deletions

File tree

.eslintrc.cjs

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

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ After main-process source changes, **restart `pnpm dev`** — Vite HMR only cove
4141
## Conventions
4242

4343
- TypeScript strict; no `.js` files in source tree.
44+
- ESLint 9 flat config (`eslint.config.js`). No `.eslintrc.*` files. Run `pnpm lint` (no `--ext` flag needed — extensions are inferred from the globs in the config).
4445
- All persistent state goes through main-process stores (`workspace-store`, `pad-history-store`, `settings-store`, `window-state-store`). Renderer NEVER touches disk.
4546
- IPC payloads are Zod-validated in main via `wrapHandler(channel, schema, handler)`. Channels are constants in `src/shared/ipc/channels.ts` (`CH.WORKSPACE_LIST` etc.).
4647
- Each `WebContentsView` is created via `pad-view-factory.ts` — single seam for future offline-cache / embedded-server work.
@@ -74,7 +75,7 @@ These are real bugs we've hit and fixed in this codebase. Keep them in mind:
7475

7576
2. **`exactOptionalPropertyTypes: true`** mismatches with Zod's `.optional()` (which infers `T | undefined` rather than `T?`). Where this bites: `PadHistoryEntry.title?` vs `padHistoryEntrySchema.title.optional()` → cast at the boundary. See `src/main/pads/pad-history-store.ts`.
7677

77-
3. **Vitest 4.x**`test.workspace` was removed. Projects are now declared inline via `test.projects` in `vitest.config.ts`. The `vitest.workspace.ts` file (with `defineWorkspace`) is no longer auto-discovered; keep projects in `vitest.config.ts`. The `--workspace` CLI flag is also gone. ESLint 9 + flat-config migration is a known follow-up (currently on ESLint 8.x + legacy plugin config).
78+
3. **Vitest 4.x**`test.workspace` was removed. Projects are now declared inline via `test.projects` in `vitest.config.ts`. The `vitest.workspace.ts` file (with `defineWorkspace`) is no longer auto-discovered; keep projects in `vitest.config.ts`. The `--workspace` CLI flag is also gone.
7879

7980
4. **TypeScript composite projects** — leaf configs (`main`, `preload`, `renderer`) use `references: [{ path: './tsconfig.shared.json' }]` AND `paths: { '@shared/*': ['src/shared/*'] }`. Do NOT add `'src/shared/**'` to leaf `include` arrays — that double-compiles shared sources and corrupts cross-project type checking once shared has real types.
8081

eslint.config.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// eslint.config.js
2+
import js from '@eslint/js';
3+
import tseslint from 'typescript-eslint';
4+
import react from 'eslint-plugin-react';
5+
import reactHooks from 'eslint-plugin-react-hooks';
6+
import prettier from 'eslint-config-prettier';
7+
import globals from 'globals';
8+
9+
export default [
10+
{
11+
ignores: ['out/**', 'dist/**', 'release/**', 'coverage/**', 'playwright-report/**', 'test-results/**', 'node_modules/**'],
12+
},
13+
js.configs.recommended,
14+
...tseslint.configs.recommended,
15+
{
16+
files: ['src/**/*.{ts,tsx}', 'tests/**/*.{ts,tsx}'],
17+
languageOptions: {
18+
parser: tseslint.parser,
19+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
20+
globals: { ...globals.browser, ...globals.node },
21+
},
22+
plugins: {
23+
react,
24+
'react-hooks': reactHooks,
25+
},
26+
settings: { react: { version: '19' } },
27+
rules: {
28+
...react.configs.recommended.rules,
29+
...reactHooks.configs.recommended.rules,
30+
'react/react-in-jsx-scope': 'off',
31+
'react/prop-types': 'off',
32+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }],
33+
'@typescript-eslint/consistent-type-imports': 'error',
34+
// Derived local-edit state from a store prop is a legitimate pattern;
35+
// downgrade to warn rather than error until we refactor to key-based reset.
36+
'react-hooks/exhaustive-deps': 'warn',
37+
'react-hooks/set-state-in-effect': 'warn',
38+
},
39+
},
40+
prettier,
41+
];

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"dev": "electron-vite dev",
1818
"build": "electron-vite build",
1919
"typecheck": "tsc -b --pretty",
20-
"lint": "eslint --ext .ts,.tsx --no-error-on-unmatched-pattern src tests",
20+
"lint": "eslint src tests",
2121
"format": "prettier --write src tests",
2222
"test": "vitest run",
2323
"test:watch": "vitest",
@@ -34,6 +34,7 @@
3434
"zustand": "^5.0.12"
3535
},
3636
"devDependencies": {
37+
"@eslint/js": "^9.39.4",
3738
"@playwright/test": "^1.59.1",
3839
"@testing-library/jest-dom": "^6.6.3",
3940
"@testing-library/react": "^16.3.2",
@@ -48,15 +49,17 @@
4849
"electron": "^41.5.0",
4950
"electron-builder": "^26.6.0",
5051
"electron-vite": "^5.0.0",
51-
"eslint": "^8.57.1",
52-
"eslint-config-prettier": "^9.1.0",
52+
"eslint": "^9.39.4",
53+
"eslint-config-prettier": "^10.1.8",
5354
"eslint-plugin-react": "^7.37.5",
5455
"eslint-plugin-react-hooks": "^7.1.1",
56+
"globals": "^16.5.0",
5557
"jsdom": "^29.1.1",
5658
"playwright-core": "^1.59.1",
5759
"prettier": "^3.5.3",
5860
"tmp": "^0.2.3",
5961
"typescript": "^5.8.3",
62+
"typescript-eslint": "^8.59.2",
6063
"vite": "^7.3.1",
6164
"vitest": "^4.1.5"
6265
}

0 commit comments

Comments
 (0)