@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
|
@@ -17,6 +17,10 @@ const MAX_JSX_CHILDREN = 30;
|
|
|
17
17
|
// event bindings (@click="fn" / v-on:click="fn"). PascalCase children (<VPNav/>)
|
|
18
18
|
// are already caught by JSX_TAG_RE via the SFC component node.
|
|
19
19
|
const VUE_KEBAB_RE = /<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)[\s/>]/g;
|
|
20
|
+
// PascalCase component tags — `<MediaCard ...>`, `<NavBar/>`. HTML elements are
|
|
21
|
+
// lowercase, so an uppercase-initial tag is a component usage; built-ins
|
|
22
|
+
// (`<NuxtLink>`, `<Transition>`) simply resolve to nothing and emit no edge.
|
|
23
|
+
const VUE_PASCAL_RE = /<([A-Z][A-Za-z0-9]*)[\s/>]/g;
|
|
20
24
|
const VUE_HANDLER_RE = /(?:@|v-on:)([a-zA-Z][\w-]*)(?:\.[\w]+)*\s*=\s*"([^"]+)"/g;
|
|
21
25
|
// Composable/hook destructure: `const { close: closeSidebar } = useSidebarControl()`.
|
|
22
26
|
// Captures the destructure body + the called composable; only `use*` calls qualify.
|
|
@@ -36,6 +40,33 @@ const CC_FANOUT_CAP = 8; // skip a field name with more dispatchers/registrars t
|
|
|
36
40
|
function kebabToPascal(s) {
|
|
37
41
|
return s.split('-').map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
|
|
38
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Nuxt auto-import name for a component, derived from its path UNDER `components/`:
|
|
45
|
+
* `components/media/Card.vue` → `MediaCard`, `components/base/foo/Bar.vue` →
|
|
46
|
+
* `BaseFooBar`. Each directory segment and the filename is PascalCased and
|
|
47
|
+
* concatenated; a directory whose PascalCase name prefixes the next segment is
|
|
48
|
+
* collapsed (Nuxt's de-dup: `base/BaseButton.vue` → `BaseButton`, not
|
|
49
|
+
* `BaseBaseButton`). Returns null for a flat component (`components/NavBar.vue`)
|
|
50
|
+
* — its node is already named by basename, so a direct tag match finds it.
|
|
51
|
+
*/
|
|
52
|
+
function nuxtComponentName(filePath) {
|
|
53
|
+
const marker = filePath.lastIndexOf('components/');
|
|
54
|
+
if (marker === -1)
|
|
55
|
+
return null;
|
|
56
|
+
const rel = filePath.slice(marker + 'components/'.length).replace(/\.(vue|ts|tsx|js|jsx)$/i, '');
|
|
57
|
+
const segs = rel.split('/').filter(Boolean).map(kebabToPascal);
|
|
58
|
+
if (segs.length < 2)
|
|
59
|
+
return null;
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const s of segs) {
|
|
62
|
+
const prev = out[out.length - 1];
|
|
63
|
+
if (prev && s.startsWith(prev))
|
|
64
|
+
out[out.length - 1] = s;
|
|
65
|
+
else
|
|
66
|
+
out.push(s);
|
|
67
|
+
}
|
|
68
|
+
return out.join('');
|
|
69
|
+
}
|
|
39
70
|
function sliceLines(content, startLine, endLine) {
|
|
40
71
|
if (!startLine || !endLine)
|
|
41
72
|
return null;
|
|
@@ -69,12 +100,21 @@ function enclosingFn(nodesInFile, line) {
|
|
|
69
100
|
}
|
|
70
101
|
return best;
|
|
71
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Stream method + function nodes lazily. The synthesizers only scan-and-filter
|
|
105
|
+
* down to a tiny matched subset, so materializing every function/method (which
|
|
106
|
+
* is gigabytes on a symbol-dense project) just to iterate it once is what OOM'd
|
|
107
|
+
* #610. Iterating keeps memory O(1) in the node count.
|
|
108
|
+
*/
|
|
109
|
+
function* methodAndFunctionNodes(queries) {
|
|
110
|
+
yield* queries.iterateNodesByKind('method');
|
|
111
|
+
yield* queries.iterateNodesByKind('function');
|
|
112
|
+
}
|
|
72
113
|
/** Phase 1: field-backed observer channels (registrar/dispatcher share a store). */
|
|
73
114
|
function fieldChannelEdges(queries, ctx) {
|
|
74
|
-
const candidates = [...queries.getNodesByKind('method'), ...queries.getNodesByKind('function')];
|
|
75
115
|
const registrars = [];
|
|
76
116
|
const dispatchers = [];
|
|
77
|
-
for (const m of
|
|
117
|
+
for (const m of methodAndFunctionNodes(queries)) {
|
|
78
118
|
const isReg = REGISTRAR_NAME.test(m.name);
|
|
79
119
|
const isDisp = DISPATCHER_NAME.test(m.name);
|
|
80
120
|
if (!isReg && !isDisp)
|
|
@@ -156,7 +196,6 @@ function fieldChannelEdges(queries, ctx) {
|
|
|
156
196
|
* name shared across unrelated classes can't fan out into noise.
|
|
157
197
|
*/
|
|
158
198
|
function closureCollectionEdges(queries, ctx) {
|
|
159
|
-
const candidates = [...queries.getNodesByKind('method'), ...queries.getNodesByKind('function')];
|
|
160
199
|
const dispatchers = new Map(); // field → dispatcher methods + forEach line
|
|
161
200
|
const registrars = new Map(); // field → registrar methods + append line
|
|
162
201
|
const addReg = (field, node, absLine) => {
|
|
@@ -167,7 +206,7 @@ function closureCollectionEdges(queries, ctx) {
|
|
|
167
206
|
arr.push({ node, line: absLine });
|
|
168
207
|
registrars.set(field, arr);
|
|
169
208
|
};
|
|
170
|
-
for (const m of
|
|
209
|
+
for (const m of methodAndFunctionNodes(queries)) {
|
|
171
210
|
const content = ctx.readFile(m.filePath);
|
|
172
211
|
const src = content && sliceLines(content, m.startLine, m.endLine);
|
|
173
212
|
if (!src)
|
|
@@ -446,8 +485,210 @@ function cppOverrideEdges(queries) {
|
|
|
446
485
|
// and are added below; their concrete-side nodes can be a `struct` (Swift)
|
|
447
486
|
// or an `object` (Scala) so the loop also iterates those kinds.
|
|
448
487
|
const IFACE_OVERRIDE_LANGS = new Set([
|
|
449
|
-
'java', 'kotlin', 'csharp', 'typescript', 'javascript', 'swift', 'scala',
|
|
488
|
+
'java', 'kotlin', 'csharp', 'typescript', 'javascript', 'swift', 'scala', 'go', 'rust',
|
|
450
489
|
]);
|
|
490
|
+
/**
|
|
491
|
+
* Go implicit interface satisfaction (#584). Go has no `implements` keyword — a
|
|
492
|
+
* struct satisfies an interface structurally when its method set covers the
|
|
493
|
+
* interface's. Synthesize the missing `implements` edge (struct → interface) by
|
|
494
|
+
* matching method-NAME sets, so impl-navigation works and the interface-dispatch
|
|
495
|
+
* bridge ({@link interfaceOverrideEdges}, now 'go'-enabled) can link an interface
|
|
496
|
+
* method call to the concrete overrides.
|
|
497
|
+
*
|
|
498
|
+
* Name-only matching (signatures ignored) — over-approximation accepted, in line
|
|
499
|
+
* with the other dispatch synthesizers; capped per interface. Empty interfaces
|
|
500
|
+
* (`any`) are skipped so they don't match every struct.
|
|
501
|
+
*/
|
|
502
|
+
function goImplementsEdges(queries) {
|
|
503
|
+
const edges = [];
|
|
504
|
+
const seen = new Set();
|
|
505
|
+
const methodNameSet = (id) => new Set(queries
|
|
506
|
+
.getOutgoingEdges(id, ['contains'])
|
|
507
|
+
.map((e) => queries.getNodeById(e.target))
|
|
508
|
+
.filter((n) => !!n && n.kind === 'method')
|
|
509
|
+
.map((n) => n.name));
|
|
510
|
+
const goStructs = queries.getNodesByKind('struct').filter((s) => s.language === 'go');
|
|
511
|
+
const structMethods = new Map();
|
|
512
|
+
for (const s of goStructs)
|
|
513
|
+
structMethods.set(s.id, methodNameSet(s.id));
|
|
514
|
+
for (const iface of queries.getNodesByKind('interface')) {
|
|
515
|
+
if (iface.language !== 'go')
|
|
516
|
+
continue;
|
|
517
|
+
const want = methodNameSet(iface.id);
|
|
518
|
+
if (want.size === 0)
|
|
519
|
+
continue; // empty interface (`any`) — would match everything
|
|
520
|
+
let added = 0;
|
|
521
|
+
for (const s of goStructs) {
|
|
522
|
+
if (added >= MAX_CALLBACKS_PER_CHANNEL)
|
|
523
|
+
break;
|
|
524
|
+
const have = structMethods.get(s.id);
|
|
525
|
+
if (!have || have.size < want.size)
|
|
526
|
+
continue;
|
|
527
|
+
let all = true;
|
|
528
|
+
for (const m of want) {
|
|
529
|
+
if (!have.has(m)) {
|
|
530
|
+
all = false;
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (!all)
|
|
535
|
+
continue;
|
|
536
|
+
const key = `${s.id}>${iface.id}`;
|
|
537
|
+
if (seen.has(key))
|
|
538
|
+
continue;
|
|
539
|
+
seen.add(key);
|
|
540
|
+
edges.push({
|
|
541
|
+
source: s.id,
|
|
542
|
+
target: iface.id,
|
|
543
|
+
kind: 'implements',
|
|
544
|
+
line: s.startLine,
|
|
545
|
+
provenance: 'heuristic',
|
|
546
|
+
metadata: { synthesizedBy: 'go-implements', via: iface.name, registeredAt: `${s.filePath}:${s.startLine}` },
|
|
547
|
+
});
|
|
548
|
+
added++;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return edges;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Cross-file Go method → receiver-type `contains` edges. In Go a type's methods
|
|
555
|
+
* are commonly declared in a different file from the `type` declaration itself
|
|
556
|
+
* (`type User struct{…}` in `user.go`, `func (u *User) Save()` in
|
|
557
|
+
* `user_store.go`). Extraction attaches the struct→method `contains` edge only
|
|
558
|
+
* when the receiver type is in the SAME file — the owner lookup in
|
|
559
|
+
* `tree-sitter.ts` is scoped to the file being parsed — so a cross-file method
|
|
560
|
+
* is left orphaned from its type (it's still `contains`ed by its file, just not
|
|
561
|
+
* its struct). That breaks `codegraph_node` member outlines, any
|
|
562
|
+
* callers/callees/impact traversal that goes through the type's `contains`
|
|
563
|
+
* edges, and the {@link goImplementsEdges} method-set computation (which derives
|
|
564
|
+
* a struct's method set from those same edges, so it under-counts interfaces a
|
|
565
|
+
* cross-file struct satisfies).
|
|
566
|
+
*
|
|
567
|
+
* Go guarantees a method's receiver type is declared in the SAME PACKAGE as the
|
|
568
|
+
* method, and a Go package is a single directory — so this is a deterministic
|
|
569
|
+
* structural link, not a heuristic: find the same-named type in the method's own
|
|
570
|
+
* directory and add the missing `contains` edge (no `provenance: 'heuristic'`,
|
|
571
|
+
* matching the same-file edges extraction already emits). Skips methods that
|
|
572
|
+
* already have a type parent (the same-file case). (#583, cross-file half)
|
|
573
|
+
*/
|
|
574
|
+
function goCrossFileMethodContainsEdges(queries) {
|
|
575
|
+
const edges = [];
|
|
576
|
+
const seen = new Set();
|
|
577
|
+
const TYPE_KINDS = new Set(['struct', 'class', 'interface', 'enum', 'type_alias']);
|
|
578
|
+
const dirOf = (p) => {
|
|
579
|
+
const i = p.replace(/\\/g, '/').lastIndexOf('/');
|
|
580
|
+
return i >= 0 ? p.slice(0, i) : '';
|
|
581
|
+
};
|
|
582
|
+
for (const method of queries.getNodesByKind('method')) {
|
|
583
|
+
if (method.language !== 'go')
|
|
584
|
+
continue;
|
|
585
|
+
// The receiver type is encoded in the method's qualifiedName as `Recv::name`
|
|
586
|
+
// (extraction sets `${receiverType}::${name}` for receiver methods).
|
|
587
|
+
const qn = method.qualifiedName;
|
|
588
|
+
if (!qn)
|
|
589
|
+
continue;
|
|
590
|
+
const sep = qn.lastIndexOf('::');
|
|
591
|
+
if (sep <= 0)
|
|
592
|
+
continue;
|
|
593
|
+
const receiver = qn.slice(0, sep);
|
|
594
|
+
if (!receiver)
|
|
595
|
+
continue;
|
|
596
|
+
// Already attached to its type (same-file case handled at extraction)?
|
|
597
|
+
const hasTypeParent = queries
|
|
598
|
+
.getIncomingEdges(method.id, ['contains'])
|
|
599
|
+
.some((e) => {
|
|
600
|
+
const src = queries.getNodeById(e.source);
|
|
601
|
+
return src != null && TYPE_KINDS.has(src.kind);
|
|
602
|
+
});
|
|
603
|
+
if (hasTypeParent)
|
|
604
|
+
continue;
|
|
605
|
+
// Find the receiver type in the SAME directory (= same Go package). Go forbids
|
|
606
|
+
// duplicate type names within a package, so a same-name same-dir match is
|
|
607
|
+
// unambiguous; scoping to the directory avoids linking to a same-named type
|
|
608
|
+
// in another package.
|
|
609
|
+
const dir = dirOf(method.filePath);
|
|
610
|
+
const owner = queries
|
|
611
|
+
.getNodesByName(receiver)
|
|
612
|
+
.find((n) => n.language === 'go' && TYPE_KINDS.has(n.kind) && dirOf(n.filePath) === dir);
|
|
613
|
+
if (!owner)
|
|
614
|
+
continue;
|
|
615
|
+
const key = `${owner.id}>${method.id}`;
|
|
616
|
+
if (seen.has(key))
|
|
617
|
+
continue;
|
|
618
|
+
seen.add(key);
|
|
619
|
+
edges.push({ source: owner.id, target: method.id, kind: 'contains', line: method.startLine });
|
|
620
|
+
}
|
|
621
|
+
return edges;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Kotlin Multiplatform `expect`/`actual` linking. A `common` source set declares
|
|
625
|
+
* `expect fun foo()` / `expect class Bar`; each platform source set (jvm, native,
|
|
626
|
+
* js, …) provides an `actual` implementation with the IDENTICAL fully-qualified
|
|
627
|
+
* name in a different file. Callers in common code resolve to the `expect`
|
|
628
|
+
* declaration, so every `actual` impl ends up with zero dependents — invisible to
|
|
629
|
+
* impact/affected even though editing it can break every caller of the API.
|
|
630
|
+
*
|
|
631
|
+
* Synthesize a `calls` edge from the common declaration to each platform `actual`
|
|
632
|
+
* (mirroring the interface-impl bridge: abstract → concrete), so editing a
|
|
633
|
+
* platform impl surfaces the common `expect` and its callers, and the impl file
|
|
634
|
+
* participates in the graph.
|
|
635
|
+
*
|
|
636
|
+
* `expect`/`actual` are captured onto the node's `decorators` list at extraction
|
|
637
|
+
* (kotlin.ts `extractModifiers`). Members of an `expect class` are NOT themselves
|
|
638
|
+
* keyword-marked, so the declaration side is matched as the same-FQN, same-kind
|
|
639
|
+
* node that is NOT marked `actual`. Requiring an `actual`-marked counterpart also
|
|
640
|
+
* gates out plain cross-file overloads (neither side is marked).
|
|
641
|
+
*/
|
|
642
|
+
// Kinds that an `expect`/`actual` pair may legitimately straddle. `expect class`
|
|
643
|
+
// is routinely fulfilled by an `actual typealias` (e.g. `actual typealias
|
|
644
|
+
// CancellationException = …`, `actual typealias SchedulerTask = Task`), so a
|
|
645
|
+
// strict kind match would miss those one-line alias files. Same-FQN + the
|
|
646
|
+
// `actual` marker already gates out unrelated symbols, so widening to the
|
|
647
|
+
// type-like kinds is safe.
|
|
648
|
+
const KMP_TYPE_KINDS = new Set(['class', 'interface', 'struct', 'enum', 'type_alias']);
|
|
649
|
+
function kmpKindsCompatible(a, b) {
|
|
650
|
+
return a === b || (KMP_TYPE_KINDS.has(a) && KMP_TYPE_KINDS.has(b));
|
|
651
|
+
}
|
|
652
|
+
function kotlinExpectActualEdges(queries) {
|
|
653
|
+
const edges = [];
|
|
654
|
+
const seen = new Set();
|
|
655
|
+
const actuals = queries
|
|
656
|
+
.getAllNodes()
|
|
657
|
+
.filter((n) => n.language === 'kotlin' && !!n.decorators?.includes('actual'));
|
|
658
|
+
for (const act of actuals) {
|
|
659
|
+
let added = 0;
|
|
660
|
+
for (const cand of queries.getNodesByQualifiedNameExact(act.qualifiedName)) {
|
|
661
|
+
if (added >= MAX_CALLBACKS_PER_CHANNEL)
|
|
662
|
+
break;
|
|
663
|
+
// The declaration side: same FQN + compatible kind, a different file, NOT
|
|
664
|
+
// itself an `actual` (that would be a sibling platform impl, not the decl).
|
|
665
|
+
if (cand.language !== 'kotlin' || cand.id === act.id)
|
|
666
|
+
continue;
|
|
667
|
+
if (!kmpKindsCompatible(cand.kind, act.kind) || cand.filePath === act.filePath)
|
|
668
|
+
continue;
|
|
669
|
+
if (cand.decorators?.includes('actual'))
|
|
670
|
+
continue;
|
|
671
|
+
const key = `${cand.id}>${act.id}`;
|
|
672
|
+
if (seen.has(key))
|
|
673
|
+
continue;
|
|
674
|
+
seen.add(key);
|
|
675
|
+
edges.push({
|
|
676
|
+
source: cand.id,
|
|
677
|
+
target: act.id,
|
|
678
|
+
kind: 'calls',
|
|
679
|
+
line: cand.startLine,
|
|
680
|
+
provenance: 'heuristic',
|
|
681
|
+
metadata: {
|
|
682
|
+
synthesizedBy: 'kotlin-expect-actual',
|
|
683
|
+
via: act.name,
|
|
684
|
+
registeredAt: `${act.filePath}:${act.startLine}`,
|
|
685
|
+
},
|
|
686
|
+
});
|
|
687
|
+
added++;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return edges;
|
|
691
|
+
}
|
|
451
692
|
function interfaceOverrideEdges(queries) {
|
|
452
693
|
const edges = [];
|
|
453
694
|
const seen = new Set();
|
|
@@ -689,6 +930,17 @@ function vueTemplateEdges(ctx) {
|
|
|
689
930
|
// A composable's returned member may be a fn (`function close(){}`) or an
|
|
690
931
|
// arrow assigned to a const (`const close = () => {}`).
|
|
691
932
|
const RETURN_KINDS = new Set(['method', 'function', 'variable', 'constant']);
|
|
933
|
+
// Nuxt auto-imports nested components by a DIRECTORY-PREFIXED name —
|
|
934
|
+
// `components/media/Card.vue` is used as `<MediaCard/>`, not `<Card/>` — but
|
|
935
|
+
// the component node is named by basename (`Card`), so a direct tag match
|
|
936
|
+
// misses it (flat components match by basename and don't need this). Map each
|
|
937
|
+
// nested component's Nuxt name → node so those template usages resolve.
|
|
938
|
+
const nuxtComponents = new Map();
|
|
939
|
+
for (const c of ctx.getNodesByKind('component')) {
|
|
940
|
+
const nn = nuxtComponentName(c.filePath);
|
|
941
|
+
if (nn && !nuxtComponents.has(nn))
|
|
942
|
+
nuxtComponents.set(nn, c);
|
|
943
|
+
}
|
|
692
944
|
for (const file of ctx.getAllFiles()) {
|
|
693
945
|
if (!file.endsWith('.vue'))
|
|
694
946
|
continue;
|
|
@@ -734,8 +986,18 @@ function vueTemplateEdges(ctx) {
|
|
|
734
986
|
};
|
|
735
987
|
let m;
|
|
736
988
|
VUE_KEBAB_RE.lastIndex = 0;
|
|
737
|
-
while ((m = VUE_KEBAB_RE.exec(tpl)))
|
|
738
|
-
|
|
989
|
+
while ((m = VUE_KEBAB_RE.exec(tpl))) {
|
|
990
|
+
const tag = kebabToPascal(m[1]);
|
|
991
|
+
addEdge(resolve(tag, COMPONENT_KINDS) ?? nuxtComponents.get(tag), { synthesizedBy: 'jsx-render', via: m[1] });
|
|
992
|
+
}
|
|
993
|
+
// PascalCase component tags. Try a direct name match first (flat components
|
|
994
|
+
// and explicit registrations), then the Nuxt dir-prefixed auto-import name
|
|
995
|
+
// (`<MediaCard>` → components/media/Card.vue). Built-ins match neither → no edge.
|
|
996
|
+
VUE_PASCAL_RE.lastIndex = 0;
|
|
997
|
+
while ((m = VUE_PASCAL_RE.exec(tpl))) {
|
|
998
|
+
const tag = m[1];
|
|
999
|
+
addEdge(resolve(tag, COMPONENT_KINDS) ?? nuxtComponents.get(tag), { synthesizedBy: 'jsx-render', via: tag });
|
|
1000
|
+
}
|
|
739
1001
|
VUE_HANDLER_RE.lastIndex = 0;
|
|
740
1002
|
while ((m = VUE_HANDLER_RE.exec(tpl))) {
|
|
741
1003
|
const event = m[1];
|
|
@@ -805,6 +1067,14 @@ const RN_SWIFT_SEND_RE = /\bsendEvent\s*\(\s*withName\s*:\s*"([^"]+)"/g;
|
|
|
805
1067
|
// JVM source files in the consumer so we don't re-process JS emits
|
|
806
1068
|
// (which `eventEmitterEdges` already handles).
|
|
807
1069
|
const RN_JVM_EMIT_RE = /\.emit\s*\(\s*"([^"]+)"\s*,/g;
|
|
1070
|
+
// Custom `sendEvent(reactContext, "X", body)` wrapper — extremely common
|
|
1071
|
+
// (react-native-device-info and many libs wrap `DeviceEventManagerModule…emit`
|
|
1072
|
+
// behind a helper whose `.emit(eventName, …)` uses a VARIABLE, so RN_JVM_EMIT_RE
|
|
1073
|
+
// misses it; the literal lives in the wrapper CALL instead). Captures the first
|
|
1074
|
+
// string literal inside a `sendEvent(...)` call. `[^;{}]*?` keeps it on one
|
|
1075
|
+
// statement and stops at a block boundary, so the wrapper DEFINITION (whose `(`
|
|
1076
|
+
// is followed by `… ) {`) never matches. Multi-line tolerant. (java/kotlin/swift)
|
|
1077
|
+
const RN_NATIVE_SENDEVENT_RE = /\bsendEvent\s*\([^;{}]*?"([^"]+)"/g;
|
|
808
1078
|
function rnEventEdges(ctx) {
|
|
809
1079
|
// Native dispatchers (source = the native method whose body sends the
|
|
810
1080
|
// event) and JS handlers (target = the function/method registered as
|
|
@@ -843,17 +1113,28 @@ function rnEventEdges(ctx) {
|
|
|
843
1113
|
if (m[1])
|
|
844
1114
|
addDispatcher(m[1], lineOf(m.index));
|
|
845
1115
|
}
|
|
1116
|
+
RN_NATIVE_SENDEVENT_RE.lastIndex = 0;
|
|
1117
|
+
while ((m = RN_NATIVE_SENDEVENT_RE.exec(content))) {
|
|
1118
|
+
if (m[1])
|
|
1119
|
+
addDispatcher(m[1], lineOf(m.index));
|
|
1120
|
+
}
|
|
846
1121
|
}
|
|
847
|
-
// JVM side: `.emit("X", …)` in Java/Kotlin
|
|
848
|
-
//
|
|
849
|
-
//
|
|
1122
|
+
// JVM side: `.emit("X", …)` in Java/Kotlin, plus the common
|
|
1123
|
+
// `sendEvent(ctx, "X", body)` wrapper. (We pattern-match anywhere in the
|
|
1124
|
+
// file; the JS in-language path uses a separate emitter object pattern and
|
|
1125
|
+
// is already handled by eventEmitterEdges.)
|
|
850
1126
|
if (file.endsWith('.java') || file.endsWith('.kt')) {
|
|
851
|
-
RN_JVM_EMIT_RE.lastIndex = 0;
|
|
852
1127
|
let m;
|
|
1128
|
+
RN_JVM_EMIT_RE.lastIndex = 0;
|
|
853
1129
|
while ((m = RN_JVM_EMIT_RE.exec(content))) {
|
|
854
1130
|
if (m[1])
|
|
855
1131
|
addDispatcher(m[1], lineOf(m.index));
|
|
856
1132
|
}
|
|
1133
|
+
RN_NATIVE_SENDEVENT_RE.lastIndex = 0;
|
|
1134
|
+
while ((m = RN_NATIVE_SENDEVENT_RE.exec(content))) {
|
|
1135
|
+
if (m[1])
|
|
1136
|
+
addDispatcher(m[1], lineOf(m.index));
|
|
1137
|
+
}
|
|
857
1138
|
}
|
|
858
1139
|
// JS subscribers (.addListener("X", handler)). Restrict to JS-family
|
|
859
1140
|
// files so a native file's `addListener:` (the ObjC method) doesn't
|
|
@@ -977,6 +1258,142 @@ function rnEventEdges(ctx) {
|
|
|
977
1258
|
* conflict otherwise).
|
|
978
1259
|
*/
|
|
979
1260
|
const FABRIC_NATIVE_SUFFIXES = ['', 'View', 'ViewManager', 'ComponentView', 'Manager'];
|
|
1261
|
+
/**
|
|
1262
|
+
* Expo Modules cross-platform pairing. An Expo Module exposes the SAME
|
|
1263
|
+
* JS-visible method (`AsyncFunction("getBatteryLevelAsync")`) from BOTH an iOS
|
|
1264
|
+
* (Swift) and an Android (Kotlin) implementation. A JS callsite name-resolves to
|
|
1265
|
+
* only ONE of them, so the other platform's impl looked like nothing called it
|
|
1266
|
+
* (and editing it showed no blast radius). Link the iOS and Android impls of the
|
|
1267
|
+
* same `<module>.<method>` to each other (both directions), so a JS call that
|
|
1268
|
+
* reaches one platform reaches the other, and editing either surfaces the JS
|
|
1269
|
+
* caller. The Expo method nodes are id-prefixed `expo-module:` and qualified
|
|
1270
|
+
* `<file>::<module>.<method>` by the framework extractor.
|
|
1271
|
+
*/
|
|
1272
|
+
function expoCrossPlatformEdges(queries) {
|
|
1273
|
+
const edges = [];
|
|
1274
|
+
const seen = new Set();
|
|
1275
|
+
const byKey = new Map();
|
|
1276
|
+
for (const m of queries.getNodesByKind('method')) {
|
|
1277
|
+
if (!m.id.startsWith('expo-module:'))
|
|
1278
|
+
continue;
|
|
1279
|
+
const key = m.qualifiedName.split('::').pop(); // `<module>.<method>`
|
|
1280
|
+
if (!key)
|
|
1281
|
+
continue;
|
|
1282
|
+
const arr = byKey.get(key);
|
|
1283
|
+
if (arr)
|
|
1284
|
+
arr.push(m);
|
|
1285
|
+
else
|
|
1286
|
+
byKey.set(key, [m]);
|
|
1287
|
+
}
|
|
1288
|
+
for (const group of byKey.values()) {
|
|
1289
|
+
if (group.length < 2)
|
|
1290
|
+
continue;
|
|
1291
|
+
for (const a of group) {
|
|
1292
|
+
for (const b of group) {
|
|
1293
|
+
if (a.id === b.id || a.language === b.language)
|
|
1294
|
+
continue; // cross-platform only
|
|
1295
|
+
const key = `${a.id}>${b.id}`;
|
|
1296
|
+
if (seen.has(key))
|
|
1297
|
+
continue;
|
|
1298
|
+
seen.add(key);
|
|
1299
|
+
edges.push({
|
|
1300
|
+
source: a.id,
|
|
1301
|
+
target: b.id,
|
|
1302
|
+
kind: 'calls',
|
|
1303
|
+
line: a.startLine,
|
|
1304
|
+
provenance: 'heuristic',
|
|
1305
|
+
metadata: { synthesizedBy: 'expo-cross-platform', via: a.name },
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return edges;
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Classic React Native NativeModules cross-platform pairing. A native module
|
|
1314
|
+
* method (`@ReactMethod` on Android, `RCT_EXPORT_METHOD` on iOS) is implemented
|
|
1315
|
+
* on BOTH platforms, but a JS callsite name-resolves to only ONE — so the other
|
|
1316
|
+
* platform's impl looked like nothing called it. A native method that HAS a JS
|
|
1317
|
+
* caller is a confirmed bridge method; link it to the same-named native method
|
|
1318
|
+
* in another language (the other platform's impl) so a JS call reaching one
|
|
1319
|
+
* platform reaches the other, and editing either surfaces the JS caller.
|
|
1320
|
+
*
|
|
1321
|
+
* Names are normalized to the first selector keyword (`getFreeDiskStorage:` →
|
|
1322
|
+
* `getFreeDiskStorage`) — that's the JS-visible name, and how the iOS selector
|
|
1323
|
+
* lines up with the bare Android method name.
|
|
1324
|
+
*/
|
|
1325
|
+
function rnCrossPlatformEdges(queries) {
|
|
1326
|
+
const edges = [];
|
|
1327
|
+
const seen = new Set();
|
|
1328
|
+
const NATIVE = new Set(['java', 'kotlin', 'objc', 'cpp']);
|
|
1329
|
+
const JS = new Set(['typescript', 'tsx', 'javascript', 'jsx']);
|
|
1330
|
+
// RN module INFRASTRUCTURE methods exist on every native module (called by the
|
|
1331
|
+
// RN runtime, not user JS), so pairing them by name would cross-link unrelated
|
|
1332
|
+
// modules in a multi-module repo. Skip them — they aren't user-facing methods.
|
|
1333
|
+
const RN_INFRA = new Set([
|
|
1334
|
+
'addListener', 'removeListeners', 'getConstants', 'constantsToExport', 'getName',
|
|
1335
|
+
'invalidate', 'initialize', 'getDefaultEventTypes', 'supportedEvents',
|
|
1336
|
+
'requiresMainQueueSetup', 'methodQueue',
|
|
1337
|
+
]);
|
|
1338
|
+
const norm = (name) => {
|
|
1339
|
+
const i = name.indexOf(':');
|
|
1340
|
+
return i >= 0 ? name.slice(0, i) : name;
|
|
1341
|
+
};
|
|
1342
|
+
// Index native methods by their JS-visible (normalized) name. Only names with
|
|
1343
|
+
// impls in ≥2 native languages can pair, so the per-method JS-caller check
|
|
1344
|
+
// below only runs for genuine cross-platform candidates.
|
|
1345
|
+
const byName = new Map();
|
|
1346
|
+
for (const m of queries.iterateNodesByKind('method')) {
|
|
1347
|
+
if (!NATIVE.has(m.language))
|
|
1348
|
+
continue;
|
|
1349
|
+
const key = norm(m.name);
|
|
1350
|
+
const arr = byName.get(key);
|
|
1351
|
+
if (arr)
|
|
1352
|
+
arr.push(m);
|
|
1353
|
+
else
|
|
1354
|
+
byName.set(key, [m]);
|
|
1355
|
+
}
|
|
1356
|
+
for (const [groupName, group] of byName) {
|
|
1357
|
+
if (RN_INFRA.has(groupName))
|
|
1358
|
+
continue;
|
|
1359
|
+
const langs = new Set(group.map((m) => m.language));
|
|
1360
|
+
if (langs.size < 2)
|
|
1361
|
+
continue; // single-platform — nothing to pair
|
|
1362
|
+
for (const m of group) {
|
|
1363
|
+
// Is m a bridge method? (a JS-language `calls` edge points at it)
|
|
1364
|
+
const incoming = queries.getIncomingEdges(m.id, ['calls']);
|
|
1365
|
+
if (incoming.length === 0)
|
|
1366
|
+
continue;
|
|
1367
|
+
const sources = queries.getNodesByIds(incoming.map((e) => e.source));
|
|
1368
|
+
const isBridge = incoming.some((e) => {
|
|
1369
|
+
const s = sources.get(e.source);
|
|
1370
|
+
return !!s && JS.has(s.language);
|
|
1371
|
+
});
|
|
1372
|
+
if (!isBridge)
|
|
1373
|
+
continue;
|
|
1374
|
+
// Link to the other-platform impls (both directions).
|
|
1375
|
+
for (const sib of group) {
|
|
1376
|
+
if (sib.id === m.id || sib.language === m.language)
|
|
1377
|
+
continue;
|
|
1378
|
+
for (const [a, b] of [[m, sib], [sib, m]]) {
|
|
1379
|
+
const key = `${a.id}>${b.id}`;
|
|
1380
|
+
if (seen.has(key))
|
|
1381
|
+
continue;
|
|
1382
|
+
seen.add(key);
|
|
1383
|
+
edges.push({
|
|
1384
|
+
source: a.id,
|
|
1385
|
+
target: b.id,
|
|
1386
|
+
kind: 'calls',
|
|
1387
|
+
line: a.startLine,
|
|
1388
|
+
provenance: 'heuristic',
|
|
1389
|
+
metadata: { synthesizedBy: 'rn-cross-platform', via: norm(m.name) },
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
return edges;
|
|
1396
|
+
}
|
|
980
1397
|
function fabricNativeImplEdges(ctx) {
|
|
981
1398
|
const edges = [];
|
|
982
1399
|
const seen = new Set();
|
|
@@ -1045,7 +1462,7 @@ function mybatisJavaXmlEdges(queries) {
|
|
|
1045
1462
|
const seen = new Set();
|
|
1046
1463
|
// Index Java methods by `<ClassName>::<methodName>` for O(1) lookup.
|
|
1047
1464
|
const javaIndex = new Map();
|
|
1048
|
-
for (const m of queries.
|
|
1465
|
+
for (const m of queries.iterateNodesByKind('method')) {
|
|
1049
1466
|
if (m.language !== 'java' && m.language !== 'kotlin')
|
|
1050
1467
|
continue;
|
|
1051
1468
|
const parts = m.qualifiedName.split('::');
|
|
@@ -1060,7 +1477,7 @@ function mybatisJavaXmlEdges(queries) {
|
|
|
1060
1477
|
else
|
|
1061
1478
|
javaIndex.set(key, [m]);
|
|
1062
1479
|
}
|
|
1063
|
-
for (const xml of queries.
|
|
1480
|
+
for (const xml of queries.iterateNodesByKind('method')) {
|
|
1064
1481
|
if (xml.language !== 'xml')
|
|
1065
1482
|
continue;
|
|
1066
1483
|
// Qualified name: `<namespace>::<id>`. Extract the simple class name.
|
|
@@ -1176,13 +1593,15 @@ function goHandlerIdent(expr) {
|
|
|
1176
1593
|
}
|
|
1177
1594
|
function ginMiddlewareChainEdges(queries, ctx) {
|
|
1178
1595
|
// 1. Find the chain dispatcher(s): a Go method that invokes a `handlers` slice by index.
|
|
1179
|
-
const dispatchers =
|
|
1596
|
+
const dispatchers = [];
|
|
1597
|
+
for (const n of queries.iterateNodesByKind('method')) {
|
|
1180
1598
|
if (n.language !== 'go')
|
|
1181
|
-
|
|
1599
|
+
continue;
|
|
1182
1600
|
const content = ctx.readFile(n.filePath);
|
|
1183
1601
|
const src = content && sliceLines(content, n.startLine, n.endLine);
|
|
1184
|
-
|
|
1185
|
-
|
|
1602
|
+
if (src && GIN_DISPATCH_RE.test(src))
|
|
1603
|
+
dispatchers.push(n);
|
|
1604
|
+
}
|
|
1186
1605
|
if (dispatchers.length === 0)
|
|
1187
1606
|
return []; // not a gin repo — bail
|
|
1188
1607
|
// 2. Collect handler identifiers registered via gin registration calls
|
|
@@ -1238,25 +1657,129 @@ function ginMiddlewareChainEdges(queries, ctx) {
|
|
|
1238
1657
|
}
|
|
1239
1658
|
return edges;
|
|
1240
1659
|
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Delphi form code-behind: a form unit `UFRMAbout.pas` owns its visual form
|
|
1662
|
+
* definition `UFRMAbout.dfm` (VCL) / `.fmx` (FireMonkey) — paired by basename in
|
|
1663
|
+
* the same directory, wired by the `{$R *.dfm}` directive rather than a `uses`
|
|
1664
|
+
* clause. Link the unit → its form so a `.dfm`/`.fmx` used only as a form
|
|
1665
|
+
* definition isn't orphaned, and editing the form surfaces its code-behind unit.
|
|
1666
|
+
*/
|
|
1667
|
+
function pascalFormEdges(ctx) {
|
|
1668
|
+
const edges = [];
|
|
1669
|
+
const allFiles = new Set(ctx.getAllFiles());
|
|
1670
|
+
for (const file of allFiles) {
|
|
1671
|
+
if (!/\.(dfm|fmx)$/i.test(file))
|
|
1672
|
+
continue;
|
|
1673
|
+
const pasFile = file.replace(/\.(dfm|fmx)$/i, '.pas');
|
|
1674
|
+
if (!allFiles.has(pasFile))
|
|
1675
|
+
continue;
|
|
1676
|
+
const formNode = ctx.getNodesInFile(file).find((n) => n.kind === 'file');
|
|
1677
|
+
const unitNode = ctx.getNodesInFile(pasFile).find((n) => n.kind === 'file');
|
|
1678
|
+
if (!formNode || !unitNode)
|
|
1679
|
+
continue;
|
|
1680
|
+
edges.push({
|
|
1681
|
+
source: unitNode.id,
|
|
1682
|
+
target: formNode.id,
|
|
1683
|
+
kind: 'references',
|
|
1684
|
+
line: unitNode.startLine,
|
|
1685
|
+
provenance: 'heuristic',
|
|
1686
|
+
metadata: { synthesizedBy: 'pascal-form', registeredAt: pasFile },
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
return edges;
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
* SvelteKit file-convention data flow. A route directory's `+page.svelte` (a
|
|
1693
|
+
* `component` node) receives its `data` from the sibling `+page.server.{ts,js}`
|
|
1694
|
+
* / `+page.{ts,js}` `load` function and posts forms to its `actions` — wired by
|
|
1695
|
+
* the framework BY FILE PATH, with no static import between them. So editing a
|
|
1696
|
+
* `load` shows no impact on the page it feeds, and the page looks like it has no
|
|
1697
|
+
* server-side dependency. Link the page component to its sibling loader's
|
|
1698
|
+
* `load` / `actions` (same for `+layout`). The pairing is path-deterministic
|
|
1699
|
+
* (same directory, matching `+page`/`+layout` prefix), so it's precise — but
|
|
1700
|
+
* it's a framework-convention edge, so provenance stays `heuristic`.
|
|
1701
|
+
*
|
|
1702
|
+
* Direction: page → load, so `getImpactRadius(load)` surfaces the page (editing
|
|
1703
|
+
* a loader's data shows the page it feeds) and the page's dependencies include
|
|
1704
|
+
* its loader.
|
|
1705
|
+
*/
|
|
1706
|
+
function svelteKitLoadEdges(ctx) {
|
|
1707
|
+
const edges = [];
|
|
1708
|
+
const allFiles = new Set(ctx.getAllFiles());
|
|
1709
|
+
const HOOKS = new Set(['load', 'actions']);
|
|
1710
|
+
const HOOK_KINDS = new Set(['function', 'method', 'constant', 'variable']);
|
|
1711
|
+
for (const file of allFiles) {
|
|
1712
|
+
const m = file.match(/(.*\/)(\+(?:page|layout))\.svelte$/);
|
|
1713
|
+
if (!m)
|
|
1714
|
+
continue;
|
|
1715
|
+
const dir = m[1];
|
|
1716
|
+
const prefix = m[2];
|
|
1717
|
+
const page = ctx.getNodesInFile(file).find((n) => n.kind === 'component');
|
|
1718
|
+
if (!page)
|
|
1719
|
+
continue;
|
|
1720
|
+
for (const ext of ['.server.ts', '.server.js', '.ts', '.js']) {
|
|
1721
|
+
const loaderFile = `${dir}${prefix}${ext}`;
|
|
1722
|
+
if (!allFiles.has(loaderFile))
|
|
1723
|
+
continue;
|
|
1724
|
+
for (const hook of ctx.getNodesInFile(loaderFile)) {
|
|
1725
|
+
if (!HOOK_KINDS.has(hook.kind) || !HOOKS.has(hook.name))
|
|
1726
|
+
continue;
|
|
1727
|
+
edges.push({
|
|
1728
|
+
source: page.id,
|
|
1729
|
+
target: hook.id,
|
|
1730
|
+
kind: 'references',
|
|
1731
|
+
line: page.startLine,
|
|
1732
|
+
provenance: 'heuristic',
|
|
1733
|
+
metadata: {
|
|
1734
|
+
synthesizedBy: 'sveltekit-load',
|
|
1735
|
+
via: hook.name,
|
|
1736
|
+
registeredAt: `${loaderFile}:${hook.startLine ?? 0}`,
|
|
1737
|
+
},
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
return edges;
|
|
1743
|
+
}
|
|
1241
1744
|
/**
|
|
1242
1745
|
* Synthesize dispatcher→callback edges (field observers + EventEmitters +
|
|
1243
|
-
* React re-render + JSX children + Vue templates + RN event
|
|
1244
|
-
* Fabric native-impl + MyBatis Java↔XML + Gin middleware chain).
|
|
1245
|
-
* count added. Never throws into indexing — callers wrap in try/catch.
|
|
1746
|
+
* React re-render + JSX children + Vue templates + SvelteKit load + RN event
|
|
1747
|
+
* channel + Fabric native-impl + MyBatis Java↔XML + Gin middleware chain).
|
|
1748
|
+
* Returns the count added. Never throws into indexing — callers wrap in try/catch.
|
|
1246
1749
|
*/
|
|
1247
1750
|
function synthesizeCallbackEdges(queries, ctx) {
|
|
1751
|
+
// Cross-file Go method→type `contains` edges must be synthesized AND persisted
|
|
1752
|
+
// FIRST: a method declared in a different file from its receiver type is
|
|
1753
|
+
// otherwise orphaned from the struct, and goImplementsEdges (next) derives a
|
|
1754
|
+
// struct's method set from its `contains` edges — so without this it would
|
|
1755
|
+
// under-count the interfaces a cross-file struct satisfies. (#583)
|
|
1756
|
+
const goMethodContains = goCrossFileMethodContainsEdges(queries);
|
|
1757
|
+
if (goMethodContains.length > 0)
|
|
1758
|
+
queries.insertEdges(goMethodContains);
|
|
1759
|
+
// Go implicit `implements` edges must be synthesized AND persisted next: the
|
|
1760
|
+
// interface-dispatch bridge below reads `implements` edges from the DB, and
|
|
1761
|
+
// Go has none statically. (Other languages already have static implements
|
|
1762
|
+
// edges from extraction, so they don't need this pre-pass.)
|
|
1763
|
+
const goImpl = goImplementsEdges(queries);
|
|
1764
|
+
if (goImpl.length > 0)
|
|
1765
|
+
queries.insertEdges(goImpl);
|
|
1248
1766
|
const fieldEdges = fieldChannelEdges(queries, ctx);
|
|
1249
1767
|
const closureCollEdges = closureCollectionEdges(queries, ctx);
|
|
1250
1768
|
const emitterEdges = eventEmitterEdges(ctx);
|
|
1251
1769
|
const renderEdges = reactRenderEdges(queries, ctx);
|
|
1252
1770
|
const jsxEdges = reactJsxChildEdges(ctx);
|
|
1253
1771
|
const vueEdges = vueTemplateEdges(ctx);
|
|
1772
|
+
const svelteKitEdges = svelteKitLoadEdges(ctx);
|
|
1773
|
+
const pascalEdges = pascalFormEdges(ctx);
|
|
1254
1774
|
const flutterEdges = flutterBuildEdges(queries, ctx);
|
|
1255
1775
|
const cppEdges = cppOverrideEdges(queries);
|
|
1256
1776
|
const ifaceEdges = interfaceOverrideEdges(queries);
|
|
1777
|
+
const kotlinExpectActual = kotlinExpectActualEdges(queries);
|
|
1257
1778
|
const goGrpcEdges = goGrpcStubImplEdges(queries);
|
|
1258
1779
|
const rnEventEdgesList = rnEventEdges(ctx);
|
|
1259
1780
|
const fabricNativeEdges = fabricNativeImplEdges(ctx);
|
|
1781
|
+
const expoXPlatEdges = expoCrossPlatformEdges(queries);
|
|
1782
|
+
const rnXPlatEdges = rnCrossPlatformEdges(queries);
|
|
1260
1783
|
const mybatisEdges = mybatisJavaXmlEdges(queries);
|
|
1261
1784
|
const ginEdges = ginMiddlewareChainEdges(queries, ctx);
|
|
1262
1785
|
const merged = [];
|
|
@@ -1268,12 +1791,17 @@ function synthesizeCallbackEdges(queries, ctx) {
|
|
|
1268
1791
|
...renderEdges,
|
|
1269
1792
|
...jsxEdges,
|
|
1270
1793
|
...vueEdges,
|
|
1794
|
+
...svelteKitEdges,
|
|
1795
|
+
...pascalEdges,
|
|
1271
1796
|
...flutterEdges,
|
|
1272
1797
|
...cppEdges,
|
|
1273
1798
|
...ifaceEdges,
|
|
1799
|
+
...kotlinExpectActual,
|
|
1274
1800
|
...goGrpcEdges,
|
|
1275
1801
|
...rnEventEdgesList,
|
|
1276
1802
|
...fabricNativeEdges,
|
|
1803
|
+
...expoXPlatEdges,
|
|
1804
|
+
...rnXPlatEdges,
|
|
1277
1805
|
...mybatisEdges,
|
|
1278
1806
|
...ginEdges,
|
|
1279
1807
|
]) {
|
|
@@ -1285,6 +1813,6 @@ function synthesizeCallbackEdges(queries, ctx) {
|
|
|
1285
1813
|
}
|
|
1286
1814
|
if (merged.length > 0)
|
|
1287
1815
|
queries.insertEdges(merged);
|
|
1288
|
-
return merged.length;
|
|
1816
|
+
return merged.length + goImpl.length + goMethodContains.length;
|
|
1289
1817
|
}
|
|
1290
1818
|
//# sourceMappingURL=callback-synthesizer.js.map
|