Repository navigation
Reuse programs across edits to files with synthetic imports - #65104
Merged
Merged
Conversation
Since content mappers were added, ReuseProgram falls back to a full program build whenever the edited file needs a synthetic tslib or JSX runtime import. Every .tsx file in a project using the automatic JSX runtime has one, so each keystroke in such a file rebuilds the whole program in the language server. A synthetic import's resolution doesn't depend on the file's contents, so the program can be reused when the edit leaves the imports unchanged. The synthetic nodes are parented to the old file, so they are recreated for the new one, and include reasons now refer to synthetic imports by kind so they stay valid in the reused program.
Jake Bailey (jakebailey)
requested review from
Andrew Branch (andrewbranch)
and
a balanced review from Copilot
November 2, 2026 17:44
Andrew Branch (andrewbranch)
approved these changes
Nov 2, 2026
Andrew Branch (andrewbranch)
left a comment
Member
There was a problem hiding this comment.
Nice find!
Jake Bailey (jakebailey)
enabled auto-merge
November 2, 2026 17:49
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The reuse check permits cloning after a virtual extension changes from .cts to .mts, retaining stale CommonJS metadata and require-mode tslib resolution instead of rebuilding for ESM.
1 open issue
What changed in this PR
This PR lets the language server reuse programs after edits that leave synthetic tslib and JSX runtime imports unchanged, avoiding full program rebuilds for common TSX edits.
Changes:
- Allow reuse when synthetic imports match, recreating their nodes for the updated files.
- Track synthetic imports by kind so file-inclusion reasons remain valid across reuse.
- Add coverage for reuse, synthetic-node ownership, diagnostics, and changed JSX import sources.
| File | Description |
|---|---|
tsc/internal/project/project_test.go |
Tests reuse with unchanged synthetic imports and fallback when the JSX import source changes. |
tsc/internal/compiler/program.go |
Checks synthetic-import compatibility and recreates nodes during reuse. |
tsc/internal/compiler/fileloader.go |
Records synthetic-import kinds and extracts shared node creation. |
tsc/internal/compiler/fileInclude.go |
Resolves synthetic-import inclusion reasons through the current program. |
Review effort: Balanced
Jake Bailey (jakebailey)
disabled auto-merge
November 2, 2026 17:58
A content mapper can switch a file's virtual extension between edits, for example from .cts to .mts. The parse options stay the same, so the program was reused with the file's old implied module format and the resolutions made under it, such as a require-mode tslib import.
Jake Bailey (jakebailey)
requested a review
from Andrew Branch (andrewbranch)
November 2, 2026 18:59
Andrew Branch (andrewbranch)
approved these changes
Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Since content mappers were added, ReuseProgram falls back to a full program build whenever the edited file needs a synthetic tslib or JSX runtime import. Every .tsx file in a project using the automatic JSX runtime has one, so each keystroke in such a file rebuilds the whole program in the language server.
A synthetic import's resolution doesn't depend on the file's contents, so the program can be reused when the edit leaves the imports unchanged. The synthetic nodes are parented to the old file, so they are recreated for the new one, and include reasons now refer to synthetic imports by kind so they stay valid in the reused program.