@aroman22/codegraph-vba-darwin-arm64 1.3.3
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/bin/codegraph +14 -0
- package/lib/dist/bin/codegraph.d.ts +26 -0
- package/lib/dist/bin/codegraph.d.ts.map +1 -0
- package/lib/dist/bin/codegraph.js +2018 -0
- package/lib/dist/bin/codegraph.js.map +1 -0
- package/lib/dist/bin/command-supervision.d.ts +12 -0
- package/lib/dist/bin/command-supervision.d.ts.map +1 -0
- package/lib/dist/bin/command-supervision.js +76 -0
- package/lib/dist/bin/command-supervision.js.map +1 -0
- package/lib/dist/bin/fatal-handler.d.ts +20 -0
- package/lib/dist/bin/fatal-handler.d.ts.map +1 -0
- package/lib/dist/bin/fatal-handler.js +118 -0
- package/lib/dist/bin/fatal-handler.js.map +1 -0
- package/lib/dist/bin/node-version-check.d.ts +37 -0
- package/lib/dist/bin/node-version-check.d.ts.map +1 -0
- package/lib/dist/bin/node-version-check.js +79 -0
- package/lib/dist/bin/node-version-check.js.map +1 -0
- package/lib/dist/bin/uninstall.d.ts +14 -0
- package/lib/dist/bin/uninstall.d.ts.map +1 -0
- package/lib/dist/bin/uninstall.js +36 -0
- package/lib/dist/bin/uninstall.js.map +1 -0
- package/lib/dist/context/formatter.d.ts +30 -0
- package/lib/dist/context/formatter.d.ts.map +1 -0
- package/lib/dist/context/formatter.js +263 -0
- package/lib/dist/context/formatter.js.map +1 -0
- package/lib/dist/context/index.d.ts +119 -0
- package/lib/dist/context/index.d.ts.map +1 -0
- package/lib/dist/context/index.js +1296 -0
- package/lib/dist/context/index.js.map +1 -0
- package/lib/dist/context/markers.d.ts +19 -0
- package/lib/dist/context/markers.d.ts.map +1 -0
- package/lib/dist/context/markers.js +22 -0
- package/lib/dist/context/markers.js.map +1 -0
- package/lib/dist/db/index.d.ts +122 -0
- package/lib/dist/db/index.d.ts.map +1 -0
- package/lib/dist/db/index.js +296 -0
- package/lib/dist/db/index.js.map +1 -0
- package/lib/dist/db/migrations.d.ts +44 -0
- package/lib/dist/db/migrations.d.ts.map +1 -0
- package/lib/dist/db/migrations.js +140 -0
- package/lib/dist/db/migrations.js.map +1 -0
- package/lib/dist/db/queries.d.ts +402 -0
- package/lib/dist/db/queries.d.ts.map +1 -0
- package/lib/dist/db/queries.js +1626 -0
- package/lib/dist/db/queries.js.map +1 -0
- package/lib/dist/db/schema.sql +152 -0
- package/lib/dist/db/sqlite-adapter.d.ts +53 -0
- package/lib/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/lib/dist/db/sqlite-adapter.js +117 -0
- package/lib/dist/db/sqlite-adapter.js.map +1 -0
- package/lib/dist/directory.d.ts +179 -0
- package/lib/dist/directory.d.ts.map +1 -0
- package/lib/dist/directory.js +625 -0
- package/lib/dist/directory.js.map +1 -0
- package/lib/dist/errors.d.ts +136 -0
- package/lib/dist/errors.d.ts.map +1 -0
- package/lib/dist/errors.js +219 -0
- package/lib/dist/errors.js.map +1 -0
- package/lib/dist/extraction/astro-extractor.d.ts +79 -0
- package/lib/dist/extraction/astro-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/astro-extractor.js +320 -0
- package/lib/dist/extraction/astro-extractor.js.map +1 -0
- package/lib/dist/extraction/dfm-extractor.d.ts +31 -0
- package/lib/dist/extraction/dfm-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/dfm-extractor.js +151 -0
- package/lib/dist/extraction/dfm-extractor.js.map +1 -0
- package/lib/dist/extraction/extraction-version.d.ts +25 -0
- package/lib/dist/extraction/extraction-version.d.ts.map +1 -0
- package/lib/dist/extraction/extraction-version.js +28 -0
- package/lib/dist/extraction/extraction-version.js.map +1 -0
- package/lib/dist/extraction/function-ref.d.ts +118 -0
- package/lib/dist/extraction/function-ref.d.ts.map +1 -0
- package/lib/dist/extraction/function-ref.js +727 -0
- package/lib/dist/extraction/function-ref.js.map +1 -0
- package/lib/dist/extraction/generated-detection.d.ts +30 -0
- package/lib/dist/extraction/generated-detection.d.ts.map +1 -0
- package/lib/dist/extraction/generated-detection.js +83 -0
- package/lib/dist/extraction/generated-detection.js.map +1 -0
- package/lib/dist/extraction/grammars.d.ts +128 -0
- package/lib/dist/extraction/grammars.d.ts.map +1 -0
- package/lib/dist/extraction/grammars.js +537 -0
- package/lib/dist/extraction/grammars.js.map +1 -0
- package/lib/dist/extraction/index.d.ts +187 -0
- package/lib/dist/extraction/index.d.ts.map +1 -0
- package/lib/dist/extraction/index.js +1951 -0
- package/lib/dist/extraction/index.js.map +1 -0
- package/lib/dist/extraction/languages/c-cpp.d.ts +12 -0
- package/lib/dist/extraction/languages/c-cpp.d.ts.map +1 -0
- package/lib/dist/extraction/languages/c-cpp.js +275 -0
- package/lib/dist/extraction/languages/c-cpp.js.map +1 -0
- package/lib/dist/extraction/languages/csharp.d.ts +25 -0
- package/lib/dist/extraction/languages/csharp.d.ts.map +1 -0
- package/lib/dist/extraction/languages/csharp.js +175 -0
- package/lib/dist/extraction/languages/csharp.js.map +1 -0
- package/lib/dist/extraction/languages/dart.d.ts +3 -0
- package/lib/dist/extraction/languages/dart.d.ts.map +1 -0
- package/lib/dist/extraction/languages/dart.js +374 -0
- package/lib/dist/extraction/languages/dart.js.map +1 -0
- package/lib/dist/extraction/languages/go.d.ts +3 -0
- package/lib/dist/extraction/languages/go.d.ts.map +1 -0
- package/lib/dist/extraction/languages/go.js +111 -0
- package/lib/dist/extraction/languages/go.js.map +1 -0
- package/lib/dist/extraction/languages/index.d.ts +10 -0
- package/lib/dist/extraction/languages/index.d.ts.map +1 -0
- package/lib/dist/extraction/languages/index.js +53 -0
- package/lib/dist/extraction/languages/index.js.map +1 -0
- package/lib/dist/extraction/languages/java.d.ts +3 -0
- package/lib/dist/extraction/languages/java.d.ts.map +1 -0
- package/lib/dist/extraction/languages/java.js +315 -0
- package/lib/dist/extraction/languages/java.js.map +1 -0
- package/lib/dist/extraction/languages/javascript.d.ts +3 -0
- package/lib/dist/extraction/languages/javascript.d.ts.map +1 -0
- package/lib/dist/extraction/languages/javascript.js +106 -0
- package/lib/dist/extraction/languages/javascript.js.map +1 -0
- package/lib/dist/extraction/languages/kotlin.d.ts +3 -0
- package/lib/dist/extraction/languages/kotlin.d.ts.map +1 -0
- package/lib/dist/extraction/languages/kotlin.js +379 -0
- package/lib/dist/extraction/languages/kotlin.js.map +1 -0
- package/lib/dist/extraction/languages/lua.d.ts +3 -0
- package/lib/dist/extraction/languages/lua.d.ts.map +1 -0
- package/lib/dist/extraction/languages/lua.js +150 -0
- package/lib/dist/extraction/languages/lua.js.map +1 -0
- package/lib/dist/extraction/languages/luau.d.ts +3 -0
- package/lib/dist/extraction/languages/luau.d.ts.map +1 -0
- package/lib/dist/extraction/languages/luau.js +37 -0
- package/lib/dist/extraction/languages/luau.js.map +1 -0
- package/lib/dist/extraction/languages/objc.d.ts +3 -0
- package/lib/dist/extraction/languages/objc.d.ts.map +1 -0
- package/lib/dist/extraction/languages/objc.js +175 -0
- package/lib/dist/extraction/languages/objc.js.map +1 -0
- package/lib/dist/extraction/languages/pascal.d.ts +3 -0
- package/lib/dist/extraction/languages/pascal.d.ts.map +1 -0
- package/lib/dist/extraction/languages/pascal.js +77 -0
- package/lib/dist/extraction/languages/pascal.js.map +1 -0
- package/lib/dist/extraction/languages/php.d.ts +3 -0
- package/lib/dist/extraction/languages/php.d.ts.map +1 -0
- package/lib/dist/extraction/languages/php.js +196 -0
- package/lib/dist/extraction/languages/php.js.map +1 -0
- package/lib/dist/extraction/languages/python.d.ts +3 -0
- package/lib/dist/extraction/languages/python.d.ts.map +1 -0
- package/lib/dist/extraction/languages/python.js +56 -0
- package/lib/dist/extraction/languages/python.js.map +1 -0
- package/lib/dist/extraction/languages/r.d.ts +3 -0
- package/lib/dist/extraction/languages/r.d.ts.map +1 -0
- package/lib/dist/extraction/languages/r.js +314 -0
- package/lib/dist/extraction/languages/r.js.map +1 -0
- package/lib/dist/extraction/languages/ruby.d.ts +3 -0
- package/lib/dist/extraction/languages/ruby.d.ts.map +1 -0
- package/lib/dist/extraction/languages/ruby.js +149 -0
- package/lib/dist/extraction/languages/ruby.js.map +1 -0
- package/lib/dist/extraction/languages/rust.d.ts +3 -0
- package/lib/dist/extraction/languages/rust.d.ts.map +1 -0
- package/lib/dist/extraction/languages/rust.js +142 -0
- package/lib/dist/extraction/languages/rust.js.map +1 -0
- package/lib/dist/extraction/languages/scala.d.ts +3 -0
- package/lib/dist/extraction/languages/scala.d.ts.map +1 -0
- package/lib/dist/extraction/languages/scala.js +209 -0
- package/lib/dist/extraction/languages/scala.js.map +1 -0
- package/lib/dist/extraction/languages/swift.d.ts +3 -0
- package/lib/dist/extraction/languages/swift.d.ts.map +1 -0
- package/lib/dist/extraction/languages/swift.js +152 -0
- package/lib/dist/extraction/languages/swift.js.map +1 -0
- package/lib/dist/extraction/languages/typescript.d.ts +16 -0
- package/lib/dist/extraction/languages/typescript.d.ts.map +1 -0
- package/lib/dist/extraction/languages/typescript.js +167 -0
- package/lib/dist/extraction/languages/typescript.js.map +1 -0
- package/lib/dist/extraction/liquid-extractor.d.ts +59 -0
- package/lib/dist/extraction/liquid-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/liquid-extractor.js +357 -0
- package/lib/dist/extraction/liquid-extractor.js.map +1 -0
- package/lib/dist/extraction/mybatis-extractor.d.ts +48 -0
- package/lib/dist/extraction/mybatis-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/mybatis-extractor.js +198 -0
- package/lib/dist/extraction/mybatis-extractor.js.map +1 -0
- package/lib/dist/extraction/parse-pool.d.ts +126 -0
- package/lib/dist/extraction/parse-pool.d.ts.map +1 -0
- package/lib/dist/extraction/parse-pool.js +319 -0
- package/lib/dist/extraction/parse-pool.js.map +1 -0
- package/lib/dist/extraction/parse-worker.d.ts +8 -0
- package/lib/dist/extraction/parse-worker.d.ts.map +1 -0
- package/lib/dist/extraction/parse-worker.js +97 -0
- package/lib/dist/extraction/parse-worker.js.map +1 -0
- package/lib/dist/extraction/razor-extractor.d.ts +42 -0
- package/lib/dist/extraction/razor-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/razor-extractor.js +285 -0
- package/lib/dist/extraction/razor-extractor.js.map +1 -0
- package/lib/dist/extraction/sql-query-extractor.d.ts +25 -0
- package/lib/dist/extraction/sql-query-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/sql-query-extractor.js +177 -0
- package/lib/dist/extraction/sql-query-extractor.js.map +1 -0
- package/lib/dist/extraction/svelte-extractor.d.ts +56 -0
- package/lib/dist/extraction/svelte-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/svelte-extractor.js +275 -0
- package/lib/dist/extraction/svelte-extractor.js.map +1 -0
- package/lib/dist/extraction/tree-sitter-helpers.d.ts +28 -0
- package/lib/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
- package/lib/dist/extraction/tree-sitter-helpers.js +152 -0
- package/lib/dist/extraction/tree-sitter-helpers.js.map +1 -0
- package/lib/dist/extraction/tree-sitter-types.d.ts +239 -0
- package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -0
- package/lib/dist/extraction/tree-sitter-types.js +10 -0
- package/lib/dist/extraction/tree-sitter-types.js.map +1 -0
- package/lib/dist/extraction/tree-sitter.d.ts +647 -0
- package/lib/dist/extraction/tree-sitter.d.ts.map +1 -0
- package/lib/dist/extraction/tree-sitter.js +5647 -0
- package/lib/dist/extraction/tree-sitter.js.map +1 -0
- package/lib/dist/extraction/vba-extractor.d.ts +394 -0
- package/lib/dist/extraction/vba-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/vba-extractor.js +1716 -0
- package/lib/dist/extraction/vba-extractor.js.map +1 -0
- package/lib/dist/extraction/vba-form-extractor.d.ts +89 -0
- package/lib/dist/extraction/vba-form-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/vba-form-extractor.js +367 -0
- package/lib/dist/extraction/vba-form-extractor.js.map +1 -0
- package/lib/dist/extraction/vba-preprocess.d.ts +81 -0
- package/lib/dist/extraction/vba-preprocess.d.ts.map +1 -0
- package/lib/dist/extraction/vba-preprocess.js +287 -0
- package/lib/dist/extraction/vba-preprocess.js.map +1 -0
- package/lib/dist/extraction/vue-extractor.d.ts +51 -0
- package/lib/dist/extraction/vue-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/vue-extractor.js +254 -0
- package/lib/dist/extraction/vue-extractor.js.map +1 -0
- package/lib/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
- package/lib/dist/extraction/wasm-runtime-flags.d.ts +38 -0
- package/lib/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
- package/lib/dist/extraction/wasm-runtime-flags.js +106 -0
- package/lib/dist/extraction/wasm-runtime-flags.js.map +1 -0
- package/lib/dist/graph/index.d.ts +8 -0
- package/lib/dist/graph/index.d.ts.map +1 -0
- package/lib/dist/graph/index.js +13 -0
- package/lib/dist/graph/index.js.map +1 -0
- package/lib/dist/graph/queries.d.ts +106 -0
- package/lib/dist/graph/queries.d.ts.map +1 -0
- package/lib/dist/graph/queries.js +339 -0
- package/lib/dist/graph/queries.js.map +1 -0
- package/lib/dist/graph/traversal.d.ts +127 -0
- package/lib/dist/graph/traversal.d.ts.map +1 -0
- package/lib/dist/graph/traversal.js +540 -0
- package/lib/dist/graph/traversal.js.map +1 -0
- package/lib/dist/index.d.ts +563 -0
- package/lib/dist/index.d.ts.map +1 -0
- package/lib/dist/index.js +1041 -0
- package/lib/dist/index.js.map +1 -0
- package/lib/dist/installer/config-writer.d.ts +28 -0
- package/lib/dist/installer/config-writer.d.ts.map +1 -0
- package/lib/dist/installer/config-writer.js +91 -0
- package/lib/dist/installer/config-writer.js.map +1 -0
- package/lib/dist/installer/index.d.ts +100 -0
- package/lib/dist/installer/index.d.ts.map +1 -0
- package/lib/dist/installer/index.js +524 -0
- package/lib/dist/installer/index.js.map +1 -0
- package/lib/dist/installer/instructions-template.d.ts +41 -0
- package/lib/dist/installer/instructions-template.d.ts.map +1 -0
- package/lib/dist/installer/instructions-template.js +53 -0
- package/lib/dist/installer/instructions-template.js.map +1 -0
- package/lib/dist/installer/targets/antigravity.d.ts +57 -0
- package/lib/dist/installer/targets/antigravity.d.ts.map +1 -0
- package/lib/dist/installer/targets/antigravity.js +308 -0
- package/lib/dist/installer/targets/antigravity.js.map +1 -0
- package/lib/dist/installer/targets/claude.d.ts +62 -0
- package/lib/dist/installer/targets/claude.d.ts.map +1 -0
- package/lib/dist/installer/targets/claude.js +454 -0
- package/lib/dist/installer/targets/claude.js.map +1 -0
- package/lib/dist/installer/targets/codex.d.ts +18 -0
- package/lib/dist/installer/targets/codex.d.ts.map +1 -0
- package/lib/dist/installer/targets/codex.js +185 -0
- package/lib/dist/installer/targets/codex.js.map +1 -0
- package/lib/dist/installer/targets/cursor.d.ts +35 -0
- package/lib/dist/installer/targets/cursor.d.ts.map +1 -0
- package/lib/dist/installer/targets/cursor.js +254 -0
- package/lib/dist/installer/targets/cursor.js.map +1 -0
- package/lib/dist/installer/targets/gemini.d.ts +26 -0
- package/lib/dist/installer/targets/gemini.d.ts.map +1 -0
- package/lib/dist/installer/targets/gemini.js +165 -0
- package/lib/dist/installer/targets/gemini.js.map +1 -0
- package/lib/dist/installer/targets/hermes.d.ts +18 -0
- package/lib/dist/installer/targets/hermes.d.ts.map +1 -0
- package/lib/dist/installer/targets/hermes.js +359 -0
- package/lib/dist/installer/targets/hermes.js.map +1 -0
- package/lib/dist/installer/targets/kiro.d.ts +27 -0
- package/lib/dist/installer/targets/kiro.d.ts.map +1 -0
- package/lib/dist/installer/targets/kiro.js +178 -0
- package/lib/dist/installer/targets/kiro.js.map +1 -0
- package/lib/dist/installer/targets/opencode.d.ts +38 -0
- package/lib/dist/installer/targets/opencode.d.ts.map +1 -0
- package/lib/dist/installer/targets/opencode.js +307 -0
- package/lib/dist/installer/targets/opencode.js.map +1 -0
- package/lib/dist/installer/targets/registry.d.ts +35 -0
- package/lib/dist/installer/targets/registry.d.ts.map +1 -0
- package/lib/dist/installer/targets/registry.js +91 -0
- package/lib/dist/installer/targets/registry.js.map +1 -0
- package/lib/dist/installer/targets/shared.d.ts +101 -0
- package/lib/dist/installer/targets/shared.d.ts.map +1 -0
- package/lib/dist/installer/targets/shared.js +264 -0
- package/lib/dist/installer/targets/shared.js.map +1 -0
- package/lib/dist/installer/targets/toml.d.ts +52 -0
- package/lib/dist/installer/targets/toml.d.ts.map +1 -0
- package/lib/dist/installer/targets/toml.js +147 -0
- package/lib/dist/installer/targets/toml.js.map +1 -0
- package/lib/dist/installer/targets/types.d.ts +108 -0
- package/lib/dist/installer/targets/types.d.ts.map +1 -0
- package/lib/dist/installer/targets/types.js +16 -0
- package/lib/dist/installer/targets/types.js.map +1 -0
- package/lib/dist/mcp/daemon-manager.d.ts +42 -0
- package/lib/dist/mcp/daemon-manager.d.ts.map +1 -0
- package/lib/dist/mcp/daemon-manager.js +129 -0
- package/lib/dist/mcp/daemon-manager.js.map +1 -0
- package/lib/dist/mcp/daemon-paths.d.ts +73 -0
- package/lib/dist/mcp/daemon-paths.d.ts.map +1 -0
- package/lib/dist/mcp/daemon-paths.js +165 -0
- package/lib/dist/mcp/daemon-paths.js.map +1 -0
- package/lib/dist/mcp/daemon-registry.d.ts +47 -0
- package/lib/dist/mcp/daemon-registry.d.ts.map +1 -0
- package/lib/dist/mcp/daemon-registry.js +233 -0
- package/lib/dist/mcp/daemon-registry.js.map +1 -0
- package/lib/dist/mcp/daemon.d.ts +258 -0
- package/lib/dist/mcp/daemon.d.ts.map +1 -0
- package/lib/dist/mcp/daemon.js +770 -0
- package/lib/dist/mcp/daemon.js.map +1 -0
- package/lib/dist/mcp/dynamic-boundaries.d.ts +41 -0
- package/lib/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
- package/lib/dist/mcp/dynamic-boundaries.js +359 -0
- package/lib/dist/mcp/dynamic-boundaries.js.map +1 -0
- package/lib/dist/mcp/engine.d.ts +122 -0
- package/lib/dist/mcp/engine.d.ts.map +1 -0
- package/lib/dist/mcp/engine.js +350 -0
- package/lib/dist/mcp/engine.js.map +1 -0
- package/lib/dist/mcp/index.d.ts +113 -0
- package/lib/dist/mcp/index.d.ts.map +1 -0
- package/lib/dist/mcp/index.js +481 -0
- package/lib/dist/mcp/index.js.map +1 -0
- package/lib/dist/mcp/liveness-watchdog.d.ts +18 -0
- package/lib/dist/mcp/liveness-watchdog.d.ts.map +1 -0
- package/lib/dist/mcp/liveness-watchdog.js +207 -0
- package/lib/dist/mcp/liveness-watchdog.js.map +1 -0
- package/lib/dist/mcp/ppid-watchdog.d.ts +62 -0
- package/lib/dist/mcp/ppid-watchdog.d.ts.map +1 -0
- package/lib/dist/mcp/ppid-watchdog.js +64 -0
- package/lib/dist/mcp/ppid-watchdog.js.map +1 -0
- package/lib/dist/mcp/proxy.d.ts +87 -0
- package/lib/dist/mcp/proxy.d.ts.map +1 -0
- package/lib/dist/mcp/proxy.js +641 -0
- package/lib/dist/mcp/proxy.js.map +1 -0
- package/lib/dist/mcp/query-pool.d.ts +94 -0
- package/lib/dist/mcp/query-pool.d.ts.map +1 -0
- package/lib/dist/mcp/query-pool.js +297 -0
- package/lib/dist/mcp/query-pool.js.map +1 -0
- package/lib/dist/mcp/query-worker.d.ts +24 -0
- package/lib/dist/mcp/query-worker.d.ts.map +1 -0
- package/lib/dist/mcp/query-worker.js +87 -0
- package/lib/dist/mcp/query-worker.js.map +1 -0
- package/lib/dist/mcp/server-instructions.d.ts +34 -0
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -0
- package/lib/dist/mcp/server-instructions.js +126 -0
- package/lib/dist/mcp/server-instructions.js.map +1 -0
- package/lib/dist/mcp/session.d.ts +79 -0
- package/lib/dist/mcp/session.d.ts.map +1 -0
- package/lib/dist/mcp/session.js +330 -0
- package/lib/dist/mcp/session.js.map +1 -0
- package/lib/dist/mcp/stdin-teardown.d.ts +27 -0
- package/lib/dist/mcp/stdin-teardown.d.ts.map +1 -0
- package/lib/dist/mcp/stdin-teardown.js +49 -0
- package/lib/dist/mcp/stdin-teardown.js.map +1 -0
- package/lib/dist/mcp/tools.d.ts +591 -0
- package/lib/dist/mcp/tools.d.ts.map +1 -0
- package/lib/dist/mcp/tools.js +4211 -0
- package/lib/dist/mcp/tools.js.map +1 -0
- package/lib/dist/mcp/transport.d.ts +188 -0
- package/lib/dist/mcp/transport.d.ts.map +1 -0
- package/lib/dist/mcp/transport.js +359 -0
- package/lib/dist/mcp/transport.js.map +1 -0
- package/lib/dist/mcp/version.d.ts +19 -0
- package/lib/dist/mcp/version.d.ts.map +1 -0
- package/lib/dist/mcp/version.js +71 -0
- package/lib/dist/mcp/version.js.map +1 -0
- package/lib/dist/project-config.d.ts +56 -0
- package/lib/dist/project-config.d.ts.map +1 -0
- package/lib/dist/project-config.js +297 -0
- package/lib/dist/project-config.js.map +1 -0
- package/lib/dist/reasoning/config.d.ts +45 -0
- package/lib/dist/reasoning/config.d.ts.map +1 -0
- package/lib/dist/reasoning/config.js +171 -0
- package/lib/dist/reasoning/config.js.map +1 -0
- package/lib/dist/reasoning/credentials.d.ts +5 -0
- package/lib/dist/reasoning/credentials.d.ts.map +1 -0
- package/lib/dist/reasoning/credentials.js +83 -0
- package/lib/dist/reasoning/credentials.js.map +1 -0
- package/lib/dist/reasoning/login.d.ts +21 -0
- package/lib/dist/reasoning/login.d.ts.map +1 -0
- package/lib/dist/reasoning/login.js +85 -0
- package/lib/dist/reasoning/login.js.map +1 -0
- package/lib/dist/reasoning/reasoner.d.ts +43 -0
- package/lib/dist/reasoning/reasoner.d.ts.map +1 -0
- package/lib/dist/reasoning/reasoner.js +308 -0
- package/lib/dist/reasoning/reasoner.js.map +1 -0
- package/lib/dist/resolution/c-fnptr-synthesizer.d.ts +5 -0
- package/lib/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -0
- package/lib/dist/resolution/c-fnptr-synthesizer.js +1038 -0
- package/lib/dist/resolution/c-fnptr-synthesizer.js.map +1 -0
- package/lib/dist/resolution/callback-synthesizer.d.ts +15 -0
- package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -0
- package/lib/dist/resolution/callback-synthesizer.js +2926 -0
- package/lib/dist/resolution/callback-synthesizer.js.map +1 -0
- package/lib/dist/resolution/frameworks/astro.d.ts +9 -0
- package/lib/dist/resolution/frameworks/astro.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/astro.js +169 -0
- package/lib/dist/resolution/frameworks/astro.js.map +1 -0
- package/lib/dist/resolution/frameworks/cargo-workspace.d.ts +18 -0
- package/lib/dist/resolution/frameworks/cargo-workspace.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/cargo-workspace.js +225 -0
- package/lib/dist/resolution/frameworks/cargo-workspace.js.map +1 -0
- package/lib/dist/resolution/frameworks/csharp.d.ts +8 -0
- package/lib/dist/resolution/frameworks/csharp.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/csharp.js +241 -0
- package/lib/dist/resolution/frameworks/csharp.js.map +1 -0
- package/lib/dist/resolution/frameworks/drupal.d.ts +51 -0
- package/lib/dist/resolution/frameworks/drupal.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/drupal.js +367 -0
- package/lib/dist/resolution/frameworks/drupal.js.map +1 -0
- package/lib/dist/resolution/frameworks/expo-modules.d.ts +3 -0
- package/lib/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/expo-modules.js +148 -0
- package/lib/dist/resolution/frameworks/expo-modules.js.map +1 -0
- package/lib/dist/resolution/frameworks/express.d.ts +8 -0
- package/lib/dist/resolution/frameworks/express.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/express.js +308 -0
- package/lib/dist/resolution/frameworks/express.js.map +1 -0
- package/lib/dist/resolution/frameworks/fabric.d.ts +3 -0
- package/lib/dist/resolution/frameworks/fabric.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/fabric.js +354 -0
- package/lib/dist/resolution/frameworks/fabric.js.map +1 -0
- package/lib/dist/resolution/frameworks/go.d.ts +8 -0
- package/lib/dist/resolution/frameworks/go.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/go.js +161 -0
- package/lib/dist/resolution/frameworks/go.js.map +1 -0
- package/lib/dist/resolution/frameworks/goframe.d.ts +41 -0
- package/lib/dist/resolution/frameworks/goframe.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/goframe.js +112 -0
- package/lib/dist/resolution/frameworks/goframe.js.map +1 -0
- package/lib/dist/resolution/frameworks/index.d.ts +50 -0
- package/lib/dist/resolution/frameworks/index.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/index.js +169 -0
- package/lib/dist/resolution/frameworks/index.js.map +1 -0
- package/lib/dist/resolution/frameworks/java.d.ts +8 -0
- package/lib/dist/resolution/frameworks/java.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/java.js +509 -0
- package/lib/dist/resolution/frameworks/java.js.map +1 -0
- package/lib/dist/resolution/frameworks/laravel.d.ts +13 -0
- package/lib/dist/resolution/frameworks/laravel.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/laravel.js +257 -0
- package/lib/dist/resolution/frameworks/laravel.js.map +1 -0
- package/lib/dist/resolution/frameworks/nestjs.d.ts +26 -0
- package/lib/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/nestjs.js +698 -0
- package/lib/dist/resolution/frameworks/nestjs.js.map +1 -0
- package/lib/dist/resolution/frameworks/play.d.ts +19 -0
- package/lib/dist/resolution/frameworks/play.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/play.js +111 -0
- package/lib/dist/resolution/frameworks/play.js.map +1 -0
- package/lib/dist/resolution/frameworks/python.d.ts +10 -0
- package/lib/dist/resolution/frameworks/python.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/python.js +400 -0
- package/lib/dist/resolution/frameworks/python.js.map +1 -0
- package/lib/dist/resolution/frameworks/react-native.d.ts +3 -0
- package/lib/dist/resolution/frameworks/react-native.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/react-native.js +410 -0
- package/lib/dist/resolution/frameworks/react-native.js.map +1 -0
- package/lib/dist/resolution/frameworks/react.d.ts +8 -0
- package/lib/dist/resolution/frameworks/react.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/react.js +334 -0
- package/lib/dist/resolution/frameworks/react.js.map +1 -0
- package/lib/dist/resolution/frameworks/ruby.d.ts +8 -0
- package/lib/dist/resolution/frameworks/ruby.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/ruby.js +302 -0
- package/lib/dist/resolution/frameworks/ruby.js.map +1 -0
- package/lib/dist/resolution/frameworks/rust.d.ts +8 -0
- package/lib/dist/resolution/frameworks/rust.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/rust.js +304 -0
- package/lib/dist/resolution/frameworks/rust.js.map +1 -0
- package/lib/dist/resolution/frameworks/svelte.d.ts +9 -0
- package/lib/dist/resolution/frameworks/svelte.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/svelte.js +253 -0
- package/lib/dist/resolution/frameworks/svelte.js.map +1 -0
- package/lib/dist/resolution/frameworks/swift-objc.d.ts +37 -0
- package/lib/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/swift-objc.js +252 -0
- package/lib/dist/resolution/frameworks/swift-objc.js.map +1 -0
- package/lib/dist/resolution/frameworks/swift.d.ts +10 -0
- package/lib/dist/resolution/frameworks/swift.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/swift.js +400 -0
- package/lib/dist/resolution/frameworks/swift.js.map +1 -0
- package/lib/dist/resolution/frameworks/vue.d.ts +9 -0
- package/lib/dist/resolution/frameworks/vue.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/vue.js +303 -0
- package/lib/dist/resolution/frameworks/vue.js.map +1 -0
- package/lib/dist/resolution/go-module.d.ts +26 -0
- package/lib/dist/resolution/go-module.d.ts.map +1 -0
- package/lib/dist/resolution/go-module.js +78 -0
- package/lib/dist/resolution/go-module.js.map +1 -0
- package/lib/dist/resolution/goframe-synthesizer.d.ts +28 -0
- package/lib/dist/resolution/goframe-synthesizer.d.ts.map +1 -0
- package/lib/dist/resolution/goframe-synthesizer.js +158 -0
- package/lib/dist/resolution/goframe-synthesizer.js.map +1 -0
- package/lib/dist/resolution/import-resolver.d.ts +78 -0
- package/lib/dist/resolution/import-resolver.d.ts.map +1 -0
- package/lib/dist/resolution/import-resolver.js +1849 -0
- package/lib/dist/resolution/import-resolver.js.map +1 -0
- package/lib/dist/resolution/index.d.ts +196 -0
- package/lib/dist/resolution/index.d.ts.map +1 -0
- package/lib/dist/resolution/index.js +1328 -0
- package/lib/dist/resolution/index.js.map +1 -0
- package/lib/dist/resolution/lru-cache.d.ts +24 -0
- package/lib/dist/resolution/lru-cache.d.ts.map +1 -0
- package/lib/dist/resolution/lru-cache.js +62 -0
- package/lib/dist/resolution/lru-cache.js.map +1 -0
- package/lib/dist/resolution/name-matcher.d.ts +93 -0
- package/lib/dist/resolution/name-matcher.d.ts.map +1 -0
- package/lib/dist/resolution/name-matcher.js +1256 -0
- package/lib/dist/resolution/name-matcher.js.map +1 -0
- package/lib/dist/resolution/path-aliases.d.ts +68 -0
- package/lib/dist/resolution/path-aliases.d.ts.map +1 -0
- package/lib/dist/resolution/path-aliases.js +238 -0
- package/lib/dist/resolution/path-aliases.js.map +1 -0
- package/lib/dist/resolution/strip-comments.d.ts +27 -0
- package/lib/dist/resolution/strip-comments.d.ts.map +1 -0
- package/lib/dist/resolution/strip-comments.js +443 -0
- package/lib/dist/resolution/strip-comments.js.map +1 -0
- package/lib/dist/resolution/swift-objc-bridge.d.ts +134 -0
- package/lib/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
- package/lib/dist/resolution/swift-objc-bridge.js +256 -0
- package/lib/dist/resolution/swift-objc-bridge.js.map +1 -0
- package/lib/dist/resolution/types.d.ts +233 -0
- package/lib/dist/resolution/types.d.ts.map +1 -0
- package/lib/dist/resolution/types.js +8 -0
- package/lib/dist/resolution/types.js.map +1 -0
- package/lib/dist/resolution/workspace-packages.d.ts +48 -0
- package/lib/dist/resolution/workspace-packages.d.ts.map +1 -0
- package/lib/dist/resolution/workspace-packages.js +208 -0
- package/lib/dist/resolution/workspace-packages.js.map +1 -0
- package/lib/dist/search/query-parser.d.ts +57 -0
- package/lib/dist/search/query-parser.d.ts.map +1 -0
- package/lib/dist/search/query-parser.js +177 -0
- package/lib/dist/search/query-parser.js.map +1 -0
- package/lib/dist/search/query-utils.d.ts +87 -0
- package/lib/dist/search/query-utils.d.ts.map +1 -0
- package/lib/dist/search/query-utils.js +449 -0
- package/lib/dist/search/query-utils.js.map +1 -0
- package/lib/dist/sync/git-hooks.d.ts +45 -0
- package/lib/dist/sync/git-hooks.d.ts.map +1 -0
- package/lib/dist/sync/git-hooks.js +225 -0
- package/lib/dist/sync/git-hooks.js.map +1 -0
- package/lib/dist/sync/index.d.ts +19 -0
- package/lib/dist/sync/index.d.ts.map +1 -0
- package/lib/dist/sync/index.js +35 -0
- package/lib/dist/sync/index.js.map +1 -0
- package/lib/dist/sync/watch-policy.d.ts +48 -0
- package/lib/dist/sync/watch-policy.d.ts.map +1 -0
- package/lib/dist/sync/watch-policy.js +124 -0
- package/lib/dist/sync/watch-policy.js.map +1 -0
- package/lib/dist/sync/watcher.d.ts +358 -0
- package/lib/dist/sync/watcher.d.ts.map +1 -0
- package/lib/dist/sync/watcher.js +829 -0
- package/lib/dist/sync/watcher.js.map +1 -0
- package/lib/dist/sync/worktree.d.ts +54 -0
- package/lib/dist/sync/worktree.d.ts.map +1 -0
- package/lib/dist/sync/worktree.js +145 -0
- package/lib/dist/sync/worktree.js.map +1 -0
- package/lib/dist/telemetry/index.d.ts +143 -0
- package/lib/dist/telemetry/index.d.ts.map +1 -0
- package/lib/dist/telemetry/index.js +541 -0
- package/lib/dist/telemetry/index.js.map +1 -0
- package/lib/dist/types.d.ts +409 -0
- package/lib/dist/types.d.ts.map +1 -0
- package/lib/dist/types.js +101 -0
- package/lib/dist/types.js.map +1 -0
- package/lib/dist/ui/glyphs.d.ts +42 -0
- package/lib/dist/ui/glyphs.d.ts.map +1 -0
- package/lib/dist/ui/glyphs.js +78 -0
- package/lib/dist/ui/glyphs.js.map +1 -0
- package/lib/dist/ui/shimmer-progress.d.ts +11 -0
- package/lib/dist/ui/shimmer-progress.d.ts.map +1 -0
- package/lib/dist/ui/shimmer-progress.js +90 -0
- package/lib/dist/ui/shimmer-progress.js.map +1 -0
- package/lib/dist/ui/shimmer-worker.d.ts +2 -0
- package/lib/dist/ui/shimmer-worker.d.ts.map +1 -0
- package/lib/dist/ui/shimmer-worker.js +118 -0
- package/lib/dist/ui/shimmer-worker.js.map +1 -0
- package/lib/dist/ui/types.d.ts +17 -0
- package/lib/dist/ui/types.d.ts.map +1 -0
- package/lib/dist/ui/types.js +3 -0
- package/lib/dist/ui/types.js.map +1 -0
- package/lib/dist/upgrade/index.d.ts +132 -0
- package/lib/dist/upgrade/index.d.ts.map +1 -0
- package/lib/dist/upgrade/index.js +576 -0
- package/lib/dist/upgrade/index.js.map +1 -0
- package/lib/dist/utils.d.ts +224 -0
- package/lib/dist/utils.d.ts.map +1 -0
- package/lib/dist/utils.js +583 -0
- package/lib/dist/utils.js.map +1 -0
- package/lib/node_modules/.modules.yaml +45 -0
- package/lib/node_modules/.pnpm/lock.yaml +126 -0
- package/lib/node_modules/.pnpm-workspace-state-v1.json +25 -0
- package/lib/node_modules/@clack/core/CHANGELOG.md +397 -0
- package/lib/node_modules/@clack/core/LICENSE +9 -0
- package/lib/node_modules/@clack/core/README.md +22 -0
- package/lib/node_modules/@clack/core/dist/index.d.mts +469 -0
- package/lib/node_modules/@clack/core/dist/index.mjs +981 -0
- package/lib/node_modules/@clack/core/package.json +64 -0
- package/lib/node_modules/@clack/prompts/CHANGELOG.md +672 -0
- package/lib/node_modules/@clack/prompts/LICENSE +9 -0
- package/lib/node_modules/@clack/prompts/README.md +396 -0
- package/lib/node_modules/@clack/prompts/dist/index.d.mts +928 -0
- package/lib/node_modules/@clack/prompts/dist/index.mjs +1398 -0
- package/lib/node_modules/@clack/prompts/package.json +68 -0
- package/lib/node_modules/commander/LICENSE +22 -0
- package/lib/node_modules/commander/Readme.md +1176 -0
- package/lib/node_modules/commander/esm.mjs +16 -0
- package/lib/node_modules/commander/index.js +24 -0
- package/lib/node_modules/commander/lib/argument.js +150 -0
- package/lib/node_modules/commander/lib/command.js +2777 -0
- package/lib/node_modules/commander/lib/error.js +39 -0
- package/lib/node_modules/commander/lib/help.js +747 -0
- package/lib/node_modules/commander/lib/option.js +380 -0
- package/lib/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/lib/node_modules/commander/package-support.json +19 -0
- package/lib/node_modules/commander/package.json +82 -0
- package/lib/node_modules/commander/typings/esm.d.mts +3 -0
- package/lib/node_modules/commander/typings/index.d.ts +1113 -0
- package/lib/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
- package/lib/node_modules/fast-string-truncated-width/dist/index.js +111 -0
- package/lib/node_modules/fast-string-truncated-width/dist/types.d.ts +19 -0
- package/lib/node_modules/fast-string-truncated-width/dist/types.js +2 -0
- package/lib/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
- package/lib/node_modules/fast-string-truncated-width/dist/utils.js +20 -0
- package/lib/node_modules/fast-string-truncated-width/license +21 -0
- package/lib/node_modules/fast-string-truncated-width/package.json +35 -0
- package/lib/node_modules/fast-string-truncated-width/readme.md +59 -0
- package/lib/node_modules/fast-string-width/dist/index.d.ts +4 -0
- package/lib/node_modules/fast-string-width/dist/index.js +14 -0
- package/lib/node_modules/fast-string-width/license +21 -0
- package/lib/node_modules/fast-string-width/package.json +34 -0
- package/lib/node_modules/fast-string-width/readme.md +42 -0
- package/lib/node_modules/fast-wrap-ansi/LICENSE +23 -0
- package/lib/node_modules/fast-wrap-ansi/README.md +26 -0
- package/lib/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
- package/lib/node_modules/fast-wrap-ansi/lib/main.js +218 -0
- package/lib/node_modules/fast-wrap-ansi/package.json +51 -0
- package/lib/node_modules/ignore/LICENSE-MIT +21 -0
- package/lib/node_modules/ignore/README.md +452 -0
- package/lib/node_modules/ignore/index.d.ts +81 -0
- package/lib/node_modules/ignore/index.js +784 -0
- package/lib/node_modules/ignore/legacy.js +681 -0
- package/lib/node_modules/ignore/package.json +87 -0
- package/lib/node_modules/jsonc-parser/CHANGELOG.md +76 -0
- package/lib/node_modules/jsonc-parser/LICENSE.md +21 -0
- package/lib/node_modules/jsonc-parser/README.md +364 -0
- package/lib/node_modules/jsonc-parser/SECURITY.md +41 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/edit.js +185 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/format.js +261 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/parser.js +659 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/scanner.js +443 -0
- package/lib/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +29 -0
- package/lib/node_modules/jsonc-parser/lib/esm/main.d.ts +351 -0
- package/lib/node_modules/jsonc-parser/lib/esm/main.js +178 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/edit.js +201 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/format.js +275 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/parser.js +682 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/scanner.js +456 -0
- package/lib/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +42 -0
- package/lib/node_modules/jsonc-parser/lib/umd/main.d.ts +351 -0
- package/lib/node_modules/jsonc-parser/lib/umd/main.js +194 -0
- package/lib/node_modules/jsonc-parser/package.json +37 -0
- package/lib/node_modules/picomatch/LICENSE +21 -0
- package/lib/node_modules/picomatch/README.md +749 -0
- package/lib/node_modules/picomatch/index.js +17 -0
- package/lib/node_modules/picomatch/lib/constants.js +184 -0
- package/lib/node_modules/picomatch/lib/parse.js +1386 -0
- package/lib/node_modules/picomatch/lib/picomatch.js +349 -0
- package/lib/node_modules/picomatch/lib/scan.js +391 -0
- package/lib/node_modules/picomatch/lib/utils.js +72 -0
- package/lib/node_modules/picomatch/package.json +82 -0
- package/lib/node_modules/picomatch/posix.js +3 -0
- package/lib/node_modules/sisteransi/license +21 -0
- package/lib/node_modules/sisteransi/package.json +34 -0
- package/lib/node_modules/sisteransi/readme.md +113 -0
- package/lib/node_modules/sisteransi/src/index.js +58 -0
- package/lib/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
- package/lib/node_modules/tree-sitter-wasms/LICENSE +24 -0
- package/lib/node_modules/tree-sitter-wasms/README.md +23 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-bash.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-c.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-c_sharp.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-cpp.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-css.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-dart.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-elisp.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-elixir.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-elm.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-embedded_template.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-go.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-html.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-java.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-javascript.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-json.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-kotlin.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-lua.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-objc.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-ocaml.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-php.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-python.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-ql.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-rescript.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-ruby.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-rust.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-scala.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-solidity.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-swift.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-systemrdl.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-tlaplus.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-toml.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-tsx.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-typescript.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-vue.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-yaml.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/out/tree-sitter-zig.wasm +0 -0
- package/lib/node_modules/tree-sitter-wasms/package.json +64 -0
- package/lib/node_modules/web-tree-sitter/LICENSE +21 -0
- package/lib/node_modules/web-tree-sitter/README.md +269 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.cjs +4558 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.cjs.map +7 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.js +4516 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.js.map +7 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.wasm +0 -0
- package/lib/node_modules/web-tree-sitter/debug/tree-sitter.wasm.map +31 -0
- package/lib/node_modules/web-tree-sitter/lib/alloc.c +48 -0
- package/lib/node_modules/web-tree-sitter/lib/alloc.h +41 -0
- package/lib/node_modules/web-tree-sitter/lib/array.h +291 -0
- package/lib/node_modules/web-tree-sitter/lib/atomic.h +68 -0
- package/lib/node_modules/web-tree-sitter/lib/clock.h +146 -0
- package/lib/node_modules/web-tree-sitter/lib/error_costs.h +11 -0
- package/lib/node_modules/web-tree-sitter/lib/get_changed_ranges.c +523 -0
- package/lib/node_modules/web-tree-sitter/lib/get_changed_ranges.h +36 -0
- package/lib/node_modules/web-tree-sitter/lib/host.h +21 -0
- package/lib/node_modules/web-tree-sitter/lib/language.c +293 -0
- package/lib/node_modules/web-tree-sitter/lib/language.h +293 -0
- package/lib/node_modules/web-tree-sitter/lib/length.h +52 -0
- package/lib/node_modules/web-tree-sitter/lib/lexer.c +483 -0
- package/lib/node_modules/web-tree-sitter/lib/lexer.h +54 -0
- package/lib/node_modules/web-tree-sitter/lib/lib.c +12 -0
- package/lib/node_modules/web-tree-sitter/lib/node.c +875 -0
- package/lib/node_modules/web-tree-sitter/lib/parser.c +2297 -0
- package/lib/node_modules/web-tree-sitter/lib/parser.h +286 -0
- package/lib/node_modules/web-tree-sitter/lib/point.h +48 -0
- package/lib/node_modules/web-tree-sitter/lib/query.c +4347 -0
- package/lib/node_modules/web-tree-sitter/lib/reduce_action.h +34 -0
- package/lib/node_modules/web-tree-sitter/lib/reusable_node.h +95 -0
- package/lib/node_modules/web-tree-sitter/lib/stack.c +912 -0
- package/lib/node_modules/web-tree-sitter/lib/stack.h +133 -0
- package/lib/node_modules/web-tree-sitter/lib/subtree.c +1034 -0
- package/lib/node_modules/web-tree-sitter/lib/subtree.h +399 -0
- package/lib/node_modules/web-tree-sitter/lib/tree-sitter.c +987 -0
- package/lib/node_modules/web-tree-sitter/lib/tree-sitter.cjs +2988 -0
- package/lib/node_modules/web-tree-sitter/lib/tree-sitter.wasm +0 -0
- package/lib/node_modules/web-tree-sitter/lib/tree-sitter.wasm.map +1 -0
- package/lib/node_modules/web-tree-sitter/lib/tree.c +170 -0
- package/lib/node_modules/web-tree-sitter/lib/tree.h +31 -0
- package/lib/node_modules/web-tree-sitter/lib/tree_cursor.c +716 -0
- package/lib/node_modules/web-tree-sitter/lib/tree_cursor.h +48 -0
- package/lib/node_modules/web-tree-sitter/lib/ts_assert.h +11 -0
- package/lib/node_modules/web-tree-sitter/lib/unicode.h +75 -0
- package/lib/node_modules/web-tree-sitter/lib/wasm_store.c +1937 -0
- package/lib/node_modules/web-tree-sitter/lib/wasm_store.h +31 -0
- package/lib/node_modules/web-tree-sitter/package.json +98 -0
- package/lib/node_modules/web-tree-sitter/src/bindings.ts +26 -0
- package/lib/node_modules/web-tree-sitter/src/constants.ts +133 -0
- package/lib/node_modules/web-tree-sitter/src/index.ts +31 -0
- package/lib/node_modules/web-tree-sitter/src/language.ts +291 -0
- package/lib/node_modules/web-tree-sitter/src/lookahead_iterator.ts +75 -0
- package/lib/node_modules/web-tree-sitter/src/marshal.ts +176 -0
- package/lib/node_modules/web-tree-sitter/src/node.ts +646 -0
- package/lib/node_modules/web-tree-sitter/src/parser.ts +325 -0
- package/lib/node_modules/web-tree-sitter/src/query.ts +973 -0
- package/lib/node_modules/web-tree-sitter/src/tree.ts +145 -0
- package/lib/node_modules/web-tree-sitter/src/tree_cursor.ts +318 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.cjs +4031 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.cjs.map +7 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.js +3980 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.js.map +7 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.wasm +0 -0
- package/lib/node_modules/web-tree-sitter/tree-sitter.wasm.map +30 -0
- package/lib/node_modules/web-tree-sitter/web-tree-sitter.d.ts +1030 -0
- package/lib/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +56 -0
- package/lib/package.json +57 -0
- package/node +0 -0
- package/package.json +17 -0
|
@@ -0,0 +1,1716 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.VbaExtractor = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* VbaExtractor — regex extractor for `.bas` / `.cls` / `.frm` / `.dsr` source.
|
|
39
|
+
*
|
|
40
|
+
* Emits the symbol + edge shape expected by `codegraph_explore`:
|
|
41
|
+
* - one `module` node per `.bas` / one `class` node per `.cls` / one `file`
|
|
42
|
+
* stub for `.frm` / `.dsr` legacy
|
|
43
|
+
* - one `function` node per `Sub` / `Function` / `Property Get|Let|Set`
|
|
44
|
+
* declaration, with `node.visibility` set to the canonical lowercase enum
|
|
45
|
+
* (`'public'` / `'private'`; `Friend`, `Static`, and missing visibility fold
|
|
46
|
+
* to `'public'`).
|
|
47
|
+
* - `contains` edges from the module/class node to each function node.
|
|
48
|
+
* - `calls` edges from a procedure to a same-file procedure (`Sub Outer()` →
|
|
49
|
+
* `Inner()`); qualified cross-module calls carry `provenance: 'heuristic'`
|
|
50
|
+
* and `metadata.synthesizedBy: 'vba-name-resolution'`.
|
|
51
|
+
* - `implements` edges for `Implements IFoo` declarations.
|
|
52
|
+
* - `references` edges for `Dim x As Foo.Bar` (qualified Dim → outer type)
|
|
53
|
+
* tagged `synthesizedBy: 'vba-name-resolution'`.
|
|
54
|
+
* - `references` edges for `WithEvents m_X As Form_Foo` tagged
|
|
55
|
+
* `synthesizedBy: 'vba-withevents'`.
|
|
56
|
+
* - `references` edges for SQL table names found inside string literals passed
|
|
57
|
+
* directly to SQL wrappers, or assigned to variables before
|
|
58
|
+
* `getdb().OpenRecordset(...)` / `getdb().Execute ...`, tagged
|
|
59
|
+
* `synthesizedBy: 'vba-sql-table'`.
|
|
60
|
+
*
|
|
61
|
+
* Rejects `.form.txt` / `.report.txt` (REQ-CODE-9 — the form UI extractor
|
|
62
|
+
* owns those). `Option ...` directives are inert — `stripVbaComments()`
|
|
63
|
+
* drops them.
|
|
64
|
+
*/
|
|
65
|
+
const path = __importStar(require("path"));
|
|
66
|
+
const tree_sitter_helpers_1 = require("./tree-sitter-helpers");
|
|
67
|
+
const vba_preprocess_1 = require("./vba-preprocess");
|
|
68
|
+
class VbaExtractor {
|
|
69
|
+
filePath;
|
|
70
|
+
source;
|
|
71
|
+
nodes = [];
|
|
72
|
+
edges = [];
|
|
73
|
+
errors = [];
|
|
74
|
+
unresolvedReferences = [];
|
|
75
|
+
moduleOrClassNode = null;
|
|
76
|
+
/**
|
|
77
|
+
* Class-name prefix for `qualifiedName` composition.
|
|
78
|
+
*
|
|
79
|
+
* B3 (hueco 5): for `.cls` files every function node's `qualifiedName`
|
|
80
|
+
* is composed as `${className}.${procName}` (e.g. `Form_TestForm.Form_Load`)
|
|
81
|
+
* so cross-class callers can disambiguate which form each `Form_Load`
|
|
82
|
+
* belongs to. For `.bas` files this is `null` — module-scoped procs
|
|
83
|
+
* keep their bare-name `qualifiedName` (e.g. `DoThing`), preserving
|
|
84
|
+
* the pre-hueco-5 behavior and every existing qualifiedName-based
|
|
85
|
+
* assertion in `extraction-vba.test.ts`.
|
|
86
|
+
*
|
|
87
|
+
* Resolved once per `extract()` from `isCls` + `vbName` (both already
|
|
88
|
+
* computed for `createModuleOrClassNode`); `sweepProcedures` reads it
|
|
89
|
+
* to set `ProcInfo.qualifiedName` and the function node's qualifiedName.
|
|
90
|
+
*/
|
|
91
|
+
classNamePrefix = null;
|
|
92
|
+
/**
|
|
93
|
+
* Map of procedure name (within this file) → list of ProcInfo. The list
|
|
94
|
+
* (rather than a single value) is needed because VBA allows multiple
|
|
95
|
+
* Property accessors with the same name: `Property Get Foo`, `Property
|
|
96
|
+
* Let Foo`, and `Property Set Foo` all share the name `Foo`. Audit W6
|
|
97
|
+
* (June 2026): the previous Map<string, ProcInfo> kept only the last
|
|
98
|
+
* accessor, breaking same-file call resolution and emitting the wrong
|
|
99
|
+
* `findFunctionNodeByName` match.
|
|
100
|
+
*/
|
|
101
|
+
localProcs = new Map();
|
|
102
|
+
/**
|
|
103
|
+
* Cache: procedure name → first matching function node emitted for that
|
|
104
|
+
* name. Audit S2 (June 2026): the previous implementation did an O(n)
|
|
105
|
+
* linear scan of all nodes for every call site; with W6's multimap
|
|
106
|
+
* the same-name collision can return multiple matches but bare-name
|
|
107
|
+
* call resolution only needs the first.
|
|
108
|
+
*/
|
|
109
|
+
functionNodeByName = new Map();
|
|
110
|
+
/**
|
|
111
|
+
* Cache: startLine → function node emitted for that line. Used by Fix 1
|
|
112
|
+
* (June 2026): when Property Get/Let/Set share a name, we need to find
|
|
113
|
+
* the SPECIFIC accessor's node by its declaration line rather than relying
|
|
114
|
+
* on `functionNodeByName` which only stores the first accessor.
|
|
115
|
+
*/
|
|
116
|
+
functionNodeByStartLine = new Map();
|
|
117
|
+
constructor(filePath, source) {
|
|
118
|
+
this.filePath = filePath;
|
|
119
|
+
this.source = source;
|
|
120
|
+
}
|
|
121
|
+
extract() {
|
|
122
|
+
const startTime = Date.now();
|
|
123
|
+
try {
|
|
124
|
+
// REQ-CODE-9: hand form/report files off to VbaFormExtractor. If
|
|
125
|
+
// somehow routed here, emit just a file node and return.
|
|
126
|
+
if (this.isFormOrReportFile()) {
|
|
127
|
+
this.nodes.push(this.createFileNode());
|
|
128
|
+
return this.result(startTime);
|
|
129
|
+
}
|
|
130
|
+
// Pre-process pipeline (per design): join continuations, strip comments,
|
|
131
|
+
// then we sweep the joined-but-uncommented source for call sites and
|
|
132
|
+
// SQL strings. Comments are gone before regex runs, so no commented
|
|
133
|
+
// SQL can match the SQL regex.
|
|
134
|
+
const joined = (0, vba_preprocess_1.joinLineContinuations)(this.source);
|
|
135
|
+
const uncommented = (0, vba_preprocess_1.stripVbaComments)(joined);
|
|
136
|
+
// Create the file node.
|
|
137
|
+
this.nodes.push(this.createFileNode());
|
|
138
|
+
// Resolve the file's "kind" — .cls → class, else module (including .bas
|
|
139
|
+
// and the legacy .frm/.dsr stubs).
|
|
140
|
+
const isCls = this.filePath.toLowerCase().endsWith('.cls');
|
|
141
|
+
// Detect VB_Name attribute (first non-empty line).
|
|
142
|
+
const vbName = this.detectVbName(this.source);
|
|
143
|
+
// B3 (hueco 5): compute the class-name prefix used to compose every
|
|
144
|
+
// `function` node's `qualifiedName` in this file. For `.cls` files
|
|
145
|
+
// the prefix is the resolved VB_Name (or the file basename when
|
|
146
|
+
// VB_Name is absent), producing qualifiedNames like
|
|
147
|
+
// `Form_TestForm.Form_Load`. For `.bas` (and `.frm`/`.dsr`) files
|
|
148
|
+
// the prefix is null, preserving the bare-name qualifiedName that
|
|
149
|
+
// every existing test asserts.
|
|
150
|
+
const fallbackName = path
|
|
151
|
+
.basename(this.filePath)
|
|
152
|
+
.replace(/\.[^.]+$/, '');
|
|
153
|
+
const className = isCls ? (vbName ?? fallbackName) : null;
|
|
154
|
+
this.classNamePrefix = className;
|
|
155
|
+
// Module/class node — created lazily so a file containing only
|
|
156
|
+
// Option directives (REQ-CODE-10: "emits nothing") doesn't carry a
|
|
157
|
+
// module node with no symbols attached.
|
|
158
|
+
let hasAnySymbols = false;
|
|
159
|
+
// Sweep: procedures → function nodes + contains edges.
|
|
160
|
+
const procs = this.sweepProcedures(uncommented);
|
|
161
|
+
if (procs.length > 0)
|
|
162
|
+
hasAnySymbols = true;
|
|
163
|
+
// Sweep: Implements (REQ-CODE-5).
|
|
164
|
+
const implCount = this.sweepImplements(uncommented);
|
|
165
|
+
if (implCount > 0)
|
|
166
|
+
hasAnySymbols = true;
|
|
167
|
+
// Sweep: qualified Dim (REQ-CODE-6) and WithEvents (REQ-CODE-7).
|
|
168
|
+
const dimCount = this.sweepDimsAndWithEvents(uncommented);
|
|
169
|
+
if (dimCount > 0)
|
|
170
|
+
hasAnySymbols = true;
|
|
171
|
+
// Sweep: Enum/Const declarations (REQ-CODE-12, REQ-CODE-13). Dysflow
|
|
172
|
+
// exports the full module text, so a constants module carries its
|
|
173
|
+
// `Const` lines and `Enum ... End Enum` blocks verbatim — these are the
|
|
174
|
+
// project's domain dictionary and must be in the graph.
|
|
175
|
+
const enumConstCount = this.sweepEnumsAndConsts(uncommented);
|
|
176
|
+
if (enumConstCount > 0)
|
|
177
|
+
hasAnySymbols = true;
|
|
178
|
+
// Sweep: call sites (REQ-CODE-4) and SQL-in-strings (REQ-CODE-8).
|
|
179
|
+
// Both walk the same per-line view; combining them in one pass is
|
|
180
|
+
// simpler and keeps line tracking consistent.
|
|
181
|
+
this.sweepCallsAndSql(uncommented);
|
|
182
|
+
// Create the module/class node ONLY when the file has actual symbols
|
|
183
|
+
// (REQ-CODE-10 — a file with ONLY Option directives emits zero symbol
|
|
184
|
+
// nodes. A file with Enum/Const but no procedures DOES emit a module
|
|
185
|
+
// node, since those are real symbols — see REQ-CODE-12/13).
|
|
186
|
+
if (hasAnySymbols) {
|
|
187
|
+
this.moduleOrClassNode = this.createModuleOrClassNode(isCls, vbName);
|
|
188
|
+
this.nodes.push(this.moduleOrClassNode);
|
|
189
|
+
// Re-attribute contains/implements/reference edges whose source was
|
|
190
|
+
// held in `pendingModuleOrClassSource` — see below. Since we now
|
|
191
|
+
// know the module id, redirect those edges to it.
|
|
192
|
+
for (const edge of this.pendingModuleOrClassSource) {
|
|
193
|
+
edge.source = this.moduleOrClassNode.id;
|
|
194
|
+
}
|
|
195
|
+
this.pendingModuleOrClassSource.length = 0;
|
|
196
|
+
// Sub New marker on the class node (REQ-CODE-3).
|
|
197
|
+
if (isCls) {
|
|
198
|
+
const hasNew = procs.some((p) => p.name === 'New');
|
|
199
|
+
if (hasNew) {
|
|
200
|
+
const md = (this.moduleOrClassNode.metadata ?? {});
|
|
201
|
+
md.hasClassInitializer = true;
|
|
202
|
+
md.initializerName = 'New';
|
|
203
|
+
this.moduleOrClassNode.metadata = md;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
this.errors.push({
|
|
210
|
+
message: `VBA extraction error: ${error instanceof Error ? error.message : String(error)}`,
|
|
211
|
+
filePath: this.filePath,
|
|
212
|
+
severity: 'error',
|
|
213
|
+
code: 'parse_error',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return this.result(startTime);
|
|
217
|
+
}
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// Helpers
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
result(startTime) {
|
|
222
|
+
return {
|
|
223
|
+
nodes: this.nodes,
|
|
224
|
+
edges: this.edges,
|
|
225
|
+
unresolvedReferences: this.unresolvedReferences,
|
|
226
|
+
errors: this.errors,
|
|
227
|
+
durationMs: Date.now() - startTime,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
isFormOrReportFile() {
|
|
231
|
+
const lower = this.filePath.toLowerCase();
|
|
232
|
+
return lower.endsWith('.form.txt') || lower.endsWith('.report.txt');
|
|
233
|
+
}
|
|
234
|
+
createFileNode() {
|
|
235
|
+
const lines = this.source.split('\n');
|
|
236
|
+
return {
|
|
237
|
+
id: (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'file', this.filePath, 1),
|
|
238
|
+
kind: 'file',
|
|
239
|
+
name: path.basename(this.filePath),
|
|
240
|
+
qualifiedName: this.filePath,
|
|
241
|
+
filePath: this.filePath,
|
|
242
|
+
language: 'vba',
|
|
243
|
+
startLine: 1,
|
|
244
|
+
endLine: lines.length,
|
|
245
|
+
startColumn: 0,
|
|
246
|
+
endColumn: lines[lines.length - 1]?.length ?? 0,
|
|
247
|
+
updatedAt: Date.now(),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
detectVbName(src) {
|
|
251
|
+
// Walk past the Access class metadata header block (VERSION / BEGIN /
|
|
252
|
+
// MultiUse / END / Attribute …) so VB_Name on a later line is found.
|
|
253
|
+
// The previous implementation returned null at the first non-Attribute
|
|
254
|
+
// line, which meant real Access .cls files — which start with
|
|
255
|
+
// `VERSION 1.0 CLASS / BEGIN / MultiUse = ... / END` before
|
|
256
|
+
// `Attribute VB_Name = "..."` — always fell through to the basename
|
|
257
|
+
// fallback. Audit W2 (June 2026).
|
|
258
|
+
for (const line of src.split(/\r?\n/)) {
|
|
259
|
+
const trimmed = line.trim();
|
|
260
|
+
if (!trimmed)
|
|
261
|
+
continue;
|
|
262
|
+
const m = /^\s*Attribute\s+VB_Name\s*=\s*"([^"]+)"/i.exec(trimmed);
|
|
263
|
+
if (m)
|
|
264
|
+
return m[1] ?? null;
|
|
265
|
+
// Skip known header keywords so we keep scanning.
|
|
266
|
+
if (/^\s*VERSION\b/i.test(trimmed) ||
|
|
267
|
+
/^\s*BEGIN\b/i.test(trimmed) ||
|
|
268
|
+
/^\s*END\b/i.test(trimmed) ||
|
|
269
|
+
/^\s*(?:MultiUse|Persistable|DataBindingBehavior|DataSourceBehavior)\s*=/i.test(trimmed) ||
|
|
270
|
+
/^\s*Attribute\s+/i.test(trimmed)) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
// Any other non-empty line that isn't a known header — stop.
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
createModuleOrClassNode(isCls, vbName) {
|
|
279
|
+
const fallbackName = path.basename(this.filePath).replace(/\.[^.]+$/, '');
|
|
280
|
+
const name = vbName ?? fallbackName;
|
|
281
|
+
return {
|
|
282
|
+
id: (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, isCls ? 'class' : 'module', name, 1),
|
|
283
|
+
kind: isCls ? 'class' : 'module',
|
|
284
|
+
name,
|
|
285
|
+
qualifiedName: name,
|
|
286
|
+
filePath: this.filePath,
|
|
287
|
+
language: 'vba',
|
|
288
|
+
startLine: 1,
|
|
289
|
+
endLine: this.source.split('\n').length,
|
|
290
|
+
startColumn: 0,
|
|
291
|
+
endColumn: 0,
|
|
292
|
+
metadata: {},
|
|
293
|
+
updatedAt: Date.now(),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/** Sub/Function/Property regex — captures visibility prefix, kind, and name. */
|
|
297
|
+
static PROC_RE = /^\s*((?:Public|Private|Friend|Static)\s+)?(?:Static\s+)?(Sub|Function|Property(?:\s+(?:Get|Let|Set))?)\s+(\p{L}[\p{L}\p{N}_]*)/iu;
|
|
298
|
+
/**
|
|
299
|
+
* Walk the (uncommented, line-joined) source and emit one `function` node
|
|
300
|
+
* per `Sub` / `Function` / `Property` declaration. Also records the proc
|
|
301
|
+
* in `localProcs` so call-site resolution can distinguish same-file from
|
|
302
|
+
* cross-module calls.
|
|
303
|
+
*/
|
|
304
|
+
sweepProcedures(src) {
|
|
305
|
+
const procs = [];
|
|
306
|
+
const lines = src.split('\n');
|
|
307
|
+
for (let i = 0; i < lines.length; i++) {
|
|
308
|
+
const line = lines[i] ?? '';
|
|
309
|
+
const m = VbaExtractor.PROC_RE.exec(line);
|
|
310
|
+
if (!m)
|
|
311
|
+
continue;
|
|
312
|
+
const visibilityRaw = (m[1] ?? '').trim();
|
|
313
|
+
const kindRaw = (m[2] ?? '').trim().toLowerCase();
|
|
314
|
+
const name = m[3] ?? '';
|
|
315
|
+
if (!name)
|
|
316
|
+
continue;
|
|
317
|
+
const lineNum = i + 1;
|
|
318
|
+
// Normalize the visibility. VBA's "Static" keyword is a storage
|
|
319
|
+
// specifier, not visibility, so we treat bare-static declarations as
|
|
320
|
+
// 'Public' (the default) per spec R1. "Friend" is not in the Node
|
|
321
|
+
// visibility enum; treat it as 'Public' since it's the closest
|
|
322
|
+
// broader-than-private modifier.
|
|
323
|
+
let visibility;
|
|
324
|
+
switch (visibilityRaw.toLowerCase()) {
|
|
325
|
+
case 'private':
|
|
326
|
+
visibility = 'private';
|
|
327
|
+
break;
|
|
328
|
+
case 'public':
|
|
329
|
+
case 'static':
|
|
330
|
+
case 'friend':
|
|
331
|
+
case '':
|
|
332
|
+
default:
|
|
333
|
+
visibility = 'public';
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
const kind = kindRaw.startsWith('sub')
|
|
337
|
+
? 'sub'
|
|
338
|
+
: kindRaw.startsWith('function')
|
|
339
|
+
? 'function'
|
|
340
|
+
: 'property';
|
|
341
|
+
const proc = {
|
|
342
|
+
name,
|
|
343
|
+
// B3 (hueco 5): when this file is a `.cls`, prefix the
|
|
344
|
+
// qualifiedName with the resolved class name so cross-class
|
|
345
|
+
// queries (e.g. `Form_Load`) match only the owning class.
|
|
346
|
+
// `.bas` files leave `qualifiedName === name`.
|
|
347
|
+
qualifiedName: this.classNamePrefix
|
|
348
|
+
? `${this.classNamePrefix}.${name}`
|
|
349
|
+
: name,
|
|
350
|
+
kind,
|
|
351
|
+
visibility,
|
|
352
|
+
startLine: lineNum,
|
|
353
|
+
};
|
|
354
|
+
procs.push(proc);
|
|
355
|
+
const bucket = this.localProcs.get(name);
|
|
356
|
+
if (bucket)
|
|
357
|
+
bucket.push(proc);
|
|
358
|
+
else
|
|
359
|
+
this.localProcs.set(name, [proc]);
|
|
360
|
+
const nodeId = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'function', name, lineNum);
|
|
361
|
+
const fnNode = {
|
|
362
|
+
id: nodeId,
|
|
363
|
+
kind: 'function',
|
|
364
|
+
name,
|
|
365
|
+
// B3 (hueco 5): same prefix rule as the ProcInfo above — class
|
|
366
|
+
// methods get `${className}.${name}`, module-level Subs keep
|
|
367
|
+
// their bare name.
|
|
368
|
+
qualifiedName: this.classNamePrefix
|
|
369
|
+
? `${this.classNamePrefix}.${name}`
|
|
370
|
+
: name,
|
|
371
|
+
filePath: this.filePath,
|
|
372
|
+
language: 'vba',
|
|
373
|
+
startLine: lineNum,
|
|
374
|
+
endLine: lineNum,
|
|
375
|
+
startColumn: 0,
|
|
376
|
+
endColumn: line.length,
|
|
377
|
+
visibility,
|
|
378
|
+
updatedAt: Date.now(),
|
|
379
|
+
};
|
|
380
|
+
this.nodes.push(fnNode);
|
|
381
|
+
// Cache the first node emitted for this name — `findFunctionNodeByName`
|
|
382
|
+
// (audit S2) becomes O(1) instead of O(n) per call site.
|
|
383
|
+
if (!this.functionNodeByName.has(name)) {
|
|
384
|
+
this.functionNodeByName.set(name, fnNode);
|
|
385
|
+
}
|
|
386
|
+
// Fix 1: also index by startLine so Property Get/Let/Set with the same
|
|
387
|
+
// name can each be found by their exact declaration line.
|
|
388
|
+
this.functionNodeByStartLine.set(lineNum, fnNode);
|
|
389
|
+
// Hueco 3: synthesize the event-handler edge for Access naming
|
|
390
|
+
// convention `<ControlName>_<EventName>` (e.g. `ComandoAltaPM_Click`,
|
|
391
|
+
// `MotivoBorrado_AfterUpdate`). The `<X>_<Y>` shape is split on the
|
|
392
|
+
// LAST underscore so multi-word events like `BeforeDelConfirm` parse
|
|
393
|
+
// correctly. Form-level events (`Form_Load`, `Form_Open`,
|
|
394
|
+
// `Form_Unload`, …) are NOT control handlers — they fire on the
|
|
395
|
+
// form object itself, not on a control — so they are skipped here
|
|
396
|
+
// (the form module node is the conceptual source for those).
|
|
397
|
+
//
|
|
398
|
+
// Scope guard: only emit when this .cls looks like a form code-
|
|
399
|
+
// behind (`Form_*.cls`). Without this guard, regular service classes
|
|
400
|
+
// whose methods happen to have underscores in their names
|
|
401
|
+
// (e.g. `InformeRiesgoPDFServicio.cls` declares
|
|
402
|
+
// `GenerarHTML_Principal`, `GetEstilosCSS_PDF`,
|
|
403
|
+
// `Class_Initialize`) would synthesize ~550 spurious
|
|
404
|
+
// `form-instance-control` stubs in real Dysflow projects. The
|
|
405
|
+
// `Form_` prefix is the canonical Access code-behind naming
|
|
406
|
+
// convention and matches the .form.txt siblings' basename.
|
|
407
|
+
//
|
|
408
|
+
// Cross-file synthesis caveat: the edge's source is a
|
|
409
|
+
// `form-instance-control` node that lives in the sibling .form.txt,
|
|
410
|
+
// not this .cls file. Two consequences:
|
|
411
|
+
// 1. We also emit a STUB form-instance-control node locally with
|
|
412
|
+
// the deterministic id so the per-file edge filter
|
|
413
|
+
// (`insertedIds.has(source)`) accepts the edge. When the
|
|
414
|
+
// sibling .form.txt is later indexed, VbaFormExtractor emits
|
|
415
|
+
// the real form-instance-control node with the same id; the
|
|
416
|
+
// `INSERT OR REPLACE` semantics in queries.ts:insertNode
|
|
417
|
+
// overwrite the stub with the real one (preserving the
|
|
418
|
+
// metadata.controlType, filePath, line range, etc.).
|
|
419
|
+
// 2. When the .form.txt is processed FIRST (alphabetically
|
|
420
|
+
// unlikely but possible), the real node exists in the DB
|
|
421
|
+
// before this edge is committed; insertEdges's DB-level
|
|
422
|
+
// endpoint check passes the edge naturally without the stub.
|
|
423
|
+
// Either order converges on the same final state.
|
|
424
|
+
// See vba-form-extractor.ts:findControlName for the matching real
|
|
425
|
+
// form-instance-control node emission.
|
|
426
|
+
const handler = parseEventHandlerName(name);
|
|
427
|
+
const isFormCodeBehind = /Form_[^/\\]*\.cls$/i.test(this.filePath);
|
|
428
|
+
if (handler && isFormCodeBehind) {
|
|
429
|
+
const formFilePath = this.filePath.replace(/\.cls$/i, '.form.txt');
|
|
430
|
+
const controlNodeId = (0, tree_sitter_helpers_1.generateNodeId)(formFilePath, 'form-instance-control', handler.controlName, 0);
|
|
431
|
+
// Stub form-instance-control: local so the per-file edge filter
|
|
432
|
+
// passes the event-handler edge. Overwritten by the real node
|
|
433
|
+
// emitted from the sibling .form.txt at index time (same id, same
|
|
434
|
+
// schema, INSERT OR REPLACE). No metadata.controlType here — the
|
|
435
|
+
// .form.txt side carries the real control type.
|
|
436
|
+
this.nodes.push({
|
|
437
|
+
id: controlNodeId,
|
|
438
|
+
kind: 'form-instance-control',
|
|
439
|
+
name: handler.controlName,
|
|
440
|
+
qualifiedName: `${formFilePath}::${handler.controlName}`,
|
|
441
|
+
filePath: formFilePath,
|
|
442
|
+
language: 'vba',
|
|
443
|
+
startLine: 0,
|
|
444
|
+
endLine: 0,
|
|
445
|
+
startColumn: 0,
|
|
446
|
+
endColumn: 0,
|
|
447
|
+
updatedAt: Date.now(),
|
|
448
|
+
});
|
|
449
|
+
this.edges.push({
|
|
450
|
+
source: nodeId,
|
|
451
|
+
target: controlNodeId,
|
|
452
|
+
kind: 'event-handler',
|
|
453
|
+
provenance: 'heuristic',
|
|
454
|
+
metadata: { eventName: handler.eventName },
|
|
455
|
+
line: lineNum,
|
|
456
|
+
column: 0,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
if (this.moduleOrClassNode) {
|
|
460
|
+
this.edges.push({
|
|
461
|
+
source: this.moduleOrClassNode.id,
|
|
462
|
+
target: nodeId,
|
|
463
|
+
kind: 'contains',
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
// Module/class node is created lazily (see extract). Hold the edge in
|
|
468
|
+
// pending so its source can be rewritten once the module exists.
|
|
469
|
+
const edge = {
|
|
470
|
+
source: '',
|
|
471
|
+
target: nodeId,
|
|
472
|
+
kind: 'contains',
|
|
473
|
+
};
|
|
474
|
+
this.edges.push(edge);
|
|
475
|
+
this.pendingModuleOrClassSource.push(edge);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return procs;
|
|
479
|
+
}
|
|
480
|
+
/** Implements regex. */
|
|
481
|
+
static IMPLEMENTS_RE = /^\s*Implements\s+(\p{L}[\p{L}\p{N}_]*)/iu;
|
|
482
|
+
/** Edges whose source needs to be set to the module/class id once it exists. */
|
|
483
|
+
pendingModuleOrClassSource = [];
|
|
484
|
+
sweepImplements(src) {
|
|
485
|
+
const lines = src.split('\n');
|
|
486
|
+
let count = 0;
|
|
487
|
+
for (let i = 0; i < lines.length; i++) {
|
|
488
|
+
const line = lines[i] ?? '';
|
|
489
|
+
const m = VbaExtractor.IMPLEMENTS_RE.exec(line);
|
|
490
|
+
if (!m)
|
|
491
|
+
continue;
|
|
492
|
+
const name = m[1] ?? '';
|
|
493
|
+
if (!name)
|
|
494
|
+
continue;
|
|
495
|
+
const lineNum = i + 1;
|
|
496
|
+
const targetId = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'interface', name, lineNum);
|
|
497
|
+
this.nodes.push({
|
|
498
|
+
id: targetId,
|
|
499
|
+
kind: 'interface',
|
|
500
|
+
name,
|
|
501
|
+
qualifiedName: name,
|
|
502
|
+
filePath: this.filePath,
|
|
503
|
+
language: 'vba',
|
|
504
|
+
startLine: lineNum,
|
|
505
|
+
endLine: lineNum,
|
|
506
|
+
startColumn: 0,
|
|
507
|
+
endColumn: line.length,
|
|
508
|
+
updatedAt: Date.now(),
|
|
509
|
+
});
|
|
510
|
+
const edge = {
|
|
511
|
+
source: '', // placeholder; rewritten after module node exists
|
|
512
|
+
target: targetId,
|
|
513
|
+
kind: 'implements',
|
|
514
|
+
// S4 fix: `Implements IFoo` is a static, source-declared fact —
|
|
515
|
+
// not a guess. Use the `parser` provenance (generalizes
|
|
516
|
+
// `tree-sitter` for non-tree-sitter extractors like our regex
|
|
517
|
+
// sweepers) instead of `heuristic`, which is reserved for
|
|
518
|
+
// guessed/inferred edges.
|
|
519
|
+
provenance: 'parser',
|
|
520
|
+
line: lineNum,
|
|
521
|
+
column: 0,
|
|
522
|
+
};
|
|
523
|
+
this.edges.push(edge);
|
|
524
|
+
this.pendingModuleOrClassSource.push(edge);
|
|
525
|
+
count++;
|
|
526
|
+
}
|
|
527
|
+
return count;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Check that a line is a variable declaration and NOT a Sub/Function/
|
|
531
|
+
* Property/Const/WithEvents header (those have their own sweeps).
|
|
532
|
+
* Fix 1+3 (Issues #1,#3): replaced the old single-match DIM_QUAL_RE /
|
|
533
|
+
* DIM_UNQUAL_RE pair with a prefix-check + global scan that handles
|
|
534
|
+
* `As New <Type>`, multi-variable `Dim a As Foo, b As Bar`, and all
|
|
535
|
+
* visibility keywords in one pass.
|
|
536
|
+
*/
|
|
537
|
+
static DIM_DECL_PREFIX_RE = /^\s*(?:Dim|Private|Public)\s+(?!(?:Function|Sub|Property|Const|WithEvents)\b)/i;
|
|
538
|
+
/**
|
|
539
|
+
* Globally scan all `identifier As [New] TypePart1[.TypePart2]` on a
|
|
540
|
+
* variable declaration line. Run with /g after confirming DIM_DECL_PREFIX_RE.
|
|
541
|
+
*
|
|
542
|
+
* Groups: (1) variable name, (2) type outer part, (3) type inner part (if qualified).
|
|
543
|
+
* `(?:New\s+)?` consumes the VBA auto-instantiation keyword so it is
|
|
544
|
+
* never captured as the type name (Fix 1).
|
|
545
|
+
*/
|
|
546
|
+
static DIM_ALL_VARS_RE = /\b(\p{L}[\p{L}\p{N}_]*)\s+As\s+(?:New\s+)?(\p{L}[\p{L}\p{N}_]*)(?:\.(\p{L}[\p{L}\p{N}_]*))?/giu;
|
|
547
|
+
/**
|
|
548
|
+
* VBA primitive type names — skipped when emitted as Dim targets so
|
|
549
|
+
* we don't pollute the graph with `As Long` / `As String` references.
|
|
550
|
+
* Fix 4: all entries are LOWERCASE and the lookup lowercases the
|
|
551
|
+
* captured type name so `As long`, `As LONG`, `As Long` all match.
|
|
552
|
+
* Fix 1 (Issue #1): added `'new'` as a backstop so that if the `As New`
|
|
553
|
+
* pattern is ever captured as a type name it is silently skipped.
|
|
554
|
+
*/
|
|
555
|
+
static PRIMITIVE_TYPES = new Set([
|
|
556
|
+
'long', 'integer', 'short', 'byte', 'single', 'double', 'currency',
|
|
557
|
+
'string', 'boolean', 'date', 'variant', 'object', 'error',
|
|
558
|
+
'empty', 'null', 'longptr', 'longlong', 'new',
|
|
559
|
+
]);
|
|
560
|
+
/** `WithEvents m_X As Form_Foo` — Dim/Private/Public prefix is optional. */
|
|
561
|
+
static WITHEVENTS_RE = /^\s*(?:(?:Dim|Private|Public)\s+)?WithEvents\s+\p{L}[\p{L}\p{N}_]*\s+As\s+(\p{L}[\p{L}\p{N}_]*)/iu;
|
|
562
|
+
sweepDimsAndWithEvents(src) {
|
|
563
|
+
const lines = src.split('\n');
|
|
564
|
+
let count = 0;
|
|
565
|
+
for (let i = 0; i < lines.length; i++) {
|
|
566
|
+
const line = lines[i] ?? '';
|
|
567
|
+
const lineNum = i + 1;
|
|
568
|
+
// Fix 1 + Fix 3 (Issues #1, #3): replace the old single-match
|
|
569
|
+
// DIM_QUAL_RE / DIM_UNQUAL_RE pair with a global scan that handles
|
|
570
|
+
// `As New <Type>`, multi-variable `Dim a As Foo, b As Bar`, and
|
|
571
|
+
// qualified `Dim x As Foo.Bar` in a single pass.
|
|
572
|
+
if (VbaExtractor.DIM_DECL_PREFIX_RE.test(line)) {
|
|
573
|
+
VbaExtractor.DIM_ALL_VARS_RE.lastIndex = 0;
|
|
574
|
+
let m;
|
|
575
|
+
while ((m = VbaExtractor.DIM_ALL_VARS_RE.exec(line)) !== null) {
|
|
576
|
+
const varName = m[1] ?? '';
|
|
577
|
+
const outerType = m[2] ?? '';
|
|
578
|
+
const innerType = m[3] ?? '';
|
|
579
|
+
// Fix 2 (Issue #2): populate the local var type map so that
|
|
580
|
+
// `sweepCallsAndSql` can gate qualified statement-form calls.
|
|
581
|
+
if (varName && outerType) {
|
|
582
|
+
this.localVarTypeMap.set(varName.toLowerCase(), {
|
|
583
|
+
outer: outerType,
|
|
584
|
+
qualified: !!innerType,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
if (innerType) {
|
|
588
|
+
// Qualified form (`Dim x As Foo.Bar`) — emit reference to the
|
|
589
|
+
// outer type `Foo` (same behaviour as the old DIM_QUAL_RE path).
|
|
590
|
+
if (outerType) {
|
|
591
|
+
this.emitReference(outerType, lineNum, 0, 'vba-name-resolution');
|
|
592
|
+
count++;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
// Unqualified form (`Dim x As SomeType`, `Dim x As New SomeType`)
|
|
597
|
+
// — emit reference only when the type is not a primitive or keyword.
|
|
598
|
+
if (outerType && !VbaExtractor.PRIMITIVE_TYPES.has(outerType.toLowerCase())) {
|
|
599
|
+
this.emitReference(outerType, lineNum, 0, 'vba-name-resolution');
|
|
600
|
+
count++;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
// WithEvents declarations — handled by their own regex; also populate
|
|
606
|
+
// the local var type map for completeness.
|
|
607
|
+
const weMatch = VbaExtractor.WITHEVENTS_RE.exec(line);
|
|
608
|
+
if (weMatch) {
|
|
609
|
+
const formType = weMatch[1] ?? '';
|
|
610
|
+
if (formType) {
|
|
611
|
+
// Extract the variable name from the WithEvents line for the map.
|
|
612
|
+
const weVarM = /^\s*(?:(?:Dim|Private|Public)\s+)?WithEvents\s+(\p{L}[\p{L}\p{N}_]*)/iu.exec(line);
|
|
613
|
+
const weVarName = weVarM?.[1] ?? '';
|
|
614
|
+
if (weVarName) {
|
|
615
|
+
this.localVarTypeMap.set(weVarName.toLowerCase(), {
|
|
616
|
+
outer: formType,
|
|
617
|
+
qualified: false,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
this.emitReference(formType, lineNum, 0, 'vba-withevents');
|
|
621
|
+
count++;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return count;
|
|
626
|
+
}
|
|
627
|
+
/** `[visibility] Enum <Name>` — opens an enum block. */
|
|
628
|
+
static ENUM_START_RE = /^\s*(?:(Public|Private|Friend|Global)\s+)?Enum\s+(\p{L}[\p{L}\p{N}_]*)/iu;
|
|
629
|
+
/** `End Enum` — closes the current enum block. */
|
|
630
|
+
static ENUM_END_RE = /^\s*End\s+Enum\b/i;
|
|
631
|
+
/**
|
|
632
|
+
* An enum member line: a leading identifier optionally followed by `=
|
|
633
|
+
* <value>`. Runs only inside an open Enum block, on the (already
|
|
634
|
+
* comment-stripped) source, so a trailing `'comment` never reaches here.
|
|
635
|
+
* `\p{L}` covers accented member names (e.g. `Sí`).
|
|
636
|
+
*/
|
|
637
|
+
static ENUM_MEMBER_RE = /^\s*(\p{L}[\p{L}\p{N}_]*)\s*(?:=|$)/u;
|
|
638
|
+
/** `[visibility] Const <decls>` — captures visibility (1) and the rest (2). */
|
|
639
|
+
static CONST_DECL_RE = /^\s*(?:(Public|Private|Friend|Global)\s+)?Const\s+(.+)$/i;
|
|
640
|
+
/**
|
|
641
|
+
* One declared name inside a `Const` body. A name sits at a declaration
|
|
642
|
+
* boundary (start-of-body or after a comma), optionally followed by
|
|
643
|
+
* `As <Type>`, then `=`. Run with /g over the CONST_DECL_RE group 2 so
|
|
644
|
+
* multi-name lines (`Const A = 1, B = 2`) emit one node per name.
|
|
645
|
+
*/
|
|
646
|
+
static CONST_NAME_RE = /(?:^|,)\s*(\p{L}[\p{L}\p{N}_]*)\s*(?:As\s+[\p{L}\p{N}_.]+\s*)?=/giu;
|
|
647
|
+
/**
|
|
648
|
+
* Fold a VBA visibility keyword to the canonical lowercase enum, matching
|
|
649
|
+
* the procedure convention: `Private` → 'private'; `Public`, `Global`,
|
|
650
|
+
* `Friend`, or none → 'public' (VBA's default module-level `Const`/`Enum`
|
|
651
|
+
* is Private, but we follow the same broader-than-private fold the proc
|
|
652
|
+
* sweep uses so visibility is consistent across symbol kinds).
|
|
653
|
+
*/
|
|
654
|
+
static foldVisibility(raw) {
|
|
655
|
+
return raw.toLowerCase() === 'private' ? 'private' : 'public';
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Walk the (uncommented, line-joined) source and emit:
|
|
659
|
+
* - one `enum` node per `Enum <Name>` block, with one `enum_member` node
|
|
660
|
+
* per member and a `contains` edge enum→member;
|
|
661
|
+
* - one `constant` node per name declared on a `Const` line (multi-name
|
|
662
|
+
* lines emit one node per name);
|
|
663
|
+
* - a `contains` edge from the module/class node to each enum and constant
|
|
664
|
+
* (held in `pendingModuleOrClassSource` until the module node exists).
|
|
665
|
+
*
|
|
666
|
+
* Returns the number of top-level symbols (enums + constants) emitted so
|
|
667
|
+
* the caller can flip `hasAnySymbols`.
|
|
668
|
+
*/
|
|
669
|
+
sweepEnumsAndConsts(src) {
|
|
670
|
+
const lines = src.split('\n');
|
|
671
|
+
let count = 0;
|
|
672
|
+
let currentEnum = null;
|
|
673
|
+
for (let i = 0; i < lines.length; i++) {
|
|
674
|
+
const line = lines[i] ?? '';
|
|
675
|
+
const lineNum = i + 1;
|
|
676
|
+
if (currentEnum) {
|
|
677
|
+
if (VbaExtractor.ENUM_END_RE.test(line)) {
|
|
678
|
+
currentEnum = null;
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
const mm = VbaExtractor.ENUM_MEMBER_RE.exec(line);
|
|
682
|
+
if (mm) {
|
|
683
|
+
const memberName = mm[1] ?? '';
|
|
684
|
+
if (memberName) {
|
|
685
|
+
const memberId = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'enum_member', memberName, lineNum);
|
|
686
|
+
this.nodes.push({
|
|
687
|
+
id: memberId,
|
|
688
|
+
kind: 'enum_member',
|
|
689
|
+
name: memberName,
|
|
690
|
+
qualifiedName: `${currentEnum.name}.${memberName}`,
|
|
691
|
+
filePath: this.filePath,
|
|
692
|
+
language: 'vba',
|
|
693
|
+
startLine: lineNum,
|
|
694
|
+
endLine: lineNum,
|
|
695
|
+
startColumn: 0,
|
|
696
|
+
endColumn: line.length,
|
|
697
|
+
updatedAt: Date.now(),
|
|
698
|
+
});
|
|
699
|
+
// enum → member: source is known, emit directly (not pending).
|
|
700
|
+
this.edges.push({
|
|
701
|
+
source: currentEnum.id,
|
|
702
|
+
target: memberId,
|
|
703
|
+
kind: 'contains',
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
const enumStart = VbaExtractor.ENUM_START_RE.exec(line);
|
|
710
|
+
if (enumStart) {
|
|
711
|
+
const visibility = VbaExtractor.foldVisibility(enumStart[1] ?? '');
|
|
712
|
+
const name = enumStart[2] ?? '';
|
|
713
|
+
if (!name)
|
|
714
|
+
continue;
|
|
715
|
+
const enumId = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'enum', name, lineNum);
|
|
716
|
+
this.nodes.push({
|
|
717
|
+
id: enumId,
|
|
718
|
+
kind: 'enum',
|
|
719
|
+
name,
|
|
720
|
+
qualifiedName: name,
|
|
721
|
+
filePath: this.filePath,
|
|
722
|
+
language: 'vba',
|
|
723
|
+
startLine: lineNum,
|
|
724
|
+
endLine: lineNum,
|
|
725
|
+
startColumn: 0,
|
|
726
|
+
endColumn: line.length,
|
|
727
|
+
visibility,
|
|
728
|
+
updatedAt: Date.now(),
|
|
729
|
+
});
|
|
730
|
+
this.pushContainsFromModule(enumId);
|
|
731
|
+
currentEnum = { id: enumId, name };
|
|
732
|
+
count++;
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
const constDecl = VbaExtractor.CONST_DECL_RE.exec(line);
|
|
736
|
+
if (constDecl) {
|
|
737
|
+
const visibility = VbaExtractor.foldVisibility(constDecl[1] ?? '');
|
|
738
|
+
const body = constDecl[2] ?? '';
|
|
739
|
+
VbaExtractor.CONST_NAME_RE.lastIndex = 0;
|
|
740
|
+
let cm;
|
|
741
|
+
while ((cm = VbaExtractor.CONST_NAME_RE.exec(body)) !== null) {
|
|
742
|
+
const constName = cm[1] ?? '';
|
|
743
|
+
if (!constName)
|
|
744
|
+
continue;
|
|
745
|
+
const constId = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'constant', constName, lineNum);
|
|
746
|
+
this.nodes.push({
|
|
747
|
+
id: constId,
|
|
748
|
+
kind: 'constant',
|
|
749
|
+
name: constName,
|
|
750
|
+
qualifiedName: constName,
|
|
751
|
+
filePath: this.filePath,
|
|
752
|
+
language: 'vba',
|
|
753
|
+
startLine: lineNum,
|
|
754
|
+
endLine: lineNum,
|
|
755
|
+
startColumn: 0,
|
|
756
|
+
endColumn: line.length,
|
|
757
|
+
visibility,
|
|
758
|
+
updatedAt: Date.now(),
|
|
759
|
+
});
|
|
760
|
+
this.pushContainsFromModule(constId);
|
|
761
|
+
count++;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return count;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Emit a `contains` edge from the (lazily-created) module/class node to
|
|
769
|
+
* `targetId`. Mirrors the pending-source pattern the procedure and
|
|
770
|
+
* implements sweeps use: the module node doesn't exist yet, so the edge's
|
|
771
|
+
* source is rewritten once `extract()` creates it.
|
|
772
|
+
*/
|
|
773
|
+
pushContainsFromModule(targetId) {
|
|
774
|
+
if (this.moduleOrClassNode) {
|
|
775
|
+
this.edges.push({
|
|
776
|
+
source: this.moduleOrClassNode.id,
|
|
777
|
+
target: targetId,
|
|
778
|
+
kind: 'contains',
|
|
779
|
+
});
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const edge = { source: '', target: targetId, kind: 'contains' };
|
|
783
|
+
this.edges.push(edge);
|
|
784
|
+
this.pendingModuleOrClassSource.push(edge);
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Walk the (uncommented) source per line. For each line:
|
|
788
|
+
* - If it starts a `Sub`/`Function`/`Property`, push the proc onto a stack.
|
|
789
|
+
* - If it ends one (`End Sub` / `End Function` / `End Property`), pop.
|
|
790
|
+
* - While inside a procedure, scan the line for call-site patterns and
|
|
791
|
+
* SQL-wrapper patterns.
|
|
792
|
+
*
|
|
793
|
+
* Call-site regex: `(?<!\w)([A-Za-z_]\w*)(?:\.([A-Za-z_]\w*))?\s*\(` —
|
|
794
|
+
* captures either `Name(...)` (same-file candidate) or `Receiver.Member(...)`
|
|
795
|
+
* (qualified — emit a synthetic node + heuristic edge).
|
|
796
|
+
*/
|
|
797
|
+
static CALL_RE = /(?<![\w.])(\p{L}[\p{L}\p{N}_]*)(?:\.(\p{L}[\p{L}\p{N}_]*))?\s*\(/gu;
|
|
798
|
+
/** SQL wrapper helpers — order matters because `db.Execute` is a suffix of others. */
|
|
799
|
+
static SQL_WRAPPERS = [
|
|
800
|
+
{ name: 'DoCmd.RunSQL', re: /\bDoCmd\.RunSQL\s+"((?:[^"]|"")*)"/g },
|
|
801
|
+
{ name: 'CurrentDb.OpenRecordset', re: /\bCurrentDb\.OpenRecordset\s+"((?:[^"]|"")*)"/g },
|
|
802
|
+
{ name: 'CurrentDb.Execute', re: /\bCurrentDb\.Execute\s+"((?:[^"]|"")*)"/g },
|
|
803
|
+
{ name: 'db.Execute', re: /\bdb\.Execute\s+"((?:[^"]|"")*)"/g },
|
|
804
|
+
// Fix 4 (Issue #4): inline-literal forms `getdb().Execute "..."` and
|
|
805
|
+
// `getdb().OpenRecordset "..."` — the variable form is covered by
|
|
806
|
+
// SQL_VAR_EXEC_RE but the direct-literal form was missing.
|
|
807
|
+
{ name: 'getdb().Execute', re: /\bgetdb\(\)\.Execute\s+"((?:[^"]|"")*)"/g },
|
|
808
|
+
{ name: 'getdb().OpenRecordset', re: /\bgetdb\(\)\.OpenRecordset\s+"((?:[^"]|"")*)"/g },
|
|
809
|
+
];
|
|
810
|
+
/**
|
|
811
|
+
* `DoCmd.OpenForm "<FormName>"` modelling regex — B4 (hueco 6).
|
|
812
|
+
*
|
|
813
|
+
* Real VBA idiom (matches both forms):
|
|
814
|
+
* `DoCmd.OpenForm "MyForm"`
|
|
815
|
+
* `DoCmd.OpenForm "MyForm", acNormal, , , acFormEdit`
|
|
816
|
+
*
|
|
817
|
+
* Captures the form NAME (group 1) so the extractor can synthesize an
|
|
818
|
+
* `opens-form` heuristic edge from the calling Sub to a stub for the
|
|
819
|
+
* target form. The trailing positional args (`acNormal`, `acFormEdit`,
|
|
820
|
+
* etc.) are intentionally NOT captured — the orchestrator's scope decision
|
|
821
|
+
* was to cover ONLY `OpenForm` for this commit; `OpenReport`, `OpenQuery`,
|
|
822
|
+
* `OpenTable`, … are flagged in the commit body as follow-up work.
|
|
823
|
+
*
|
|
824
|
+
* Why a separate dispatch: `DoCmd` is in `RUNTIME_RECEIVER_BLACKLIST`
|
|
825
|
+
* (R4 invariant), so `DoCmd.OpenForm` is intentionally SKIPPED by the
|
|
826
|
+
* generic `CALL_RE` path that would otherwise emit a junk `calls` edge
|
|
827
|
+
* to a synthetic `function` node for `DoCmd.OpenForm`. This regex
|
|
828
|
+
* matches BEFORE the call-site scan and uses its own dispatch to emit
|
|
829
|
+
* the `opens-form` edge instead — sharing no logic with CALL_RE.
|
|
830
|
+
*/
|
|
831
|
+
static OPEN_FORM_RE = /\bDoCmd\.OpenForm\s+"([^"]+)"/g;
|
|
832
|
+
/** SQL assigned to a local variable, e.g. `m_SQL = "SELECT ..." & ...`. */
|
|
833
|
+
static SQL_VAR_ASSIGN_RE = /^\s*(\p{L}[\p{L}\p{N}_]*)\s*=\s*(.*)$/iu;
|
|
834
|
+
/** SQL wrapper called with a variable, e.g. `getdb().Execute m_SQL`. */
|
|
835
|
+
static SQL_VAR_EXEC_RE = /\b(?:getdb\(\)|CurrentDb|db)\.(?:OpenRecordset|Execute)\s*\(?\s*(\p{L}[\p{L}\p{N}_]*)\s*\)?/giu;
|
|
836
|
+
/** SQL table-name regex scoped to FROM / INTO / UPDATE. */
|
|
837
|
+
static SQL_TABLE_RE = /\b(?:FROM|INTO|UPDATE)\s+(\[?\p{L}[\p{L}\p{N}_]*\]?)/giu;
|
|
838
|
+
/**
|
|
839
|
+
* `Me.<ControlName>` reference capture — hole 1 of VBA control-modeling.
|
|
840
|
+
*
|
|
841
|
+
* Real VBA idiom:
|
|
842
|
+
* `Me.lblTitulo.Caption = "Hello"` ← property assignment
|
|
843
|
+
* `Me.txtDescripcion.Value = "World"` ← property assignment
|
|
844
|
+
* `Me.ComandoGrabar.Enabled = True` ← property assignment
|
|
845
|
+
* `If Nz(Me.MotivoBorrado, "") = "" Then` ← read in expression
|
|
846
|
+
*
|
|
847
|
+
* The existing call-site scanner (CALL_RE) only fires on `Name(`
|
|
848
|
+
* (paren form) and `Me` is in its keyword blacklist anyway, so
|
|
849
|
+
* `Me.<Control>` references are silently invisible. This regex matches
|
|
850
|
+
* the FIRST identifier after `Me.` regardless of what follows (a
|
|
851
|
+
* property, an index, an assignment, a call argument, etc.) so the
|
|
852
|
+
* form → control binding is surfaced as an UnresolvedReference for the
|
|
853
|
+
* resolver to pick up later. Subsequent segments (`.Caption`, `.Value`,
|
|
854
|
+
* `.Enabled`) are intentionally NOT captured — they are properties of
|
|
855
|
+
* the control, not new symbols.
|
|
856
|
+
*
|
|
857
|
+
* Provenance: `metadata.synthesizedBy = 'vba-me-control'`. Mirrors the
|
|
858
|
+
* `vba-form-binding` (form→sibling-`.cls`) and `vba-name-resolution`
|
|
859
|
+
* (qualified Dim) patterns already documented on `UnresolvedReference.metadata`
|
|
860
|
+
* in `src/types.ts`.
|
|
861
|
+
*/
|
|
862
|
+
static ME_CONTROL_RE = /\bMe\.(\p{L}[\p{L}\p{N}_]*)/gu;
|
|
863
|
+
/** Keywords we never want to match as call receivers. */
|
|
864
|
+
static CALL_KEYWORD_BLACKLIST = new Set([
|
|
865
|
+
'If',
|
|
866
|
+
'For',
|
|
867
|
+
'While',
|
|
868
|
+
'Do',
|
|
869
|
+
'Select',
|
|
870
|
+
'Case',
|
|
871
|
+
'Then',
|
|
872
|
+
'Else',
|
|
873
|
+
'ElseIf',
|
|
874
|
+
'With',
|
|
875
|
+
'Loop',
|
|
876
|
+
'Wend',
|
|
877
|
+
'End',
|
|
878
|
+
'Return',
|
|
879
|
+
'Dim',
|
|
880
|
+
'Set',
|
|
881
|
+
'Let',
|
|
882
|
+
'Const',
|
|
883
|
+
'ReDim',
|
|
884
|
+
'Static',
|
|
885
|
+
'Public',
|
|
886
|
+
'Private',
|
|
887
|
+
'Friend',
|
|
888
|
+
'Sub',
|
|
889
|
+
'Function',
|
|
890
|
+
'Property',
|
|
891
|
+
'Class',
|
|
892
|
+
'Module',
|
|
893
|
+
'Option',
|
|
894
|
+
'On',
|
|
895
|
+
'Error',
|
|
896
|
+
'Resume',
|
|
897
|
+
'Exit',
|
|
898
|
+
'New',
|
|
899
|
+
'Call',
|
|
900
|
+
'Rem',
|
|
901
|
+
'LBound',
|
|
902
|
+
'UBound',
|
|
903
|
+
'Me',
|
|
904
|
+
'Nothing',
|
|
905
|
+
]);
|
|
906
|
+
/**
|
|
907
|
+
* Access runtime objects and singletons. Calls on these receivers are
|
|
908
|
+
* real VBA calls but the targets are NOT user-defined modules or
|
|
909
|
+
* classes — they're Access/DAO/ADO runtime types. Synthesizing a
|
|
910
|
+
* `function` node for each would pollute the graph with ~20+ junk
|
|
911
|
+
* nodes per real-world file (audit W4, June 2026). Skip synthesis
|
|
912
|
+
* for any receiver or member in this set.
|
|
913
|
+
*
|
|
914
|
+
* Note: `DoCmd.RunSQL`, `DoCmd.OpenForm`, etc. still get SQL/edge
|
|
915
|
+
* tracking via the dedicated `SQL_WRAPPERS` regex path (REQ-CODE-8),
|
|
916
|
+
* which fires BEFORE this scan and uses its own dispatch — so
|
|
917
|
+
* blacklisting DoCmd here doesn't lose the SQL-flow edges.
|
|
918
|
+
*/
|
|
919
|
+
static RUNTIME_RECEIVER_BLACKLIST = new Set([
|
|
920
|
+
// Form / page references
|
|
921
|
+
'Screen',
|
|
922
|
+
// Access application singletons
|
|
923
|
+
'Application',
|
|
924
|
+
'DoCmd',
|
|
925
|
+
'SysCmd',
|
|
926
|
+
// VBA debugging intrinsic — Debug.Print / Debug.Assert
|
|
927
|
+
'Debug',
|
|
928
|
+
// Access object collections
|
|
929
|
+
'Forms',
|
|
930
|
+
'Reports',
|
|
931
|
+
'Modules',
|
|
932
|
+
'References',
|
|
933
|
+
'CommandBars',
|
|
934
|
+
// Error-handling intrinsic
|
|
935
|
+
'Err',
|
|
936
|
+
// Late-binding factories (return IDispatch — not user code)
|
|
937
|
+
'CreateObject',
|
|
938
|
+
'GetObject',
|
|
939
|
+
// DAO/ADO recordset field collection access (e.g. rcdDatos.Fields("ID"))
|
|
940
|
+
'Fields',
|
|
941
|
+
]);
|
|
942
|
+
/**
|
|
943
|
+
* Fix 2 (Issue #2): replace string-literal content with spaces so that
|
|
944
|
+
* call-site patterns inside `"..."` spans are invisible to CALL_RE and
|
|
945
|
+
* the statement-form detectors. Column positions are preserved (each
|
|
946
|
+
* character is replaced 1-for-1) so any col-based metadata stays correct.
|
|
947
|
+
*/
|
|
948
|
+
static maskStringContent(line) {
|
|
949
|
+
let result = '';
|
|
950
|
+
let i = 0;
|
|
951
|
+
while (i < line.length) {
|
|
952
|
+
const ch = line[i];
|
|
953
|
+
if (ch === '"') {
|
|
954
|
+
result += ' '; // opening quote masked
|
|
955
|
+
i++;
|
|
956
|
+
while (i < line.length) {
|
|
957
|
+
const c = line[i];
|
|
958
|
+
if (c === '"' && line[i + 1] === '"') {
|
|
959
|
+
result += ' '; // doubled-quote escape masked (2 chars)
|
|
960
|
+
i += 2;
|
|
961
|
+
}
|
|
962
|
+
else if (c === '"') {
|
|
963
|
+
result += ' '; // closing quote masked
|
|
964
|
+
i++;
|
|
965
|
+
break;
|
|
966
|
+
}
|
|
967
|
+
else {
|
|
968
|
+
result += ' '; // string content → space
|
|
969
|
+
i++;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
result += ch;
|
|
975
|
+
i++;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return result;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Fix 2 (Issue #2): return true iff `receiverName` is a file-local variable
|
|
982
|
+
* (appears in `localVarTypeMap`) whose declared type is a SIMPLE (non-
|
|
983
|
+
* qualified, non-primitive) identifier — a candidate project-defined class.
|
|
984
|
+
* Qualified types (e.g. `DAO.Recordset`) and primitives (`String`, `Long`)
|
|
985
|
+
* return false so runtime/DAO calls are suppressed.
|
|
986
|
+
*/
|
|
987
|
+
isLocalProjectClassVar(receiverName) {
|
|
988
|
+
const entry = this.localVarTypeMap.get(receiverName.toLowerCase());
|
|
989
|
+
if (!entry)
|
|
990
|
+
return false; // not declared in this file → silent
|
|
991
|
+
if (entry.qualified)
|
|
992
|
+
return false; // DAO.Recordset etc. → silent
|
|
993
|
+
if (VbaExtractor.PRIMITIVE_TYPES.has(entry.outer.toLowerCase()))
|
|
994
|
+
return false;
|
|
995
|
+
return true;
|
|
996
|
+
}
|
|
997
|
+
sweepCallsAndSql(src) {
|
|
998
|
+
const lines = src.split('\n');
|
|
999
|
+
const procedureStartLines = new Set();
|
|
1000
|
+
// S5 fix: also match `End Sub`/`End Function`/`End Property` after a
|
|
1001
|
+
// colon (`:`), so single-line `Public Sub X(): End Sub` is recognized
|
|
1002
|
+
// as ending the procedure. The previous `/^\s*End...` only matched at
|
|
1003
|
+
// line start, so the proc stack never popped for colon-separated
|
|
1004
|
+
// single-line declarations.
|
|
1005
|
+
const procedureEndRe = /(?:^|:\s*)End\s+(?:Sub|Function|Property)\b/i;
|
|
1006
|
+
const sqlTargetsThisFile = new Set();
|
|
1007
|
+
// Walk the source once, emitting call edges and SQL edges per line and
|
|
1008
|
+
// tracking the current procedure stack. The previous implementation
|
|
1009
|
+
// did this in two passes; audit S1 (June 2026) flagged the first pass
|
|
1010
|
+
// as dead code (its `procStack` was never read after the loop). One
|
|
1011
|
+
// pass suffices.
|
|
1012
|
+
const stack = [];
|
|
1013
|
+
const sqlVariables = new Map();
|
|
1014
|
+
// C2 fix: track each procedure's `endLine` (the line containing the
|
|
1015
|
+
// matching `End Sub`/`End Function`/`End Property`) keyed by its
|
|
1016
|
+
// `startLine`. After the loop, we update every function node's
|
|
1017
|
+
// `endLine` so `codegraph_explore` returns the full body span —
|
|
1018
|
+
// not just the signature line.
|
|
1019
|
+
const procEndLines = new Map();
|
|
1020
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1021
|
+
const line = lines[i] ?? '';
|
|
1022
|
+
const lineNum = i + 1;
|
|
1023
|
+
const procStart = VbaExtractor.PROC_RE.exec(line);
|
|
1024
|
+
if (procStart) {
|
|
1025
|
+
const name = procStart[3] ?? '';
|
|
1026
|
+
const bucket = this.localProcs.get(name);
|
|
1027
|
+
if (bucket) {
|
|
1028
|
+
// Fix 1: select the ProcInfo whose startLine matches the current
|
|
1029
|
+
// line, not always bucket[0]. When Property Get/Let/Set share a
|
|
1030
|
+
// name, all three exist in the bucket; pushing bucket[0] every time
|
|
1031
|
+
// meant Let/Set bodies erroneously attributed to the Get's ProcInfo
|
|
1032
|
+
// (wrong endLine and wrong caller source on call edges).
|
|
1033
|
+
const proc = bucket.find((p) => p.startLine === lineNum) ?? bucket[0];
|
|
1034
|
+
if (proc)
|
|
1035
|
+
stack.push(proc);
|
|
1036
|
+
}
|
|
1037
|
+
procedureStartLines.add(lineNum);
|
|
1038
|
+
}
|
|
1039
|
+
else if (procedureEndRe.test(line) && stack.length > 0) {
|
|
1040
|
+
const ending = stack.pop();
|
|
1041
|
+
procEndLines.set(ending.startLine, lineNum);
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
// Fix 2 (Issue #2): mask string-literal content before call scanning so
|
|
1045
|
+
// patterns like `modHelper.BuildQuery(` inside a string argument are not
|
|
1046
|
+
// mistakenly treated as call sites. SQL scanning still uses the original
|
|
1047
|
+
// line because SQL lives INSIDE string literals.
|
|
1048
|
+
const callScanLine = VbaExtractor.maskStringContent(line);
|
|
1049
|
+
// Don't scan call sites on the line that declares the procedure — it
|
|
1050
|
+
// would match the proc name itself in `Sub Outer()`.
|
|
1051
|
+
if (!procedureStartLines.has(lineNum) && stack.length > 0) {
|
|
1052
|
+
this.scanCallSites(callScanLine, stack[stack.length - 1], lineNum);
|
|
1053
|
+
}
|
|
1054
|
+
// Hueco 1: capture `Me.<Control>` references (property assignments,
|
|
1055
|
+
// read expressions, method calls, anything after `Me.`). Only inside
|
|
1056
|
+
// procedures because `Me` is only meaningful inside a form's class
|
|
1057
|
+
// module. Skipped on the proc-declaration line for the same reason as
|
|
1058
|
+
// the call-site scan above.
|
|
1059
|
+
if (!procedureStartLines.has(lineNum) && stack.length > 0) {
|
|
1060
|
+
this.scanMeControlReferences(callScanLine, stack[stack.length - 1], lineNum);
|
|
1061
|
+
}
|
|
1062
|
+
// SQL wrappers — only inside a procedure (don't pollute module scope
|
|
1063
|
+
// with a stray string literal). Use the ORIGINAL line — SQL is inside
|
|
1064
|
+
// string literals, so the masked line would strip the SQL content.
|
|
1065
|
+
if (stack.length > 0) {
|
|
1066
|
+
this.trackSqlVariableAssignment(lines, i, sqlVariables);
|
|
1067
|
+
this.scanSqlInLine(line, lineNum, sqlTargetsThisFile, sqlVariables);
|
|
1068
|
+
}
|
|
1069
|
+
// H1 fix: detect statement-form Sub calls (no parens, no `Call`
|
|
1070
|
+
// keyword). Audit H1 (June 2026): the parens-only CALL_RE made the
|
|
1071
|
+
// dominant VBA idiom invisible — `EstablecerDatos m_Error` on the
|
|
1072
|
+
// Form_FormNCAuditoriaMotivoEliminado.cls fixture contributed
|
|
1073
|
+
// nothing to the call graph. Walked here so we share the proc stack
|
|
1074
|
+
// already maintained by this loop.
|
|
1075
|
+
if (stack.length > 0 && !procedureStartLines.has(lineNum)) {
|
|
1076
|
+
const stmtCall = this.detectStatementCall(callScanLine);
|
|
1077
|
+
if (stmtCall) {
|
|
1078
|
+
const caller = stack[stack.length - 1];
|
|
1079
|
+
this.emitStatementCallEdge(caller, stmtCall, lineNum);
|
|
1080
|
+
}
|
|
1081
|
+
// Fix 7 + Fix 2: qualified statement-form calls (`Receiver.Member args`) —
|
|
1082
|
+
// the dominant cross-object call shape in real Dysflow fixtures.
|
|
1083
|
+
// `CALL_RE` only matches the paren form; this path covers the no-paren
|
|
1084
|
+
// statement form and emits a heuristic `calls` edge ONLY when the
|
|
1085
|
+
// receiver is a file-local variable typed as a candidate project class
|
|
1086
|
+
// (Fix 2: REQ-CODE-4 "unresolvable call is silent").
|
|
1087
|
+
const qualStmt = this.detectQualifiedStatementCall(callScanLine);
|
|
1088
|
+
if (qualStmt) {
|
|
1089
|
+
const caller = stack[stack.length - 1];
|
|
1090
|
+
if (this.isLocalProjectClassVar(qualStmt.receiver)) {
|
|
1091
|
+
this.emitQualifiedStatementCallEdge(caller, qualStmt.receiver, qualStmt.member, lineNum);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
// B4 (hueco 6): `DoCmd.OpenForm "FormName"` modelling.
|
|
1095
|
+
//
|
|
1096
|
+
// The literal form name lives INSIDE a string literal, so we
|
|
1097
|
+
// scan the ORIGINAL (unmasked) line — `callScanLine` has its
|
|
1098
|
+
// string content replaced with spaces. The receiver is the same
|
|
1099
|
+
// proc-stack frame so we attribute the edge to the calling Sub,
|
|
1100
|
+
// not the file-level module.
|
|
1101
|
+
//
|
|
1102
|
+
// Cross-file edge filter note: the synthesized stub node is
|
|
1103
|
+
// emitted locally (same file's extraction result), so the
|
|
1104
|
+
// per-file filter at `index.ts:insertedIds.has(source/target)`
|
|
1105
|
+
// passes the edge naturally — no exemption to the filter is
|
|
1106
|
+
// required. The real form-layout node, when VbaFormExtractor
|
|
1107
|
+
// later processes the matching .form.txt file, gets a DIFFERENT
|
|
1108
|
+
// node id (it uses the real .form.txt path); the stub and the
|
|
1109
|
+
// real node coexist harmlessly — the edge from THIS file still
|
|
1110
|
+
// references the stub. Future work can collapse the stub once
|
|
1111
|
+
// the indexer learns to re-resolve cross-file edges to the real
|
|
1112
|
+
// node id (the same pattern the cross-file incoming-edges
|
|
1113
|
+
// snapshot already uses at `index.ts:getCrossFileIncomingEdges`).
|
|
1114
|
+
const caller2 = stack[stack.length - 1];
|
|
1115
|
+
this.scanOpenFormCalls(line, caller2, lineNum);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
// Apply endLine to every emitted function node keyed by its startLine.
|
|
1119
|
+
// Functions without a recorded endLine (e.g. malformed VBA without an
|
|
1120
|
+
// `End`) keep their `endLine = startLine` from sweepProcedures —
|
|
1121
|
+
// which is the correct "single line" representation.
|
|
1122
|
+
for (const n of this.nodes) {
|
|
1123
|
+
if (n.kind !== 'function')
|
|
1124
|
+
continue;
|
|
1125
|
+
const end = procEndLines.get(n.startLine);
|
|
1126
|
+
if (end !== undefined)
|
|
1127
|
+
n.endLine = end;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
scanCallSites(line, from, lineNum) {
|
|
1131
|
+
VbaExtractor.CALL_RE.lastIndex = 0;
|
|
1132
|
+
let m;
|
|
1133
|
+
while ((m = VbaExtractor.CALL_RE.exec(line)) !== null) {
|
|
1134
|
+
const receiver = m[1] ?? '';
|
|
1135
|
+
const member = m[2] ?? '';
|
|
1136
|
+
if (!receiver)
|
|
1137
|
+
continue;
|
|
1138
|
+
// Skip VBA control-flow keywords.
|
|
1139
|
+
if (VbaExtractor.CALL_KEYWORD_BLACKLIST.has(receiver))
|
|
1140
|
+
continue;
|
|
1141
|
+
if (member && VbaExtractor.CALL_KEYWORD_BLACKLIST.has(member))
|
|
1142
|
+
continue;
|
|
1143
|
+
// Skip Access runtime objects — `Me`, `DoCmd`, `Application`, etc.
|
|
1144
|
+
// These calls are real but the targets are NOT user code; emitting
|
|
1145
|
+
// synthetic function nodes for them pollutes the graph (audit W4).
|
|
1146
|
+
if (VbaExtractor.RUNTIME_RECEIVER_BLACKLIST.has(receiver))
|
|
1147
|
+
continue;
|
|
1148
|
+
if (member && VbaExtractor.RUNTIME_RECEIVER_BLACKLIST.has(member))
|
|
1149
|
+
continue;
|
|
1150
|
+
// Skip the receiver when it equals the containing procedure (self-call).
|
|
1151
|
+
if (receiver === from.name && !member)
|
|
1152
|
+
continue;
|
|
1153
|
+
const col = m.index;
|
|
1154
|
+
if (!member) {
|
|
1155
|
+
// Bare `Name(...)` — same-file resolution.
|
|
1156
|
+
const bucket = this.localProcs.get(receiver);
|
|
1157
|
+
const local = bucket?.[0];
|
|
1158
|
+
if (!local)
|
|
1159
|
+
continue; // unresolvable — silent per spec R4.
|
|
1160
|
+
const localFuncNode = this.findFunctionNodeByName(receiver);
|
|
1161
|
+
if (!localFuncNode)
|
|
1162
|
+
continue;
|
|
1163
|
+
this.edges.push({
|
|
1164
|
+
source: this.findOrCreateFunctionNodeId(from),
|
|
1165
|
+
target: localFuncNode.id,
|
|
1166
|
+
kind: 'calls',
|
|
1167
|
+
line: lineNum,
|
|
1168
|
+
column: col,
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
// Qualified `Receiver.Member(...)` — synthesize the call target.
|
|
1173
|
+
const qualified = `${receiver}.${member}`;
|
|
1174
|
+
// Avoid emitting duplicate edges for the same call (within a line).
|
|
1175
|
+
const dedupeKey = `${from.name}->${qualified}@${lineNum}`;
|
|
1176
|
+
if (this.callDedupe.has(dedupeKey))
|
|
1177
|
+
continue;
|
|
1178
|
+
this.callDedupe.add(dedupeKey);
|
|
1179
|
+
const synthId = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'function', qualified, lineNum);
|
|
1180
|
+
// Only add the synthetic function node once per (file, qualified, line).
|
|
1181
|
+
if (!this.synthFunctionNodeIds.has(synthId)) {
|
|
1182
|
+
this.synthFunctionNodeIds.add(synthId);
|
|
1183
|
+
this.nodes.push({
|
|
1184
|
+
id: synthId,
|
|
1185
|
+
kind: 'function',
|
|
1186
|
+
name: qualified,
|
|
1187
|
+
qualifiedName: qualified,
|
|
1188
|
+
filePath: this.filePath,
|
|
1189
|
+
language: 'vba',
|
|
1190
|
+
startLine: lineNum,
|
|
1191
|
+
endLine: lineNum,
|
|
1192
|
+
startColumn: col,
|
|
1193
|
+
endColumn: col + qualified.length,
|
|
1194
|
+
visibility: 'public',
|
|
1195
|
+
updatedAt: Date.now(),
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
this.edges.push({
|
|
1199
|
+
source: this.findOrCreateFunctionNodeId(from),
|
|
1200
|
+
target: synthId,
|
|
1201
|
+
kind: 'calls',
|
|
1202
|
+
provenance: 'heuristic',
|
|
1203
|
+
metadata: { synthesizedBy: 'vba-name-resolution' },
|
|
1204
|
+
line: lineNum,
|
|
1205
|
+
column: col,
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
/** Cache so we don't re-emit the same proc function node per call site. */
|
|
1211
|
+
procNodeIdCache = new Map();
|
|
1212
|
+
callDedupe = new Set();
|
|
1213
|
+
synthFunctionNodeIds = new Set();
|
|
1214
|
+
/**
|
|
1215
|
+
* B4 (hueco 6): cache of stub `form-layout` node ids we've already emitted
|
|
1216
|
+
* for a given target form name in this file. Avoids emitting duplicate
|
|
1217
|
+
* stubs when `DoCmd.OpenForm "FormTest"` shows up N times across N calls.
|
|
1218
|
+
* Keyed by the lowercased form name so `FormTest` / `formtest` collapse.
|
|
1219
|
+
*/
|
|
1220
|
+
opensFormStubIdsByName = new Map();
|
|
1221
|
+
/**
|
|
1222
|
+
* Hueco 1: scan a line for `Me.<ControlName>` patterns and emit one
|
|
1223
|
+
* UnresolvedReference per occurrence, tagged
|
|
1224
|
+
* `metadata.synthesizedBy: 'vba-me-control'`.
|
|
1225
|
+
*
|
|
1226
|
+
* Operates on the masked `callScanLine` (string-literal content already
|
|
1227
|
+
* replaced with spaces) so `Me.X` inside a string literal is not falsely
|
|
1228
|
+
* captured. Per-site emission — every `Me.lblTitulo` reference site
|
|
1229
|
+
* produces its own UnresolvedReference carrying the line/column; the
|
|
1230
|
+
* resolver fans them out into multiple `references` edges at index time
|
|
1231
|
+
* once the matching `form-instance-control` node exists.
|
|
1232
|
+
*
|
|
1233
|
+
* `fromNodeId` is the current procedure's function node — that's the
|
|
1234
|
+
* "owner" of the reference (the Sub body that wrote `Me.lblTitulo = …`).
|
|
1235
|
+
*/
|
|
1236
|
+
scanMeControlReferences(line, from, lineNum) {
|
|
1237
|
+
VbaExtractor.ME_CONTROL_RE.lastIndex = 0;
|
|
1238
|
+
let m;
|
|
1239
|
+
while ((m = VbaExtractor.ME_CONTROL_RE.exec(line)) !== null) {
|
|
1240
|
+
const controlName = m[1] ?? '';
|
|
1241
|
+
if (!controlName)
|
|
1242
|
+
continue;
|
|
1243
|
+
this.unresolvedReferences.push({
|
|
1244
|
+
fromNodeId: this.findOrCreateFunctionNodeId(from),
|
|
1245
|
+
referenceName: controlName,
|
|
1246
|
+
referenceKind: 'references',
|
|
1247
|
+
line: lineNum,
|
|
1248
|
+
column: m.index + 3, // +3 to skip the `Me.` prefix
|
|
1249
|
+
filePath: this.filePath,
|
|
1250
|
+
language: 'vba',
|
|
1251
|
+
metadata: { synthesizedBy: 'vba-me-control' },
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
findOrCreateFunctionNodeId(proc) {
|
|
1256
|
+
// Fix 1: key the cache by `name:startLine` so Property Get/Let/Set
|
|
1257
|
+
// accessors with the same name each resolve to their own node.
|
|
1258
|
+
const cacheKey = `${proc.name}:${proc.startLine}`;
|
|
1259
|
+
const cached = this.procNodeIdCache.get(cacheKey);
|
|
1260
|
+
if (cached)
|
|
1261
|
+
return cached;
|
|
1262
|
+
// Prefer the startLine-indexed lookup (O(1), correct for all three
|
|
1263
|
+
// Property accessors). Fall back to the name-indexed cache for procs
|
|
1264
|
+
// whose startLine wasn't recorded (should not happen in practice).
|
|
1265
|
+
const fn = this.functionNodeByStartLine.get(proc.startLine) ??
|
|
1266
|
+
this.findFunctionNodeByName(proc.name);
|
|
1267
|
+
if (fn) {
|
|
1268
|
+
this.procNodeIdCache.set(cacheKey, fn.id);
|
|
1269
|
+
return fn.id;
|
|
1270
|
+
}
|
|
1271
|
+
// Fallback: synthesize a node id matching generateNodeId's input shape.
|
|
1272
|
+
const id = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'function', proc.name, proc.startLine);
|
|
1273
|
+
this.procNodeIdCache.set(cacheKey, id);
|
|
1274
|
+
return id;
|
|
1275
|
+
}
|
|
1276
|
+
findFunctionNodeByName(name) {
|
|
1277
|
+
// O(1) via the cache populated as function nodes are emitted in
|
|
1278
|
+
// `sweepProcedures`. Audit S2 (June 2026): the previous
|
|
1279
|
+
// `this.nodes.find(...)` was an O(n) linear scan per call site —
|
|
1280
|
+
// meaningful on real .cls files with hundreds of procedures.
|
|
1281
|
+
return this.functionNodeByName.get(name);
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* H1: detect a statement-form Sub call.
|
|
1285
|
+
*
|
|
1286
|
+
* Real VBA idioms:
|
|
1287
|
+
* `MySub` — bare no-argument statement call
|
|
1288
|
+
* `MySub arg1, Nz(x, 0)` — bare statement call
|
|
1289
|
+
* `Call MySub arg1, Nz(x, 0)` — Call keyword, no parens around call
|
|
1290
|
+
* `MySub(arg1, arg2)` — parens call (handled by CALL_RE)
|
|
1291
|
+
* `Call MySub(arg1, arg2)` — Call keyword + parens (also CALL_RE)
|
|
1292
|
+
*
|
|
1293
|
+
* Returns the called proc name if the line matches the statement form
|
|
1294
|
+
* AND the proc name is in localProcs (same-file resolution). Returns
|
|
1295
|
+
* null for declarations, assignments, comments, keyword lines, etc.
|
|
1296
|
+
*
|
|
1297
|
+
* The `Call` keyword form is handled by stripping `Call ` and
|
|
1298
|
+
* running the same logic on the remainder — they're structurally the
|
|
1299
|
+
* same call after the keyword.
|
|
1300
|
+
*/
|
|
1301
|
+
detectStatementCall(line) {
|
|
1302
|
+
let trimmed = line.trimStart();
|
|
1303
|
+
if (!trimmed)
|
|
1304
|
+
return null;
|
|
1305
|
+
// Strip `Call ` keyword if present — same call shape after.
|
|
1306
|
+
if (/^Call\s/i.test(trimmed)) {
|
|
1307
|
+
trimmed = trimmed.replace(/^Call\s+/i, '');
|
|
1308
|
+
}
|
|
1309
|
+
// Skip comment lines.
|
|
1310
|
+
if (trimmed.startsWith("'") || trimmed.startsWith('Rem '))
|
|
1311
|
+
return null;
|
|
1312
|
+
// Skip declarations: Dim/Private/Public/Static/Global/Const/ReDim.
|
|
1313
|
+
if (/^(Dim|Private|Public|Static|Global|Const|ReDim)\s/i.test(trimmed))
|
|
1314
|
+
return null;
|
|
1315
|
+
// Extract the leading identifier.
|
|
1316
|
+
const m = /^(\p{L}[\p{L}\p{N}_]*)/u.exec(trimmed);
|
|
1317
|
+
if (!m)
|
|
1318
|
+
return null;
|
|
1319
|
+
const procName = m[1] ?? '';
|
|
1320
|
+
const rest = trimmed.slice(procName.length);
|
|
1321
|
+
// `MySub(...)` is parens-form and already handled by CALL_RE. Parentheses
|
|
1322
|
+
// are valid later in statement-form argument expressions (`MySub Nz(x, 0)`),
|
|
1323
|
+
// so only the character immediately after the proc name distinguishes the
|
|
1324
|
+
// call form.
|
|
1325
|
+
if (rest.startsWith('('))
|
|
1326
|
+
return null;
|
|
1327
|
+
// Bare `MySub` is a valid no-argument statement-form Sub call.
|
|
1328
|
+
if (rest.length === 0)
|
|
1329
|
+
return procName;
|
|
1330
|
+
const nextCh = trimmed.charAt(procName.length);
|
|
1331
|
+
if (nextCh !== ' ' && nextCh !== '\t')
|
|
1332
|
+
return null;
|
|
1333
|
+
const args = rest.trimStart();
|
|
1334
|
+
// Skip leading-identifier assignments (`X = ...`). Do not reject `=` inside
|
|
1335
|
+
// argument expressions because named arguments use `:=` and comparisons can
|
|
1336
|
+
// appear in expressions.
|
|
1337
|
+
if (args.startsWith('='))
|
|
1338
|
+
return null;
|
|
1339
|
+
return procName;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* H1: emit a same-file `calls` edge for a statement-form Sub call.
|
|
1343
|
+
* `caller` is the current procedure (from the stack); `procName` is
|
|
1344
|
+
* the called procedure name. Emits a plain `calls` edge to the
|
|
1345
|
+
* already-emitted function node.
|
|
1346
|
+
*/
|
|
1347
|
+
emitStatementCallEdge(caller, procName, lineNum) {
|
|
1348
|
+
if (procName === caller.name)
|
|
1349
|
+
return; // skip self-call
|
|
1350
|
+
if (VbaExtractor.CALL_KEYWORD_BLACKLIST.has(procName))
|
|
1351
|
+
return;
|
|
1352
|
+
if (VbaExtractor.RUNTIME_RECEIVER_BLACKLIST.has(procName))
|
|
1353
|
+
return;
|
|
1354
|
+
const bucket = this.localProcs.get(procName);
|
|
1355
|
+
if (!bucket || !bucket[0])
|
|
1356
|
+
return;
|
|
1357
|
+
const target = this.findFunctionNodeByName(procName);
|
|
1358
|
+
if (!target)
|
|
1359
|
+
return;
|
|
1360
|
+
this.edges.push({
|
|
1361
|
+
source: this.findOrCreateFunctionNodeId(caller),
|
|
1362
|
+
target: target.id,
|
|
1363
|
+
kind: 'calls',
|
|
1364
|
+
line: lineNum,
|
|
1365
|
+
column: 0,
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Fix 7: detect a qualified statement-form call — `Receiver.Member <args>`
|
|
1370
|
+
* where `Receiver.Member` is NOT followed by `(`.
|
|
1371
|
+
*
|
|
1372
|
+
* Real VBA idioms this covers:
|
|
1373
|
+
* `m_NCOp.Registrar m_ARAlInicio, p_Error`
|
|
1374
|
+
* `m_Obj.Init`
|
|
1375
|
+
*
|
|
1376
|
+
* These are distinct from the paren form (`m_NCOp.Registrar(...)`) which is
|
|
1377
|
+
* already handled by `CALL_RE`. Returns `{receiver, member}` or `null`.
|
|
1378
|
+
*
|
|
1379
|
+
* Property assignments (`Receiver.Prop = value`) are excluded via the `=`
|
|
1380
|
+
* guard — consistent with `detectStatementCall`'s same-file assignment skip.
|
|
1381
|
+
* Blacklisted receivers (runtime objects, keywords) are excluded too.
|
|
1382
|
+
*/
|
|
1383
|
+
detectQualifiedStatementCall(line) {
|
|
1384
|
+
let trimmed = line.trimStart();
|
|
1385
|
+
if (!trimmed)
|
|
1386
|
+
return null;
|
|
1387
|
+
// Strip `Call` keyword — same call shape after it.
|
|
1388
|
+
if (/^Call\s/i.test(trimmed))
|
|
1389
|
+
trimmed = trimmed.replace(/^Call\s+/i, '');
|
|
1390
|
+
// Skip comment lines.
|
|
1391
|
+
if (trimmed.startsWith("'") || /^Rem(\s|$)/i.test(trimmed))
|
|
1392
|
+
return null;
|
|
1393
|
+
// Skip declarations.
|
|
1394
|
+
if (/^(Dim|Private|Public|Static|Global|Const|ReDim)\s/i.test(trimmed))
|
|
1395
|
+
return null;
|
|
1396
|
+
// Extract receiver identifier.
|
|
1397
|
+
const receiverM = /^(\p{L}[\p{L}\p{N}_]*)/u.exec(trimmed);
|
|
1398
|
+
if (!receiverM)
|
|
1399
|
+
return null;
|
|
1400
|
+
const receiver = receiverM[1] ?? '';
|
|
1401
|
+
const rest = trimmed.slice(receiver.length);
|
|
1402
|
+
// Must have a dot separator.
|
|
1403
|
+
if (!rest.startsWith('.'))
|
|
1404
|
+
return null;
|
|
1405
|
+
// Extract member identifier.
|
|
1406
|
+
const memberRest = rest.slice(1); // skip the dot
|
|
1407
|
+
const memberM = /^(\p{L}[\p{L}\p{N}_]*)/u.exec(memberRest);
|
|
1408
|
+
if (!memberM)
|
|
1409
|
+
return null;
|
|
1410
|
+
const member = memberM[1] ?? '';
|
|
1411
|
+
const afterMember = memberRest.slice(member.length);
|
|
1412
|
+
// Must NOT be followed by `(` — the paren form is handled by CALL_RE.
|
|
1413
|
+
if (afterMember.startsWith('('))
|
|
1414
|
+
return null;
|
|
1415
|
+
// Must be followed by space/tab (args present) OR end of line (no args).
|
|
1416
|
+
if (afterMember.length > 0) {
|
|
1417
|
+
const ch = afterMember.charAt(0);
|
|
1418
|
+
if (ch !== ' ' && ch !== '\t')
|
|
1419
|
+
return null;
|
|
1420
|
+
// Skip property assignments: `Receiver.Prop = value`.
|
|
1421
|
+
const argsText = afterMember.trimStart();
|
|
1422
|
+
if (argsText.startsWith('='))
|
|
1423
|
+
return null;
|
|
1424
|
+
}
|
|
1425
|
+
// Respect the keyword and runtime blacklists.
|
|
1426
|
+
if (VbaExtractor.CALL_KEYWORD_BLACKLIST.has(receiver))
|
|
1427
|
+
return null;
|
|
1428
|
+
if (VbaExtractor.RUNTIME_RECEIVER_BLACKLIST.has(receiver))
|
|
1429
|
+
return null;
|
|
1430
|
+
if (VbaExtractor.CALL_KEYWORD_BLACKLIST.has(member))
|
|
1431
|
+
return null;
|
|
1432
|
+
if (VbaExtractor.RUNTIME_RECEIVER_BLACKLIST.has(member))
|
|
1433
|
+
return null;
|
|
1434
|
+
return { receiver, member };
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Fix 7: emit a heuristic `calls` edge for a qualified statement-form call.
|
|
1438
|
+
* Same shape as the qualified-paren path in `scanCallSites` — reuses the
|
|
1439
|
+
* same `callDedupe` / `synthFunctionNodeIds` sets for de-duplication so a
|
|
1440
|
+
* paren and non-paren form on the same line don't create duplicate edges.
|
|
1441
|
+
*/
|
|
1442
|
+
emitQualifiedStatementCallEdge(caller, receiver, member, lineNum) {
|
|
1443
|
+
const qualified = `${receiver}.${member}`;
|
|
1444
|
+
const dedupeKey = `${caller.name}->${qualified}@${lineNum}`;
|
|
1445
|
+
if (this.callDedupe.has(dedupeKey))
|
|
1446
|
+
return;
|
|
1447
|
+
this.callDedupe.add(dedupeKey);
|
|
1448
|
+
const synthId = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'function', qualified, lineNum);
|
|
1449
|
+
if (!this.synthFunctionNodeIds.has(synthId)) {
|
|
1450
|
+
this.synthFunctionNodeIds.add(synthId);
|
|
1451
|
+
this.nodes.push({
|
|
1452
|
+
id: synthId,
|
|
1453
|
+
kind: 'function',
|
|
1454
|
+
name: qualified,
|
|
1455
|
+
qualifiedName: qualified,
|
|
1456
|
+
filePath: this.filePath,
|
|
1457
|
+
language: 'vba',
|
|
1458
|
+
startLine: lineNum,
|
|
1459
|
+
endLine: lineNum,
|
|
1460
|
+
startColumn: 0,
|
|
1461
|
+
endColumn: qualified.length,
|
|
1462
|
+
visibility: 'public',
|
|
1463
|
+
updatedAt: Date.now(),
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
this.edges.push({
|
|
1467
|
+
source: this.findOrCreateFunctionNodeId(caller),
|
|
1468
|
+
target: synthId,
|
|
1469
|
+
kind: 'calls',
|
|
1470
|
+
provenance: 'heuristic',
|
|
1471
|
+
metadata: { synthesizedBy: 'vba-name-resolution' },
|
|
1472
|
+
line: lineNum,
|
|
1473
|
+
column: 0,
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* B4 (hueco 6): scan one line of VBA source for `DoCmd.OpenForm "X"`
|
|
1478
|
+
* calls. For each match, emit:
|
|
1479
|
+
* - a stub `form-layout` node for the target form (cached by name so
|
|
1480
|
+
* the same form referenced from N sites emits exactly ONE stub),
|
|
1481
|
+
* - an `opens-form` heuristic edge from the calling Sub to that stub.
|
|
1482
|
+
*
|
|
1483
|
+
* Both endpoints are pushed into `this.nodes` / `this.edges`, so the
|
|
1484
|
+
* per-file edge filter at `index.ts:insertedIds.has(source) &&
|
|
1485
|
+
* insertedIds.has(target)` passes the edge naturally without any
|
|
1486
|
+
* exemption to the filter.
|
|
1487
|
+
*
|
|
1488
|
+
* Why a stub and not a direct lookup: the target form lives in a
|
|
1489
|
+
* DIFFERENT file (its own `.form.txt`), and the extractor doesn't have
|
|
1490
|
+
* DB access at parse time. The stub's synthetic file path
|
|
1491
|
+
* (`synthetic:opensFormStub/<FormName>.form.txt`) guarantees a
|
|
1492
|
+
* deterministic node id so re-indexes collapse to the same stub.
|
|
1493
|
+
* When the consumer's `.form.txt` is later indexed, the real
|
|
1494
|
+
* `form-layout` node carries a different id (it uses the real file
|
|
1495
|
+
* path); the stub and the real coexist harmlessly. The orchestrator
|
|
1496
|
+
* flagged this as acceptable for B4 — only `OpenForm` is in scope.
|
|
1497
|
+
* `OpenReport`, `OpenQuery`, `OpenTable`, … are follow-up work.
|
|
1498
|
+
*
|
|
1499
|
+
* Scope note: this regex matches ONLY the literal-string form
|
|
1500
|
+
* `DoCmd.OpenForm "X"`. Variable-form calls like
|
|
1501
|
+
* `DoCmd.OpenForm m_FormName` are intentionally NOT captured here
|
|
1502
|
+
* because resolving the variable to a concrete form name would
|
|
1503
|
+
* require data-flow analysis that is out of scope.
|
|
1504
|
+
*/
|
|
1505
|
+
scanOpenFormCalls(line, caller, lineNum) {
|
|
1506
|
+
// Each regex has /g so we MUST reset `lastIndex` before use; cloning
|
|
1507
|
+
// the regex is the simplest way to avoid leaking state across lines.
|
|
1508
|
+
const localRe = new RegExp(VbaExtractor.OPEN_FORM_RE.source, VbaExtractor.OPEN_FORM_RE.flags);
|
|
1509
|
+
let m;
|
|
1510
|
+
while ((m = localRe.exec(line)) !== null) {
|
|
1511
|
+
const targetFormName = (m[1] ?? '').trim();
|
|
1512
|
+
if (!targetFormName)
|
|
1513
|
+
continue;
|
|
1514
|
+
this.emitOpensFormEdge(caller, targetFormName, lineNum, m.index);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* B4 (hueco 6): emit a stub `form-layout` node for `targetFormName`
|
|
1519
|
+
* (cached so duplicates collapse) and an `opens-form` heuristic edge
|
|
1520
|
+
* from `caller` to that stub.
|
|
1521
|
+
*
|
|
1522
|
+
* The edge carries:
|
|
1523
|
+
* - `kind: 'opens-form'` — new cross-file edge kind
|
|
1524
|
+
* - `provenance: 'heuristic'` — synthesized, not parsed
|
|
1525
|
+
* - `metadata.targetFormName` — the captured literal
|
|
1526
|
+
* - `metadata.synthesizedBy: 'vba-opens-form'` — distinguishes this
|
|
1527
|
+
* synthesis from the dim/sql/event-handler families
|
|
1528
|
+
*
|
|
1529
|
+
* The stub's `metadata.stub: true` flag lets downstream UI render
|
|
1530
|
+
* unresolved references distinctly (e.g. with a dashed border) and
|
|
1531
|
+
* gives later re-resolution pass a hook for collapse. The stub is
|
|
1532
|
+
* line-independent (`line = 0`) so re-indexes produce identical ids.
|
|
1533
|
+
*/
|
|
1534
|
+
emitOpensFormEdge(caller, targetFormName, lineNum, column) {
|
|
1535
|
+
const key = targetFormName.toLowerCase();
|
|
1536
|
+
let stubId = this.opensFormStubIdsByName.get(key);
|
|
1537
|
+
if (!stubId) {
|
|
1538
|
+
// Synthetic file path keeps the stub's id deterministic AND
|
|
1539
|
+
// disambiguates it from any real `.form.txt` indexed later.
|
|
1540
|
+
// The directory prefix (`synthetic:opensFormStub/`) is intentionally
|
|
1541
|
+
// not a real filesystem path — it just namespaces the id space.
|
|
1542
|
+
const syntheticFilePath = `synthetic:opensFormStub/${targetFormName}.form.txt`;
|
|
1543
|
+
stubId = (0, tree_sitter_helpers_1.generateNodeId)(syntheticFilePath, 'form-layout', targetFormName, 0);
|
|
1544
|
+
this.opensFormStubIdsByName.set(key, stubId);
|
|
1545
|
+
this.nodes.push({
|
|
1546
|
+
id: stubId,
|
|
1547
|
+
kind: 'form-layout',
|
|
1548
|
+
name: targetFormName,
|
|
1549
|
+
// Convention: form module names in Access are `Form_<Name>`.
|
|
1550
|
+
// We follow the same convention in the synthetic stub's
|
|
1551
|
+
// qualifiedName so cross-file lookups can find it consistently.
|
|
1552
|
+
qualifiedName: `Form_${targetFormName}`,
|
|
1553
|
+
filePath: syntheticFilePath,
|
|
1554
|
+
language: 'vba',
|
|
1555
|
+
startLine: lineNum,
|
|
1556
|
+
endLine: lineNum,
|
|
1557
|
+
startColumn: 0,
|
|
1558
|
+
endColumn: 0,
|
|
1559
|
+
metadata: { stub: true },
|
|
1560
|
+
updatedAt: Date.now(),
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
this.edges.push({
|
|
1564
|
+
source: this.findOrCreateFunctionNodeId(caller),
|
|
1565
|
+
target: stubId,
|
|
1566
|
+
kind: 'opens-form',
|
|
1567
|
+
provenance: 'heuristic',
|
|
1568
|
+
metadata: {
|
|
1569
|
+
synthesizedBy: 'vba-opens-form',
|
|
1570
|
+
targetFormName,
|
|
1571
|
+
},
|
|
1572
|
+
line: lineNum,
|
|
1573
|
+
column,
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
scanSqlInLine(line, lineNum, dedupe, sqlVariables) {
|
|
1577
|
+
for (const { re } of VbaExtractor.SQL_WRAPPERS) {
|
|
1578
|
+
// Each wrapper regex is stateful (has /g); reset before use.
|
|
1579
|
+
const localRe = new RegExp(re.source, re.flags);
|
|
1580
|
+
let m;
|
|
1581
|
+
while ((m = localRe.exec(line)) !== null) {
|
|
1582
|
+
this.emitSqlTableReferences(m[1] ?? '', lineNum, dedupe);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
const localRe = new RegExp(VbaExtractor.SQL_VAR_EXEC_RE.source, VbaExtractor.SQL_VAR_EXEC_RE.flags);
|
|
1586
|
+
let vm;
|
|
1587
|
+
while ((vm = localRe.exec(line)) !== null) {
|
|
1588
|
+
const varName = (vm[1] ?? '').toLowerCase();
|
|
1589
|
+
const sqlString = sqlVariables.get(varName);
|
|
1590
|
+
if (!sqlString)
|
|
1591
|
+
continue;
|
|
1592
|
+
this.emitSqlTableReferences(sqlString, lineNum, dedupe);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
trackSqlVariableAssignment(lines, lineIndex, sqlVariables) {
|
|
1596
|
+
const line = lines[lineIndex] ?? '';
|
|
1597
|
+
const m = VbaExtractor.SQL_VAR_ASSIGN_RE.exec(line);
|
|
1598
|
+
if (!m)
|
|
1599
|
+
return;
|
|
1600
|
+
const varName = (m[1] ?? '').toLowerCase();
|
|
1601
|
+
const sqlText = this.collectStringLiteralText(lines, lineIndex);
|
|
1602
|
+
if (!sqlText)
|
|
1603
|
+
return;
|
|
1604
|
+
sqlVariables.set(varName, sqlText);
|
|
1605
|
+
}
|
|
1606
|
+
collectStringLiteralText(lines, startIndex) {
|
|
1607
|
+
const fragments = [];
|
|
1608
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
1609
|
+
const line = lines[i] ?? '';
|
|
1610
|
+
for (const lit of (0, vba_preprocess_1.extractStringLiterals)(line)) {
|
|
1611
|
+
fragments.push(lit.text);
|
|
1612
|
+
}
|
|
1613
|
+
if (!line.trimEnd().endsWith('&'))
|
|
1614
|
+
break;
|
|
1615
|
+
}
|
|
1616
|
+
return fragments.join(' ');
|
|
1617
|
+
}
|
|
1618
|
+
emitSqlTableReferences(sqlString, lineNum, dedupe) {
|
|
1619
|
+
// Scan the SQL string for FROM/INTO/UPDATE <table>.
|
|
1620
|
+
// Preserve the source regex's `/u` flag (Unicode property classes)
|
|
1621
|
+
// — hardcoding `'gi'` here would silently break non-ASCII identifiers.
|
|
1622
|
+
const tableRe = new RegExp(VbaExtractor.SQL_TABLE_RE.source, VbaExtractor.SQL_TABLE_RE.flags);
|
|
1623
|
+
let tm;
|
|
1624
|
+
while ((tm = tableRe.exec(sqlString)) !== null) {
|
|
1625
|
+
let table = (tm[1] ?? '').replace(/[\[\]]/g, '');
|
|
1626
|
+
if (!table)
|
|
1627
|
+
continue;
|
|
1628
|
+
const key = `${lineNum}:${table}`;
|
|
1629
|
+
if (dedupe.has(key))
|
|
1630
|
+
continue;
|
|
1631
|
+
dedupe.add(key);
|
|
1632
|
+
this.emitReference(table, lineNum, 0, 'vba-sql-table');
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
/**
|
|
1636
|
+
* Emit a `references` edge from the file's module/class node to a synthetic
|
|
1637
|
+
* node named `targetName`. Used by Dim, WithEvents, Implements, and SQL.
|
|
1638
|
+
*/
|
|
1639
|
+
emitReference(targetName, lineNum, column, synthesizedBy) {
|
|
1640
|
+
if (!targetName)
|
|
1641
|
+
return;
|
|
1642
|
+
// Fix 5: key the synthetic node id on (filePath, kind, name) WITHOUT
|
|
1643
|
+
// lineNum so the same type/table referenced on N different lines produces
|
|
1644
|
+
// exactly ONE node. The edge carries the per-site `line`/`column`.
|
|
1645
|
+
const targetId = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, 'class', // placeholder kind; cross-file resolution will re-type at lookup
|
|
1646
|
+
targetName, 0);
|
|
1647
|
+
if (this.synthClassNodeIds.has(targetId)) {
|
|
1648
|
+
// Node already emitted for this name — only add the edge below.
|
|
1649
|
+
}
|
|
1650
|
+
else {
|
|
1651
|
+
this.synthClassNodeIds.add(targetId);
|
|
1652
|
+
this.nodes.push({
|
|
1653
|
+
id: targetId,
|
|
1654
|
+
kind: 'class',
|
|
1655
|
+
name: targetName,
|
|
1656
|
+
qualifiedName: targetName,
|
|
1657
|
+
filePath: this.filePath,
|
|
1658
|
+
language: 'vba',
|
|
1659
|
+
startLine: lineNum,
|
|
1660
|
+
endLine: lineNum,
|
|
1661
|
+
startColumn: column,
|
|
1662
|
+
endColumn: column + targetName.length,
|
|
1663
|
+
updatedAt: Date.now(),
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
const edge = {
|
|
1667
|
+
source: '', // rewritten after module node exists
|
|
1668
|
+
target: targetId,
|
|
1669
|
+
kind: 'references',
|
|
1670
|
+
provenance: 'heuristic',
|
|
1671
|
+
metadata: { synthesizedBy },
|
|
1672
|
+
line: lineNum,
|
|
1673
|
+
column,
|
|
1674
|
+
};
|
|
1675
|
+
this.edges.push(edge);
|
|
1676
|
+
this.pendingModuleOrClassSource.push(edge);
|
|
1677
|
+
}
|
|
1678
|
+
synthClassNodeIds = new Set();
|
|
1679
|
+
/**
|
|
1680
|
+
* Fix 2 (Issue #2): maps `variableName.toLowerCase()` → declared type info.
|
|
1681
|
+
* Built by `sweepDimsAndWithEvents`; consulted by `sweepCallsAndSql` to gate
|
|
1682
|
+
* qualified statement-form calls — only receivers that are file-local variables
|
|
1683
|
+
* typed as a SIMPLE (non-qualified, non-primitive) identifier emit edges.
|
|
1684
|
+
*/
|
|
1685
|
+
localVarTypeMap = new Map();
|
|
1686
|
+
}
|
|
1687
|
+
exports.VbaExtractor = VbaExtractor;
|
|
1688
|
+
/**
|
|
1689
|
+
* Hueco 3 helper: parse an Access event-handler Sub name into its
|
|
1690
|
+
* `<ControlName>_<EventName>` components. Returns null when the name does
|
|
1691
|
+
* not match the convention, when the split yields an empty segment, or
|
|
1692
|
+
* when the control name is `Form` (form-level events are NOT control
|
|
1693
|
+
* handlers — `Form_Load` is the form's own lifecycle event, not a
|
|
1694
|
+
* command-button click).
|
|
1695
|
+
*
|
|
1696
|
+
* Splitting on the LAST underscore (rather than the first) lets
|
|
1697
|
+
* multi-word event names parse correctly: `ComandoAltaPM_BeforeDelConfirm`
|
|
1698
|
+
* yields control=`ComandoAltaPM`, event=`BeforeDelConfirm`, not
|
|
1699
|
+
* control=`ComandoAlta`, event=`PM_BeforeDelConfirm`.
|
|
1700
|
+
*/
|
|
1701
|
+
function parseEventHandlerName(name) {
|
|
1702
|
+
if (!name)
|
|
1703
|
+
return null;
|
|
1704
|
+
const lastUnderscore = name.lastIndexOf('_');
|
|
1705
|
+
if (lastUnderscore <= 0)
|
|
1706
|
+
return null; // no underscore OR starts with underscore
|
|
1707
|
+
const controlName = name.slice(0, lastUnderscore);
|
|
1708
|
+
const eventName = name.slice(lastUnderscore + 1);
|
|
1709
|
+
if (!controlName || !eventName)
|
|
1710
|
+
return null;
|
|
1711
|
+
// Form-level events live on the form, not on a control.
|
|
1712
|
+
if (controlName.toLowerCase() === 'form')
|
|
1713
|
+
return null;
|
|
1714
|
+
return { controlName, eventName };
|
|
1715
|
+
}
|
|
1716
|
+
//# sourceMappingURL=vba-extractor.js.map
|