universal-ast-mapper 1.27.0 → 2.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.
Files changed (55) hide show
  1. package/BLUEPRINT.md +230 -230
  2. package/CHANGELOG.md +466 -321
  3. package/README.md +878 -877
  4. package/package.json +48 -47
  5. package/scripts/install-skill.mjs +187 -187
  6. package/dist/analysis.js +0 -134
  7. package/dist/callgraph.js +0 -467
  8. package/dist/check.js +0 -112
  9. package/dist/cli.js +0 -1275
  10. package/dist/complexity.js +0 -98
  11. package/dist/config.js +0 -53
  12. package/dist/contextpack.js +0 -79
  13. package/dist/coupling.js +0 -35
  14. package/dist/crosslang.js +0 -425
  15. package/dist/diskcache.js +0 -97
  16. package/dist/explorer.js +0 -123
  17. package/dist/extractors/c.js +0 -204
  18. package/dist/extractors/common.js +0 -56
  19. package/dist/extractors/cpp.js +0 -272
  20. package/dist/extractors/csharp.js +0 -209
  21. package/dist/extractors/go.js +0 -212
  22. package/dist/extractors/java.js +0 -152
  23. package/dist/extractors/kotlin.js +0 -159
  24. package/dist/extractors/php.js +0 -208
  25. package/dist/extractors/python.js +0 -153
  26. package/dist/extractors/ruby.js +0 -146
  27. package/dist/extractors/rust.js +0 -249
  28. package/dist/extractors/swift.js +0 -192
  29. package/dist/extractors/typescript.js +0 -577
  30. package/dist/gitdiff.js +0 -178
  31. package/dist/graph-analysis.js +0 -279
  32. package/dist/graph.js +0 -165
  33. package/dist/html.js +0 -326
  34. package/dist/index.js +0 -1407
  35. package/dist/layers.js +0 -36
  36. package/dist/modulecoupling.js +0 -0
  37. package/dist/parser.js +0 -84
  38. package/dist/pool.js +0 -114
  39. package/dist/prompts.js +0 -67
  40. package/dist/registry.js +0 -87
  41. package/dist/report.js +0 -187
  42. package/dist/resolver.js +0 -222
  43. package/dist/roots.js +0 -47
  44. package/dist/search.js +0 -68
  45. package/dist/semantic.js +0 -365
  46. package/dist/sfc.js +0 -27
  47. package/dist/skeleton.js +0 -132
  48. package/dist/sourcemap.js +0 -60
  49. package/dist/testmap.js +0 -167
  50. package/dist/tsconfig.js +0 -212
  51. package/dist/typeflow.js +0 -124
  52. package/dist/types.js +0 -5
  53. package/dist/unused-params.js +0 -127
  54. package/dist/worker.js +0 -27
  55. package/dist/workspace.js +0 -330
@@ -1,152 +0,0 @@
1
- import { namedChildren, nameOf, headerSignature, leadingComment } from "../parser.js";
2
- import { makeSymbol } from "./common.js";
3
- /* ─── helpers ─────────────────────────────────────────────────────────────── */
4
- function childOfType(node, type) {
5
- for (let i = 0; i < node.childCount; i++) {
6
- const c = node.child(i);
7
- if (c && c.type === type)
8
- return c;
9
- }
10
- return null;
11
- }
12
- function modifiersText(node) {
13
- const m = childOfType(node, "modifiers");
14
- return m ? m.text : "";
15
- }
16
- function vis(node) {
17
- const m = modifiersText(node);
18
- if (/\b(private|protected)\b/.test(m))
19
- return "private";
20
- return "public";
21
- }
22
- function exported(node) {
23
- return /\bpublic\b/.test(modifiersText(node));
24
- }
25
- /* ─── directives (package declaration) ────────────────────────────────────── */
26
- export function extractDirectivesJava(root, _source) {
27
- for (const child of namedChildren(root)) {
28
- if (child.type !== "package_declaration")
29
- continue;
30
- const id = childOfType(child, "scoped_identifier") ?? childOfType(child, "identifier");
31
- if (id)
32
- return [`package:${id.text}`];
33
- }
34
- return [];
35
- }
36
- /* ─── import extraction ───────────────────────────────────────────────────── */
37
- export function extractImportsJava(root, _source) {
38
- const out = [];
39
- for (const child of namedChildren(root)) {
40
- if (child.type !== "import_declaration")
41
- continue;
42
- const isStatic = /\bstatic\b/.test(child.text);
43
- const isWildcard = /\.\s*\*/.test(child.text);
44
- const pathNode = childOfType(child, "scoped_identifier") ?? childOfType(child, "identifier");
45
- const from = pathNode ? pathNode.text : child.text.replace(/^import\s+|;\s*$/g, "").trim();
46
- if (isWildcard) {
47
- out.push({ symbol: "*", from, isNamespaceImport: true, isTypeOnly: !isStatic });
48
- }
49
- else {
50
- const symbol = from.split(".").pop() ?? from;
51
- out.push({ symbol, from, isTypeOnly: !isStatic });
52
- }
53
- }
54
- return out;
55
- }
56
- /* ─── symbol extraction ───────────────────────────────────────────────────── */
57
- export function extractJava(root, _source) {
58
- return collect(namedChildren(root));
59
- }
60
- function collect(nodes) {
61
- const out = [];
62
- for (const n of nodes) {
63
- const res = handle(n);
64
- if (Array.isArray(res))
65
- out.push(...res);
66
- else if (res)
67
- out.push(res);
68
- }
69
- return out;
70
- }
71
- function handle(node) {
72
- switch (node.type) {
73
- case "class_declaration":
74
- case "record_declaration": {
75
- const body = node.childForFieldName("body");
76
- return makeSymbol({
77
- name: nameOf(node) ?? "(class)",
78
- kind: "class",
79
- node,
80
- rawKind: node.type,
81
- visibility: vis(node),
82
- exported: exported(node),
83
- doc: leadingComment(node),
84
- children: body ? collect(namedChildren(body)) : [],
85
- });
86
- }
87
- case "interface_declaration": {
88
- const body = node.childForFieldName("body");
89
- return makeSymbol({
90
- name: nameOf(node) ?? "(interface)",
91
- kind: "interface",
92
- node,
93
- rawKind: node.type,
94
- visibility: vis(node),
95
- exported: exported(node),
96
- doc: leadingComment(node),
97
- children: body ? collect(namedChildren(body)) : [],
98
- });
99
- }
100
- case "enum_declaration": {
101
- const body = node.childForFieldName("body");
102
- return makeSymbol({
103
- name: nameOf(node) ?? "(enum)",
104
- kind: "enum",
105
- node,
106
- rawKind: node.type,
107
- visibility: vis(node),
108
- exported: exported(node),
109
- doc: leadingComment(node),
110
- children: body ? collect(namedChildren(body).filter((c) => c.type !== "enum_constant")) : [],
111
- });
112
- }
113
- case "method_declaration":
114
- case "constructor_declaration":
115
- return makeSymbol({
116
- name: nameOf(node) ?? "(method)",
117
- kind: "method",
118
- node,
119
- rawKind: node.type,
120
- signature: headerSignature(node, node.childForFieldName("body")),
121
- visibility: vis(node),
122
- exported: exported(node),
123
- doc: leadingComment(node),
124
- });
125
- case "field_declaration":
126
- return fieldDeclarators(node);
127
- default:
128
- return null;
129
- }
130
- }
131
- function fieldDeclarators(node) {
132
- const m = modifiersText(node);
133
- const kind = /\bstatic\b/.test(m) && /\bfinal\b/.test(m) ? "const" : "field";
134
- const out = [];
135
- for (const decl of namedChildren(node)) {
136
- if (decl.type !== "variable_declarator")
137
- continue;
138
- const name = nameOf(decl);
139
- if (!name)
140
- continue;
141
- out.push(makeSymbol({
142
- name,
143
- kind,
144
- node: decl,
145
- rawKind: node.type,
146
- signature: node.text.replace(/\s+/g, " ").replace(/;$/, "").trim(),
147
- visibility: vis(node),
148
- exported: exported(node),
149
- }));
150
- }
151
- return out;
152
- }
@@ -1,159 +0,0 @@
1
- import { namedChildren, headerSignature, leadingComment } from "../parser.js";
2
- import { makeSymbol } from "./common.js";
3
- /* ─── helpers ─────────────────────────────────────────────────────────────── */
4
- function childOfType(node, type) {
5
- for (let i = 0; i < node.childCount; i++) {
6
- const c = node.child(i);
7
- if (c && c.type === type)
8
- return c;
9
- }
10
- return null;
11
- }
12
- function firstChildOfTypes(node, types) {
13
- for (let i = 0; i < node.namedChildCount; i++) {
14
- const c = node.namedChild(i);
15
- if (c && types.has(c.type))
16
- return c;
17
- }
18
- return null;
19
- }
20
- const TYPE_NAME_NODES = new Set(["type_identifier", "simple_identifier"]);
21
- const SIMPLE_NAME_NODES = new Set(["simple_identifier"]);
22
- function modifiersText(node) {
23
- const m = childOfType(node, "modifiers");
24
- return m ? m.text : "";
25
- }
26
- function vis(node) {
27
- const m = modifiersText(node);
28
- if (/\b(private|protected|internal)\b/.test(m))
29
- return "private";
30
- return "public"; // Kotlin default is public
31
- }
32
- function exported(node) {
33
- const m = modifiersText(node);
34
- if (/\b(private|protected|internal)\b/.test(m))
35
- return false;
36
- return true;
37
- }
38
- function classKind(node) {
39
- const m = modifiersText(node);
40
- if (/\bdata\b/.test(node.text.slice(0, 80)))
41
- return "class";
42
- if (/\benum\b/.test(node.text.slice(0, 80)))
43
- return "enum";
44
- return "class";
45
- }
46
- /* ─── imports + package ───────────────────────────────────────────────────── */
47
- export function extractDirectivesKotlin(root, _source) {
48
- for (const c of namedChildren(root)) {
49
- if (c.type === "package_header") {
50
- const id = childOfType(c, "identifier");
51
- if (id)
52
- return [`package:${id.text}`];
53
- }
54
- }
55
- return [];
56
- }
57
- export function extractImportsKotlin(root, _source) {
58
- const out = [];
59
- const list = childOfType(root, "import_list");
60
- if (!list)
61
- return out;
62
- for (const h of namedChildren(list)) {
63
- if (h.type !== "import_header")
64
- continue;
65
- const id = childOfType(h, "identifier");
66
- if (!id)
67
- continue;
68
- const isWildcard = /\.\*\s*$/.test(h.text);
69
- const from = id.text;
70
- const sym = isWildcard ? "*" : (from.split(".").pop() ?? from);
71
- out.push({ symbol: sym, from, isNamespaceImport: isWildcard });
72
- }
73
- return out;
74
- }
75
- /* ─── symbol extraction ───────────────────────────────────────────────────── */
76
- export function extractKotlin(root, _source) {
77
- return collect(namedChildren(root), false);
78
- }
79
- function collect(nodes, insideClass) {
80
- const out = [];
81
- for (const n of nodes) {
82
- const res = handle(n, insideClass);
83
- if (res)
84
- out.push(res);
85
- }
86
- return out;
87
- }
88
- function handle(node, insideClass) {
89
- switch (node.type) {
90
- case "class_declaration": {
91
- const nameNode = firstChildOfTypes(node, TYPE_NAME_NODES);
92
- if (!nameNode)
93
- return null;
94
- const body = childOfType(node, "class_body") ?? childOfType(node, "enum_class_body");
95
- return makeSymbol({
96
- name: nameNode.text,
97
- kind: classKind(node),
98
- node,
99
- rawKind: node.type,
100
- visibility: vis(node),
101
- exported: exported(node),
102
- doc: leadingComment(node),
103
- children: body ? collect(namedChildren(body), true) : [],
104
- });
105
- }
106
- case "object_declaration": {
107
- const nameNode = firstChildOfTypes(node, TYPE_NAME_NODES);
108
- if (!nameNode)
109
- return null;
110
- const body = childOfType(node, "class_body");
111
- return makeSymbol({
112
- name: nameNode.text,
113
- kind: "class",
114
- node,
115
- rawKind: node.type,
116
- visibility: vis(node),
117
- exported: exported(node),
118
- doc: leadingComment(node),
119
- children: body ? collect(namedChildren(body), true) : [],
120
- });
121
- }
122
- case "function_declaration": {
123
- const nameNode = firstChildOfTypes(node, SIMPLE_NAME_NODES);
124
- if (!nameNode)
125
- return null;
126
- const body = childOfType(node, "function_body");
127
- return makeSymbol({
128
- name: nameNode.text,
129
- kind: insideClass ? "method" : "function",
130
- node,
131
- rawKind: node.type,
132
- signature: headerSignature(node, body),
133
- visibility: vis(node),
134
- exported: exported(node),
135
- doc: leadingComment(node),
136
- });
137
- }
138
- case "property_declaration": {
139
- // variable_declaration → simple_identifier
140
- const vd = childOfType(node, "variable_declaration");
141
- const nameNode = vd ? firstChildOfTypes(vd, SIMPLE_NAME_NODES) : firstChildOfTypes(node, SIMPLE_NAME_NODES);
142
- if (!nameNode)
143
- return null;
144
- const m = modifiersText(node);
145
- const kind = insideClass ? "field" : (/\bconst\b/.test(m) ? "const" : "var");
146
- return makeSymbol({
147
- name: nameNode.text,
148
- kind,
149
- node,
150
- rawKind: node.type,
151
- signature: node.text.replace(/\s+/g, " ").trim(),
152
- visibility: vis(node),
153
- exported: exported(node),
154
- });
155
- }
156
- default:
157
- return null;
158
- }
159
- }
@@ -1,208 +0,0 @@
1
- import { namedChildren, nameOf, headerSignature, leadingComment } from "../parser.js";
2
- import { makeSymbol } from "./common.js";
3
- // ─── PHP extractor (tree-sitter-php) ──────────────────────────────────────────
4
- export function extractPhp(root, _source) {
5
- return collect(namedChildren(root));
6
- }
7
- function collect(nodes) {
8
- const out = [];
9
- for (const n of nodes) {
10
- const sym = handle(n);
11
- if (sym)
12
- out.push(sym);
13
- else if (n.type === "namespace_definition") {
14
- // `namespace Foo;` (no braces) — siblings follow; emit the namespace marker.
15
- const name = nameOf(n)?.replace(/\s+/g, "") ?? namespaceName(n);
16
- if (name) {
17
- out.push(makeSymbol({ name, kind: "namespace", node: n, rawKind: n.type }));
18
- }
19
- const body = n.childForFieldName("body");
20
- if (body)
21
- out[out.length - 1].children = collect(namedChildren(body));
22
- }
23
- else if (n.type === "expression_statement" || n.type === "if_statement") {
24
- // skip — top-level statements
25
- }
26
- }
27
- return out;
28
- }
29
- function namespaceName(n) {
30
- for (const c of namedChildren(n)) {
31
- if (c.type === "namespace_name")
32
- return c.text.replace(/\s+/g, "");
33
- }
34
- return null;
35
- }
36
- function phpVisibility(node) {
37
- for (const c of namedChildren(node)) {
38
- if (c.type === "visibility_modifier") {
39
- const t = c.text;
40
- if (t === "private" || t === "protected")
41
- return "private";
42
- return "public";
43
- }
44
- }
45
- return "public";
46
- }
47
- function classLike(node, kind) {
48
- const name = nameOf(node) ?? "(class)";
49
- const body = node.childForFieldName("body") ?? findChild(node, "declaration_list");
50
- return makeSymbol({
51
- name,
52
- kind,
53
- node,
54
- rawKind: node.type,
55
- signature: headerSignature(node, body),
56
- doc: leadingComment(node),
57
- children: body ? collect(namedChildren(body)) : [],
58
- });
59
- }
60
- function findChild(node, type) {
61
- for (const c of namedChildren(node))
62
- if (c.type === type)
63
- return c;
64
- return null;
65
- }
66
- function handle(node) {
67
- switch (node.type) {
68
- case "class_declaration":
69
- case "trait_declaration":
70
- return classLike(node, "class");
71
- case "interface_declaration":
72
- return classLike(node, "interface");
73
- case "enum_declaration": {
74
- const body = findChild(node, "enum_declaration_list");
75
- return makeSymbol({
76
- name: nameOf(node) ?? "(enum)",
77
- kind: "enum",
78
- node,
79
- rawKind: node.type,
80
- signature: headerSignature(node, body),
81
- doc: leadingComment(node),
82
- children: body
83
- ? namedChildren(body)
84
- .filter((c) => c.type === "enum_case")
85
- .map((c) => makeSymbol({ name: nameOf(c) ?? c.text, kind: "const", node: c, rawKind: c.type }))
86
- : [],
87
- });
88
- }
89
- case "function_definition": {
90
- const body = node.childForFieldName("body") ?? findChild(node, "compound_statement");
91
- return makeSymbol({
92
- name: nameOf(node) ?? "(function)",
93
- kind: "function",
94
- node,
95
- rawKind: node.type,
96
- signature: headerSignature(node, body),
97
- doc: leadingComment(node),
98
- });
99
- }
100
- case "method_declaration": {
101
- const body = node.childForFieldName("body") ?? findChild(node, "compound_statement");
102
- const vis = phpVisibility(node);
103
- return makeSymbol({
104
- name: nameOf(node) ?? "(method)",
105
- kind: "method",
106
- node,
107
- rawKind: node.type,
108
- signature: headerSignature(node, body),
109
- visibility: vis,
110
- exported: vis === "public",
111
- doc: leadingComment(node),
112
- });
113
- }
114
- case "const_declaration": {
115
- const el = findChild(node, "const_element");
116
- const name = el ? namedChildren(el)[0]?.text : null;
117
- if (!name)
118
- return null;
119
- const vis = phpVisibility(node);
120
- return makeSymbol({
121
- name,
122
- kind: "const",
123
- node,
124
- rawKind: node.type,
125
- visibility: vis,
126
- exported: vis === "public",
127
- });
128
- }
129
- case "property_declaration": {
130
- const decl = findChild(node, "property_element");
131
- const name = decl?.text.replace(/\s*=.*$/, "").trim();
132
- if (!name)
133
- return null;
134
- const vis = phpVisibility(node);
135
- return makeSymbol({
136
- name,
137
- kind: "field",
138
- node,
139
- rawKind: node.type,
140
- visibility: vis,
141
- exported: vis === "public",
142
- });
143
- }
144
- default:
145
- return null;
146
- }
147
- }
148
- // ─── Import extraction ────────────────────────────────────────────────────────
149
- // `use App\Models\User;`, grouped `use App\{A, B};`, and require/include calls.
150
- export function extractImportsPhp(root, _source) {
151
- const imports = [];
152
- walk(root, imports, 0);
153
- return imports;
154
- }
155
- function walk(node, out, depth) {
156
- if (depth > 4)
157
- return;
158
- for (const c of namedChildren(node)) {
159
- if (c.type === "namespace_use_declaration")
160
- parseUse(c, out);
161
- else if (c.type === "require_expression" ||
162
- c.type === "require_once_expression" ||
163
- c.type === "include_expression" ||
164
- c.type === "include_once_expression") {
165
- const str = findString(c);
166
- if (str)
167
- out.push({ symbol: "*", from: str, isSideEffect: true });
168
- }
169
- else {
170
- walk(c, out, depth + 1);
171
- }
172
- }
173
- }
174
- function findString(node) {
175
- for (const c of namedChildren(node)) {
176
- if (c.type === "string")
177
- return c.text.replace(/^['"]|['"]$/g, "");
178
- const deep = findString(c);
179
- if (deep)
180
- return deep;
181
- }
182
- return null;
183
- }
184
- function parseUse(node, out) {
185
- let groupBase = null;
186
- for (const c of namedChildren(node)) {
187
- if (c.type === "namespace_name")
188
- groupBase = c.text.replace(/\s+/g, "");
189
- else if (c.type === "namespace_use_clause") {
190
- const qn = c.text.replace(/\s+as\s+.*$/, "").replace(/\s+/g, "");
191
- const alias = /\s+as\s+(\w+)/.exec(c.text)?.[1];
192
- const leaf = qn.split("\\").pop() ?? qn;
193
- const imp = { symbol: leaf, from: qn };
194
- if (alias)
195
- imp.alias = alias;
196
- out.push(imp);
197
- }
198
- else if (c.type === "namespace_use_group") {
199
- for (const g of namedChildren(c)) {
200
- if (g.type !== "namespace_use_group_clause")
201
- continue;
202
- const txt = g.text.replace(/\s+/g, "");
203
- const leaf = txt.split("\\").pop() ?? txt;
204
- out.push({ symbol: leaf, from: groupBase ? `${groupBase}\\${txt}` : txt });
205
- }
206
- }
207
- }
208
- }
@@ -1,153 +0,0 @@
1
- import { namedChildren, nameOf, headerSignature } from "../parser.js";
2
- import { makeSymbol, pythonVisibility } from "./common.js";
3
- export function extractPython(root, _source) {
4
- return collect(namedChildren(root), false);
5
- }
6
- function collect(nodes, insideClass) {
7
- const out = [];
8
- for (const n of nodes) {
9
- const res = handle(n, insideClass);
10
- if (res)
11
- out.push(res);
12
- }
13
- return out;
14
- }
15
- function handle(node, insideClass) {
16
- if (node.type === "decorated_definition") {
17
- const inner = innerDefinition(node);
18
- if (!inner)
19
- return null;
20
- const sym = handle(inner, insideClass);
21
- if (sym) {
22
- const decs = namedChildren(node)
23
- .filter((c) => c.type === "decorator")
24
- .map((d) => d.text.replace(/^@\s*/, "").replace(/\s+/g, " ").trim())
25
- .filter((t) => t.length > 0);
26
- if (decs.length > 0)
27
- sym.decorators = decs;
28
- }
29
- return sym;
30
- }
31
- if (node.type === "class_definition") {
32
- const name = nameOf(node) ?? "(class)";
33
- const body = node.childForFieldName("body");
34
- const children = body ? collect(namedChildren(body), true) : [];
35
- return makeSymbol({
36
- name,
37
- kind: "class",
38
- node,
39
- rawKind: node.type,
40
- visibility: pythonVisibility(name),
41
- exported: pythonVisibility(name) === "public",
42
- doc: body ? docstring(body) : null,
43
- children,
44
- });
45
- }
46
- if (node.type === "function_definition") {
47
- const name = nameOf(node) ?? "(function)";
48
- const body = node.childForFieldName("body");
49
- return makeSymbol({
50
- name,
51
- kind: insideClass ? "method" : "function",
52
- node,
53
- rawKind: node.type,
54
- signature: headerSignature(node, body),
55
- visibility: pythonVisibility(name),
56
- exported: pythonVisibility(name) === "public",
57
- doc: body ? docstring(body) : null,
58
- });
59
- }
60
- return null;
61
- }
62
- function innerDefinition(decorated) {
63
- const byField = decorated.childForFieldName("definition");
64
- if (byField)
65
- return byField;
66
- for (const c of namedChildren(decorated)) {
67
- if (c.type === "function_definition" || c.type === "class_definition")
68
- return c;
69
- }
70
- return null;
71
- }
72
- // ─── Import extraction ────────────────────────────────────────────────────────
73
- export function extractImportsPython(root, _source) {
74
- const imports = [];
75
- for (const child of namedChildren(root)) {
76
- if (child.type === "import_statement")
77
- parseSimpleImport(child, imports);
78
- else if (child.type === "import_from_statement")
79
- parseFromImport(child, imports);
80
- }
81
- return imports;
82
- }
83
- function parseSimpleImport(node, out) {
84
- for (let i = 0; i < node.namedChildCount; i++) {
85
- const c = node.namedChild(i);
86
- if (!c)
87
- continue;
88
- if (c.type === "dotted_name" || c.type === "identifier") {
89
- out.push({ symbol: c.text, from: c.text });
90
- }
91
- else if (c.type === "aliased_import") {
92
- const nameNode = c.childForFieldName("name");
93
- const aliasNode = c.childForFieldName("alias");
94
- if (nameNode) {
95
- const imp = { symbol: nameNode.text, from: nameNode.text };
96
- if (aliasNode)
97
- imp.alias = aliasNode.text;
98
- out.push(imp);
99
- }
100
- }
101
- }
102
- }
103
- /**
104
- * Convert Python import path to a JS-style relative path.
105
- * ".models" → "./models", "..utils" → "../utils", "os" → "os" (external).
106
- */
107
- function pythonFromPath(raw) {
108
- const m = raw.match(/^(\.+)(.*)/);
109
- if (!m)
110
- return raw; // absolute/external import
111
- const dotCount = m[1].length;
112
- const rest = m[2]; // module name after the dots, e.g. "models" or ""
113
- const upDirs = dotCount === 1 ? "." : Array(dotCount - 1).fill("..").join("/");
114
- return rest ? `${upDirs}/${rest.replace(/\./g, "/")}` : upDirs;
115
- }
116
- function parseFromImport(node, out) {
117
- const moduleNode = node.childForFieldName("module_name");
118
- const from = moduleNode ? pythonFromPath(moduleNode.text) : ".";
119
- for (let i = 0; i < node.namedChildCount; i++) {
120
- const c = node.namedChild(i);
121
- if (!c || c === moduleNode)
122
- continue;
123
- if (c.type === "wildcard_import") {
124
- out.push({ symbol: "*", from, isNamespaceImport: true });
125
- }
126
- else if (c.type === "relative_import" || c.type === "dotted_name") {
127
- // skip — these are part of the module path, not the imported names
128
- }
129
- else if (c.type === "identifier") {
130
- out.push({ symbol: c.text, from });
131
- }
132
- else if (c.type === "aliased_import") {
133
- const nameNode = c.childForFieldName("name");
134
- const aliasNode = c.childForFieldName("alias");
135
- if (nameNode) {
136
- const imp = { symbol: nameNode.text, from };
137
- if (aliasNode)
138
- imp.alias = aliasNode.text;
139
- out.push(imp);
140
- }
141
- }
142
- }
143
- }
144
- /** Extract a leading triple-quoted docstring from a `block`. */
145
- function docstring(body) {
146
- const first = body.namedChild(0);
147
- if (!first || first.type !== "expression_statement")
148
- return null;
149
- const str = first.namedChild(0);
150
- if (!str || str.type !== "string")
151
- return null;
152
- return str.text.replace(/^[rbuRBU]*("""|'''|"|')/, "").replace(/("""|'''|"|')$/, "").trim().slice(0, 500);
153
- }