codeviz 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,9 +1,3026 @@
1
+ import {
2
+ AggregateNode,
3
+ BundledEdge,
4
+ CallEdge,
5
+ CodeMap,
6
+ DEFAULT_ANIMATION_CONFIG,
7
+ DEFAULT_CODEMAP_CONFIG,
8
+ DEFAULT_DARK_SIGMA_THEME,
9
+ DEFAULT_LAYOUT_CONFIG,
10
+ DEFAULT_LIGHT_SIGMA_THEME,
11
+ DEFAULT_SIGMA_THEME,
12
+ DEFAULT_ZOOM_LEVELS,
13
+ EDGE_SIZES,
14
+ FileNode,
15
+ Flow,
16
+ ImportEdge,
17
+ NODE_SIZES,
18
+ OverlayLayer,
19
+ OverlayPort,
20
+ PortalOverlayLayer,
21
+ SVGOverlayLayer,
22
+ ScopeNode,
23
+ SigmaRenderer,
24
+ SymbolNode,
25
+ ThemeProvider,
26
+ assignClusterColors,
27
+ assignInitialPositions,
28
+ boundingBoxFromCorners,
29
+ boundingBoxesOverlap,
30
+ brightenColor,
31
+ buildDirectoryGraph,
32
+ calculateNodeSize,
33
+ calculateOverlayPosition,
34
+ clampPosition,
35
+ codeGraphToGraphology,
36
+ codeMapToReactFlowEdges,
37
+ codeMapToReactFlowNodes,
38
+ computeDirectoryForces,
39
+ computeEdges,
40
+ computeHierarchy,
41
+ computeLayout,
42
+ computeZoomLevels,
43
+ createBoundingBox,
44
+ createEdgeReducer,
45
+ createNodeReducer,
46
+ createOverlayPort,
47
+ createScopeColorGetter,
48
+ darkTheme,
49
+ darken,
50
+ detectCommunities,
51
+ detectLanguage,
52
+ dimColor,
53
+ distance,
54
+ edgeTypes,
55
+ expandBoundingBox,
56
+ formatLoc,
57
+ formatNumber,
58
+ generateCallEdgeId,
59
+ generateDirectoryId,
60
+ generateFileId,
61
+ generateImportEdgeId,
62
+ generateScopeDepthColors,
63
+ generateSymbolId,
64
+ getAnchorOffset,
65
+ getBasename,
66
+ getBoundingBoxCenter,
67
+ getChildScopes,
68
+ getClusterStats,
69
+ getContrastRatio,
70
+ getDefaultExpandedScopes,
71
+ getDefaultTheme,
72
+ getDirectory,
73
+ getDirectoryConnectionWeight,
74
+ getDirectoryPosition,
75
+ getEdgeLabel,
76
+ getEdgesAtZoomLevel,
77
+ getEntityId,
78
+ getExtension,
79
+ getFA2Settings,
80
+ getFileIcon,
81
+ getFilename,
82
+ getFilesInDirectory,
83
+ getIconName,
84
+ getLayoutDuration,
85
+ getLuminance,
86
+ getMediumFileLabel,
87
+ getNodeCluster,
88
+ getNodeKey,
89
+ getNodeMass,
90
+ getNodeOpacityAtLevel,
91
+ getNodeSizeAtLevel,
92
+ getNodesInCluster,
93
+ getParentDirectories,
94
+ getPathDepth,
95
+ getPositionsAtDepth,
96
+ getRootScopes,
97
+ getScaledNodeSize,
98
+ getScopeByDirectoryId,
99
+ getScopeDepthColor,
100
+ getShortFileLabel,
101
+ getSigmaTheme,
102
+ getSymbolIcon,
103
+ getSymbolKindLabel,
104
+ getTextColorForBackground,
105
+ getTheme,
106
+ getThemeIds,
107
+ getVisibleEdges,
108
+ getVisibleNodes,
109
+ getVisibleScopes,
110
+ getZoomLevel,
111
+ getZoomLevelFromZoom,
112
+ graphToScreen,
113
+ hexToRgb,
114
+ hslToHex,
115
+ hslToRgb,
116
+ isCodeFile,
117
+ isPointInBoundingBox,
118
+ lerp,
119
+ lightTheme,
120
+ lighten,
121
+ matchesPattern,
122
+ mergeTheme,
123
+ mix,
124
+ nexusToGraphology,
125
+ nodeTypes,
126
+ normalizePath,
127
+ normalizePositions,
128
+ positionAllNodes,
129
+ positionNodesInScope,
130
+ registerTheme,
131
+ removeAllOverlaps,
132
+ removeOverlapsInScope,
133
+ rgbToHex,
134
+ rgbToHsl,
135
+ rgbaToCss,
136
+ saturate,
137
+ scaleSize,
138
+ screenToGraph,
139
+ shouldUseDarkText,
140
+ shrinkBoundingBox,
141
+ structureToGraphology,
142
+ truncateLabel,
143
+ uniformPadding,
144
+ unionBoundingBoxes,
145
+ useCurrentTheme,
146
+ useIsDarkTheme,
147
+ useLayout,
148
+ useOverlayPort,
149
+ useSigma,
150
+ useTheme,
151
+ withOpacity
152
+ } from "./chunk-HMMRZXQ4.mjs";
153
+
154
+ // src/analyzer/index.ts
155
+ import * as fs5 from "fs";
156
+ import * as path4 from "path";
157
+
158
+ // src/analyzer/scanner.ts
159
+ import * as fs from "fs";
160
+ import * as path from "path";
161
+ import ignore from "ignore";
162
+
163
+ // src/analyzer/grammars/extensions.ts
164
+ var EXTENSION_TO_LANGUAGE = {
165
+ // Go
166
+ ".go": "go",
167
+ // Rust
168
+ ".rs": "rust",
169
+ // Java
170
+ ".java": "java",
171
+ // Kotlin
172
+ ".kt": "kotlin",
173
+ ".kts": "kotlin",
174
+ // C
175
+ ".c": "c",
176
+ ".h": "c",
177
+ // C++
178
+ ".cpp": "cpp",
179
+ ".cc": "cpp",
180
+ ".cxx": "cpp",
181
+ ".c++": "cpp",
182
+ ".hpp": "cpp",
183
+ ".hh": "cpp",
184
+ ".hxx": "cpp",
185
+ ".h++": "cpp",
186
+ // C#
187
+ ".cs": "c_sharp",
188
+ // Ruby
189
+ ".rb": "ruby",
190
+ ".rake": "ruby",
191
+ ".gemspec": "ruby",
192
+ ".ru": "ruby",
193
+ // PHP
194
+ ".php": "php",
195
+ ".phtml": "php",
196
+ ".php3": "php",
197
+ ".php4": "php",
198
+ ".php5": "php",
199
+ ".phps": "php",
200
+ // Swift
201
+ ".swift": "swift",
202
+ // Scala
203
+ ".scala": "scala",
204
+ ".sc": "scala",
205
+ // Shell/Bash
206
+ ".sh": "bash",
207
+ ".bash": "bash",
208
+ ".zsh": "bash",
209
+ ".fish": "fish",
210
+ // Lua
211
+ ".lua": "lua",
212
+ // Perl
213
+ ".pl": "perl",
214
+ ".pm": "perl",
215
+ // R
216
+ ".r": "r",
217
+ ".R": "r",
218
+ // Haskell
219
+ ".hs": "haskell",
220
+ ".lhs": "haskell",
221
+ // Elixir
222
+ ".ex": "elixir",
223
+ ".exs": "elixir",
224
+ // Erlang
225
+ ".erl": "erlang",
226
+ ".hrl": "erlang",
227
+ // Clojure
228
+ ".clj": "clojure",
229
+ ".cljs": "clojure",
230
+ ".cljc": "clojure",
231
+ ".edn": "clojure",
232
+ // OCaml
233
+ ".ml": "ocaml",
234
+ ".mli": "ocaml",
235
+ // F#
236
+ ".fs": "fsharp",
237
+ ".fsi": "fsharp",
238
+ ".fsx": "fsharp",
239
+ // Julia
240
+ ".jl": "julia",
241
+ // Dart
242
+ ".dart": "dart",
243
+ // Objective-C
244
+ ".m": "objc",
245
+ ".mm": "objc",
246
+ // Groovy
247
+ ".groovy": "groovy",
248
+ ".gradle": "groovy",
249
+ // SQL
250
+ ".sql": "sql",
251
+ // GraphQL
252
+ ".graphql": "graphql",
253
+ ".gql": "graphql",
254
+ // HTML
255
+ ".html": "html",
256
+ ".htm": "html",
257
+ // CSS
258
+ ".css": "css",
259
+ // SCSS/Sass
260
+ ".scss": "scss",
261
+ ".sass": "scss",
262
+ // YAML
263
+ ".yaml": "yaml",
264
+ ".yml": "yaml",
265
+ // TOML
266
+ ".toml": "toml",
267
+ // JSON (tree-sitter-json)
268
+ ".json": "json",
269
+ // XML
270
+ ".xml": "xml",
271
+ ".xsd": "xml",
272
+ ".xsl": "xml",
273
+ ".xslt": "xml",
274
+ ".svg": "xml",
275
+ // Markdown
276
+ ".md": "markdown",
277
+ ".markdown": "markdown",
278
+ // LaTeX
279
+ ".tex": "latex",
280
+ ".latex": "latex",
281
+ // Dockerfile
282
+ dockerfile: "dockerfile",
283
+ // Makefile
284
+ makefile: "make",
285
+ ".mk": "make",
286
+ // CMake
287
+ ".cmake": "cmake",
288
+ // Terraform/HCL
289
+ ".tf": "hcl",
290
+ ".hcl": "hcl",
291
+ ".tfvars": "hcl",
292
+ // Zig
293
+ ".zig": "zig",
294
+ // Nim
295
+ ".nim": "nim",
296
+ // V
297
+ ".v": "v",
298
+ // Elm
299
+ ".elm": "elm",
300
+ // PureScript
301
+ ".purs": "purescript",
302
+ // ReasonML
303
+ ".re": "reason",
304
+ ".rei": "reason",
305
+ // Nix
306
+ ".nix": "nix",
307
+ // Solidity
308
+ ".sol": "solidity",
309
+ // GLSL (shaders)
310
+ ".glsl": "glsl",
311
+ ".vert": "glsl",
312
+ ".frag": "glsl",
313
+ // WGSL
314
+ ".wgsl": "wgsl",
315
+ // Protocol Buffers
316
+ ".proto": "proto",
317
+ // Thrift
318
+ ".thrift": "thrift"
319
+ };
320
+ var FILENAME_TO_LANGUAGE = {
321
+ dockerfile: "dockerfile",
322
+ makefile: "make",
323
+ gnumakefile: "make",
324
+ cmakelists: "cmake",
325
+ rakefile: "ruby",
326
+ gemfile: "ruby",
327
+ podfile: "ruby",
328
+ vagrantfile: "ruby",
329
+ brewfile: "ruby"
330
+ };
331
+ function getLanguageForFile(filePath) {
332
+ const basename2 = filePath.split(/[/\\]/).pop() || "";
333
+ const lowerBasename = basename2.toLowerCase();
334
+ const basenameNoExt = lowerBasename.replace(/\.[^.]+$/, "");
335
+ if (FILENAME_TO_LANGUAGE[basenameNoExt]) {
336
+ return FILENAME_TO_LANGUAGE[basenameNoExt];
337
+ }
338
+ const lastDot = basename2.lastIndexOf(".");
339
+ if (lastDot === -1) {
340
+ return FILENAME_TO_LANGUAGE[lowerBasename] || null;
341
+ }
342
+ const ext = basename2.slice(lastDot).toLowerCase();
343
+ return EXTENSION_TO_LANGUAGE[ext] || null;
344
+ }
345
+
346
+ // src/analyzer/languages/index.ts
347
+ var LanguageRegistry = class {
348
+ loaders = /* @__PURE__ */ new Map();
349
+ loaded = /* @__PURE__ */ new Map();
350
+ extensionMap = /* @__PURE__ */ new Map();
351
+ constructor() {
352
+ this.registerBuiltinLanguages();
353
+ }
354
+ /**
355
+ * Register built-in language loaders
356
+ */
357
+ registerBuiltinLanguages() {
358
+ this.registerLoader({
359
+ name: "typescript",
360
+ extensions: [".ts", ".tsx"],
361
+ load: async () => {
362
+ const lang = await import("tree-sitter-typescript");
363
+ return lang.default.typescript;
364
+ }
365
+ });
366
+ this.registerLoader({
367
+ name: "javascript",
368
+ extensions: [".js", ".jsx", ".mjs", ".cjs"],
369
+ load: async () => {
370
+ const lang = await import("tree-sitter-javascript");
371
+ return lang.default;
372
+ }
373
+ });
374
+ this.registerLoader({
375
+ name: "python",
376
+ extensions: [".py", ".pyi"],
377
+ load: async () => {
378
+ const lang = await import("tree-sitter-python");
379
+ return lang.default;
380
+ }
381
+ });
382
+ }
383
+ /**
384
+ * Register a language loader
385
+ */
386
+ registerLoader(loader) {
387
+ this.loaders.set(loader.name, loader);
388
+ for (const ext of loader.extensions) {
389
+ this.extensionMap.set(ext, loader.name);
390
+ }
391
+ }
392
+ /**
393
+ * Get language by name, loading if necessary
394
+ */
395
+ async getLanguage(name) {
396
+ if (this.loaded.has(name)) {
397
+ return this.loaded.get(name);
398
+ }
399
+ const loader = this.loaders.get(name);
400
+ if (!loader) {
401
+ return null;
402
+ }
403
+ const grammar = await loader.load();
404
+ const language = {
405
+ name: loader.name,
406
+ extensions: loader.extensions,
407
+ grammar
408
+ };
409
+ this.loaded.set(name, language);
410
+ return language;
411
+ }
412
+ /**
413
+ * Get language by file extension
414
+ */
415
+ async getLanguageForExtension(extension) {
416
+ const langName = this.extensionMap.get(extension);
417
+ if (!langName) {
418
+ return null;
419
+ }
420
+ return this.getLanguage(langName);
421
+ }
422
+ /**
423
+ * Get language for a file path
424
+ */
425
+ async getLanguageForFile(filePath) {
426
+ const ext = this.getExtension(filePath);
427
+ if (!ext) {
428
+ return null;
429
+ }
430
+ return this.getLanguageForExtension(ext);
431
+ }
432
+ /**
433
+ * Extract file extension from path
434
+ */
435
+ getExtension(filePath) {
436
+ const lastDot = filePath.lastIndexOf(".");
437
+ if (lastDot === -1 || lastDot === filePath.length - 1) {
438
+ return null;
439
+ }
440
+ return filePath.slice(lastDot);
441
+ }
442
+ /**
443
+ * Get list of supported languages
444
+ */
445
+ getSupportedLanguages() {
446
+ return Array.from(this.loaders.keys());
447
+ }
448
+ /**
449
+ * Get list of supported extensions
450
+ */
451
+ getSupportedExtensions() {
452
+ return Array.from(this.extensionMap.keys());
453
+ }
454
+ /**
455
+ * Check if a file extension is supported (native only)
456
+ */
457
+ isSupported(extension) {
458
+ return this.extensionMap.has(extension);
459
+ }
460
+ /**
461
+ * Check if a file extension is supported by WASM grammars
462
+ */
463
+ isWasmSupported(extension) {
464
+ return extension in EXTENSION_TO_LANGUAGE;
465
+ }
466
+ /**
467
+ * Check if a file is supported based on its path
468
+ * Considers both native tree-sitter and WASM-based grammars
469
+ */
470
+ isFileSupported(filePath) {
471
+ const ext = this.getExtension(filePath);
472
+ if (ext !== null && this.isSupported(ext)) {
473
+ return true;
474
+ }
475
+ const langName = getLanguageForFile(filePath);
476
+ return langName !== null;
477
+ }
478
+ };
479
+ var registry = null;
480
+ function getLanguageRegistry() {
481
+ if (!registry) {
482
+ registry = new LanguageRegistry();
483
+ }
484
+ return registry;
485
+ }
486
+ function resetLanguageRegistry() {
487
+ registry = null;
488
+ }
489
+
490
+ // src/analyzer/scanner.ts
491
+ var DEFAULT_CONFIG = {
492
+ include: [],
493
+ exclude: [],
494
+ respectGitignore: true,
495
+ maxDepth: Infinity,
496
+ maxFiles: 1e4
497
+ };
498
+ var MAX_FILE_SIZE = 1024 * 1024;
499
+ var DirectoryScanner = class {
500
+ config;
501
+ ignoreStack = [];
502
+ files = [];
503
+ directories = [];
504
+ skipped = [];
505
+ fileCount = 0;
506
+ constructor(config) {
507
+ this.config = {
508
+ ...DEFAULT_CONFIG,
509
+ ...config,
510
+ rootPath: path.resolve(config.rootPath)
511
+ };
512
+ }
513
+ /**
514
+ * Scan the directory tree
515
+ */
516
+ async scan() {
517
+ this.files = [];
518
+ this.directories = [];
519
+ this.skipped = [];
520
+ this.fileCount = 0;
521
+ this.ignoreStack = [];
522
+ if (!fs.existsSync(this.config.rootPath)) {
523
+ throw new Error(`Root path does not exist: ${this.config.rootPath}`);
524
+ }
525
+ const stats = fs.statSync(this.config.rootPath);
526
+ if (!stats.isDirectory()) {
527
+ throw new Error(`Root path is not a directory: ${this.config.rootPath}`);
528
+ }
529
+ await this.scanDirectory(this.config.rootPath, 0, null);
530
+ return {
531
+ directories: this.directories,
532
+ files: this.files,
533
+ skipped: this.skipped,
534
+ rootPath: this.config.rootPath
535
+ };
536
+ }
537
+ /**
538
+ * Recursively scan a directory
539
+ */
540
+ async scanDirectory(dirPath, depth, parentId) {
541
+ if (depth > this.config.maxDepth) {
542
+ return;
543
+ }
544
+ if (this.config.respectGitignore) {
545
+ await this.loadGitignore(dirPath);
546
+ }
547
+ const relativePath = path.relative(this.config.rootPath, dirPath);
548
+ const dirId = generateDirectoryId(relativePath || ".");
549
+ const dirName = path.basename(dirPath) || path.basename(this.config.rootPath);
550
+ const dirNode = {
551
+ id: dirId,
552
+ name: dirName,
553
+ path: relativePath || ".",
554
+ parentId,
555
+ children: [],
556
+ files: [],
557
+ metrics: {
558
+ fileCount: 0,
559
+ totalLoc: 0
560
+ },
561
+ depth
562
+ };
563
+ this.directories.push(dirNode);
564
+ let entries;
565
+ try {
566
+ entries = fs.readdirSync(dirPath, { withFileTypes: true });
567
+ } catch {
568
+ this.skipped.push({ path: relativePath, reason: "permission" });
569
+ return;
570
+ }
571
+ entries.sort((a, b) => a.name.localeCompare(b.name));
572
+ for (const entry of entries) {
573
+ if (this.fileCount >= this.config.maxFiles) {
574
+ this.skipped.push({
575
+ path: path.join(relativePath, entry.name),
576
+ reason: "max-files"
577
+ });
578
+ continue;
579
+ }
580
+ const entryPath = path.join(dirPath, entry.name);
581
+ const entryRelativePath = path.join(relativePath, entry.name);
582
+ if (this.isIgnored(entryRelativePath, entry.isDirectory())) {
583
+ this.skipped.push({ path: entryRelativePath, reason: "gitignore" });
584
+ continue;
585
+ }
586
+ if (this.isExcluded(entryRelativePath)) {
587
+ this.skipped.push({ path: entryRelativePath, reason: "excluded" });
588
+ continue;
589
+ }
590
+ if (entry.isDirectory()) {
591
+ if (this.shouldSkipDirectory(entry.name)) {
592
+ this.skipped.push({ path: entryRelativePath, reason: "excluded" });
593
+ continue;
594
+ }
595
+ dirNode.children.push(generateDirectoryId(entryRelativePath));
596
+ await this.scanDirectory(entryPath, depth + 1, dirId);
597
+ } else if (entry.isFile()) {
598
+ const fileNode = await this.processFile(entryPath, entryRelativePath, dirId);
599
+ if (fileNode) {
600
+ dirNode.files.push(fileNode.id);
601
+ }
602
+ }
603
+ }
604
+ if (this.config.respectGitignore) {
605
+ this.popGitignore(dirPath);
606
+ }
607
+ }
608
+ /**
609
+ * Process a single file
610
+ */
611
+ async processFile(filePath, relativePath, parentId) {
612
+ const registry2 = getLanguageRegistry();
613
+ if (!registry2.isFileSupported(filePath)) {
614
+ this.skipped.push({ path: relativePath, reason: "not-code" });
615
+ return null;
616
+ }
617
+ let stats;
618
+ try {
619
+ stats = fs.statSync(filePath);
620
+ } catch {
621
+ this.skipped.push({ path: relativePath, reason: "permission" });
622
+ return null;
623
+ }
624
+ if (stats.size > MAX_FILE_SIZE) {
625
+ this.skipped.push({ path: relativePath, reason: "too-large" });
626
+ return null;
627
+ }
628
+ const language = detectLanguage(filePath) || "unknown";
629
+ const extension = getExtension(filePath) || "";
630
+ const fileId = generateFileId(relativePath);
631
+ const fileNode = {
632
+ id: fileId,
633
+ name: path.basename(filePath),
634
+ path: relativePath,
635
+ extension,
636
+ directoryId: parentId,
637
+ language,
638
+ symbols: [],
639
+ // Populated during symbol extraction
640
+ metrics: {
641
+ loc: 0,
642
+ // Populated during symbol extraction
643
+ totalLines: 0,
644
+ exportCount: 0,
645
+ importCount: 0
646
+ }
647
+ };
648
+ this.files.push(fileNode);
649
+ this.fileCount++;
650
+ return fileNode;
651
+ }
652
+ /**
653
+ * Load .gitignore file from directory
654
+ */
655
+ async loadGitignore(dirPath) {
656
+ const gitignorePath = path.join(dirPath, ".gitignore");
657
+ if (fs.existsSync(gitignorePath)) {
658
+ try {
659
+ const content = fs.readFileSync(gitignorePath, "utf-8");
660
+ const ig = ignore().add(content);
661
+ this.ignoreStack.push({ path: dirPath, ig });
662
+ } catch {
663
+ }
664
+ }
665
+ }
666
+ /**
667
+ * Pop gitignore when leaving directory
668
+ */
669
+ popGitignore(dirPath) {
670
+ if (this.ignoreStack.length > 0 && this.ignoreStack[this.ignoreStack.length - 1].path === dirPath) {
671
+ this.ignoreStack.pop();
672
+ }
673
+ }
674
+ /**
675
+ * Check if a path is ignored by any gitignore in the stack
676
+ */
677
+ isIgnored(relativePath, isDirectory) {
678
+ for (const { ig, path: igPath } of this.ignoreStack) {
679
+ const relativeToGitignore = path.relative(
680
+ path.relative(this.config.rootPath, igPath),
681
+ relativePath
682
+ );
683
+ if (relativeToGitignore && !relativeToGitignore.startsWith("..")) {
684
+ if (ig.ignores(isDirectory ? relativeToGitignore + "/" : relativeToGitignore)) {
685
+ return true;
686
+ }
687
+ }
688
+ }
689
+ return false;
690
+ }
691
+ /**
692
+ * Check if path matches exclude patterns
693
+ */
694
+ isExcluded(relativePath) {
695
+ if (this.config.exclude.length === 0) {
696
+ return false;
697
+ }
698
+ for (const pattern of this.config.exclude) {
699
+ if (this.matchGlob(relativePath, pattern)) {
700
+ return true;
701
+ }
702
+ }
703
+ return false;
704
+ }
705
+ /**
706
+ * Simple glob matching
707
+ */
708
+ matchGlob(filePath, pattern) {
709
+ const regex = pattern.replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/{{GLOBSTAR}}/g, ".*").replace(/\?/g, ".");
710
+ return new RegExp(`^${regex}$`).test(filePath);
711
+ }
712
+ /**
713
+ * Check if directory should be skipped (common non-code directories)
714
+ */
715
+ shouldSkipDirectory(name) {
716
+ const skipDirs = [
717
+ "node_modules",
718
+ ".git",
719
+ ".svn",
720
+ ".hg",
721
+ "__pycache__",
722
+ ".pytest_cache",
723
+ ".mypy_cache",
724
+ "dist",
725
+ "build",
726
+ "out",
727
+ ".next",
728
+ ".nuxt",
729
+ "coverage",
730
+ ".nyc_output",
731
+ "vendor",
732
+ ".venv",
733
+ "venv",
734
+ "env",
735
+ ".env",
736
+ ".idea",
737
+ ".vscode"
738
+ ];
739
+ return skipDirs.includes(name);
740
+ }
741
+ };
742
+ async function scanDirectory(config) {
743
+ const scanner = new DirectoryScanner(config);
744
+ return scanner.scan();
745
+ }
746
+
747
+ // src/analyzer/symbols.ts
748
+ import * as fs3 from "fs";
749
+
750
+ // src/analyzer/parser.ts
751
+ import Parser2 from "tree-sitter";
752
+
753
+ // src/analyzer/grammars/index.ts
754
+ import { Parser, Language } from "web-tree-sitter";
755
+ import * as fs2 from "fs";
756
+ import * as path2 from "path";
757
+ var WasmGrammarLoader = class {
758
+ parser = null;
759
+ initialized = false;
760
+ initPromise = null;
761
+ grammars = /* @__PURE__ */ new Map();
762
+ grammarPaths = /* @__PURE__ */ new Map();
763
+ discoveredGrammars = /* @__PURE__ */ new Set();
764
+ /**
765
+ * Initialize the WASM parser (lazy, singleton)
766
+ */
767
+ async initialize() {
768
+ if (this.initialized) return;
769
+ if (this.initPromise) {
770
+ return this.initPromise;
771
+ }
772
+ this.initPromise = this.doInitialize();
773
+ return this.initPromise;
774
+ }
775
+ async doInitialize() {
776
+ try {
777
+ await Parser.init();
778
+ this.parser = new Parser();
779
+ this.initialized = true;
780
+ await this.discoverGrammars();
781
+ } catch (error) {
782
+ this.initialized = false;
783
+ this.initPromise = null;
784
+ throw new Error(
785
+ `Failed to initialize WASM parser: ${error instanceof Error ? error.message : String(error)}`
786
+ );
787
+ }
788
+ }
789
+ /**
790
+ * Discover available WASM grammars from known locations
791
+ */
792
+ async discoverGrammars() {
793
+ const searchPaths = this.getGrammarSearchPaths();
794
+ for (const searchPath of searchPaths) {
795
+ if (!fs2.existsSync(searchPath)) continue;
796
+ try {
797
+ const files = fs2.readdirSync(searchPath);
798
+ for (const file of files) {
799
+ if (file.endsWith(".wasm") && file.startsWith("tree-sitter-")) {
800
+ const langName = file.replace("tree-sitter-", "").replace(".wasm", "");
801
+ const fullPath = path2.join(searchPath, file);
802
+ this.grammarPaths.set(langName, fullPath);
803
+ this.discoveredGrammars.add(langName);
804
+ }
805
+ }
806
+ } catch {
807
+ }
808
+ }
809
+ }
810
+ /**
811
+ * Get paths to search for WASM grammars
812
+ */
813
+ getGrammarSearchPaths() {
814
+ const paths = [];
815
+ const bundledPath = path2.join(__dirname, "wasm");
816
+ paths.push(bundledPath);
817
+ const cwd = process.cwd();
818
+ const userPath = path2.join(cwd, ".codeviz", "grammars");
819
+ paths.push(userPath);
820
+ const nodeModulesPath = path2.join(cwd, "node_modules");
821
+ if (fs2.existsSync(nodeModulesPath)) {
822
+ const wasmsPkgPath = path2.join(nodeModulesPath, "tree-sitter-wasms", "out");
823
+ if (fs2.existsSync(wasmsPkgPath)) {
824
+ paths.push(wasmsPkgPath);
825
+ }
826
+ const potentialPackages = [
827
+ "tree-sitter-go",
828
+ "tree-sitter-rust",
829
+ "tree-sitter-java",
830
+ "tree-sitter-c",
831
+ "tree-sitter-ruby",
832
+ "tree-sitter-cpp",
833
+ "tree-sitter-c-sharp",
834
+ "tree-sitter-php",
835
+ "tree-sitter-swift",
836
+ "tree-sitter-kotlin"
837
+ ];
838
+ for (const pkg of potentialPackages) {
839
+ const pkgPath = path2.join(nodeModulesPath, pkg);
840
+ if (fs2.existsSync(pkgPath)) {
841
+ paths.push(pkgPath);
842
+ }
843
+ }
844
+ }
845
+ return paths;
846
+ }
847
+ /**
848
+ * Check if a grammar is available for a language
849
+ */
850
+ async hasGrammar(language) {
851
+ await this.initialize();
852
+ return this.grammarPaths.has(language) || this.grammars.has(language);
853
+ }
854
+ /**
855
+ * Load a grammar by language name
856
+ */
857
+ async loadGrammar(language) {
858
+ await this.initialize();
859
+ if (this.grammars.has(language)) {
860
+ return this.grammars.get(language);
861
+ }
862
+ const grammarPath = this.grammarPaths.get(language);
863
+ if (!grammarPath) {
864
+ return null;
865
+ }
866
+ try {
867
+ const grammar = await Language.load(grammarPath);
868
+ this.grammars.set(language, grammar);
869
+ return grammar;
870
+ } catch (error) {
871
+ console.warn(
872
+ `Failed to load grammar for ${language}: ${error instanceof Error ? error.message : String(error)}`
873
+ );
874
+ return null;
875
+ }
876
+ }
877
+ /**
878
+ * Get the parser instance (must be initialized first)
879
+ */
880
+ getParser() {
881
+ return this.parser;
882
+ }
883
+ /**
884
+ * Parse content with a specific language grammar
885
+ */
886
+ async parse(content, language) {
887
+ const grammar = await this.loadGrammar(language);
888
+ if (!grammar || !this.parser) {
889
+ return null;
890
+ }
891
+ this.parser.setLanguage(grammar);
892
+ return this.parser.parse(content);
893
+ }
894
+ /**
895
+ * List all available grammar languages
896
+ */
897
+ async listAvailableGrammars() {
898
+ await this.initialize();
899
+ return Array.from(this.discoveredGrammars);
900
+ }
901
+ /**
902
+ * Add a grammar path manually
903
+ */
904
+ addGrammarPath(language, wasmPath) {
905
+ if (fs2.existsSync(wasmPath)) {
906
+ this.grammarPaths.set(language, wasmPath);
907
+ this.discoveredGrammars.add(language);
908
+ }
909
+ }
910
+ /**
911
+ * Check if the loader is initialized
912
+ */
913
+ isInitialized() {
914
+ return this.initialized;
915
+ }
916
+ /**
917
+ * Reset the loader (useful for testing)
918
+ */
919
+ reset() {
920
+ this.parser = null;
921
+ this.initialized = false;
922
+ this.initPromise = null;
923
+ this.grammars.clear();
924
+ this.grammarPaths.clear();
925
+ this.discoveredGrammars.clear();
926
+ }
927
+ };
928
+ var loaderInstance = null;
929
+ function getWasmGrammarLoader() {
930
+ if (!loaderInstance) {
931
+ loaderInstance = new WasmGrammarLoader();
932
+ }
933
+ return loaderInstance;
934
+ }
935
+
936
+ // src/analyzer/parser.ts
937
+ var ParserManager = class {
938
+ parsers = /* @__PURE__ */ new Map();
939
+ treeCache = /* @__PURE__ */ new Map();
940
+ maxCacheSize;
941
+ constructor(maxCacheSize = 1e3) {
942
+ this.maxCacheSize = maxCacheSize;
943
+ }
944
+ /**
945
+ * Get or create a parser for the specified language
946
+ */
947
+ async getParser(language) {
948
+ const langName = language.name;
949
+ if (this.parsers.has(langName)) {
950
+ return this.parsers.get(langName);
951
+ }
952
+ const parser = new Parser2();
953
+ parser.setLanguage(language.grammar);
954
+ this.parsers.set(langName, parser);
955
+ return parser;
956
+ }
957
+ /**
958
+ * Parse source code and return the syntax tree
959
+ */
960
+ async parse(filePath, content, language) {
961
+ const cached = this.treeCache.get(filePath);
962
+ if (cached) {
963
+ return cached;
964
+ }
965
+ const parser = await this.getParser(language);
966
+ const startTime = performance.now();
967
+ const tree = parser.parse(content);
968
+ const parseTime = performance.now() - startTime;
969
+ const result = {
970
+ tree,
971
+ language: language.name,
972
+ filePath,
973
+ parseTime
974
+ };
975
+ this.addToCache(filePath, result);
976
+ return result;
977
+ }
978
+ /**
979
+ * Parse with error handling - returns result or error
980
+ */
981
+ async safeParse(filePath, content, language) {
982
+ try {
983
+ const result = await this.parse(filePath, content, language);
984
+ return { result };
985
+ } catch (err) {
986
+ const message = err instanceof Error ? err.message : String(err);
987
+ return {
988
+ error: {
989
+ message,
990
+ filePath,
991
+ language: language.name
992
+ }
993
+ };
994
+ }
995
+ }
996
+ /**
997
+ * Add parsed tree to cache with LRU eviction
998
+ */
999
+ addToCache(filePath, tree) {
1000
+ if (this.treeCache.size >= this.maxCacheSize) {
1001
+ const firstKey = this.treeCache.keys().next().value;
1002
+ if (firstKey) {
1003
+ this.treeCache.delete(firstKey);
1004
+ }
1005
+ }
1006
+ this.treeCache.set(filePath, tree);
1007
+ }
1008
+ /**
1009
+ * Clear the cache for a specific file (e.g., when file changes)
1010
+ */
1011
+ invalidate(filePath) {
1012
+ this.treeCache.delete(filePath);
1013
+ }
1014
+ /**
1015
+ * Clear all cached trees
1016
+ */
1017
+ clearCache() {
1018
+ this.treeCache.clear();
1019
+ }
1020
+ /**
1021
+ * Get cache statistics
1022
+ */
1023
+ getCacheStats() {
1024
+ return {
1025
+ size: this.treeCache.size,
1026
+ maxSize: this.maxCacheSize
1027
+ };
1028
+ }
1029
+ };
1030
+ var defaultParserManager = null;
1031
+ function getParserManager() {
1032
+ if (!defaultParserManager) {
1033
+ defaultParserManager = new ParserManager();
1034
+ }
1035
+ return defaultParserManager;
1036
+ }
1037
+ function resetParserManager() {
1038
+ if (defaultParserManager) {
1039
+ defaultParserManager.clearCache();
1040
+ defaultParserManager = null;
1041
+ }
1042
+ }
1043
+ var NATIVE_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "javascript", "python"]);
1044
+ var HybridParserManager = class {
1045
+ nativeManager;
1046
+ treeCache = /* @__PURE__ */ new Map();
1047
+ maxCacheSize;
1048
+ constructor(maxCacheSize = 1e3) {
1049
+ this.nativeManager = new ParserManager(maxCacheSize);
1050
+ this.maxCacheSize = maxCacheSize;
1051
+ }
1052
+ /**
1053
+ * Check if a language has a native parser
1054
+ */
1055
+ hasNativeParser(language) {
1056
+ return NATIVE_LANGUAGES.has(language);
1057
+ }
1058
+ /**
1059
+ * Check if a WASM grammar is available for a language
1060
+ */
1061
+ async hasWasmGrammar(language) {
1062
+ const wasmLoader = getWasmGrammarLoader();
1063
+ return wasmLoader.hasGrammar(language);
1064
+ }
1065
+ /**
1066
+ * Detect language from file path
1067
+ */
1068
+ detectLanguage(filePath) {
1069
+ return getLanguageForFile(filePath);
1070
+ }
1071
+ /**
1072
+ * Parse a file using the appropriate parser
1073
+ * Returns null if no parser is available for the language
1074
+ */
1075
+ async parse(filePath, content, nativeLanguage) {
1076
+ const cached = this.treeCache.get(filePath);
1077
+ if (cached) {
1078
+ return cached;
1079
+ }
1080
+ const langName = nativeLanguage?.name || this.detectLanguage(filePath);
1081
+ if (!langName) {
1082
+ return null;
1083
+ }
1084
+ if (nativeLanguage && this.hasNativeParser(langName)) {
1085
+ const parsed = await this.nativeManager.parse(filePath, content, nativeLanguage);
1086
+ const result = {
1087
+ tree: parsed.tree,
1088
+ parserType: "native",
1089
+ language: langName,
1090
+ filePath,
1091
+ parseTime: parsed.parseTime
1092
+ };
1093
+ this.addToCache(filePath, result);
1094
+ return result;
1095
+ }
1096
+ const wasmLoader = getWasmGrammarLoader();
1097
+ if (await wasmLoader.hasGrammar(langName)) {
1098
+ const startTime = performance.now();
1099
+ const tree = await wasmLoader.parse(content, langName);
1100
+ const parseTime = performance.now() - startTime;
1101
+ if (tree) {
1102
+ const result = {
1103
+ tree,
1104
+ parserType: "wasm",
1105
+ language: langName,
1106
+ filePath,
1107
+ parseTime
1108
+ };
1109
+ this.addToCache(filePath, result);
1110
+ return result;
1111
+ }
1112
+ }
1113
+ return null;
1114
+ }
1115
+ /**
1116
+ * Parse with error handling
1117
+ */
1118
+ async safeParse(filePath, content, nativeLanguage) {
1119
+ try {
1120
+ const result = await this.parse(filePath, content, nativeLanguage);
1121
+ if (!result) {
1122
+ const langName = nativeLanguage?.name || this.detectLanguage(filePath) || "unknown";
1123
+ return {
1124
+ error: {
1125
+ message: `No parser available for language: ${langName}`,
1126
+ filePath,
1127
+ language: langName
1128
+ }
1129
+ };
1130
+ }
1131
+ return { result };
1132
+ } catch (err) {
1133
+ const message = err instanceof Error ? err.message : String(err);
1134
+ return {
1135
+ error: {
1136
+ message,
1137
+ filePath,
1138
+ language: nativeLanguage?.name || this.detectLanguage(filePath) || void 0
1139
+ }
1140
+ };
1141
+ }
1142
+ }
1143
+ /**
1144
+ * Check if a file can be parsed (has available parser)
1145
+ */
1146
+ async canParse(filePath) {
1147
+ const langName = this.detectLanguage(filePath);
1148
+ if (!langName) {
1149
+ return false;
1150
+ }
1151
+ if (this.hasNativeParser(langName)) {
1152
+ return true;
1153
+ }
1154
+ return this.hasWasmGrammar(langName);
1155
+ }
1156
+ /**
1157
+ * Get the parser type that would be used for a file
1158
+ */
1159
+ async getParserType(filePath) {
1160
+ const langName = this.detectLanguage(filePath);
1161
+ if (!langName) {
1162
+ return null;
1163
+ }
1164
+ if (this.hasNativeParser(langName)) {
1165
+ return "native";
1166
+ }
1167
+ if (await this.hasWasmGrammar(langName)) {
1168
+ return "wasm";
1169
+ }
1170
+ return null;
1171
+ }
1172
+ /**
1173
+ * Add to cache with LRU eviction
1174
+ */
1175
+ addToCache(filePath, result) {
1176
+ if (this.treeCache.size >= this.maxCacheSize) {
1177
+ const firstKey = this.treeCache.keys().next().value;
1178
+ if (firstKey) {
1179
+ this.treeCache.delete(firstKey);
1180
+ }
1181
+ }
1182
+ this.treeCache.set(filePath, result);
1183
+ }
1184
+ /**
1185
+ * Clear cache for a specific file
1186
+ */
1187
+ invalidate(filePath) {
1188
+ this.treeCache.delete(filePath);
1189
+ this.nativeManager.invalidate(filePath);
1190
+ }
1191
+ /**
1192
+ * Clear all caches
1193
+ */
1194
+ clearCache() {
1195
+ this.treeCache.clear();
1196
+ this.nativeManager.clearCache();
1197
+ }
1198
+ /**
1199
+ * Get cache statistics
1200
+ */
1201
+ getCacheStats() {
1202
+ const nativeStats = this.nativeManager.getCacheStats();
1203
+ return {
1204
+ hybridSize: this.treeCache.size,
1205
+ nativeSize: nativeStats.size,
1206
+ maxSize: this.maxCacheSize
1207
+ };
1208
+ }
1209
+ };
1210
+ var defaultHybridManager = null;
1211
+ function getHybridParserManager() {
1212
+ if (!defaultHybridManager) {
1213
+ defaultHybridManager = new HybridParserManager();
1214
+ }
1215
+ return defaultHybridManager;
1216
+ }
1217
+
1218
+ // src/analyzer/languages/base.ts
1219
+ function nodeToLocation(node) {
1220
+ return {
1221
+ startLine: node.startPosition.row + 1,
1222
+ endLine: node.endPosition.row + 1,
1223
+ startColumn: node.startPosition.column,
1224
+ endColumn: node.endPosition.column
1225
+ };
1226
+ }
1227
+ function getNodeText(node) {
1228
+ return node.text;
1229
+ }
1230
+ function findChild(node, type) {
1231
+ for (const child of node.children) {
1232
+ if (child.type === type) {
1233
+ return child;
1234
+ }
1235
+ }
1236
+ return null;
1237
+ }
1238
+ function findChildren(node, type) {
1239
+ return node.children.filter((child) => child.type === type);
1240
+ }
1241
+ function findChildByTypes(node, types) {
1242
+ for (const child of node.children) {
1243
+ if (types.includes(child.type)) {
1244
+ return child;
1245
+ }
1246
+ }
1247
+ return null;
1248
+ }
1249
+ function collectNodes(root, types) {
1250
+ const result = [];
1251
+ function walk(node) {
1252
+ if (types.includes(node.type)) {
1253
+ result.push(node);
1254
+ }
1255
+ for (const child of node.children) {
1256
+ walk(child);
1257
+ }
1258
+ }
1259
+ walk(root);
1260
+ return result;
1261
+ }
1262
+ function calculateMetrics(content, commentPatterns) {
1263
+ const lines = content.split("\n");
1264
+ const totalLines = lines.length;
1265
+ let blankLines = 0;
1266
+ let commentLines = 0;
1267
+ let inBlockComment = false;
1268
+ for (const line of lines) {
1269
+ const trimmed = line.trim();
1270
+ if (trimmed === "") {
1271
+ blankLines++;
1272
+ continue;
1273
+ }
1274
+ if (commentPatterns.blockStart && commentPatterns.blockEnd) {
1275
+ if (inBlockComment) {
1276
+ commentLines++;
1277
+ if (commentPatterns.blockEnd.test(trimmed)) {
1278
+ inBlockComment = false;
1279
+ }
1280
+ continue;
1281
+ }
1282
+ if (commentPatterns.blockStart.test(trimmed)) {
1283
+ commentLines++;
1284
+ if (!commentPatterns.blockEnd.test(trimmed)) {
1285
+ inBlockComment = true;
1286
+ }
1287
+ continue;
1288
+ }
1289
+ }
1290
+ if (commentPatterns.line && commentPatterns.line.test(trimmed)) {
1291
+ commentLines++;
1292
+ continue;
1293
+ }
1294
+ }
1295
+ const loc = totalLines - blankLines - commentLines;
1296
+ return { loc, totalLines, blankLines, commentLines };
1297
+ }
1298
+ function rawSymbolsToNodes(rawSymbols, fileId, filePath) {
1299
+ return rawSymbols.map((raw) => {
1300
+ const id = generateSymbolId(filePath, raw.name, raw.location.startLine);
1301
+ return {
1302
+ id,
1303
+ name: raw.name,
1304
+ kind: raw.kind,
1305
+ fileId,
1306
+ location: raw.location,
1307
+ exported: raw.exported,
1308
+ metrics: {
1309
+ loc: raw.loc || raw.location.endLine - raw.location.startLine + 1
1310
+ }
1311
+ };
1312
+ });
1313
+ }
1314
+ function extractDocstring(node) {
1315
+ const parent = node.parent;
1316
+ if (!parent) return void 0;
1317
+ const nodeIndex = parent.children.indexOf(node);
1318
+ if (nodeIndex <= 0) return void 0;
1319
+ const prevNode = parent.children[nodeIndex - 1];
1320
+ if (prevNode.type === "comment" || prevNode.type === "block_comment") {
1321
+ return cleanDocstring(prevNode.text);
1322
+ }
1323
+ return void 0;
1324
+ }
1325
+ function cleanDocstring(text) {
1326
+ return text.replace(/^\/\*\*?/, "").replace(/\*\/$/, "").replace(/^\/\/\s?/, "").replace(/^\s*\*\s?/gm, "").replace(/^#\s?/, "").replace(/^"""/gm, "").replace(/"""$/gm, "").trim();
1327
+ }
1328
+ function isTopLevel(node) {
1329
+ let current = node.parent;
1330
+ while (current) {
1331
+ if (current.type === "function_body" || current.type === "block" || current.type === "class_body" || current.type === "statement_block") {
1332
+ return false;
1333
+ }
1334
+ if (current.type === "program" || current.type === "module") {
1335
+ return true;
1336
+ }
1337
+ current = current.parent;
1338
+ }
1339
+ return true;
1340
+ }
1341
+ function findContainingScope(node, scopeTypes) {
1342
+ let current = node.parent;
1343
+ while (current) {
1344
+ if (scopeTypes.includes(current.type)) {
1345
+ return current;
1346
+ }
1347
+ current = current.parent;
1348
+ }
1349
+ return null;
1350
+ }
1351
+
1352
+ // src/analyzer/languages/typescript.ts
1353
+ var TS_COMMENT_PATTERNS = {
1354
+ line: /^\/\//,
1355
+ blockStart: /^\/\*/,
1356
+ blockEnd: /\*\/$/
1357
+ };
1358
+ function extractTSSymbols(tree, content) {
1359
+ const symbols = [];
1360
+ const root = tree.rootNode;
1361
+ extractFunctions(root, symbols);
1362
+ extractClasses(root, symbols);
1363
+ extractInterfaces(root, symbols);
1364
+ extractTypes(root, symbols);
1365
+ extractEnums(root, symbols);
1366
+ extractVariables(root, symbols);
1367
+ const baseMetrics = calculateMetrics(content, TS_COMMENT_PATTERNS);
1368
+ const exportCount = symbols.filter((s) => s.exported).length;
1369
+ const importCount = extractTSImports(tree).length;
1370
+ return {
1371
+ symbols,
1372
+ metrics: {
1373
+ ...baseMetrics,
1374
+ exportCount,
1375
+ importCount
1376
+ }
1377
+ };
1378
+ }
1379
+ function extractFunctions(root, symbols) {
1380
+ const funcDecls = collectNodes(root, [
1381
+ "function_declaration",
1382
+ "function",
1383
+ "generator_function_declaration",
1384
+ "generator_function"
1385
+ ]);
1386
+ for (const node of funcDecls) {
1387
+ const nameNode = findChild(node, "identifier");
1388
+ if (!nameNode) continue;
1389
+ symbols.push({
1390
+ name: getNodeText(nameNode),
1391
+ kind: "function",
1392
+ location: nodeToLocation(node),
1393
+ exported: isExported(node),
1394
+ docstring: extractDocstring(node)
1395
+ });
1396
+ }
1397
+ const varDecls = collectNodes(root, ["lexical_declaration", "variable_declaration"]);
1398
+ for (const decl of varDecls) {
1399
+ const declarators = findChildren(decl, "variable_declarator");
1400
+ for (const declarator of declarators) {
1401
+ const nameNode = findChild(declarator, "identifier");
1402
+ const valueNode = findChildByTypes(declarator, ["arrow_function", "function"]);
1403
+ if (nameNode && valueNode) {
1404
+ symbols.push({
1405
+ name: getNodeText(nameNode),
1406
+ kind: "function",
1407
+ location: nodeToLocation(decl),
1408
+ exported: isExported(decl),
1409
+ docstring: extractDocstring(decl)
1410
+ });
1411
+ }
1412
+ }
1413
+ }
1414
+ }
1415
+ function extractClasses(root, symbols) {
1416
+ const classDecls = collectNodes(root, [
1417
+ "class_declaration",
1418
+ "class",
1419
+ "abstract_class_declaration"
1420
+ ]);
1421
+ for (const node of classDecls) {
1422
+ const nameNode = findChildByTypes(node, ["identifier", "type_identifier"]);
1423
+ if (!nameNode) continue;
1424
+ const className = getNodeText(nameNode);
1425
+ symbols.push({
1426
+ name: className,
1427
+ kind: "class",
1428
+ location: nodeToLocation(node),
1429
+ exported: isExported(node),
1430
+ docstring: extractDocstring(node)
1431
+ });
1432
+ const classBody = findChild(node, "class_body");
1433
+ if (classBody) {
1434
+ extractMethods(classBody, symbols, className);
1435
+ }
1436
+ }
1437
+ }
1438
+ function extractMethods(classBody, symbols, className) {
1439
+ const methodTypes = [
1440
+ "method_definition",
1441
+ "public_field_definition",
1442
+ "field_definition"
1443
+ ];
1444
+ for (const child of classBody.children) {
1445
+ if (!methodTypes.includes(child.type)) continue;
1446
+ if (child.type === "method_definition") {
1447
+ const nameNode = findChild(child, "property_identifier");
1448
+ if (nameNode) {
1449
+ symbols.push({
1450
+ name: getNodeText(nameNode),
1451
+ kind: "method",
1452
+ location: nodeToLocation(child),
1453
+ exported: true,
1454
+ // Methods are accessible through the class
1455
+ parentName: className,
1456
+ docstring: extractDocstring(child)
1457
+ });
1458
+ }
1459
+ }
1460
+ if (child.type === "public_field_definition" || child.type === "field_definition") {
1461
+ const nameNode = findChild(child, "property_identifier");
1462
+ const valueNode = findChild(child, "arrow_function");
1463
+ if (nameNode && valueNode) {
1464
+ symbols.push({
1465
+ name: getNodeText(nameNode),
1466
+ kind: "method",
1467
+ location: nodeToLocation(child),
1468
+ exported: true,
1469
+ parentName: className,
1470
+ docstring: extractDocstring(child)
1471
+ });
1472
+ }
1473
+ }
1474
+ }
1475
+ }
1476
+ function extractInterfaces(root, symbols) {
1477
+ const interfaces = collectNodes(root, ["interface_declaration"]);
1478
+ for (const node of interfaces) {
1479
+ const nameNode = findChild(node, "type_identifier");
1480
+ if (!nameNode) continue;
1481
+ symbols.push({
1482
+ name: getNodeText(nameNode),
1483
+ kind: "interface",
1484
+ location: nodeToLocation(node),
1485
+ exported: isExported(node),
1486
+ docstring: extractDocstring(node)
1487
+ });
1488
+ }
1489
+ }
1490
+ function extractTypes(root, symbols) {
1491
+ const typeAliases = collectNodes(root, ["type_alias_declaration"]);
1492
+ for (const node of typeAliases) {
1493
+ const nameNode = findChild(node, "type_identifier");
1494
+ if (!nameNode) continue;
1495
+ symbols.push({
1496
+ name: getNodeText(nameNode),
1497
+ kind: "type",
1498
+ location: nodeToLocation(node),
1499
+ exported: isExported(node),
1500
+ docstring: extractDocstring(node)
1501
+ });
1502
+ }
1503
+ }
1504
+ function extractEnums(root, symbols) {
1505
+ const enums = collectNodes(root, ["enum_declaration"]);
1506
+ for (const node of enums) {
1507
+ const nameNode = findChild(node, "identifier");
1508
+ if (!nameNode) continue;
1509
+ symbols.push({
1510
+ name: getNodeText(nameNode),
1511
+ kind: "enum",
1512
+ location: nodeToLocation(node),
1513
+ exported: isExported(node),
1514
+ docstring: extractDocstring(node)
1515
+ });
1516
+ }
1517
+ }
1518
+ function extractVariables(root, symbols) {
1519
+ const varDecls = collectNodes(root, ["lexical_declaration", "variable_declaration"]);
1520
+ for (const decl of varDecls) {
1521
+ if (!isTopLevel(decl)) continue;
1522
+ const declarators = findChildren(decl, "variable_declarator");
1523
+ for (const declarator of declarators) {
1524
+ const nameNode = findChild(declarator, "identifier");
1525
+ const valueNode = findChildByTypes(declarator, ["arrow_function", "function"]);
1526
+ if (valueNode) continue;
1527
+ if (nameNode) {
1528
+ symbols.push({
1529
+ name: getNodeText(nameNode),
1530
+ kind: "variable",
1531
+ location: nodeToLocation(decl),
1532
+ exported: isExported(decl),
1533
+ docstring: extractDocstring(decl)
1534
+ });
1535
+ }
1536
+ }
1537
+ }
1538
+ }
1539
+ function isExported(node) {
1540
+ const parent = node.parent;
1541
+ if (parent?.type === "export_statement") {
1542
+ return true;
1543
+ }
1544
+ for (const child of node.children) {
1545
+ if (child.type === "export") {
1546
+ return true;
1547
+ }
1548
+ }
1549
+ return false;
1550
+ }
1551
+ function extractTSImports(tree) {
1552
+ const imports = [];
1553
+ const root = tree.rootNode;
1554
+ const importStatements = collectNodes(root, ["import_statement"]);
1555
+ for (const node of importStatements) {
1556
+ const source = extractImportSource(node);
1557
+ if (!source) continue;
1558
+ const isTypeOnly = hasTypeOnlyModifier(node);
1559
+ const importedSymbols = extractImportedSymbols(node);
1560
+ imports.push({
1561
+ source,
1562
+ importType: isTypeOnly ? "type-only" : "static",
1563
+ importedSymbols: importedSymbols.names,
1564
+ isDefault: importedSymbols.hasDefault,
1565
+ isNamespace: importedSymbols.isNamespace,
1566
+ location: nodeToLocation(node)
1567
+ });
1568
+ }
1569
+ const callExpressions = collectNodes(root, ["call_expression"]);
1570
+ for (const call of callExpressions) {
1571
+ const fnNode = call.children[0];
1572
+ if (fnNode?.type === "import") {
1573
+ const args = findChild(call, "arguments");
1574
+ if (args) {
1575
+ const stringNode = findChildByTypes(args, ["string", "template_string"]);
1576
+ if (stringNode) {
1577
+ imports.push({
1578
+ source: extractStringContent(stringNode),
1579
+ importType: "dynamic",
1580
+ importedSymbols: [],
1581
+ isDefault: false,
1582
+ isNamespace: true,
1583
+ location: nodeToLocation(call)
1584
+ });
1585
+ }
1586
+ }
1587
+ }
1588
+ }
1589
+ for (const call of callExpressions) {
1590
+ const fnNode = call.children[0];
1591
+ if (fnNode?.type === "identifier" && getNodeText(fnNode) === "require") {
1592
+ const args = findChild(call, "arguments");
1593
+ if (args) {
1594
+ const stringNode = findChildByTypes(args, ["string", "template_string"]);
1595
+ if (stringNode) {
1596
+ imports.push({
1597
+ source: extractStringContent(stringNode),
1598
+ importType: "static",
1599
+ importedSymbols: [],
1600
+ isDefault: false,
1601
+ isNamespace: true,
1602
+ location: nodeToLocation(call)
1603
+ });
1604
+ }
1605
+ }
1606
+ }
1607
+ }
1608
+ return imports;
1609
+ }
1610
+ function extractImportSource(node) {
1611
+ const stringNode = findChildByTypes(node, ["string", "template_string"]);
1612
+ if (!stringNode) return null;
1613
+ return extractStringContent(stringNode);
1614
+ }
1615
+ function extractStringContent(node) {
1616
+ const text = getNodeText(node);
1617
+ return text.slice(1, -1);
1618
+ }
1619
+ function hasTypeOnlyModifier(node) {
1620
+ for (const child of node.children) {
1621
+ if (child.type === "type" && getNodeText(child) === "type") {
1622
+ return true;
1623
+ }
1624
+ }
1625
+ return false;
1626
+ }
1627
+ function extractImportedSymbols(node) {
1628
+ const names = [];
1629
+ let hasDefault = false;
1630
+ let isNamespace = false;
1631
+ const importClause = findChild(node, "import_clause");
1632
+ if (!importClause) {
1633
+ return { names, hasDefault, isNamespace };
1634
+ }
1635
+ for (const child of importClause.children) {
1636
+ if (child.type === "identifier") {
1637
+ hasDefault = true;
1638
+ names.push(getNodeText(child));
1639
+ }
1640
+ if (child.type === "namespace_import") {
1641
+ isNamespace = true;
1642
+ const asNode = findChild(child, "identifier");
1643
+ if (asNode) {
1644
+ names.push(getNodeText(asNode));
1645
+ }
1646
+ }
1647
+ if (child.type === "named_imports") {
1648
+ const specifiers = findChildren(child, "import_specifier");
1649
+ for (const spec of specifiers) {
1650
+ const nameNode = findChild(spec, "identifier");
1651
+ if (nameNode) {
1652
+ names.push(getNodeText(nameNode));
1653
+ }
1654
+ }
1655
+ }
1656
+ }
1657
+ return { names, hasDefault, isNamespace };
1658
+ }
1659
+ function extractTSCalls(tree, symbols) {
1660
+ const calls = [];
1661
+ const root = tree.rootNode;
1662
+ const symbolNames = new Set(symbols.map((s) => s.name));
1663
+ const callExpressions = collectNodes(root, ["call_expression", "new_expression"]);
1664
+ for (const call of callExpressions) {
1665
+ const callInfo = extractCallInfo(call, symbolNames);
1666
+ if (callInfo) {
1667
+ const scope = findContainingScope(call, [
1668
+ "function_declaration",
1669
+ "generator_function_declaration",
1670
+ "generator_function",
1671
+ "method_definition",
1672
+ "arrow_function",
1673
+ "function"
1674
+ ]);
1675
+ if (scope) {
1676
+ const callerName = getScopeName(scope);
1677
+ if (callerName) {
1678
+ calls.push({
1679
+ callerName,
1680
+ calleeName: callInfo.name,
1681
+ callType: callInfo.type,
1682
+ location: nodeToLocation(call)
1683
+ });
1684
+ }
1685
+ }
1686
+ }
1687
+ }
1688
+ return calls;
1689
+ }
1690
+ function extractCallInfo(node, symbolNames) {
1691
+ if (node.type === "new_expression") {
1692
+ const constructorNode = node.children[1];
1693
+ if (constructorNode?.type === "identifier") {
1694
+ const name = getNodeText(constructorNode);
1695
+ if (symbolNames.has(name)) {
1696
+ return { name, type: "constructor" };
1697
+ }
1698
+ }
1699
+ return null;
1700
+ }
1701
+ const fnNode = node.children[0];
1702
+ if (fnNode?.type === "identifier") {
1703
+ const name = getNodeText(fnNode);
1704
+ if (symbolNames.has(name)) {
1705
+ return { name, type: "direct" };
1706
+ }
1707
+ return null;
1708
+ }
1709
+ if (fnNode?.type === "member_expression") {
1710
+ const objectNode = findChild(fnNode, "this");
1711
+ const propertyNode = findChild(fnNode, "property_identifier");
1712
+ if (objectNode && propertyNode) {
1713
+ return {
1714
+ name: getNodeText(propertyNode),
1715
+ type: "method"
1716
+ };
1717
+ }
1718
+ }
1719
+ return null;
1720
+ }
1721
+ function getScopeName(scope) {
1722
+ if (scope.type === "function_declaration" || scope.type === "function" || scope.type === "generator_function_declaration" || scope.type === "generator_function") {
1723
+ const nameNode = findChild(scope, "identifier");
1724
+ return nameNode ? getNodeText(nameNode) : null;
1725
+ }
1726
+ if (scope.type === "method_definition") {
1727
+ const nameNode = findChild(scope, "property_identifier");
1728
+ return nameNode ? getNodeText(nameNode) : null;
1729
+ }
1730
+ if (scope.type === "arrow_function") {
1731
+ const parent = scope.parent;
1732
+ if (parent?.type === "variable_declarator") {
1733
+ const nameNode = findChild(parent, "identifier");
1734
+ return nameNode ? getNodeText(nameNode) : null;
1735
+ }
1736
+ }
1737
+ return null;
1738
+ }
1739
+
1740
+ // src/analyzer/languages/python.ts
1741
+ var PYTHON_COMMENT_PATTERNS = {
1742
+ line: /^#/,
1743
+ blockStart: /^"""/,
1744
+ blockEnd: /"""$/
1745
+ };
1746
+ function extractPythonSymbols(tree, content) {
1747
+ const symbols = [];
1748
+ const root = tree.rootNode;
1749
+ extractFunctions2(root, symbols);
1750
+ extractClasses2(root, symbols);
1751
+ extractVariables2(root, symbols);
1752
+ const baseMetrics = calculateMetrics(content, PYTHON_COMMENT_PATTERNS);
1753
+ const exportCount = symbols.filter((s) => s.exported).length;
1754
+ const importCount = extractPythonImports(tree).length;
1755
+ return {
1756
+ symbols,
1757
+ metrics: {
1758
+ ...baseMetrics,
1759
+ exportCount,
1760
+ importCount
1761
+ }
1762
+ };
1763
+ }
1764
+ function extractFunctions2(root, symbols) {
1765
+ const funcDefs = collectNodes(root, ["function_definition"]);
1766
+ for (const node of funcDefs) {
1767
+ if (isMethod(node)) continue;
1768
+ const nameNode = findChild(node, "identifier");
1769
+ if (!nameNode) continue;
1770
+ const name = getNodeText(nameNode);
1771
+ const isPrivate = name.startsWith("_") && !name.startsWith("__");
1772
+ symbols.push({
1773
+ name,
1774
+ kind: "function",
1775
+ location: nodeToLocation(node),
1776
+ exported: !isPrivate,
1777
+ // All top-level non-private symbols are exported
1778
+ docstring: extractPythonDocstring(node)
1779
+ });
1780
+ }
1781
+ }
1782
+ function extractClasses2(root, symbols) {
1783
+ const classDefs = collectNodes(root, ["class_definition"]);
1784
+ for (const node of classDefs) {
1785
+ const nameNode = findChild(node, "identifier");
1786
+ if (!nameNode) continue;
1787
+ const className = getNodeText(nameNode);
1788
+ const isPrivate = className.startsWith("_") && !className.startsWith("__");
1789
+ symbols.push({
1790
+ name: className,
1791
+ kind: "class",
1792
+ location: nodeToLocation(node),
1793
+ exported: !isPrivate,
1794
+ docstring: extractPythonDocstring(node)
1795
+ });
1796
+ const body = findChild(node, "block");
1797
+ if (body) {
1798
+ extractMethods2(body, symbols, className);
1799
+ }
1800
+ }
1801
+ }
1802
+ function extractMethods2(classBody, symbols, className) {
1803
+ const methods = collectNodes(classBody, ["function_definition"]);
1804
+ for (const method of methods) {
1805
+ const nameNode = findChild(method, "identifier");
1806
+ if (!nameNode) continue;
1807
+ const name = getNodeText(nameNode);
1808
+ const isDunder = name.startsWith("__") && name.endsWith("__");
1809
+ const isPrivate = name.startsWith("_") && !isDunder;
1810
+ symbols.push({
1811
+ name,
1812
+ kind: "method",
1813
+ location: nodeToLocation(method),
1814
+ exported: !isPrivate,
1815
+ // Methods are accessible through the class
1816
+ parentName: className,
1817
+ docstring: extractPythonDocstring(method)
1818
+ });
1819
+ }
1820
+ }
1821
+ function extractVariables2(root, symbols) {
1822
+ for (const child of root.children) {
1823
+ if (child.type === "expression_statement") {
1824
+ const assignment = findChild(child, "assignment");
1825
+ if (assignment) {
1826
+ const leftNode = assignment.children[0];
1827
+ if (leftNode?.type === "identifier") {
1828
+ const name = getNodeText(leftNode);
1829
+ const isPrivate = name.startsWith("_");
1830
+ symbols.push({
1831
+ name,
1832
+ kind: "variable",
1833
+ location: nodeToLocation(child),
1834
+ exported: !isPrivate,
1835
+ docstring: void 0
1836
+ });
1837
+ }
1838
+ }
1839
+ }
1840
+ }
1841
+ }
1842
+ function isMethod(node) {
1843
+ let current = node.parent;
1844
+ while (current) {
1845
+ if (current.type === "class_definition") {
1846
+ return true;
1847
+ }
1848
+ if (current.type === "function_definition") {
1849
+ return false;
1850
+ }
1851
+ current = current.parent;
1852
+ }
1853
+ return false;
1854
+ }
1855
+ function extractPythonDocstring(node) {
1856
+ const body = findChild(node, "block");
1857
+ if (!body) return void 0;
1858
+ for (const child of body.children) {
1859
+ if (child.type === "expression_statement") {
1860
+ const stringNode = findChild(child, "string");
1861
+ if (stringNode) {
1862
+ return cleanPythonDocstring(getNodeText(stringNode));
1863
+ }
1864
+ }
1865
+ if (child.type !== "comment") {
1866
+ break;
1867
+ }
1868
+ }
1869
+ return void 0;
1870
+ }
1871
+ function cleanPythonDocstring(text) {
1872
+ return text.replace(/^["']{3}/, "").replace(/["']{3}$/, "").trim();
1873
+ }
1874
+ function extractPythonImports(tree) {
1875
+ const imports = [];
1876
+ const root = tree.rootNode;
1877
+ const importStatements = collectNodes(root, ["import_statement"]);
1878
+ for (const node of importStatements) {
1879
+ const modules = extractImportedModules(node);
1880
+ for (const moduleName of modules) {
1881
+ imports.push({
1882
+ source: moduleName,
1883
+ importType: "static",
1884
+ importedSymbols: [],
1885
+ isDefault: false,
1886
+ isNamespace: true,
1887
+ location: nodeToLocation(node)
1888
+ });
1889
+ }
1890
+ }
1891
+ const fromStatements = collectNodes(root, ["import_from_statement"]);
1892
+ for (const node of fromStatements) {
1893
+ const moduleInfo = extractFromImport(node);
1894
+ if (moduleInfo) {
1895
+ imports.push({
1896
+ source: moduleInfo.module,
1897
+ importType: "static",
1898
+ importedSymbols: moduleInfo.symbols,
1899
+ isDefault: false,
1900
+ isNamespace: moduleInfo.isWildcard,
1901
+ location: nodeToLocation(node)
1902
+ });
1903
+ }
1904
+ }
1905
+ return imports;
1906
+ }
1907
+ function extractImportedModules(node) {
1908
+ const modules = [];
1909
+ for (const child of node.children) {
1910
+ if (child.type === "dotted_name") {
1911
+ modules.push(getNodeText(child));
1912
+ }
1913
+ if (child.type === "aliased_import") {
1914
+ const dottedName = findChild(child, "dotted_name");
1915
+ if (dottedName) {
1916
+ modules.push(getNodeText(dottedName));
1917
+ }
1918
+ }
1919
+ }
1920
+ return modules;
1921
+ }
1922
+ function extractFromImport(node) {
1923
+ let module = "";
1924
+ const symbols = [];
1925
+ let isWildcard = false;
1926
+ let foundModule = false;
1927
+ for (const child of node.children) {
1928
+ if (child.type === "import") {
1929
+ foundModule = true;
1930
+ }
1931
+ if (!foundModule && child.type === "dotted_name") {
1932
+ module = getNodeText(child);
1933
+ }
1934
+ if (child.type === "relative_import") {
1935
+ const dots = collectNodes(child, ["import_prefix"]);
1936
+ const dottedName = findChild(child, "dotted_name");
1937
+ const prefix = dots.length > 0 ? getNodeText(dots[0]) : "";
1938
+ const modulePart = dottedName ? getNodeText(dottedName) : "";
1939
+ module = prefix + modulePart;
1940
+ foundModule = true;
1941
+ }
1942
+ }
1943
+ let afterImport = false;
1944
+ for (const child of node.children) {
1945
+ if (child.type === "import") {
1946
+ afterImport = true;
1947
+ continue;
1948
+ }
1949
+ if (!afterImport) continue;
1950
+ if (child.type === "wildcard_import") {
1951
+ isWildcard = true;
1952
+ }
1953
+ if (child.type === "dotted_name") {
1954
+ symbols.push(getNodeText(child));
1955
+ }
1956
+ if (child.type === "aliased_import") {
1957
+ const name = findChild(child, "dotted_name");
1958
+ if (name) {
1959
+ symbols.push(getNodeText(name));
1960
+ }
1961
+ }
1962
+ if (child.type === "identifier") {
1963
+ symbols.push(getNodeText(child));
1964
+ }
1965
+ }
1966
+ if (!module && !isWildcard) {
1967
+ return null;
1968
+ }
1969
+ return { module, symbols, isWildcard };
1970
+ }
1971
+ function extractPythonCalls(tree, symbols) {
1972
+ const calls = [];
1973
+ const root = tree.rootNode;
1974
+ const symbolNames = new Set(symbols.map((s) => s.name));
1975
+ const callNodes = collectNodes(root, ["call"]);
1976
+ for (const call of callNodes) {
1977
+ const callInfo = extractCallInfo2(call, symbolNames);
1978
+ if (callInfo) {
1979
+ const scope = findContainingScope(call, ["function_definition"]);
1980
+ if (scope) {
1981
+ const callerName = getScopeName2(scope);
1982
+ if (callerName) {
1983
+ calls.push({
1984
+ callerName,
1985
+ calleeName: callInfo.name,
1986
+ callType: callInfo.type,
1987
+ location: nodeToLocation(call)
1988
+ });
1989
+ }
1990
+ }
1991
+ }
1992
+ }
1993
+ return calls;
1994
+ }
1995
+ function extractCallInfo2(node, symbolNames) {
1996
+ const fnNode = node.children[0];
1997
+ if (fnNode?.type === "identifier") {
1998
+ const name = getNodeText(fnNode);
1999
+ if (symbolNames.has(name)) {
2000
+ const isClass = /^[A-Z]/.test(name);
2001
+ return {
2002
+ name,
2003
+ type: isClass ? "constructor" : "direct"
2004
+ };
2005
+ }
2006
+ return null;
2007
+ }
2008
+ if (fnNode?.type === "attribute") {
2009
+ const objectNode = fnNode.children[0];
2010
+ const identifiers = fnNode.children.filter((c) => c.type === "identifier");
2011
+ const attrNode = identifiers.length > 1 ? identifiers[identifiers.length - 1] : null;
2012
+ if (objectNode?.type === "identifier" && getNodeText(objectNode) === "self") {
2013
+ if (attrNode) {
2014
+ return {
2015
+ name: getNodeText(attrNode),
2016
+ type: "method"
2017
+ };
2018
+ }
2019
+ }
2020
+ }
2021
+ return null;
2022
+ }
2023
+ function getScopeName2(scope) {
2024
+ const nameNode = findChild(scope, "identifier");
2025
+ return nameNode ? getNodeText(nameNode) : null;
2026
+ }
2027
+
2028
+ // src/analyzer/languages/generic.ts
2029
+ var SYMBOL_PATTERNS = {
2030
+ function: [
2031
+ /^function_declaration$/,
2032
+ /^function_definition$/,
2033
+ /^function_item$/,
2034
+ // Rust
2035
+ /^func_literal$/,
2036
+ // Go
2037
+ /^method_declaration$/,
2038
+ /^function_expression$/,
2039
+ /^arrow_function$/,
2040
+ /^lambda_expression$/,
2041
+ /^anonymous_function$/
2042
+ ],
2043
+ method: [
2044
+ /^method_definition$/,
2045
+ /^method_declaration$/,
2046
+ /^instance_method$/,
2047
+ /^class_method$/,
2048
+ /^static_method$/
2049
+ ],
2050
+ class: [
2051
+ /^class_declaration$/,
2052
+ /^class_definition$/,
2053
+ /^class$/,
2054
+ // Ruby
2055
+ /^class_specifier$/,
2056
+ // C++
2057
+ /^struct_item$/,
2058
+ // Rust
2059
+ /^struct_declaration$/,
2060
+ // C
2061
+ /^struct_specifier$/,
2062
+ // C
2063
+ /^impl_item$/
2064
+ // Rust
2065
+ ],
2066
+ interface: [
2067
+ /^interface_declaration$/,
2068
+ /^interface_definition$/,
2069
+ /^trait_item$/,
2070
+ // Rust
2071
+ /^protocol_declaration$/
2072
+ // Swift
2073
+ ],
2074
+ type: [
2075
+ /^type_alias_declaration$/,
2076
+ /^type_declaration$/,
2077
+ /^type_definition$/,
2078
+ /^type_spec$/,
2079
+ // Go
2080
+ /^typedef_declaration$/
2081
+ // C
2082
+ ],
2083
+ enum: [
2084
+ /^enum_declaration$/,
2085
+ /^enum_definition$/,
2086
+ /^enum_item$/,
2087
+ // Rust
2088
+ /^enum_specifier$/
2089
+ // C
2090
+ ],
2091
+ variable: [
2092
+ /^variable_declaration$/,
2093
+ /^const_declaration$/,
2094
+ /^let_declaration$/,
2095
+ /^const_item$/,
2096
+ // Rust
2097
+ /^static_item$/,
2098
+ // Rust
2099
+ /^var_declaration$/,
2100
+ // Go
2101
+ /^short_var_declaration$/
2102
+ // Go
2103
+ ],
2104
+ constant: [
2105
+ /^const_declaration$/,
2106
+ /^const_item$/,
2107
+ // Rust
2108
+ /^constant_declaration$/
2109
+ ],
2110
+ property: [
2111
+ /^field_declaration$/,
2112
+ /^property_declaration$/,
2113
+ /^field_definition$/
2114
+ ],
2115
+ namespace: [
2116
+ /^module_declaration$/,
2117
+ /^module$/,
2118
+ // Ruby
2119
+ /^mod_item$/,
2120
+ // Rust
2121
+ /^package_declaration$/,
2122
+ // Java
2123
+ /^namespace_declaration$/
2124
+ ]
2125
+ };
2126
+ var IMPORT_PATTERNS = [
2127
+ /^import_declaration$/,
2128
+ /^import_statement$/,
2129
+ /^import_from_statement$/,
2130
+ /^use_declaration$/,
2131
+ // Rust
2132
+ /^use_statement$/,
2133
+ /^include_directive$/,
2134
+ // C/C++
2135
+ /^preproc_include$/,
2136
+ // C/C++
2137
+ /^require_expression$/,
2138
+ /^require_statement$/,
2139
+ /^import_spec$/
2140
+ // Go
2141
+ ];
2142
+ var NAME_NODE_TYPES = [
2143
+ "identifier",
2144
+ "name",
2145
+ "type_identifier",
2146
+ "property_identifier",
2147
+ "field_identifier",
2148
+ "constant_identifier",
2149
+ "simple_identifier",
2150
+ "constant"
2151
+ // Ruby class/module names
2152
+ ];
2153
+ var COMMENT_NODE_TYPES = [
2154
+ "comment",
2155
+ "line_comment",
2156
+ "block_comment",
2157
+ "doc_comment",
2158
+ "multiline_comment"
2159
+ ];
2160
+ function nodeToLocation2(node) {
2161
+ return {
2162
+ startLine: node.startPosition.row + 1,
2163
+ endLine: node.endPosition.row + 1,
2164
+ startColumn: node.startPosition.column,
2165
+ endColumn: node.endPosition.column
2166
+ };
2167
+ }
2168
+ function findName(node) {
2169
+ for (let i = 0; i < node.childCount; i++) {
2170
+ const child = node.child(i);
2171
+ if (child && NAME_NODE_TYPES.includes(child.type)) {
2172
+ return child.text;
2173
+ }
2174
+ }
2175
+ for (let i = 0; i < node.namedChildCount; i++) {
2176
+ const child = node.namedChild(i);
2177
+ if (child && NAME_NODE_TYPES.includes(child.type)) {
2178
+ return child.text;
2179
+ }
2180
+ }
2181
+ for (let i = 0; i < node.childCount; i++) {
2182
+ const child = node.child(i);
2183
+ if (child) {
2184
+ for (let j = 0; j < child.childCount; j++) {
2185
+ const grandchild = child.child(j);
2186
+ if (grandchild && NAME_NODE_TYPES.includes(grandchild.type)) {
2187
+ return grandchild.text;
2188
+ }
2189
+ }
2190
+ }
2191
+ }
2192
+ return null;
2193
+ }
2194
+ function getSymbolKind(nodeType) {
2195
+ for (const [kind, patterns] of Object.entries(SYMBOL_PATTERNS)) {
2196
+ for (const pattern of patterns) {
2197
+ if (pattern.test(nodeType)) {
2198
+ return kind;
2199
+ }
2200
+ }
2201
+ }
2202
+ return null;
2203
+ }
2204
+ function isImportNode(nodeType) {
2205
+ return IMPORT_PATTERNS.some((pattern) => pattern.test(nodeType));
2206
+ }
2207
+ function extractImportSource2(node) {
2208
+ const systemLib = node.descendantsOfType("system_lib_string")[0];
2209
+ if (systemLib) {
2210
+ const text = systemLib.text;
2211
+ return text.replace(/^<|>$/g, "");
2212
+ }
2213
+ const stringLiteral = node.descendantsOfType("string_literal")[0];
2214
+ if (stringLiteral) {
2215
+ const text = stringLiteral.text;
2216
+ return text.replace(/^["']|["']$/g, "");
2217
+ }
2218
+ const stringNode = node.descendantsOfType("string")[0];
2219
+ if (stringNode) {
2220
+ const text = stringNode.text;
2221
+ return text.replace(/^["']|["']$/g, "");
2222
+ }
2223
+ const stringContent = node.descendantsOfType("string_content")[0];
2224
+ if (stringContent) {
2225
+ return stringContent.text;
2226
+ }
2227
+ const interpretedString = node.descendantsOfType("interpreted_string_literal")[0];
2228
+ if (interpretedString) {
2229
+ const text = interpretedString.text;
2230
+ return text.replace(/^["']|["']$/g, "");
2231
+ }
2232
+ const dottedName = node.descendantsOfType("dotted_name")[0];
2233
+ if (dottedName) {
2234
+ return dottedName.text;
2235
+ }
2236
+ const scopedId = node.descendantsOfType("scoped_identifier")[0];
2237
+ if (scopedId) {
2238
+ return scopedId.text;
2239
+ }
2240
+ const identifier = node.descendantsOfType("identifier")[0];
2241
+ if (identifier) {
2242
+ return identifier.text;
2243
+ }
2244
+ return null;
2245
+ }
2246
+ function isTopLevel2(node) {
2247
+ let current = node.parent;
2248
+ while (current) {
2249
+ const type = current.type;
2250
+ if (type.includes("body") || type.includes("block") || type === "compound_statement") {
2251
+ if (!current.parent || current.parent.type === "source_file") {
2252
+ return true;
2253
+ }
2254
+ return false;
2255
+ }
2256
+ if (type === "source_file" || type === "program" || type === "module" || type === "translation_unit") {
2257
+ return true;
2258
+ }
2259
+ current = current.parent;
2260
+ }
2261
+ return true;
2262
+ }
2263
+ function walkTree(node, callback) {
2264
+ callback(node);
2265
+ for (let i = 0; i < node.childCount; i++) {
2266
+ const child = node.child(i);
2267
+ if (child) {
2268
+ walkTree(child, callback);
2269
+ }
2270
+ }
2271
+ }
2272
+ function calculateMetrics2(content, tree) {
2273
+ const lines = content.split("\n");
2274
+ const totalLines = lines.length;
2275
+ let blankLines = 0;
2276
+ let commentLines = 0;
2277
+ for (const line of lines) {
2278
+ if (line.trim() === "") {
2279
+ blankLines++;
2280
+ }
2281
+ }
2282
+ walkTree(tree.rootNode, (node) => {
2283
+ if (COMMENT_NODE_TYPES.includes(node.type)) {
2284
+ const startLine = node.startPosition.row;
2285
+ const endLine = node.endPosition.row;
2286
+ commentLines += endLine - startLine + 1;
2287
+ }
2288
+ });
2289
+ const loc = Math.max(0, totalLines - blankLines - commentLines);
2290
+ return {
2291
+ loc,
2292
+ totalLines,
2293
+ blankLines,
2294
+ commentLines
2295
+ };
2296
+ }
2297
+ function extractGeneric(tree, content) {
2298
+ const symbols = [];
2299
+ const imports = [];
2300
+ walkTree(tree.rootNode, (node) => {
2301
+ const nodeType = node.type;
2302
+ const kind = getSymbolKind(nodeType);
2303
+ if (kind) {
2304
+ if (kind !== "method" && !isTopLevel2(node)) {
2305
+ return;
2306
+ }
2307
+ const name = findName(node);
2308
+ if (name) {
2309
+ symbols.push({
2310
+ name,
2311
+ kind,
2312
+ location: nodeToLocation2(node),
2313
+ exported: true
2314
+ // Can't reliably detect exports in generic mode
2315
+ });
2316
+ }
2317
+ }
2318
+ if (isImportNode(nodeType)) {
2319
+ const source = extractImportSource2(node);
2320
+ if (source) {
2321
+ imports.push({
2322
+ source,
2323
+ location: nodeToLocation2(node)
2324
+ });
2325
+ }
2326
+ }
2327
+ });
2328
+ const metrics = calculateMetrics2(content, tree);
2329
+ return {
2330
+ symbols,
2331
+ imports,
2332
+ metrics
2333
+ };
2334
+ }
2335
+
2336
+ // src/analyzer/symbols.ts
2337
+ async function extractSymbols(filePath, content, fileId) {
2338
+ const registry2 = getLanguageRegistry();
2339
+ const language = await registry2.getLanguageForFile(filePath);
2340
+ if (language) {
2341
+ const parserManager = getParserManager();
2342
+ const parseResult = await parserManager.safeParse(filePath, content, language);
2343
+ if ("error" in parseResult) {
2344
+ return {
2345
+ symbols: [],
2346
+ metrics: { loc: 0, totalLines: 0, exportCount: 0, importCount: 0 },
2347
+ parseErrors: [parseResult.error.message]
2348
+ };
2349
+ }
2350
+ const tree = parseResult.result.tree;
2351
+ let extractionResult;
2352
+ switch (language.name) {
2353
+ case "typescript":
2354
+ case "javascript":
2355
+ extractionResult = extractTSSymbols(tree, content);
2356
+ break;
2357
+ case "python":
2358
+ extractionResult = extractPythonSymbols(tree, content);
2359
+ break;
2360
+ default:
2361
+ break;
2362
+ }
2363
+ if (extractionResult) {
2364
+ const symbols2 = rawSymbolsToNodes(extractionResult.symbols, fileId, filePath);
2365
+ return {
2366
+ symbols: symbols2,
2367
+ metrics: {
2368
+ loc: extractionResult.metrics.loc,
2369
+ totalLines: extractionResult.metrics.totalLines,
2370
+ exportCount: extractionResult.metrics.exportCount,
2371
+ importCount: extractionResult.metrics.importCount
2372
+ }
2373
+ };
2374
+ }
2375
+ }
2376
+ const hybridManager = getHybridParserManager();
2377
+ const hybridResult = await hybridManager.safeParse(filePath, content);
2378
+ if ("error" in hybridResult) {
2379
+ return {
2380
+ symbols: [],
2381
+ metrics: { loc: 0, totalLines: 0, exportCount: 0, importCount: 0 },
2382
+ parseErrors: [hybridResult.error.message]
2383
+ };
2384
+ }
2385
+ if (hybridResult.result.parserType !== "wasm") {
2386
+ return {
2387
+ symbols: [],
2388
+ metrics: { loc: 0, totalLines: 0, exportCount: 0, importCount: 0 },
2389
+ parseErrors: [`No extractor available for: ${hybridResult.result.language}`]
2390
+ };
2391
+ }
2392
+ const genericResult = extractGeneric(
2393
+ hybridResult.result.tree,
2394
+ content
2395
+ );
2396
+ const rawSymbols = genericResult.symbols.map((sym) => ({
2397
+ name: sym.name,
2398
+ kind: sym.kind,
2399
+ location: sym.location,
2400
+ exported: sym.exported
2401
+ }));
2402
+ const symbols = rawSymbolsToNodes(rawSymbols, fileId, filePath);
2403
+ return {
2404
+ symbols,
2405
+ metrics: {
2406
+ loc: genericResult.metrics.loc,
2407
+ totalLines: genericResult.metrics.totalLines,
2408
+ exportCount: symbols.filter((s) => s.exported).length,
2409
+ importCount: genericResult.imports.length
2410
+ }
2411
+ };
2412
+ }
2413
+ async function extractSymbolsFromFile(filePath, fileId) {
2414
+ try {
2415
+ const content = fs3.readFileSync(filePath, "utf-8");
2416
+ return extractSymbols(filePath, content, fileId);
2417
+ } catch (err) {
2418
+ const message = err instanceof Error ? err.message : String(err);
2419
+ return {
2420
+ symbols: [],
2421
+ metrics: { loc: 0, totalLines: 0, exportCount: 0, importCount: 0 },
2422
+ parseErrors: [`Failed to read file: ${message}`]
2423
+ };
2424
+ }
2425
+ }
2426
+
2427
+ // src/analyzer/imports.ts
2428
+ import * as fs4 from "fs";
2429
+ import * as path3 from "path";
2430
+ async function extractImports(filePath, content) {
2431
+ const registry2 = getLanguageRegistry();
2432
+ const language = await registry2.getLanguageForFile(filePath);
2433
+ if (!language) {
2434
+ return [];
2435
+ }
2436
+ const parserManager = getParserManager();
2437
+ const parseResult = await parserManager.safeParse(filePath, content, language);
2438
+ if ("error" in parseResult) {
2439
+ return [];
2440
+ }
2441
+ const tree = parseResult.result.tree;
2442
+ let rawImports;
2443
+ switch (language.name) {
2444
+ case "typescript":
2445
+ case "javascript":
2446
+ rawImports = extractTSImports(tree);
2447
+ break;
2448
+ case "python":
2449
+ rawImports = extractPythonImports(tree);
2450
+ break;
2451
+ default:
2452
+ return [];
2453
+ }
2454
+ return rawImports.map((raw) => ({
2455
+ source: raw.source,
2456
+ importType: raw.importType,
2457
+ importedSymbols: raw.importedSymbols,
2458
+ location: raw.location
2459
+ }));
2460
+ }
2461
+ function resolveImport(importInfo, fromFileId, fromFilePath, rootPath, fileIndex) {
2462
+ const { source, importType, importedSymbols, location } = importInfo;
2463
+ const isRelative = source.startsWith("./") || source.startsWith("../");
2464
+ const isBareSpecifier = !isRelative && !source.startsWith("/");
2465
+ if (isBareSpecifier) {
2466
+ return createUnresolvedImport(fromFileId, source, importType, importedSymbols, location);
2467
+ }
2468
+ const resolvedPath = resolveRelativeImport(fromFilePath, source, rootPath);
2469
+ if (resolvedPath) {
2470
+ const normalizedPath = normalizePathForIndex(resolvedPath, rootPath);
2471
+ const toFileId = fileIndex.get(normalizedPath);
2472
+ if (toFileId) {
2473
+ return {
2474
+ edge: {
2475
+ id: `import-${fromFileId}-${toFileId}-${location.startLine}`,
2476
+ sourceId: fromFileId,
2477
+ targetId: toFileId,
2478
+ importType,
2479
+ importedSymbols,
2480
+ resolved: true
2481
+ },
2482
+ resolved: true,
2483
+ resolvedPath
2484
+ };
2485
+ }
2486
+ }
2487
+ return createUnresolvedImport(fromFileId, source, importType, importedSymbols, location);
2488
+ }
2489
+ function createUnresolvedImport(fromFileId, source, importType, importedSymbols, location) {
2490
+ const externalId = `external:${source}`;
2491
+ return {
2492
+ edge: {
2493
+ id: `import-${fromFileId}-${externalId}-${location.startLine}`,
2494
+ sourceId: fromFileId,
2495
+ targetId: externalId,
2496
+ importType,
2497
+ importedSymbols,
2498
+ resolved: false
2499
+ },
2500
+ resolved: false
2501
+ };
2502
+ }
2503
+ function resolveRelativeImport(fromFilePath, importSource, _rootPath) {
2504
+ const fromDir = path3.dirname(fromFilePath);
2505
+ const baseResolved = path3.resolve(fromDir, importSource);
2506
+ const candidates = generateResolutionCandidates(baseResolved);
2507
+ for (const candidate of candidates) {
2508
+ if (fs4.existsSync(candidate) && fs4.statSync(candidate).isFile()) {
2509
+ return candidate;
2510
+ }
2511
+ }
2512
+ return null;
2513
+ }
2514
+ function generateResolutionCandidates(basePath) {
2515
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
2516
+ const indexFiles = extensions.map((ext) => `index${ext}`);
2517
+ const candidates = [];
2518
+ candidates.push(basePath);
2519
+ for (const ext of extensions) {
2520
+ candidates.push(basePath + ext);
2521
+ }
2522
+ for (const indexFile of indexFiles) {
2523
+ candidates.push(path3.join(basePath, indexFile));
2524
+ }
2525
+ return candidates;
2526
+ }
2527
+ function normalizePathForIndex(filePath, rootPath) {
2528
+ const relativePath = path3.relative(rootPath, filePath);
2529
+ return relativePath.replace(/\\/g, "/");
2530
+ }
2531
+ function buildFileIndex(files) {
2532
+ const index = /* @__PURE__ */ new Map();
2533
+ for (const file of files) {
2534
+ const normalized = file.path.replace(/\\/g, "/");
2535
+ index.set(normalized, file.id);
2536
+ const withoutExt = removeExtension(normalized);
2537
+ if (withoutExt !== normalized && !index.has(withoutExt)) {
2538
+ index.set(withoutExt, file.id);
2539
+ }
2540
+ }
2541
+ return index;
2542
+ }
2543
+ function removeExtension(filePath) {
2544
+ const ext = path3.extname(filePath);
2545
+ if (ext) {
2546
+ return filePath.slice(0, -ext.length);
2547
+ }
2548
+ return filePath;
2549
+ }
2550
+ async function resolveFileImports(filePath, content, fileId, rootPath, fileIndex) {
2551
+ const imports = await extractImports(filePath, content);
2552
+ return imports.map(
2553
+ (importInfo) => resolveImport(importInfo, fileId, filePath, rootPath, fileIndex)
2554
+ );
2555
+ }
2556
+
2557
+ // src/analyzer/calls.ts
2558
+ async function extractCalls(filePath, content, symbols) {
2559
+ const registry2 = getLanguageRegistry();
2560
+ const language = await registry2.getLanguageForFile(filePath);
2561
+ if (!language) {
2562
+ return [];
2563
+ }
2564
+ const parserManager = getParserManager();
2565
+ const parseResult = await parserManager.safeParse(filePath, content, language);
2566
+ if ("error" in parseResult) {
2567
+ return [];
2568
+ }
2569
+ const tree = parseResult.result.tree;
2570
+ const rawSymbols = symbols.map((s) => ({
2571
+ name: s.name,
2572
+ kind: s.kind,
2573
+ location: s.location,
2574
+ exported: s.exported,
2575
+ parentName: void 0
2576
+ }));
2577
+ switch (language.name) {
2578
+ case "typescript":
2579
+ case "javascript":
2580
+ return extractTSCalls(tree, rawSymbols);
2581
+ case "python":
2582
+ return extractPythonCalls(tree, rawSymbols);
2583
+ default:
2584
+ return [];
2585
+ }
2586
+ }
2587
+ function resolveCall(call, fileSymbols, symbolIndex, callCounts) {
2588
+ const callerSymbol = fileSymbols.find(
2589
+ (s) => s.name === call.callerName && (s.kind === "function" || s.kind === "method")
2590
+ );
2591
+ if (!callerSymbol) {
2592
+ return null;
2593
+ }
2594
+ let calleeSymbol;
2595
+ if (call.callType === "method") {
2596
+ const callerClass = fileSymbols.find(
2597
+ (s) => s.kind === "class" && fileSymbols.some(
2598
+ (m) => m.kind === "method" && m.name === call.callerName && m.fileId === s.fileId
2599
+ )
2600
+ );
2601
+ if (callerClass) {
2602
+ calleeSymbol = fileSymbols.find(
2603
+ (s) => s.kind === "method" && s.name === call.calleeName && s.fileId === callerClass.fileId
2604
+ );
2605
+ }
2606
+ } else {
2607
+ calleeSymbol = symbolIndex.get(call.calleeName);
2608
+ if (!calleeSymbol) {
2609
+ calleeSymbol = fileSymbols.find((s) => s.name === call.calleeName);
2610
+ }
2611
+ }
2612
+ const edgeKey = calleeSymbol ? `${callerSymbol.id}-${calleeSymbol.id}` : `${callerSymbol.id}-unresolved:${call.calleeName}`;
2613
+ const currentCount = callCounts.get(edgeKey) || 0;
2614
+ callCounts.set(edgeKey, currentCount + 1);
2615
+ if (!calleeSymbol) {
2616
+ return {
2617
+ edge: {
2618
+ id: `call-${callerSymbol.id}-unresolved-${call.calleeName}`,
2619
+ sourceId: callerSymbol.id,
2620
+ targetId: `unresolved:${call.calleeName}`,
2621
+ callCount: callCounts.get(edgeKey) || 1,
2622
+ resolved: false
2623
+ },
2624
+ resolved: false
2625
+ };
2626
+ }
2627
+ return {
2628
+ edge: {
2629
+ id: `call-${callerSymbol.id}-${calleeSymbol.id}`,
2630
+ sourceId: callerSymbol.id,
2631
+ targetId: calleeSymbol.id,
2632
+ callCount: callCounts.get(edgeKey) || 1,
2633
+ resolved: true
2634
+ },
2635
+ resolved: true
2636
+ };
2637
+ }
2638
+ async function resolveFileCalls(filePath, content, fileSymbols, globalSymbolIndex) {
2639
+ const rawCalls = await extractCalls(filePath, content, fileSymbols);
2640
+ const resolvedCalls = [];
2641
+ const callCounts = /* @__PURE__ */ new Map();
2642
+ const seenEdges = /* @__PURE__ */ new Set();
2643
+ for (const call of rawCalls) {
2644
+ const resolved = resolveCall(call, fileSymbols, globalSymbolIndex, callCounts);
2645
+ if (resolved && !seenEdges.has(resolved.edge.id)) {
2646
+ seenEdges.add(resolved.edge.id);
2647
+ resolvedCalls.push(resolved);
2648
+ }
2649
+ }
2650
+ for (const resolvedCall of resolvedCalls) {
2651
+ const actualEdgeKey = resolvedCall.edge.sourceId + "-" + (resolvedCall.resolved ? resolvedCall.edge.targetId : resolvedCall.edge.targetId.replace("unresolved:", ""));
2652
+ resolvedCall.edge.callCount = callCounts.get(actualEdgeKey) || 1;
2653
+ }
2654
+ return resolvedCalls;
2655
+ }
2656
+ function buildGlobalSymbolIndex(allSymbols) {
2657
+ const index = /* @__PURE__ */ new Map();
2658
+ for (const symbol of allSymbols) {
2659
+ if (symbol.exported) {
2660
+ if (!index.has(symbol.name)) {
2661
+ index.set(symbol.name, symbol);
2662
+ }
2663
+ }
2664
+ }
2665
+ return index;
2666
+ }
2667
+
2668
+ // src/analyzer/index.ts
2669
+ var DEFAULT_CONFIG2 = {
2670
+ include: [],
2671
+ exclude: [],
2672
+ respectGitignore: true,
2673
+ languages: [],
2674
+ extractCalls: true,
2675
+ maxFiles: 1e4
2676
+ };
2677
+ async function analyzeCodebase(config) {
2678
+ const startTime = performance.now();
2679
+ const errors = [];
2680
+ const fullConfig = {
2681
+ ...DEFAULT_CONFIG2,
2682
+ ...config,
2683
+ rootPath: path4.resolve(config.rootPath)
2684
+ };
2685
+ const { onProgress } = config;
2686
+ onProgress?.({ phase: "scanning", current: 0, total: 1 });
2687
+ const scanConfig = {
2688
+ rootPath: fullConfig.rootPath,
2689
+ include: fullConfig.include,
2690
+ exclude: fullConfig.exclude,
2691
+ respectGitignore: fullConfig.respectGitignore,
2692
+ maxFiles: fullConfig.maxFiles
2693
+ };
2694
+ let scanResult;
2695
+ try {
2696
+ scanResult = await scanDirectory(scanConfig);
2697
+ } catch (err) {
2698
+ const message = err instanceof Error ? err.message : String(err);
2699
+ throw new Error(`Failed to scan directory: ${message}`);
2700
+ }
2701
+ const { directories, files, skipped } = scanResult;
2702
+ const fileIndex = buildFileIndex(
2703
+ files.map((f) => ({ id: f.id, path: f.path }))
2704
+ );
2705
+ const parseStartTime = performance.now();
2706
+ const allSymbols = [];
2707
+ const fileSymbolMap = /* @__PURE__ */ new Map();
2708
+ const fileContentMap = /* @__PURE__ */ new Map();
2709
+ const detectedLanguages = /* @__PURE__ */ new Set();
2710
+ for (let i = 0; i < files.length; i++) {
2711
+ const file = files[i];
2712
+ const filePath = path4.join(fullConfig.rootPath, file.path);
2713
+ onProgress?.({
2714
+ phase: "parsing",
2715
+ current: i + 1,
2716
+ total: files.length,
2717
+ currentFile: file.path
2718
+ });
2719
+ try {
2720
+ const content = fs5.readFileSync(filePath, "utf-8");
2721
+ fileContentMap.set(file.id, content);
2722
+ const result = await extractSymbols(filePath, content, file.id);
2723
+ file.metrics = result.metrics;
2724
+ file.symbols = result.symbols.map((s) => s.id);
2725
+ if (file.language !== "unknown") {
2726
+ detectedLanguages.add(file.language);
2727
+ }
2728
+ allSymbols.push(...result.symbols);
2729
+ fileSymbolMap.set(file.id, result.symbols);
2730
+ if (result.parseErrors) {
2731
+ for (const error of result.parseErrors) {
2732
+ errors.push({ file: file.path, message: error, phase: "parse" });
2733
+ }
2734
+ }
2735
+ } catch (err) {
2736
+ const message = err instanceof Error ? err.message : String(err);
2737
+ errors.push({ file: file.path, message, phase: "parse" });
2738
+ }
2739
+ }
2740
+ const parseTime = performance.now() - parseStartTime;
2741
+ const importEdges = [];
2742
+ const callEdges = [];
2743
+ let resolvedImports = 0;
2744
+ let resolvedCalls = 0;
2745
+ const globalSymbolIndex = buildGlobalSymbolIndex(allSymbols);
2746
+ for (let i = 0; i < files.length; i++) {
2747
+ const file = files[i];
2748
+ const filePath = path4.join(fullConfig.rootPath, file.path);
2749
+ const content = fileContentMap.get(file.id);
2750
+ const fileSymbols = fileSymbolMap.get(file.id) || [];
2751
+ onProgress?.({
2752
+ phase: "resolving",
2753
+ current: i + 1,
2754
+ total: files.length,
2755
+ currentFile: file.path
2756
+ });
2757
+ if (!content) continue;
2758
+ try {
2759
+ const imports = await resolveFileImports(
2760
+ filePath,
2761
+ content,
2762
+ file.id,
2763
+ fullConfig.rootPath,
2764
+ fileIndex
2765
+ );
2766
+ for (const imp of imports) {
2767
+ importEdges.push(imp.edge);
2768
+ if (imp.resolved) {
2769
+ resolvedImports++;
2770
+ }
2771
+ }
2772
+ if (fullConfig.extractCalls) {
2773
+ const calls = await resolveFileCalls(
2774
+ filePath,
2775
+ content,
2776
+ fileSymbols,
2777
+ globalSymbolIndex
2778
+ );
2779
+ for (const call of calls) {
2780
+ callEdges.push(call.edge);
2781
+ if (call.resolved) {
2782
+ resolvedCalls++;
2783
+ }
2784
+ }
2785
+ }
2786
+ } catch (err) {
2787
+ const message = err instanceof Error ? err.message : String(err);
2788
+ errors.push({ file: file.path, message, phase: "resolve" });
2789
+ }
2790
+ }
2791
+ aggregateDirectoryMetrics(directories, files);
2792
+ const totalTime = performance.now() - startTime;
2793
+ const graph = {
2794
+ directories,
2795
+ files,
2796
+ symbols: allSymbols,
2797
+ imports: importEdges,
2798
+ calls: callEdges,
2799
+ metadata: {
2800
+ rootPath: fullConfig.rootPath,
2801
+ analyzedAt: (/* @__PURE__ */ new Date()).toISOString(),
2802
+ totalFiles: files.length,
2803
+ totalDirectories: directories.length,
2804
+ totalSymbols: allSymbols.length,
2805
+ languages: Array.from(detectedLanguages),
2806
+ analysisDurationMs: totalTime
2807
+ }
2808
+ };
2809
+ const stats = {
2810
+ totalFiles: files.length,
2811
+ totalDirectories: directories.length,
2812
+ totalSymbols: allSymbols.length,
2813
+ totalImports: importEdges.length,
2814
+ totalCalls: callEdges.length,
2815
+ resolvedImports,
2816
+ resolvedCalls,
2817
+ parseTime,
2818
+ totalTime,
2819
+ skippedFiles: skipped.length
2820
+ };
2821
+ return { graph, stats, errors };
2822
+ }
2823
+ function aggregateDirectoryMetrics(directories, files) {
2824
+ const dirMap = /* @__PURE__ */ new Map();
2825
+ for (const dir of directories) {
2826
+ dirMap.set(dir.id, dir);
2827
+ }
2828
+ const filesByDir = /* @__PURE__ */ new Map();
2829
+ for (const file of files) {
2830
+ const dirFiles = filesByDir.get(file.directoryId) || [];
2831
+ dirFiles.push(file);
2832
+ filesByDir.set(file.directoryId, dirFiles);
2833
+ }
2834
+ const sortedDirs = [...directories].sort((a, b) => b.depth - a.depth);
2835
+ for (const dir of sortedDirs) {
2836
+ const dirFiles = filesByDir.get(dir.id) || [];
2837
+ let totalLoc = dirFiles.reduce((sum, f) => sum + f.metrics.loc, 0);
2838
+ let totalFiles = dirFiles.length;
2839
+ for (const childId of dir.children) {
2840
+ const child = dirMap.get(childId);
2841
+ if (child) {
2842
+ totalLoc += child.metrics.totalLoc;
2843
+ totalFiles += child.metrics.fileCount;
2844
+ }
2845
+ }
2846
+ dir.metrics = {
2847
+ fileCount: totalFiles,
2848
+ totalLoc
2849
+ };
2850
+ }
2851
+ }
2852
+ function resetAnalyzer() {
2853
+ resetParserManager();
2854
+ resetLanguageRegistry();
2855
+ }
2856
+
1
2857
  // src/index.ts
2
2858
  var VERSION = "0.1.0";
3
- function createCodeViz(_config) {
4
- console.log("codeviz", VERSION);
5
- }
6
2859
  export {
2860
+ AggregateNode as AggregateNodeComponent,
2861
+ BundledEdge as BundledEdgeComponent,
2862
+ CallEdge as CallEdgeComponent,
2863
+ CodeMap as CodeMapComponent,
2864
+ DEFAULT_ANIMATION_CONFIG,
2865
+ DEFAULT_CODEMAP_CONFIG,
2866
+ DEFAULT_DARK_SIGMA_THEME,
2867
+ DEFAULT_LAYOUT_CONFIG,
2868
+ DEFAULT_LIGHT_SIGMA_THEME,
2869
+ DEFAULT_SIGMA_THEME,
2870
+ DEFAULT_ZOOM_LEVELS,
2871
+ EDGE_SIZES,
2872
+ FileNode as FileNodeComponent,
2873
+ Flow,
2874
+ ImportEdge as ImportEdgeComponent,
2875
+ NODE_SIZES,
2876
+ OverlayLayer,
2877
+ OverlayPort,
2878
+ PortalOverlayLayer,
2879
+ SVGOverlayLayer,
2880
+ ScopeNode as ScopeNodeComponent,
2881
+ SigmaRenderer,
2882
+ SymbolNode as SymbolNodeComponent,
2883
+ ThemeProvider,
7
2884
  VERSION,
8
- createCodeViz
2885
+ analyzeCodebase,
2886
+ assignClusterColors,
2887
+ assignInitialPositions,
2888
+ boundingBoxFromCorners,
2889
+ boundingBoxesOverlap,
2890
+ brightenColor,
2891
+ buildDirectoryGraph,
2892
+ buildFileIndex,
2893
+ buildGlobalSymbolIndex,
2894
+ calculateNodeSize,
2895
+ calculateOverlayPosition,
2896
+ clampPosition,
2897
+ codeGraphToGraphology,
2898
+ codeMapToReactFlowEdges,
2899
+ codeMapToReactFlowNodes,
2900
+ computeDirectoryForces,
2901
+ computeEdges,
2902
+ computeHierarchy,
2903
+ computeLayout,
2904
+ computeZoomLevels,
2905
+ createBoundingBox,
2906
+ createEdgeReducer,
2907
+ createNodeReducer,
2908
+ createOverlayPort,
2909
+ createScopeColorGetter,
2910
+ darkTheme,
2911
+ darken,
2912
+ detectCommunities,
2913
+ detectLanguage,
2914
+ dimColor,
2915
+ distance,
2916
+ edgeTypes,
2917
+ expandBoundingBox,
2918
+ extractCalls,
2919
+ extractImports,
2920
+ extractSymbols,
2921
+ extractSymbolsFromFile,
2922
+ formatLoc,
2923
+ formatNumber,
2924
+ generateCallEdgeId,
2925
+ generateDirectoryId,
2926
+ generateFileId,
2927
+ generateImportEdgeId,
2928
+ generateScopeDepthColors,
2929
+ generateSymbolId,
2930
+ getAnchorOffset,
2931
+ getBasename,
2932
+ getBoundingBoxCenter,
2933
+ getChildScopes,
2934
+ getClusterStats,
2935
+ getContrastRatio,
2936
+ getDefaultExpandedScopes,
2937
+ getDefaultTheme,
2938
+ getDirectory,
2939
+ getDirectoryConnectionWeight,
2940
+ getDirectoryPosition,
2941
+ getEdgeLabel,
2942
+ getEdgesAtZoomLevel,
2943
+ getEntityId,
2944
+ getExtension,
2945
+ getFA2Settings,
2946
+ getFileIcon,
2947
+ getFilename,
2948
+ getFilesInDirectory,
2949
+ getIconName,
2950
+ getLanguageRegistry,
2951
+ getLayoutDuration,
2952
+ getLuminance,
2953
+ getMediumFileLabel,
2954
+ getNodeCluster,
2955
+ getNodeKey,
2956
+ getNodeMass,
2957
+ getNodeOpacityAtLevel,
2958
+ getNodeSizeAtLevel,
2959
+ getNodesInCluster,
2960
+ getParentDirectories,
2961
+ getParserManager,
2962
+ getPathDepth,
2963
+ getPositionsAtDepth,
2964
+ getRootScopes,
2965
+ getScaledNodeSize,
2966
+ getScopeByDirectoryId,
2967
+ getScopeDepthColor,
2968
+ getShortFileLabel,
2969
+ getSigmaTheme,
2970
+ getSymbolIcon,
2971
+ getSymbolKindLabel,
2972
+ getTextColorForBackground,
2973
+ getTheme,
2974
+ getThemeIds,
2975
+ getVisibleEdges,
2976
+ getVisibleNodes,
2977
+ getVisibleScopes,
2978
+ getZoomLevel,
2979
+ getZoomLevelFromZoom,
2980
+ graphToScreen,
2981
+ hexToRgb,
2982
+ hslToHex,
2983
+ hslToRgb,
2984
+ isCodeFile,
2985
+ isPointInBoundingBox,
2986
+ lerp,
2987
+ lightTheme,
2988
+ lighten,
2989
+ matchesPattern,
2990
+ mergeTheme,
2991
+ mix,
2992
+ nexusToGraphology,
2993
+ nodeTypes,
2994
+ normalizePath,
2995
+ normalizePositions,
2996
+ positionAllNodes,
2997
+ positionNodesInScope,
2998
+ registerTheme,
2999
+ removeAllOverlaps,
3000
+ removeOverlapsInScope,
3001
+ resetAnalyzer,
3002
+ resetLanguageRegistry,
3003
+ resetParserManager,
3004
+ resolveFileCalls,
3005
+ resolveImport,
3006
+ rgbToHex,
3007
+ rgbToHsl,
3008
+ rgbaToCss,
3009
+ saturate,
3010
+ scaleSize,
3011
+ scanDirectory,
3012
+ screenToGraph,
3013
+ shouldUseDarkText,
3014
+ shrinkBoundingBox,
3015
+ structureToGraphology,
3016
+ truncateLabel,
3017
+ uniformPadding,
3018
+ unionBoundingBoxes,
3019
+ useCurrentTheme,
3020
+ useIsDarkTheme,
3021
+ useLayout,
3022
+ useOverlayPort,
3023
+ useSigma,
3024
+ useTheme,
3025
+ withOpacity
9
3026
  };