mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-29 06:46:49 +08:00
fix(js,ts): a function value imported from the project links only to what its import binds (#2673)
A function passed as a value whose name the file imports from its own project still fell back to the project's one function of that name whenever the import resolver bound the name to something that is not a function: outline's `isCloudHosted` constant landed on the server's `isCloudHosted()` check, its `Star` model class on the `<Star>` icon, `import * as Slack` on the Slack settings component, and topcoder's `import TCO21 from './themes/TCO21.scss'` on a route component. matchFunctionRef's unique cross-file fallback now also declines when a project import binds the name to something else (importBindsOther): a namespace import, an asset import (stylesheet, image, GraphQL, JSON, font, media; judged by the specifier's extension), or an import the import resolver binds to a node that is not a function or method. A wrapper declared beside the namesake (`export default styled(Disclosure)`) and a lazy loader (`lazy(() => import(...))`) still lead to it, and an import the resolver can't follow at all keeps the fallback. Follow-up to #2671. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a50104c895
commit
ab6ec218ff
@@ -1198,9 +1198,11 @@ export function matchFunctionRef(
|
||||
// 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.
|
||||
// Nor a name a project import binds to something else (importBindsOther).
|
||||
if (candidates.length === 1 && (named.length === 1 ||
|
||||
(ref.language === 'python' && pythonFromImports(ref.filePath, context).has(ref.referenceName))) &&
|
||||
!(JS_FAMILY.has(ref.language) && isOutOfRepoBinding(ref.referenceName, ref, context))) {
|
||||
!(JS_FAMILY.has(ref.language) && isOutOfRepoBinding(ref.referenceName, ref, context)) &&
|
||||
!(JS_FAMILY.has(ref.language) && importBindsOther(candidates[0]!, ref, context))) {
|
||||
return {
|
||||
original: ref,
|
||||
targetNodeId: candidates[0]!.id,
|
||||
@@ -14996,6 +14998,33 @@ function isOutOfRepoBinding(name: string, ref: UnresolvedRef, context: Resolutio
|
||||
return !!binding && context.isOutOfRepoImport?.(binding.source, ref.filePath, ref.language) === true;
|
||||
}
|
||||
|
||||
/** A module a bundler loads as data, not code: a stylesheet, an image, a GraphQL document, JSON, a font. */
|
||||
const ASSET_MODULE = /\.(?:css|scss|sass|less|styl|stylus|pcss|graphql|gql|png|jpe?g|gif|svg|webp|avif|ico|bmp|tiff?|json5?|ya?ml|toml|md|mdx|txt|html?|xml|csv|woff2?|ttf|otf|eot|mp3|mp4|webm|ogg|wav|flac|pdf|wasm)$/i;
|
||||
|
||||
/**
|
||||
* Does a project import bind `ref`'s name to something other than `candidate`,
|
||||
* a function of that name in another file? A namespace import is the module
|
||||
* object, and an asset import is what the bundler makes of the file: topcoder's
|
||||
* `import TCO21 from './themes/TCO21.scss'` is no `TCO21()` route. An import
|
||||
* the import resolver binds to a constant, class or component names that:
|
||||
* outline's `isCloudHosted` constant is not the server's `isCloudHosted()`.
|
||||
* Two bindings still lead to the namesake: a wrapper declared beside it
|
||||
* (`export default styled(Disclosure)`), and a lazy loader (signoz's
|
||||
* `Loadable(() => import('pages/Home'))`), which loads the page it is named
|
||||
* after. An import the resolver can't follow at all says nothing.
|
||||
*/
|
||||
function importBindsOther(candidate: Node, ref: UnresolvedRef, context: ResolutionContext): boolean {
|
||||
if (!/^[A-Za-z_$][\w$]*$/.test(ref.referenceName)) return false;
|
||||
const binding = context.getImportMappings(ref.filePath, ref.language).find((m) => m.localName === ref.referenceName);
|
||||
if (!binding) return false;
|
||||
if (binding.isNamespace || ASSET_MODULE.test(binding.source.replace(/[?#].*$/, ''))) return true;
|
||||
const bound = context.resolveImport?.(ref);
|
||||
const node = bound ? context.getNodeById?.(bound.targetNodeId) : null;
|
||||
if (!node || node.kind === 'function' || node.kind === 'method' || node.filePath === candidate.filePath) return false;
|
||||
const lines = (context.getFileLines?.(node.filePath) ?? context.readFile(node.filePath)?.split(/\r?\n/)) ?? [];
|
||||
return !/\bimport\s*\(/.test(lines.slice(node.startLine - 1, Math.min(node.endLine, node.startLine + 15)).join('\n'));
|
||||
}
|
||||
|
||||
/** The directory of a Go file, which is its package: Go keeps one package per directory. */
|
||||
function goPackageDir(filePath: string): string {
|
||||
return path.posix.dirname(filePath.replace(/\\/g, '/'));
|
||||
|
||||
Reference in New Issue
Block a user