Skip to content

Adopt @exadev/eslint-config - #22

Merged
Mearman merged 13 commits into
mainfrom
feat/adopt-exadev-eslint-config
Sep 14, 2026
Merged

Mearman merged 13 commits into
mainfrom
feat/adopt-exadev-eslint-config

Conversation

@Mearman

@Mearman Mearman commented Sep 14, 2026

Copy link
Copy Markdown
Member

Summary

  • Adopts @exadev/eslint-config as an exact-pinned dependency, replacing the hand-rolled flat config (js.configs.recommended + tseslint presets + a local no-pointless-reassignment rule + a manual re-export ban), all of which the shared config already covers.
  • Migrates off tseslint.config (now deprecated by typescript-eslint itself) to defineConfig from eslint/config.
  • Bumps the pinned package manager to pnpm 12.4.1 and turns on saveExact.
  • Converts scripts/stamp-schema-ids.mjs to real TypeScript (.mts), removing the need for the old allowDefaultProject carve-out.
  • Fixes every resulting lint violation at its root cause across the codebase (no suppressions or rule exclusions): numeric template-literal interpolations, magic-number literals, non-null assertions, exhaustive-switch consistent-return gaps, TSDoc escaping, and a genuine pre-existing type bug in farm.ts's listSubtree (a readonly array parameter callers were pushing into, previously masked by a stale tsconfig.tsbuildinfo).
  • Repairs a pre-existing data-corruption bug found along the way: src/resolve/decide.ts had literal NUL bytes standing in for spaces inside a template literal, syntactically valid but clearly unintended.

Test plan

  • pnpm lint clean
  • pnpm exec tsc --noEmit clean (verified with a fresh tsbuildinfo)
  • pnpm test — 804 tests passing

Adds @exadev/eslint-config as an exact-pinned dev dependency and
excludes it from the minimum-release-age gate, since it is a
first-party ExaDev package. Also bumps the pinned package manager to
pnpm 12.4.1 and turns on saveExact so future dependency additions
resolve to exact versions rather than caret ranges.
Replaces the hand-rolled flat config (js.configs.recommended plus the
tseslint presets, a local no-pointless-reassignment rule, and a
manual re-export ban) with the shared @exadev/eslint-config array,
which already covers all of the same ground plus stricter rules this
repo wasn't previously enforcing. Also switches from tseslint.config,
which typescript-eslint itself now flags as deprecated, to
defineConfig from eslint/config.

The custom local/no-pointless-reassignment rule and its
eslint-rules/ directory are removed entirely, superseded by the
equivalent exadev/no-pointless-reassignment rule the shared config
already includes.
Rewrites scripts/stamp-schema-ids.mjs as stamp-schema-ids.mts with
real types throughout: an isRecord type guard replaces an `as` cast,
resolveTag and main both carry explicit return types. This also
removes the need for eslint.config.ts's own allowDefaultProject
fallback, since the file is no longer a plain untyped script excluded
from the type-aware project service.

Updates every reference to the old .mjs path (the CI workflow,
gen-schema-core.ts, README.md).
The diagnostic-dedup key template literal in src/resolve/decide.ts
contained literal NUL bytes standing in for the spaces between
segments, syntactically valid to tsc/ESLint but not what the code
was meant to do. Replaces them with real spaces and wraps the numeric
layer values with String() so the same line also satisfies
restrict-template-expressions.
Fixes the resolver module's own lint violations under the newly
adopted shared config: numeric template-literal interpolations
wrapped in String(), magic-number literals extracted into named
constants describing what they represent, a readonly parameter
annotation, and a provably-unreachable undefined check removed from
flattenLayers now that CategoryMap's own type already guarantees a
present entry is a boolean.
Fixes the launcher module's own lint violations under the newly
adopted shared config. Notably, refactors listSubtree in farm.ts from
a readonly out-parameter callers were pushing into (a real pre-existing
type error, masked until now by a stale tsconfig.tsbuildinfo) into a
pure function returning a new array via flatMap; removes a provably
dead os.constants.signals undefined branch in spawn.ts, since that
mapping is closed over every NodeJS.Signals name; replaces
non-null-assertion index reads with explicit undefined guards
throughout; and extracts magic-number literals into named constants.
Fixes config module violations: schema.test.ts derives its
toHaveLength assertion from the actual rules array instead of a
hardcoded count, store.test.ts replaces a non-null-assertion index
read with an explicit undefined guard, and store.ts/load.ts pick up
the same numeric-template and magic-number fixes as elsewhere.
Adds a default: return target satisfies never; branch to
describeWriteTarget's switch (an exhaustive switch with no default
still needs one for consistent-return, since TypeScript's own
exhaustiveness proof doesn't satisfy the rule), replaces
non-null-assertion index reads in configure.ts and directoryRules.ts
with explicit undefined guards, adds braces to void-return arrow
shorthands in configure.test.ts, and fixes a template already reading
an already-narrowed local instead of re-reading the wider option.
Adds a default: return severity satisfies never; branch to
severityPrefix's switch and a String() wrap for a numeric template
interpolation in doctor.ts; simplifies doctor.test.ts's report
assertions by dropping the let-plus-not.toThrow()-plus-repeated-!
pattern in favour of a plain const, since an uncaught throw already
fails the test; and adds a proper compare function to a .sort() call
that previously relied on default lexicographic ordering.
Adds an EXECUTABLE_FILE_MODE named constant for the 0o755 permission
bits shared by claudeShim.ts and its tests, replaces two
Object.assign(new Error(...), { code }) calls in
claudeShim.test.ts with a proper CodedError subclass (exadev bans
Object.assign for this), rewords a TSDoc arrow in cli/parsers.ts, and
extracts a named constant for a magic PID literal in cliError.test.ts.
Fixes check.ts's numeric template interpolations, paths.test.ts's
non-null assertion, realPorts.ts/test.ts and versionDiscovery.ts's
TSDoc escaping, and test-helpers.ts/vitest.config.ts's magic-number
and void-return-arrow violations, matching the pattern already
applied across the rest of the codebase.
…onfig

Backtick-wraps package names and rewords -> arrows in release.config.ts's
TSDoc, extracts named constants for build.mts's magic numbers
(MIN_BUILD_SEA_NODE_MAJOR/MINOR, EXECUTABLE_FILE_MODE,
BYTES_PER_KIB/MIB), and adds a currentNodeMajor() helper in
gen-schema.mts with an explicit undefined guard in place of an inline
process.versions.node.split(".")[0] index read.
@eslint/js is no longer imported directly now that eslint.config.ts
gets it transitively through @exadev/eslint-config, and
knip.config.ts still listed eslint-rules/**/*.ts as a project
pattern after that directory was removed. Both were left over from
the eslint-config adoption and knip correctly flagged them.
@Mearman
Mearman marked this pull request as ready for review September 14, 2026 16:09
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-14T16:15:41.509460Z c152bf0 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Mearman
Mearman merged commit 3cd6ceb into main Sep 14, 2026
30 checks passed
@Mearman
Mearman deleted the feat/adopt-exadev-eslint-config branch September 14, 2026 16:09
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