Replace ESLint and Prettier with Oxlint and Oxfmt

This commit is contained in:
James Brunton
2026-07-07 14:45:44 +01:00
committed by James Brunton
parent 596f5fd42c
commit 3a57da928d
9 changed files with 10 additions and 1274 deletions
+4 -47
View File
@@ -195,15 +195,9 @@ tasks:
desc: "Run linting"
deps: [install]
cmds:
- task: lint:eslint
- task: lint:oxlint
- task: lint:dpdm
lint:eslint:
desc: "Run ESLint linting"
deps: [install]
cmds:
- npx eslint --max-warnings=0
lint:dpdm:
desc: "Run circular import linting"
deps: [install]
@@ -216,19 +210,19 @@ tasks:
desc: "Auto-fix lint issues"
deps: [install]
cmds:
- npx eslint --fix
- npx oxlint --fix
format:
desc: "Auto-fix code formatting"
deps: [install]
cmds:
- npx prettier --write .
- npx oxfmt --write .
format:check:
desc: "Check code formatting"
deps: [install]
cmds:
- npx prettier --check .
- npx oxfmt --check .
fix:
desc: "Auto-fix lint and format"
@@ -236,49 +230,12 @@ tasks:
- task: format
- task: lint:fix
# ------------------------------------------------------------
# OSS oxc toolchain (oxlint + oxfmt) - evaluation
# Runs in parallel with the ESLint/Prettier tasks above so the
# two toolchains can be benchmarked head-to-head before any swap.
# Config: frontend/oxlint.config.ts, frontend/.oxfmtrc.json
# ------------------------------------------------------------
lint:oxlint:
desc: "Run oxlint linting"
deps: [install]
cmds:
- npx oxlint --max-warnings=0
lint:oxlint:fix:
desc: "Auto-fix lint issues with oxlint"
deps: [install]
cmds:
- npx oxlint --fix
format:oxfmt:
desc: "Auto-fix code formatting with oxfmt"
deps: [install]
cmds:
- npx oxfmt --write .
format:oxfmt:check:
desc: "Check code formatting with oxfmt"
deps: [install]
cmds:
- npx oxfmt --check .
bench:lint:
desc: "Time ESLint against oxlint on the same tree"
deps: [install]
cmds:
- bash -c 'echo "=== ESLint ===" && time npx eslint --max-warnings=0 || true; echo; echo "=== oxlint ===" && time npx oxlint --max-warnings=0 || true'
bench:format:
desc: "Time Prettier against oxfmt on the same tree"
deps: [install]
cmds:
- bash -c 'echo "=== Prettier ===" && time npx prettier --check . || true; echo; echo "=== oxfmt ===" && time npx oxfmt --check . || true'
typecheck:
desc: "Typecheck default build of the app"
cmds:
+1 -1
View File
@@ -19,6 +19,6 @@
"yzhang.markdown-all-in-one", // Markdown All-in-One extension for enhanced Markdown editing
"stylelint.vscode-stylelint", // Stylelint extension for CSS and SCSS linting
"redhat.vscode-yaml", // YAML extension for Visual Studio Code
"dbaeumer.vscode-eslint", // ESLint extension for TypeScript linting
"oxc.oxc-vscode", // Oxc (oxlint) extension for TypeScript/JavaScript linting
]
}
+1 -1
View File
@@ -158,7 +158,7 @@ Stirling-PDF/
│ │ │ └── locales/ # Internationalization files (JSON)
│ │ └── vite.config.ts # Vite configuration
│ ├── package.json # Shared workspace dependencies
│ └── eslint.config.mjs # Shared lint config
│ └── oxlint.config.ts # Shared lint config (oxlint)
├── customFiles/ # Custom static files and templates (generated at runtime used to replace existing files)
├── docs/ # Documentation files
├── exampleYmlFiles/ # Example YAML configuration files
-27
View File
@@ -1,27 +0,0 @@
dist/
editor/dist/
# Tauri/Cargo build output (binary assets named *.js etc. confuse Prettier).
# Match nested target/ dirs too - provisioner/ and thumbnail-handler/ each
# have their own Cargo workspace under src-tauri/.
editor/src-tauri/**/target/
editor/src-tauri/gen/
node_modules/
editor/public/vendor/
# Auto-generated by MSW (`msw init`); regenerated verbatim, not hand-formatted.
editor/public/mockServiceWorker.js
# Auto-generated OG/social-preview metadata (scripts/generate-og-metadata.mjs); regenerated verbatim.
editor/public/og-metadata.json
editor/src/core/data/ogImageMap.json
editor/public/pdfjs*/
editor/public/js/thirdParty/
editor/public/css/cookieconsent.css
# Build / test artifacts that may exist locally even though they're gitignored
storybook-static/
playwright-report/
editor/playwright-report/
test-results/
editor/test-results/
*.min.*
*.md
*.wxs
editor/src/output.css
-6
View File
@@ -1,6 +0,0 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"endOfLine": "lf"
}
-227
View File
@@ -1,227 +0,0 @@
// @ts-check
import eslint from "@eslint/js";
import globals from "globals";
import { defineConfig } from "eslint/config";
import tseslint from "typescript-eslint";
const srcGlobs = [
// The portal layer lives under editor/src/portal, so editor/src/** covers it.
"editor/src/**/*.{js,mjs,jsx,ts,tsx}",
];
const nodeGlobs = [
"scripts/**/*.{js,ts,mjs,mts}",
"editor/scripts/**/*.{js,ts,mjs,mts}",
// Covers editor/vite.config.ts and editor/vitest.config.ts.
"editor/*.config.{js,ts,mjs}",
"*.config.{js,ts,mjs}",
".storybook/*.{js,ts,mjs,mts,tsx}",
];
const baseRestrictedImportPatterns = [
{
regex: "^\\.",
message:
"Use a workspace alias (@app/* for editor, @portal/* for portal) instead of relative imports.",
},
{
regex: "^src/",
message: "Use a workspace alias instead of absolute src/ imports.",
},
];
export default defineConfig(
{
// Everything that contains 3rd party code that we don't want to lint
ignores: [
"dist",
"node_modules",
"playwright-report",
"storybook-static",
"test-results",
"editor/dist",
"editor/public",
"editor/src-tauri",
"editor/playwright-report",
"editor/test-results",
],
},
eslint.configs.recommended,
tseslint.configs.recommended,
{
rules: {
"no-restricted-imports": [
"error",
{
patterns: baseRestrictedImportPatterns,
},
],
"@typescript-eslint/no-empty-object-type": [
"error",
{
// Allow empty extending interfaces because there's no real reason not to, and it makes it obvious where to put extra attributes in the future
allowInterfaces: "with-single-extends",
},
],
"@typescript-eslint/no-explicit-any": "off", // Temporarily disabled until codebase conformant
"@typescript-eslint/no-unused-vars": [
"error",
{
args: "all", // All function args must be used (or explicitly ignored)
argsIgnorePattern: "^_", // Allow unused variables beginning with an underscore
caughtErrors: "all", // Caught errors must be used (or explicitly ignored)
caughtErrorsIgnorePattern: "^_", // Allow unused variables beginning with an underscore
destructuredArrayIgnorePattern: "^_", // Allow unused variables beginning with an underscore
varsIgnorePattern: "^_", // Allow unused variables beginning with an underscore
ignoreRestSiblings: true, // Allow unused variables when removing attributes from objects (otherwise this requires explicit renaming like `({ x: _x, ...y }) => y`, which is clunky)
},
],
},
},
// Desktop-only packages must not be imported from core or proprietary code.
// Use the stub/shadow pattern instead: define a stub in editor/src/core/ and override in editor/src/desktop/.
{
files: srcGlobs,
ignores: ["editor/src/desktop/**"],
rules: {
"no-restricted-imports": [
"error",
{
patterns: [
...baseRestrictedImportPatterns,
{
regex: "^@tauri-apps/",
message:
"Tauri APIs are desktop-only. Review frontend/editor/DeveloperGuide.md for structure advice.",
},
],
},
],
},
},
// The cloud/ layer is the SHARED hosted/SaaS experience consumed by BOTH the
// saas and desktop leaves, so it must stay platform-portable. It must not
// reach platform-specific things directly (Supabase, Tauri, raw fetch,
// window.location, web storage, or import.meta.env.VITE_*) — those arrive via
// @app/* seams (services/apiClient, auth/session, platform/openExternal, ...)
// that each leaf provides for its own platform.
{
files: ["editor/src/cloud/**/*.{js,mjs,jsx,ts,tsx}"],
rules: {
"no-restricted-imports": [
"error",
{
patterns: [
...baseRestrictedImportPatterns,
{
regex: "^@supabase/",
message:
"cloud/ must stay platform-portable. Reach Supabase via an @app/* seam (e.g. @app/auth/supabase, @app/auth/session) provided per-platform in saas/ and desktop/.",
},
{
regex: "^@tauri-apps/",
message:
"cloud/ must stay platform-portable. Tauri APIs are desktop-only — reach native features via an @app/* seam (e.g. @app/platform/openExternal).",
},
],
},
],
"no-restricted-globals": [
"error",
{
name: "fetch",
message:
"cloud/ must not call raw fetch — use @app/services/apiClient so each platform supplies its own transport.",
},
{
name: "localStorage",
message:
"cloud/ must not touch localStorage — use an @app/* storage seam so desktop/web can differ.",
},
{
name: "sessionStorage",
message:
"cloud/ must not touch sessionStorage — use an @app/* storage seam so desktop/web can differ.",
},
],
"no-restricted-syntax": [
"error",
{
selector:
"MemberExpression[object.name='window'][property.name='location']",
message:
"cloud/ must not touch window.location — use an @app/* seam (e.g. @app/platform/openExternal) so desktop/web can differ.",
},
{
selector:
"MemberExpression[property.name='env'][object.type='MetaProperty'][object.meta.name='import'][object.property.name='meta']",
message:
"cloud/ must not read import.meta.env — use @app/constants/app / @app/platform seams so config is supplied per-platform.",
},
],
},
},
// Stricter rules that not all sub-folders are conformant to yet
{
files: srcGlobs,
ignores: [
"editor/src/core/components/annotation/**/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/pageEditor/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/pageEditor/commands/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/pageEditor/hooks/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/shared/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/shared/config/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/shared/config/configSections/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/shared/pageEditor/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/tools/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/tools/addStamp/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/tools/automate/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/tools/bookletImposition/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/tools/certSign/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/tools/pdfTextEditor/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/tools/shared/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/components/viewer/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/contexts/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/contexts/file/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/contexts/viewer/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/hooks/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/hooks/signing/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/hooks/tools/adjustContrast/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/hooks/tools/convert/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/hooks/tools/removePassword/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/hooks/tools/shared/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/services/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/tools/annotate/useAnnotationSelection.ts",
"editor/src/core/types/*.{js,mjs,jsx,ts,tsx}",
"editor/src/core/utils/*.{js,mjs,jsx,ts,tsx}",
],
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
},
},
// Config for browser scripts
{
files: srcGlobs,
languageOptions: {
globals: {
...globals.browser,
},
},
},
// Config for node scripts
{
files: nodeGlobs,
languageOptions: {
globals: {
...globals.node,
},
},
},
);
+1 -1
View File
@@ -1,6 +1,6 @@
import { defineConfig } from "oxlint";
// App source globs (mirrors srcGlobs in eslint.config.mjs).
// App source globs
const srcGlobs = [
"editor/src/**/*.{js,mjs,jsx,ts,tsx}",
"portal/src/**/*.{js,mjs,jsx,ts,tsx}",
+3 -957
View File
File diff suppressed because it is too large Load Diff
-7
View File
@@ -58,7 +58,6 @@
"autoprefixer": "^10.4.21",
"axios": "^1.15.0",
"d3": "^7.9.0",
"globals": "^17.5.0",
"i18next": "^25.10.10",
"i18next-browser-languagedetector": "^8.2.0",
"jszip": "^3.10.1",
@@ -107,7 +106,6 @@
]
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@iconify-json/material-symbols": "^1.2.53",
"@iconify/utils": "^3.1.0",
"@playwright/test": "^1.55.0",
@@ -128,14 +126,11 @@
"@types/node": "^24.5.2",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9",
"@typescript-eslint/eslint-plugin": "^8.61.1",
"@typescript-eslint/parser": "^8.61.1",
"@typescript/native-preview": "7.0.0-dev.20260624.1",
"@vitejs/plugin-react-swc": "^4.1.0",
"@vitest/coverage-v8": "^3.2.4",
"dotenv": "^16.4.7",
"dpdm": "^3.14.0",
"eslint": "^10.0.2",
"fake-indexeddb": "^6.2.5",
"jsdom": "^27.0.0",
"json-schema-to-typescript": "^15.0.4",
@@ -148,13 +143,11 @@
"postcss-cli": "^11.0.1",
"postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "^3.8.1",
"puppeteer": "^24.25.0",
"rollup-plugin-visualizer": "^7.0.1",
"storybook": "^9.1.20",
"tsx": "^4.21.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.61.1",
"vite": "^7.3.2",
"vite-plugin-compression2": "^2.5.3",
"vite-plugin-static-copy": "^3.1.4",