Repository navigation
fix(resolution): a barrel's export { A } from './x' links what ./x exports, not a namesake - #2685
Merged
Merged
Conversation
…ports, not a namesake
A barrel's own export { A, B as C } from './x' emits one imports
reference per name it passes on, named as ./x calls it. No import binds
a re-exported name, so the resolver looked each one up by its name alone
and could reach a namesake elsewhere in the project, or a project
function named like a package's export.
Each such name now resolves through the module the statement names, as
an import does, following barrels of barrels and renames. A name passed
on from an outside package counts as bound to that package, so it never
name-matches a project symbol. A name two statements re-export from
different modules keeps the old lookup.
Sync re-opens a barrel's re-exported names when the module they go
through is deleted or added, as it already did for the names an importer
binds, so a synced graph keeps matching a fresh index.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Problem
A barrel's own
export { A, B as C } from './x'emits oneimportsreference per name it passes on (A,B: the names as./xcalls them). #2526 added the statement's import node and module edge, and left those per-name references as they were.No import statement binds a re-exported name, so
resolveViaImportnever matched one, and each was looked up by its name alone. That picked whichever same-named symbol the name matcher liked:export { DefaultExpandedProvider, SetDefaultExpandedProvider, … } from './context'. These linked to a namesake in the build table'scontext.tsas soon as fix(extraction): a build folder inside src/ is indexed for its TypeScript, JSX, Vue, Svelte and Astro files #2681 indexedsrc/build/. On main they were right only because no namesake was indexed. fix(extraction): a build folder inside src/ is indexed for its TypeScript, JSX, Vue, Svelte and Astro files #2681's A/B flagged them.index.tsxpasses onSidebarandCommandPalette. They linked to an example app'sExampleSidebar.tsxand to a main-menu item.runtime/app/navigation/client.jspasses onafterNavigate,gotoand the rest from../../client/client.js. They linked to the server stubs.export { initialize, log } from "electron-log"in Folo orexport { HookableCore } from "hookable"in nitro, linked to a project function or interface of that name.Fix
resolveViaImport(src/resolution/import-resolver.ts): a JS/TSimportsreference that no import binds, and that the file re-exports by that name from exactly one module, resolves through that module. That'sresolveImportPathplusfindExportedSymbol, which follows barrels of barrels and renames (astro'srenderisrenderTemplate as renderone barrel further). A name two statements re-export from different modules keeps the old lookup.isOutOfRepoBinding(src/resolution/name-matcher.ts): a name re-exported from an outside package counts as bound to that package, as an imported one already does. So it never name-matches a project namesake.reExportSourcereads the re-exports the resolver already parses (getReExports, the same tablefindExportedSymbolchases for importers). It lives in name-matcher, which import-resolver already imports from.src/resolution/module-importers.ts,bindingsOf).sync-module-importers.test.ts's two ES-barrel convergence tests failed without this.Extraction doesn't change, so neither does the kernel. An existing index keeps its old links until the barrel changes or the project is re-indexed, so the CHANGELOG says to re-index.
Verification
__tests__/named-reexport-module.test.ts, 4 tests, all failing on main:export { Provider as TableProvider });isOutOfRepoBindingchange, only the package test fails.bindingsOfchange,sync-module-importers.test.ts's ES-module barrel tests "when it is deleted" and "when it appears after its importers were bound without it" fail (fix(ts): a re-export imports its module, and sync follows barrels of barrels #2526's tests). Their synced graph keeps the barrel's re-export edge resolved through a module that is gone, or by name while the module was missing.tsc --noEmitis clean.CODEGRAPH_KERNEL_EXPECT=1,--maxWorkers=4 --minWorkers=1): 7,603 tests, 7,523 passed, 0 failed, 80 skipped.try!(fix(swift): a call written after try! links what it calls on Windows #2683, which changed the kernel's grammars) and C/C++ includes (fix(c,cpp): a name in C or C++ code outside tests links to what the file's own includes declare #2684, which editsname-matcher.tsfar from this change's hunks). Rebuilt the kernel and reran tsc plus this PR's tests, the incoming PRs' tests, the sync re-export suites, the fix(extraction): a build folder inside src/ is indexed for its TypeScript, JSX, Vue, Svelte and Astro files #2681 tests and every kernel parity suite: 21 files, 321 tests, all passed, none skipped.A/B on real repos
Arms: origin/main 16d1c95, and this branch on it. They differ only in
import-resolver.jsandname-matcher.js, and both carry the same kernel. Each repo was indexed fresh withcodegraph init -yand dumped withscripts/dump-graph.mjs. Edges are paired by kind, source, line, column and name.16 byte-identical: ghostfolio, RuoYi-Vue3, mantis, matx, takenote, react-redux-realworld, proshop-v2, supertokens/dashboard, Reactime, mihailgaberov/chat, ziya, kkFileView, Games-and-Go, Mimi.jl, pypa/build (Python) and phpip (PHP).
14 changed. Every change is a barrel's own
importsedge; no other edge, node or file moved.packages/astro)packages/kit)Same target (934): these edges kept their target and now resolve through the module (
resolvedBy: import, 0.9) instead of by name (exact-match0.5–0.9, or the React framework rule's 0.8).Moved (55): I checked about 25 against the source and found no wrong move:
src/gitiles/.../context/context.tsx. AlsoInvocationProviderreaches./provider(notcommon/store/invocation_state), andComponentreacheshealth_page.tsx(not a Lit interface).v4/mini/parse.tsre-exportsparse,encode,decodeand the rest from../core/index.js, and now reachescore/parse.ts(notclassic/parse.ts).runtime/client/client.js.Sidebarreachescomponents/Sidebar/Sidebar.tsx, where main had an example app's sidebar;CommandPalettereachescomponents/CommandPalette/CommandPalette.tsx, where main had a menu item;ChatMessagereaches the component, where main had a type alias.RootPortalreachespackages/internal/components/src/ui/portal, where main had the landing app's;EmptyStatereachesChatSessionComponents.tsx;ShortcutModalContentreaches its own folder's file.defineActionandgetActionContextreachactions/runtime/server.ts, where main had the client entrypoint.renderreachesrenderTemplate.runTaskreachessrc/task.ts.Now resolved (5): previously failed refs such as milo's
search_input/index.tsSearchInput.No longer linked (3): all re-exports from packages: Folo's
electron-log(2) and nitro'shookable.Sync A/B. In #2526's two scenarios, every 2nd barrel
index.{ts,tsx,js,jsx}with anexport … fromis either deleted after indexing, or added back after an index built without it. Each arm's synced graph is compared with its own fresh index of the final tree.milo's drift lines are byte-identical across arms. That drift exists on main and isn't from re-exports.
Cost: none measurable. Each re-exported name does one table lookup plus the module resolution an import already does.
Overlap
Open #2483 makes type-only re-exports (
export type { X } from) visible toextractReExports. If it lands, those names resolve through their module by this rule too, with no change here.🤖 Generated with Claude Code