Skip to content

Commit 37a48aa

Browse files
authored
Replace ESLint and dpdm with Oxlint (#7330)
# Description of Changes Smaller scope than #6689 to try and get this finished. Replace ESLint and dpdm with Oxlint, a TS linter written in Rust so its performance is dramatically better than the existing tools we use. ## Speed improvement - Current ESLint run: 13.76s - Current dpdm run: 3.59s - Total time: 17.35s - New Oxlint run: 0.90s So Oxlint is about a 20x speed improvement. ## Differences When I last tried to do this, we could recreate our rules identically with Oxlint, but that's not true any more. Oxlint has no current equivalent for ESLint's `no-restricted-syntax` rule, which we were using to ban usages of `<button>` and stuff in specific components to try and encourage them to use our shared UI. This is a very recent addition to our linting config, and personally I'm willing to drop it for now at least. We can still ban specific imports in files, so the files which we were trying to enforce shared UI will still ban directly importing Mantine, so that'll probably be most of the cases still caught, but I think there are other ways we can encourage using the shared UI beyond just using the linter for it. I did try building a custom TS rule for it and it only slowed it down a tiny bit (it took 1.1s) but it had to be built on an unreleased alpha API which just sounds like a maintenance headache we don't need to deal with for a rule that we don't really need.
1 parent 408f9ef commit 37a48aa

34 files changed

Lines changed: 895 additions & 1704 deletions

File tree

.github/dependabot.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,6 @@ updates:
7373
- "react-dom"
7474
- "@types/react"
7575
- "@types/react-dom"
76-
typescript-eslint:
77-
patterns:
78-
- "@typescript-eslint/*"
79-
- "typescript-eslint"
80-
eslint:
81-
patterns:
82-
- "eslint"
83-
- "@eslint/*"
8476
vite:
8577
patterns:
8678
- "vite"

.taskfiles/frontend.yml

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,7 @@ tasks:
260260
desc: "Run linting"
261261
deps: [install]
262262
cmds:
263-
- task: lint:eslint
264-
- task: lint:dpdm
263+
- task: lint:oxlint
265264
- task: lint:colors
266265
- task: lint:css
267266

@@ -290,25 +289,17 @@ tasks:
290289
cmds:
291290
- node editor/scripts/lint/theme-lint.mjs contrast
292291

293-
lint:eslint:
294-
desc: "Run ESLint linting"
292+
lint:oxlint:
293+
desc: "Run oxlint linting"
295294
deps: [install]
296295
cmds:
297-
- npx eslint --max-warnings=0
298-
299-
lint:dpdm:
300-
desc: "Run circular import linting"
301-
deps: [install]
302-
cmds:
303-
# Globs so dpdm walks the whole tree. dpdm expands the braces itself, so this is
304-
# shell-agnostic. Covers the whole editor tree, including the portal layer.
305-
- npx dpdm "editor/src/**/*.{ts,tsx}" --circular --no-warning --no-tree --exit-code circular:1
296+
- npx oxlint --config oxlint.config.ts --max-warnings=0
306297

307298
lint:fix:
308299
desc: "Auto-fix lint issues"
309300
deps: [install]
310301
cmds:
311-
- npx eslint --fix
302+
- npx oxlint --config oxlint.config.ts --fix
312303

313304
format:
314305
desc: "Auto-fix code formatting"

.vscode/extensions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
"yzhang.markdown-all-in-one", // Markdown All-in-One extension for enhanced Markdown editing
2020
"stylelint.vscode-stylelint", // Stylelint extension for CSS and SCSS linting
2121
"redhat.vscode-yaml", // YAML extension for Visual Studio Code
22-
"dbaeumer.vscode-eslint", // ESLint extension for TypeScript linting
22+
"oxc.oxc-vscode", // Oxc (oxlint) extension for JavaScript/TypeScript linting
2323
]
2424
}

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ What goes where:
192192
- **saas** — web-only: Supabase web auth, AuthCallback, avatar canvas, `window.location`.
193193
- **desktop** — Tauri-only: keyring authService, tauriHttpClient, native files/windows, backend routing.
194194

195-
`cloud/` MUST NOT import `@supabase/*`, `@tauri-apps/*`, raw `fetch`, `window.location`, `localStorage`, `sessionStorage`, or `import.meta.env.VITE_*` (enforced by ESLint). It reaches platform-specific things only via `@app/*` seams: `services/apiClient`, `auth/session.getAccessToken`, `auth/supabase`, `platform/openExternal`, `services/billing`, `hooks/useSaaSMode` — each provided per-platform in `saas/` and `desktop/`.
195+
`cloud/` MUST NOT import `@supabase/*`, `@tauri-apps/*`, raw `fetch`, `window.location`, `localStorage`, `sessionStorage`, or `import.meta.env.VITE_*` (all enforced by the linter). It reaches platform-specific things only via `@app/*` seams: `services/apiClient`, `auth/session.getAccessToken`, `auth/supabase`, `platform/openExternal`, `services/billing`, `hooks/useSaaSMode` — each provided per-platform in `saas/` and `desktop/`.
196196

197197
Rule of thumb — **move, don't copy**: share via `cloud/`, override by shadowing the same `@app/*` path in a leaf (`saas/` or `desktop/`).
198198

DeveloperGuide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ Stirling-PDF/
158158
│ │ │ └── locales/ # Internationalization files (JSON)
159159
│ │ └── vite.config.ts # Vite configuration
160160
│ ├── package.json # Shared workspace dependencies
161-
│ └── eslint.config.mjs # Shared lint config
161+
│ └── oxlint.config.ts # Shared lint config
162162
├── customFiles/ # Custom static files and templates (generated at runtime used to replace existing files)
163163
├── docs/ # Documentation files
164164
├── exampleYmlFiles/ # Example YAML configuration files

frontend/.storybook/vitest.setup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { beforeAll } from "vitest";
22
import { setProjectAnnotations } from "@storybook/react-vite";
33
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
4-
// eslint-disable-next-line no-restricted-imports -- Storybook-only: the sibling preview config has no @-alias.
4+
// oxlint-disable-next-line no-restricted-imports -- Storybook-only: the sibling preview config has no @-alias.
55
import * as projectAnnotations from "./preview";
66

77
// Include addon-a11y's annotations so its axe checks run under Vitest, not only

frontend/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ All frontend commands are run from the repository root using [Task](https://task
66
- `task frontend:build` — production build
77
- `task frontend:test` — run tests
88
- `task frontend:test:watch` — run tests in watch mode
9-
- `task frontend:lint` — run ESLint + cycle detection
9+
- `task frontend:lint` — run linting
1010
- `task frontend:typecheck` — run TypeScript type checking
1111
- `task frontend:check` — run typecheck + lint + test
1212
- `task frontend:install` — install npm dependencies
@@ -18,7 +18,7 @@ For desktop app development, see the [Tauri](#tauri) section below.
1818
`frontend/` is a workspace containing one or more apps. Today it holds the
1919
PDF editor under `frontend/editor/`; new apps (the developer portal, etc.)
2020
will sit alongside it as siblings. Shared tooling — `package.json`, `node_modules`,
21-
`.storybook/`, ESLint, Prettier — lives at `frontend/` so every app installs
21+
`.storybook/`, oxlint, Prettier — lives at `frontend/` so every app installs
2222
once and lints with the same config.
2323

2424
## Environment Variables

frontend/editor/scripts/sync-portal-docs.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { mkdirSync, writeFileSync } from "node:fs";
1313
import { dirname, resolve } from "node:path";
1414
import { fileURLToPath } from "node:url";
1515
// tsx/node16 can't resolve the @portal alias here, so import by relative .ts path.
16-
// eslint-disable-next-line no-restricted-imports
16+
// oxlint-disable-next-line no-restricted-imports
1717
import {
1818
buildManifest,
1919
type CategoryMap,

frontend/editor/scripts/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
// sync-portal-docs.mts imports the shared transform by its .ts path (run via tsx).
99
"allowImportingTsExtensions": true
1010
},
11-
"include": ["./**/*.ts", "./**/*.mts"]
11+
"include": ["./**/*.ts", "./**/*.mts", "../../oxlint.config.ts"]
1212
}

frontend/editor/src/cloud/components/shared/config/configSections/Payg.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ import {
3232
prepaidSnapshotFromWallet,
3333
} from "@app/components/shared/config/configSections/usageMeters";
3434
// Relative (not @app/*) so the co-located CSS + sibling component resolve directly.
35-
// eslint-disable-next-line no-restricted-imports
35+
// oxlint-disable-next-line no-restricted-imports
3636
import "./Payg.css";
37-
// eslint-disable-next-line no-restricted-imports
37+
// oxlint-disable-next-line no-restricted-imports
3838
import SpendCapControl from "./SpendCapControl";
3939
import { useTranslation } from "react-i18next";
4040
import type { Wallet } from "@app/hooks/useWallet";

0 commit comments

Comments
 (0)