ctxloom-pro 1.0.4 → 1.0.6

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.
@@ -1,1599 +0,0 @@
1
- import {
2
- logger
3
- } from "./chunk-IHXVD5SO.js";
4
-
5
- // src/ast/ASTParser.js
6
- import * as TreeSitter from "web-tree-sitter";
7
- import fs2 from "fs";
8
- import path2 from "path";
9
- import { fileURLToPath } from "url";
10
- import { createRequire } from "module";
11
-
12
- // src/grammars/GrammarLoader.js
13
- import fs from "fs";
14
- import path from "path";
15
- import os from "os";
16
- import https from "https";
17
- import crypto from "crypto";
18
-
19
- // src/grammars/grammar-manifest.js
20
- var GRAMMAR_MANIFEST = [
21
- {
22
- language: "python",
23
- extensions: [".py"],
24
- npmPackage: "tree-sitter-python",
25
- version: "0.23.6",
26
- wasmFile: "tree-sitter-python.wasm",
27
- sha256: null
28
- // TODO: populate after first download with: shasum -a 256 tree-sitter-python.wasm
29
- },
30
- {
31
- language: "go",
32
- extensions: [".go"],
33
- npmPackage: "tree-sitter-go",
34
- version: "0.23.4",
35
- wasmFile: "tree-sitter-go.wasm",
36
- sha256: null
37
- },
38
- {
39
- language: "rust",
40
- extensions: [".rs"],
41
- npmPackage: "tree-sitter-rust",
42
- version: "0.23.2",
43
- wasmFile: "tree-sitter-rust.wasm",
44
- sha256: null
45
- },
46
- {
47
- language: "java",
48
- extensions: [".java"],
49
- npmPackage: "tree-sitter-java",
50
- version: "0.23.5",
51
- wasmFile: "tree-sitter-java.wasm",
52
- sha256: null
53
- },
54
- {
55
- language: "csharp",
56
- extensions: [".cs"],
57
- npmPackage: "@vscode/tree-sitter-wasm",
58
- version: "0.3.1",
59
- wasmFile: "wasm/tree-sitter-c-sharp.wasm",
60
- sha256: null
61
- },
62
- {
63
- language: "ruby",
64
- extensions: [".rb"],
65
- npmPackage: "tree-sitter-ruby",
66
- version: "0.23.1",
67
- wasmFile: "tree-sitter-ruby.wasm",
68
- sha256: null
69
- },
70
- {
71
- language: "kotlin",
72
- extensions: [".kt", ".kts"],
73
- npmPackage: "tree-sitter-kotlin",
74
- version: "0.3.8",
75
- wasmFile: "tree-sitter-kotlin.wasm",
76
- sha256: null
77
- },
78
- {
79
- language: "swift",
80
- extensions: [".swift"],
81
- npmPackage: "tree-sitter-swift",
82
- version: "0.7.1",
83
- wasmFile: "tree-sitter-swift.wasm",
84
- sha256: null
85
- },
86
- {
87
- language: "php",
88
- extensions: [".php"],
89
- npmPackage: "tree-sitter-php",
90
- version: "0.23.11",
91
- wasmFile: "tree-sitter-php.wasm",
92
- sha256: null
93
- },
94
- {
95
- language: "dart",
96
- extensions: [".dart"],
97
- npmPackage: "tree-sitter-dart",
98
- version: "1.0.0",
99
- wasmFile: "tree-sitter-dart.wasm",
100
- sha256: null
101
- }
102
- ];
103
- function findGrammar(language) {
104
- return GRAMMAR_MANIFEST.find((g) => g.language === language);
105
- }
106
-
107
- // src/grammars/GrammarLoader.js
108
- var DEFAULT_CDN = "https://cdn.jsdelivr.net/npm";
109
- var DEFAULT_CACHE_DIR = path.join(os.homedir(), ".ctxloom", "grammars");
110
- var GrammarLoader = class {
111
- cacheDir;
112
- cdn;
113
- skipVerify;
114
- constructor(cacheDir = DEFAULT_CACHE_DIR) {
115
- const envCdn = process.env.CTXLOOM_GRAMMAR_CDN ?? "";
116
- this.skipVerify = envCdn === "unsafe";
117
- this.cdn = this.skipVerify ? DEFAULT_CDN : envCdn || DEFAULT_CDN;
118
- this.cacheDir = cacheDir;
119
- }
120
- /** List all known grammars and their cache status. */
121
- listGrammars() {
122
- return GRAMMAR_MANIFEST.map((entry) => {
123
- const cachedPath = this.getCachedPath(entry.language);
124
- return {
125
- language: entry.language,
126
- extensions: entry.extensions,
127
- version: entry.version,
128
- status: cachedPath !== null ? "cached" : "missing",
129
- cachedPath
130
- };
131
- });
132
- }
133
- /** Returns the cached WASM path if it exists, null otherwise. */
134
- getCachedPath(language) {
135
- const entry = findGrammar(language);
136
- if (!entry)
137
- return null;
138
- const p = path.join(this.cacheDir, entry.wasmFile);
139
- return fs.existsSync(p) ? p : null;
140
- }
141
- isCached(language) {
142
- return this.getCachedPath(language) !== null;
143
- }
144
- /**
145
- * Ensures the grammar WASM is present in the cache.
146
- * Downloads and verifies if missing. Returns the local path.
147
- */
148
- async ensureGrammar(language) {
149
- const entry = findGrammar(language);
150
- if (!entry)
151
- throw new Error(`Unknown grammar: ${language}`);
152
- const cached = this.getCachedPath(language);
153
- if (cached)
154
- return cached;
155
- const url = entry.downloadUrl?.trim() ? entry.downloadUrl : `${this.cdn}/${entry.npmPackage}@${entry.version}/${entry.wasmFile}`;
156
- const dest = path.join(this.cacheDir, entry.wasmFile);
157
- logger.info("Downloading grammar", { language, url, source: entry.downloadUrl?.trim() ? "custom" : "cdn" });
158
- fs.mkdirSync(this.cacheDir, { recursive: true });
159
- await this.download(url, dest);
160
- if (entry.sha256 && !this.skipVerify) {
161
- await this.verifyHash(dest, entry.sha256, language);
162
- } else if (!entry.sha256) {
163
- logger.warn("Grammar SHA-256 not set \u2014 skipping verification", { language });
164
- }
165
- logger.info("Grammar cached", { language, path: dest });
166
- return dest;
167
- }
168
- download(url, dest, redirectsLeft = 5) {
169
- return new Promise((resolve, reject) => {
170
- const tmp = dest + ".tmp";
171
- const file = fs.createWriteStream(tmp);
172
- const request = https.get(url, (response) => {
173
- if (response.statusCode === 301 || response.statusCode === 302) {
174
- const location = response.headers.location;
175
- if (!location) {
176
- reject(new Error(`Redirect with no location from ${url}`));
177
- return;
178
- }
179
- if (redirectsLeft <= 0) {
180
- reject(new Error(`Too many redirects from ${url}`));
181
- return;
182
- }
183
- response.resume();
184
- file.close();
185
- fs.rmSync(tmp, { force: true });
186
- this.download(location, dest, redirectsLeft - 1).then(resolve).catch(reject);
187
- return;
188
- }
189
- if (response.statusCode !== 200) {
190
- file.close();
191
- fs.rmSync(tmp, { force: true });
192
- reject(new Error(`Failed to download grammar from ${url}: HTTP ${response.statusCode}`));
193
- return;
194
- }
195
- response.pipe(file);
196
- response.on("error", (err) => {
197
- file.destroy();
198
- fs.rmSync(tmp, { force: true });
199
- reject(err);
200
- });
201
- file.on("error", (err) => {
202
- file.destroy();
203
- fs.rmSync(tmp, { force: true });
204
- reject(err);
205
- });
206
- file.on("finish", () => {
207
- fs.renameSync(tmp, dest);
208
- resolve();
209
- });
210
- });
211
- request.on("error", (err) => {
212
- file.close();
213
- fs.rmSync(tmp, { force: true });
214
- reject(err);
215
- });
216
- });
217
- }
218
- async verifyHash(filePath, expectedHex, language) {
219
- const buf = fs.readFileSync(filePath);
220
- const actual = crypto.createHash("sha256").update(buf).digest("hex");
221
- if (actual !== expectedHex) {
222
- fs.rmSync(filePath, { force: true });
223
- throw new Error(`SHA-256 mismatch for ${language} grammar.
224
- Expected: ${expectedHex}
225
- Got: ${actual}
226
- The CDN may have served a different version. Update grammar-manifest.ts.`);
227
- }
228
- }
229
- };
230
-
231
- // src/utils/notebookExtractor.js
232
- function parseNotebook(content) {
233
- try {
234
- return JSON.parse(content);
235
- } catch {
236
- return { cells: [], metadata: {} };
237
- }
238
- }
239
- function cellSource(cell) {
240
- if (Array.isArray(cell.source))
241
- return cell.source.join("");
242
- return typeof cell.source === "string" ? cell.source : "";
243
- }
244
- function extractNotebookPythonSource(content) {
245
- const nb = parseNotebook(content);
246
- if (!nb.cells)
247
- return "";
248
- return nb.cells.filter((c) => c.cell_type === "code").map((c) => cellSource(c)).join("\n");
249
- }
250
-
251
- // src/ast/ASTParser.js
252
- var __dirname = path2.dirname(fileURLToPath(import.meta.url));
253
- function findWasmDir() {
254
- const candidates = [
255
- // Built output: __dirname is dist/ → wasm/ is right next to it
256
- path2.join(__dirname, "wasm"),
257
- // Source mode: __dirname is src/ast/ → need to go up to project root, then dist/wasm
258
- path2.join(__dirname, "..", "..", "dist", "wasm"),
259
- // node_modules location (tree-sitter.wasm lives inside web-tree-sitter)
260
- path2.join(__dirname, "..", "node_modules"),
261
- path2.join(__dirname, "..", "..", "node_modules")
262
- ];
263
- for (const dir of candidates) {
264
- if (fs2.existsSync(path2.join(dir, "tree-sitter.wasm"))) {
265
- return dir;
266
- }
267
- }
268
- try {
269
- const _require = createRequire(import.meta.url);
270
- const pkgPath = _require.resolve("web-tree-sitter/package.json");
271
- const pkgDir = path2.dirname(pkgPath);
272
- if (fs2.existsSync(path2.join(pkgDir, "tree-sitter.wasm"))) {
273
- return pkgDir;
274
- }
275
- } catch {
276
- }
277
- return path2.join(__dirname, "wasm");
278
- }
279
- var WASM_DIR = findWasmDir();
280
- var ASTParser = class {
281
- tsLang = null;
282
- pyLang = null;
283
- goLang = null;
284
- rustLang = null;
285
- javaLang = null;
286
- csLang = null;
287
- rubyLang = null;
288
- kotlinLang = null;
289
- swiftLang = null;
290
- phpLang = null;
291
- dartLang = null;
292
- grammarLoader = new GrammarLoader();
293
- async init() {
294
- await TreeSitter.Parser.init({
295
- locateFile: () => path2.join(WASM_DIR, "tree-sitter.wasm")
296
- });
297
- const grammarCandidates = [
298
- path2.join(WASM_DIR, "tree-sitter-typescript.wasm"),
299
- path2.join(WASM_DIR, "tree-sitter-typescript", "tree-sitter-typescript.wasm"),
300
- // Also check node_modules for the grammar
301
- path2.join(__dirname, "..", "node_modules", "web-tree-sitter", "tree-sitter-typescript.wasm"),
302
- path2.join(__dirname, "..", "..", "node_modules", "web-tree-sitter", "tree-sitter-typescript.wasm")
303
- ];
304
- let grammarPath = "";
305
- for (const candidate of grammarCandidates) {
306
- if (fs2.existsSync(candidate)) {
307
- grammarPath = candidate;
308
- break;
309
- }
310
- }
311
- if (!grammarPath) {
312
- throw new Error("Could not locate tree-sitter-typescript.wasm grammar file");
313
- }
314
- this.tsLang = await TreeSitter.Language.load(grammarPath);
315
- }
316
- /**
317
- * Load Python grammar on demand. Downloads and caches WASM if needed.
318
- */
319
- async loadPython() {
320
- if (this.pyLang)
321
- return;
322
- try {
323
- const wasmPath = await this.grammarLoader.ensureGrammar("python");
324
- this.pyLang = await TreeSitter.Language.load(wasmPath);
325
- } catch (err) {
326
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
327
- logger2.warn("Python grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
328
- }
329
- }
330
- /**
331
- * Load Go grammar on demand. Downloads and caches WASM if needed.
332
- */
333
- async loadGo() {
334
- if (this.goLang)
335
- return;
336
- try {
337
- const wasmPath = await this.grammarLoader.ensureGrammar("go");
338
- this.goLang = await TreeSitter.Language.load(wasmPath);
339
- } catch (err) {
340
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
341
- logger2.warn("Go grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
342
- }
343
- }
344
- /**
345
- * Load Rust grammar on demand. Downloads and caches WASM if needed.
346
- */
347
- async loadRust() {
348
- if (this.rustLang)
349
- return;
350
- try {
351
- const wasmPath = await this.grammarLoader.ensureGrammar("rust");
352
- this.rustLang = await TreeSitter.Language.load(wasmPath);
353
- } catch (err) {
354
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
355
- logger2.warn("Rust grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
356
- }
357
- }
358
- /**
359
- * Load Java grammar on demand. Downloads and caches WASM if needed.
360
- */
361
- async loadJava() {
362
- if (this.javaLang)
363
- return;
364
- try {
365
- const wasmPath = await this.grammarLoader.ensureGrammar("java");
366
- this.javaLang = await TreeSitter.Language.load(wasmPath);
367
- } catch (err) {
368
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
369
- logger2.warn("Java grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
370
- }
371
- }
372
- async loadCSharp() {
373
- if (this.csLang)
374
- return;
375
- try {
376
- const wasmPath = await this.grammarLoader.ensureGrammar("csharp");
377
- this.csLang = await TreeSitter.Language.load(wasmPath);
378
- } catch (err) {
379
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
380
- logger2.warn("C# grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
381
- }
382
- }
383
- async loadRuby() {
384
- if (this.rubyLang)
385
- return;
386
- try {
387
- const wasmPath = await this.grammarLoader.ensureGrammar("ruby");
388
- this.rubyLang = await TreeSitter.Language.load(wasmPath);
389
- } catch (err) {
390
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
391
- logger2.warn("Ruby grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
392
- }
393
- }
394
- async loadKotlin() {
395
- if (this.kotlinLang)
396
- return;
397
- try {
398
- const wasmPath = await this.grammarLoader.ensureGrammar("kotlin");
399
- this.kotlinLang = await TreeSitter.Language.load(wasmPath);
400
- } catch (err) {
401
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
402
- logger2.warn("Kotlin grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
403
- }
404
- }
405
- async loadSwift() {
406
- if (this.swiftLang)
407
- return;
408
- try {
409
- const wasmPath = await this.grammarLoader.ensureGrammar("swift");
410
- this.swiftLang = await TreeSitter.Language.load(wasmPath);
411
- } catch (err) {
412
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
413
- logger2.warn("Swift grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
414
- }
415
- }
416
- async loadPhp() {
417
- if (this.phpLang)
418
- return;
419
- try {
420
- const wasmPath = await this.grammarLoader.ensureGrammar("php");
421
- this.phpLang = await TreeSitter.Language.load(wasmPath);
422
- } catch (err) {
423
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
424
- logger2.warn("PHP grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
425
- }
426
- }
427
- async loadDart() {
428
- if (this.dartLang)
429
- return;
430
- try {
431
- const wasmPath = await this.grammarLoader.ensureGrammar("dart");
432
- this.dartLang = await TreeSitter.Language.load(wasmPath);
433
- } catch (err) {
434
- const { logger: logger2 } = await import("./logger-2FVN3AGZ.js");
435
- logger2.warn("Dart grammar unavailable", { detail: err instanceof Error ? err.message : String(err) });
436
- }
437
- }
438
- async parse(filePath) {
439
- if (!this.tsLang)
440
- throw new Error("ASTParser not initialized. Call init() first.");
441
- const ext = path2.extname(filePath).toLowerCase();
442
- if (ext === ".ipynb")
443
- return this.parseNotebook(filePath);
444
- if (ext === ".py") {
445
- return this.parsePython(filePath);
446
- }
447
- if (ext === ".go")
448
- return this.parseGo(filePath);
449
- if (ext === ".rs")
450
- return this.parseRust(filePath);
451
- if (ext === ".java")
452
- return this.parseJava(filePath);
453
- if (ext === ".cs")
454
- return this.parseCSharp(filePath);
455
- if (ext === ".rb")
456
- return this.parseRuby(filePath);
457
- if (ext === ".kt" || ext === ".kts")
458
- return this.parseKotlin(filePath);
459
- if (ext === ".swift")
460
- return this.parseSwift(filePath);
461
- if (ext === ".php")
462
- return this.parsePhp(filePath);
463
- if (ext === ".dart")
464
- return this.parseDart(filePath);
465
- if (ext === ".vue")
466
- return this.parseVue(filePath);
467
- const parser = new TreeSitter.Parser();
468
- parser.setLanguage(this.tsLang);
469
- const source = fs2.readFileSync(filePath, "utf-8");
470
- const tree = parser.parse(source);
471
- if (!tree)
472
- return [];
473
- return this.extractTSNodes(tree.rootNode, filePath, source.split("\n"));
474
- }
475
- extractTSNodes(rootNode, _filePath, lines) {
476
- const nodes = [];
477
- const processedIds = /* @__PURE__ */ new Set();
478
- const walk = (node) => {
479
- if (processedIds.has(node.id))
480
- return;
481
- switch (node.type) {
482
- // ─── Import statements (both grammar versions) ────────────────────
483
- case "import_statement":
484
- case "import_declaration": {
485
- const srcNode = node.children.find((c) => c?.type === "string");
486
- if (srcNode) {
487
- nodes.push({
488
- type: "import",
489
- name: srcNode.text.replace(/['"]/g, ""),
490
- source: srcNode.text.replace(/['"]/g, ""),
491
- startLine: node.startPosition.row + 1,
492
- endLine: node.endPosition.row + 1
493
- });
494
- }
495
- processedIds.add(node.id);
496
- return;
497
- }
498
- // ─── Export statements (export function, export default) ────────
499
- case "export_statement": {
500
- const hasDefault = node.children.some((c) => c?.type === "default");
501
- if (hasDefault) {
502
- const innerFunc = node.children.find((c) => c?.type === "function_declaration");
503
- const innerClass = node.children.find((c) => c?.type === "class_declaration");
504
- if (innerFunc) {
505
- processedIds.add(innerFunc.id);
506
- const nameNode = innerFunc.childForFieldName?.("name") ?? innerFunc.children.find((c) => c?.type === "identifier");
507
- const sig = lines[node.startPosition.row] ?? "";
508
- nodes.push({
509
- type: "export_default",
510
- name: nameNode?.text ?? "default",
511
- signature: sig.trim(),
512
- startLine: node.startPosition.row + 1,
513
- endLine: node.endPosition.row + 1
514
- });
515
- } else if (innerClass) {
516
- processedIds.add(innerClass.id);
517
- const nameNode = innerClass.childForFieldName?.("name");
518
- const sig = lines[node.startPosition.row] ?? "";
519
- nodes.push({
520
- type: "export_default",
521
- name: nameNode?.text ?? "default",
522
- signature: sig.trim(),
523
- startLine: node.startPosition.row + 1,
524
- endLine: node.endPosition.row + 1
525
- });
526
- } else {
527
- const exprChild = node.children.find((c) => c?.type === "identifier" || c?.type === "call_expression" || c?.type === "class");
528
- if (exprChild) {
529
- const sig = lines[node.startPosition.row] ?? "";
530
- nodes.push({
531
- type: "export_default",
532
- name: exprChild.text.split("(")[0].trim().slice(0, 50),
533
- signature: sig.trim(),
534
- startLine: node.startPosition.row + 1,
535
- endLine: node.endPosition.row + 1
536
- });
537
- }
538
- }
539
- } else {
540
- const innerChild = node.children.find((c) => c?.type === "function_declaration" || c?.type === "class_declaration" || c?.type === "interface_declaration" || c?.type === "lexical_declaration" || c?.type === "type_alias_declaration");
541
- if (innerChild) {
542
- walk(innerChild);
543
- }
544
- }
545
- processedIds.add(node.id);
546
- return;
547
- }
548
- // ─── Function declarations ──────────────────────────────────────
549
- case "function_declaration": {
550
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
551
- if (nameNode) {
552
- const sig = lines[node.startPosition.row] ?? "";
553
- nodes.push({
554
- type: "function",
555
- name: nameNode.text,
556
- signature: sig.trim(),
557
- startLine: node.startPosition.row + 1,
558
- endLine: node.endPosition.row + 1
559
- });
560
- }
561
- processedIds.add(node.id);
562
- return;
563
- }
564
- // ─── Class declarations ─────────────────────────────────────────
565
- case "class_declaration": {
566
- const nameNode = node.childForFieldName?.("name");
567
- if (nameNode) {
568
- const body = node.childForFieldName?.("body");
569
- const methodNodes = (body?.children ?? []).filter((c) => c?.type === "method_definition" || c?.type === "public_field_definition");
570
- const methods = methodNodes.map((c) => c.childForFieldName?.("name")?.text ?? "").filter(Boolean);
571
- const methodRanges = methodNodes.map((c) => {
572
- const mName = c.childForFieldName?.("name")?.text ?? "";
573
- return mName ? { name: mName, signatureLine: c.startPosition.row + 1 } : null;
574
- }).filter((x) => x !== null);
575
- nodes.push({
576
- type: "class",
577
- name: nameNode.text,
578
- signature: `class ${nameNode.text}`,
579
- methods,
580
- methodRanges,
581
- startLine: node.startPosition.row + 1,
582
- endLine: node.endPosition.row + 1
583
- });
584
- }
585
- processedIds.add(node.id);
586
- return;
587
- }
588
- // ─── Interface declarations ─────────────────────────────────────
589
- case "interface_declaration": {
590
- const nameNode = node.childForFieldName?.("name");
591
- if (nameNode) {
592
- nodes.push({
593
- type: "interface",
594
- name: nameNode.text,
595
- signature: `interface ${nameNode.text}`,
596
- startLine: node.startPosition.row + 1,
597
- endLine: node.endPosition.row + 1
598
- });
599
- }
600
- processedIds.add(node.id);
601
- return;
602
- }
603
- // ─── Lexical declarations (const fn = () => {}) ────────────────
604
- case "lexical_declaration": {
605
- for (const child of node.children) {
606
- if (!child || child.type !== "variable_declarator")
607
- continue;
608
- const declarator = child;
609
- const nameNode = declarator.childForFieldName?.("name");
610
- const valueNode = declarator.childForFieldName?.("value");
611
- if (nameNode && valueNode) {
612
- if (valueNode.type === "arrow_function" || valueNode.type === "function") {
613
- const sig = lines[declarator.startPosition.row] ?? "";
614
- nodes.push({
615
- type: "arrow_function",
616
- name: nameNode.text,
617
- signature: sig.trim().replace(/\s*\=>\s*\{.*$/, " => ..."),
618
- startLine: declarator.startPosition.row + 1,
619
- endLine: declarator.endPosition.row + 1
620
- });
621
- }
622
- }
623
- }
624
- processedIds.add(node.id);
625
- return;
626
- }
627
- }
628
- for (const child of node.children) {
629
- if (child)
630
- walk(child);
631
- }
632
- };
633
- walk(rootNode);
634
- return nodes;
635
- }
636
- async parseVue(filePath) {
637
- let source;
638
- try {
639
- source = fs2.readFileSync(filePath, "utf-8");
640
- } catch {
641
- return [];
642
- }
643
- const match = source.match(/<script(?:\s[^>]*)?>([^]*?)<\/script>/i);
644
- if (!match?.[1]?.trim())
645
- return [];
646
- const scriptContent = match[1];
647
- if (!this.tsLang)
648
- return [];
649
- const parser = new TreeSitter.Parser();
650
- parser.setLanguage(this.tsLang);
651
- const tree = parser.parse(scriptContent);
652
- if (!tree)
653
- return [];
654
- return this.extractTSNodes(tree.rootNode, filePath, scriptContent.split("\n"));
655
- }
656
- async parsePython(filePath) {
657
- if (!this.pyLang)
658
- await this.loadPython();
659
- if (!this.pyLang)
660
- return [];
661
- const parser = new TreeSitter.Parser();
662
- parser.setLanguage(this.pyLang);
663
- const source = fs2.readFileSync(filePath, "utf-8");
664
- const tree = parser.parse(source);
665
- if (!tree)
666
- return [];
667
- return this.extractPythonNodes(tree.rootNode, filePath, source);
668
- }
669
- async parseNotebook(filePath) {
670
- if (!this.pyLang)
671
- await this.loadPython();
672
- if (!this.pyLang)
673
- return [];
674
- try {
675
- const raw = fs2.readFileSync(filePath, "utf-8");
676
- const pythonSource = extractNotebookPythonSource(raw);
677
- if (!pythonSource.trim())
678
- return [];
679
- const parser = new TreeSitter.Parser();
680
- parser.setLanguage(this.pyLang);
681
- const tree = parser.parse(pythonSource);
682
- if (!tree)
683
- return [];
684
- return this.extractPythonNodes(tree.rootNode, filePath, pythonSource);
685
- } catch {
686
- return [];
687
- }
688
- }
689
- extractPythonNodes(rootNode, filePath, source) {
690
- const nodes = [];
691
- const lines = source.split("\n");
692
- void filePath;
693
- const walk = (node) => {
694
- switch (node.type) {
695
- case "import_statement": {
696
- const nameNode = node.children.find((c) => c?.type === "dotted_name" || c?.type === "aliased_import");
697
- if (nameNode) {
698
- nodes.push({
699
- type: "import",
700
- name: nameNode.text,
701
- source: nameNode.text,
702
- startLine: node.startPosition.row + 1,
703
- endLine: node.endPosition.row + 1
704
- });
705
- }
706
- return;
707
- }
708
- case "import_from_statement": {
709
- const moduleNode = node.children.find((c) => c?.type === "dotted_name" || c?.type === "relative_import");
710
- nodes.push({
711
- type: "import",
712
- name: moduleNode?.text ?? "",
713
- source: moduleNode?.text ?? "",
714
- startLine: node.startPosition.row + 1,
715
- endLine: node.endPosition.row + 1
716
- });
717
- return;
718
- }
719
- case "function_definition": {
720
- const nameNode = node.childForFieldName?.("name");
721
- if (nameNode) {
722
- const sig = lines[node.startPosition.row] ?? "";
723
- nodes.push({
724
- type: "function",
725
- name: nameNode.text,
726
- signature: sig.trim(),
727
- startLine: node.startPosition.row + 1,
728
- endLine: node.endPosition.row + 1
729
- });
730
- }
731
- return;
732
- }
733
- case "class_definition": {
734
- const nameNode = node.childForFieldName?.("name");
735
- if (nameNode) {
736
- const body = node.childForFieldName?.("body");
737
- const methods = (body?.children ?? []).filter((c) => c !== null && c.type === "function_definition").map((c) => c.childForFieldName?.("name")?.text ?? "").filter(Boolean);
738
- nodes.push({
739
- type: "class",
740
- name: nameNode.text,
741
- signature: `class ${nameNode.text}`,
742
- methods,
743
- startLine: node.startPosition.row + 1,
744
- endLine: node.endPosition.row + 1
745
- });
746
- }
747
- return;
748
- }
749
- case "decorated_definition": {
750
- const inner = node.children.find((c) => c?.type === "function_definition" || c?.type === "class_definition");
751
- if (inner)
752
- walk(inner);
753
- return;
754
- }
755
- }
756
- for (const child of node.children) {
757
- if (child)
758
- walk(child);
759
- }
760
- };
761
- walk(rootNode);
762
- return nodes;
763
- }
764
- async parseGo(filePath) {
765
- if (!this.goLang)
766
- await this.loadGo();
767
- if (!this.goLang)
768
- return [];
769
- const parser = new TreeSitter.Parser();
770
- parser.setLanguage(this.goLang);
771
- const source = fs2.readFileSync(filePath, "utf-8");
772
- const tree = parser.parse(source);
773
- if (!tree)
774
- return [];
775
- const nodes = [];
776
- const lines = source.split("\n");
777
- const walk = (node) => {
778
- switch (node.type) {
779
- case "function_declaration": {
780
- const nameNode = node.childForFieldName?.("name");
781
- if (nameNode) {
782
- nodes.push({
783
- type: "function",
784
- name: nameNode.text,
785
- signature: (lines[node.startPosition.row] ?? "").trim(),
786
- startLine: node.startPosition.row + 1,
787
- endLine: node.endPosition.row + 1
788
- });
789
- }
790
- return;
791
- }
792
- case "method_declaration": {
793
- const nameNode = node.childForFieldName?.("name");
794
- if (nameNode) {
795
- nodes.push({
796
- type: "function",
797
- name: nameNode.text,
798
- signature: (lines[node.startPosition.row] ?? "").trim(),
799
- startLine: node.startPosition.row + 1,
800
- endLine: node.endPosition.row + 1
801
- });
802
- }
803
- return;
804
- }
805
- case "type_declaration": {
806
- for (const child of node.children) {
807
- if (child?.type === "type_spec") {
808
- const nameNode = child.childForFieldName?.("name");
809
- if (nameNode) {
810
- const typeNode = child.childForFieldName?.("type");
811
- const isInterface = typeNode?.type === "interface_type";
812
- nodes.push({
813
- type: isInterface ? "interface" : "class",
814
- name: nameNode.text,
815
- signature: `type ${nameNode.text} ${typeNode?.type ?? ""}`.trim(),
816
- startLine: child.startPosition.row + 1,
817
- endLine: child.endPosition.row + 1
818
- });
819
- }
820
- }
821
- }
822
- return;
823
- }
824
- case "import_declaration": {
825
- const walkImport = (n) => {
826
- if (n.type === "import_spec") {
827
- const pathNode = n.childForFieldName?.("path");
828
- if (pathNode) {
829
- const spec = pathNode.text.replace(/^"|"$/g, "");
830
- nodes.push({
831
- type: "import",
832
- name: spec,
833
- source: spec,
834
- startLine: n.startPosition.row + 1,
835
- endLine: n.endPosition.row + 1
836
- });
837
- }
838
- }
839
- for (const c of n.children) {
840
- if (c)
841
- walkImport(c);
842
- }
843
- };
844
- walkImport(node);
845
- return;
846
- }
847
- }
848
- for (const child of node.children) {
849
- if (child)
850
- walk(child);
851
- }
852
- };
853
- walk(tree.rootNode);
854
- return nodes;
855
- }
856
- async parseRust(filePath) {
857
- if (!this.rustLang)
858
- await this.loadRust();
859
- if (!this.rustLang)
860
- return [];
861
- const parser = new TreeSitter.Parser();
862
- parser.setLanguage(this.rustLang);
863
- const source = fs2.readFileSync(filePath, "utf-8");
864
- const tree = parser.parse(source);
865
- if (!tree)
866
- return [];
867
- const nodes = [];
868
- const lines = source.split("\n");
869
- const walk = (node) => {
870
- switch (node.type) {
871
- case "function_item": {
872
- const nameNode = node.childForFieldName?.("name");
873
- if (nameNode) {
874
- nodes.push({
875
- type: "function",
876
- name: nameNode.text,
877
- signature: (lines[node.startPosition.row] ?? "").trim(),
878
- startLine: node.startPosition.row + 1,
879
- endLine: node.endPosition.row + 1
880
- });
881
- }
882
- return;
883
- }
884
- case "struct_item": {
885
- const nameNode = node.childForFieldName?.("name");
886
- if (nameNode) {
887
- nodes.push({
888
- type: "class",
889
- name: nameNode.text,
890
- signature: `struct ${nameNode.text}`,
891
- startLine: node.startPosition.row + 1,
892
- endLine: node.endPosition.row + 1
893
- });
894
- }
895
- return;
896
- }
897
- case "enum_item": {
898
- const nameNode = node.childForFieldName?.("name");
899
- if (nameNode) {
900
- nodes.push({
901
- type: "class",
902
- name: nameNode.text,
903
- signature: `enum ${nameNode.text}`,
904
- startLine: node.startPosition.row + 1,
905
- endLine: node.endPosition.row + 1
906
- });
907
- }
908
- return;
909
- }
910
- case "trait_item": {
911
- const nameNode = node.childForFieldName?.("name");
912
- if (nameNode) {
913
- nodes.push({
914
- type: "interface",
915
- name: nameNode.text,
916
- signature: `trait ${nameNode.text}`,
917
- startLine: node.startPosition.row + 1,
918
- endLine: node.endPosition.row + 1
919
- });
920
- }
921
- return;
922
- }
923
- case "impl_item": {
924
- const body = node.childForFieldName?.("body");
925
- if (body) {
926
- for (const child of body.children) {
927
- if (child)
928
- walk(child);
929
- }
930
- }
931
- return;
932
- }
933
- case "mod_item": {
934
- const body = node.childForFieldName?.("body");
935
- if (body)
936
- return;
937
- const nameNode = node.childForFieldName?.("name");
938
- if (nameNode) {
939
- nodes.push({
940
- type: "import",
941
- name: nameNode.text,
942
- source: nameNode.text,
943
- startLine: node.startPosition.row + 1,
944
- endLine: node.endPosition.row + 1
945
- });
946
- }
947
- return;
948
- }
949
- case "use_declaration": {
950
- const arg = node.childForFieldName?.("argument");
951
- if (arg) {
952
- nodes.push({
953
- type: "import",
954
- name: arg.text,
955
- source: arg.text,
956
- startLine: node.startPosition.row + 1,
957
- endLine: node.endPosition.row + 1
958
- });
959
- }
960
- return;
961
- }
962
- }
963
- for (const child of node.children) {
964
- if (child)
965
- walk(child);
966
- }
967
- };
968
- walk(tree.rootNode);
969
- return nodes;
970
- }
971
- async parseJava(filePath) {
972
- if (!this.javaLang)
973
- await this.loadJava();
974
- if (!this.javaLang)
975
- return [];
976
- const parser = new TreeSitter.Parser();
977
- parser.setLanguage(this.javaLang);
978
- const source = fs2.readFileSync(filePath, "utf-8");
979
- const tree = parser.parse(source);
980
- if (!tree)
981
- return [];
982
- const nodes = [];
983
- const lines = source.split("\n");
984
- const walk = (node) => {
985
- switch (node.type) {
986
- case "class_declaration": {
987
- const nameNode = node.childForFieldName?.("name");
988
- if (nameNode) {
989
- const body = node.childForFieldName?.("body");
990
- const methods = (body?.children ?? []).filter((c) => c !== null && (c.type === "method_declaration" || c.type === "constructor_declaration")).map((c) => c.childForFieldName?.("name")?.text ?? "").filter(Boolean);
991
- const methodRanges = (body?.children ?? []).filter((c) => c !== null && (c.type === "method_declaration" || c.type === "constructor_declaration")).map((c) => {
992
- const mName = c.childForFieldName?.("name")?.text ?? "";
993
- return mName ? { name: mName, signatureLine: c.startPosition.row + 1 } : null;
994
- }).filter((x) => x !== null);
995
- nodes.push({
996
- type: "class",
997
- name: nameNode.text,
998
- signature: `class ${nameNode.text}`,
999
- methods,
1000
- methodRanges,
1001
- startLine: node.startPosition.row + 1,
1002
- endLine: node.endPosition.row + 1
1003
- });
1004
- if (body) {
1005
- for (const child of body.children) {
1006
- if (child)
1007
- walk(child);
1008
- }
1009
- }
1010
- }
1011
- return;
1012
- }
1013
- case "interface_declaration": {
1014
- const nameNode = node.childForFieldName?.("name");
1015
- if (nameNode) {
1016
- nodes.push({
1017
- type: "interface",
1018
- name: nameNode.text,
1019
- signature: `interface ${nameNode.text}`,
1020
- startLine: node.startPosition.row + 1,
1021
- endLine: node.endPosition.row + 1
1022
- });
1023
- }
1024
- return;
1025
- }
1026
- case "method_declaration": {
1027
- const nameNode = node.childForFieldName?.("name");
1028
- if (nameNode) {
1029
- nodes.push({
1030
- type: "function",
1031
- name: nameNode.text,
1032
- signature: (lines[node.startPosition.row] ?? "").trim(),
1033
- startLine: node.startPosition.row + 1,
1034
- endLine: node.endPosition.row + 1
1035
- });
1036
- }
1037
- return;
1038
- }
1039
- case "constructor_declaration": {
1040
- const nameNode = node.childForFieldName?.("name");
1041
- if (nameNode) {
1042
- nodes.push({
1043
- type: "function",
1044
- name: nameNode.text,
1045
- signature: (lines[node.startPosition.row] ?? "").trim(),
1046
- startLine: node.startPosition.row + 1,
1047
- endLine: node.endPosition.row + 1
1048
- });
1049
- }
1050
- return;
1051
- }
1052
- case "import_declaration": {
1053
- const child = node.children.find((c) => c?.type === "scoped_identifier" || c?.type === "identifier");
1054
- if (child) {
1055
- nodes.push({
1056
- type: "import",
1057
- name: child.text,
1058
- source: child.text,
1059
- startLine: node.startPosition.row + 1,
1060
- endLine: node.endPosition.row + 1
1061
- });
1062
- }
1063
- return;
1064
- }
1065
- }
1066
- for (const child of node.children) {
1067
- if (child)
1068
- walk(child);
1069
- }
1070
- };
1071
- walk(tree.rootNode);
1072
- return nodes;
1073
- }
1074
- async parseCSharp(filePath) {
1075
- if (!this.csLang)
1076
- await this.loadCSharp();
1077
- if (!this.csLang)
1078
- return [];
1079
- const parser = new TreeSitter.Parser();
1080
- parser.setLanguage(this.csLang);
1081
- const source = fs2.readFileSync(filePath, "utf-8");
1082
- const tree = parser.parse(source);
1083
- if (!tree)
1084
- return [];
1085
- const nodes = [];
1086
- const lines = source.split("\n");
1087
- const walk = (node) => {
1088
- switch (node.type) {
1089
- case "using_directive": {
1090
- const nameNode = node.children.find((c) => c?.type === "identifier" || c?.type === "qualified_name" || c?.type === "name");
1091
- if (nameNode) {
1092
- nodes.push({
1093
- type: "import",
1094
- name: nameNode.text,
1095
- source: nameNode.text,
1096
- startLine: node.startPosition.row + 1,
1097
- endLine: node.endPosition.row + 1
1098
- });
1099
- }
1100
- return;
1101
- }
1102
- case "method_declaration": {
1103
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
1104
- if (nameNode) {
1105
- nodes.push({
1106
- type: "function",
1107
- name: nameNode.text,
1108
- signature: (lines[node.startPosition.row] ?? "").trim(),
1109
- startLine: node.startPosition.row + 1,
1110
- endLine: node.endPosition.row + 1
1111
- });
1112
- }
1113
- return;
1114
- }
1115
- case "class_declaration":
1116
- case "struct_declaration": {
1117
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
1118
- if (nameNode) {
1119
- const body = node.childForFieldName?.("body");
1120
- const methods = (body?.children ?? []).filter((c) => c !== null && c.type === "method_declaration").map((c) => (c.childForFieldName?.("name") ?? c.children.find((ch) => ch?.type === "identifier"))?.text ?? "").filter(Boolean);
1121
- nodes.push({
1122
- type: "class",
1123
- name: nameNode.text,
1124
- signature: `class ${nameNode.text}`,
1125
- methods,
1126
- startLine: node.startPosition.row + 1,
1127
- endLine: node.endPosition.row + 1
1128
- });
1129
- }
1130
- return;
1131
- }
1132
- case "interface_declaration": {
1133
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
1134
- if (nameNode) {
1135
- nodes.push({
1136
- type: "interface",
1137
- name: nameNode.text,
1138
- signature: `interface ${nameNode.text}`,
1139
- startLine: node.startPosition.row + 1,
1140
- endLine: node.endPosition.row + 1
1141
- });
1142
- }
1143
- return;
1144
- }
1145
- }
1146
- for (const child of node.children) {
1147
- if (child)
1148
- walk(child);
1149
- }
1150
- };
1151
- walk(tree.rootNode);
1152
- return nodes;
1153
- }
1154
- async parseRuby(filePath) {
1155
- if (!this.rubyLang)
1156
- await this.loadRuby();
1157
- if (!this.rubyLang)
1158
- return [];
1159
- const parser = new TreeSitter.Parser();
1160
- parser.setLanguage(this.rubyLang);
1161
- const source = fs2.readFileSync(filePath, "utf-8");
1162
- const tree = parser.parse(source);
1163
- if (!tree)
1164
- return [];
1165
- const nodes = [];
1166
- const lines = source.split("\n");
1167
- const walk = (node) => {
1168
- switch (node.type) {
1169
- case "method":
1170
- case "singleton_method": {
1171
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
1172
- if (nameNode) {
1173
- nodes.push({
1174
- type: "function",
1175
- name: nameNode.text,
1176
- signature: (lines[node.startPosition.row] ?? "").trim(),
1177
- startLine: node.startPosition.row + 1,
1178
- endLine: node.endPosition.row + 1
1179
- });
1180
- }
1181
- return;
1182
- }
1183
- case "class": {
1184
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "constant");
1185
- if (nameNode) {
1186
- const body = node.childForFieldName?.("body");
1187
- const methods = (body?.children ?? []).filter((c) => c !== null && (c.type === "method" || c.type === "singleton_method")).map((c) => (c.childForFieldName?.("name") ?? c.children.find((ch) => ch?.type === "identifier"))?.text ?? "").filter(Boolean);
1188
- nodes.push({
1189
- type: "class",
1190
- name: nameNode.text,
1191
- signature: `class ${nameNode.text}`,
1192
- methods,
1193
- startLine: node.startPosition.row + 1,
1194
- endLine: node.endPosition.row + 1
1195
- });
1196
- }
1197
- return;
1198
- }
1199
- case "module": {
1200
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "constant");
1201
- if (nameNode) {
1202
- nodes.push({
1203
- type: "class",
1204
- name: nameNode.text,
1205
- signature: `module ${nameNode.text}`,
1206
- startLine: node.startPosition.row + 1,
1207
- endLine: node.endPosition.row + 1
1208
- });
1209
- }
1210
- return;
1211
- }
1212
- }
1213
- for (const child of node.children) {
1214
- if (child)
1215
- walk(child);
1216
- }
1217
- };
1218
- walk(tree.rootNode);
1219
- return nodes;
1220
- }
1221
- async parseKotlin(filePath) {
1222
- if (!this.kotlinLang)
1223
- await this.loadKotlin();
1224
- if (!this.kotlinLang)
1225
- return [];
1226
- const parser = new TreeSitter.Parser();
1227
- parser.setLanguage(this.kotlinLang);
1228
- const source = fs2.readFileSync(filePath, "utf-8");
1229
- const tree = parser.parse(source);
1230
- if (!tree)
1231
- return [];
1232
- const nodes = [];
1233
- const lines = source.split("\n");
1234
- const walk = (node) => {
1235
- switch (node.type) {
1236
- case "function_declaration": {
1237
- const nameNode = node.children.find((c) => c?.type === "simple_identifier");
1238
- if (nameNode) {
1239
- nodes.push({
1240
- type: "function",
1241
- name: nameNode.text,
1242
- signature: (lines[node.startPosition.row] ?? "").trim(),
1243
- startLine: node.startPosition.row + 1,
1244
- endLine: node.endPosition.row + 1
1245
- });
1246
- }
1247
- return;
1248
- }
1249
- case "class_declaration":
1250
- case "object_declaration": {
1251
- const nameNode = node.children.find((c) => c?.type === "type_identifier" || c?.type === "simple_identifier");
1252
- if (nameNode) {
1253
- nodes.push({
1254
- type: "class",
1255
- name: nameNode.text,
1256
- signature: `class ${nameNode.text}`,
1257
- startLine: node.startPosition.row + 1,
1258
- endLine: node.endPosition.row + 1
1259
- });
1260
- }
1261
- return;
1262
- }
1263
- case "import_header": {
1264
- const identifier = node.children.find((c) => c?.type === "identifier");
1265
- if (identifier) {
1266
- nodes.push({
1267
- type: "import",
1268
- name: identifier.text,
1269
- source: identifier.text,
1270
- startLine: node.startPosition.row + 1,
1271
- endLine: node.endPosition.row + 1
1272
- });
1273
- }
1274
- return;
1275
- }
1276
- }
1277
- for (const child of node.children) {
1278
- if (child)
1279
- walk(child);
1280
- }
1281
- };
1282
- walk(tree.rootNode);
1283
- return nodes;
1284
- }
1285
- async parseSwift(filePath) {
1286
- if (!this.swiftLang)
1287
- await this.loadSwift();
1288
- if (!this.swiftLang)
1289
- return [];
1290
- const parser = new TreeSitter.Parser();
1291
- parser.setLanguage(this.swiftLang);
1292
- const source = fs2.readFileSync(filePath, "utf-8");
1293
- const tree = parser.parse(source);
1294
- if (!tree)
1295
- return [];
1296
- const nodes = [];
1297
- const lines = source.split("\n");
1298
- const walk = (node) => {
1299
- switch (node.type) {
1300
- case "function_declaration": {
1301
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "simple_identifier");
1302
- if (nameNode) {
1303
- nodes.push({
1304
- type: "function",
1305
- name: nameNode.text,
1306
- signature: (lines[node.startPosition.row] ?? "").trim(),
1307
- startLine: node.startPosition.row + 1,
1308
- endLine: node.endPosition.row + 1
1309
- });
1310
- }
1311
- return;
1312
- }
1313
- case "class_declaration": {
1314
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "type_identifier");
1315
- if (nameNode) {
1316
- nodes.push({
1317
- type: "class",
1318
- name: nameNode.text,
1319
- signature: `class ${nameNode.text}`,
1320
- startLine: node.startPosition.row + 1,
1321
- endLine: node.endPosition.row + 1
1322
- });
1323
- }
1324
- return;
1325
- }
1326
- case "protocol_declaration": {
1327
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "type_identifier");
1328
- if (nameNode) {
1329
- nodes.push({
1330
- type: "interface",
1331
- name: nameNode.text,
1332
- signature: `protocol ${nameNode.text}`,
1333
- startLine: node.startPosition.row + 1,
1334
- endLine: node.endPosition.row + 1
1335
- });
1336
- }
1337
- return;
1338
- }
1339
- case "import_declaration": {
1340
- const nameNode = node.children.find((c) => c?.type === "identifier");
1341
- if (nameNode) {
1342
- nodes.push({
1343
- type: "import",
1344
- name: nameNode.text,
1345
- source: nameNode.text,
1346
- startLine: node.startPosition.row + 1,
1347
- endLine: node.endPosition.row + 1
1348
- });
1349
- }
1350
- return;
1351
- }
1352
- }
1353
- for (const child of node.children) {
1354
- if (child)
1355
- walk(child);
1356
- }
1357
- };
1358
- walk(tree.rootNode);
1359
- return nodes;
1360
- }
1361
- async parsePhp(filePath) {
1362
- if (!this.phpLang)
1363
- await this.loadPhp();
1364
- if (!this.phpLang)
1365
- return [];
1366
- const parser = new TreeSitter.Parser();
1367
- parser.setLanguage(this.phpLang);
1368
- const source = fs2.readFileSync(filePath, "utf-8");
1369
- const tree = parser.parse(source);
1370
- if (!tree)
1371
- return [];
1372
- const nodes = [];
1373
- const lines = source.split("\n");
1374
- const walk = (node) => {
1375
- switch (node.type) {
1376
- case "namespace_use_declaration": {
1377
- for (const child of node.children) {
1378
- if (child?.type === "namespace_use_clause") {
1379
- const nameNode = child.children.find((c) => c?.type === "qualified_name" || c?.type === "name");
1380
- if (nameNode) {
1381
- nodes.push({
1382
- type: "import",
1383
- name: nameNode.text,
1384
- source: nameNode.text,
1385
- startLine: node.startPosition.row + 1,
1386
- endLine: node.endPosition.row + 1
1387
- });
1388
- }
1389
- }
1390
- }
1391
- return;
1392
- }
1393
- case "function_definition": {
1394
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "name");
1395
- if (nameNode) {
1396
- nodes.push({
1397
- type: "function",
1398
- name: nameNode.text,
1399
- signature: (lines[node.startPosition.row] ?? "").trim(),
1400
- startLine: node.startPosition.row + 1,
1401
- endLine: node.endPosition.row + 1
1402
- });
1403
- }
1404
- return;
1405
- }
1406
- case "class_declaration": {
1407
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "name");
1408
- if (nameNode) {
1409
- const body = node.childForFieldName?.("body");
1410
- const methods = (body?.children ?? []).filter((c) => c !== null && c.type === "method_declaration").map((c) => (c.childForFieldName?.("name") ?? c.children.find((ch) => ch?.type === "name"))?.text ?? "").filter(Boolean);
1411
- nodes.push({
1412
- type: "class",
1413
- name: nameNode.text,
1414
- signature: `class ${nameNode.text}`,
1415
- methods,
1416
- startLine: node.startPosition.row + 1,
1417
- endLine: node.endPosition.row + 1
1418
- });
1419
- }
1420
- return;
1421
- }
1422
- case "interface_declaration": {
1423
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "name");
1424
- if (nameNode) {
1425
- nodes.push({
1426
- type: "interface",
1427
- name: nameNode.text,
1428
- signature: `interface ${nameNode.text}`,
1429
- startLine: node.startPosition.row + 1,
1430
- endLine: node.endPosition.row + 1
1431
- });
1432
- }
1433
- return;
1434
- }
1435
- }
1436
- for (const child of node.children) {
1437
- if (child)
1438
- walk(child);
1439
- }
1440
- };
1441
- walk(tree.rootNode);
1442
- return nodes;
1443
- }
1444
- async parseDart(filePath) {
1445
- if (!this.dartLang)
1446
- await this.loadDart();
1447
- if (!this.dartLang)
1448
- return [];
1449
- const parser = new TreeSitter.Parser();
1450
- parser.setLanguage(this.dartLang);
1451
- const source = fs2.readFileSync(filePath, "utf-8");
1452
- const tree = parser.parse(source);
1453
- if (!tree)
1454
- return [];
1455
- const nodes = [];
1456
- const lines = source.split("\n");
1457
- const walk = (node) => {
1458
- switch (node.type) {
1459
- case "import_or_export": {
1460
- const uriNode = node.children.find((c) => c?.type === "uri");
1461
- const uri = uriNode?.text?.replace(/['"]/g, "") ?? "";
1462
- if (uri.startsWith(".")) {
1463
- nodes.push({
1464
- type: "import",
1465
- name: uri,
1466
- source: uri,
1467
- startLine: node.startPosition.row + 1,
1468
- endLine: node.endPosition.row + 1
1469
- });
1470
- }
1471
- return;
1472
- }
1473
- case "function_signature":
1474
- case "function_declaration": {
1475
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
1476
- if (nameNode) {
1477
- nodes.push({
1478
- type: "function",
1479
- name: nameNode.text,
1480
- signature: (lines[node.startPosition.row] ?? "").trim(),
1481
- startLine: node.startPosition.row + 1,
1482
- endLine: node.endPosition.row + 1
1483
- });
1484
- }
1485
- return;
1486
- }
1487
- case "class_definition": {
1488
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
1489
- if (nameNode) {
1490
- nodes.push({
1491
- type: "class",
1492
- name: nameNode.text,
1493
- signature: `class ${nameNode.text}`,
1494
- methods: [],
1495
- startLine: node.startPosition.row + 1,
1496
- endLine: node.endPosition.row + 1
1497
- });
1498
- }
1499
- return;
1500
- }
1501
- }
1502
- for (const child of node.children) {
1503
- if (child)
1504
- walk(child);
1505
- }
1506
- };
1507
- walk(tree.rootNode);
1508
- return nodes;
1509
- }
1510
- /**
1511
- * Find all call sites of a symbol in a file.
1512
- */
1513
- async findCallSites(filePath, symbolName) {
1514
- if (!this.tsLang)
1515
- throw new Error("ASTParser not initialized. Call init() first.");
1516
- const parser = new TreeSitter.Parser();
1517
- parser.setLanguage(this.tsLang);
1518
- const source = fs2.readFileSync(filePath, "utf-8");
1519
- const tree = parser.parse(source);
1520
- if (!tree)
1521
- return [];
1522
- const lines = source.split("\n");
1523
- const results = [];
1524
- const walk = (node) => {
1525
- if (node.type === "call_expression" || node.type === "new_expression") {
1526
- const fn = node.childForFieldName?.("function") ?? node.children[0];
1527
- if (fn) {
1528
- const name = fn.type === "identifier" ? fn.text : fn.type === "member_expression" ? fn.childForFieldName?.("property")?.text ?? "" : "";
1529
- if (name === symbolName) {
1530
- const lineIdx = node.startPosition.row;
1531
- results.push({
1532
- filePath,
1533
- line: lineIdx + 1,
1534
- snippet: (lines[lineIdx] ?? "").trim()
1535
- });
1536
- }
1537
- }
1538
- }
1539
- for (const child of node.children) {
1540
- if (child)
1541
- walk(child);
1542
- }
1543
- };
1544
- walk(tree.rootNode);
1545
- return results;
1546
- }
1547
- /**
1548
- * Extract all call edges in a TypeScript/TSX file.
1549
- * Tracks the enclosing function/method context for each call site.
1550
- * Used to populate CallGraphIndex during indexing.
1551
- */
1552
- async parseAllCallEdges(filePath) {
1553
- if (!this.tsLang)
1554
- throw new Error("ASTParser not initialized. Call init() first.");
1555
- const parser = new TreeSitter.Parser();
1556
- parser.setLanguage(this.tsLang);
1557
- const source = fs2.readFileSync(filePath, "utf-8");
1558
- const tree = parser.parse(source);
1559
- if (!tree)
1560
- return [];
1561
- const results = [];
1562
- const walk = (node, contextStack) => {
1563
- let newStack = contextStack;
1564
- if (node.type === "function_declaration" || node.type === "method_definition" || node.type === "arrow_function" || node.type === "function") {
1565
- const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
1566
- const name = nameNode?.text ?? "";
1567
- if (name) {
1568
- newStack = [...contextStack, name];
1569
- }
1570
- }
1571
- if (node.type === "call_expression" || node.type === "new_expression") {
1572
- const fn = node.childForFieldName?.("function") ?? node.children[0];
1573
- if (fn) {
1574
- const name = fn.type === "identifier" ? fn.text : fn.type === "member_expression" ? fn.childForFieldName?.("property")?.text ?? "" : "";
1575
- if (name && name.length > 0) {
1576
- results.push({
1577
- callerSymbol: newStack[newStack.length - 1] ?? "",
1578
- calleeSymbol: name,
1579
- line: node.startPosition.row + 1
1580
- });
1581
- }
1582
- }
1583
- }
1584
- for (const child of node.children) {
1585
- if (child)
1586
- walk(child, newStack);
1587
- }
1588
- };
1589
- walk(tree.rootNode, []);
1590
- return results;
1591
- }
1592
- };
1593
-
1594
- export {
1595
- GrammarLoader,
1596
- extractNotebookPythonSource,
1597
- ASTParser
1598
- };
1599
- //# sourceMappingURL=chunk-5CJIMX6D.js.map