50 workspace packages still use ESLint for JS/TS linting. One package (tools/require-exhaustive-property-definitions) has been migrated and serves as the reference. The goal is to migrate each package so that oxlint handles JS/TS linting and ESLint is retained only for JSON/JSONC files.
For each package, repeat these steps:
cd <package-dir>
pnpm exec soxhub-oxlint-migrate --minimalThis generates oxlint.config.ts and oxlint-migration-report.md. Review the report for unsupported rules — most plugins are supported via jsPlugins in the @soxhub/lint/oxlint base config or the migration tool output.
Replace the content to restrict ESLint to JSON/JSONC files only. Use the reference pattern:
import { defineConfig, globalIgnores } from "eslint/config";
import soxhubLint from "@soxhub/lint/eslint";
const { node } = soxhubLint;
const nodeConfig = node({ root: true, path: import.meta.dirname });
export default defineConfig([
globalIgnores(["**/*", "!**/*.json", "!**/*.jsonc"]),
globalIgnores(["build", "dist"]), // keep package-specific ignores
nodeConfig,
]);Key: globalIgnores(['**/*', '!**/*.json', '!**/*.jsonc']) ensures ESLint only processes JSON files.
If the package had custom rules in ESLint that applied to JSON files specifically (e.g., json-schema-validator/no-invalid on package.json), keep those overrides. If the package had JS/TS-only custom rules, those should already be in the generated oxlint.config.ts.
Replace lint:js / lint:js:fix and add lint:json / lint:json:fix:
"lint:js": "NODE_OPTIONS=\"--import @oxc-node/core/register\" oxlint --deny-warnings --type-aware --report-unused-disable-directives $FILES",
"lint:js:fix": "NODE_OPTIONS=\"--import @oxc-node/core/register\" oxlint --deny-warnings --type-aware --report-unused-disable-directives --fix $FILES",
"lint:json": "eslint --max-warnings=0 --cache --cache-location=.eslintcache.json --cache-strategy=content ${FILES:-.}",
"lint:json:fix": "eslint --fix --max-warnings=0 --cache --cache-location=.eslintcache.json --cache-strategy=content ${FILES:-.}",Remove any NODE_OPTIONS='--max-old-space-size=8192' from old lint:js — oxlint is native and doesn't need it.
Ensure these are in devDependencies: @oxc-node/core@^0.0.35, oxlint@^1.50.0, oxlint-tsgolint@^0.15.0.
pnpm lintFix any failures (unused disable directives, rule name changes, etc.).
All 50 packages that need migration, grouped by directory:
utils/ (9 packages)
utils/promise,utils/array,utils/b-tree,utils/ipc,utils/redis-client,utils/redis-streams,utils/logger,utils/config,utils/common
common/ (8 packages)
common/consts,common/di,common/errors,common/permissions,common/routing-layer,common/shared-config,common/ml-service-local-client,common/permissions-service-client
contexts/ (7 packages)
contexts/auth(special: has no eslint config — may need no migration or needs config created from scratch)contexts/service-layer,contexts/compliance,contexts/scoring,contexts/versioning,contexts/workflow-engine,contexts/regulatory-compliance
data-access-layer/ (4 packages)
data-access-layer/data-layer,data-access-layer/json-api,data-access-layer/legacy-data-layer,data-access-layer/legacy-json-api
tools/ (3 packages)
tools/eslint-rules,tools/template-lint-rules,tools/codemods
integrations/ (1 package)
integrations/storage
test-runner/ (1 package)
test-runner
templates (2 packages)
create-new-package,create-new-dal-package
Two scripts at /tmp/ (not committed) handle the mechanical migration:
/tmp/migrate-to-oxlint.sh <package-dir>— full pipeline for a single package/tmp/fix-oxlint-config.js— helper to rewrite the migration-tool-generated oxlint.config.ts
- Run
soxhub-oxlint-migrate --minimal - Update
package.json: replace lint:js scripts, add lint:json scripts, add devDeps (@oxc-node/core,oxlint,oxlint-tsgolint,globals), removelint:ox - Rewrite
oxlint.config.tsvia fix-oxlint-config.js:- Fix indentation, add
globalsimport and CJS override - Remove JSON-file overrides (tsconfig*.json), remove
@auditboard/eslint-pluginfrom jsPlugins - Add
no-unsafe-assignmentdisable comment
- Fix indentation, add
- Write JSON-only
eslint.config.mjs(detects@auditboard/no-dependency-tsconfig-pathsrule automatically) - Autofix: lint:json:fix, lint:js:fix, lint:prettier:fix, clean up generated files
- Run final lint checks (lint:js, lint:json, lint:prettier, lint:types) and report pass/fail
- Git branching/committing/PR creation
- Fixing remaining lint failures after autofix
- Decisions about unsupported plugins or rule behavior differences
Each package gets its own PR targeted at the oxlint-packages branch. We ramp up in batches:
utils/promiseutils/arrayutils/b-tree
utils/ipcutils/redis-clientutils/redis-streamsutils/loggerutils/config
utils/commoncommon/constscommon/dicommon/errorscommon/shared-configcommon/ml-service-local-clientcommon/permissions-service-clientcommon/routing-layercommon/permissionsintegrations/storage
tools/eslint-rulestools/template-lint-rulestools/codemodscontexts/service-layerdata-access-layer/data-layerdata-access-layer/json-apidata-access-layer/legacy-data-layerdata-access-layer/legacy-json-apicontexts/compliancecontexts/scoringcontexts/versioningcontexts/workflow-enginecontexts/regulatory-compliancetest-runnercreate-new-packagecreate-new-dal-packagecontexts/auth(special case — investigate first)
Update this plan with learnings after each batch.
contexts/auth: Has no eslint config orlint:jsscript. Currently linted by root config only. Investigate whether it needs its own oxlint config or can remain as-is.tools/eslint-rules: This IS@auditboard/eslint-plugin. Migrating its own linting is fine, but the package must continue to work as an ESLint plugin.create-new-package/create-new-dal-package: Templates for new packages. After migration, new packages scaffolded from these will have oxlint configs.- Packages with unsupported plugins (
tsdoc,decorator-position,simple-import-sort): The migration tool will flag these. Most plugins used in this repo (freeze-global,formatjs,no-only-tests, etc.) are already supported asjsPluginsin the@soxhub/lint/oxlintbase config. For truly unsupported plugins, decide per-package whether to drop, keep in a hybrid ESLint config, or add as jsPlugin.
After each package migration, pnpm lint in that package must pass. After all packages are migrated, run pnpm lint from the root to verify the full monorepo.
utils/promise— PR #31259utils/array— PR #31260utils/b-tree— PR #31261
utils/ipc— PR #31264utils/redis-client— pendingutils/redis-streams— pendingutils/logger— pendingutils/config— pending
oxlint's ESM mode doesn't know about CJS globals (require, module, exports). Files like .prettierrc.cjs will fail no-undef. Fix: add globals package as devDep and use globals.commonjs in an override for **/*.cjs files. The script handles this automatically.
oxlint autofixes Array<T> → T[] and import sorting. Running lint:js:fix before final checks handles these automatically.
Some legitimate patterns trigger oxlint rules:
unicorn/no-thenableon test objects implementing.then()— needsoxlint-disable-next-lineunicorn/no-new-arrayon intentional pre-allocations — needsoxlint-disable-next-line- oxlint uses
oxlint-disable-next-linesyntax, noteslint-disable-next-line
The repo's pre-commit hook uses readlink -f "$0" which resolves to the main worktree path (auditboard-backend) rather than the current worktree (auditboard-backend-tertiary). When pnpm-lock.yaml is staged, the hook fails with "outside repository". Workaround: commit code changes first (without lockfile), then commit lockfile separately (which succeeds because the node-interfaces/DEPRULES checks pass on the second attempt).
The script no longer manages pnpm overrides (add/remove @soxhub/lint and @soxhub/eslint-plugin file overrides). The @soxhub/lint version in the repo now supports the migration tool directly.
The @auditboard/no-dependency-tsconfig-paths rule only applies to tsconfig*.json files. The script detects this rule in the existing eslint config and preserves it in the JSON-only eslint config. The plugin must be removed from oxlint's jsPlugins.