Repository navigation
Merge export * tables without per-name collision trackers - #64722
Draft
Gavin Kline (gwkline) wants to merge 2 commits into
Draft
Gavin Kline (gwkline) wants to merge 2 commits into
Gavin Kline (gwkline) wants to merge 2 commits into
Conversation
getExportsOfModuleWorker allocated an ExportCollision struct for every name contributed by every export * declaration, kept the names in a separate table before merging them, and then iterated that table once more to look for duplicates. For modules that re-export large modules this dominated the allocations of module export resolution. The merge now writes straight into the module's table, records only the first contributing declaration per name and only when a module has more than one export * declaration, and keeps the names that collided in a map that is usually empty. Modules without export * declarations no longer have their tables cloned when visited from an importer, and the names used to undo type-only marking are collected only when an 'export type *' was seen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Covers the cases the export * merge distinguishes: colliding names from two and three declarations, the same symbol reached twice, a module's own export silencing the collision, a single export *, export type * on either side, and a chain of re-exporting modules. Baselines were generated before the merge was rewritten and are unchanged by it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Gavin Kline (@gwkline) please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
This branch has not been deployed
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.
Fixes #64719
Analysis
getExportsOfModuleWorkerresolves a module's exports by cloning its own table. For eachexport *declaration it merges the re-exported module's table into a scratch table, then merges the scratch table into the result. To report TS2308 ("Module ... has already exported a member named ...") it allocates anExportCollisionstruct, holding the module specifier text, for every name contributed by everyexport *. Afterwards it iterates that per-name table again to find the entries that actually collided. Every visited module is cloned as well, and every visited module's export names are copied into a set that is only consulted when anexport type *was seen.So each
export *layer costs several allocations per re-exported name. That holds even for a module with a singleexport *, where nothing can collide. Barrels over large modules (generated clients, ordate-fns- and AWS-SDK-style entry points) pay this on every layer. The generator in the issue spends nearly all of its 0.23 s check here.Fix
export *per name is recorded, and only when the module has more than oneexport *. Names that do collide go into a map that is almost always empty. The specifier text is computed only when a diagnostic is reported.export *are no longer cloned when visited from an importer. The clone is kept for the module whose table is cached, sincegetExportsOfModulemay extend it.typeOnlyExportStarMaponly when that map exists.Semantics are unchanged. The first contributing declaration still wins, and a module's own exports still shadow re-exports without an error.
defaultandexport=are handled as before, and so are theexport type *override rules.extendExportSymbolsandExportCollisionTablehad no other users and are removed.New test
exportStarNameCollisionscovers each case the merge distinguishes: collisions from two and three declarations, the same symbol reached twice, a local export silencing a collision, a singleexport *,export type *on either side, and a chain of re-exporting modules. Its baselines were generated with the old code and are unchanged. No existing baseline changes.Results on an M3 Pro, with medians of alternating runs:
The profiling, the patch and this description were produced with Claude Code (Claude Fable 5.1 and Claude Opus 5.5). I have read and understand the change and will handle review myself.
Copilot Checklist
I successfully ran the applicable command at the end of my session, and it completed without error:
🤖 Generated with Claude Code