Skip to content

build: replace ESLint with type-aware Oxlint - #79

Draft
SBoudrias wants to merge 12 commits into
mainfrom
replace-eslint-oxlint
Draft

build: replace ESLint with type-aware Oxlint#79
SBoudrias wants to merge 12 commits into
mainfrom
replace-eslint-oxlint

Conversation

@SBoudrias

Copy link
Copy Markdown
Owner

Summary

Replace the ESLint stack with Oxlint and enable type-aware linting via oxlint-tsgolint:

  • Remove eslint, eslint-config-xo, typescript-eslint, eslint-config-prettier; add oxlint@^1.78.0 + oxlint-tsgolint@^7.0.2001.
  • New oxlint.config.ts (TS config, same style as oxfmt.config.ts): options.typeAware: true, plugins typescript, unicorn, oxc, import, promise, node, vitest, and all seven rule categories at error (correctness, suspicious, pedantic, perf, style, restriction, nursery) — 545 rules active. Rules that would force codebase-wide rewrites are disabled individually, each with a comment explaining why (e.g. node/no-sync — sync APIs are the library's purpose; unicorn/no-null — vinyl uses null contents; typescript/consistent-type-definitions — interfaces are required for the intentional declaration merging in mem-fs-editor).
  • Type-aware rules now enforce the typescript/no-unsafe-* family, strict-boolean-expressions, no-unnecessary-condition, no-floating-promises, etc., which the old config never ran (XO + tseslint.configs.recommended had no type information).

Toolchain changes

  • TypeScript 6 → 7.0.2 and Yarn 4.15.0 → 4.18.0: Yarn's built-in compat/typescript patch hard-fails on TS 7's native package layout (ENOENT lib/_tsc.js); the fix shipped in Yarn 4.17.1 (Handle optional compat patch failures for TypeScript 7 yarnpkg/berry#7190).
  • pretest, nano-staged, the CI lint job and dependabot groups now reference oxlint; eslint.config.js is deleted.
  • The root tsconfig now includes **/*.spec.ts and packages/*/test/**/*.ts, so tests and test helpers are type-checked and get accurate type-aware linting (they were previously invisible to tsc).

Code changes (~220 violations resolved)

  • Explicit return types and member accessibility across the public API (Store, editor actions, state.ts).
  • Explicit nullish/boolean comparisons (strict-boolean-expressions, no-unnecessary-condition, prefer-nullish-coalescing).
  • Type-safe tests: fixtures are typed (state.spec.ts had an untyped let file), JSON assertions use whole-object toEqual instead of member access on any, narrowing casts replaced with typed locals.
  • no-param-reassign/no-shadow cleanups, merged duplicate imports, node: protocol imports, async/await instead of .then() chains in Store.getAsync and the commit transform.
  • One pre-existing test bug fixed: state.spec.ts asserted on file.clearedState (nonexistent property, always undefined) — it now asserts on stateCleared with the expected value.

Intentional suppressions (documented inline)

  • Store extends EventEmitter — EventEmitter is the public API (unicorn/prefer-event-target).
  • The pipeline() calls in mem-fs keep their any assertions behind a block disable — the stream overloads can't express the object-mode generic types.
  • copy-tpl's compat-mode 4th argument cast, and the Transform callback pattern in transform.ts (callback-based Node API; rules conflict).
  • Two defensive non-Error fallback branches carry /* v8 ignore */ hints to keep the 100% branch-coverage threshold.

Validation

  • yarn oxlint0 errors, 0 warnings (545 rules, type-aware)
  • yarn oxfmt --check — clean
  • yarn tsc — clean (packages + specs)
  • yarn test — 230/230 pass, 100% branch coverage maintained

Worked on with Oz: https://app.warp.dev/conversation/06648078-feae-4605-a4d4-78df45a31302

Co-Authored-By: Oz oz-agent@warp.dev

@SBoudrias
SBoudrias requested a review from mshima August 17, 2026 20:05
SBoudrias and others added 8 commits August 17, 2026 17:15
- Remove eslint, eslint-config-xo, typescript-eslint, eslint-config-prettier
- Add oxlint + oxlint-tsgolint with typeAware enabled and all rule
  categories at error severity; churn-heavy rules documented as off
- Bump typescript to ^7.0.2 and yarn to 4.18.0 (TS7 compat patch fix)
- Update pretest, nano-staged, CI lint step and dependabot groups
- Type-check spec files from the root tsconfig
- Core fixes: mem-fs Store (accessibility, param reassignment, promise
  chains), editor state.ts return types, typed state.spec.ts fixture

Co-Authored-By: Oz <oz-agent@warp.dev>
- Replace file.contents?.toString() with a contentsString() helper that narrows contents to Buffer first (typescript/no-base-to-string)
- Narrow vinyl stat access to file.stat?.isDirectory() (typescript/no-unnecessary-condition)
- Use string describe titles distinct from imported function names (vitest/valid-title, vitest/prefer-describe-function-title)
- Replace named node:path import with default import (unicorn/import-style)
- Use import.meta.dirname instead of __dirname (unicorn/prefer-module)
- Replace files[index]! with a checked local and index++ with index += 1 (typescript/no-non-null-assertion, eslint/no-plusplus)
- Type stream data handlers as (file: File) instead of implicit any (typescript/no-unsafe-member-access)
- Type the change listener mock as vi.fn<(path: string) => void>() (vitest/require-mock-type-parameters, typescript/strict-void-return)
- Name the async generator and use a ternary yield (eslint/func-names, unicorn/prefer-ternary)
- Replace (store as any).store with an internalStoreMap() helper using a documented, disabled narrowing assertion into the store internals (typescript/no-unsafe-*)
- Drop now-unneeded eslint-disable comments

Co-Authored-By: Oz <oz-agent@warp.dev>
Add explicit return types to exported actions, make nullable boolean
checks explicit, replace param reassignment with locals, merge duplicate
imports, name default-exported functions, reorder helpers before use,
convert transform.ts to async/await, and scope two justified oxlint
disables (Transform callback API, compat-mode EJS options assertion).

Co-Authored-By: Oz <oz-agent@warp.dev>
- Merge duplicate-path import handling per unicorn/import-style (default-only
  node:path imports)
- Replace for-loop over describes with describe.each (vitest/prefer-each) and
  extract the copyTpl-only test into its own describe (vitest/no-conditional-tests)
- Use template-literal titles (vitest/valid-title) and typed vi.fn()/hoisted
  mocks (vitest/require-mock-type-parameters)
- Type tinyglobby mocks through non-deprecated glob/globSync signatures
  (typescript/no-deprecated); string-form vi.mock with a documented
  prefer-import-in-mock suppression
- Replace narrowing 'as' casts on readJSON results with whole-object toEqual
  assertions (typescript/no-unsafe-type-assertion)
- Type JSON.stringify replacer params as unknown (typescript/no-unsafe-return)
- Store spies in typed MockInstance locals (typescript/unbound-method) and use
  chai assert for the beforeEach invariant (vitest/no-standalone-expect)
- Name async generator filters (eslint/func-names), use += 1 and a for loop
  (eslint/no-plusplus), and convert eslint-disable comments to oxlint form
- Add explicit types to fixtures.ts; suppress its type-aware no-unsafe-* rules
  with a documented block disable (file is outside every tsconfig include, so
  type-aware linting resolves imports to error types)
- Fix latent tsc errors: index-signature property access via brackets
  (TS4111), undefined-safe random tmpdir segment, and per-branch typed
  fileTransform mocks instead of union-call mismatches (TS2322)
- Add expect.hasAssertions() where assertions run inside the fileTransform
  callback (vitest/expect-expect)

Co-Authored-By: Oz <oz-agent@warp.dev>
- resolveGlobOptions: branch-neutral explicit boolean comparisons
- transform/read-json: v8-ignore hints on defensive non-Error fallbacks
- include packages/*/test/**/*.ts in root tsconfig so type-aware linting
  covers shared test helpers (drops the fixtures.ts suppression block)

Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Drops the contentsString() helper — no stringification is needed at all,
which also keeps no-base-to-string satisfied without narrowing casts.

Co-Authored-By: Oz <oz-agent@warp.dev>
FileTransform is now PipelineTransform<AsyncIterable<StoreFile>, StoreFile>,
so pipeline() accepts the store stream and user transforms without any.

Co-Authored-By: Oz <oz-agent@warp.dev>
@SBoudrias
SBoudrias force-pushed the replace-eslint-oxlint branch from 8046222 to 018c129 Compare August 17, 2026 21:16
SBoudrias and others added 4 commits August 17, 2026 17:20
Co-Authored-By: Oz <oz-agent@warp.dev>
Compare Buffer contents directly instead of stringifying.

Co-Authored-By: Oz <oz-agent@warp.dev>
Allow that one loose-equality form in oxlint. Keep strict ===/!==
when null is a vinyl contents sentinel.

Co-Authored-By: Oz <oz-agent@warp.dev>
- Remove the beforeEach addSpy length check; the test that needs it
  already uses toHaveBeenCalledOnce()
- Restore the original Transform .then() callback
- Drop explicit public member accessibility
- Turn off Node callback/bitwise rules that only produced ignore comments

Co-Authored-By: Oz <oz-agent@warp.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant