Repository navigation
perf: sign typed-lint results once per build rather than once per batch - #397
Conversation
While webpack builds, the ESLint check is handed files in batches, and every batch signed its files from scratch: it parsed each config file again and checked every file of the program again, so a build paid for the whole project once per batch. One signer now answers for a build, reading each file and config file once and keeping the signatures it worked out for the batches after. Over 3,000 modules checked by typed ESLint and the TypeScript check with threads: false, a warm build took 13.3-13.7 s and now takes 11.9-12.4 s; signing took 1.9 s of it and now takes 0.3 s. Refs #374. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013FhutQipYi25fJ4rs9WE1c
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #397 +/- ##
=======================================
Coverage 99.69% 99.69%
=======================================
Files 21 21
Lines 6552 6630 +78
=======================================
+ Hits 6532 6610 +78
Misses 20 20 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
No actionable comments were generated in the recent review.
|
There was a problem hiding this comment.
Actionable comments posted: 1

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
822ff5b9-a3a0-425e-befa-00a87d660916
Files selected for processing: 4-
.changeset/sign-once-per-build.mdsrc/checks/eslint.jssrc/import-graph.jstest/import-graph.test.js
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.
-
…g file A config file first reached in a later batch had no hash recorded and was taken for one that changed: the signer forgot everything the build had read and read the batch again. In a monorepo with a tsconfig.json per package that happened for every batch reaching a new package. Only a config file whose recorded hash differs now starts over. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013FhutQipYi25fJ4rs9WE1c
Refs #374.
While webpack builds, the ESLint check gets files in batches of about 80. Under typed linting each batch called
signatures()from scratch: it parsed everytsconfig.jsonagain and stat-ed every file of the program again. So one build paid for the whole project once per batch.The fix
createSigner(graph)returns a signer that one ESLint check instance keeps for its build, which is one compilation (src/check.js:207). Within a build it:If a config file turns out to have changed, it starts over, as before.
signatures(graph, files)stays as a one-batch wrapper.Measured inside a warm build of the barrel-shaped project from #395 (3,000 modules):
Memory is the peak memory of the whole process tree, including fork-ts-checker's own process.
What's left with
threads: falseAbout 1.2 s and 350 MiB still separate
threads: falsefrom the old plugins. That cost is the TypeScript check itself: 824 ms increateProgramand 310 ms building the incremental state, run on webpack's thread and held in webpack's heap. fork-ts-checker pays roughly the same, but in a separate process alongside the build. That's whatthreads: falseasks for, and the README already says so. With threads on (the default), the plugin matches or beats the old plugins.Tests
test/import-graph.test.jsadds two tests.tsconfig.jsonagain. Per-batch signing, as onmain, reads it 3 times for the first batch and 7 times after three; the signer stays at 3.npm run lintpasses and the full suite (369 tests) passes, withNODE_PATHunset as before.Generated by Claude Code
Summary by CodeRabbit