@nanonets/graft 0.13.0 → 0.16.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/README.md +26 -10
- package/dist/ai/crux.d.ts +14 -1
- package/dist/ai/crux.d.ts.map +1 -1
- package/dist/ai/crux.js +39 -51
- package/dist/ai/crux.js.map +1 -1
- package/dist/ai/failure.d.ts +13 -2
- package/dist/ai/failure.d.ts.map +1 -1
- package/dist/ai/failure.js +16 -4
- package/dist/ai/failure.js.map +1 -1
- package/dist/ai/llm/factory.d.ts +10 -1
- package/dist/ai/llm/factory.d.ts.map +1 -1
- package/dist/ai/llm/factory.js +16 -0
- package/dist/ai/llm/factory.js.map +1 -1
- package/dist/ai/llm/litellm.d.ts +34 -0
- package/dist/ai/llm/litellm.d.ts.map +1 -0
- package/dist/ai/llm/litellm.js +45 -0
- package/dist/ai/llm/litellm.js.map +1 -0
- package/dist/ai/llm/openai.d.ts.map +1 -1
- package/dist/ai/llm/openai.js +66 -7
- package/dist/ai/llm/openai.js.map +1 -1
- package/dist/ai/llm/orcarouter.d.ts +34 -0
- package/dist/ai/llm/orcarouter.d.ts.map +1 -0
- package/dist/ai/llm/orcarouter.js +45 -0
- package/dist/ai/llm/orcarouter.js.map +1 -0
- package/dist/ai/llm/recover-tool.d.ts +19 -0
- package/dist/ai/llm/recover-tool.d.ts.map +1 -0
- package/dist/ai/llm/recover-tool.js +109 -0
- package/dist/ai/llm/recover-tool.js.map +1 -0
- package/dist/ai/providers.d.ts.map +1 -1
- package/dist/ai/providers.js +15 -3
- package/dist/ai/providers.js.map +1 -1
- package/dist/ai/synthesize.d.ts.map +1 -1
- package/dist/ai/synthesize.js +19 -5
- package/dist/ai/synthesize.js.map +1 -1
- package/dist/app/checkout.d.ts +28 -0
- package/dist/app/checkout.d.ts.map +1 -0
- package/dist/app/checkout.js +100 -0
- package/dist/app/checkout.js.map +1 -0
- package/dist/app/events.d.ts +37 -0
- package/dist/app/events.d.ts.map +1 -0
- package/dist/app/events.js +59 -0
- package/dist/app/events.js.map +1 -0
- package/dist/app/identity.d.ts +54 -0
- package/dist/app/identity.d.ts.map +1 -0
- package/dist/app/identity.js +96 -0
- package/dist/app/identity.js.map +1 -0
- package/dist/app/main.d.ts +2 -0
- package/dist/app/main.d.ts.map +1 -0
- package/dist/app/main.js +56 -0
- package/dist/app/main.js.map +1 -0
- package/dist/app/pages.d.ts +39 -0
- package/dist/app/pages.d.ts.map +1 -0
- package/dist/app/pages.js +86 -0
- package/dist/app/pages.js.map +1 -0
- package/dist/app/queue.d.ts +40 -0
- package/dist/app/queue.d.ts.map +1 -0
- package/dist/app/queue.js +68 -0
- package/dist/app/queue.js.map +1 -0
- package/dist/app/review.d.ts +23 -0
- package/dist/app/review.d.ts.map +1 -0
- package/dist/app/review.js +139 -0
- package/dist/app/review.js.map +1 -0
- package/dist/app/server.d.ts +40 -0
- package/dist/app/server.d.ts.map +1 -0
- package/dist/app/server.js +117 -0
- package/dist/app/server.js.map +1 -0
- package/dist/ask/ask.d.ts +35 -0
- package/dist/ask/ask.d.ts.map +1 -1
- package/dist/ask/ask.js +427 -63
- package/dist/ask/ask.js.map +1 -1
- package/dist/ask/file-rank.d.ts +72 -0
- package/dist/ask/file-rank.d.ts.map +1 -0
- package/dist/ask/file-rank.js +160 -0
- package/dist/ask/file-rank.js.map +1 -0
- package/dist/ask/file-selection.d.ts +4 -0
- package/dist/ask/file-selection.d.ts.map +1 -1
- package/dist/ask/file-selection.js +25 -0
- package/dist/ask/file-selection.js.map +1 -1
- package/dist/ask/fuse.d.ts +16 -0
- package/dist/ask/fuse.d.ts.map +1 -1
- package/dist/ask/fuse.js +21 -4
- package/dist/ask/fuse.js.map +1 -1
- package/dist/ask/graphrank.d.ts +18 -0
- package/dist/ask/graphrank.d.ts.map +1 -1
- package/dist/ask/graphrank.js +65 -19
- package/dist/ask/graphrank.js.map +1 -1
- package/dist/blast/blast-cli.d.ts +5 -0
- package/dist/blast/blast-cli.d.ts.map +1 -1
- package/dist/blast/blast-cli.js +21 -30
- package/dist/blast/blast-cli.js.map +1 -1
- package/dist/blast/blast.d.ts +9 -0
- package/dist/blast/blast.d.ts.map +1 -1
- package/dist/blast/blast.js.map +1 -1
- package/dist/blast/name.d.ts +13 -0
- package/dist/blast/name.d.ts.map +1 -1
- package/dist/blast/name.js +36 -0
- package/dist/blast/name.js.map +1 -1
- package/dist/blast/owners.d.ts +85 -0
- package/dist/blast/owners.d.ts.map +1 -0
- package/dist/blast/owners.js +272 -0
- package/dist/blast/owners.js.map +1 -0
- package/dist/blast/render.d.ts.map +1 -1
- package/dist/blast/render.js +105 -0
- package/dist/blast/render.js.map +1 -1
- package/dist/blast/viz.d.ts.map +1 -1
- package/dist/blast/viz.js +10 -0
- package/dist/blast/viz.js.map +1 -1
- package/dist/claude/format.js +1 -1
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +132 -22
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.d.ts +1 -0
- package/dist/claude/init.d.ts.map +1 -1
- package/dist/claude/init.js +1 -1
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/session-metrics.d.ts +63 -0
- package/dist/claude/session-metrics.d.ts.map +1 -0
- package/dist/claude/session-metrics.js +181 -0
- package/dist/claude/session-metrics.js.map +1 -0
- package/dist/claude/settings-merge.d.ts +18 -1
- package/dist/claude/settings-merge.d.ts.map +1 -1
- package/dist/claude/settings-merge.js +70 -27
- package/dist/claude/settings-merge.js.map +1 -1
- package/dist/claude/state.d.ts +25 -0
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js +14 -1
- package/dist/claude/state.js.map +1 -1
- package/dist/claude/statusline.d.ts +5 -3
- package/dist/claude/statusline.d.ts.map +1 -1
- package/dist/claude/statusline.js +6 -4
- package/dist/claude/statusline.js.map +1 -1
- package/dist/claude/tally.d.ts +23 -0
- package/dist/claude/tally.d.ts.map +1 -0
- package/dist/claude/tally.js +138 -0
- package/dist/claude/tally.js.map +1 -0
- package/dist/cli.js +145 -4
- package/dist/cli.js.map +1 -1
- package/dist/context/build.d.ts +11 -0
- package/dist/context/build.d.ts.map +1 -1
- package/dist/context/build.js +49 -9
- package/dist/context/build.js.map +1 -1
- package/dist/context/check.d.ts.map +1 -1
- package/dist/context/check.js +5 -12
- package/dist/context/check.js.map +1 -1
- package/dist/context/node-file.d.ts +2 -1
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +70 -3
- package/dist/context/node-file.js.map +1 -1
- package/dist/context/savings.d.ts +8 -0
- package/dist/context/savings.d.ts.map +1 -1
- package/dist/context/savings.js +14 -0
- package/dist/context/savings.js.map +1 -1
- package/dist/engine.d.ts +4 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +2 -0
- package/dist/engine.js.map +1 -1
- package/dist/graph/bindings.d.ts +6 -5
- package/dist/graph/bindings.d.ts.map +1 -1
- package/dist/graph/bindings.js +117 -5
- package/dist/graph/bindings.js.map +1 -1
- package/dist/graph/build.d.ts +4 -0
- package/dist/graph/build.d.ts.map +1 -1
- package/dist/graph/build.js +7 -4
- package/dist/graph/build.js.map +1 -1
- package/dist/graph/cards.d.ts +4 -3
- package/dist/graph/cards.d.ts.map +1 -1
- package/dist/graph/cards.js +34 -6
- package/dist/graph/cards.js.map +1 -1
- package/dist/graph/check.d.ts.map +1 -1
- package/dist/graph/check.js +36 -6
- package/dist/graph/check.js.map +1 -1
- package/dist/graph/enrich.d.ts +4 -4
- package/dist/graph/enrich.d.ts.map +1 -1
- package/dist/graph/enrich.js +32 -6
- package/dist/graph/enrich.js.map +1 -1
- package/dist/graph/extract.d.ts +9 -2
- package/dist/graph/extract.d.ts.map +1 -1
- package/dist/graph/extract.js +356 -12
- package/dist/graph/extract.js.map +1 -1
- package/dist/graph/fingerprint.d.ts +6 -1
- package/dist/graph/fingerprint.d.ts.map +1 -1
- package/dist/graph/fingerprint.js +7 -3
- package/dist/graph/fingerprint.js.map +1 -1
- package/dist/graph/generic.d.ts +10 -0
- package/dist/graph/generic.d.ts.map +1 -1
- package/dist/graph/generic.js +20 -3
- package/dist/graph/generic.js.map +1 -1
- package/dist/graph/invariants.d.ts +3 -2
- package/dist/graph/invariants.d.ts.map +1 -1
- package/dist/graph/invariants.js +6 -3
- package/dist/graph/invariants.js.map +1 -1
- package/dist/graph/refresh.d.ts.map +1 -1
- package/dist/graph/refresh.js +6 -2
- package/dist/graph/refresh.js.map +1 -1
- package/dist/graph/resolve.d.ts +1 -1
- package/dist/graph/resolve.d.ts.map +1 -1
- package/dist/graph/resolve.js +106 -5
- package/dist/graph/resolve.js.map +1 -1
- package/dist/graph/scopes.d.ts.map +1 -1
- package/dist/graph/scopes.js +2 -1
- package/dist/graph/scopes.js.map +1 -1
- package/dist/graph/source-files.d.ts +7 -2
- package/dist/graph/source-files.d.ts.map +1 -1
- package/dist/graph/source-files.js +19 -6
- package/dist/graph/source-files.js.map +1 -1
- package/dist/graph/workspace-cli.d.ts +2 -0
- package/dist/graph/workspace-cli.d.ts.map +1 -1
- package/dist/graph/workspace-cli.js +3 -0
- package/dist/graph/workspace-cli.js.map +1 -1
- package/dist/graph/workspace.d.ts +2 -0
- package/dist/graph/workspace.d.ts.map +1 -1
- package/dist/graph/workspace.js +173 -6
- package/dist/graph/workspace.js.map +1 -1
- package/dist/hosts/antigravity.d.ts +2 -2
- package/dist/hosts/antigravity.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.d.ts +2 -6
- package/dist/hosts/codex-hooks.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.js +7 -30
- package/dist/hosts/codex-hooks.js.map +1 -1
- package/dist/hosts/config-write.d.ts +36 -0
- package/dist/hosts/config-write.d.ts.map +1 -0
- package/dist/hosts/config-write.js +63 -0
- package/dist/hosts/config-write.js.map +1 -0
- package/dist/hosts/cursor-hooks.d.ts +18 -0
- package/dist/hosts/cursor-hooks.d.ts.map +1 -0
- package/dist/hosts/cursor-hooks.js +100 -0
- package/dist/hosts/cursor-hooks.js.map +1 -0
- package/dist/hosts/init.d.ts +2 -2
- package/dist/hosts/init.d.ts.map +1 -1
- package/dist/hosts/init.js +8 -2
- package/dist/hosts/init.js.map +1 -1
- package/dist/hosts/mcp-config.d.ts +15 -5
- package/dist/hosts/mcp-config.d.ts.map +1 -1
- package/dist/hosts/mcp-config.js +51 -15
- package/dist/hosts/mcp-config.js.map +1 -1
- package/dist/hosts/plan.d.ts.map +1 -1
- package/dist/hosts/plan.js +2 -0
- package/dist/hosts/plan.js.map +1 -1
- package/dist/hosts/retract.d.ts +47 -0
- package/dist/hosts/retract.d.ts.map +1 -0
- package/dist/hosts/retract.js +473 -0
- package/dist/hosts/retract.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest/fs.d.ts +39 -5
- package/dist/ingest/fs.d.ts.map +1 -1
- package/dist/ingest/fs.js +104 -11
- package/dist/ingest/fs.js.map +1 -1
- package/dist/mcp/tool-names.d.ts +20 -0
- package/dist/mcp/tool-names.d.ts.map +1 -0
- package/dist/mcp/tool-names.js +36 -0
- package/dist/mcp/tool-names.js.map +1 -0
- package/dist/mcp/tools.d.ts +11 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +9 -21
- package/dist/mcp/tools.js.map +1 -1
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +17 -3
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/flush.d.ts.map +1 -1
- package/dist/telemetry/flush.js +1 -0
- package/dist/telemetry/flush.js.map +1 -1
- package/dist/telemetry/identity.d.ts +7 -0
- package/dist/telemetry/identity.d.ts.map +1 -1
- package/dist/telemetry/identity.js.map +1 -1
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/sessions.d.ts +19 -2
- package/dist/telemetry/sessions.d.ts.map +1 -1
- package/dist/telemetry/sessions.js +57 -34
- package/dist/telemetry/sessions.js.map +1 -1
- package/dist/telemetry/track.d.ts +22 -3
- package/dist/telemetry/track.d.ts.map +1 -1
- package/dist/telemetry/track.js +34 -5
- package/dist/telemetry/track.js.map +1 -1
- package/dist/upkeep-run.js +1 -1
- package/dist/upkeep-run.js.map +1 -1
- package/dist/upkeep.d.ts +7 -4
- package/dist/upkeep.d.ts.map +1 -1
- package/dist/upkeep.js +2 -1
- package/dist/upkeep.js.map +1 -1
- package/dist/util/state.d.ts +12 -2
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js +4 -0
- package/dist/util/state.js.map +1 -1
- package/dist/viewer/app.js +2 -1
- package/dist/viewer/style.css +14 -0
- package/dist/viz/assemble.d.ts +17 -0
- package/dist/viz/assemble.d.ts.map +1 -1
- package/dist/viz/assemble.js.map +1 -1
- package/package.json +12 -4
- package/scripts/graph-quality.mjs +3 -3
- package/scripts/postinstall.mjs +42 -3
- package/scripts/run-tests.mjs +14 -0
- package/scripts/tally-audit.mjs +140 -0
- package/dist/ai/llm/claude-cli.d.ts +0 -60
- package/dist/ai/llm/claude-cli.d.ts.map +0 -1
- package/dist/ai/llm/claude-cli.js +0 -417
- package/dist/ai/llm/claude-cli.js.map +0 -1
- package/dist/util/num.d.ts +0 -3
- package/dist/util/num.d.ts.map +0 -1
- package/dist/util/num.js +0 -20
- package/dist/util/num.js.map +0 -1
package/dist/graph/extract.js
CHANGED
|
@@ -13,6 +13,7 @@ import Go from "tree-sitter-go";
|
|
|
13
13
|
import R from "tree-sitter-r";
|
|
14
14
|
import Java from "tree-sitter-java";
|
|
15
15
|
import Kotlin from "tree-sitter-kotlin";
|
|
16
|
+
import Swift from "tree-sitter-swift";
|
|
16
17
|
import PHP from "tree-sitter-php";
|
|
17
18
|
import { basename } from "node:path";
|
|
18
19
|
import { contentHash } from "../util/id.js";
|
|
@@ -47,6 +48,7 @@ const EXTENSIONS = [
|
|
|
47
48
|
{ ext: ".java", grammar: "java", label: "java" },
|
|
48
49
|
{ ext: ".kt", grammar: "kotlin", label: "kotlin" },
|
|
49
50
|
{ ext: ".kts", grammar: "kotlin", label: "kotlin" },
|
|
51
|
+
{ ext: ".swift", grammar: "swift", label: "swift" },
|
|
50
52
|
{ ext: ".php", grammar: "php", label: "php" },
|
|
51
53
|
// `entryFor` lower-cases the path before matching, so this one entry covers
|
|
52
54
|
// both `.R` (the conventional case in real R codebases) and `.r`.
|
|
@@ -164,6 +166,29 @@ const KOTLIN_KINDS = {
|
|
|
164
166
|
* "class" also covers object_declaration (it maps to "class"); interface/enum are the
|
|
165
167
|
* same class_declaration node rekinded in describeKotlin, so all three land in the set. */
|
|
166
168
|
const KOTLIN_TYPE_KINDS = new Set(["class", "interface", "enum"]);
|
|
169
|
+
const SWIFT_KINDS = {
|
|
170
|
+
class_declaration: "class", // → "struct" / "enum" in describeSwift (one node type covers all five keywords)
|
|
171
|
+
protocol_declaration: "interface",
|
|
172
|
+
function_declaration: "function", // → "method" inside a type (resolved in the walk)
|
|
173
|
+
protocol_function_declaration: "method", // a protocol requirement is always a member
|
|
174
|
+
init_declaration: "method", // the type's own initializer (named after it, like a Java constructor)
|
|
175
|
+
typealias_declaration: "type",
|
|
176
|
+
property_declaration: "variable", // top-level `let`/`var` only (fields resolved in the walk)
|
|
177
|
+
};
|
|
178
|
+
/** Swift type declarations: they set `enclosingClass` for the members nested in them.
|
|
179
|
+
* class/struct/enum are the same class_declaration node rekinded in describeSwift
|
|
180
|
+
* (an actor takes "class"); protocols are "interface". "module" is an extension
|
|
181
|
+
* body — a member-contributing scope named after the extended type, deliberately
|
|
182
|
+
* NOT a type kind in the graph so it can never make the real declaration's name
|
|
183
|
+
* ambiguous (see describeSwift's extension branch) — but its members still
|
|
184
|
+
* promote to methods owned by that type, which is why it belongs in this set. */
|
|
185
|
+
const SWIFT_TYPE_KINDS = new Set([
|
|
186
|
+
"class",
|
|
187
|
+
"struct",
|
|
188
|
+
"enum",
|
|
189
|
+
"interface",
|
|
190
|
+
"module",
|
|
191
|
+
]);
|
|
167
192
|
// PHP: definition node types are all distinct (no py-style function→method
|
|
168
193
|
// promotion needed — a class body uses `method_declaration`, not
|
|
169
194
|
// `function_definition`). `trait_declaration` maps to the PHP-only `trait` kind.
|
|
@@ -183,6 +208,7 @@ const KINDS_BY_LANG = {
|
|
|
183
208
|
r: R_KINDS,
|
|
184
209
|
java: JAVA_KINDS,
|
|
185
210
|
kotlin: KOTLIN_KINDS,
|
|
211
|
+
swift: SWIFT_KINDS,
|
|
186
212
|
php: PHP_KINDS,
|
|
187
213
|
};
|
|
188
214
|
/**
|
|
@@ -201,6 +227,7 @@ const CALL_TYPES = {
|
|
|
201
227
|
go: new Set(["call_expression"]),
|
|
202
228
|
java: new Set(["method_invocation", "object_creation_expression"]),
|
|
203
229
|
kotlin: new Set(["call_expression"]),
|
|
230
|
+
swift: new Set(["call_expression"]),
|
|
204
231
|
php: new Set([
|
|
205
232
|
"function_call_expression",
|
|
206
233
|
"member_call_expression",
|
|
@@ -225,6 +252,7 @@ const GRAMMARS = {
|
|
|
225
252
|
r: R,
|
|
226
253
|
java: Java,
|
|
227
254
|
kotlin: Kotlin,
|
|
255
|
+
swift: Swift,
|
|
228
256
|
php: PHP.php,
|
|
229
257
|
};
|
|
230
258
|
/** tree-sitter's string `parse()` fails with "Invalid argument" on any input
|
|
@@ -429,9 +457,11 @@ function walk(node, ctx, out, edges, minted) {
|
|
|
429
457
|
? javaExported(node)
|
|
430
458
|
: ctx.lang === "kotlin"
|
|
431
459
|
? kotlinExported(node)
|
|
432
|
-
: ctx.lang === "
|
|
433
|
-
?
|
|
434
|
-
:
|
|
460
|
+
: ctx.lang === "swift"
|
|
461
|
+
? swiftExported(node)
|
|
462
|
+
: ctx.lang === "php"
|
|
463
|
+
? phpExported(node)
|
|
464
|
+
: tsExported(node),
|
|
435
465
|
origin: "ast",
|
|
436
466
|
body_hash: contentHash(desc.hashNode.text),
|
|
437
467
|
body_text: searchBody(desc.hashNode.text),
|
|
@@ -448,11 +478,14 @@ function walk(node, ctx, out, edges, minted) {
|
|
|
448
478
|
// may `implements`, so every type declaration is a heritage site, not just a class.
|
|
449
479
|
const javaTypeDecl = ctx.lang === "java" && JAVA_TYPE_KINDS.has(desc.kind);
|
|
450
480
|
const kotlinTypeDecl = ctx.lang === "kotlin" && KOTLIN_TYPE_KINDS.has(desc.kind);
|
|
451
|
-
|
|
481
|
+
const swiftTypeDecl = ctx.lang === "swift" && SWIFT_TYPE_KINDS.has(desc.kind);
|
|
482
|
+
if (desc.kind === "class" || javaTypeDecl || kotlinTypeDecl || swiftTypeDecl)
|
|
452
483
|
edges.push(...heritageEdges(node, id, ctx));
|
|
453
484
|
if (ctx.lang === "php")
|
|
454
485
|
edges.push(...phpAttributeReferenceEdges(node, id, ctx));
|
|
455
|
-
|
|
486
|
+
if (ctx.lang === "java")
|
|
487
|
+
edges.push(...javaAnnotationReferenceEdges(node, id, ctx));
|
|
488
|
+
const enclosingClass = desc.kind === "class" || javaTypeDecl || kotlinTypeDecl || swiftTypeDecl
|
|
456
489
|
? desc.name
|
|
457
490
|
: isGoMethod
|
|
458
491
|
? goReceiverType(node)
|
|
@@ -474,9 +507,17 @@ function walk(node, ctx, out, edges, minted) {
|
|
|
474
507
|
// defined inside one).
|
|
475
508
|
rR6Access: null,
|
|
476
509
|
// Unlike rR6Access, only reset when entering a genuinely new class (so it
|
|
477
|
-
// stays live through a method's whole body, where super$ calls
|
|
478
|
-
// happen) — inherited unchanged for every other definition kind.
|
|
479
|
-
|
|
510
|
+
// stays live through a method's whole body, where super$ / super. calls
|
|
511
|
+
// actually happen) — inherited unchanged for every other definition kind.
|
|
512
|
+
// Swift reads it off the declaration's own `:` clause, so `super.ping()`
|
|
513
|
+
// resolves against the PARENT type, not the overriding current one.
|
|
514
|
+
rSuperClass: desc.kind === "class"
|
|
515
|
+
? ctx.lang === "r"
|
|
516
|
+
? rR6ParentClass(node)
|
|
517
|
+
: ctx.lang === "swift"
|
|
518
|
+
? swiftSuperClassName(node)
|
|
519
|
+
: null
|
|
520
|
+
: ctx.rSuperClass,
|
|
480
521
|
};
|
|
481
522
|
walkNamedChildren(node.namedChildren, childCtx, out, edges, minted);
|
|
482
523
|
return;
|
|
@@ -541,12 +582,44 @@ function walk(node, ctx, out, edges, minted) {
|
|
|
541
582
|
file: ctx.rel,
|
|
542
583
|
...(callee.kinds ? { kinds: callee.kinds } : {}),
|
|
543
584
|
};
|
|
544
|
-
//
|
|
545
|
-
|
|
585
|
+
// Overloading languages: the call site's argument count, to pick the right
|
|
586
|
+
// overload (see RawEdge.argCount).
|
|
587
|
+
const argCount = ctx.lang === "java" ? javaArgCount(node) : ctx.lang === "swift" ? swiftArgCount(node) : undefined;
|
|
546
588
|
if (argCount !== undefined)
|
|
547
589
|
callEdge.argCount = argCount;
|
|
548
|
-
|
|
549
|
-
|
|
590
|
+
// Swift: a bare lowercase call inside a type body may be an implicit-`self`
|
|
591
|
+
// member call (`walk()` for `self.walk()`), syntactically indistinguishable
|
|
592
|
+
// from a free-function call — and Swift's own lookup is member-FIRST (inner
|
|
593
|
+
// scope wins). So the edge is emitted as the member reading, typed to the
|
|
594
|
+
// enclosing class — resolved through the owner-qualified method index and
|
|
595
|
+
// the class's in-repo ancestor chain (`clearLogs()` in a test subclass
|
|
596
|
+
// finds the base class's method) — with `implicitSelf` letting resolve.ts
|
|
597
|
+
// fall back to the free-function reading only when no member exists on the
|
|
598
|
+
// chain. One edge, both readings, language-order precedence. This is
|
|
599
|
+
// deliberately NOT a bare-name kind widening: dogfooding on
|
|
600
|
+
// swift-composable-architecture, a global unique-name match bound
|
|
601
|
+
// `contains(element)` inside `extension Set` — a stdlib call — to an
|
|
602
|
+
// unrelated type's only in-repo `contains`. And not for an UpperCamelCase
|
|
603
|
+
// callee: that is an initializer call (`Text("hi")`), which takes
|
|
604
|
+
// resolve.ts's class/struct/enum fallback instead — extension nodes (kind
|
|
605
|
+
// "module") can never false-match it.
|
|
606
|
+
const swiftImplicitSelf = ctx.lang === "swift" &&
|
|
607
|
+
!callee.viaMember &&
|
|
608
|
+
!callee.kinds &&
|
|
609
|
+
ctx.enclosingClass &&
|
|
610
|
+
!/^[A-Z]/.test(callee.name);
|
|
611
|
+
if (swiftImplicitSelf) {
|
|
612
|
+
edges.push({
|
|
613
|
+
...callEdge,
|
|
614
|
+
viaMember: true,
|
|
615
|
+
recvType: ctx.enclosingClass,
|
|
616
|
+
implicitSelf: true,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
const recvType = resolveRecvType(callee.receiver, ctx);
|
|
621
|
+
edges.push(recvType ? { ...callEdge, recvType } : callEdge);
|
|
622
|
+
}
|
|
550
623
|
}
|
|
551
624
|
}
|
|
552
625
|
else if (ctx.lang === "php" && node.type === "use_declaration") {
|
|
@@ -750,6 +823,38 @@ function phpAttributeClassRef(attr, ctx) {
|
|
|
750
823
|
return { name: imported.name, specifier: imported.specifier };
|
|
751
824
|
return { name: bare };
|
|
752
825
|
}
|
|
826
|
+
/** Java annotations on a definition → `references` edges to the annotation type. */
|
|
827
|
+
function javaAnnotationReferenceEdges(node, sourceId, ctx) {
|
|
828
|
+
const edges = [];
|
|
829
|
+
const mods = node.namedChildren.find((c) => c.type === "modifiers");
|
|
830
|
+
if (!mods)
|
|
831
|
+
return edges;
|
|
832
|
+
for (const child of mods.namedChildren) {
|
|
833
|
+
if (child.type !== "marker_annotation" && child.type !== "annotation")
|
|
834
|
+
continue;
|
|
835
|
+
const name = javaAnnotationTypeName(child);
|
|
836
|
+
if (name) {
|
|
837
|
+
edges.push({
|
|
838
|
+
source: sourceId,
|
|
839
|
+
relation: "references",
|
|
840
|
+
name,
|
|
841
|
+
file: ctx.rel,
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return edges;
|
|
846
|
+
}
|
|
847
|
+
/** The type named by `@Foo` / `@a.b.Foo(...)`. Arguments are ignored (issue #89).
|
|
848
|
+
* A scoped name is kept whole, matching heritage: a bare last segment would
|
|
849
|
+
* false-match an unrelated in-repo type (#103). */
|
|
850
|
+
function javaAnnotationTypeName(anno) {
|
|
851
|
+
const nameNode = anno.childForFieldName("name");
|
|
852
|
+
if (!nameNode)
|
|
853
|
+
return null;
|
|
854
|
+
if (nameNode.type === "identifier" || nameNode.type === "scoped_identifier")
|
|
855
|
+
return nameNode.text;
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
753
858
|
function collectTsImportBindings(node, specifier, out) {
|
|
754
859
|
if (node.type === "import_specifier") {
|
|
755
860
|
const name = node.childForFieldName("name")?.text;
|
|
@@ -846,6 +951,8 @@ function describe(node, ctx) {
|
|
|
846
951
|
return describeJava(node, ctx);
|
|
847
952
|
if (ctx.lang === "kotlin")
|
|
848
953
|
return describeKotlin(node, ctx);
|
|
954
|
+
if (ctx.lang === "swift")
|
|
955
|
+
return describeSwift(node, ctx);
|
|
849
956
|
// PHP closures: `$h = function () {…}` / `fn() => …`, and bare callbacks
|
|
850
957
|
// (`$routes->get('/x', function () {…})`). Captured as function nodes so a
|
|
851
958
|
// closure-only file (a routing table, a DI container) keeps its structure
|
|
@@ -1489,6 +1596,121 @@ function describeKotlin(node, ctx) {
|
|
|
1489
1596
|
}
|
|
1490
1597
|
return null;
|
|
1491
1598
|
}
|
|
1599
|
+
/** Swift definition shapes. Like Kotlin's, tree-sitter-swift exposes no `name` or
|
|
1600
|
+
* `body` fields: a definition's name is a direct `type_identifier` (types) or
|
|
1601
|
+
* `simple_identifier` (functions) child, and its body is a `class_body` /
|
|
1602
|
+
* `enum_class_body` / `protocol_body` / `function_body` child. One
|
|
1603
|
+
* `class_declaration` node type covers `class`, `struct`, `enum`, `actor` AND
|
|
1604
|
+
* `extension` — the declaration's own keyword token tells them apart. */
|
|
1605
|
+
function describeSwift(node, ctx) {
|
|
1606
|
+
// The first direct `type_identifier` is the declared name (generic parameters and
|
|
1607
|
+
// inheritance specifiers are all nested beneath other child nodes).
|
|
1608
|
+
const typeName = () => node.namedChildren.find((c) => c.type === "type_identifier")?.text ?? null;
|
|
1609
|
+
// The first direct `simple_identifier` is the function name (parameters and
|
|
1610
|
+
// generic parameters are all nested beneath other child nodes).
|
|
1611
|
+
const funcName = () => node.namedChildren.find((c) => c.type === "simple_identifier")?.text ?? null;
|
|
1612
|
+
const headEnd = (...types) => {
|
|
1613
|
+
const body = node.namedChildren.find((c) => types.includes(c.type));
|
|
1614
|
+
return body ? body.startIndex : node.endIndex;
|
|
1615
|
+
};
|
|
1616
|
+
if (node.type === "class_declaration") {
|
|
1617
|
+
const kw = node.children.find((c) => c.type === "class" ||
|
|
1618
|
+
c.type === "struct" ||
|
|
1619
|
+
c.type === "enum" ||
|
|
1620
|
+
c.type === "actor" ||
|
|
1621
|
+
c.type === "extension")?.type;
|
|
1622
|
+
let name;
|
|
1623
|
+
if (kw === "extension") {
|
|
1624
|
+
// `extension Point { … }` has no name of its own — the extended type's IS its
|
|
1625
|
+
// identity, so the node takes that name: members mint as `Point.method`,
|
|
1626
|
+
// `enclosingClass` becomes `Point`, and a member call on a Point receiver
|
|
1627
|
+
// resolves to them exactly as if they were declared on the type. A qualified
|
|
1628
|
+
// target (`extension Swift.Array`) reduces to its last component, matching
|
|
1629
|
+
// how the extended type is itself named in the graph.
|
|
1630
|
+
//
|
|
1631
|
+
// Its KIND is "module", not "class": the type usually already has a real
|
|
1632
|
+
// declaration, and a second same-named "class" node would make the name
|
|
1633
|
+
// AMBIGUOUS to resolveName — every `Point(...)` initializer call and every
|
|
1634
|
+
// `: Point` heritage target would then drop instead of resolving (resolve
|
|
1635
|
+
// never guesses between same-named candidates). "module" keeps the node out
|
|
1636
|
+
// of type-name resolution entirely while SWIFT_TYPE_KINDS still makes it
|
|
1637
|
+
// own its members; typed member calls are untouched either way, since they
|
|
1638
|
+
// go through the owner-qualified method index, not the type's own node.
|
|
1639
|
+
const ut = node.namedChildren.find((c) => c.type === "user_type");
|
|
1640
|
+
const ids = ut?.namedChildren.filter((c) => c.type === "type_identifier") ?? [];
|
|
1641
|
+
name = ids.length ? ids[ids.length - 1].text : null;
|
|
1642
|
+
}
|
|
1643
|
+
else {
|
|
1644
|
+
name = typeName();
|
|
1645
|
+
}
|
|
1646
|
+
if (!name)
|
|
1647
|
+
return null;
|
|
1648
|
+
// An actor is class-like (reference semantics, methods) and there is no
|
|
1649
|
+
// dedicated actor kind, so it takes "class".
|
|
1650
|
+
const kind = kw === "extension" ? "module" : kw === "struct" ? "struct" : kw === "enum" ? "enum" : "class";
|
|
1651
|
+
return { name, kind, headerEnd: headEnd("class_body", "enum_class_body"), hashNode: node };
|
|
1652
|
+
}
|
|
1653
|
+
if (node.type === "protocol_declaration") {
|
|
1654
|
+
const name = typeName();
|
|
1655
|
+
if (!name)
|
|
1656
|
+
return null;
|
|
1657
|
+
return { name, kind: "interface", headerEnd: headEnd("protocol_body"), hashNode: node };
|
|
1658
|
+
}
|
|
1659
|
+
// A protocol requirement (`protocol_function_declaration`) has no body and is
|
|
1660
|
+
// always a member; an ordinary `function_declaration` is a method exactly when
|
|
1661
|
+
// it is nested in a type (or an extension of one).
|
|
1662
|
+
if (node.type === "function_declaration" || node.type === "protocol_function_declaration") {
|
|
1663
|
+
const name = funcName();
|
|
1664
|
+
if (!name)
|
|
1665
|
+
return null;
|
|
1666
|
+
const kind = node.type === "protocol_function_declaration" ||
|
|
1667
|
+
SWIFT_TYPE_KINDS.has(ctx.enclosingKind ?? "file")
|
|
1668
|
+
? "method"
|
|
1669
|
+
: "function";
|
|
1670
|
+
return {
|
|
1671
|
+
name,
|
|
1672
|
+
kind,
|
|
1673
|
+
headerEnd: headEnd("function_body"),
|
|
1674
|
+
hashNode: node,
|
|
1675
|
+
...swiftArity(node),
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
if (node.type === "init_declaration") {
|
|
1679
|
+
// Initializers carry no name of their own — they are the type's own, so scope
|
|
1680
|
+
// the node under the enclosing type the same way Java's constructor_declaration
|
|
1681
|
+
// does. (A protocol's `init` requirement lands here too, owned by the protocol.)
|
|
1682
|
+
if (!ctx.enclosingClass)
|
|
1683
|
+
return null;
|
|
1684
|
+
return {
|
|
1685
|
+
name: ctx.enclosingClass,
|
|
1686
|
+
kind: "method",
|
|
1687
|
+
headerEnd: headEnd("function_body"),
|
|
1688
|
+
hashNode: node,
|
|
1689
|
+
...swiftArity(node),
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
if (node.type === "typealias_declaration") {
|
|
1693
|
+
const name = typeName();
|
|
1694
|
+
if (!name)
|
|
1695
|
+
return null;
|
|
1696
|
+
return { name, kind: "type", headerEnd: node.endIndex, hashNode: node };
|
|
1697
|
+
}
|
|
1698
|
+
if (node.type === "property_declaration") {
|
|
1699
|
+
// Top-level `let`/`var` only — a stored/computed property inside a type is a
|
|
1700
|
+
// field, not a definition node (no depth tier emits fields), so it must not
|
|
1701
|
+
// become one. `deinit` and `subscript` are likewise skipped: neither is ever
|
|
1702
|
+
// the target of a resolvable call edge, and neither carries a usable name.
|
|
1703
|
+
if (ctx.enclosingKind !== null)
|
|
1704
|
+
return null;
|
|
1705
|
+
const name = node.namedChildren
|
|
1706
|
+
.find((c) => c.type === "pattern")
|
|
1707
|
+
?.namedChildren.find((c) => c.type === "simple_identifier")?.text;
|
|
1708
|
+
if (!name)
|
|
1709
|
+
return null;
|
|
1710
|
+
return { name, kind: "variable", headerEnd: node.endIndex, hashNode: node };
|
|
1711
|
+
}
|
|
1712
|
+
return null;
|
|
1713
|
+
}
|
|
1492
1714
|
/** Java visibility: `public` (or `protected`) on the declaration's own modifier list.
|
|
1493
1715
|
* A package-private or private member is not part of the API surface. Read off the
|
|
1494
1716
|
* `modifiers` child's tokens, ignoring annotations, which live in the same node. */
|
|
@@ -1507,6 +1729,56 @@ function kotlinExported(node) {
|
|
|
1507
1729
|
const vis = mods.namedChildren.find((c) => c.type === "visibility_modifier");
|
|
1508
1730
|
return !vis || vis.text === "public";
|
|
1509
1731
|
}
|
|
1732
|
+
/** Declared parameter count for a Swift callable, for overload disambiguation
|
|
1733
|
+
* (the same role Java's `arity`/`argCount` pair plays). `parameter` nodes are
|
|
1734
|
+
* direct children of the declaration; a default value's `=` sits as a SIBLING
|
|
1735
|
+
* token after its parameter, and a variadic `...` sits inside its parameter.
|
|
1736
|
+
* `arity` is the REQUIRED minimum (parameters minus defaults) and `variadic`
|
|
1737
|
+
* marks any default or variadic parameter, so `narrowByArity`'s at-least
|
|
1738
|
+
* semantics keeps every overload a call of that shape could reach. */
|
|
1739
|
+
function swiftArity(node) {
|
|
1740
|
+
const params = node.children.filter((c) => c.type === "parameter");
|
|
1741
|
+
const defaults = node.children.filter((c) => c.type === "=").length;
|
|
1742
|
+
const hasVariadic = params.some((p) => p.children.some((c) => c.type === "..."));
|
|
1743
|
+
const arity = Math.max(0, params.length - defaults);
|
|
1744
|
+
return hasVariadic || defaults > 0 ? { arity, variadic: true } : { arity };
|
|
1745
|
+
}
|
|
1746
|
+
/** Argument count at a Swift call site: the `value_argument`s plus one for a
|
|
1747
|
+
* trailing closure (`run(x) { … }` calls a two-parameter function). */
|
|
1748
|
+
function swiftArgCount(node) {
|
|
1749
|
+
const suffix = node.namedChildren.find((c) => c.type === "call_suffix");
|
|
1750
|
+
if (!suffix)
|
|
1751
|
+
return undefined;
|
|
1752
|
+
const args = suffix.namedChildren
|
|
1753
|
+
.find((c) => c.type === "value_arguments")
|
|
1754
|
+
?.namedChildren.filter((c) => c.type === "value_argument").length ?? 0;
|
|
1755
|
+
const trailing = suffix.namedChildren.some((c) => c.type === "lambda_literal") ? 1 : 0;
|
|
1756
|
+
return args + trailing;
|
|
1757
|
+
}
|
|
1758
|
+
/** Swift visibility: the default (`internal`) is module-wide, and a repo is
|
|
1759
|
+
* typically one module — so `public` / `open` / `package` / `internal` all count
|
|
1760
|
+
* as API surface, and only an explicit `private` / `fileprivate` hides a
|
|
1761
|
+
* definition. A setter-only restriction (`private(set)`) leaves the getter
|
|
1762
|
+
* visible, so it does not hide the symbol either. */
|
|
1763
|
+
function swiftExported(node) {
|
|
1764
|
+
const mods = node.namedChildren.find((c) => c.type === "modifiers");
|
|
1765
|
+
const vis = mods?.namedChildren.find((c) => c.type === "visibility_modifier");
|
|
1766
|
+
return !vis || (vis.text !== "private" && vis.text !== "fileprivate");
|
|
1767
|
+
}
|
|
1768
|
+
/** The superclass a Swift class declaration names: its FIRST inheritance
|
|
1769
|
+
* specifier — Swift's grammar requires the superclass to precede any protocol
|
|
1770
|
+
* in the `:` list, so when a superclass exists it is always this entry. A
|
|
1771
|
+
* class conforming only to protocols yields that protocol's name instead, but
|
|
1772
|
+
* `super` is illegal in such a class, so no call site ever consults it. Null
|
|
1773
|
+
* for a bare `class Foo` (and for an extension, whose declaration carries no
|
|
1774
|
+
* heritage for the original type — `super` inside one stays unresolved). */
|
|
1775
|
+
function swiftSuperClassName(node) {
|
|
1776
|
+
const spec = node.namedChildren.find((c) => c.type === "inheritance_specifier");
|
|
1777
|
+
const ids = spec?.namedChildren
|
|
1778
|
+
.find((c) => c.type === "user_type")
|
|
1779
|
+
?.namedChildren.filter((c) => c.type === "type_identifier");
|
|
1780
|
+
return ids?.length ? ids[ids.length - 1].text : null;
|
|
1781
|
+
}
|
|
1510
1782
|
/** The receiver's base type name for a Go method, unwrapping a pointer receiver
|
|
1511
1783
|
* (`func (u *User) …` → `User`). Null if it can't be read. */
|
|
1512
1784
|
function goReceiverType(node) {
|
|
@@ -1593,6 +1865,27 @@ function heritageEdges(node, classId, ctx) {
|
|
|
1593
1865
|
}
|
|
1594
1866
|
return edges;
|
|
1595
1867
|
}
|
|
1868
|
+
if (ctx.lang === "swift") {
|
|
1869
|
+
// `class A: B, C` — each `inheritance_specifier` (a direct child of the
|
|
1870
|
+
// declaration; protocols and extensions carry them too) wraps a `user_type`
|
|
1871
|
+
// whose LAST direct `type_identifier` is the bare supertype name: a
|
|
1872
|
+
// module-qualified `Foundation.NSObject` reduces to `NSObject`, and generic
|
|
1873
|
+
// arguments live in nested nodes so they never leak in. Swift cannot say
|
|
1874
|
+
// syntactically whether a specifier is the superclass or a protocol
|
|
1875
|
+
// conformance (that needs the target's kind), so every edge is `extends` —
|
|
1876
|
+
// the same collapse Kotlin's delegation specifiers make.
|
|
1877
|
+
for (const child of node.namedChildren) {
|
|
1878
|
+
if (child.type !== "inheritance_specifier")
|
|
1879
|
+
continue;
|
|
1880
|
+
const ids = child.namedChildren
|
|
1881
|
+
.find((c) => c.type === "user_type")
|
|
1882
|
+
?.namedChildren.filter((c) => c.type === "type_identifier");
|
|
1883
|
+
const t = ids?.length ? ids[ids.length - 1] : undefined;
|
|
1884
|
+
if (t)
|
|
1885
|
+
edges.push({ source: classId, relation: "extends", name: t.text, file: ctx.rel });
|
|
1886
|
+
}
|
|
1887
|
+
return edges;
|
|
1888
|
+
}
|
|
1596
1889
|
if (ctx.lang === "python") {
|
|
1597
1890
|
const supers = node.childForFieldName("superclasses"); // argument_list
|
|
1598
1891
|
for (const c of supers?.namedChildren ?? []) {
|
|
@@ -1758,6 +2051,47 @@ function calleeName(node, lang) {
|
|
|
1758
2051
|
}
|
|
1759
2052
|
return null;
|
|
1760
2053
|
}
|
|
2054
|
+
if (lang === "swift") {
|
|
2055
|
+
// Same shape as Kotlin's: `call_expression` = callee expression + `call_suffix`.
|
|
2056
|
+
// A bare `foo()` names a plain call (this also covers `Animal()` initializer
|
|
2057
|
+
// calls, which have no distinguishing syntax); `obj.foo()` is a
|
|
2058
|
+
// `navigation_expression` whose trailing `navigation_suffix` holds the member
|
|
2059
|
+
// name and whose head is the receiver.
|
|
2060
|
+
const target = node.namedChildren[0];
|
|
2061
|
+
if (target?.type === "simple_identifier")
|
|
2062
|
+
return { name: target.text, viaMember: false };
|
|
2063
|
+
if (target?.type === "navigation_expression") {
|
|
2064
|
+
const suffix = target.namedChildren.find((c) => c.type === "navigation_suffix");
|
|
2065
|
+
const name = suffix?.namedChildren.find((c) => c.type === "simple_identifier");
|
|
2066
|
+
const receiver = target.namedChildren[0];
|
|
2067
|
+
if (!name)
|
|
2068
|
+
return null;
|
|
2069
|
+
if (receiver?.type === "simple_identifier")
|
|
2070
|
+
return { name: name.text, viaMember: true, receiver: receiver.text };
|
|
2071
|
+
if (receiver?.type === "self_expression" || receiver?.type === "super_expression")
|
|
2072
|
+
return {
|
|
2073
|
+
name: name.text,
|
|
2074
|
+
viaMember: true,
|
|
2075
|
+
receiver: receiver.type === "self_expression" ? "self" : "super",
|
|
2076
|
+
};
|
|
2077
|
+
if (receiver?.type === "navigation_expression") {
|
|
2078
|
+
// `self.repo.save()` — one hop off self is a field access and binds like
|
|
2079
|
+
// TS's `this.x`. Deeper chains and call-result receivers carry no
|
|
2080
|
+
// confident local clue, so those fall through with no receiver.
|
|
2081
|
+
const head = receiver.namedChildren[0];
|
|
2082
|
+
const field = receiver.namedChildren
|
|
2083
|
+
.find((c) => c.type === "navigation_suffix")
|
|
2084
|
+
?.namedChildren.find((c) => c.type === "simple_identifier");
|
|
2085
|
+
if (head?.type === "self_expression" && field)
|
|
2086
|
+
return { name: name.text, viaMember: true, receiver: `self.${field.text}` };
|
|
2087
|
+
}
|
|
2088
|
+
// Still a member call even with an unknowable receiver (a chained call, a
|
|
2089
|
+
// literal, a subscript): recvType stays unset and resolve drops it rather
|
|
2090
|
+
// than guessing — same contract as Java's and TS's unknown receivers.
|
|
2091
|
+
return { name: name.text, viaMember: true };
|
|
2092
|
+
}
|
|
2093
|
+
return null;
|
|
2094
|
+
}
|
|
1761
2095
|
if (lang === "php")
|
|
1762
2096
|
return phpCallee(node);
|
|
1763
2097
|
const fn = node.childForFieldName("function");
|
|
@@ -1987,6 +2321,8 @@ function isImport(node, lang) {
|
|
|
1987
2321
|
return node.type === "import_declaration";
|
|
1988
2322
|
if (lang === "kotlin")
|
|
1989
2323
|
return node.type === "import_header";
|
|
2324
|
+
if (lang === "swift")
|
|
2325
|
+
return node.type === "import_declaration";
|
|
1990
2326
|
// PHP: one edge per imported symbol — the clause leaf inside a (possibly
|
|
1991
2327
|
// grouped) `use A\B, C\D;` / `use A\{B, C};` declaration.
|
|
1992
2328
|
if (lang === "php")
|
|
@@ -2030,6 +2366,14 @@ function importSpecifier(node, lang) {
|
|
|
2030
2366
|
// identifier text is already the module path (wildcards dropped, like Java).
|
|
2031
2367
|
return node.namedChildren.find((c) => c.type === "identifier")?.text ?? null;
|
|
2032
2368
|
}
|
|
2369
|
+
if (lang === "swift") {
|
|
2370
|
+
// `import UIKit` / `import struct Foundation.Date` — the dotted path is the
|
|
2371
|
+
// `identifier` child (an import-kind keyword like `struct` is a separate
|
|
2372
|
+
// token). Swift imports name MODULES, not files, so the specifier resolves
|
|
2373
|
+
// to a repo file only when a same-named module target exists; external
|
|
2374
|
+
// frameworks stay as unresolved (but truthful) import intents.
|
|
2375
|
+
return node.namedChildren.find((c) => c.type === "identifier")?.text ?? null;
|
|
2376
|
+
}
|
|
2033
2377
|
const str = node.namedChildren.find((c) => c.type === "string");
|
|
2034
2378
|
if (!str)
|
|
2035
2379
|
return null;
|