Skip to content

feat: TypeScript 7.1 support, one place for compiler-lived state, shared TypeScript runs - #392

Open
alexander-akait wants to merge 6 commits into
mainfrom
perf/watch-and-typescript
Open

alexander-akait wants to merge 6 commits into
mainfrom
perf/watch-and-typescript

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

One PR in place of #371, #384, #387, #389, #390 and #391, which are closed.

Core: one place for what lives as long as a compiler

src/compiler-state.js replaces the per-compiler WeakMaps and the lifecycle code each check carried on its own:

  • Per-compiler state: keptFor(compiler, key, create) holds check results, detached reports, the import graph and the TypeScript program state.
  • Shared workers and processes: with acquire(compiler, key, start, stop) / release(compiler, key), a worker or process is:
    • started once per key;
    • shared by every compiler asking under that key;
    • stopped when the last of them shuts down or closes its watch.
  • Users: the TypeScript check's worker and TypeScript 7's process both go through it, so sharing across a MultiCompiler is one call rather than something each check implements.

The checker API is unchanged. Nothing is added to it, and the options are unchanged too.

TypeScript 7

  • The API used: TypeScript 7 has no compiler API in JavaScript, so the check talks to the native compiler through the API TypeScript ships: typescript/async, from 7.1.
  • Finding it:
    • The API is found by what the package exports, through the package's own name, rather than by parsing its version.
    • Snapshots are made with createSnapshot and kept up to date with snapshot.update({ fileNotifications, ensurePrograms }) from the files webpack saw change.
  • Output: diagnostics are printed by TypeScript's own formatDiagnosticsWithColorAndContext. The output is byte-for-byte what the TypeScript 6 check prints for the same project.
  • TypeScript 7.0: it shipped this API only as an unstable/* preview, with none of these calls. It is not used. A TypeScript offering no API to check with now says so, rather than failing on a missing function.
  • Concurrency: compilers sharing the native process take turns, so one run never disposes of a snapshot another is still reading.
  • Timing (600 files): a one-shot build takes 1.2–1.3 s on 7.1. On TypeScript 6 it takes 3.8 s without the saved state and 2.0–2.1 s with it.

Other changes

  • One TypeScript worker across compilers (from perf: share the TypeScript worker between compilers checking the same project #390): compilers checking the same project alike share one worker and one program. Two compilers building one after the other went from 9.7–10.3 s and 803–853 MB to 5.6–5.8 s and 467–483 MB.
  • Biome and oxlint in one run (from perf: hand Biome and oxlint every file at once #387): they use the existing lintsOnce adapter hook. Child CPU went from 2.9 → 2.2 s for Biome and from 1.2–1.7 → 0.3 s for oxlint.
  • No hand-kept lists:
    • Assets and scripts are told apart by the extensions TypeScript supports, replacing the asset regex and the script regex.
    • The fallback resolution mode and the error category come from the loaded TypeScript.

Removed after measuring again on this branch

  • lintDependentsAfterBuild (from feat: add lintDependentsAfterBuild #371): it made webpack's build finish sooner. It did not make an error in an importer appear sooner: 1.96–2.64 s with the option against 2.09–2.21 s without it. What it hid is a gap in the existing import-graph pruning: a file's exported shape is only known after its first change, so the first edit to a file re-lints everything importing it. The second edit to the same file re-lints one file, in 257 ms. Closing that gap is a separate, follow-up change.
  • cancel() on the checker API (from perf: stop a TypeScript run nothing will report once the next rebuild starts #389): it shortened back-to-back full rechecks from 4.4–5.5 s to 2.7–2.8 s. That only applied to a TypeScript 6 check on a worker reporting as advisory, and TypeScript 7 does not use it. It was not worth a new adapter method, a shared stop flag per worker run, and bookkeeping of which runs other compilers joined.

Tests

  • New: test/compiler-state.test.js and the share and TypeScript 7 tests. The TypeScript 7 tests run against typescript@7.1.0-beta, installed for tests as typescript7; they include one for a TypeScript with no API at all.
  • Tooling: typescript7's tsc bin replaces TypeScript 6's in node_modules/.bin, so build:types and lint:types call TypeScript 6 by path.
  • Locally: lint, types, schema, prettier, cspell and the full suite (354 tests) pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01App18sYbe6Vf6HDCjCXbvf

Summary by CodeRabbit

  • New Features
    • Added support for TypeScript 7.1 checks, with diagnostics available during watch builds. TypeScript 7 checks do not support build mode, writing modes, or plugin-level compiler options.
    • Matching checks across multiple webpack compilers can share resources, reducing repeated work.
  • Improvements
    • Biome and oxlint now run once after webpack finishes building, rather than in batches.
    • Asset imports are recognized using file types supported by the project’s TypeScript configuration, avoiding unnecessary linting of unrelated files.
    • Projects using TypeScript 7 can still lint files when import-reading APIs are unavailable.

…runs

One change in place of #371, #384, #387, #389, #390 and #391.

Core:
- One place keeps what lives as long as a compiler (src/compiler-state.js):
  keptFor() for state kept per compiler (results, detached reports, the
  import graph, the TypeScript program), and acquire()/release() for the
  workers and processes compilers share, started once per key and stopped
  when the last compiler using them shuts down or closes its watch. The
  TypeScript check's worker and TypeScript 7's process both go through it,
  in place of a lifecycle of their own each.
- lintDependentsAfterBuild (opt-in): a watch rebuild of a check that reads
  across files lints only what webpack rebuilt, and what the change
  reaches after the build.
- A check can stop a run of an older compilation nothing will report
  (cancel()); one another compiler joined is never stopped.
- Biome and oxlint are handed every file at once rather than in batches.

TypeScript:
- TypeScript 7 runs through the API it ships, in a process of its own
  kept open while webpack watches; the import graph answers without a
  graph there.
- A stale run stops between files; compilers checking the same project
  alike share one worker and one program.

Nothing is listed by hand where the tool can say it: assets and the
scripts a config file picks up are told apart by the extensions
TypeScript reads, the fallback resolution mode and the error category
come from the loaded TypeScript, and each run carries its own stop flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01App18sYbe6Vf6HDCjCXbvf
@linux-foundation-easycla

linux-foundation-easycla Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

CLA Not Signed

One or more co-authors of this pull request were not found. You must specify co-authors in commit message trailer via:

Co-authored-by: name <email>

Supported Co-authored-by: formats include:

  1. Anything <id+login@users.noreply.github.com> - it will locate your GitHub user by id part.
  2. Anything <login@users.noreply.github.com> - it will locate your GitHub user by login part.
  3. Anything <public-email> - it will locate your GitHub user by public-email part. Note that this email must be made public on Github.
  4. Anything <other-email> - it will locate your GitHub user by other-email part but only if that email was used before for any other CLA as a main commit author.
  5. login <any-valid-email> - it will locate your GitHub user by login part, note that login part must be at least 3 characters long.

Alternatively, if the co-author should not be included, remove the Co-authored-by: line from the commit message.

Please update your commit message(s) by doing git commit --amend and then git push [--force] and then request re-running CLA check via commenting on this pull request:

/easycla

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent 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: f53e42da-e7bd-4366-802e-6635a982cca8
Commits

Reviewing files that changed from the base of the PR and between f6fd1bd and 85d0b8c.

Files selected for processing: 2
  • src/checks/typescript-native.js
  • test/typescript/native.test.js
Files skipped from review as they are similar to previous changes: 1
  • test/typescript/native.test.js

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

Walkthrough

The changes add compiler-scoped state and shared TypeScript resources, including support for TypeScript 7’s native async checking API. TypeScript check results report supported file extensions, and import graph asset classification uses TypeScript’s extensions. Biome and oxlint checks declare that they lint once. Tests, declarations, release notes, and documentation are updated.

Priority: Normal

Merge Risk: Minimal · up to 85d0b

The previously identified shared-check and extended-config watch risks are addressed. No outstanding issue identified here prevents merge after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 56 functions across 46 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly summarizes the main changes: TypeScript 7.1 support, centralized compiler-lived state, and shared TypeScript runs.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Finishing Touches: 💡 2
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 Biome (2.5.15)
src/checks/typescript-native.js

Biome could not lint this file: configuration resulted in errors. Check the repository's Biome configuration and plugins.

test/typescript/native.test.js

Biome could not lint this file: configuration resulted in errors. Check the repository's Biome configuration and plugins.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

test/typescript/native.test.js

Oops! Something went wrong! :(

ESLint: 10.10.0

SyntaxError: Unexpected token ':'
at compileSourceTextModule (node:internal/modules/esm/utils:319:16)
at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:89:18)
at #translate (node:internal/modules/esm/loader:438:20)
at afterLoad (node:internal/modules/esm/loader:506:29)
at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:511:12)
at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:564:36)
at afterResolve (node:internal/modules/esm/loader:611:52)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:617:12)
at node:internal/modules/esm/loader:636:32
at TracingChannel.tracePromise (node:diagnostics_channel:370:14)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedtypescript@​7.1.0-beta991008910090

View full report

@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.68%. Comparing base (0abded0) to head (85d0b8c).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #392      +/-   ##
==========================================
+ Coverage   99.64%   99.68%   +0.03%     
==========================================
  Files          19       21       +2     
  Lines        5618     6319     +701     
==========================================
+ Hits         5598     6299     +701     
  Misses         20       20              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The line under a diagnostic is printed from the text the native compiler
checked (Program.getSourceFile) rather than read from disk again, and a
file gone since webpack saw it is told by statSync without throwing, so
neither needs a catch. A project with no tsconfig.json above it is now
tested from a directory outside this repository.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01App18sYbe6Vf6HDCjCXbvf
… on Windows

webpack names a changed file with the platform's separators, the native
compiler with forward slashes, and Windows tells no case apart, so both
are now compared by the same key. The cancel test accepts a run already
stopped by the time it looks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01App18sYbe6Vf6HDCjCXbvf

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ 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: 54408489-f152-4f8e-88f1-8d39f2e2b612
📥 Commits

Reviewing files that changed from the base of the PR and between 623f1c0 and c7bdd37.

⛔ Files ignored due to path filters: 1
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing: 57
  • .changeset/assets-named-by-typescript.md
  • .changeset/cli-checks-one-run.md
  • .changeset/import-graph-typescript-7.md
  • .changeset/lint-dependents-after-build.md
  • .changeset/ts-share-program-across-compilers.md
  • .changeset/ts-stop-stale-runs.md
  • .changeset/typescript-7.md
  • .cspell.json
  • README.md
  • package.json
  • src/check.js
  • src/checks/biome.check.js
  • src/checks/biome.js
  • src/checks/eslint.check.js
  • src/checks/index.js
  • src/checks/oxlint.check.js
  • src/checks/oxlint.js
  • src/checks/stylelint.check.js
  • src/checks/typescript-native.js
  • src/checks/typescript-program.js
  • src/checks/typescript-worker.js
  • src/checks/typescript.check.js
  • src/checks/typescript.js
  • src/compiler-state.js
  • src/import-graph.js
  • src/index.js
  • src/options.check.js
  • src/options.js
  • src/options.json
  • src/shared-options.json
  • src/threads.js
  • test/after-build.test.js
  • test/compiler-state.test.js
  • test/import-graph.test.js
  • test/incremental.test.js
  • test/lints-once.test.js
  • test/store.test.js
  • test/typescript/cancel.test.js
  • test/typescript/fixtures/cancel/index.js
  • test/typescript/fixtures/native-watch/index.js
  • test/typescript/mock/typescript-counter/index.js
  • test/typescript/mock/typescript-stall/index.js
  • test/typescript/native.test.js
  • test/typescript/share.test.js
  • types/check.d.ts
  • types/checks/biome.d.ts
  • types/checks/index.d.ts
  • types/checks/oxlint.d.ts
  • types/checks/stylelint.check.d.ts
  • types/checks/typescript-native.d.ts
  • types/checks/typescript-program.d.ts
  • types/checks/typescript-worker.d.ts
  • types/checks/typescript.d.ts
  • types/compiler-state.d.ts
  • types/import-graph.d.ts
  • types/options.d.ts
  • types/threads.d.ts

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

Comment thread src/checks/typescript.js
Comment thread src/checks/typescript.js Outdated
Comment thread src/index.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: e6f32033-e51d-4c01-a042-55d72da834b6
📥 Commits

Reviewing files that changed from the base of the PR and between c7bdd37 and ab97c48.

📒 Files selected for processing: 2
  • src/checks/typescript-native.js
  • test/typescript/cancel.test.js

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

Comment thread test/typescript/cancel.test.js Outdated
…d run

Compilers checking the same project share the native compiler's process,
and a run replaces the snapshot and disposes of the last one, which a run
for another compiler could still be reading: runs on one process now take
turns. A run stopped for a newer compilation found nothing, so it is no
longer kept as what a rebuild the program does not see is answered with.
A directory no config file covers is now tested in the import graph.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01App18sYbe6Vf6HDCjCXbvf
…ot pay off

TypeScript 7.1 ships its API as typescript/async, makes snapshots with
createSnapshot and snapshot.update, and formats diagnostics itself. The
native check now uses that API, found by what the package exports rather
than by its version, and TypeScript's own formatter in place of one
written here. TypeScript 7.0's API was an unstable preview with none of
these calls; a TypeScript offering no API to check with now says so.

Measured again on this branch, two additions did not earn their place:
- lintDependentsAfterBuild finished the build sooner but printed an
  error it found in an importer no sooner (1.96-2.64 s against
  2.09-2.21 s without it), at the cost of an option and a second report.
- cancel() on the checker API only ever served a TypeScript 6 check on
  a worker reporting as advisory, and TypeScript 7 does not use it.
Both are removed, with what they needed: a stop flag per worker run,
bookkeeping of which runs another compiler joined, and their tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01App18sYbe6Vf6HDCjCXbvf
@alexander-akait alexander-akait changed the title perf: watch rebuilds, TypeScript 7, and shared or stopped TypeScript runs feat: TypeScript 7.1 support, one place for compiler-lived state, shared TypeScript runs Oct 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

Minor · Wait for the active native turn before closing the API. · compiler-state.js:110-116

src/compiler-state.js:110-116
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Wait for the active native turn before closing the API.

A detached advisory TypeScript check can still be awaiting diagnostics when watchClose releases the last shared client. stopNative() disposes the snapshot without waiting for native.turn, so the active check can use a disposed snapshot and lose its result.

Suggested fix
 async function stopNative(native) {
+  await native.turn;
+
   if (native.snapshot) await native.snapshot.dispose();

   await native.api.close();
 }

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: 2db36893-6d78-42ec-9380-d62309aa5834
Commits

Reviewing files that changed from the base of the PR and between ab97c48 and f6fd1bd.

Files ignored due to path filters: 1
  • package-lock.json is excluded by !**/package-lock.json
Files selected for processing: 17
  • .changeset/ts-share-program-across-compilers.md
  • .changeset/typescript-7.md
  • README.md
  • package.json
  • src/check.js
  • src/checks/biome.js
  • src/checks/oxlint.js
  • src/checks/typescript-native.js
  • src/checks/typescript-program.js
  • src/checks/typescript-worker.js
  • src/checks/typescript.js
  • test/import-graph.test.js
  • test/typescript/mock/typescript-version-only/index.js
  • test/typescript/native.test.js
  • types/check.d.ts
  • types/checks/typescript-native.d.ts
  • types/checks/typescript-program.d.ts
Files with no reviewable changes: 1
  • types/check.d.ts
Files skipped from review as they are similar to previous changes: 4
  • src/checks/oxlint.js
  • .changeset/ts-share-program-across-compilers.md
  • src/checks/biome.js
  • .changeset/typescript-7.md

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

Comment thread src/checks/typescript-native.js Outdated
… run finish before closing

The native check reported only the root config file to the watcher, so an
edit to a config file it extends, outside the folder watched, went
unnoticed unless webpack's own tsconfig resolution happened to watch it.
It now reports every config file the program names (getConfigFileNames),
and tells the compiler of a change to one as a change rather than a new
file. Closing the native process now waits for a run still going.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01App18sYbe6Vf6HDCjCXbvf
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.

2 participants