fix(js,ts): a function value imported from a package no longer links a project function of that name (#2671)

A function passed as a value (`styled(Link)`, `createContext({ onClose:
noop })`, `Chart.register(Legend)`) whose name the file imports from a
package is that package's function. matchFunctionRef still bound it to
the one project function of that name whenever there was exactly one:
outline's react-router-dom `Link` landed on an email template's
`Footer.tsx:Link`, cord-field's lodash `noop` on a test's own stub.

The cross-file fallback now consults the same out-of-repo guard the
call and value paths use (isOutOfRepoBinding). A same-file declaration
still wins (a local that shadows the import is what the name means
there), a project import still resolves through its import, and a
specifier no package.json declares may be an alias and stays the
project's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-10-19 18:11:07 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 1aebd3a30f
commit b8f81e2c8e
3 changed files with 188 additions and 2 deletions
+6 -2
View File
@@ -1194,9 +1194,13 @@ export function matchFunctionRef(
// Cross-file (imported names the import resolver didn't already claim):
// only an unambiguous match resolves — or, in Python, the one in reach of
// a name the file imports (netbox's `sender=CustomField` beside a test's
// own nested `CustomField`).
// own nested `CustomField`). Never a name the file imports from a package
// outside the repository, which no project function is: outline's
// `styled(Link)`, with `Link` from react-router-dom, is not an email
// template's `Link`, nor is cord-field's lodash `noop` a test's own stub.
if (candidates.length === 1 && (named.length === 1 ||
(ref.language === 'python' && pythonFromImports(ref.filePath, context).has(ref.referenceName)))) {
(ref.language === 'python' && pythonFromImports(ref.filePath, context).has(ref.referenceName))) &&
!(JS_FAMILY.has(ref.language) && isOutOfRepoBinding(ref.referenceName, ref, context))) {
return {
original: ref,
targetNodeId: candidates[0]!.id,