@nithin554/code-crawler 0.1.39 → 0.1.40

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 (44) hide show
  1. package/README.md +206 -33
  2. package/dist/crawler.d.ts +27 -1
  3. package/dist/crawler.js +360 -11
  4. package/dist/crawler.js.map +1 -1
  5. package/dist/graph/queries.d.ts +70 -4
  6. package/dist/graph/queries.js +125 -1
  7. package/dist/graph/queries.js.map +1 -1
  8. package/dist/graph/schema.d.ts +24 -2
  9. package/dist/graph/schema.js +9 -0
  10. package/dist/graph/schema.js.map +1 -1
  11. package/dist/parser/controlFlow.d.ts +27 -0
  12. package/dist/parser/controlFlow.js +82 -0
  13. package/dist/parser/controlFlow.js.map +1 -0
  14. package/dist/parser/language.d.ts +101 -2
  15. package/dist/parser/languages/index.js +4 -0
  16. package/dist/parser/languages/index.js.map +1 -1
  17. package/dist/parser/languages/java.js +160 -6
  18. package/dist/parser/languages/java.js.map +1 -1
  19. package/dist/parser/languages/python.d.ts +2 -0
  20. package/dist/parser/languages/python.js +956 -0
  21. package/dist/parser/languages/python.js.map +1 -0
  22. package/dist/parser/languages/typescript.d.ts +2 -0
  23. package/dist/parser/languages/typescript.js +1111 -0
  24. package/dist/parser/languages/typescript.js.map +1 -0
  25. package/dist/parser/moduleId.d.ts +35 -0
  26. package/dist/parser/moduleId.js +93 -0
  27. package/dist/parser/moduleId.js.map +1 -0
  28. package/dist/parser/treeSitter.d.ts +6 -0
  29. package/dist/parser/treeSitter.js +13 -2
  30. package/dist/parser/treeSitter.js.map +1 -1
  31. package/dist/resolver/common.d.ts +52 -0
  32. package/dist/resolver/common.js +624 -0
  33. package/dist/resolver/common.js.map +1 -0
  34. package/dist/resolver/dynamic.js +66 -1
  35. package/dist/resolver/dynamic.js.map +1 -1
  36. package/dist/resolver/genericResolver.d.ts +45 -0
  37. package/dist/resolver/genericResolver.js +235 -0
  38. package/dist/resolver/genericResolver.js.map +1 -0
  39. package/dist/resolver/types.d.ts +24 -1
  40. package/dist/resolver/types.js +50 -0
  41. package/dist/resolver/types.js.map +1 -1
  42. package/dist/server.js +108 -5
  43. package/dist/server.js.map +1 -1
  44. package/package.json +2 -2
@@ -0,0 +1,1111 @@
1
+ import { childOfType, childrenOfType, createParser, fieldChild, loadGrammar, walk, } from "../treeSitter.js";
2
+ import { controlFlowOf, insideAny } from "../controlFlow.js";
3
+ import { DEFAULT_EXPORT_SUFFIX, moduleNodeId, resolveRelativeModule, tsModuleId, } from "../moduleId.js";
4
+ /**
5
+ * TypeScript / JavaScript parser (TS, TSX, JS, JSX).
6
+ *
7
+ * TypeScript has no packages either, so a module id is derived from the
8
+ * project-relative path (`src/services/order.ts` -> `src.services.order`) and
9
+ * module-level functions become methods of the module node. Classes,
10
+ * interfaces, enums and object type aliases become graph types.
11
+ *
12
+ * Modelled beyond plain declarations:
13
+ * - decorators as annotations (NestJS/Angular entry points, DI, metadata)
14
+ * - constructor parameter properties and decorator-driven injections
15
+ * - express/fastify route registrations
16
+ * - run-time lookups (`import()`, `require`, `Reflect.get`)
17
+ * - control-flow context of every call site
18
+ */
19
+ const TS_FLOWS = [
20
+ { type: "if_statement", kind: "branch", guardField: "condition" },
21
+ { type: "else_clause", kind: "branch", guard: "else" },
22
+ { type: "switch_statement", kind: "branch", guardField: "value" },
23
+ { type: "switch_case", kind: "branch", guard: "case" },
24
+ { type: "switch_default", kind: "branch", guard: "default" },
25
+ { type: "ternary_expression", kind: "branch", guardField: "condition" },
26
+ { type: "for_statement", kind: "loop", guardField: "condition" },
27
+ { type: "for_in_statement", kind: "loop", guardField: "right" },
28
+ { type: "while_statement", kind: "loop", guardField: "condition" },
29
+ { type: "do_statement", kind: "loop", guardField: "condition" },
30
+ { type: "try_statement", kind: "exception", guard: "try" },
31
+ { type: "catch_clause", kind: "exception", guardField: "parameter" },
32
+ { type: "finally_clause", kind: "exception", guard: "finally" },
33
+ { type: "binary_expression", kind: "predicate", operatorIn: ["&&", "||", "??"] },
34
+ ];
35
+ const SKIP_CALL_ANCESTORS = new Set(["decorator", "import_statement"]);
36
+ const HTTP_DECORATORS = new Set(["Get", "Post", "Put", "Delete", "Patch", "Head", "Options", "All"]);
37
+ const HTTP_METHODS = new Set(["get", "post", "put", "delete", "patch", "head", "options", "all"]);
38
+ const ROUTE_RECEIVERS = new Set([
39
+ "app",
40
+ "router",
41
+ "server",
42
+ "api",
43
+ "fastify",
44
+ "routes",
45
+ "express",
46
+ "application",
47
+ ]);
48
+ const PARAM_DECORATORS = new Set([
49
+ "Body",
50
+ "Param",
51
+ "Query",
52
+ "Headers",
53
+ "Req",
54
+ "Res",
55
+ "Session",
56
+ "UploadedFile",
57
+ "UploadedFiles",
58
+ "Ip",
59
+ "HostParam",
60
+ ]);
61
+ const INJECT_DECORATORS = new Set([
62
+ "Inject",
63
+ "InjectRepository",
64
+ "InjectModel",
65
+ "InjectDataSource",
66
+ "Autowired",
67
+ ]);
68
+ const DI_CLASS_DECORATORS = new Set([
69
+ "Injectable",
70
+ "Controller",
71
+ "Component",
72
+ "Service",
73
+ "Directive",
74
+ "Pipe",
75
+ "Guard",
76
+ "Gateway",
77
+ "Resolver",
78
+ "Module",
79
+ ]);
80
+ export const typescriptConfig = {
81
+ name: "typescript",
82
+ extensions: ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"],
83
+ loadGrammar() {
84
+ return loadGrammar("@vscode/tree-sitter-wasm", "wasm/tree-sitter-typescript.wasm");
85
+ },
86
+ async parseFile(source, filePath, context) {
87
+ const usesTsxGrammar = /\.(tsx|jsx|js|mjs|cjs)$/i.test(filePath);
88
+ const grammar = await loadGrammar("@vscode/tree-sitter-wasm", usesTsxGrammar ? "wasm/tree-sitter-tsx.wasm" : "wasm/tree-sitter-typescript.wasm");
89
+ const parser = createParser(grammar);
90
+ const tree = parser.parse(source);
91
+ if (!tree)
92
+ throw new Error(`failed to parse ${filePath}`);
93
+ const root = tree.rootNode;
94
+ const moduleId = tsModuleId(filePath, context?.projectRoot);
95
+ const moduleTypeId = moduleNodeId(moduleId);
96
+ const types = [
97
+ {
98
+ id: moduleTypeId,
99
+ kind: "module",
100
+ name: moduleId.split(".").pop() ?? moduleId,
101
+ extendsTypes: [],
102
+ implementsTypes: [],
103
+ startLine: 0,
104
+ endLine: root.endPosition.row,
105
+ annotations: [],
106
+ exported: true,
107
+ },
108
+ ];
109
+ const methods = [];
110
+ const fields = [];
111
+ const imports = [];
112
+ const callSites = [];
113
+ const localsByMethod = new Map();
114
+ const scopeTypes = new Map();
115
+ const scopeModules = new Map();
116
+ const externalModules = new Map();
117
+ const reflectiveRefs = [];
118
+ const injections = [];
119
+ const routes = [];
120
+ const methodRanges = [];
121
+ const typeRanges = [{ startLine: 0, endLine: root.endPosition.row, id: moduleTypeId }];
122
+ const fieldIds = new Set();
123
+ let defaultExportName;
124
+ // ---------------------------------------------------------------- helpers
125
+ function stringContent(raw) {
126
+ const trimmed = raw.trim();
127
+ const match = trimmed.match(/^["'`]([\s\S]*?)["'`]$/);
128
+ return match ? (match[1] ?? "") : trimmed;
129
+ }
130
+ function typeNameOf(node) {
131
+ if (!node)
132
+ return "";
133
+ switch (node.type) {
134
+ case "identifier":
135
+ case "type_identifier":
136
+ case "property_identifier":
137
+ case "nested_type_identifier":
138
+ return node.text;
139
+ case "generic_type": {
140
+ const name = fieldChild(node, "name") ?? node.namedChildren[0];
141
+ return name ? name.text : node.text;
142
+ }
143
+ case "member_expression": {
144
+ const property = fieldChild(node, "property");
145
+ return property ? property.text : node.text;
146
+ }
147
+ case "call_expression": {
148
+ const fn = fieldChild(node, "function") ?? node.namedChildren[0];
149
+ return typeNameOf(fn ?? null);
150
+ }
151
+ case "type_annotation":
152
+ return typeNameOf(node.namedChildren[0] ?? null);
153
+ default:
154
+ return (node.text.split("<")[0] ?? "").trim();
155
+ }
156
+ }
157
+ function annotationTypeText(node) {
158
+ if (!node)
159
+ return "";
160
+ return node.text.replace(/^\s*:\s*/, "").trim();
161
+ }
162
+ function decoratorName(node) {
163
+ const expression = node.namedChildren[0] ?? null;
164
+ if (!expression)
165
+ return null;
166
+ if (expression.type === "call_expression") {
167
+ const fn = fieldChild(expression, "function") ?? expression.namedChildren[0];
168
+ if (!fn)
169
+ return null;
170
+ if (fn.type === "identifier")
171
+ return { name: fn.text };
172
+ if (fn.type === "member_expression") {
173
+ const property = fieldChild(fn, "property");
174
+ if (!property)
175
+ return null;
176
+ return { name: property.text, fqn: fn.text };
177
+ }
178
+ return { name: fn.text };
179
+ }
180
+ if (expression.type === "identifier")
181
+ return { name: expression.text };
182
+ if (expression.type === "member_expression") {
183
+ const property = fieldChild(expression, "property");
184
+ if (!property)
185
+ return null;
186
+ return { name: property.text, fqn: expression.text };
187
+ }
188
+ return null;
189
+ }
190
+ function annotationOf(node) {
191
+ const named = decoratorName(node);
192
+ if (!named)
193
+ return null;
194
+ let args = [];
195
+ const expression = node.namedChildren[0];
196
+ if (expression && expression.type === "call_expression") {
197
+ const argsNode = fieldChild(expression, "arguments");
198
+ if (argsNode) {
199
+ args = argsNode.namedChildren.filter((c) => c.type !== "comment").map((c) => c.text);
200
+ }
201
+ }
202
+ return { name: named.name, fqn: named.fqn, arguments: args, line: node.startPosition.row };
203
+ }
204
+ function annotationsOf(decorators) {
205
+ return decorators
206
+ .map((d) => annotationOf(d))
207
+ .filter((a) => a !== null);
208
+ }
209
+ function annotationString(annotation) {
210
+ for (const arg of annotation.arguments) {
211
+ const value = stringContent(arg);
212
+ if (value !== arg.trim())
213
+ return value;
214
+ }
215
+ return "";
216
+ }
217
+ function joinPath(base, suffix) {
218
+ const joined = `${base}/${suffix}`.replace(/\/+/g, "/").replace(/(.)\/$/, "$1");
219
+ return joined === "" ? "/" : joined.startsWith("/") ? joined : `/${joined}`;
220
+ }
221
+ function entryPointOf(annotations, controllerPath, name) {
222
+ for (const annotation of annotations) {
223
+ const simple = annotation.name;
224
+ if (HTTP_DECORATORS.has(simple)) {
225
+ const path = annotationString(annotation);
226
+ return { kind: "http", detail: `${simple.toUpperCase()} ${joinPath(controllerPath, path)}`.trim() };
227
+ }
228
+ if (simple === "MessagePattern" || simple === "EventPattern" || simple === "OnMessage") {
229
+ return { kind: "messaging", detail: `${simple} ${annotationString(annotation)}`.trim() };
230
+ }
231
+ if (simple === "Cron" || simple === "Interval" || simple === "Timeout") {
232
+ const first = annotation.arguments[0] ? stringContent(annotation.arguments[0]) : "";
233
+ return { kind: "scheduler", detail: `${simple} ${first}`.trim() };
234
+ }
235
+ if (simple === "OnEvent" || simple === "EventSubscriber" || simple === "SubscribeMessage") {
236
+ return { kind: "event", detail: simple };
237
+ }
238
+ if (simple === "Test" || simple === "It")
239
+ return { kind: "test", detail: simple };
240
+ }
241
+ if (/^(test|it)$/i.test(name))
242
+ return { kind: "test", detail: name };
243
+ return undefined;
244
+ }
245
+ function inferExpressionType(node) {
246
+ if (!node)
247
+ return "";
248
+ switch (node.type) {
249
+ case "new_expression": {
250
+ const ctor = fieldChild(node, "constructor") ?? node.namedChildren[0];
251
+ return typeNameOf(ctor ?? null);
252
+ }
253
+ case "call_expression": {
254
+ const fn = fieldChild(node, "function") ?? node.namedChildren[0];
255
+ if (!fn)
256
+ return "";
257
+ const name = typeNameOf(fn);
258
+ return /^[A-Z]/.test(name) ? name : "";
259
+ }
260
+ case "string":
261
+ return "string";
262
+ case "number":
263
+ return "number";
264
+ case "true":
265
+ case "false":
266
+ return "boolean";
267
+ case "array":
268
+ return "Array";
269
+ case "object":
270
+ return "object";
271
+ case "arrow_function":
272
+ case "function_expression":
273
+ return "";
274
+ default:
275
+ return "";
276
+ }
277
+ }
278
+ function addField(typeId, name, type, line, annotations = []) {
279
+ const id = `${typeId}$${name}`;
280
+ if (fieldIds.has(id))
281
+ return;
282
+ fieldIds.add(id);
283
+ fields.push({ id, typeId, name, type, startLine: line, modifiers: [], annotations });
284
+ }
285
+ function parseParams(paramsNode) {
286
+ if (!paramsNode)
287
+ return [];
288
+ const out = [];
289
+ for (const param of paramsNode.namedChildren) {
290
+ if (param.type === "required_parameter" || param.type === "optional_parameter") {
291
+ const pattern = fieldChild(param, "pattern") ?? param.namedChildren[0];
292
+ if (pattern) {
293
+ out.push({ name: pattern.text.replace(/\s+/g, " "), type: annotationTypeText(fieldChild(param, "type")) });
294
+ }
295
+ }
296
+ else if (param.type === "identifier") {
297
+ out.push({ name: param.text, type: "" });
298
+ }
299
+ else if (param.type === "rest_pattern") {
300
+ out.push({ name: param.text, type: "" });
301
+ }
302
+ else if (param.type === "object_pattern" || param.type === "array_pattern") {
303
+ out.push({ name: param.text.replace(/\s+/g, " "), type: "" });
304
+ }
305
+ else if (param.type !== "comment") {
306
+ out.push({ name: param.text.replace(/\s+/g, " "), type: "" });
307
+ }
308
+ }
309
+ return out;
310
+ }
311
+ function modifiersOf(node) {
312
+ const modifiers = [];
313
+ for (const child of node.children) {
314
+ switch (child.type) {
315
+ case "accessibility_modifier":
316
+ case "static":
317
+ case "async":
318
+ case "get":
319
+ case "set":
320
+ case "abstract":
321
+ case "override":
322
+ case "readonly":
323
+ case "declare":
324
+ if (!modifiers.includes(child.type))
325
+ modifiers.push(child.type);
326
+ break;
327
+ default:
328
+ break;
329
+ }
330
+ }
331
+ if (node.type === "public_field_definition")
332
+ modifiers.push("field");
333
+ return modifiers;
334
+ }
335
+ // ---------------------------------------------------------- declarations
336
+ function heritageOf(node) {
337
+ const extendsTypes = [];
338
+ const implementsTypes = [];
339
+ const heritage = childOfType(node, "class_heritage");
340
+ if (!heritage)
341
+ return { extendsTypes, implementsTypes };
342
+ for (const clause of childrenOfType(heritage, "extends_clause")) {
343
+ for (const value of clause.namedChildren) {
344
+ if (value.type === "type_arguments")
345
+ continue;
346
+ extendsTypes.push(typeNameOf(value));
347
+ }
348
+ }
349
+ for (const clause of childrenOfType(heritage, "implements_clause")) {
350
+ for (const value of clause.namedChildren)
351
+ implementsTypes.push(typeNameOf(value));
352
+ }
353
+ return { extendsTypes, implementsTypes };
354
+ }
355
+ function visitClass(node, decorators, parentTypeId, exported) {
356
+ const nameNode = fieldChild(node, "name") ?? childOfType(node, "type_identifier");
357
+ if (!nameNode)
358
+ return;
359
+ const name = nameNode.text;
360
+ const id = parentTypeId ? `${parentTypeId}.${name}` : `${moduleId}.${name}`;
361
+ const { extendsTypes, implementsTypes } = heritageOf(node);
362
+ const annotations = decorators;
363
+ types.push({
364
+ id,
365
+ kind: "class",
366
+ name,
367
+ extendsTypes,
368
+ implementsTypes,
369
+ startLine: node.startPosition.row,
370
+ endLine: node.endPosition.row,
371
+ annotations,
372
+ isAspect: annotations.some((a) => a.name === "Aspect"),
373
+ exported,
374
+ });
375
+ typeRanges.push({ startLine: node.startPosition.row, endLine: node.endPosition.row, id });
376
+ scopeTypes.set(name, id);
377
+ const injectable = annotations.some((a) => DI_CLASS_DECORATORS.has(a.name));
378
+ const controller = annotations.find((a) => a.name === "Controller");
379
+ const controllerPath = controller ? annotationString(controller) : "";
380
+ const body = fieldChild(node, "body") ?? childOfType(node, "class_body");
381
+ if (!body)
382
+ return;
383
+ const buffer = [];
384
+ for (const member of body.namedChildren) {
385
+ if (member.type === "decorator") {
386
+ buffer.push(member);
387
+ continue;
388
+ }
389
+ const memberDecorators = buffer.splice(0, buffer.length);
390
+ for (const fieldDecorator of childrenOfType(member, "decorator"))
391
+ memberDecorators.push(fieldDecorator);
392
+ const memberAnnotations = annotationsOf(memberDecorators);
393
+ if (member.type === "method_definition") {
394
+ visitMethod(member, memberAnnotations, id, injectable, controllerPath);
395
+ }
396
+ else if (member.type === "public_field_definition") {
397
+ visitClassField(member, memberAnnotations, id, injectable);
398
+ }
399
+ else if (member.type === "abstract_method_signature" || member.type === "method_signature") {
400
+ visitSignature(member, memberAnnotations, id, controllerPath);
401
+ }
402
+ else if (member.type === "class_declaration" || member.type === "abstract_class_declaration") {
403
+ visitClass(member, memberAnnotations, id, true);
404
+ }
405
+ }
406
+ }
407
+ function visitClassField(node, annotations, classId, injectable) {
408
+ const nameNode = fieldChild(node, "name") ?? node.namedChildren.find((c) => c.type === "property_identifier");
409
+ if (!nameNode)
410
+ return;
411
+ const name = nameNode.text;
412
+ const declared = annotationTypeText(fieldChild(node, "type"));
413
+ const value = fieldChild(node, "value");
414
+ if (value && (value.type === "arrow_function" || value.type === "function_expression")) {
415
+ visitCallable({
416
+ name,
417
+ node,
418
+ paramsNode: fieldChild(value, "parameters") ?? childOfType(value, "formal_parameters"),
419
+ returnTypeNode: fieldChild(value, "return_type"),
420
+ bodyNode: fieldChild(value, "body"),
421
+ annotations,
422
+ ownerTypeId: classId,
423
+ extraModifiers: modifiersOf(node),
424
+ injectable,
425
+ isConstructor: false,
426
+ });
427
+ return;
428
+ }
429
+ addField(classId, name, declared || inferExpressionType(value), node.startPosition.row, annotations);
430
+ const injectDecorator = annotations.find((a) => INJECT_DECORATORS.has(a.name));
431
+ if (injectDecorator || (injectable && !declared)) {
432
+ injections.push({
433
+ kind: "field",
434
+ ownerId: classId,
435
+ name,
436
+ type: declared || (injectDecorator?.arguments[0] ?? "").trim(),
437
+ framework: injectDecorator?.name === "Autowired" ? "spring" : "nestjs",
438
+ line: node.startPosition.row,
439
+ });
440
+ }
441
+ }
442
+ function visitMethod(node, annotations, classId, injectable, controllerPath) {
443
+ const nameNode = fieldChild(node, "name") ?? node.namedChildren.find((c) => c.type === "property_identifier");
444
+ if (!nameNode)
445
+ return;
446
+ const rawName = nameNode.text;
447
+ visitCallable({
448
+ name: rawName,
449
+ node,
450
+ paramsNode: fieldChild(node, "parameters") ?? childOfType(node, "formal_parameters"),
451
+ returnTypeNode: fieldChild(node, "return_type"),
452
+ bodyNode: fieldChild(node, "body") ?? childOfType(node, "statement_block"),
453
+ annotations,
454
+ ownerTypeId: classId,
455
+ extraModifiers: modifiersOf(node),
456
+ entryPoint: entryPointOf(annotations, controllerPath, rawName),
457
+ injectable,
458
+ isConstructor: rawName === "constructor",
459
+ });
460
+ for (const annotation of annotations) {
461
+ if (!HTTP_DECORATORS.has(annotation.name))
462
+ continue;
463
+ routes.push({
464
+ framework: "nestjs",
465
+ httpMethod: annotation.name.toUpperCase(),
466
+ path: joinPath(controllerPath, annotationString(annotation)),
467
+ handlerName: rawName,
468
+ moduleId,
469
+ enclosingTypeId: classId,
470
+ line: annotation.line,
471
+ });
472
+ }
473
+ }
474
+ function visitSignature(node, annotations, classId, controllerPath) {
475
+ const nameNode = fieldChild(node, "name") ?? node.namedChildren.find((c) => c.type === "property_identifier");
476
+ if (!nameNode)
477
+ return;
478
+ const rawName = nameNode.text;
479
+ const id = `${classId}#${rawName}`;
480
+ methods.push({
481
+ id,
482
+ typeId: classId,
483
+ name: rawName,
484
+ isConstructor: false,
485
+ params: parseParams(fieldChild(node, "parameters") ?? childOfType(node, "formal_parameters")),
486
+ startLine: node.startPosition.row,
487
+ endLine: node.endPosition.row,
488
+ returnType: annotationTypeText(fieldChild(node, "return_type")),
489
+ modifiers: modifiersOf(node),
490
+ annotations,
491
+ entryPoint: entryPointOf(annotations, controllerPath, rawName),
492
+ });
493
+ methodRanges.push({ startLine: node.startPosition.row, endLine: node.endPosition.row, id, typeId: classId });
494
+ }
495
+ function visitCallable(options) {
496
+ const { name, node, paramsNode, returnTypeNode, bodyNode, annotations, ownerTypeId } = options;
497
+ const isConstructor = options.isConstructor;
498
+ const id = `${ownerTypeId}#${isConstructor ? "<init>" : name}`;
499
+ const params = parseParams(paramsNode);
500
+ methods.push({
501
+ id,
502
+ typeId: ownerTypeId,
503
+ name: isConstructor ? "<init>" : name,
504
+ isConstructor,
505
+ params,
506
+ startLine: node.startPosition.row,
507
+ endLine: node.endPosition.row,
508
+ returnType: annotationTypeText(returnTypeNode),
509
+ modifiers: options.extraModifiers,
510
+ annotations,
511
+ entryPoint: options.entryPoint,
512
+ });
513
+ methodRanges.push({ startLine: node.startPosition.row, endLine: node.endPosition.row, id, typeId: ownerTypeId });
514
+ const locals = new Map();
515
+ for (const param of params) {
516
+ if (param.type)
517
+ locals.set(param.name.replace(/\?$/, ""), param.type);
518
+ }
519
+ collectParamInjections(paramsNode, ownerTypeId, isConstructor, options.injectable);
520
+ if (bodyNode)
521
+ collectLocals(bodyNode, locals, ownerTypeId);
522
+ localsByMethod.set(id, locals);
523
+ }
524
+ function collectParamInjections(paramsNode, ownerTypeId, isConstructor, injectable) {
525
+ if (!paramsNode)
526
+ return;
527
+ for (const param of paramsNode.namedChildren) {
528
+ if (param.type !== "required_parameter" && param.type !== "optional_parameter")
529
+ continue;
530
+ const pattern = fieldChild(param, "pattern") ?? param.namedChildren[0];
531
+ if (!pattern)
532
+ continue;
533
+ const name = pattern.text.replace(/\s+/g, " ");
534
+ const declared = annotationTypeText(fieldChild(param, "type"));
535
+ const hasPropertyModifier = param.children.some((c) => c.type === "accessibility_modifier");
536
+ const paramDecorators = childrenOfType(param, "decorator")
537
+ .map((d) => decoratorName(d))
538
+ .filter((d) => d !== null);
539
+ if (isConstructor && hasPropertyModifier) {
540
+ addField(ownerTypeId, name, declared, param.startPosition.row);
541
+ }
542
+ const injectDecorator = paramDecorators.find((d) => INJECT_DECORATORS.has(d.name));
543
+ const bindingDecorator = paramDecorators.find((d) => PARAM_DECORATORS.has(d.name));
544
+ if (injectDecorator) {
545
+ const decoratorNode = childrenOfType(param, "decorator").find((d) => decoratorName(d)?.name === injectDecorator.name);
546
+ const annotation = decoratorNode ? annotationOf(decoratorNode) : null;
547
+ injections.push({
548
+ kind: isConstructor ? "constructor-parameter" : "parameter",
549
+ ownerId: ownerTypeId,
550
+ name,
551
+ type: declared || (annotation?.arguments[0] ?? "").trim(),
552
+ framework: "nestjs",
553
+ line: param.startPosition.row,
554
+ });
555
+ continue;
556
+ }
557
+ if (bindingDecorator) {
558
+ injections.push({
559
+ kind: "parameter",
560
+ ownerId: ownerTypeId,
561
+ name,
562
+ type: declared || bindingDecorator.name,
563
+ framework: "nestjs-param",
564
+ line: param.startPosition.row,
565
+ });
566
+ continue;
567
+ }
568
+ if (isConstructor && (hasPropertyModifier || injectable) && declared) {
569
+ injections.push({
570
+ kind: "constructor-parameter",
571
+ ownerId: ownerTypeId,
572
+ name,
573
+ type: declared,
574
+ framework: "ts-di",
575
+ line: param.startPosition.row,
576
+ });
577
+ }
578
+ }
579
+ }
580
+ function collectLocals(body, locals, classId) {
581
+ walk(body, (node) => {
582
+ if (node.type === "variable_declarator") {
583
+ const nameNode = fieldChild(node, "name");
584
+ if (!nameNode)
585
+ return;
586
+ const declared = annotationTypeText(fieldChild(node, "type"));
587
+ const value = fieldChild(node, "value");
588
+ const inferred = declared || inferExpressionType(value);
589
+ if (nameNode.type === "identifier" && inferred)
590
+ locals.set(nameNode.text, inferred);
591
+ return;
592
+ }
593
+ if (node.type === "assignment_expression") {
594
+ const left = fieldChild(node, "left");
595
+ const right = fieldChild(node, "right");
596
+ if (!left || !right || left.type !== "member_expression")
597
+ return;
598
+ const object = fieldChild(left, "object");
599
+ const property = fieldChild(left, "property");
600
+ if (!object || !property)
601
+ return;
602
+ if (object.type !== "this" && object.text !== "this")
603
+ return;
604
+ const inferred = inferExpressionType(right);
605
+ if (inferred) {
606
+ addField(classId, property.text, inferred, node.startPosition.row);
607
+ locals.set(`this.${property.text}`, inferred);
608
+ }
609
+ return;
610
+ }
611
+ if (node.type === "catch_clause") {
612
+ const param = fieldChild(node, "parameter");
613
+ const type = annotationTypeText(fieldChild(node, "type"));
614
+ if (param && param.type === "identifier" && type)
615
+ locals.set(param.text, type);
616
+ }
617
+ });
618
+ }
619
+ function visitInterface(node, decorators, exported) {
620
+ const nameNode = fieldChild(node, "name") ?? childOfType(node, "type_identifier");
621
+ if (!nameNode)
622
+ return;
623
+ const name = nameNode.text;
624
+ const id = `${moduleId}.${name}`;
625
+ const extendsTypes = [];
626
+ for (const clause of childrenOfType(node, "extends_type_clause")) {
627
+ for (const value of clause.namedChildren)
628
+ extendsTypes.push(typeNameOf(value));
629
+ }
630
+ types.push({
631
+ id,
632
+ kind: "interface",
633
+ name,
634
+ extendsTypes,
635
+ implementsTypes: [],
636
+ startLine: node.startPosition.row,
637
+ endLine: node.endPosition.row,
638
+ annotations: decorators,
639
+ exported,
640
+ });
641
+ typeRanges.push({ startLine: node.startPosition.row, endLine: node.endPosition.row, id });
642
+ scopeTypes.set(name, id);
643
+ const body = fieldChild(node, "body") ?? childOfType(node, "interface_body");
644
+ if (!body)
645
+ return;
646
+ for (const member of body.namedChildren) {
647
+ if (member.type === "method_signature" || member.type === "abstract_method_signature") {
648
+ visitSignature(member, [], id, "");
649
+ }
650
+ }
651
+ }
652
+ function visitTypeAlias(node, decorators, exported) {
653
+ const nameNode = fieldChild(node, "name") ?? childOfType(node, "type_identifier");
654
+ const value = fieldChild(node, "value") ?? node.namedChildren.find((c) => c.type === "object_type");
655
+ if (!nameNode || !value || value.type !== "object_type")
656
+ return;
657
+ const name = nameNode.text;
658
+ const id = `${moduleId}.${name}`;
659
+ types.push({
660
+ id,
661
+ kind: "interface",
662
+ name,
663
+ extendsTypes: [],
664
+ implementsTypes: [],
665
+ startLine: node.startPosition.row,
666
+ endLine: node.endPosition.row,
667
+ annotations: decorators,
668
+ exported,
669
+ });
670
+ typeRanges.push({ startLine: node.startPosition.row, endLine: node.endPosition.row, id });
671
+ scopeTypes.set(name, id);
672
+ for (const member of value.namedChildren) {
673
+ if (member.type === "method_signature")
674
+ visitSignature(member, [], id, "");
675
+ }
676
+ }
677
+ function visitEnum(node, decorators, exported) {
678
+ const nameNode = fieldChild(node, "name") ?? childOfType(node, "identifier");
679
+ if (!nameNode)
680
+ return;
681
+ const name = nameNode.text;
682
+ const id = `${moduleId}.${name}`;
683
+ types.push({
684
+ id,
685
+ kind: "enum",
686
+ name,
687
+ extendsTypes: [],
688
+ implementsTypes: [],
689
+ startLine: node.startPosition.row,
690
+ endLine: node.endPosition.row,
691
+ annotations: decorators,
692
+ exported,
693
+ });
694
+ typeRanges.push({ startLine: node.startPosition.row, endLine: node.endPosition.row, id });
695
+ scopeTypes.set(name, id);
696
+ }
697
+ function visitFunctionDeclaration(node, decorators) {
698
+ const nameNode = fieldChild(node, "name");
699
+ if (!nameNode)
700
+ return;
701
+ visitCallable({
702
+ name: nameNode.text,
703
+ node,
704
+ paramsNode: fieldChild(node, "parameters") ?? childOfType(node, "formal_parameters"),
705
+ returnTypeNode: fieldChild(node, "return_type"),
706
+ bodyNode: fieldChild(node, "body") ?? childOfType(node, "statement_block"),
707
+ annotations: decorators,
708
+ ownerTypeId: moduleTypeId,
709
+ extraModifiers: modifiersOf(node),
710
+ entryPoint: entryPointOf(decorators, "", nameNode.text),
711
+ injectable: false,
712
+ isConstructor: false,
713
+ });
714
+ }
715
+ function visitLexicalDeclaration(node, decorators) {
716
+ for (const declarator of childrenOfType(node, "variable_declarator")) {
717
+ const nameNode = fieldChild(declarator, "name");
718
+ if (!nameNode)
719
+ continue;
720
+ const declared = annotationTypeText(fieldChild(declarator, "type"));
721
+ const value = fieldChild(declarator, "value");
722
+ if (value && (value.type === "arrow_function" || value.type === "function_expression")) {
723
+ visitCallable({
724
+ name: nameNode.text,
725
+ node: declarator,
726
+ paramsNode: fieldChild(value, "parameters") ?? childOfType(value, "formal_parameters"),
727
+ returnTypeNode: fieldChild(value, "return_type"),
728
+ bodyNode: fieldChild(value, "body"),
729
+ annotations: decorators,
730
+ ownerTypeId: moduleTypeId,
731
+ extraModifiers: ["const"],
732
+ entryPoint: entryPointOf(decorators, "", nameNode.text),
733
+ injectable: false,
734
+ isConstructor: false,
735
+ });
736
+ continue;
737
+ }
738
+ if (nameNode.type !== "identifier")
739
+ continue;
740
+ const inferred = declared || inferExpressionType(value);
741
+ addField(moduleTypeId, nameNode.text, inferred, declarator.startPosition.row, decorators);
742
+ if (inferred && inferred !== nameNode.text)
743
+ scopeTypes.set(nameNode.text, inferred);
744
+ }
745
+ }
746
+ // -------------------------------------------------------------- imports
747
+ function addImport(entry) {
748
+ imports.push(entry);
749
+ }
750
+ function bindModule(binding, candidate, external) {
751
+ if (external) {
752
+ externalModules.set(binding, candidate);
753
+ return;
754
+ }
755
+ scopeModules.set(binding, candidate);
756
+ }
757
+ function bindSymbol(binding, candidate, specificModule) {
758
+ if (specificModule === null) {
759
+ externalModules.set(binding, candidate);
760
+ return;
761
+ }
762
+ scopeTypes.set(binding, candidate);
763
+ }
764
+ function stripSpecifierExtension(specifier) {
765
+ return specifier.replace(/\.(m|c)?[jt]sx?$/, "");
766
+ }
767
+ function normalizeResolved(candidate) {
768
+ return candidate.replace(/\.(index|mod)$/, "");
769
+ }
770
+ function handleImportStatement(node) {
771
+ const sourceNode = fieldChild(node, "source") ?? childOfType(node, "string");
772
+ if (!sourceNode)
773
+ return;
774
+ const specifier = stringContent(sourceNode.text);
775
+ const isRelative = specifier.startsWith(".");
776
+ const aliasMatch = specifier.startsWith("@/") || specifier.startsWith("~/");
777
+ const candidateModule = isRelative
778
+ ? normalizeResolved(resolveRelativeModule(moduleId, stripSpecifierExtension(specifier)) ?? specifier)
779
+ : aliasMatch
780
+ ? specifier.slice(2).replace(/\//g, ".")
781
+ : specifier;
782
+ const external = !isRelative && !aliasMatch;
783
+ const moduleCandidate = aliasMatch ? `?suffix:${candidateModule}` : candidateModule;
784
+ const clause = childOfType(node, "import_clause");
785
+ if (!clause) {
786
+ addImport({ fqn: specifier, simpleName: specifier, isStatic: false, module: candidateModule, external });
787
+ return;
788
+ }
789
+ for (const child of clause.namedChildren) {
790
+ if (child.type === "identifier") {
791
+ const binding = child.text;
792
+ bindSymbol(binding, external ? specifier : `${moduleCandidate}${DEFAULT_EXPORT_SUFFIX}`, external ? null : moduleCandidate);
793
+ addImport({
794
+ fqn: external ? `${specifier}.default` : `${candidateModule}.default`,
795
+ simpleName: binding,
796
+ isStatic: false,
797
+ module: candidateModule,
798
+ importedName: "default",
799
+ external,
800
+ });
801
+ }
802
+ else if (child.type === "namespace_import") {
803
+ const nameNode = childOfType(child, "identifier");
804
+ if (!nameNode)
805
+ continue;
806
+ bindModule(nameNode.text, moduleCandidate, external);
807
+ addImport({
808
+ fqn: candidateModule,
809
+ simpleName: nameNode.text,
810
+ isStatic: false,
811
+ module: candidateModule,
812
+ isNamespace: true,
813
+ alias: nameNode.text,
814
+ external,
815
+ });
816
+ }
817
+ else if (child.type === "named_imports") {
818
+ for (const specifierNode of childrenOfType(child, "import_specifier")) {
819
+ const nameNode = fieldChild(specifierNode, "name") ?? specifierNode.namedChildren[0];
820
+ const aliasNode = fieldChild(specifierNode, "alias");
821
+ if (!nameNode)
822
+ continue;
823
+ const importedName = nameNode.text;
824
+ const binding = aliasNode?.text ?? importedName;
825
+ bindSymbol(binding, external ? specifier : `${moduleCandidate}.${importedName}`, external ? null : moduleCandidate);
826
+ addImport({
827
+ fqn: external ? `${specifier}.${importedName}` : `${candidateModule}.${importedName}`,
828
+ simpleName: binding,
829
+ isStatic: false,
830
+ module: candidateModule,
831
+ importedName,
832
+ alias: aliasNode?.text,
833
+ external,
834
+ });
835
+ }
836
+ }
837
+ }
838
+ }
839
+ // ------------------------------------------------- top-level declarations
840
+ function handleDeclaration(node, decorators, exported) {
841
+ switch (node.type) {
842
+ case "class_declaration":
843
+ case "abstract_class_declaration":
844
+ visitClass(node, decorators, null, exported);
845
+ break;
846
+ case "interface_declaration":
847
+ visitInterface(node, decorators, exported);
848
+ break;
849
+ case "enum_declaration":
850
+ visitEnum(node, decorators, exported);
851
+ break;
852
+ case "type_alias_declaration":
853
+ visitTypeAlias(node, decorators, exported);
854
+ break;
855
+ case "function_declaration":
856
+ case "generator_function_declaration":
857
+ visitFunctionDeclaration(node, decorators);
858
+ break;
859
+ case "lexical_declaration":
860
+ visitLexicalDeclaration(node, decorators);
861
+ break;
862
+ default:
863
+ break;
864
+ }
865
+ }
866
+ const programBuffer = [];
867
+ for (const child of root.namedChildren) {
868
+ if (child.type === "decorator") {
869
+ programBuffer.push(child);
870
+ continue;
871
+ }
872
+ if (child.type === "import_statement") {
873
+ handleImportStatement(child);
874
+ continue;
875
+ }
876
+ if (child.type === "export_statement") {
877
+ const declaration = fieldChild(child, "declaration");
878
+ const decorators = [
879
+ ...programBuffer.splice(0, programBuffer.length),
880
+ ...childrenOfType(child, "decorator"),
881
+ ];
882
+ if (declaration) {
883
+ handleDeclaration(declaration, annotationsOf(decorators), true);
884
+ if (/^\s*export\s+default\b/.test(child.text)) {
885
+ const nameNode = fieldChild(declaration, "name");
886
+ if (nameNode)
887
+ defaultExportName = nameNode.text;
888
+ }
889
+ }
890
+ else {
891
+ const value = fieldChild(child, "value") ?? child.namedChildren.find((c) => c.type === "identifier");
892
+ if (value && (value.type === "identifier" || value.type === "member_expression")) {
893
+ defaultExportName = value.text.split(".").pop() ?? value.text;
894
+ }
895
+ for (const clause of childrenOfType(child, "export_clause")) {
896
+ for (const spec of childrenOfType(clause, "export_specifier")) {
897
+ const name = fieldChild(spec, "name");
898
+ const alias = fieldChild(spec, "alias");
899
+ if (name)
900
+ scopeTypes.set(alias?.text ?? name.text, `${moduleId}.${name.text}`);
901
+ }
902
+ }
903
+ }
904
+ continue;
905
+ }
906
+ const decorators = programBuffer.splice(0, programBuffer.length);
907
+ handleDeclaration(child, annotationsOf(decorators), false);
908
+ }
909
+ // ------------------------------------------------------------ call sites
910
+ function enclosingOf(line) {
911
+ let typeId = moduleTypeId;
912
+ let typeStart = -1;
913
+ for (const range of typeRanges) {
914
+ if (line >= range.startLine && line <= range.endLine && range.startLine >= typeStart) {
915
+ typeStart = range.startLine;
916
+ typeId = range.id;
917
+ }
918
+ }
919
+ let methodId = typeId;
920
+ let methodStart = -1;
921
+ for (const range of methodRanges) {
922
+ if (line >= range.startLine && line <= range.endLine && range.startLine >= methodStart) {
923
+ methodStart = range.startLine;
924
+ methodId = range.id;
925
+ }
926
+ }
927
+ return { typeId, methodId };
928
+ }
929
+ walk(root, (node) => {
930
+ if (node.type !== "call_expression" && node.type !== "new_expression")
931
+ return;
932
+ if (insideAny(node, SKIP_CALL_ANCESTORS))
933
+ return;
934
+ const enclosing = enclosingOf(node.startPosition.row);
935
+ const controlFlow = controlFlowOf(node, TS_FLOWS);
936
+ const argsNode = fieldChild(node, "arguments");
937
+ const args = argsNode
938
+ ? argsNode.namedChildren.filter((c) => c.type !== "comment").map((c) => c.text)
939
+ : [];
940
+ const base = {
941
+ line: node.startPosition.row,
942
+ argumentTexts: args,
943
+ enclosingTypeId: enclosing.typeId,
944
+ enclosingMethodId: enclosing.methodId,
945
+ controlFlow,
946
+ };
947
+ if (node.type === "new_expression") {
948
+ const ctor = fieldChild(node, "constructor") ?? node.namedChildren[0];
949
+ callSites.push({
950
+ ...base,
951
+ receiver: null,
952
+ name: typeNameOf(ctor ?? null),
953
+ isConstructorCall: true,
954
+ qualifiedReceiver: false,
955
+ });
956
+ return;
957
+ }
958
+ const fn = fieldChild(node, "function") ?? node.namedChildren[0];
959
+ if (!fn)
960
+ return;
961
+ // Dynamic import is a run-time module lookup, not a normal call.
962
+ if (fn.type === "import") {
963
+ const target = args[0] ? stringContent(args[0]) : "";
964
+ if (target) {
965
+ reflectiveRefs.push({
966
+ mechanism: "import()",
967
+ targetKind: "module",
968
+ target,
969
+ enclosingTypeId: enclosing.typeId,
970
+ enclosingMethodId: enclosing.methodId,
971
+ line: node.startPosition.row,
972
+ });
973
+ }
974
+ return;
975
+ }
976
+ if (fn.type === "identifier") {
977
+ const name = fn.text;
978
+ callSites.push({
979
+ ...base,
980
+ receiver: null,
981
+ name,
982
+ isConstructorCall: /^[A-Z]/.test(name),
983
+ qualifiedReceiver: false,
984
+ });
985
+ if (name === "require" && args[0]) {
986
+ const target = stringContent(args[0]);
987
+ if (target) {
988
+ reflectiveRefs.push({
989
+ mechanism: "require",
990
+ targetKind: "module",
991
+ target,
992
+ enclosingTypeId: enclosing.typeId,
993
+ enclosingMethodId: enclosing.methodId,
994
+ line: node.startPosition.row,
995
+ });
996
+ }
997
+ }
998
+ return;
999
+ }
1000
+ if (fn.type === "member_expression") {
1001
+ const object = fieldChild(fn, "object");
1002
+ const property = fieldChild(fn, "property");
1003
+ if (!property)
1004
+ return;
1005
+ let receiver = object ? object.text : null;
1006
+ if (receiver && receiver.endsWith("!"))
1007
+ receiver = receiver.slice(0, -1);
1008
+ const name = property.text;
1009
+ callSites.push({
1010
+ ...base,
1011
+ receiver,
1012
+ name,
1013
+ isConstructorCall: false,
1014
+ qualifiedReceiver: !!receiver && /^[A-Z]/.test(receiver),
1015
+ });
1016
+ if (object && object.type === "identifier" && object.text === "Reflect") {
1017
+ const target = args[1] ? stringContent(args[1]) : "";
1018
+ if (target && args[1] && target !== args[1].trim()) {
1019
+ reflectiveRefs.push({
1020
+ mechanism: `Reflect.${name}`,
1021
+ targetKind: "method",
1022
+ target,
1023
+ receiver: args[0],
1024
+ enclosingTypeId: enclosing.typeId,
1025
+ enclosingMethodId: enclosing.methodId,
1026
+ line: node.startPosition.row,
1027
+ });
1028
+ }
1029
+ }
1030
+ const rootReceiver = object
1031
+ ? object.type === "identifier"
1032
+ ? object.text
1033
+ : object.type === "this"
1034
+ ? "this"
1035
+ : ""
1036
+ : "";
1037
+ if (HTTP_METHODS.has(name.toLowerCase()) && ROUTE_RECEIVERS.has(rootReceiver) && args.length >= 2) {
1038
+ const path = stringContent(args[0] ?? "");
1039
+ const handlerNode = argsNode?.namedChildren[1] ?? null;
1040
+ // Only a plain reference names a handler; anything else (arrow,
1041
+ // function expression) is an inline root we index synthetically.
1042
+ let handler = "";
1043
+ if (handlerNode &&
1044
+ (handlerNode.type === "identifier" || handlerNode.type === "member_expression")) {
1045
+ handler = typeNameOf(handlerNode);
1046
+ }
1047
+ if (!handler &&
1048
+ handlerNode &&
1049
+ (handlerNode.type === "arrow_function" || handlerNode.type === "function_expression")) {
1050
+ // Inline handlers are framework-invoked roots with no source name,
1051
+ // so give them a stable synthetic id and index them as methods.
1052
+ handler = `<anonymous@${node.startPosition.row + 1}>`;
1053
+ visitCallable({
1054
+ name: handler,
1055
+ node: handlerNode,
1056
+ paramsNode: fieldChild(handlerNode, "parameters") ?? childOfType(handlerNode, "formal_parameters"),
1057
+ returnTypeNode: fieldChild(handlerNode, "return_type"),
1058
+ bodyNode: fieldChild(handlerNode, "body"),
1059
+ annotations: [],
1060
+ ownerTypeId: moduleTypeId,
1061
+ extraModifiers: ["inline"],
1062
+ entryPoint: { kind: "http", detail: `${name.toUpperCase()} ${path}`.trim() },
1063
+ injectable: false,
1064
+ isConstructor: false,
1065
+ });
1066
+ }
1067
+ if (handler) {
1068
+ routes.push({
1069
+ framework: rootReceiver === "fastify" ? "fastify" : "express",
1070
+ httpMethod: name.toUpperCase(),
1071
+ path,
1072
+ handlerName: handler,
1073
+ moduleId,
1074
+ enclosingTypeId: enclosing.typeId,
1075
+ line: node.startPosition.row,
1076
+ });
1077
+ }
1078
+ }
1079
+ return;
1080
+ }
1081
+ callSites.push({
1082
+ ...base,
1083
+ receiver: fn.text,
1084
+ name: typeNameOf(fn) || fn.text,
1085
+ isConstructorCall: false,
1086
+ qualifiedReceiver: true,
1087
+ });
1088
+ });
1089
+ tree.delete();
1090
+ return {
1091
+ filePath,
1092
+ language: "typescript",
1093
+ packageName: null,
1094
+ imports,
1095
+ types,
1096
+ methods,
1097
+ fields,
1098
+ callSites,
1099
+ localsByMethod,
1100
+ moduleId,
1101
+ scopeTypes,
1102
+ scopeModules,
1103
+ externalModules,
1104
+ defaultExportName,
1105
+ reflectiveRefs,
1106
+ injections,
1107
+ routes,
1108
+ };
1109
+ },
1110
+ };
1111
+ //# sourceMappingURL=typescript.js.map