@optave/codegraph 3.10.0 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -33
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +91 -60
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/rules/index.d.ts.map +1 -1
- package/dist/ast-analysis/rules/index.js +77 -0
- package/dist/ast-analysis/rules/index.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts +3 -0
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +83 -49
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +78 -62
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +61 -42
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/audit.js +1 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +2 -0
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/check.js +1 -1
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/children.js +1 -1
- package/dist/cli/commands/children.js.map +1 -1
- package/dist/cli/commands/diff-impact.js +1 -1
- package/dist/cli/commands/diff-impact.js.map +1 -1
- package/dist/cli/commands/embed.d.ts.map +1 -1
- package/dist/cli/commands/embed.js +49 -4
- package/dist/cli/commands/embed.js.map +1 -1
- package/dist/cli/commands/roles.js +1 -1
- package/dist/cli/commands/roles.js.map +1 -1
- package/dist/cli/commands/structure.js +1 -1
- package/dist/cli/commands/structure.js.map +1 -1
- package/dist/cli/shared/options.js +1 -1
- package/dist/cli/shared/options.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +8 -0
- package/dist/db/connection.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +106 -80
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +77 -52
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +132 -121
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/graph/builder/helpers.d.ts +4 -4
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +47 -33
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts +6 -6
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +148 -99
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts +1 -0
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +23 -637
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +141 -98
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +82 -65
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +84 -56
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +60 -51
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts +8 -6
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +107 -122
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts +14 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js +77 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts +62 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js +747 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -0
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +73 -22
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -4
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +50 -55
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/journal.d.ts.map +1 -1
- package/dist/domain/graph/journal.js +89 -70
- package/dist/domain/graph/journal.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +28 -20
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +12 -23
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +153 -80
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.d.ts +3 -1
- package/dist/domain/search/generator.d.ts.map +1 -1
- package/dist/domain/search/generator.js +68 -45
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +18 -0
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +72 -4
- package/dist/domain/search/models.js.map +1 -1
- package/dist/domain/search/search/hybrid.d.ts.map +1 -1
- package/dist/domain/search/search/hybrid.js +49 -40
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/domain/search/search/semantic.d.ts.map +1 -1
- package/dist/domain/search/search/semantic.js +69 -49
- package/dist/domain/search/search/semantic.js.map +1 -1
- package/dist/domain/wasm-worker-entry.js +209 -137
- package/dist/domain/wasm-worker-entry.js.map +1 -1
- package/dist/extractors/c.js +25 -6
- package/dist/extractors/c.js.map +1 -1
- package/dist/extractors/cpp.js +47 -6
- package/dist/extractors/cpp.js.map +1 -1
- package/dist/extractors/cuda.js +90 -14
- package/dist/extractors/cuda.js.map +1 -1
- package/dist/extractors/elixir.js +108 -4
- package/dist/extractors/elixir.js.map +1 -1
- package/dist/extractors/erlang.js +56 -20
- package/dist/extractors/erlang.js.map +1 -1
- package/dist/extractors/fsharp.d.ts +7 -0
- package/dist/extractors/fsharp.d.ts.map +1 -1
- package/dist/extractors/fsharp.js +94 -0
- package/dist/extractors/fsharp.js.map +1 -1
- package/dist/extractors/gleam.d.ts.map +1 -1
- package/dist/extractors/gleam.js +29 -33
- package/dist/extractors/gleam.js.map +1 -1
- package/dist/extractors/groovy.js +41 -1
- package/dist/extractors/groovy.js.map +1 -1
- package/dist/extractors/haskell.js +48 -4
- package/dist/extractors/haskell.js.map +1 -1
- package/dist/extractors/helpers.d.ts +79 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +137 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.d.ts.map +1 -1
- package/dist/extractors/java.js +37 -49
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +44 -44
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/julia.js +198 -74
- package/dist/extractors/julia.js.map +1 -1
- package/dist/extractors/kotlin.js +4 -0
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/objc.js +184 -47
- package/dist/extractors/objc.js.map +1 -1
- package/dist/extractors/python.js +7 -4
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/r.d.ts.map +1 -1
- package/dist/extractors/r.js +103 -87
- package/dist/extractors/r.js.map +1 -1
- package/dist/extractors/scala.d.ts.map +1 -1
- package/dist/extractors/scala.js +18 -32
- package/dist/extractors/scala.js.map +1 -1
- package/dist/extractors/solidity.d.ts.map +1 -1
- package/dist/extractors/solidity.js +55 -69
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/verilog.js +80 -15
- package/dist/extractors/verilog.js.map +1 -1
- package/dist/features/boundaries.d.ts.map +1 -1
- package/dist/features/boundaries.js +49 -39
- package/dist/features/boundaries.js.map +1 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +90 -63
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +43 -34
- package/dist/features/check.js.map +1 -1
- package/dist/features/cochange.d.ts.map +1 -1
- package/dist/features/cochange.js +68 -56
- package/dist/features/cochange.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +105 -75
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +37 -29
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +31 -22
- package/dist/features/flow.js.map +1 -1
- package/dist/features/graph-enrichment.d.ts.map +1 -1
- package/dist/features/graph-enrichment.js +77 -70
- package/dist/features/graph-enrichment.js.map +1 -1
- package/dist/features/owners.d.ts +17 -26
- package/dist/features/owners.d.ts.map +1 -1
- package/dist/features/owners.js +120 -109
- package/dist/features/owners.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +59 -54
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +60 -60
- package/dist/features/structure-query.js.map +1 -1
- package/dist/features/structure.js +28 -36
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +100 -69
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +63 -59
- package/dist/graph/classifiers/roles.js.map +1 -1
- package/dist/infrastructure/config.d.ts +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +1 -1
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/mcp/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tool-registry.js +4 -0
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/mcp/tools/semantic-search.d.ts +1 -0
- package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
- package/dist/mcp/tools/semantic-search.js +1 -0
- package/dist/mcp/tools/semantic-search.js.map +1 -1
- package/dist/presentation/cfg.d.ts.map +1 -1
- package/dist/presentation/cfg.js +44 -29
- package/dist/presentation/cfg.js.map +1 -1
- package/dist/presentation/flow.d.ts.map +1 -1
- package/dist/presentation/flow.js +58 -38
- package/dist/presentation/flow.js.map +1 -1
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-fsharp.wasm +0 -0
- package/grammars/tree-sitter-fsharp_signature.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/package.json +10 -10
- package/src/ast-analysis/engine.ts +145 -61
- package/src/ast-analysis/rules/index.ts +87 -0
- package/src/ast-analysis/visitor-utils.ts +86 -46
- package/src/ast-analysis/visitors/ast-store-visitor.ts +104 -69
- package/src/ast-analysis/visitors/dataflow-visitor.ts +86 -47
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/build.ts +2 -0
- package/src/cli/commands/check.ts +1 -1
- package/src/cli/commands/children.ts +1 -1
- package/src/cli/commands/diff-impact.ts +1 -1
- package/src/cli/commands/embed.ts +54 -4
- package/src/cli/commands/roles.ts +1 -1
- package/src/cli/commands/structure.ts +1 -1
- package/src/cli/shared/options.ts +1 -1
- package/src/db/connection.ts +8 -0
- package/src/domain/analysis/dependencies.ts +166 -85
- package/src/domain/analysis/fn-impact.ts +120 -50
- package/src/domain/analysis/module-map.ts +175 -140
- package/src/domain/graph/builder/helpers.ts +85 -76
- package/src/domain/graph/builder/incremental.ts +223 -131
- package/src/domain/graph/builder/pipeline.ts +32 -785
- package/src/domain/graph/builder/stages/build-edges.ts +207 -142
- package/src/domain/graph/builder/stages/build-structure.ts +115 -82
- package/src/domain/graph/builder/stages/detect-changes.ts +107 -64
- package/src/domain/graph/builder/stages/finalize.ts +72 -70
- package/src/domain/graph/builder/stages/insert-nodes.ts +154 -120
- package/src/domain/graph/builder/stages/native-db-lifecycle.ts +74 -0
- package/src/domain/graph/builder/stages/native-orchestrator.ts +942 -0
- package/src/domain/graph/builder/stages/resolve-imports.ts +79 -25
- package/src/domain/graph/cycles.ts +51 -49
- package/src/domain/graph/journal.ts +84 -69
- package/src/domain/graph/watcher.ts +29 -25
- package/src/domain/parser.ts +170 -67
- package/src/domain/search/generator.ts +132 -74
- package/src/domain/search/models.ts +75 -4
- package/src/domain/search/search/hybrid.ts +53 -42
- package/src/domain/search/search/semantic.ts +105 -65
- package/src/domain/wasm-worker-entry.ts +243 -153
- package/src/extractors/c.ts +27 -8
- package/src/extractors/cpp.ts +50 -8
- package/src/extractors/cuda.ts +90 -16
- package/src/extractors/elixir.ts +103 -4
- package/src/extractors/erlang.ts +63 -20
- package/src/extractors/fsharp.ts +104 -0
- package/src/extractors/gleam.ts +40 -39
- package/src/extractors/groovy.ts +45 -1
- package/src/extractors/haskell.ts +45 -4
- package/src/extractors/helpers.ts +205 -1
- package/src/extractors/java.ts +42 -45
- package/src/extractors/javascript.ts +44 -43
- package/src/extractors/julia.ts +191 -77
- package/src/extractors/kotlin.ts +4 -0
- package/src/extractors/objc.ts +171 -47
- package/src/extractors/python.ts +5 -3
- package/src/extractors/r.ts +104 -82
- package/src/extractors/scala.ts +24 -36
- package/src/extractors/solidity.ts +59 -78
- package/src/extractors/verilog.ts +83 -15
- package/src/features/boundaries.ts +64 -46
- package/src/features/cfg.ts +145 -74
- package/src/features/check.ts +60 -43
- package/src/features/cochange.ts +95 -72
- package/src/features/complexity.ts +134 -79
- package/src/features/dataflow.ts +57 -34
- package/src/features/flow.ts +48 -24
- package/src/features/graph-enrichment.ts +105 -70
- package/src/features/owners.ts +186 -146
- package/src/features/sequence.ts +99 -69
- package/src/features/structure-query.ts +94 -79
- package/src/features/structure.ts +56 -56
- package/src/graph/algorithms/leiden/optimiser.ts +142 -87
- package/src/graph/classifiers/roles.ts +64 -54
- package/src/infrastructure/config.ts +1 -1
- package/src/mcp/tool-registry.ts +5 -0
- package/src/mcp/tools/semantic-search.ts +2 -0
- package/src/presentation/cfg.ts +48 -32
- package/src/presentation/flow.ts +100 -52
- package/src/types.ts +16 -1
|
@@ -753,6 +753,146 @@ function allNativeDataComplete(
|
|
|
753
753
|
|
|
754
754
|
// ─── Public API ──────────────────────────────────────────────────────────
|
|
755
755
|
|
|
756
|
+
/** Distribute the per-file walk time equally among the visitors that ran. */
|
|
757
|
+
function accumulateWalkTime(
|
|
758
|
+
timing: AnalysisTiming,
|
|
759
|
+
walkMs: number,
|
|
760
|
+
astVisitor: Visitor | null,
|
|
761
|
+
complexityVisitor: Visitor | null,
|
|
762
|
+
cfgVisitor: Visitor | null,
|
|
763
|
+
dataflowVisitor: Visitor | null,
|
|
764
|
+
): void {
|
|
765
|
+
const activeCount = [astVisitor, complexityVisitor, cfgVisitor, dataflowVisitor].filter(
|
|
766
|
+
Boolean,
|
|
767
|
+
).length;
|
|
768
|
+
if (activeCount === 0) return;
|
|
769
|
+
|
|
770
|
+
const share = walkMs / activeCount;
|
|
771
|
+
if (astVisitor) timing.astMs += share;
|
|
772
|
+
if (complexityVisitor) timing.complexityMs += share;
|
|
773
|
+
if (cfgVisitor) timing.cfgMs += share;
|
|
774
|
+
if (dataflowVisitor) timing.dataflowMs += share;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/** Apply visitor walk results to the per-file symbols/definitions. */
|
|
778
|
+
function applyVisitorResults(
|
|
779
|
+
results: WalkResults,
|
|
780
|
+
symbols: ExtractorOutput,
|
|
781
|
+
langId: string,
|
|
782
|
+
astVisitor: Visitor | null,
|
|
783
|
+
complexityVisitor: Visitor | null,
|
|
784
|
+
cfgVisitor: Visitor | null,
|
|
785
|
+
dataflowVisitor: Visitor | null,
|
|
786
|
+
): void {
|
|
787
|
+
const defs = symbols.definitions || [];
|
|
788
|
+
|
|
789
|
+
if (astVisitor) {
|
|
790
|
+
const astRows = (results['ast-store'] || []) as ASTNodeRow[];
|
|
791
|
+
if (astRows.length > 0) symbols.astNodes = astRows;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (complexityVisitor) storeComplexityResults(results, defs, langId);
|
|
795
|
+
if (cfgVisitor) storeCfgResults(results, defs);
|
|
796
|
+
if (dataflowVisitor) symbols.dataflow = results.dataflow as DataflowResult;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/** Process a single file: set up visitors, walk the tree, and apply results. */
|
|
800
|
+
function processFileWalk(
|
|
801
|
+
db: BetterSqlite3Database,
|
|
802
|
+
relPath: string,
|
|
803
|
+
symbols: ExtractorOutput,
|
|
804
|
+
langId: string,
|
|
805
|
+
opts: AnalysisOpts,
|
|
806
|
+
timing: AnalysisTiming,
|
|
807
|
+
): void {
|
|
808
|
+
if (!symbols._tree) return;
|
|
809
|
+
|
|
810
|
+
const { visitors, walkerOpts, astVisitor, complexityVisitor, cfgVisitor, dataflowVisitor } =
|
|
811
|
+
setupVisitors(db, relPath, symbols, langId, opts);
|
|
812
|
+
|
|
813
|
+
if (visitors.length === 0) return;
|
|
814
|
+
|
|
815
|
+
const walkStart = performance.now();
|
|
816
|
+
const results = walkWithVisitors(symbols._tree.rootNode, visitors, langId, walkerOpts);
|
|
817
|
+
const walkMs = performance.now() - walkStart;
|
|
818
|
+
|
|
819
|
+
accumulateWalkTime(timing, walkMs, astVisitor, complexityVisitor, cfgVisitor, dataflowVisitor);
|
|
820
|
+
applyVisitorResults(
|
|
821
|
+
results,
|
|
822
|
+
symbols,
|
|
823
|
+
langId,
|
|
824
|
+
astVisitor,
|
|
825
|
+
complexityVisitor,
|
|
826
|
+
cfgVisitor,
|
|
827
|
+
dataflowVisitor,
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Unified pre-walk: run all applicable visitors in a single DFS per file.
|
|
833
|
+
* Returns the total wall-clock time for diagnostics.
|
|
834
|
+
*/
|
|
835
|
+
function runUnifiedWalkPass(
|
|
836
|
+
db: BetterSqlite3Database,
|
|
837
|
+
fileSymbols: Map<string, ExtractorOutput>,
|
|
838
|
+
extToLang: Map<string, string>,
|
|
839
|
+
opts: AnalysisOpts,
|
|
840
|
+
timing: AnalysisTiming,
|
|
841
|
+
): number {
|
|
842
|
+
const t0walk = performance.now();
|
|
843
|
+
|
|
844
|
+
for (const [relPath, symbols] of fileSymbols) {
|
|
845
|
+
if (!symbols._tree) continue;
|
|
846
|
+
|
|
847
|
+
const ext = path.extname(relPath).toLowerCase();
|
|
848
|
+
const langId = symbols._langId || extToLang.get(ext);
|
|
849
|
+
if (!langId) continue;
|
|
850
|
+
|
|
851
|
+
processFileWalk(db, relPath, symbols, langId, opts, timing);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
return performance.now() - t0walk;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/** Try native Rust standalone analysis to fill gaps before WASM fallback. */
|
|
858
|
+
function tryNativeStandaloneAnalysis(
|
|
859
|
+
fileSymbols: Map<string, ExtractorOutput>,
|
|
860
|
+
rootDir: string,
|
|
861
|
+
opts: AnalysisOpts,
|
|
862
|
+
extToLang: Map<string, string>,
|
|
863
|
+
): void {
|
|
864
|
+
const native = loadNative();
|
|
865
|
+
if (!native?.analyzeComplexity && !native?.buildCfgAnalysis && !native?.extractDataflowAnalysis) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
const t0native = performance.now();
|
|
869
|
+
runNativeAnalysis(native, fileSymbols, rootDir, opts, extToLang);
|
|
870
|
+
debug(`native standalone analysis: ${(performance.now() - t0native).toFixed(1)}ms`);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Fast path: when all files were parsed by the native engine with full analysis,
|
|
875
|
+
* skip WASM re-parse and JS visitor walks entirely and go straight to DB persistence.
|
|
876
|
+
* Returns true if the fast path handled the work.
|
|
877
|
+
*/
|
|
878
|
+
async function runFastPathIfApplicable(
|
|
879
|
+
db: BetterSqlite3Database,
|
|
880
|
+
fileSymbols: Map<string, ExtractorOutput>,
|
|
881
|
+
rootDir: string,
|
|
882
|
+
opts: AnalysisOpts,
|
|
883
|
+
engineOpts: EngineOpts | undefined,
|
|
884
|
+
timing: AnalysisTiming,
|
|
885
|
+
): Promise<boolean> {
|
|
886
|
+
if (!allNativeDataComplete(fileSymbols, opts)) return false;
|
|
887
|
+
|
|
888
|
+
debug('native full-analysis fast path: all data present, skipping WASM/visitor passes');
|
|
889
|
+
const doComplexity = opts.complexity !== false;
|
|
890
|
+
const doCfg = opts.cfg !== false;
|
|
891
|
+
if (doComplexity && doCfg) reconcileCfgCyclomatic(fileSymbols);
|
|
892
|
+
await delegateToBuildFunctions(db, fileSymbols, rootDir, opts, engineOpts, timing);
|
|
893
|
+
return true;
|
|
894
|
+
}
|
|
895
|
+
|
|
756
896
|
export async function runAnalyses(
|
|
757
897
|
db: BetterSqlite3Database,
|
|
758
898
|
fileSymbols: Map<string, ExtractorOutput>,
|
|
@@ -771,80 +911,24 @@ export async function runAnalyses(
|
|
|
771
911
|
|
|
772
912
|
const extToLang = buildExtToLangMap();
|
|
773
913
|
|
|
774
|
-
|
|
775
|
-
// (parseFilesFull), all data is already present — skip WASM re-parse and JS
|
|
776
|
-
// visitor walks entirely, go straight to DB persistence.
|
|
777
|
-
if (allNativeDataComplete(fileSymbols, opts)) {
|
|
778
|
-
debug('native full-analysis fast path: all data present, skipping WASM/visitor passes');
|
|
779
|
-
if (doComplexity && doCfg) reconcileCfgCyclomatic(fileSymbols);
|
|
780
|
-
await delegateToBuildFunctions(db, fileSymbols, rootDir, opts, engineOpts, timing);
|
|
914
|
+
if (await runFastPathIfApplicable(db, fileSymbols, rootDir, opts, engineOpts, timing)) {
|
|
781
915
|
return timing;
|
|
782
916
|
}
|
|
783
917
|
|
|
784
918
|
// Native analysis pass: try Rust standalone functions before WASM fallback.
|
|
785
919
|
// This fills in complexity/CFG/dataflow for files that the native parse pipeline
|
|
786
920
|
// missed, avoiding the need to parse with WASM + run JS visitors.
|
|
787
|
-
|
|
788
|
-
if (native?.analyzeComplexity || native?.buildCfgAnalysis || native?.extractDataflowAnalysis) {
|
|
789
|
-
const t0native = performance.now();
|
|
790
|
-
runNativeAnalysis(native, fileSymbols, rootDir, opts, extToLang);
|
|
791
|
-
debug(`native standalone analysis: ${(performance.now() - t0native).toFixed(1)}ms`);
|
|
792
|
-
}
|
|
921
|
+
tryNativeStandaloneAnalysis(fileSymbols, rootDir, opts, extToLang);
|
|
793
922
|
|
|
794
923
|
// WASM pre-parse for files that still need it (AST store, or native gaps)
|
|
795
924
|
await ensureWasmTreesIfNeeded(fileSymbols, opts, rootDir);
|
|
796
925
|
|
|
797
|
-
// Unified pre-walk: run all applicable visitors in a single DFS per file.
|
|
798
926
|
// Time each file's walk and distribute equally among active visitors
|
|
799
927
|
// so that phase timers (astMs, complexityMs, etc.) reflect real work — not
|
|
800
928
|
// just the DB-write tail in delegateToBuildFunctions.
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if (!symbols._tree) continue;
|
|
805
|
-
|
|
806
|
-
const ext = path.extname(relPath).toLowerCase();
|
|
807
|
-
const langId = symbols._langId || extToLang.get(ext);
|
|
808
|
-
if (!langId) continue;
|
|
809
|
-
|
|
810
|
-
const { visitors, walkerOpts, astVisitor, complexityVisitor, cfgVisitor, dataflowVisitor } =
|
|
811
|
-
setupVisitors(db, relPath, symbols, langId, opts);
|
|
812
|
-
|
|
813
|
-
if (visitors.length === 0) continue;
|
|
814
|
-
|
|
815
|
-
const walkStart = performance.now();
|
|
816
|
-
const results = walkWithVisitors(symbols._tree.rootNode, visitors, langId, walkerOpts);
|
|
817
|
-
const walkMs = performance.now() - walkStart;
|
|
818
|
-
|
|
819
|
-
// Distribute walk time equally among active visitors
|
|
820
|
-
const activeCount = [astVisitor, complexityVisitor, cfgVisitor, dataflowVisitor].filter(
|
|
821
|
-
Boolean,
|
|
822
|
-
).length;
|
|
823
|
-
if (activeCount > 0) {
|
|
824
|
-
const share = walkMs / activeCount;
|
|
825
|
-
if (astVisitor) timing.astMs += share;
|
|
826
|
-
if (complexityVisitor) timing.complexityMs += share;
|
|
827
|
-
if (cfgVisitor) timing.cfgMs += share;
|
|
828
|
-
if (dataflowVisitor) timing.dataflowMs += share;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
const defs = symbols.definitions || [];
|
|
832
|
-
|
|
833
|
-
if (astVisitor) {
|
|
834
|
-
const astRows = (results['ast-store'] || []) as ASTNodeRow[];
|
|
835
|
-
if (astRows.length > 0) symbols.astNodes = astRows;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
if (complexityVisitor) storeComplexityResults(results, defs, langId);
|
|
839
|
-
if (cfgVisitor) storeCfgResults(results, defs);
|
|
840
|
-
if (dataflowVisitor) symbols.dataflow = results.dataflow as DataflowResult;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// Total wall-clock time for the unified walk loop, including per-file
|
|
844
|
-
// setupVisitors overhead. Walk time is already distributed into per-phase
|
|
845
|
-
// timers above, so this field overlaps with (astMs + complexityMs + ...).
|
|
846
|
-
// It is kept as a diagnostic cross-check, not an additive bucket.
|
|
847
|
-
timing._unifiedWalkMs = performance.now() - t0walk;
|
|
929
|
+
// _unifiedWalkMs is kept as a diagnostic cross-check (overlaps with the
|
|
930
|
+
// per-phase timers above, not additive).
|
|
931
|
+
timing._unifiedWalkMs = runUnifiedWalkPass(db, fileSymbols, extToLang, opts, timing);
|
|
848
932
|
|
|
849
933
|
// Reconcile: apply CFG-derived cyclomatic override for any definitions that have
|
|
850
934
|
// both precomputed complexity and CFG data but whose cyclomatic was never overridden.
|
|
@@ -101,6 +101,11 @@ const CPP_AST_TYPES: Record<string, string> = {
|
|
|
101
101
|
raw_string_literal: 'string',
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
+
// CUDA's tree-sitter grammar inherits the full C++ node vocabulary, so the
|
|
105
|
+
// AST node types and quote rules are identical to C++. Mirrors the native
|
|
106
|
+
// `CUDA_AST_CONFIG` in `crates/codegraph-core/src/extractors/helpers.rs`.
|
|
107
|
+
const CUDA_AST_TYPES: Record<string, string> = CPP_AST_TYPES;
|
|
108
|
+
|
|
104
109
|
const KOTLIN_AST_TYPES: Record<string, string> = {
|
|
105
110
|
throw_expression: 'throw',
|
|
106
111
|
string_literal: 'string',
|
|
@@ -153,6 +158,55 @@ const OCAML_AST_TYPES: Record<string, string> = {
|
|
|
153
158
|
string: 'string',
|
|
154
159
|
};
|
|
155
160
|
|
|
161
|
+
const FSHARP_AST_TYPES: Record<string, string> = {
|
|
162
|
+
string: 'string',
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const OBJC_AST_TYPES: Record<string, string> = {
|
|
166
|
+
throw_statement: 'throw',
|
|
167
|
+
string_literal: 'string',
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const GLEAM_AST_TYPES: Record<string, string> = {
|
|
171
|
+
string: 'string',
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const JULIA_AST_TYPES: Record<string, string> = {
|
|
175
|
+
string_literal: 'string',
|
|
176
|
+
prefixed_string_literal: 'string',
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const CLOJURE_AST_TYPES: Record<string, string> = {
|
|
180
|
+
str_lit: 'string',
|
|
181
|
+
regex_lit: 'regex',
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const ERLANG_AST_TYPES: Record<string, string> = {
|
|
185
|
+
string: 'string',
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const GROOVY_AST_TYPES: Record<string, string> = {
|
|
189
|
+
object_creation_expression: 'new',
|
|
190
|
+
throw_statement: 'throw',
|
|
191
|
+
string_literal: 'string',
|
|
192
|
+
// `gstring` listed defensively: tree-sitter-groovy 0.1.x emits `string_literal`
|
|
193
|
+
// for both single- and double-quoted strings, but some grammar variants use
|
|
194
|
+
// `gstring` for double-quoted / interpolated strings.
|
|
195
|
+
gstring: 'string',
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const R_AST_TYPES: Record<string, string> = {
|
|
199
|
+
string: 'string',
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const SOLIDITY_AST_TYPES: Record<string, string> = {
|
|
203
|
+
new_expression: 'new',
|
|
204
|
+
revert_statement: 'throw',
|
|
205
|
+
string_literal: 'string',
|
|
206
|
+
hex_string_literal: 'string',
|
|
207
|
+
unicode_string_literal: 'string',
|
|
208
|
+
};
|
|
209
|
+
|
|
156
210
|
export const AST_TYPE_MAPS: Map<string, Record<string, string>> = new Map([
|
|
157
211
|
['javascript', JS_AST_TYPES],
|
|
158
212
|
['typescript', JS_AST_TYPES],
|
|
@@ -166,6 +220,7 @@ export const AST_TYPE_MAPS: Map<string, Record<string, string>> = new Map([
|
|
|
166
220
|
['php', PHP_AST_TYPES],
|
|
167
221
|
['c', C_AST_TYPES],
|
|
168
222
|
['cpp', CPP_AST_TYPES],
|
|
223
|
+
['cuda', CUDA_AST_TYPES],
|
|
169
224
|
['kotlin', KOTLIN_AST_TYPES],
|
|
170
225
|
['swift', SWIFT_AST_TYPES],
|
|
171
226
|
['scala', SCALA_AST_TYPES],
|
|
@@ -177,6 +232,16 @@ export const AST_TYPE_MAPS: Map<string, Record<string, string>> = new Map([
|
|
|
177
232
|
['haskell', HASKELL_AST_TYPES],
|
|
178
233
|
['ocaml', OCAML_AST_TYPES],
|
|
179
234
|
['ocaml-interface', OCAML_AST_TYPES],
|
|
235
|
+
['fsharp', FSHARP_AST_TYPES],
|
|
236
|
+
['fsharp-signature', FSHARP_AST_TYPES],
|
|
237
|
+
['objc', OBJC_AST_TYPES],
|
|
238
|
+
['gleam', GLEAM_AST_TYPES],
|
|
239
|
+
['julia', JULIA_AST_TYPES],
|
|
240
|
+
['clojure', CLOJURE_AST_TYPES],
|
|
241
|
+
['erlang', ERLANG_AST_TYPES],
|
|
242
|
+
['groovy', GROOVY_AST_TYPES],
|
|
243
|
+
['r', R_AST_TYPES],
|
|
244
|
+
['solidity', SOLIDITY_AST_TYPES],
|
|
180
245
|
]);
|
|
181
246
|
|
|
182
247
|
// ─── Per-language string-extraction config ───────────────────────────────
|
|
@@ -201,6 +266,8 @@ const RB_STRING_CONFIG: AstStringConfig = { quoteChars: '\'"', stringPrefixes: '
|
|
|
201
266
|
const PHP_STRING_CONFIG: AstStringConfig = { quoteChars: '\'"', stringPrefixes: '' };
|
|
202
267
|
const C_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
203
268
|
const CPP_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: 'LuUR' };
|
|
269
|
+
// CUDA shares C++ string-literal lexing, including the `L`/`u`/`U`/`R` prefixes.
|
|
270
|
+
const CUDA_STRING_CONFIG: AstStringConfig = CPP_STRING_CONFIG;
|
|
204
271
|
const KOTLIN_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
205
272
|
const SWIFT_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
206
273
|
const SCALA_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
@@ -211,6 +278,15 @@ const DART_STRING_CONFIG: AstStringConfig = { quoteChars: '\'"', stringPrefixes:
|
|
|
211
278
|
const ZIG_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
212
279
|
const HASKELL_STRING_CONFIG: AstStringConfig = { quoteChars: '"\'', stringPrefixes: '' };
|
|
213
280
|
const OCAML_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
281
|
+
const FSHARP_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
282
|
+
const OBJC_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
283
|
+
const GLEAM_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
284
|
+
const JULIA_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
285
|
+
const CLOJURE_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
286
|
+
const ERLANG_STRING_CONFIG: AstStringConfig = { quoteChars: '"', stringPrefixes: '' };
|
|
287
|
+
const GROOVY_STRING_CONFIG: AstStringConfig = { quoteChars: '\'"', stringPrefixes: '' };
|
|
288
|
+
const R_STRING_CONFIG: AstStringConfig = { quoteChars: '\'"', stringPrefixes: '' };
|
|
289
|
+
const SOLIDITY_STRING_CONFIG: AstStringConfig = { quoteChars: '"\'', stringPrefixes: '' };
|
|
214
290
|
|
|
215
291
|
export const AST_STRING_CONFIGS: Map<string, AstStringConfig> = new Map([
|
|
216
292
|
['javascript', JS_STRING_CONFIG],
|
|
@@ -225,6 +301,7 @@ export const AST_STRING_CONFIGS: Map<string, AstStringConfig> = new Map([
|
|
|
225
301
|
['php', PHP_STRING_CONFIG],
|
|
226
302
|
['c', C_STRING_CONFIG],
|
|
227
303
|
['cpp', CPP_STRING_CONFIG],
|
|
304
|
+
['cuda', CUDA_STRING_CONFIG],
|
|
228
305
|
['kotlin', KOTLIN_STRING_CONFIG],
|
|
229
306
|
['swift', SWIFT_STRING_CONFIG],
|
|
230
307
|
['scala', SCALA_STRING_CONFIG],
|
|
@@ -236,6 +313,16 @@ export const AST_STRING_CONFIGS: Map<string, AstStringConfig> = new Map([
|
|
|
236
313
|
['haskell', HASKELL_STRING_CONFIG],
|
|
237
314
|
['ocaml', OCAML_STRING_CONFIG],
|
|
238
315
|
['ocaml-interface', OCAML_STRING_CONFIG],
|
|
316
|
+
['fsharp', FSHARP_STRING_CONFIG],
|
|
317
|
+
['fsharp-signature', FSHARP_STRING_CONFIG],
|
|
318
|
+
['objc', OBJC_STRING_CONFIG],
|
|
319
|
+
['gleam', GLEAM_STRING_CONFIG],
|
|
320
|
+
['julia', JULIA_STRING_CONFIG],
|
|
321
|
+
['clojure', CLOJURE_STRING_CONFIG],
|
|
322
|
+
['erlang', ERLANG_STRING_CONFIG],
|
|
323
|
+
['groovy', GROOVY_STRING_CONFIG],
|
|
324
|
+
['r', R_STRING_CONFIG],
|
|
325
|
+
['solidity', SOLIDITY_STRING_CONFIG],
|
|
239
326
|
]);
|
|
240
327
|
|
|
241
328
|
// ─── Per-language "stop-after-collect" kinds ─────────────────────────────
|
|
@@ -88,78 +88,118 @@ export function extractParams(
|
|
|
88
88
|
return result;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/** Extract names from a rest parameter (e.g. `...args`). */
|
|
92
|
-
function extractRestParamNames(node: TreeSitterNode, rules: LanguageRules): string[] {
|
|
93
|
-
const nameNode = node.childForFieldName('name');
|
|
94
|
-
if (nameNode) return [nameNode.text];
|
|
95
|
-
for (const child of node.namedChildren) {
|
|
96
|
-
if (child.type === rules.paramIdentifier) return [child.text];
|
|
97
|
-
}
|
|
98
|
-
return [];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Extract names from an object destructuring pattern (e.g. `{ a, b: c }`). */
|
|
102
|
-
function extractObjectDestructNames(node: TreeSitterNode, rules: LanguageRules): string[] {
|
|
103
|
-
const names: string[] = [];
|
|
104
|
-
for (const child of node.namedChildren) {
|
|
105
|
-
if (rules.shorthandPropPattern && child.type === rules.shorthandPropPattern) {
|
|
106
|
-
names.push(child.text);
|
|
107
|
-
} else if (rules.pairPatternType && child.type === rules.pairPatternType) {
|
|
108
|
-
const value = child.childForFieldName('value');
|
|
109
|
-
if (value) names.push(...extractParamNames(value, rules));
|
|
110
|
-
} else if (rules.restParamType && child.type === rules.restParamType) {
|
|
111
|
-
names.push(...extractParamNames(child, rules));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return names;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Extract names from an array destructuring pattern (e.g. `[a, b]`). */
|
|
118
|
-
function extractArrayDestructNames(node: TreeSitterNode, rules: LanguageRules): string[] {
|
|
119
|
-
const names: string[] = [];
|
|
120
|
-
for (const child of node.namedChildren) {
|
|
121
|
-
names.push(...extractParamNames(child, rules));
|
|
122
|
-
}
|
|
123
|
-
return names;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
91
|
/**
|
|
127
|
-
*
|
|
92
|
+
* Resolve a single parameter node to either a direct list of names (base case)
|
|
93
|
+
* or a list of child nodes that still need processing. Returns `null` if the
|
|
94
|
+
* node yields nothing.
|
|
95
|
+
*
|
|
96
|
+
* This base case keeps destructuring helpers from recursing back into
|
|
97
|
+
* `extractParamNames`, breaking the 3-node mutual recursion cycle between
|
|
98
|
+
* `extractParamNames`, `extractObjectDestructNames`, and `extractArrayDestructNames`.
|
|
128
99
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
100
|
+
function resolveParamNode(
|
|
101
|
+
node: TreeSitterNode,
|
|
102
|
+
rules: LanguageRules,
|
|
103
|
+
): { names?: string[]; next?: TreeSitterNode[] } | null {
|
|
131
104
|
const t = node.type;
|
|
132
105
|
|
|
133
106
|
if (rules.extractParamName) {
|
|
134
107
|
const result = rules.extractParamName(node);
|
|
135
|
-
if (result) return result;
|
|
108
|
+
if (result) return { names: result };
|
|
136
109
|
}
|
|
137
110
|
|
|
138
|
-
if (t === rules.paramIdentifier) return [node.text];
|
|
111
|
+
if (t === rules.paramIdentifier) return { names: [node.text] };
|
|
139
112
|
|
|
140
113
|
if (rules.paramWrapperTypes.has(t)) {
|
|
141
114
|
const pattern = node.childForFieldName('pattern') || node.childForFieldName('name');
|
|
142
|
-
return pattern ?
|
|
115
|
+
return pattern ? { next: [pattern] } : null;
|
|
143
116
|
}
|
|
144
117
|
|
|
145
118
|
if (rules.defaultParamType && t === rules.defaultParamType) {
|
|
146
119
|
const left = node.childForFieldName('left') || node.childForFieldName('name');
|
|
147
|
-
return left ?
|
|
120
|
+
return left ? { next: [left] } : null;
|
|
148
121
|
}
|
|
149
122
|
|
|
150
123
|
if (rules.restParamType && t === rules.restParamType) {
|
|
151
|
-
|
|
124
|
+
const nameNode = node.childForFieldName('name');
|
|
125
|
+
if (nameNode) return { names: [nameNode.text] };
|
|
126
|
+
for (const child of node.namedChildren) {
|
|
127
|
+
if (child.type === rules.paramIdentifier) return { names: [child.text] };
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
152
130
|
}
|
|
153
131
|
|
|
154
132
|
if (rules.objectDestructType && t === rules.objectDestructType) {
|
|
155
|
-
return
|
|
133
|
+
return { next: collectObjectDestructChildren(node, rules) };
|
|
156
134
|
}
|
|
157
135
|
|
|
158
136
|
if (rules.arrayDestructType && t === rules.arrayDestructType) {
|
|
159
|
-
return
|
|
137
|
+
return { next: [...node.namedChildren] };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Collect child nodes from an object destructuring pattern that should be
|
|
145
|
+
* processed for further name extraction. Returns nodes (not names) so the
|
|
146
|
+
* caller drives traversal via a worklist instead of recursion.
|
|
147
|
+
*/
|
|
148
|
+
function collectObjectDestructChildren(
|
|
149
|
+
node: TreeSitterNode,
|
|
150
|
+
rules: LanguageRules,
|
|
151
|
+
): TreeSitterNode[] {
|
|
152
|
+
const next: TreeSitterNode[] = [];
|
|
153
|
+
for (const child of node.namedChildren) {
|
|
154
|
+
if (rules.shorthandPropPattern && child.type === rules.shorthandPropPattern) {
|
|
155
|
+
// Shorthand prop is a direct identifier — handled by the shorthand
|
|
156
|
+
// guard in the `extractParamNames` worklist loop (before `resolveParamNode`).
|
|
157
|
+
next.push(child);
|
|
158
|
+
} else if (rules.pairPatternType && child.type === rules.pairPatternType) {
|
|
159
|
+
const value = child.childForFieldName('value');
|
|
160
|
+
if (value) next.push(value);
|
|
161
|
+
} else if (rules.restParamType && child.type === rules.restParamType) {
|
|
162
|
+
next.push(child);
|
|
163
|
+
}
|
|
160
164
|
}
|
|
165
|
+
return next;
|
|
166
|
+
}
|
|
161
167
|
|
|
162
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Extract parameter names from a single parameter node.
|
|
170
|
+
*
|
|
171
|
+
* Uses an iterative worklist to handle nested destructuring (objects, arrays,
|
|
172
|
+
* defaults, rest, wrappers) without mutual recursion through helper functions.
|
|
173
|
+
*/
|
|
174
|
+
export function extractParamNames(node: TreeSitterNode | null, rules: LanguageRules): string[] {
|
|
175
|
+
if (!node) return [];
|
|
176
|
+
|
|
177
|
+
const names: string[] = [];
|
|
178
|
+
const stack: TreeSitterNode[] = [node];
|
|
179
|
+
|
|
180
|
+
while (stack.length > 0) {
|
|
181
|
+
const current = stack.pop();
|
|
182
|
+
if (!current) continue;
|
|
183
|
+
|
|
184
|
+
// Shorthand identifier inside an object destructuring is just the node's text.
|
|
185
|
+
if (rules.shorthandPropPattern && current.type === rules.shorthandPropPattern) {
|
|
186
|
+
names.push(current.text);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const resolved = resolveParamNode(current, rules);
|
|
191
|
+
if (!resolved) continue;
|
|
192
|
+
if (resolved.names) names.push(...resolved.names);
|
|
193
|
+
if (resolved.next) {
|
|
194
|
+
// Push in reverse so traversal order matches the previous recursive order.
|
|
195
|
+
for (let i = resolved.next.length - 1; i >= 0; i--) {
|
|
196
|
+
const child = resolved.next[i];
|
|
197
|
+
if (child) stack.push(child);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return names;
|
|
163
203
|
}
|
|
164
204
|
|
|
165
205
|
/**
|