( ref: UnresolvedRef, imports: ImportMapping[], context: ResolutionContext )
| 1948 | } |
| 1949 | |
| 1950 | function resolveModuleImportToFile( |
| 1951 | ref: UnresolvedRef, |
| 1952 | imports: ImportMapping[], |
| 1953 | context: ResolutionContext |
| 1954 | ): ResolvedRef | null { |
| 1955 | if (ref.referenceKind !== 'imports') return null; |
| 1956 | if (ref.referenceName.includes('.')) return null; |
| 1957 | |
| 1958 | for (const imp of imports) { |
| 1959 | if (imp.localName !== ref.referenceName) continue; |
| 1960 | |
| 1961 | let modulePath: string; |
| 1962 | if (imp.isNamespace || imp.isDefault) { |
| 1963 | // `import * as ns from './x'` (namespace) or `import x from './x'` |
| 1964 | // (default) — the dependency is on the MODULE FILE. A default import binds |
| 1965 | // a (possibly renamed) local to whatever the module's default export is |
| 1966 | // (`import articlesController from './article.controller'` ← `export |
| 1967 | // default router`), so the binding name can't be found as a symbol — link |
| 1968 | // the file the import resolves to instead. External modules don't resolve |
| 1969 | // (no file), so `import React from 'react'` creates no edge. |
| 1970 | modulePath = imp.source; |
| 1971 | } else if (ref.language === 'python') { |
| 1972 | // `from . import certs` — the imported NAME is a submodule of the source. |
| 1973 | // As in resolvePythonModuleMember, use the exported name so an alias |
| 1974 | // still links to the real module file (#1626). |
| 1975 | const moduleName = imp.exportedName === '*' ? imp.localName : imp.exportedName; |
| 1976 | modulePath = imp.source.endsWith('.') |
| 1977 | ? imp.source + moduleName |
| 1978 | : imp.source + '.' + moduleName; |
| 1979 | } else { |
| 1980 | // A named TS/JS import binds a symbol, not a module — leave it alone. |
| 1981 | continue; |
| 1982 | } |
| 1983 | |
| 1984 | const resolvedPath = resolveImportPath(modulePath, ref.filePath, ref.language, context); |
| 1985 | if (resolvedPath && resolvedPath !== ref.filePath) { |
| 1986 | const fileNode = context.getNodesInFile(resolvedPath).find((n) => n.kind === 'file'); |
| 1987 | if (fileNode) { |
| 1988 | return { original: ref, targetNodeId: fileNode.id, confidence: 0.9, resolvedBy: 'import' }; |
| 1989 | } |
| 1990 | } |
| 1991 | |
| 1992 | // Python absolute `from a.b import submodule` (a FastAPI router aggregator's |
| 1993 | // `from app.api.routes import authentication`): resolveImportPath only maps |
| 1994 | // RELATIVE dotted paths to a file, so resolve the absolute dotted module |
| 1995 | // directly to its file node. |
| 1996 | if (ref.language === 'python') { |
| 1997 | const modFile = findPythonModuleFile(modulePath, context, ref.filePath); |
| 1998 | if (modFile) { |
| 1999 | return { original: ref, targetNodeId: modFile.id, confidence: 0.9, resolvedBy: 'import' }; |
| 2000 | } |
| 2001 | } |
| 2002 | } |
| 2003 | return null; |
| 2004 | } |
| 2005 | |
| 2006 | /** |
| 2007 | * Find the file node for a Python dotted module path `a.b.c` — a module file |
no test coverage detected