@colbymchenry/codegraph-darwin-x64 0.9.9 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dist/bin/codegraph.d.ts +1 -0
- package/lib/dist/bin/codegraph.d.ts.map +1 -1
- package/lib/dist/bin/codegraph.js +246 -3
- package/lib/dist/bin/codegraph.js.map +1 -1
- package/lib/dist/context/index.d.ts.map +1 -1
- package/lib/dist/context/index.js +7 -0
- package/lib/dist/context/index.js.map +1 -1
- package/lib/dist/db/index.d.ts.map +1 -1
- package/lib/dist/db/index.js +2 -1
- package/lib/dist/db/index.js.map +1 -1
- package/lib/dist/db/migrations.d.ts +1 -1
- package/lib/dist/db/migrations.d.ts.map +1 -1
- package/lib/dist/db/migrations.js +10 -1
- package/lib/dist/db/migrations.js.map +1 -1
- package/lib/dist/db/queries.d.ts +43 -0
- package/lib/dist/db/queries.d.ts.map +1 -1
- package/lib/dist/db/queries.js +103 -7
- package/lib/dist/db/queries.js.map +1 -1
- package/lib/dist/db/schema.sql +1 -0
- package/lib/dist/db/sqlite-adapter.d.ts +7 -0
- package/lib/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/lib/dist/db/sqlite-adapter.js +3 -0
- package/lib/dist/db/sqlite-adapter.js.map +1 -1
- package/lib/dist/directory.d.ts +34 -2
- package/lib/dist/directory.d.ts.map +1 -1
- package/lib/dist/directory.js +129 -35
- package/lib/dist/directory.js.map +1 -1
- package/lib/dist/extraction/astro-extractor.d.ts +79 -0
- package/lib/dist/extraction/astro-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/astro-extractor.js +320 -0
- package/lib/dist/extraction/astro-extractor.js.map +1 -0
- package/lib/dist/extraction/extraction-version.d.ts +25 -0
- package/lib/dist/extraction/extraction-version.d.ts.map +1 -0
- package/lib/dist/extraction/extraction-version.js +28 -0
- package/lib/dist/extraction/extraction-version.js.map +1 -0
- package/lib/dist/extraction/function-ref.d.ts +118 -0
- package/lib/dist/extraction/function-ref.d.ts.map +1 -0
- package/lib/dist/extraction/function-ref.js +727 -0
- package/lib/dist/extraction/function-ref.js.map +1 -0
- package/lib/dist/extraction/generated-detection.d.ts.map +1 -1
- package/lib/dist/extraction/generated-detection.js +3 -0
- package/lib/dist/extraction/generated-detection.js.map +1 -1
- package/lib/dist/extraction/grammars.d.ts +7 -1
- package/lib/dist/extraction/grammars.d.ts.map +1 -1
- package/lib/dist/extraction/grammars.js +52 -4
- package/lib/dist/extraction/grammars.js.map +1 -1
- package/lib/dist/extraction/index.d.ts +34 -0
- package/lib/dist/extraction/index.d.ts.map +1 -1
- package/lib/dist/extraction/index.js +346 -62
- package/lib/dist/extraction/index.js.map +1 -1
- package/lib/dist/extraction/languages/c-cpp.d.ts +8 -0
- package/lib/dist/extraction/languages/c-cpp.d.ts.map +1 -1
- package/lib/dist/extraction/languages/c-cpp.js +87 -28
- package/lib/dist/extraction/languages/c-cpp.js.map +1 -1
- package/lib/dist/extraction/languages/csharp.d.ts +22 -0
- package/lib/dist/extraction/languages/csharp.d.ts.map +1 -1
- package/lib/dist/extraction/languages/csharp.js +84 -2
- package/lib/dist/extraction/languages/csharp.js.map +1 -1
- package/lib/dist/extraction/languages/dart.d.ts.map +1 -1
- package/lib/dist/extraction/languages/dart.js +161 -1
- package/lib/dist/extraction/languages/dart.js.map +1 -1
- package/lib/dist/extraction/languages/go.d.ts.map +1 -1
- package/lib/dist/extraction/languages/go.js +43 -2
- package/lib/dist/extraction/languages/go.js.map +1 -1
- package/lib/dist/extraction/languages/index.d.ts.map +1 -1
- package/lib/dist/extraction/languages/index.js +2 -0
- package/lib/dist/extraction/languages/index.js.map +1 -1
- package/lib/dist/extraction/languages/java.d.ts.map +1 -1
- package/lib/dist/extraction/languages/java.js +42 -1
- package/lib/dist/extraction/languages/java.js.map +1 -1
- package/lib/dist/extraction/languages/javascript.d.ts.map +1 -1
- package/lib/dist/extraction/languages/javascript.js +16 -0
- package/lib/dist/extraction/languages/javascript.js.map +1 -1
- package/lib/dist/extraction/languages/kotlin.d.ts.map +1 -1
- package/lib/dist/extraction/languages/kotlin.js +69 -0
- package/lib/dist/extraction/languages/kotlin.js.map +1 -1
- package/lib/dist/extraction/languages/objc.d.ts.map +1 -1
- package/lib/dist/extraction/languages/objc.js +42 -0
- package/lib/dist/extraction/languages/objc.js.map +1 -1
- package/lib/dist/extraction/languages/pascal.d.ts.map +1 -1
- package/lib/dist/extraction/languages/pascal.js +11 -0
- package/lib/dist/extraction/languages/pascal.js.map +1 -1
- package/lib/dist/extraction/languages/php.d.ts.map +1 -1
- package/lib/dist/extraction/languages/php.js +90 -1
- package/lib/dist/extraction/languages/php.js.map +1 -1
- package/lib/dist/extraction/languages/r.d.ts +3 -0
- package/lib/dist/extraction/languages/r.d.ts.map +1 -0
- package/lib/dist/extraction/languages/r.js +314 -0
- package/lib/dist/extraction/languages/r.js.map +1 -0
- package/lib/dist/extraction/languages/ruby.d.ts.map +1 -1
- package/lib/dist/extraction/languages/ruby.js +35 -0
- package/lib/dist/extraction/languages/ruby.js.map +1 -1
- package/lib/dist/extraction/languages/rust.d.ts.map +1 -1
- package/lib/dist/extraction/languages/rust.js +35 -2
- package/lib/dist/extraction/languages/rust.js.map +1 -1
- package/lib/dist/extraction/languages/scala.d.ts.map +1 -1
- package/lib/dist/extraction/languages/scala.js +61 -1
- package/lib/dist/extraction/languages/scala.js.map +1 -1
- package/lib/dist/extraction/languages/swift.d.ts.map +1 -1
- package/lib/dist/extraction/languages/swift.js +61 -0
- package/lib/dist/extraction/languages/swift.js.map +1 -1
- package/lib/dist/extraction/languages/typescript.d.ts +13 -0
- package/lib/dist/extraction/languages/typescript.d.ts.map +1 -1
- package/lib/dist/extraction/languages/typescript.js +38 -0
- package/lib/dist/extraction/languages/typescript.js.map +1 -1
- package/lib/dist/extraction/liquid-extractor.d.ts +7 -0
- package/lib/dist/extraction/liquid-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/liquid-extractor.js +53 -9
- package/lib/dist/extraction/liquid-extractor.js.map +1 -1
- package/lib/dist/extraction/razor-extractor.d.ts +42 -0
- package/lib/dist/extraction/razor-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/razor-extractor.js +285 -0
- package/lib/dist/extraction/razor-extractor.js.map +1 -0
- package/lib/dist/extraction/svelte-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/svelte-extractor.js +6 -3
- package/lib/dist/extraction/svelte-extractor.js.map +1 -1
- package/lib/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter-helpers.js +59 -10
- package/lib/dist/extraction/tree-sitter-helpers.js.map +1 -1
- package/lib/dist/extraction/tree-sitter-types.d.ts +33 -0
- package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter.d.ts +211 -0
- package/lib/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter.js +1681 -49
- package/lib/dist/extraction/tree-sitter.js.map +1 -1
- package/lib/dist/extraction/vue-extractor.d.ts +15 -0
- package/lib/dist/extraction/vue-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/vue-extractor.js +94 -3
- package/lib/dist/extraction/vue-extractor.js.map +1 -1
- package/lib/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/lib/dist/graph/queries.d.ts.map +1 -1
- package/lib/dist/graph/queries.js +13 -40
- package/lib/dist/graph/queries.js.map +1 -1
- package/lib/dist/graph/traversal.d.ts.map +1 -1
- package/lib/dist/graph/traversal.js +16 -4
- package/lib/dist/graph/traversal.js.map +1 -1
- package/lib/dist/index.d.ts +34 -2
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +90 -8
- package/lib/dist/index.js.map +1 -1
- package/lib/dist/installer/index.d.ts.map +1 -1
- package/lib/dist/installer/index.js +52 -2
- package/lib/dist/installer/index.js.map +1 -1
- package/lib/dist/installer/instructions-template.d.ts +34 -11
- package/lib/dist/installer/instructions-template.d.ts.map +1 -1
- package/lib/dist/installer/instructions-template.js +44 -12
- package/lib/dist/installer/instructions-template.js.map +1 -1
- package/lib/dist/installer/targets/claude.d.ts.map +1 -1
- package/lib/dist/installer/targets/claude.js +6 -10
- package/lib/dist/installer/targets/claude.js.map +1 -1
- package/lib/dist/installer/targets/codex.js +4 -6
- package/lib/dist/installer/targets/codex.js.map +1 -1
- package/lib/dist/installer/targets/gemini.js +4 -6
- package/lib/dist/installer/targets/gemini.js.map +1 -1
- package/lib/dist/installer/targets/opencode.d.ts +9 -1
- package/lib/dist/installer/targets/opencode.d.ts.map +1 -1
- package/lib/dist/installer/targets/opencode.js +91 -40
- package/lib/dist/installer/targets/opencode.js.map +1 -1
- package/lib/dist/installer/targets/shared.d.ts +14 -0
- package/lib/dist/installer/targets/shared.d.ts.map +1 -1
- package/lib/dist/installer/targets/shared.js +16 -0
- package/lib/dist/installer/targets/shared.js.map +1 -1
- package/lib/dist/mcp/daemon.d.ts +60 -1
- package/lib/dist/mcp/daemon.d.ts.map +1 -1
- package/lib/dist/mcp/daemon.js +221 -8
- package/lib/dist/mcp/daemon.js.map +1 -1
- package/lib/dist/mcp/dynamic-boundaries.d.ts +41 -0
- package/lib/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
- package/lib/dist/mcp/dynamic-boundaries.js +359 -0
- package/lib/dist/mcp/dynamic-boundaries.js.map +1 -0
- package/lib/dist/mcp/index.d.ts.map +1 -1
- package/lib/dist/mcp/index.js +18 -9
- package/lib/dist/mcp/index.js.map +1 -1
- package/lib/dist/mcp/ppid-watchdog.d.ts +44 -0
- package/lib/dist/mcp/ppid-watchdog.d.ts.map +1 -0
- package/lib/dist/mcp/ppid-watchdog.js +27 -0
- package/lib/dist/mcp/ppid-watchdog.js.map +1 -0
- package/lib/dist/mcp/proxy.d.ts +6 -0
- package/lib/dist/mcp/proxy.d.ts.map +1 -1
- package/lib/dist/mcp/proxy.js +153 -24
- package/lib/dist/mcp/proxy.js.map +1 -1
- package/lib/dist/mcp/server-instructions.d.ts +12 -1
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
- package/lib/dist/mcp/server-instructions.js +43 -16
- package/lib/dist/mcp/server-instructions.js.map +1 -1
- package/lib/dist/mcp/session.d.ts +2 -0
- package/lib/dist/mcp/session.d.ts.map +1 -1
- package/lib/dist/mcp/session.js +49 -2
- package/lib/dist/mcp/session.js.map +1 -1
- package/lib/dist/mcp/stdin-teardown.d.ts +27 -0
- package/lib/dist/mcp/stdin-teardown.d.ts.map +1 -0
- package/lib/dist/mcp/stdin-teardown.js +49 -0
- package/lib/dist/mcp/stdin-teardown.js.map +1 -0
- package/lib/dist/mcp/tools.d.ts +71 -0
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +703 -85
- package/lib/dist/mcp/tools.js.map +1 -1
- package/lib/dist/mcp/transport.d.ts.map +1 -1
- package/lib/dist/mcp/transport.js +18 -2
- package/lib/dist/mcp/transport.js.map +1 -1
- package/lib/dist/resolution/callback-synthesizer.d.ts +3 -3
- package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/lib/dist/resolution/callback-synthesizer.js +549 -21
- package/lib/dist/resolution/callback-synthesizer.js.map +1 -1
- package/lib/dist/resolution/frameworks/astro.d.ts +9 -0
- package/lib/dist/resolution/frameworks/astro.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/astro.js +169 -0
- package/lib/dist/resolution/frameworks/astro.js.map +1 -0
- package/lib/dist/resolution/frameworks/expo-modules.d.ts.map +1 -1
- package/lib/dist/resolution/frameworks/expo-modules.js +6 -1
- package/lib/dist/resolution/frameworks/expo-modules.js.map +1 -1
- package/lib/dist/resolution/frameworks/index.d.ts +1 -0
- package/lib/dist/resolution/frameworks/index.d.ts.map +1 -1
- package/lib/dist/resolution/frameworks/index.js +5 -1
- package/lib/dist/resolution/frameworks/index.js.map +1 -1
- package/lib/dist/resolution/frameworks/java.js +6 -1
- package/lib/dist/resolution/frameworks/java.js.map +1 -1
- package/lib/dist/resolution/frameworks/python.d.ts.map +1 -1
- package/lib/dist/resolution/frameworks/python.js +7 -3
- package/lib/dist/resolution/frameworks/python.js.map +1 -1
- package/lib/dist/resolution/frameworks/react-native.d.ts.map +1 -1
- package/lib/dist/resolution/frameworks/react-native.js +53 -3
- package/lib/dist/resolution/frameworks/react-native.js.map +1 -1
- package/lib/dist/resolution/frameworks/react.d.ts.map +1 -1
- package/lib/dist/resolution/frameworks/react.js +15 -3
- package/lib/dist/resolution/frameworks/react.js.map +1 -1
- package/lib/dist/resolution/frameworks/svelte.js +5 -1
- package/lib/dist/resolution/frameworks/svelte.js.map +1 -1
- package/lib/dist/resolution/frameworks/vue.js +24 -27
- package/lib/dist/resolution/frameworks/vue.js.map +1 -1
- package/lib/dist/resolution/import-resolver.d.ts +10 -0
- package/lib/dist/resolution/import-resolver.d.ts.map +1 -1
- package/lib/dist/resolution/import-resolver.js +564 -2
- package/lib/dist/resolution/import-resolver.js.map +1 -1
- package/lib/dist/resolution/index.d.ts +80 -0
- package/lib/dist/resolution/index.d.ts.map +1 -1
- package/lib/dist/resolution/index.js +457 -7
- package/lib/dist/resolution/index.js.map +1 -1
- package/lib/dist/resolution/name-matcher.d.ts +61 -0
- package/lib/dist/resolution/name-matcher.d.ts.map +1 -1
- package/lib/dist/resolution/name-matcher.js +590 -14
- package/lib/dist/resolution/name-matcher.js.map +1 -1
- package/lib/dist/resolution/types.d.ts +27 -3
- package/lib/dist/resolution/types.d.ts.map +1 -1
- package/lib/dist/resolution/workspace-packages.d.ts +48 -0
- package/lib/dist/resolution/workspace-packages.d.ts.map +1 -0
- package/lib/dist/resolution/workspace-packages.js +208 -0
- package/lib/dist/resolution/workspace-packages.js.map +1 -0
- package/lib/dist/search/query-utils.d.ts +17 -1
- package/lib/dist/search/query-utils.d.ts.map +1 -1
- package/lib/dist/search/query-utils.js +79 -10
- package/lib/dist/search/query-utils.js.map +1 -1
- package/lib/dist/sync/watcher.d.ts +124 -32
- package/lib/dist/sync/watcher.d.ts.map +1 -1
- package/lib/dist/sync/watcher.js +326 -111
- package/lib/dist/sync/watcher.js.map +1 -1
- package/lib/dist/telemetry/index.d.ts +146 -0
- package/lib/dist/telemetry/index.d.ts.map +1 -0
- package/lib/dist/telemetry/index.js +544 -0
- package/lib/dist/telemetry/index.js.map +1 -0
- package/lib/dist/types.d.ts +17 -2
- package/lib/dist/types.d.ts.map +1 -1
- package/lib/dist/types.js +3 -0
- package/lib/dist/types.js.map +1 -1
- package/lib/dist/upgrade/index.d.ts +132 -0
- package/lib/dist/upgrade/index.d.ts.map +1 -0
- package/lib/dist/upgrade/index.js +462 -0
- package/lib/dist/upgrade/index.js.map +1 -0
- package/lib/dist/utils.d.ts +30 -24
- package/lib/dist/utils.d.ts.map +1 -1
- package/lib/dist/utils.js +64 -48
- package/lib/dist/utils.js.map +1 -1
- package/lib/node_modules/.package-lock.json +1 -29
- package/lib/package.json +1 -2
- package/package.json +1 -1
- package/lib/node_modules/chokidar/LICENSE +0 -21
- package/lib/node_modules/chokidar/README.md +0 -305
- package/lib/node_modules/chokidar/esm/handler.d.ts +0 -90
- package/lib/node_modules/chokidar/esm/handler.js +0 -629
- package/lib/node_modules/chokidar/esm/index.d.ts +0 -215
- package/lib/node_modules/chokidar/esm/index.js +0 -798
- package/lib/node_modules/chokidar/esm/package.json +0 -1
- package/lib/node_modules/chokidar/handler.d.ts +0 -90
- package/lib/node_modules/chokidar/handler.js +0 -635
- package/lib/node_modules/chokidar/index.d.ts +0 -215
- package/lib/node_modules/chokidar/index.js +0 -804
- package/lib/node_modules/chokidar/package.json +0 -69
- package/lib/node_modules/readdirp/LICENSE +0 -21
- package/lib/node_modules/readdirp/README.md +0 -120
- package/lib/node_modules/readdirp/esm/index.d.ts +0 -108
- package/lib/node_modules/readdirp/esm/index.js +0 -257
- package/lib/node_modules/readdirp/esm/package.json +0 -1
- package/lib/node_modules/readdirp/index.d.ts +0 -108
- package/lib/node_modules/readdirp/index.js +0 -263
- package/lib/node_modules/readdirp/package.json +0 -70
|
@@ -41,6 +41,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
41
41
|
exports.resolveImportPath = resolveImportPath;
|
|
42
42
|
exports.clearCppIncludeDirCache = clearCppIncludeDirCache;
|
|
43
43
|
exports.loadCppIncludeDirs = loadCppIncludeDirs;
|
|
44
|
+
exports.isPhpIncludePathRef = isPhpIncludePathRef;
|
|
44
45
|
exports.extractImportMappings = extractImportMappings;
|
|
45
46
|
exports.clearImportMappingCache = clearImportMappingCache;
|
|
46
47
|
exports.extractReExports = extractReExports;
|
|
@@ -49,6 +50,7 @@ exports.resolveViaImport = resolveViaImport;
|
|
|
49
50
|
const fs = __importStar(require("fs"));
|
|
50
51
|
const path = __importStar(require("path"));
|
|
51
52
|
const path_aliases_1 = require("./path-aliases");
|
|
53
|
+
const workspace_packages_1 = require("./workspace-packages");
|
|
52
54
|
/**
|
|
53
55
|
* Extension resolution order by language
|
|
54
56
|
*/
|
|
@@ -57,6 +59,12 @@ const EXTENSION_RESOLUTION = {
|
|
|
57
59
|
javascript: ['.js', '.jsx', '.mjs', '.cjs', '/index.js', '/index.jsx'],
|
|
58
60
|
tsx: ['.tsx', '.ts', '.d.ts', '.js', '.jsx', '/index.tsx', '/index.ts', '/index.js'],
|
|
59
61
|
jsx: ['.jsx', '.js', '/index.jsx', '/index.js'],
|
|
62
|
+
// SFC consumers import plain TS/JS, sibling components, and barrels
|
|
63
|
+
// (`./lib` → `./lib/index.ts`). Without a list, relative imports from a
|
|
64
|
+
// `.svelte`/`.vue` file resolve to nothing, so barrel callers vanish (#629).
|
|
65
|
+
svelte: ['.ts', '.js', '.svelte', '.tsx', '.jsx', '/index.ts', '/index.js', '/index.svelte'],
|
|
66
|
+
vue: ['.ts', '.js', '.vue', '.tsx', '.jsx', '/index.ts', '/index.js', '/index.vue'],
|
|
67
|
+
astro: ['.ts', '.js', '.astro', '.tsx', '.jsx', '/index.ts', '/index.js', '/index.astro'],
|
|
60
68
|
python: ['.py', '/__init__.py'],
|
|
61
69
|
go: ['.go'],
|
|
62
70
|
rust: ['.rs', '/mod.rs'],
|
|
@@ -146,6 +154,14 @@ function isExternalImport(importPath, language, context) {
|
|
|
146
154
|
if (importPath.startsWith('.')) {
|
|
147
155
|
return false;
|
|
148
156
|
}
|
|
157
|
+
// Workspace-member imports (`@scope/ui`, `@scope/ui/widgets`) are LOCAL to
|
|
158
|
+
// a monorepo even though they look like bare npm specifiers. Consult the
|
|
159
|
+
// workspace map first so they aren't misclassified as external (#629). The
|
|
160
|
+
// map is null for single-package repos, so this is a no-op there.
|
|
161
|
+
const workspaces = context?.getWorkspacePackages?.();
|
|
162
|
+
if (workspaces && (0, workspace_packages_1.resolveWorkspaceImport)(importPath, workspaces)) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
149
165
|
// Common external patterns
|
|
150
166
|
if (language === 'typescript' || language === 'javascript' || language === 'tsx' || language === 'jsx') {
|
|
151
167
|
// Node built-ins
|
|
@@ -212,6 +228,25 @@ function isExternalImport(importPath, language, context) {
|
|
|
212
228
|
function resolveRelativeImport(importPath, fromDir, language, context) {
|
|
213
229
|
const projectRoot = context.getProjectRoot();
|
|
214
230
|
const extensions = EXTENSION_RESOLUTION[language] || [];
|
|
231
|
+
// Python dotted-relative imports (`from .certs import x`, `from ..pkg.mod
|
|
232
|
+
// import y`): leading dots are PACKAGE levels (1 = current package), and the
|
|
233
|
+
// remainder is a dotted submodule path. `path.resolve(dir, '.certs')` would
|
|
234
|
+
// treat `.certs` as a literal hidden filename, so translate the Python form
|
|
235
|
+
// to a real filesystem-relative path before resolving.
|
|
236
|
+
if (language === 'python' && importPath.startsWith('.')) {
|
|
237
|
+
const dots = importPath.length - importPath.replace(/^\.+/, '').length;
|
|
238
|
+
const up = '../'.repeat(Math.max(0, dots - 1)); // 1 dot = current dir
|
|
239
|
+
const rest = importPath.slice(dots).replace(/\./g, '/'); // 'sub.mod' -> 'sub/mod'
|
|
240
|
+
const pyBase = path.resolve(fromDir, up + rest);
|
|
241
|
+
const pyRel = path.relative(projectRoot, pyBase).replace(/\\/g, '/');
|
|
242
|
+
for (const ext of extensions) {
|
|
243
|
+
if (context.fileExists(pyRel + ext))
|
|
244
|
+
return pyRel + ext;
|
|
245
|
+
}
|
|
246
|
+
if (pyRel && context.fileExists(pyRel))
|
|
247
|
+
return pyRel;
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
215
250
|
// Try the path as-is first
|
|
216
251
|
const basePath = path.resolve(fromDir, importPath);
|
|
217
252
|
const relativePath = path.relative(projectRoot, basePath).replace(/\\/g, '/');
|
|
@@ -261,6 +296,18 @@ function resolveAliasedImport(importPath, projectRoot, language, context) {
|
|
|
261
296
|
return hit;
|
|
262
297
|
}
|
|
263
298
|
}
|
|
299
|
+
// 1.5 Workspace packages (`@scope/ui/widgets` → `packages/ui/widgets`).
|
|
300
|
+
// Resolves a monorepo member import to the member's directory; the
|
|
301
|
+
// extension/index permutations below then find its barrel (#629).
|
|
302
|
+
const workspaces = context.getWorkspacePackages?.();
|
|
303
|
+
if (workspaces) {
|
|
304
|
+
const base = (0, workspace_packages_1.resolveWorkspaceImport)(importPath, workspaces);
|
|
305
|
+
if (base) {
|
|
306
|
+
const hit = tryWithExt(base);
|
|
307
|
+
if (hit)
|
|
308
|
+
return hit;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
264
311
|
// 2. Hard-coded fallback list. Kept for projects that use these
|
|
265
312
|
// conventional aliases without declaring them in tsconfig.
|
|
266
313
|
const fallbackAliases = {
|
|
@@ -490,6 +537,41 @@ function resolveCppIncludePath(importPath, language, context) {
|
|
|
490
537
|
}
|
|
491
538
|
return null;
|
|
492
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Is this reference a PHP include/require PATH (vs a namespace `use` symbol)?
|
|
542
|
+
*
|
|
543
|
+
* include/require emit a file path ("lib.php", "inc/db.php", "../x.php"),
|
|
544
|
+
* whereas namespace use is an FQN (App\Foo\Bar) or a bare class symbol
|
|
545
|
+
* (Closure). PHP identifiers contain neither '/' nor '.', so a slash or dot
|
|
546
|
+
* marks a path-shaped include. Such references resolve to files only — never
|
|
547
|
+
* to a same-named symbol — so callers must not fall back to the name-matcher.
|
|
548
|
+
*/
|
|
549
|
+
function isPhpIncludePathRef(ref) {
|
|
550
|
+
return (ref.language === 'php' &&
|
|
551
|
+
ref.referenceKind === 'imports' &&
|
|
552
|
+
(ref.referenceName.includes('/') || ref.referenceName.includes('.')));
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Resolve a PHP include/require path to a project-relative file path.
|
|
556
|
+
*
|
|
557
|
+
* PHP resolves includes relative to the including file's directory (the
|
|
558
|
+
* common case for procedural codebases); php.ini `include_path` is not
|
|
559
|
+
* modeled. Callers pass an already-extracted static literal path.
|
|
560
|
+
*/
|
|
561
|
+
function resolvePhpIncludePath(includePath, fromFile, context) {
|
|
562
|
+
const projectRoot = context.getProjectRoot();
|
|
563
|
+
const fromDir = path.dirname(path.join(projectRoot, fromFile));
|
|
564
|
+
const basePath = path.resolve(fromDir, includePath);
|
|
565
|
+
const relativePath = path.relative(projectRoot, basePath).replace(/\\/g, '/');
|
|
566
|
+
if (context.fileExists(relativePath))
|
|
567
|
+
return relativePath;
|
|
568
|
+
// The literal may omit the .php extension (e.g. include "config").
|
|
569
|
+
for (const ext of EXTENSION_RESOLUTION.php ?? []) {
|
|
570
|
+
if (context.fileExists(relativePath + ext))
|
|
571
|
+
return relativePath + ext;
|
|
572
|
+
}
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
493
575
|
/**
|
|
494
576
|
* Extract import mappings from a file
|
|
495
577
|
*/
|
|
@@ -498,6 +580,18 @@ function extractImportMappings(_filePath, content, language) {
|
|
|
498
580
|
if (language === 'typescript' || language === 'javascript' || language === 'tsx' || language === 'jsx') {
|
|
499
581
|
mappings.push(...extractJSImports(content));
|
|
500
582
|
}
|
|
583
|
+
else if (language === 'svelte' || language === 'vue' || language === 'astro') {
|
|
584
|
+
// Svelte/Vue single-file components import via plain ES6 inside their
|
|
585
|
+
// `<script>` block (Astro: the `---` frontmatter). Without this, a
|
|
586
|
+
// `.svelte`/`.vue`/`.astro` consumer produces
|
|
587
|
+
// zero import mappings, so `resolveViaImport` can't run and a barrel
|
|
588
|
+
// import (`import { Foo } from './lib'`) falls back to name-matching —
|
|
589
|
+
// which silently fails whenever the re-export alias differs from the
|
|
590
|
+
// component's real name, yielding a false 0 callers (#629). The ES6
|
|
591
|
+
// import regex only matches `import … from '…'`, so running it over the
|
|
592
|
+
// whole SFC (markup + styles included) is safe.
|
|
593
|
+
mappings.push(...extractJSImports(content));
|
|
594
|
+
}
|
|
501
595
|
else if (language === 'python') {
|
|
502
596
|
mappings.push(...extractPythonImports(content));
|
|
503
597
|
}
|
|
@@ -947,13 +1041,53 @@ function resolveJvmImport(ref, context) {
|
|
|
947
1041
|
const candidates = context.getNodesByQualifiedName(`${pkg}::${sym}`);
|
|
948
1042
|
if (candidates.length === 0)
|
|
949
1043
|
return null;
|
|
1044
|
+
// Kotlin Multiplatform: an `expect` declaration and its `actual`s share one
|
|
1045
|
+
// FQN across source sets (commonMain / androidMain / appleMain). Taking the
|
|
1046
|
+
// first candidate let a single platform `actual` absorb every common-side
|
|
1047
|
+
// import, so the `expect` (the canonical API a commonMain file imports)
|
|
1048
|
+
// looked unused. Prefer the candidate CLOSEST to the importing file by
|
|
1049
|
+
// directory proximity — a commonMain import resolves to the commonMain
|
|
1050
|
+
// declaration — with the `expect` side as a tiebreak.
|
|
1051
|
+
const best = candidates.length === 1 ? candidates[0] : pickClosestJvmCandidate(candidates, ref.filePath);
|
|
950
1052
|
return {
|
|
951
1053
|
original: ref,
|
|
952
|
-
targetNodeId:
|
|
1054
|
+
targetNodeId: best.id,
|
|
953
1055
|
confidence: 0.95,
|
|
954
1056
|
resolvedBy: 'import',
|
|
955
1057
|
};
|
|
956
1058
|
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Pick the same-FQN candidate closest to `fromPath` by shared directory
|
|
1061
|
+
* prefix, preferring an `expect` declaration on a tie. Used to keep a Kotlin
|
|
1062
|
+
* Multiplatform `expect`/`actual` import resolving within the importer's own
|
|
1063
|
+
* source set instead of an arbitrary platform `actual`.
|
|
1064
|
+
*/
|
|
1065
|
+
function pickClosestJvmCandidate(candidates, fromPath) {
|
|
1066
|
+
const fromDirs = fromPath.split('/').slice(0, -1);
|
|
1067
|
+
const sharedPrefix = (p) => {
|
|
1068
|
+
const d = p.split('/').slice(0, -1);
|
|
1069
|
+
let shared = 0;
|
|
1070
|
+
for (let i = 0; i < Math.min(fromDirs.length, d.length); i++) {
|
|
1071
|
+
if (fromDirs[i] === d[i])
|
|
1072
|
+
shared++;
|
|
1073
|
+
else
|
|
1074
|
+
break;
|
|
1075
|
+
}
|
|
1076
|
+
return shared;
|
|
1077
|
+
};
|
|
1078
|
+
const isExpect = (n) => Array.isArray(n.decorators) && n.decorators.includes('expect');
|
|
1079
|
+
let best = candidates[0];
|
|
1080
|
+
let bestProx = sharedPrefix(best.filePath);
|
|
1081
|
+
for (let i = 1; i < candidates.length; i++) {
|
|
1082
|
+
const c = candidates[i];
|
|
1083
|
+
const prox = sharedPrefix(c.filePath);
|
|
1084
|
+
if (prox > bestProx || (prox === bestProx && isExpect(c) && !isExpect(best))) {
|
|
1085
|
+
best = c;
|
|
1086
|
+
bestProx = prox;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
return best;
|
|
1090
|
+
}
|
|
957
1091
|
function resolveViaImport(ref, context) {
|
|
958
1092
|
// C/C++ #include references — resolve directly to the included file
|
|
959
1093
|
// (file→file edge), bypassing symbol lookup. The extractor emits these
|
|
@@ -963,6 +1097,23 @@ function resolveViaImport(ref, context) {
|
|
|
963
1097
|
// edge — resolveViaImport's symbol lookup below would search the
|
|
964
1098
|
// resolved file for a symbol named like the file extension and fail.
|
|
965
1099
|
if ((ref.language === 'c' || ref.language === 'cpp') && ref.referenceKind === 'imports') {
|
|
1100
|
+
// C/C++ quoted includes (`#include "X.h"`) resolve relative to the
|
|
1101
|
+
// INCLUDING file's own directory first (the C standard's quoted-include
|
|
1102
|
+
// search order). Prefer a same-directory header over an -I directory or a
|
|
1103
|
+
// same-named header on another platform (windows/code/RNCAsyncStorage.h vs
|
|
1104
|
+
// apple/.../RNCAsyncStorage.h) — the include-dir heuristic below would
|
|
1105
|
+
// otherwise pick an arbitrary same-named header, leaving the real local one
|
|
1106
|
+
// with no dependents.
|
|
1107
|
+
const slash = ref.filePath.lastIndexOf('/');
|
|
1108
|
+
const fromDir = slash >= 0 ? ref.filePath.slice(0, slash) : '';
|
|
1109
|
+
const siblingPath = path.posix.normalize(fromDir ? `${fromDir}/${ref.referenceName}` : ref.referenceName);
|
|
1110
|
+
const siblingBase = siblingPath.split('/').pop();
|
|
1111
|
+
const sibling = context
|
|
1112
|
+
.getNodesByName(siblingBase)
|
|
1113
|
+
.find((n) => n.kind === 'file' && n.filePath === siblingPath);
|
|
1114
|
+
if (sibling) {
|
|
1115
|
+
return { original: ref, targetNodeId: sibling.id, confidence: 0.92, resolvedBy: 'import' };
|
|
1116
|
+
}
|
|
966
1117
|
const resolvedPath = resolveImportPath(ref.referenceName, ref.filePath, ref.language, context);
|
|
967
1118
|
if (!resolvedPath)
|
|
968
1119
|
return null;
|
|
@@ -979,6 +1130,35 @@ function resolveViaImport(ref, context) {
|
|
|
979
1130
|
}
|
|
980
1131
|
return null;
|
|
981
1132
|
}
|
|
1133
|
+
// PHP include/require — resolve the static string path to a file→file
|
|
1134
|
+
// edge, mirroring the C/C++ branch above. Distinguish include PATHS from
|
|
1135
|
+
// namespace `use` symbols by shape: an include path contains a slash or a
|
|
1136
|
+
// file extension ("lib.php", "inc/db.php", "../x.php"), whereas a namespace
|
|
1137
|
+
// use is an FQN (App\Foo\Bar) or a bare class symbol (Closure) — PHP
|
|
1138
|
+
// identifiers contain neither '/' nor '.'. Only path-shaped references are
|
|
1139
|
+
// includes; symbol references fall through to the namespace resolution.
|
|
1140
|
+
if (isPhpIncludePathRef(ref)) {
|
|
1141
|
+
const resolvedPath = resolvePhpIncludePath(ref.referenceName, ref.filePath, context);
|
|
1142
|
+
if (resolvedPath) {
|
|
1143
|
+
const basename = resolvedPath.split('/').pop();
|
|
1144
|
+
const fileNode = context
|
|
1145
|
+
.getNodesByName(basename)
|
|
1146
|
+
.find((n) => n.kind === 'file' && n.filePath === resolvedPath);
|
|
1147
|
+
if (fileNode) {
|
|
1148
|
+
return {
|
|
1149
|
+
original: ref,
|
|
1150
|
+
targetNodeId: fileNode.id,
|
|
1151
|
+
confidence: 0.9,
|
|
1152
|
+
resolvedBy: 'import',
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
// A path-shaped include that doesn't resolve to a known project file is a
|
|
1157
|
+
// dead end. Return unresolved rather than falling through to the symbol
|
|
1158
|
+
// name-matcher, which would mis-connect e.g. "inc/db.php" to an unrelated
|
|
1159
|
+
// db.php elsewhere in the tree — a wrong edge is worse than a missing one.
|
|
1160
|
+
return null;
|
|
1161
|
+
}
|
|
982
1162
|
// Use cached import mappings (avoids re-reading and re-parsing per ref)
|
|
983
1163
|
const imports = context.getImportMappings(ref.filePath, ref.language);
|
|
984
1164
|
if (imports.length === 0 && !context.readFile(ref.filePath)) {
|
|
@@ -1004,6 +1184,54 @@ function resolveViaImport(ref, context) {
|
|
|
1004
1184
|
if (javaResult)
|
|
1005
1185
|
return javaResult;
|
|
1006
1186
|
}
|
|
1187
|
+
// Python qualified access through an imported MODULE: `certs.where()` after
|
|
1188
|
+
// `from . import certs`, `mod.func()` after `import mod`. The receiver names a
|
|
1189
|
+
// submodule (a file), not a symbol, so the generic symbol lookup below would
|
|
1190
|
+
// search the *package* for `certs` instead of looking inside the module.
|
|
1191
|
+
if (ref.language === 'python') {
|
|
1192
|
+
const pyResult = resolvePythonModuleMember(ref, imports, context);
|
|
1193
|
+
if (pyResult)
|
|
1194
|
+
return pyResult;
|
|
1195
|
+
// Absolute dotted module import: `import conduit.apps.articles.signals`
|
|
1196
|
+
// (the standard Django AppConfig.ready() signal-registration pattern, and
|
|
1197
|
+
// any side-effect `import pkg.mod`). Map the dotted path to its file.
|
|
1198
|
+
const pyModResult = resolvePythonAbsoluteModule(ref, context);
|
|
1199
|
+
if (pyModResult)
|
|
1200
|
+
return pyModResult;
|
|
1201
|
+
}
|
|
1202
|
+
// Rust qualified path: resolve the module prefix of `crate::m::Item` /
|
|
1203
|
+
// `self::sub::Item` / `super::m::func` to a file, then find the leaf symbol in
|
|
1204
|
+
// it. Disambiguates common-name `pub use self::read::read` re-exports that
|
|
1205
|
+
// name-matching would land on the wrong same-named symbol.
|
|
1206
|
+
if (ref.language === 'rust' && ref.referenceName.includes('::')) {
|
|
1207
|
+
const rustResult = resolveRustPathReference(ref, context);
|
|
1208
|
+
if (rustResult)
|
|
1209
|
+
return rustResult;
|
|
1210
|
+
}
|
|
1211
|
+
// Lua / Luau `require(...)`: a dotted module path (`a.b.c` from
|
|
1212
|
+
// `require("a.b.c")`) or an instance-path leaf (`Signal` from
|
|
1213
|
+
// `require(script.Parent.Signal)`) — map it to a module file. There's no static
|
|
1214
|
+
// import statement, so the generic path-matcher can't bridge the dot↔slash /
|
|
1215
|
+
// leaf↔basename gap; resolve it explicitly to the module file.
|
|
1216
|
+
if ((ref.language === 'lua' || ref.language === 'luau') && ref.referenceKind === 'imports') {
|
|
1217
|
+
const luaResult = resolveLuaRequire(ref, context);
|
|
1218
|
+
if (luaResult)
|
|
1219
|
+
return luaResult;
|
|
1220
|
+
}
|
|
1221
|
+
// Whole-module / namespace imports → link the importing file to the module
|
|
1222
|
+
// file. Python `from . import certs` / `import mod`, and TS/JS `import * as ns
|
|
1223
|
+
// from './x'` (so a namespace touched only via a value-member read still
|
|
1224
|
+
// records the dependency). A named TS/JS import returns null here and falls
|
|
1225
|
+
// through to symbol resolution below.
|
|
1226
|
+
if (ref.language === 'python' ||
|
|
1227
|
+
ref.language === 'typescript' ||
|
|
1228
|
+
ref.language === 'tsx' ||
|
|
1229
|
+
ref.language === 'javascript' ||
|
|
1230
|
+
ref.language === 'jsx') {
|
|
1231
|
+
const moduleFile = resolveModuleImportToFile(ref, imports, context);
|
|
1232
|
+
if (moduleFile)
|
|
1233
|
+
return moduleFile;
|
|
1234
|
+
}
|
|
1007
1235
|
// Check if the reference name matches any import
|
|
1008
1236
|
for (const imp of imports) {
|
|
1009
1237
|
if (imp.localName === ref.referenceName || ref.referenceName.startsWith(imp.localName + '.')) {
|
|
@@ -1028,6 +1256,333 @@ function resolveViaImport(ref, context) {
|
|
|
1028
1256
|
}
|
|
1029
1257
|
return null;
|
|
1030
1258
|
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Resolve a Python qualified reference whose receiver is an imported MODULE:
|
|
1261
|
+
* `certs.where()` after `from . import certs`, `mod.func()` after `import mod`
|
|
1262
|
+
* or `from pkg import mod`. The receiver names a submodule (a file), not a
|
|
1263
|
+
* symbol, so the generic symbol lookup in `resolveViaImport` can't follow it —
|
|
1264
|
+
* it would search the *package* for `certs`/`mod` instead of looking inside the
|
|
1265
|
+
* module. This is the Python half of the cross-package qualified-call problem
|
|
1266
|
+
* (cf. `resolveGoCrossPackageReference` for Go's `pkg.Func`, issue #388).
|
|
1267
|
+
*
|
|
1268
|
+
* Builds the module's dotted import path from the binding — `from . import
|
|
1269
|
+
* certs` → `.certs`; `from pkg import mod` → `pkg.mod`; `import mod` → `mod` —
|
|
1270
|
+
* resolves it to the module file, and finds the member defined there. Returns
|
|
1271
|
+
* null when no module file exists at that path, so attribute access on an
|
|
1272
|
+
* imported *value* (`helper.attr` where `helper` is a function) falls through
|
|
1273
|
+
* to the other strategies untouched.
|
|
1274
|
+
*/
|
|
1275
|
+
function resolvePythonModuleMember(ref, imports, context) {
|
|
1276
|
+
const dotIdx = ref.referenceName.indexOf('.');
|
|
1277
|
+
if (dotIdx <= 0)
|
|
1278
|
+
return null;
|
|
1279
|
+
const receiver = ref.referenceName.substring(0, dotIdx);
|
|
1280
|
+
// The immediate member of the module (first segment after the receiver).
|
|
1281
|
+
const member = ref.referenceName.substring(dotIdx + 1).split('.')[0];
|
|
1282
|
+
if (!member)
|
|
1283
|
+
return null;
|
|
1284
|
+
for (const imp of imports) {
|
|
1285
|
+
if (imp.localName !== receiver)
|
|
1286
|
+
continue;
|
|
1287
|
+
// `import mod` / `import numpy as np` bind the module at `source` itself;
|
|
1288
|
+
// `from . import certs` / `from pkg import mod` bind a SUBMODULE whose
|
|
1289
|
+
// dotted path is the source joined with the imported name.
|
|
1290
|
+
const modulePath = imp.isNamespace
|
|
1291
|
+
? imp.source
|
|
1292
|
+
: imp.source.endsWith('.')
|
|
1293
|
+
? imp.source + imp.localName
|
|
1294
|
+
: imp.source + '.' + imp.localName;
|
|
1295
|
+
// resolveImportPath only maps RELATIVE dotted paths (`.mod`, `..pkg.mod`); an
|
|
1296
|
+
// ABSOLUTE package path (`pkg.module` from `from pkg import module`, or a bare
|
|
1297
|
+
// `import pkg.mod`) resolves to null there, so fall back to the dotted-module
|
|
1298
|
+
// file lookup — the same asymmetry resolveModuleImportToFile already handles
|
|
1299
|
+
// for the file→file import edge. Without this, a `module.func()` call after
|
|
1300
|
+
// `from pkg import module` dropped its `calls` edge even though the import
|
|
1301
|
+
// edge resolved (#578).
|
|
1302
|
+
let resolvedPath = resolveImportPath(modulePath, ref.filePath, ref.language, context);
|
|
1303
|
+
if (!resolvedPath) {
|
|
1304
|
+
resolvedPath = findPythonModuleFile(modulePath, context, ref.filePath)?.filePath ?? null;
|
|
1305
|
+
}
|
|
1306
|
+
if (!resolvedPath || resolvedPath === ref.filePath)
|
|
1307
|
+
continue;
|
|
1308
|
+
// Find the member as a top-level definition in the module file. Exclude
|
|
1309
|
+
// `method` so `mod.foo` never lands on a same-named class method.
|
|
1310
|
+
const target = context.getNodesInFile(resolvedPath).find((n) => n.name === member &&
|
|
1311
|
+
(n.kind === 'function' ||
|
|
1312
|
+
n.kind === 'class' ||
|
|
1313
|
+
n.kind === 'variable' ||
|
|
1314
|
+
n.kind === 'constant'));
|
|
1315
|
+
if (target) {
|
|
1316
|
+
return { original: ref, targetNodeId: target.id, confidence: 0.85, resolvedBy: 'import' };
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return null;
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Resolve a whole-MODULE import to that module's file (a file→file dependency).
|
|
1323
|
+
* The imported name is a module, not a symbol, so there's nothing to resolve to
|
|
1324
|
+
* — but importing a module IS a dependency on it. Covers:
|
|
1325
|
+
* - Python submodule imports — `from . import certs`, `from pkg import sub`;
|
|
1326
|
+
* - namespace imports — Python `import mod` / `import numpy as np`, and
|
|
1327
|
+
* TS/JS `import * as ns from './x'`.
|
|
1328
|
+
*
|
|
1329
|
+
* It is also the robust backstop for {@link resolvePythonModuleMember} and for
|
|
1330
|
+
* TS namespace usage: it records the dependency even when the used member is
|
|
1331
|
+
* re-exported elsewhere (requests' `certs.where`, re-exported from `certifi`),
|
|
1332
|
+
* the usage is module-level code that isn't extracted as a call, or a TS
|
|
1333
|
+
* namespace is touched only via a value-member read (`ns.SOME_CONST`).
|
|
1334
|
+
*
|
|
1335
|
+
* Only fires for dot-free `imports`-kind refs whose module path resolves to a
|
|
1336
|
+
* real file. A NAMED TS/JS import (`import { widget }`) is not a module, so it
|
|
1337
|
+
* returns null and normal symbol resolution handles it.
|
|
1338
|
+
*/
|
|
1339
|
+
/**
|
|
1340
|
+
* Resolve a Lua/Luau `require(...)` to its module file. The reference name is
|
|
1341
|
+
* either a dotted module path (`telescope.config` → `telescope/config.lua`) or a
|
|
1342
|
+
* Roblox instance-path leaf (`Signal` from `require(script.Parent.Signal)` →
|
|
1343
|
+
* `Signal.luau`). We try `<path>.lua|.luau` and `<path>/init.lua|.luau`, matched
|
|
1344
|
+
* by path suffix (the module root — `lua/`, `src/`, … — is project-specific).
|
|
1345
|
+
* Among suffix matches, the one sharing the longest directory prefix with the
|
|
1346
|
+
* requiring file wins (instance-path requires resolve within the same package).
|
|
1347
|
+
*/
|
|
1348
|
+
function resolveLuaRequire(ref, context) {
|
|
1349
|
+
const name = ref.referenceName;
|
|
1350
|
+
if (!name)
|
|
1351
|
+
return null;
|
|
1352
|
+
const base = name.includes('.') ? name.replace(/\./g, '/') : name;
|
|
1353
|
+
const suffixes = [`${base}.lua`, `${base}.luau`, `${base}/init.lua`, `${base}/init.luau`];
|
|
1354
|
+
const files = context.getAllFiles();
|
|
1355
|
+
const shared = (a, b) => {
|
|
1356
|
+
let i = 0;
|
|
1357
|
+
while (i < a.length && i < b.length && a[i] === b[i])
|
|
1358
|
+
i++;
|
|
1359
|
+
return i;
|
|
1360
|
+
};
|
|
1361
|
+
for (const suffix of suffixes) {
|
|
1362
|
+
const matches = files.filter((f) => f === suffix || f.endsWith('/' + suffix));
|
|
1363
|
+
if (matches.length === 0)
|
|
1364
|
+
continue;
|
|
1365
|
+
matches.sort((x, y) => shared(y, ref.filePath) - shared(x, ref.filePath));
|
|
1366
|
+
const best = matches[0];
|
|
1367
|
+
if (best === ref.filePath)
|
|
1368
|
+
continue;
|
|
1369
|
+
const fileNode = context.getNodesInFile(best).find((n) => n.kind === 'file');
|
|
1370
|
+
if (fileNode) {
|
|
1371
|
+
// Confidence ≥ 0.9 so this deterministic path/suffix match wins over
|
|
1372
|
+
// name-matching, which otherwise resolves the require to the import node
|
|
1373
|
+
// itself (a same-name self-match).
|
|
1374
|
+
return { original: ref, targetNodeId: fileNode.id, confidence: 0.9, resolvedBy: 'import' };
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
return null;
|
|
1378
|
+
}
|
|
1379
|
+
function resolveModuleImportToFile(ref, imports, context) {
|
|
1380
|
+
if (ref.referenceKind !== 'imports')
|
|
1381
|
+
return null;
|
|
1382
|
+
if (ref.referenceName.includes('.'))
|
|
1383
|
+
return null;
|
|
1384
|
+
for (const imp of imports) {
|
|
1385
|
+
if (imp.localName !== ref.referenceName)
|
|
1386
|
+
continue;
|
|
1387
|
+
let modulePath;
|
|
1388
|
+
if (imp.isNamespace || imp.isDefault) {
|
|
1389
|
+
// `import * as ns from './x'` (namespace) or `import x from './x'`
|
|
1390
|
+
// (default) — the dependency is on the MODULE FILE. A default import binds
|
|
1391
|
+
// a (possibly renamed) local to whatever the module's default export is
|
|
1392
|
+
// (`import articlesController from './article.controller'` ← `export
|
|
1393
|
+
// default router`), so the binding name can't be found as a symbol — link
|
|
1394
|
+
// the file the import resolves to instead. External modules don't resolve
|
|
1395
|
+
// (no file), so `import React from 'react'` creates no edge.
|
|
1396
|
+
modulePath = imp.source;
|
|
1397
|
+
}
|
|
1398
|
+
else if (ref.language === 'python') {
|
|
1399
|
+
// `from . import certs` — the imported NAME is a submodule of the source.
|
|
1400
|
+
modulePath = imp.source.endsWith('.')
|
|
1401
|
+
? imp.source + imp.localName
|
|
1402
|
+
: imp.source + '.' + imp.localName;
|
|
1403
|
+
}
|
|
1404
|
+
else {
|
|
1405
|
+
// A named TS/JS import binds a symbol, not a module — leave it alone.
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
const resolvedPath = resolveImportPath(modulePath, ref.filePath, ref.language, context);
|
|
1409
|
+
if (resolvedPath && resolvedPath !== ref.filePath) {
|
|
1410
|
+
const fileNode = context.getNodesInFile(resolvedPath).find((n) => n.kind === 'file');
|
|
1411
|
+
if (fileNode) {
|
|
1412
|
+
return { original: ref, targetNodeId: fileNode.id, confidence: 0.9, resolvedBy: 'import' };
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
// Python absolute `from a.b import submodule` (a FastAPI router aggregator's
|
|
1416
|
+
// `from app.api.routes import authentication`): resolveImportPath only maps
|
|
1417
|
+
// RELATIVE dotted paths to a file, so resolve the absolute dotted module
|
|
1418
|
+
// directly to its file node.
|
|
1419
|
+
if (ref.language === 'python') {
|
|
1420
|
+
const modFile = findPythonModuleFile(modulePath, context, ref.filePath);
|
|
1421
|
+
if (modFile) {
|
|
1422
|
+
return { original: ref, targetNodeId: modFile.id, confidence: 0.9, resolvedBy: 'import' };
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Find the file node for a Python dotted module path `a.b.c` — a module file
|
|
1430
|
+
* ending in `a/b/c.py`, or a package `a/b/c/__init__.py` (suffix-matched, so a
|
|
1431
|
+
* package rooted under `src/` etc. still resolves). Returns null for
|
|
1432
|
+
* stdlib/external modules (no matching repo file node), so `import os` creates
|
|
1433
|
+
* no edge. Shared by absolute `import a.b.c` and absolute `from a.b import c`
|
|
1434
|
+
* (where `c` is a submodule) resolution.
|
|
1435
|
+
*/
|
|
1436
|
+
function findPythonModuleFile(mod, context, excludeFilePath) {
|
|
1437
|
+
if (!mod || mod.startsWith('.'))
|
|
1438
|
+
return null; // relative imports handled elsewhere
|
|
1439
|
+
const rel = mod.replace(/\./g, '/');
|
|
1440
|
+
const lastSeg = mod.split('.').pop();
|
|
1441
|
+
const endsWith = (p, want) => p === want || p.endsWith('/' + want);
|
|
1442
|
+
const moduleFile = context
|
|
1443
|
+
.getNodesByName(`${lastSeg}.py`)
|
|
1444
|
+
.find((n) => n.kind === 'file' && n.filePath !== excludeFilePath && endsWith(n.filePath, `${rel}.py`));
|
|
1445
|
+
if (moduleFile)
|
|
1446
|
+
return moduleFile;
|
|
1447
|
+
const pkgFile = context
|
|
1448
|
+
.getNodesByName('__init__.py')
|
|
1449
|
+
.find((n) => n.kind === 'file' && n.filePath !== excludeFilePath && endsWith(n.filePath, `${rel}/__init__.py`));
|
|
1450
|
+
return pkgFile ?? null;
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Resolve a Python ABSOLUTE dotted module import (`import a.b.c`) to its file —
|
|
1454
|
+
* the Django `AppConfig.ready(): import myapp.signals` pattern and any
|
|
1455
|
+
* side-effect module import.
|
|
1456
|
+
*/
|
|
1457
|
+
function resolvePythonAbsoluteModule(ref, context) {
|
|
1458
|
+
if (ref.referenceKind !== 'imports')
|
|
1459
|
+
return null;
|
|
1460
|
+
// Only a DOTTED `import a.b.c` ref carries its full module path. A bare leaf
|
|
1461
|
+
// (`from app.api.routes import authentication`) is ambiguous on its own — three
|
|
1462
|
+
// `authentication.py` files may exist — so leave it to resolveModuleImportToFile,
|
|
1463
|
+
// which uses the import's source (`app.api.routes`) to build the full path.
|
|
1464
|
+
if (!ref.referenceName.includes('.'))
|
|
1465
|
+
return null;
|
|
1466
|
+
const hit = findPythonModuleFile(ref.referenceName, context, ref.filePath);
|
|
1467
|
+
return hit ? { original: ref, targetNodeId: hit.id, confidence: 0.9, resolvedBy: 'import' } : null;
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Resolve a Rust qualified reference `A::B::C` by mapping the MODULE prefix
|
|
1471
|
+
* (`A::B`) to a file and finding the leaf symbol (`C`) in it. This is the Rust
|
|
1472
|
+
* analog of {@link resolvePythonModuleMember} / {@link resolveGoCrossPackageReference}
|
|
1473
|
+
* and the precise answer to common-name re-exports (`pub use self::read::read`)
|
|
1474
|
+
* that name-matching can't disambiguate. Returns null when the prefix isn't a
|
|
1475
|
+
* real module path (e.g. `Widget::new` — `Widget` is a struct, not a module),
|
|
1476
|
+
* so associated-function calls and enum-variant paths fall through untouched.
|
|
1477
|
+
*/
|
|
1478
|
+
function resolveRustPathReference(ref, context) {
|
|
1479
|
+
const segments = ref.referenceName.split('::').filter((s) => s.length > 0);
|
|
1480
|
+
if (segments.length < 2)
|
|
1481
|
+
return null;
|
|
1482
|
+
const leaf = segments[segments.length - 1];
|
|
1483
|
+
const modSegs = segments.slice(0, -1);
|
|
1484
|
+
const file = resolveRustModuleFile(modSegs, ref.filePath, context);
|
|
1485
|
+
if (!file || file === ref.filePath)
|
|
1486
|
+
return null;
|
|
1487
|
+
const target = context.getNodesInFile(file).find((n) => n.name === leaf &&
|
|
1488
|
+
(n.kind === 'function' ||
|
|
1489
|
+
n.kind === 'struct' ||
|
|
1490
|
+
n.kind === 'enum' ||
|
|
1491
|
+
n.kind === 'trait' ||
|
|
1492
|
+
n.kind === 'type_alias' ||
|
|
1493
|
+
n.kind === 'constant' ||
|
|
1494
|
+
n.kind === 'method' ||
|
|
1495
|
+
n.kind === 'class' ||
|
|
1496
|
+
n.kind === 'interface'));
|
|
1497
|
+
if (target) {
|
|
1498
|
+
return { original: ref, targetNodeId: target.id, confidence: 0.9, resolvedBy: 'import' };
|
|
1499
|
+
}
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
/** The crate-root directory (holds `lib.rs`/`main.rs`), walking up from a file. */
|
|
1503
|
+
function rustCrateRootDir(fromFileAbs, context) {
|
|
1504
|
+
const projectRoot = context.getProjectRoot();
|
|
1505
|
+
const toRel = (p) => path.relative(projectRoot, p).replace(/\\/g, '/');
|
|
1506
|
+
let dir = path.dirname(fromFileAbs);
|
|
1507
|
+
for (let i = 0; i < 64; i++) {
|
|
1508
|
+
if (context.fileExists(toRel(path.join(dir, 'lib.rs'))) ||
|
|
1509
|
+
context.fileExists(toRel(path.join(dir, 'main.rs')))) {
|
|
1510
|
+
return dir;
|
|
1511
|
+
}
|
|
1512
|
+
const parent = path.dirname(dir);
|
|
1513
|
+
if (parent === dir)
|
|
1514
|
+
return null;
|
|
1515
|
+
dir = parent;
|
|
1516
|
+
}
|
|
1517
|
+
return null;
|
|
1518
|
+
}
|
|
1519
|
+
/** Directory under which the current file's module declares its SUBMODULES. */
|
|
1520
|
+
function rustSelfModuleDir(fromFileAbs) {
|
|
1521
|
+
const base = path.basename(fromFileAbs);
|
|
1522
|
+
const dir = path.dirname(fromFileAbs);
|
|
1523
|
+
// mod.rs / lib.rs / main.rs own their directory; `foo.rs`'s submodules live in `foo/`.
|
|
1524
|
+
if (base === 'mod.rs' || base === 'lib.rs' || base === 'main.rs')
|
|
1525
|
+
return dir;
|
|
1526
|
+
return path.join(dir, base.replace(/\.rs$/, ''));
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Resolve a Rust module path (segments WITHOUT the leaf symbol) to the file of
|
|
1530
|
+
* the last module segment — `crate::a::b` → `<crate>/a/b.rs` (or `.../b/mod.rs`).
|
|
1531
|
+
* Anchors on `crate` / `self` / `super`; a bare path is tried crate-relative.
|
|
1532
|
+
*/
|
|
1533
|
+
function resolveRustModuleFile(segments, fromFile, context) {
|
|
1534
|
+
if (segments.length === 0)
|
|
1535
|
+
return null;
|
|
1536
|
+
const projectRoot = context.getProjectRoot();
|
|
1537
|
+
const fromAbs = path.join(projectRoot, fromFile);
|
|
1538
|
+
const toRel = (p) => path.relative(projectRoot, p).replace(/\\/g, '/');
|
|
1539
|
+
// Walk a sequence of module segments down from `startDir`, mapping each to a
|
|
1540
|
+
// `<seg>.rs` or `<seg>/mod.rs` file. Returns the leaf module's file, or null
|
|
1541
|
+
// if `startDir` is null or any segment has no file on disk.
|
|
1542
|
+
const resolveUnder = (startDir, rest) => {
|
|
1543
|
+
if (!startDir)
|
|
1544
|
+
return null;
|
|
1545
|
+
let dir = startDir;
|
|
1546
|
+
let targetFile = null;
|
|
1547
|
+
for (const seg of rest) {
|
|
1548
|
+
if (seg === 'self' || seg === 'crate' || seg === 'super')
|
|
1549
|
+
continue;
|
|
1550
|
+
const asFile = toRel(path.join(dir, seg + '.rs'));
|
|
1551
|
+
const asMod = toRel(path.join(dir, seg, 'mod.rs'));
|
|
1552
|
+
if (context.fileExists(asFile))
|
|
1553
|
+
targetFile = asFile;
|
|
1554
|
+
else if (context.fileExists(asMod))
|
|
1555
|
+
targetFile = asMod;
|
|
1556
|
+
else
|
|
1557
|
+
return null;
|
|
1558
|
+
dir = path.join(dir, seg);
|
|
1559
|
+
}
|
|
1560
|
+
return targetFile;
|
|
1561
|
+
};
|
|
1562
|
+
const first = segments[0];
|
|
1563
|
+
if (first === 'crate') {
|
|
1564
|
+
return resolveUnder(rustCrateRootDir(fromAbs, context), segments.slice(1));
|
|
1565
|
+
}
|
|
1566
|
+
if (first === 'self') {
|
|
1567
|
+
return resolveUnder(rustSelfModuleDir(fromAbs), segments.slice(1));
|
|
1568
|
+
}
|
|
1569
|
+
if (first === 'super') {
|
|
1570
|
+
let supers = 0;
|
|
1571
|
+
while (segments[supers] === 'super')
|
|
1572
|
+
supers++;
|
|
1573
|
+
let dir = rustSelfModuleDir(fromAbs);
|
|
1574
|
+
for (let s = 0; s < supers && dir; s++)
|
|
1575
|
+
dir = path.dirname(dir);
|
|
1576
|
+
return resolveUnder(dir, segments.slice(supers));
|
|
1577
|
+
}
|
|
1578
|
+
// Bare path. In expression position (`submodule::item()` — the router-assembly
|
|
1579
|
+
// and general cross-module-call pattern) the prefix is a SUBMODULE of the
|
|
1580
|
+
// current module, i.e. 2018 `self::`-relative — so try self-relative FIRST.
|
|
1581
|
+
// Fall back to crate-relative for 2015-edition / crate-root items. External
|
|
1582
|
+
// crate paths (`serde::de::Error`) miss both and fall through to name-matching.
|
|
1583
|
+
return (resolveUnder(rustSelfModuleDir(fromAbs), segments) ??
|
|
1584
|
+
resolveUnder(rustCrateRootDir(fromAbs, context), segments));
|
|
1585
|
+
}
|
|
1031
1586
|
/**
|
|
1032
1587
|
* Resolve a Java/Kotlin reference whose receiver is the simple name of
|
|
1033
1588
|
* an imported FQN: `Foo.bar(...)` where `import com.example.Foo;`. The
|
|
@@ -1177,7 +1732,14 @@ function findExportedSymbol(filePath, want, language, context, visited, depth =
|
|
|
1177
1732
|
const nodesInFile = context.getNodesInFile(filePath);
|
|
1178
1733
|
// 1. Direct hit: the symbol is declared in this file.
|
|
1179
1734
|
if (want.isDefault) {
|
|
1180
|
-
|
|
1735
|
+
// Svelte/Vue single-file components ARE the module's default export,
|
|
1736
|
+
// but are extracted as kind 'component' (not function/class). Prefer
|
|
1737
|
+
// the component node; fall back to an exported function/class for the
|
|
1738
|
+
// `.ts`/`.tsx` `export default fn`/`class` case. Without the component
|
|
1739
|
+
// branch, an `export { default as X } from './X.svelte'` barrel never
|
|
1740
|
+
// resolves and the component shows a false 0 callers (#629).
|
|
1741
|
+
const direct = nodesInFile.find((n) => n.isExported && n.kind === 'component') ??
|
|
1742
|
+
nodesInFile.find((n) => n.isExported && (n.kind === 'function' || n.kind === 'class'));
|
|
1181
1743
|
if (direct)
|
|
1182
1744
|
return direct;
|
|
1183
1745
|
}
|