lockwire 0.1.29 → 0.1.31
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 +2 -1
- package/dist/actions.d.ts +25 -6
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +168 -40
- package/dist/actions.js.map +1 -1
- package/dist/cli.js +44 -7
- package/dist/cli.js.map +1 -1
- package/dist/docindex.d.ts +1 -0
- package/dist/docindex.d.ts.map +1 -1
- package/dist/docindex.js +4 -2
- package/dist/docindex.js.map +1 -1
- package/dist/extract-v2.d.ts +34 -0
- package/dist/extract-v2.d.ts.map +1 -0
- package/dist/extract-v2.js +705 -0
- package/dist/extract-v2.js.map +1 -0
- package/dist/extract.d.ts +8 -2
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +13 -2
- package/dist/extract.js.map +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +8 -2
- package/dist/format.js.map +1 -1
- package/dist/grammar.d.ts +1 -0
- package/dist/grammar.d.ts.map +1 -1
- package/dist/grammar.js +7 -1
- package/dist/grammar.js.map +1 -1
- package/dist/lockfile.d.ts +1 -1
- package/dist/lockfile.d.ts.map +1 -1
- package/dist/lockfile.js +10 -2
- package/dist/lockfile.js.map +1 -1
- package/dist/markers.d.ts +2 -0
- package/dist/markers.d.ts.map +1 -1
- package/dist/markers.js +85 -28
- package/dist/markers.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +2 -4
- package/dist/mcp-server.js.map +1 -1
- package/dist/repo.d.ts +12 -2
- package/dist/repo.d.ts.map +1 -1
- package/dist/repo.js +22 -3
- package/dist/repo.js.map +1 -1
- package/dist/tasks.js +4 -4
- package/dist/tasks.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/values.d.ts +1 -1
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +36 -11
- package/dist/values.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
/** A node as its tokens: comments and layout dropped, a trailing comma before a closer dropped. */
|
|
2
|
+
export function canon(node) {
|
|
3
|
+
const tokens = [];
|
|
4
|
+
const walk = (n) => {
|
|
5
|
+
if (n.isExtra)
|
|
6
|
+
return; // comments
|
|
7
|
+
if (n.childCount === 0) {
|
|
8
|
+
const t = n.text.trim();
|
|
9
|
+
if (t)
|
|
10
|
+
tokens.push(t);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
for (const c of n.children)
|
|
14
|
+
if (c)
|
|
15
|
+
walk(c);
|
|
16
|
+
};
|
|
17
|
+
walk(node);
|
|
18
|
+
const kept = tokens.filter((t, i) => !(t === "," && /^[)\]}>]$/.test(tokens[i + 1] ?? "")));
|
|
19
|
+
// A space only where two words meet (`async def`, `x as y`), so it reads like the source: `f(a:number)`.
|
|
20
|
+
let out = "";
|
|
21
|
+
for (const t of kept)
|
|
22
|
+
out += out && /[\w$]$/.test(out) && /^[\w$]/.test(t) ? ` ${t}` : t;
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
const FUNCTION_VALUES = new Set([
|
|
26
|
+
"arrow_function",
|
|
27
|
+
"function_expression",
|
|
28
|
+
"function",
|
|
29
|
+
"generator_function",
|
|
30
|
+
]);
|
|
31
|
+
const FUNCTION_LIKE = {
|
|
32
|
+
typescript: new Set([
|
|
33
|
+
"function_declaration",
|
|
34
|
+
"function_expression",
|
|
35
|
+
"function",
|
|
36
|
+
"generator_function_declaration",
|
|
37
|
+
"generator_function",
|
|
38
|
+
"arrow_function",
|
|
39
|
+
"method_definition",
|
|
40
|
+
]),
|
|
41
|
+
tsx: new Set([
|
|
42
|
+
"function_declaration",
|
|
43
|
+
"function_expression",
|
|
44
|
+
"function",
|
|
45
|
+
"generator_function_declaration",
|
|
46
|
+
"generator_function",
|
|
47
|
+
"arrow_function",
|
|
48
|
+
"method_definition",
|
|
49
|
+
]),
|
|
50
|
+
javascript: new Set([
|
|
51
|
+
"function_declaration",
|
|
52
|
+
"function_expression",
|
|
53
|
+
"function",
|
|
54
|
+
"generator_function_declaration",
|
|
55
|
+
"generator_function",
|
|
56
|
+
"arrow_function",
|
|
57
|
+
"method_definition",
|
|
58
|
+
]),
|
|
59
|
+
python: new Set(["function_definition", "lambda"]),
|
|
60
|
+
};
|
|
61
|
+
const CLASS_LIKE = new Set([
|
|
62
|
+
"class_declaration",
|
|
63
|
+
"abstract_class_declaration",
|
|
64
|
+
"class",
|
|
65
|
+
"interface_declaration",
|
|
66
|
+
"class_definition",
|
|
67
|
+
]);
|
|
68
|
+
const NAMESPACE = new Set(["internal_module", "module"]);
|
|
69
|
+
function hasAncestorIn(node, types) {
|
|
70
|
+
for (let n = node.parent; n; n = n.parent)
|
|
71
|
+
if (types.has(n.type))
|
|
72
|
+
return true;
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
/** `NA.NB` for a declaration inside `namespace NA { namespace NB { … } }`. */
|
|
76
|
+
function namespacePrefix(node) {
|
|
77
|
+
const names = [];
|
|
78
|
+
for (let n = node.parent; n; n = n.parent)
|
|
79
|
+
if (NAMESPACE.has(n.type)) {
|
|
80
|
+
const name = n.childForFieldName("name");
|
|
81
|
+
if (name)
|
|
82
|
+
names.unshift(name.text);
|
|
83
|
+
}
|
|
84
|
+
return names.join(".");
|
|
85
|
+
}
|
|
86
|
+
/** `export …` directly, or through a declaration wrapper (`export const f = …`, `export declare function`). */
|
|
87
|
+
function isExported(node) {
|
|
88
|
+
for (let n = node.parent, i = 0; n && i < 4; n = n.parent, i++) {
|
|
89
|
+
if (n.type === "export_statement")
|
|
90
|
+
return true;
|
|
91
|
+
if (![
|
|
92
|
+
"lexical_declaration",
|
|
93
|
+
"variable_declaration",
|
|
94
|
+
"variable_declarator",
|
|
95
|
+
"ambient_declaration",
|
|
96
|
+
"call_expression",
|
|
97
|
+
"arguments",
|
|
98
|
+
].includes(n.type))
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
/** Decorators written before a declaration: Python's `decorated_definition`, TS's on the class or export, or class-body siblings for a member. */
|
|
104
|
+
function decoratorsOf(def, member) {
|
|
105
|
+
const out = [];
|
|
106
|
+
const parent = member.parent;
|
|
107
|
+
if (parent?.type === "decorated_definition" || parent?.type === "export_statement")
|
|
108
|
+
for (const c of parent.namedChildren)
|
|
109
|
+
if (c?.type === "decorator")
|
|
110
|
+
out.push(canon(c));
|
|
111
|
+
for (const c of def.namedChildren)
|
|
112
|
+
if (c?.type === "decorator")
|
|
113
|
+
out.push(canon(c));
|
|
114
|
+
// A TS class member's decorators are the class body's children right before it.
|
|
115
|
+
if (parent?.type === "class_body")
|
|
116
|
+
for (let s = member.previousSibling; s?.type === "decorator"; s = s.previousSibling)
|
|
117
|
+
out.push(canon(s));
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
/** Keyword modifiers written before the name: `static`, `async`, `get`, `private`, `readonly`, `*`, `abstract`… */
|
|
121
|
+
function leadingModifiers(node) {
|
|
122
|
+
if (!node)
|
|
123
|
+
return [];
|
|
124
|
+
const out = [];
|
|
125
|
+
const name = node.childForFieldName("name");
|
|
126
|
+
for (const c of node.children) {
|
|
127
|
+
if (!c || (name && c.startIndex >= name.startIndex))
|
|
128
|
+
break;
|
|
129
|
+
if (c.type === "decorator" || c.isExtra)
|
|
130
|
+
continue;
|
|
131
|
+
// Only keywords: an unnamed function (an arrow) has no name to stop at, so stop at its parameters.
|
|
132
|
+
if (c.isNamed && !["accessibility_modifier", "override_modifier"].includes(c.type))
|
|
133
|
+
break;
|
|
134
|
+
const t = canon(c);
|
|
135
|
+
if ([
|
|
136
|
+
"function",
|
|
137
|
+
"def",
|
|
138
|
+
"class",
|
|
139
|
+
"interface",
|
|
140
|
+
"type",
|
|
141
|
+
"enum",
|
|
142
|
+
"=",
|
|
143
|
+
"const",
|
|
144
|
+
"let",
|
|
145
|
+
"var",
|
|
146
|
+
].includes(t))
|
|
147
|
+
continue;
|
|
148
|
+
if (t)
|
|
149
|
+
out.push(t);
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
const NAME_SLOT = "\u0000name\u0000";
|
|
154
|
+
function sigOf(def, name, lang) {
|
|
155
|
+
const n = def.node;
|
|
156
|
+
const mods = [
|
|
157
|
+
...(def.exported ? ["export"] : []),
|
|
158
|
+
...def.extra,
|
|
159
|
+
...def.decorators.map((d) => (d.startsWith("@") ? d : `@${d}`)),
|
|
160
|
+
...leadingModifiers(n),
|
|
161
|
+
];
|
|
162
|
+
const tail = ` [${mods.sort().join(",")}]`;
|
|
163
|
+
if (def.kind === "type") {
|
|
164
|
+
// An interface, type alias or enum is all contract: its whole declaration, name swapped in.
|
|
165
|
+
const nameNode = n.childForFieldName("name");
|
|
166
|
+
const body = [...n.children]
|
|
167
|
+
.filter((c) => c !== null && c !== nameNode && !c.isExtra && c.type !== "decorator")
|
|
168
|
+
.map(canon)
|
|
169
|
+
.join(" ");
|
|
170
|
+
return `${n.type} ${name} ${body}${tail}`;
|
|
171
|
+
}
|
|
172
|
+
const typeParams = n.childForFieldName("type_parameters");
|
|
173
|
+
const tp = typeParams ? canon(typeParams) : "";
|
|
174
|
+
if (def.kind === "class") {
|
|
175
|
+
const heritage = lang === "python"
|
|
176
|
+
? n.childForFieldName("superclasses")
|
|
177
|
+
: (n.namedChildren.find((c) => c?.type === "class_heritage") ?? null);
|
|
178
|
+
return `class ${name}${tp}${heritage ? ` ${canon(heritage)}` : ""}${tail}`;
|
|
179
|
+
}
|
|
180
|
+
const params = n.childForFieldName("parameters");
|
|
181
|
+
const single = lang === "python" ? null : n.childForFieldName("parameter");
|
|
182
|
+
const ps = params ? canon(params) : single ? `(${canon(single)})` : "()";
|
|
183
|
+
const ret = n.childForFieldName("return_type");
|
|
184
|
+
const rs = ret ? canon(ret).replace(/^:\s*/, "") : "";
|
|
185
|
+
return `fn ${name}${tp}${ps}${rs ? `->${rs}` : ""}${tail}`;
|
|
186
|
+
}
|
|
187
|
+
// -------------------------------------------------------------------------------------------------
|
|
188
|
+
// Locals, body and deps
|
|
189
|
+
/** Identifiers a binding pattern declares: `a`, `[k, v]`, `{ b: c, ...r }`, `(x, y)`; never a property key. */
|
|
190
|
+
function patternNames(pattern, out) {
|
|
191
|
+
if (!pattern)
|
|
192
|
+
return;
|
|
193
|
+
if (pattern.type === "identifier") {
|
|
194
|
+
out.add(pattern.text);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (pattern.type === "shorthand_property_identifier_pattern")
|
|
198
|
+
return; // `{a}` reads property a
|
|
199
|
+
for (const c of pattern.namedChildren) {
|
|
200
|
+
if (!c)
|
|
201
|
+
continue;
|
|
202
|
+
if (c.type === "pair_pattern")
|
|
203
|
+
patternNames(c.childForFieldName("value"), out);
|
|
204
|
+
else if (c.type === "property_identifier")
|
|
205
|
+
continue;
|
|
206
|
+
else
|
|
207
|
+
patternNames(c, out);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function paramNames(params, lang, out) {
|
|
211
|
+
if (!params)
|
|
212
|
+
return;
|
|
213
|
+
if (params.type === "identifier") {
|
|
214
|
+
out.add(params.text);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
for (const p of params.namedChildren) {
|
|
218
|
+
if (!p)
|
|
219
|
+
continue;
|
|
220
|
+
if (lang === "python") {
|
|
221
|
+
const name = p.type === "identifier"
|
|
222
|
+
? p
|
|
223
|
+
: (p.childForFieldName("name") ??
|
|
224
|
+
p.namedChildren.find((c) => c?.type === "identifier") ??
|
|
225
|
+
null);
|
|
226
|
+
if (name?.type === "identifier")
|
|
227
|
+
out.add(name.text);
|
|
228
|
+
}
|
|
229
|
+
else if (p.type === "required_parameter" || p.type === "optional_parameter") {
|
|
230
|
+
patternNames(p.childForFieldName("pattern"), out);
|
|
231
|
+
}
|
|
232
|
+
else if (p.type === "assignment_pattern") {
|
|
233
|
+
patternNames(p.childForFieldName("left"), out);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
patternNames(p, out);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function collectLocals(subtree, outerParams, lang) {
|
|
241
|
+
const locals = new Set();
|
|
242
|
+
paramNames(outerParams, lang, locals);
|
|
243
|
+
if (lang === "python") {
|
|
244
|
+
for (const fn of subtree.descendantsOfType(["function_definition", "lambda"]))
|
|
245
|
+
paramNames(fn?.childForFieldName("parameters") ?? null, lang, locals);
|
|
246
|
+
for (const n of subtree.descendantsOfType(["assignment", "augmented_assignment"]))
|
|
247
|
+
patternNames(n?.childForFieldName("left") ?? null, locals);
|
|
248
|
+
for (const n of subtree.descendantsOfType(["for_statement", "for_in_clause"]))
|
|
249
|
+
patternNames(n?.childForFieldName("left") ?? null, locals);
|
|
250
|
+
for (const n of subtree.descendantsOfType(["as_pattern_target"]))
|
|
251
|
+
for (const c of n?.namedChildren ?? [])
|
|
252
|
+
patternNames(c, locals);
|
|
253
|
+
for (const n of subtree.descendantsOfType(["named_expression"]))
|
|
254
|
+
patternNames(n?.childForFieldName("name") ?? null, locals);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
for (const n of subtree.descendantsOfType(["variable_declarator"]))
|
|
258
|
+
patternNames(n?.childForFieldName("name") ?? null, locals);
|
|
259
|
+
for (const n of subtree.descendantsOfType([...FUNCTION_LIKE[lang]])) {
|
|
260
|
+
paramNames(n?.childForFieldName("parameters") ?? null, lang, locals);
|
|
261
|
+
const single = n?.childForFieldName("parameter");
|
|
262
|
+
if (single)
|
|
263
|
+
patternNames(single, locals);
|
|
264
|
+
}
|
|
265
|
+
for (const n of subtree.descendantsOfType(["for_in_statement"]))
|
|
266
|
+
patternNames(n?.childForFieldName("left") ?? null, locals);
|
|
267
|
+
for (const n of subtree.descendantsOfType(["catch_clause"]))
|
|
268
|
+
patternNames(n?.childForFieldName("parameter") ?? null, locals);
|
|
269
|
+
}
|
|
270
|
+
return locals;
|
|
271
|
+
}
|
|
272
|
+
/** An identifier that names a value, not a property, attribute or keyword argument. */
|
|
273
|
+
function isValueIdentifier(node, lang) {
|
|
274
|
+
if (node.type !== "identifier")
|
|
275
|
+
return false;
|
|
276
|
+
const parent = node.parent;
|
|
277
|
+
if (!parent)
|
|
278
|
+
return true;
|
|
279
|
+
if (lang === "python") {
|
|
280
|
+
if (parent.type === "attribute" && parent.childForFieldName("attribute")?.id === node.id)
|
|
281
|
+
return false;
|
|
282
|
+
if (parent.type === "keyword_argument" && parent.childForFieldName("name")?.id === node.id)
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
function serializeBody(node, locals, normalizeLocals, lang, aliasOf) {
|
|
288
|
+
if (node.isExtra)
|
|
289
|
+
return "";
|
|
290
|
+
// Grouping parentheses carry no meaning (the tree's shape already has the precedence), and
|
|
291
|
+
// formatters add and remove them to wrap lines.
|
|
292
|
+
if (node.type === "parenthesized_expression" && node.namedChildCount === 1) {
|
|
293
|
+
const inner = node.namedChildren[0];
|
|
294
|
+
if (inner && !inner.isExtra)
|
|
295
|
+
return serializeBody(inner, locals, normalizeLocals, lang, aliasOf);
|
|
296
|
+
}
|
|
297
|
+
if (node.childCount === 0) {
|
|
298
|
+
if (node.isNamed) {
|
|
299
|
+
if (normalizeLocals && locals.has(node.text) && isValueIdentifier(node, lang)) {
|
|
300
|
+
let idx = aliasOf.get(node.text);
|
|
301
|
+
if (idx === undefined) {
|
|
302
|
+
idx = aliasOf.size + 1;
|
|
303
|
+
aliasOf.set(node.text, idx);
|
|
304
|
+
}
|
|
305
|
+
return `$${idx}`;
|
|
306
|
+
}
|
|
307
|
+
return `${node.type}:${node.text}`;
|
|
308
|
+
}
|
|
309
|
+
if (node.type === "," || node.type === ";")
|
|
310
|
+
return "";
|
|
311
|
+
return node.type;
|
|
312
|
+
}
|
|
313
|
+
const parts = [];
|
|
314
|
+
for (const child of node.children) {
|
|
315
|
+
if (!child)
|
|
316
|
+
continue;
|
|
317
|
+
const s = serializeBody(child, locals, normalizeLocals, lang, aliasOf);
|
|
318
|
+
if (s)
|
|
319
|
+
parts.push(s);
|
|
320
|
+
}
|
|
321
|
+
return `(${node.type} ${parts.join(" ")})`;
|
|
322
|
+
}
|
|
323
|
+
/** A Python function's docstring is documentation, not behaviour. */
|
|
324
|
+
function withoutDocstring(body, lang) {
|
|
325
|
+
const children = body.children.filter((c) => c !== null);
|
|
326
|
+
if (lang !== "python")
|
|
327
|
+
return children;
|
|
328
|
+
const first = body.namedChildren.find((c) => c !== null && !c.isExtra);
|
|
329
|
+
const isDoc = first?.type === "expression_statement" &&
|
|
330
|
+
first.namedChildren.length === 1 &&
|
|
331
|
+
first.namedChildren[0]?.type === "string";
|
|
332
|
+
return isDoc ? children.filter((c) => c.id !== first.id) : children;
|
|
333
|
+
}
|
|
334
|
+
/** A call's callee by structure: `client.get(url).json` is `$.get().json` when `client` is a local. */
|
|
335
|
+
function calleeName(n, locals, lang) {
|
|
336
|
+
switch (n.type) {
|
|
337
|
+
case "identifier":
|
|
338
|
+
if (lang === "python" && n.text === "self")
|
|
339
|
+
return "self";
|
|
340
|
+
return locals.has(n.text) ? "$" : n.text;
|
|
341
|
+
case "this":
|
|
342
|
+
case "super":
|
|
343
|
+
return n.type;
|
|
344
|
+
case "member_expression":
|
|
345
|
+
case "attribute": {
|
|
346
|
+
const object = n.childForFieldName("object");
|
|
347
|
+
const prop = n.childForFieldName(n.type === "attribute" ? "attribute" : "property");
|
|
348
|
+
const head = object ? calleeName(object, locals, lang) : "?";
|
|
349
|
+
return `${["this", "self"].includes(head) ? "" : `${head}.`}${prop?.text ?? "?"}`;
|
|
350
|
+
}
|
|
351
|
+
case "call_expression":
|
|
352
|
+
case "call": {
|
|
353
|
+
const fn = n.childForFieldName("function");
|
|
354
|
+
return `${fn ? calleeName(fn, locals, lang) : "?"}()`;
|
|
355
|
+
}
|
|
356
|
+
case "subscript_expression":
|
|
357
|
+
case "subscript": {
|
|
358
|
+
const object = n.childForFieldName(n.type === "subscript" ? "value" : "object");
|
|
359
|
+
return `${object ? calleeName(object, locals, lang) : "?"}[]`;
|
|
360
|
+
}
|
|
361
|
+
case "parenthesized_expression": {
|
|
362
|
+
const inner = n.namedChildren[0];
|
|
363
|
+
return inner ? calleeName(inner, locals, lang) : "?";
|
|
364
|
+
}
|
|
365
|
+
case "non_null_expression": {
|
|
366
|
+
const inner = n.namedChildren[0];
|
|
367
|
+
return inner ? calleeName(inner, locals, lang) : "?";
|
|
368
|
+
}
|
|
369
|
+
default:
|
|
370
|
+
return n.type;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function collectDeps(roots, lang, importedNames, locals) {
|
|
374
|
+
const deps = new Set();
|
|
375
|
+
const callTypes = lang === "python" ? ["call"] : ["call_expression", "new_expression"];
|
|
376
|
+
for (const root of roots) {
|
|
377
|
+
const nodes = [
|
|
378
|
+
...(callTypes.includes(root.type) ? [root] : []),
|
|
379
|
+
...root.descendantsOfType(callTypes),
|
|
380
|
+
];
|
|
381
|
+
for (const call of nodes) {
|
|
382
|
+
const callee = call?.childForFieldName(call.type === "new_expression" ? "constructor" : "function");
|
|
383
|
+
if (callee)
|
|
384
|
+
deps.add(calleeName(callee, locals, lang));
|
|
385
|
+
}
|
|
386
|
+
for (const id of [
|
|
387
|
+
...(root.type === "identifier" ? [root] : []),
|
|
388
|
+
...root.descendantsOfType(["identifier"]),
|
|
389
|
+
])
|
|
390
|
+
if (id && importedNames.has(id.text) && isValueIdentifier(id, lang))
|
|
391
|
+
deps.add(id.text);
|
|
392
|
+
}
|
|
393
|
+
return [...deps].sort();
|
|
394
|
+
}
|
|
395
|
+
function collectImportedNames(root, lang) {
|
|
396
|
+
const names = new Set();
|
|
397
|
+
const types = lang === "python"
|
|
398
|
+
? ["import_statement", "import_from_statement"]
|
|
399
|
+
: ["import_statement", "import_clause"];
|
|
400
|
+
for (const imp of root.descendantsOfType(types)) {
|
|
401
|
+
for (const id of imp?.descendantsOfType(["identifier"]) ?? [])
|
|
402
|
+
if (id)
|
|
403
|
+
names.add(id.text);
|
|
404
|
+
}
|
|
405
|
+
// CommonJS: `const x = require("…")` and `const { a, b } = require("…")`.
|
|
406
|
+
if (lang !== "python")
|
|
407
|
+
for (const d of root.descendantsOfType(["variable_declarator"])) {
|
|
408
|
+
const value = d?.childForFieldName("value");
|
|
409
|
+
if (value?.type === "call_expression" &&
|
|
410
|
+
value.childForFieldName("function")?.text === "require")
|
|
411
|
+
patternNames(d?.childForFieldName("name") ?? null, names);
|
|
412
|
+
}
|
|
413
|
+
return names;
|
|
414
|
+
}
|
|
415
|
+
// -------------------------------------------------------------------------------------------------
|
|
416
|
+
// Declarations
|
|
417
|
+
/** The function inside `memo(function X() {})`, `forwardRef((p, ref) => …)`, `memo(forwardRef(…))`. */
|
|
418
|
+
function wrappedFunction(value, depth = 0) {
|
|
419
|
+
if (FUNCTION_VALUES.has(value.type))
|
|
420
|
+
return { fn: value, wrappers: [] };
|
|
421
|
+
if (depth >= 2 || value.type !== "call_expression")
|
|
422
|
+
return null;
|
|
423
|
+
const callee = value.childForFieldName("function");
|
|
424
|
+
const args = value.childForFieldName("arguments");
|
|
425
|
+
for (const a of args?.namedChildren ?? []) {
|
|
426
|
+
if (!a)
|
|
427
|
+
continue;
|
|
428
|
+
const inner = wrappedFunction(a, depth + 1);
|
|
429
|
+
if (inner)
|
|
430
|
+
return { fn: inner.fn, wrappers: [canon(callee ?? value), ...inner.wrappers] };
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
/** `exports.f`, `module.exports.f`, `View.prototype.render` → `f`, `f`, `View.render`. */
|
|
435
|
+
function commonJsPath(left) {
|
|
436
|
+
if (left.type !== "member_expression")
|
|
437
|
+
return null;
|
|
438
|
+
const parts = canon(left).split(".");
|
|
439
|
+
if (parts.some((p) => !/^[A-Za-z_$][\w$]*$/.test(p)))
|
|
440
|
+
return null;
|
|
441
|
+
let exported = false;
|
|
442
|
+
if (parts[0] === "module" && parts[1] === "exports") {
|
|
443
|
+
parts.splice(0, 2);
|
|
444
|
+
exported = true;
|
|
445
|
+
}
|
|
446
|
+
else if (parts[0] === "exports") {
|
|
447
|
+
parts.splice(0, 1);
|
|
448
|
+
exported = true;
|
|
449
|
+
}
|
|
450
|
+
const path = parts.filter((p) => p !== "prototype").join(".");
|
|
451
|
+
return path ? { path, exported } : null;
|
|
452
|
+
}
|
|
453
|
+
export function extractFileSymbolsV2(root, lang, normalizeLocals) {
|
|
454
|
+
const importedNames = collectImportedNames(root, lang);
|
|
455
|
+
const bySymbolPath = new Map();
|
|
456
|
+
const fileExports = [];
|
|
457
|
+
const isLocal = (node) => hasAncestorIn(node, FUNCTION_LIKE[lang]) || hasAncestorIn(node, CLASS_LIKE);
|
|
458
|
+
function add(def) {
|
|
459
|
+
const name = def.path.slice(def.path.lastIndexOf(".") + 1);
|
|
460
|
+
const n = def.node;
|
|
461
|
+
const body = def.kind === "type" ? null : (n.childForFieldName("body") ?? null);
|
|
462
|
+
// A function's statements (less a docstring); an arrow's expression body; a class's whole body.
|
|
463
|
+
const isBlock = body?.type === "block" || body?.type === "statement_block";
|
|
464
|
+
const roots = !body
|
|
465
|
+
? []
|
|
466
|
+
: def.kind !== "class" && isBlock
|
|
467
|
+
? withoutDocstring(body, lang)
|
|
468
|
+
: [body];
|
|
469
|
+
const locals = def.kind === "class"
|
|
470
|
+
? collectLocals(body ?? n, null, lang)
|
|
471
|
+
: collectLocals(body ?? n, n.childForFieldName("parameters") ?? n.childForFieldName("parameter"), lang);
|
|
472
|
+
const aliasOf = new Map();
|
|
473
|
+
const bodyNormalized = roots
|
|
474
|
+
.map((r) => serializeBody(r, locals, normalizeLocals, lang, aliasOf))
|
|
475
|
+
.filter(Boolean)
|
|
476
|
+
.join(" ");
|
|
477
|
+
const deps = collectDeps(roots, lang, importedNames, locals);
|
|
478
|
+
// Rendered now, with a placeholder for the name: the tree is freed once extraction is done.
|
|
479
|
+
const template = sigOf(def, NAME_SLOT, lang);
|
|
480
|
+
const sigFor = (as) => template.split(NAME_SLOT).join(as);
|
|
481
|
+
const existing = bySymbolPath.get(def.path);
|
|
482
|
+
if (existing) {
|
|
483
|
+
// Overloads, a getter and its setter, a def per branch: one symbol made of all of them.
|
|
484
|
+
const before = existing.sigFor;
|
|
485
|
+
bySymbolPath.set(def.path, {
|
|
486
|
+
...existing,
|
|
487
|
+
sigTokens: `${existing.sigTokens}\n${sigFor(name)}`,
|
|
488
|
+
sigFor: (as) => `${before ? before(as) : existing.sigTokens}\n${sigFor(as)}`,
|
|
489
|
+
bodyText: `${existing.bodyText}\n${body?.text ?? ""}`,
|
|
490
|
+
sourceText: `${existing.sourceText}\n${def.outer.text}`,
|
|
491
|
+
bodyNormalized: `${existing.bodyNormalized}\n${bodyNormalized}`,
|
|
492
|
+
deps: [...new Set([...existing.deps, ...deps])].sort(),
|
|
493
|
+
exported: existing.exported || def.exported,
|
|
494
|
+
});
|
|
495
|
+
if (def.exported && !existing.exported)
|
|
496
|
+
fileExports.push(def.path);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
bySymbolPath.set(def.path, {
|
|
500
|
+
kind: def.kind,
|
|
501
|
+
name,
|
|
502
|
+
exported: def.exported,
|
|
503
|
+
sigTokens: sigFor(name),
|
|
504
|
+
sigFor,
|
|
505
|
+
bodyText: body?.text ?? "",
|
|
506
|
+
sourceText: def.outer.text,
|
|
507
|
+
bodyNormalized,
|
|
508
|
+
deps,
|
|
509
|
+
});
|
|
510
|
+
if (def.exported)
|
|
511
|
+
fileExports.push(def.path);
|
|
512
|
+
}
|
|
513
|
+
const qualify = (node, name) => {
|
|
514
|
+
const ns = namespacePrefix(node);
|
|
515
|
+
return ns ? `${ns}.${name}` : name;
|
|
516
|
+
};
|
|
517
|
+
/** The statement a declaration is written in, decorators and `export` included. */
|
|
518
|
+
const outerOf = (node) => {
|
|
519
|
+
let n = node;
|
|
520
|
+
while (n.parent &&
|
|
521
|
+
[
|
|
522
|
+
"decorated_definition",
|
|
523
|
+
"export_statement",
|
|
524
|
+
"variable_declarator",
|
|
525
|
+
"lexical_declaration",
|
|
526
|
+
"variable_declaration",
|
|
527
|
+
"ambient_declaration",
|
|
528
|
+
"assignment_expression",
|
|
529
|
+
"expression_statement",
|
|
530
|
+
"call_expression",
|
|
531
|
+
"arguments",
|
|
532
|
+
].includes(n.parent.type))
|
|
533
|
+
n = n.parent;
|
|
534
|
+
return n;
|
|
535
|
+
};
|
|
536
|
+
// Module-level functions, generators and function signatures (overloads, `declare function`).
|
|
537
|
+
const fnTypes = lang === "python"
|
|
538
|
+
? ["function_definition"]
|
|
539
|
+
: ["function_declaration", "generator_function_declaration", "function_signature"];
|
|
540
|
+
// Classes and the like are visited in source order with functions, so merged symbols read top-down.
|
|
541
|
+
const topTypes = [
|
|
542
|
+
...fnTypes,
|
|
543
|
+
...(lang === "python"
|
|
544
|
+
? ["class_definition"]
|
|
545
|
+
: [
|
|
546
|
+
"class_declaration",
|
|
547
|
+
"abstract_class_declaration",
|
|
548
|
+
"interface_declaration",
|
|
549
|
+
"type_alias_declaration",
|
|
550
|
+
"enum_declaration",
|
|
551
|
+
"variable_declarator",
|
|
552
|
+
"assignment_expression",
|
|
553
|
+
]),
|
|
554
|
+
];
|
|
555
|
+
function visitClass(cls, prefix) {
|
|
556
|
+
const nameNode = cls.childForFieldName("name");
|
|
557
|
+
if (!nameNode)
|
|
558
|
+
return;
|
|
559
|
+
const path = prefix ? `${prefix}.${nameNode.text}` : qualify(cls, nameNode.text);
|
|
560
|
+
const isType = cls.type === "interface_declaration";
|
|
561
|
+
add({
|
|
562
|
+
path,
|
|
563
|
+
kind: isType ? "type" : "class",
|
|
564
|
+
node: cls,
|
|
565
|
+
outer: prefix ? cls : outerOf(cls),
|
|
566
|
+
exported: prefix ? false : isExported(cls),
|
|
567
|
+
extra: [],
|
|
568
|
+
decorators: decoratorsOf(cls, cls),
|
|
569
|
+
});
|
|
570
|
+
if (isType)
|
|
571
|
+
return;
|
|
572
|
+
const body = cls.childForFieldName("body");
|
|
573
|
+
for (const child of body?.namedChildren ?? []) {
|
|
574
|
+
if (!child)
|
|
575
|
+
continue;
|
|
576
|
+
const member = child.type === "decorated_definition"
|
|
577
|
+
? (child.childForFieldName("definition") ?? child)
|
|
578
|
+
: child;
|
|
579
|
+
if (["class_definition", "class_declaration", "abstract_class_declaration"].includes(member.type)) {
|
|
580
|
+
visitClass(member, path);
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
const mName = member.childForFieldName("name");
|
|
584
|
+
if (!mName)
|
|
585
|
+
continue;
|
|
586
|
+
if (member.type === "method_definition" ||
|
|
587
|
+
member.type === "method_signature" ||
|
|
588
|
+
member.type === "abstract_method_signature" ||
|
|
589
|
+
(lang === "python" && member.type === "function_definition")) {
|
|
590
|
+
add({
|
|
591
|
+
path: `${path}.${mName.text}`,
|
|
592
|
+
kind: "method",
|
|
593
|
+
node: member,
|
|
594
|
+
outer: member.parent?.type === "decorated_definition" ? member.parent : member,
|
|
595
|
+
exported: false,
|
|
596
|
+
extra: [],
|
|
597
|
+
decorators: decoratorsOf(member, member),
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
else if (member.type === "public_field_definition" || member.type === "field_definition") {
|
|
601
|
+
// `onError = (handler) => { … }`: a method written as a field.
|
|
602
|
+
const value = member.childForFieldName("value");
|
|
603
|
+
const fn = value ? wrappedFunction(value) : null;
|
|
604
|
+
if (!fn)
|
|
605
|
+
continue;
|
|
606
|
+
add({
|
|
607
|
+
path: `${path}.${mName.text}`,
|
|
608
|
+
kind: "method",
|
|
609
|
+
node: fn.fn,
|
|
610
|
+
outer: member,
|
|
611
|
+
exported: false,
|
|
612
|
+
extra: [...leadingModifiers(member), ...fn.wrappers.map((w) => `wrap:${w}`)],
|
|
613
|
+
decorators: decoratorsOf(member, member),
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
for (const node of root.descendantsOfType(topTypes)) {
|
|
619
|
+
if (!node || isLocal(node))
|
|
620
|
+
continue;
|
|
621
|
+
const nameNode = node.childForFieldName("name");
|
|
622
|
+
if (fnTypes.includes(node.type)) {
|
|
623
|
+
if (!nameNode)
|
|
624
|
+
continue;
|
|
625
|
+
add({
|
|
626
|
+
path: qualify(node, nameNode.text),
|
|
627
|
+
kind: "function",
|
|
628
|
+
node,
|
|
629
|
+
outer: outerOf(node),
|
|
630
|
+
exported: lang === "python" ? false : isExported(node),
|
|
631
|
+
extra: [],
|
|
632
|
+
decorators: decoratorsOf(node, node),
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
else if (CLASS_LIKE.has(node.type)) {
|
|
636
|
+
visitClass(node, null);
|
|
637
|
+
}
|
|
638
|
+
else if (node.type === "type_alias_declaration" || node.type === "enum_declaration") {
|
|
639
|
+
if (!nameNode)
|
|
640
|
+
continue;
|
|
641
|
+
add({
|
|
642
|
+
path: qualify(node, nameNode.text),
|
|
643
|
+
kind: "type",
|
|
644
|
+
node,
|
|
645
|
+
outer: outerOf(node),
|
|
646
|
+
exported: isExported(node),
|
|
647
|
+
extra: [],
|
|
648
|
+
decorators: [],
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
else if (node.type === "variable_declarator") {
|
|
652
|
+
// `const f = () => {}`, `const f: Handler = function () {}`, `const X = memo(forwardRef(…))`.
|
|
653
|
+
const value = node.childForFieldName("value");
|
|
654
|
+
if (!value || nameNode?.type !== "identifier")
|
|
655
|
+
continue;
|
|
656
|
+
const fn = wrappedFunction(value);
|
|
657
|
+
if (!fn)
|
|
658
|
+
continue;
|
|
659
|
+
const type = node.childForFieldName("type");
|
|
660
|
+
add({
|
|
661
|
+
path: qualify(node, nameNode.text),
|
|
662
|
+
kind: "function",
|
|
663
|
+
node: fn.fn,
|
|
664
|
+
outer: outerOf(node),
|
|
665
|
+
exported: isExported(node),
|
|
666
|
+
extra: [
|
|
667
|
+
...(type ? [`:${canon(type).replace(/^:\s*/, "")}`] : []),
|
|
668
|
+
...fn.wrappers.map((w) => `wrap:${w}`),
|
|
669
|
+
],
|
|
670
|
+
decorators: [],
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
else if (node.type === "assignment_expression") {
|
|
674
|
+
// CommonJS: `exports.f = function () {}`, `View.prototype.render = function () {}`.
|
|
675
|
+
if (node.parent?.type !== "expression_statement")
|
|
676
|
+
continue;
|
|
677
|
+
const left = node.childForFieldName("left");
|
|
678
|
+
const right = node.childForFieldName("right");
|
|
679
|
+
const target = left ? commonJsPath(left) : null;
|
|
680
|
+
const fn = right ? wrappedFunction(right) : null;
|
|
681
|
+
if (!target || !fn)
|
|
682
|
+
continue;
|
|
683
|
+
add({
|
|
684
|
+
path: qualify(node, target.path),
|
|
685
|
+
kind: target.path.includes(".") ? "method" : "function",
|
|
686
|
+
node: fn.fn,
|
|
687
|
+
outer: outerOf(node),
|
|
688
|
+
exported: target.exported,
|
|
689
|
+
extra: fn.wrappers.map((w) => `wrap:${w}`),
|
|
690
|
+
decorators: [],
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return { bySymbolPath, fileExports };
|
|
695
|
+
}
|
|
696
|
+
/** Whole-file `body` and `deps` for a file-only anchor, read the version-2 way. */
|
|
697
|
+
export function computeWholeFileTiersV2(root, lang, normalizeLocals) {
|
|
698
|
+
const importedNames = collectImportedNames(root, lang);
|
|
699
|
+
const locals = collectLocals(root, null, lang);
|
|
700
|
+
return {
|
|
701
|
+
bodyNormalized: serializeBody(root, locals, normalizeLocals, lang, new Map()),
|
|
702
|
+
deps: collectDeps([root], lang, importedNames, locals),
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
//# sourceMappingURL=extract-v2.js.map
|