@opencodehub/ingestion 0.1.0
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/LICENSE +202 -0
- package/README.md +69 -0
- package/dist/extract/index.d.ts +8 -0
- package/dist/extract/index.d.ts.map +1 -0
- package/dist/extract/index.js +6 -0
- package/dist/extract/index.js.map +1 -0
- package/dist/extract/orm-detector.d.ts +19 -0
- package/dist/extract/orm-detector.d.ts.map +1 -0
- package/dist/extract/orm-detector.js +209 -0
- package/dist/extract/orm-detector.js.map +1 -0
- package/dist/extract/property-access.d.ts +76 -0
- package/dist/extract/property-access.d.ts.map +1 -0
- package/dist/extract/property-access.js +260 -0
- package/dist/extract/property-access.js.map +1 -0
- package/dist/extract/receiver-resolver.d.ts +86 -0
- package/dist/extract/receiver-resolver.d.ts.map +1 -0
- package/dist/extract/receiver-resolver.js +77 -0
- package/dist/extract/receiver-resolver.js.map +1 -0
- package/dist/extract/route-detector-java.d.ts +29 -0
- package/dist/extract/route-detector-java.d.ts.map +1 -0
- package/dist/extract/route-detector-java.js +190 -0
- package/dist/extract/route-detector-java.js.map +1 -0
- package/dist/extract/route-detector-nestjs.d.ts +30 -0
- package/dist/extract/route-detector-nestjs.d.ts.map +1 -0
- package/dist/extract/route-detector-nestjs.js +134 -0
- package/dist/extract/route-detector-nestjs.js.map +1 -0
- package/dist/extract/route-detector-python.d.ts +28 -0
- package/dist/extract/route-detector-python.d.ts.map +1 -0
- package/dist/extract/route-detector-python.js +100 -0
- package/dist/extract/route-detector-python.js.map +1 -0
- package/dist/extract/route-detector-rails.d.ts +28 -0
- package/dist/extract/route-detector-rails.d.ts.map +1 -0
- package/dist/extract/route-detector-rails.js +162 -0
- package/dist/extract/route-detector-rails.js.map +1 -0
- package/dist/extract/route-detector.d.ts +45 -0
- package/dist/extract/route-detector.d.ts.map +1 -0
- package/dist/extract/route-detector.js +467 -0
- package/dist/extract/route-detector.js.map +1 -0
- package/dist/extract/tool-detector.d.ts +26 -0
- package/dist/extract/tool-detector.d.ts.map +1 -0
- package/dist/extract/tool-detector.js +364 -0
- package/dist/extract/tool-detector.js.map +1 -0
- package/dist/extract/types.d.ts +89 -0
- package/dist/extract/types.d.ts.map +1 -0
- package/dist/extract/types.js +11 -0
- package/dist/extract/types.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/parse/cobol-regex.d.ts +85 -0
- package/dist/parse/cobol-regex.d.ts.map +1 -0
- package/dist/parse/cobol-regex.js +355 -0
- package/dist/parse/cobol-regex.js.map +1 -0
- package/dist/parse/grammar-registry.d.ts +115 -0
- package/dist/parse/grammar-registry.d.ts.map +1 -0
- package/dist/parse/grammar-registry.js +278 -0
- package/dist/parse/grammar-registry.js.map +1 -0
- package/dist/parse/index.d.ts +14 -0
- package/dist/parse/index.d.ts.map +1 -0
- package/dist/parse/index.js +10 -0
- package/dist/parse/index.js.map +1 -0
- package/dist/parse/language-detector.d.ts +17 -0
- package/dist/parse/language-detector.d.ts.map +1 -0
- package/dist/parse/language-detector.js +104 -0
- package/dist/parse/language-detector.js.map +1 -0
- package/dist/parse/parse-worker.d.ts +24 -0
- package/dist/parse/parse-worker.d.ts.map +1 -0
- package/dist/parse/parse-worker.js +230 -0
- package/dist/parse/parse-worker.js.map +1 -0
- package/dist/parse/types.d.ts +49 -0
- package/dist/parse/types.d.ts.map +1 -0
- package/dist/parse/types.js +11 -0
- package/dist/parse/types.js.map +1 -0
- package/dist/parse/unified-queries.d.ts +37 -0
- package/dist/parse/unified-queries.d.ts.map +1 -0
- package/dist/parse/unified-queries.js +623 -0
- package/dist/parse/unified-queries.js.map +1 -0
- package/dist/parse/wasm-fallback.d.ts +88 -0
- package/dist/parse/wasm-fallback.d.ts.map +1 -0
- package/dist/parse/wasm-fallback.js +258 -0
- package/dist/parse/wasm-fallback.js.map +1 -0
- package/dist/parse/worker-pool.d.ts +48 -0
- package/dist/parse/worker-pool.d.ts.map +1 -0
- package/dist/parse/worker-pool.js +97 -0
- package/dist/parse/worker-pool.js.map +1 -0
- package/dist/pipeline/dep-parsers/go.d.ts +25 -0
- package/dist/pipeline/dep-parsers/go.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/go.js +146 -0
- package/dist/pipeline/dep-parsers/go.js.map +1 -0
- package/dist/pipeline/dep-parsers/index.d.ts +17 -0
- package/dist/pipeline/dep-parsers/index.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/index.js +16 -0
- package/dist/pipeline/dep-parsers/index.js.map +1 -0
- package/dist/pipeline/dep-parsers/maven.d.ts +24 -0
- package/dist/pipeline/dep-parsers/maven.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/maven.js +131 -0
- package/dist/pipeline/dep-parsers/maven.js.map +1 -0
- package/dist/pipeline/dep-parsers/npm.d.ts +30 -0
- package/dist/pipeline/dep-parsers/npm.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/npm.js +309 -0
- package/dist/pipeline/dep-parsers/npm.js.map +1 -0
- package/dist/pipeline/dep-parsers/nuget.d.ts +24 -0
- package/dist/pipeline/dep-parsers/nuget.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/nuget.js +178 -0
- package/dist/pipeline/dep-parsers/nuget.js.map +1 -0
- package/dist/pipeline/dep-parsers/python.d.ts +21 -0
- package/dist/pipeline/dep-parsers/python.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/python.js +369 -0
- package/dist/pipeline/dep-parsers/python.js.map +1 -0
- package/dist/pipeline/dep-parsers/rust.d.ts +18 -0
- package/dist/pipeline/dep-parsers/rust.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/rust.js +134 -0
- package/dist/pipeline/dep-parsers/rust.js.map +1 -0
- package/dist/pipeline/dep-parsers/spdx-normalize.d.ts +15 -0
- package/dist/pipeline/dep-parsers/spdx-normalize.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/spdx-normalize.js +31 -0
- package/dist/pipeline/dep-parsers/spdx-normalize.js.map +1 -0
- package/dist/pipeline/dep-parsers/types.d.ts +63 -0
- package/dist/pipeline/dep-parsers/types.d.ts.map +1 -0
- package/dist/pipeline/dep-parsers/types.js +56 -0
- package/dist/pipeline/dep-parsers/types.js.map +1 -0
- package/dist/pipeline/gitignore-stack.d.ts +44 -0
- package/dist/pipeline/gitignore-stack.d.ts.map +1 -0
- package/dist/pipeline/gitignore-stack.js +69 -0
- package/dist/pipeline/gitignore-stack.js.map +1 -0
- package/dist/pipeline/gitignore.d.ts +67 -0
- package/dist/pipeline/gitignore.d.ts.map +1 -0
- package/dist/pipeline/gitignore.js +210 -0
- package/dist/pipeline/gitignore.js.map +1 -0
- package/dist/pipeline/index.d.ts +53 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +29 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/orchestrator.d.ts +105 -0
- package/dist/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/pipeline/orchestrator.js +175 -0
- package/dist/pipeline/orchestrator.js.map +1 -0
- package/dist/pipeline/ownership-helpers/drift.d.ts +41 -0
- package/dist/pipeline/ownership-helpers/drift.d.ts.map +1 -0
- package/dist/pipeline/ownership-helpers/drift.js +122 -0
- package/dist/pipeline/ownership-helpers/drift.js.map +1 -0
- package/dist/pipeline/ownership-helpers/gini-community.d.ts +24 -0
- package/dist/pipeline/ownership-helpers/gini-community.d.ts.map +1 -0
- package/dist/pipeline/ownership-helpers/gini-community.js +32 -0
- package/dist/pipeline/ownership-helpers/gini-community.js.map +1 -0
- package/dist/pipeline/ownership-helpers/git-blame-batcher.d.ts +71 -0
- package/dist/pipeline/ownership-helpers/git-blame-batcher.d.ts.map +1 -0
- package/dist/pipeline/ownership-helpers/git-blame-batcher.js +178 -0
- package/dist/pipeline/ownership-helpers/git-blame-batcher.js.map +1 -0
- package/dist/pipeline/ownership-helpers/line-overlap.d.ts +35 -0
- package/dist/pipeline/ownership-helpers/line-overlap.d.ts.map +1 -0
- package/dist/pipeline/ownership-helpers/line-overlap.js +62 -0
- package/dist/pipeline/ownership-helpers/line-overlap.js.map +1 -0
- package/dist/pipeline/ownership-helpers/orphan.d.ts +73 -0
- package/dist/pipeline/ownership-helpers/orphan.d.ts.map +1 -0
- package/dist/pipeline/ownership-helpers/orphan.js +117 -0
- package/dist/pipeline/ownership-helpers/orphan.js.map +1 -0
- package/dist/pipeline/phases/accesses.d.ts +44 -0
- package/dist/pipeline/phases/accesses.d.ts.map +1 -0
- package/dist/pipeline/phases/accesses.js +194 -0
- package/dist/pipeline/phases/accesses.js.map +1 -0
- package/dist/pipeline/phases/annotate.d.ts +28 -0
- package/dist/pipeline/phases/annotate.d.ts.map +1 -0
- package/dist/pipeline/phases/annotate.js +60 -0
- package/dist/pipeline/phases/annotate.js.map +1 -0
- package/dist/pipeline/phases/cochange.d.ts +42 -0
- package/dist/pipeline/phases/cochange.d.ts.map +1 -0
- package/dist/pipeline/phases/cochange.js +0 -0
- package/dist/pipeline/phases/cochange.js.map +1 -0
- package/dist/pipeline/phases/communities.d.ts +34 -0
- package/dist/pipeline/phases/communities.d.ts.map +1 -0
- package/dist/pipeline/phases/communities.js +412 -0
- package/dist/pipeline/phases/communities.js.map +1 -0
- package/dist/pipeline/phases/complexity.d.ts +50 -0
- package/dist/pipeline/phases/complexity.d.ts.map +1 -0
- package/dist/pipeline/phases/complexity.js +794 -0
- package/dist/pipeline/phases/complexity.js.map +1 -0
- package/dist/pipeline/phases/confidence-demote.d.ts +23 -0
- package/dist/pipeline/phases/confidence-demote.d.ts.map +1 -0
- package/dist/pipeline/phases/confidence-demote.js +113 -0
- package/dist/pipeline/phases/confidence-demote.js.map +1 -0
- package/dist/pipeline/phases/content-cache.d.ts +166 -0
- package/dist/pipeline/phases/content-cache.d.ts.map +1 -0
- package/dist/pipeline/phases/content-cache.js +323 -0
- package/dist/pipeline/phases/content-cache.js.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/cobertura.d.ts +25 -0
- package/dist/pipeline/phases/coverage-parsers/cobertura.d.ts.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/cobertura.js +139 -0
- package/dist/pipeline/phases/coverage-parsers/cobertura.js.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/coverage-py.d.ts +25 -0
- package/dist/pipeline/phases/coverage-parsers/coverage-py.d.ts.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/coverage-py.js +51 -0
- package/dist/pipeline/phases/coverage-parsers/coverage-py.js.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/jacoco.d.ts +32 -0
- package/dist/pipeline/phases/coverage-parsers/jacoco.d.ts.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/jacoco.js +98 -0
- package/dist/pipeline/phases/coverage-parsers/jacoco.js.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/lcov.d.ts +21 -0
- package/dist/pipeline/phases/coverage-parsers/lcov.d.ts.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/lcov.js +104 -0
- package/dist/pipeline/phases/coverage-parsers/lcov.js.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/types.d.ts +27 -0
- package/dist/pipeline/phases/coverage-parsers/types.d.ts.map +1 -0
- package/dist/pipeline/phases/coverage-parsers/types.js +39 -0
- package/dist/pipeline/phases/coverage-parsers/types.js.map +1 -0
- package/dist/pipeline/phases/coverage.d.ts +39 -0
- package/dist/pipeline/phases/coverage.d.ts.map +1 -0
- package/dist/pipeline/phases/coverage.js +154 -0
- package/dist/pipeline/phases/coverage.js.map +1 -0
- package/dist/pipeline/phases/cross-file.d.ts +40 -0
- package/dist/pipeline/phases/cross-file.d.ts.map +1 -0
- package/dist/pipeline/phases/cross-file.js +411 -0
- package/dist/pipeline/phases/cross-file.js.map +1 -0
- package/dist/pipeline/phases/dead-code.d.ts +28 -0
- package/dist/pipeline/phases/dead-code.d.ts.map +1 -0
- package/dist/pipeline/phases/dead-code.js +157 -0
- package/dist/pipeline/phases/dead-code.js.map +1 -0
- package/dist/pipeline/phases/default-set.d.ts +24 -0
- package/dist/pipeline/phases/default-set.d.ts.map +1 -0
- package/dist/pipeline/phases/default-set.js +133 -0
- package/dist/pipeline/phases/default-set.js.map +1 -0
- package/dist/pipeline/phases/dependencies.d.ts +59 -0
- package/dist/pipeline/phases/dependencies.d.ts.map +1 -0
- package/dist/pipeline/phases/dependencies.js +281 -0
- package/dist/pipeline/phases/dependencies.js.map +1 -0
- package/dist/pipeline/phases/embedder-pool.d.ts +31 -0
- package/dist/pipeline/phases/embedder-pool.d.ts.map +1 -0
- package/dist/pipeline/phases/embedder-pool.js +79 -0
- package/dist/pipeline/phases/embedder-pool.js.map +1 -0
- package/dist/pipeline/phases/embedder-worker.d.ts +28 -0
- package/dist/pipeline/phases/embedder-worker.d.ts.map +1 -0
- package/dist/pipeline/phases/embedder-worker.js +43 -0
- package/dist/pipeline/phases/embedder-worker.js.map +1 -0
- package/dist/pipeline/phases/embeddings.d.ts +117 -0
- package/dist/pipeline/phases/embeddings.d.ts.map +1 -0
- package/dist/pipeline/phases/embeddings.js +697 -0
- package/dist/pipeline/phases/embeddings.js.map +1 -0
- package/dist/pipeline/phases/fetches.d.ts +47 -0
- package/dist/pipeline/phases/fetches.d.ts.map +1 -0
- package/dist/pipeline/phases/fetches.js +207 -0
- package/dist/pipeline/phases/fetches.js.map +1 -0
- package/dist/pipeline/phases/incremental-helper.d.ts +96 -0
- package/dist/pipeline/phases/incremental-helper.d.ts.map +1 -0
- package/dist/pipeline/phases/incremental-helper.js +125 -0
- package/dist/pipeline/phases/incremental-helper.js.map +1 -0
- package/dist/pipeline/phases/incremental-scope.d.ts +67 -0
- package/dist/pipeline/phases/incremental-scope.d.ts.map +1 -0
- package/dist/pipeline/phases/incremental-scope.js +225 -0
- package/dist/pipeline/phases/incremental-scope.js.map +1 -0
- package/dist/pipeline/phases/markdown.d.ts +29 -0
- package/dist/pipeline/phases/markdown.d.ts.map +1 -0
- package/dist/pipeline/phases/markdown.js +298 -0
- package/dist/pipeline/phases/markdown.js.map +1 -0
- package/dist/pipeline/phases/mro.d.ts +24 -0
- package/dist/pipeline/phases/mro.d.ts.map +1 -0
- package/dist/pipeline/phases/mro.js +303 -0
- package/dist/pipeline/phases/mro.js.map +1 -0
- package/dist/pipeline/phases/openapi.d.ts +52 -0
- package/dist/pipeline/phases/openapi.d.ts.map +1 -0
- package/dist/pipeline/phases/openapi.js +285 -0
- package/dist/pipeline/phases/openapi.js.map +1 -0
- package/dist/pipeline/phases/orm.d.ts +26 -0
- package/dist/pipeline/phases/orm.d.ts.map +1 -0
- package/dist/pipeline/phases/orm.js +183 -0
- package/dist/pipeline/phases/orm.js.map +1 -0
- package/dist/pipeline/phases/ownership.d.ts +88 -0
- package/dist/pipeline/phases/ownership.d.ts.map +1 -0
- package/dist/pipeline/phases/ownership.js +479 -0
- package/dist/pipeline/phases/ownership.js.map +1 -0
- package/dist/pipeline/phases/parse.d.ts +63 -0
- package/dist/pipeline/phases/parse.d.ts.map +1 -0
- package/dist/pipeline/phases/parse.js +994 -0
- package/dist/pipeline/phases/parse.js.map +1 -0
- package/dist/pipeline/phases/processes.d.ts +47 -0
- package/dist/pipeline/phases/processes.d.ts.map +1 -0
- package/dist/pipeline/phases/processes.js +620 -0
- package/dist/pipeline/phases/processes.js.map +1 -0
- package/dist/pipeline/phases/profile.d.ts +33 -0
- package/dist/pipeline/phases/profile.d.ts.map +1 -0
- package/dist/pipeline/phases/profile.js +91 -0
- package/dist/pipeline/phases/profile.js.map +1 -0
- package/dist/pipeline/phases/repo-node.d.ts +112 -0
- package/dist/pipeline/phases/repo-node.d.ts.map +1 -0
- package/dist/pipeline/phases/repo-node.js +272 -0
- package/dist/pipeline/phases/repo-node.js.map +1 -0
- package/dist/pipeline/phases/risk-snapshot.d.ts +34 -0
- package/dist/pipeline/phases/risk-snapshot.d.ts.map +1 -0
- package/dist/pipeline/phases/risk-snapshot.js +63 -0
- package/dist/pipeline/phases/risk-snapshot.js.map +1 -0
- package/dist/pipeline/phases/routes.d.ts +31 -0
- package/dist/pipeline/phases/routes.d.ts.map +1 -0
- package/dist/pipeline/phases/routes.js +262 -0
- package/dist/pipeline/phases/routes.js.map +1 -0
- package/dist/pipeline/phases/sbom.d.ts +45 -0
- package/dist/pipeline/phases/sbom.d.ts.map +1 -0
- package/dist/pipeline/phases/sbom.js +289 -0
- package/dist/pipeline/phases/sbom.js.map +1 -0
- package/dist/pipeline/phases/scan.d.ts +54 -0
- package/dist/pipeline/phases/scan.d.ts.map +1 -0
- package/dist/pipeline/phases/scan.js +340 -0
- package/dist/pipeline/phases/scan.js.map +1 -0
- package/dist/pipeline/phases/scip-index.d.ts +54 -0
- package/dist/pipeline/phases/scip-index.d.ts.map +1 -0
- package/dist/pipeline/phases/scip-index.js +469 -0
- package/dist/pipeline/phases/scip-index.js.map +1 -0
- package/dist/pipeline/phases/structure.d.ts +21 -0
- package/dist/pipeline/phases/structure.d.ts.map +1 -0
- package/dist/pipeline/phases/structure.js +115 -0
- package/dist/pipeline/phases/structure.js.map +1 -0
- package/dist/pipeline/phases/summarize.d.ts +126 -0
- package/dist/pipeline/phases/summarize.d.ts.map +1 -0
- package/dist/pipeline/phases/summarize.js +401 -0
- package/dist/pipeline/phases/summarize.js.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/branch-divergence.d.ts +42 -0
- package/dist/pipeline/phases/temporal-helpers/branch-divergence.d.ts.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/branch-divergence.js +96 -0
- package/dist/pipeline/phases/temporal-helpers/branch-divergence.js.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/churn-decay.d.ts +22 -0
- package/dist/pipeline/phases/temporal-helpers/churn-decay.d.ts.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/churn-decay.js +32 -0
- package/dist/pipeline/phases/temporal-helpers/churn-decay.js.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/conventional-commits.d.ts +21 -0
- package/dist/pipeline/phases/temporal-helpers/conventional-commits.d.ts.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/conventional-commits.js +37 -0
- package/dist/pipeline/phases/temporal-helpers/conventional-commits.js.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/gini.d.ts +32 -0
- package/dist/pipeline/phases/temporal-helpers/gini.d.ts.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/gini.js +78 -0
- package/dist/pipeline/phases/temporal-helpers/gini.js.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/revert-detect.d.ts +14 -0
- package/dist/pipeline/phases/temporal-helpers/revert-detect.d.ts.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/revert-detect.js +25 -0
- package/dist/pipeline/phases/temporal-helpers/revert-detect.js.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/test-pair.d.ts +18 -0
- package/dist/pipeline/phases/temporal-helpers/test-pair.d.ts.map +1 -0
- package/dist/pipeline/phases/temporal-helpers/test-pair.js +119 -0
- package/dist/pipeline/phases/temporal-helpers/test-pair.js.map +1 -0
- package/dist/pipeline/phases/temporal.d.ts +65 -0
- package/dist/pipeline/phases/temporal.d.ts.map +1 -0
- package/dist/pipeline/phases/temporal.js +621 -0
- package/dist/pipeline/phases/temporal.js.map +1 -0
- package/dist/pipeline/phases/tools.d.ts +21 -0
- package/dist/pipeline/phases/tools.d.ts.map +1 -0
- package/dist/pipeline/phases/tools.js +118 -0
- package/dist/pipeline/phases/tools.js.map +1 -0
- package/dist/pipeline/profile-detectors/api-contracts.d.ts +18 -0
- package/dist/pipeline/profile-detectors/api-contracts.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/api-contracts.js +78 -0
- package/dist/pipeline/profile-detectors/api-contracts.js.map +1 -0
- package/dist/pipeline/profile-detectors/framework-detector.d.ts +11 -0
- package/dist/pipeline/profile-detectors/framework-detector.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/framework-detector.js +11 -0
- package/dist/pipeline/profile-detectors/framework-detector.js.map +1 -0
- package/dist/pipeline/profile-detectors/frameworks-catalog.d.ts +7 -0
- package/dist/pipeline/profile-detectors/frameworks-catalog.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/frameworks-catalog.js +7 -0
- package/dist/pipeline/profile-detectors/frameworks-catalog.js.map +1 -0
- package/dist/pipeline/profile-detectors/frameworks.d.ts +7 -0
- package/dist/pipeline/profile-detectors/frameworks.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/frameworks.js +7 -0
- package/dist/pipeline/profile-detectors/frameworks.js.map +1 -0
- package/dist/pipeline/profile-detectors/iac.d.ts +22 -0
- package/dist/pipeline/profile-detectors/iac.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/iac.js +97 -0
- package/dist/pipeline/profile-detectors/iac.js.map +1 -0
- package/dist/pipeline/profile-detectors/languages.d.ts +18 -0
- package/dist/pipeline/profile-detectors/languages.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/languages.js +60 -0
- package/dist/pipeline/profile-detectors/languages.js.map +1 -0
- package/dist/pipeline/profile-detectors/manifests.d.ts +7 -0
- package/dist/pipeline/profile-detectors/manifests.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/manifests.js +7 -0
- package/dist/pipeline/profile-detectors/manifests.js.map +1 -0
- package/dist/pipeline/profile-detectors/src-dirs.d.ts +17 -0
- package/dist/pipeline/profile-detectors/src-dirs.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/src-dirs.js +89 -0
- package/dist/pipeline/profile-detectors/src-dirs.js.map +1 -0
- package/dist/pipeline/profile-detectors/variant-detectors.d.ts +7 -0
- package/dist/pipeline/profile-detectors/variant-detectors.d.ts.map +1 -0
- package/dist/pipeline/profile-detectors/variant-detectors.js +7 -0
- package/dist/pipeline/profile-detectors/variant-detectors.js.map +1 -0
- package/dist/pipeline/runner.d.ts +54 -0
- package/dist/pipeline/runner.d.ts.map +1 -0
- package/dist/pipeline/runner.js +247 -0
- package/dist/pipeline/runner.js.map +1 -0
- package/dist/pipeline/types.d.ts +235 -0
- package/dist/pipeline/types.d.ts.map +1 -0
- package/dist/pipeline/types.js +15 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/providers/c.d.ts +3 -0
- package/dist/providers/c.d.ts.map +1 -0
- package/dist/providers/c.js +162 -0
- package/dist/providers/c.js.map +1 -0
- package/dist/providers/cobol.d.ts +19 -0
- package/dist/providers/cobol.d.ts.map +1 -0
- package/dist/providers/cobol.js +44 -0
- package/dist/providers/cobol.js.map +1 -0
- package/dist/providers/cpp.d.ts +3 -0
- package/dist/providers/cpp.d.ts.map +1 -0
- package/dist/providers/cpp.js +200 -0
- package/dist/providers/cpp.js.map +1 -0
- package/dist/providers/csharp.d.ts +3 -0
- package/dist/providers/csharp.d.ts.map +1 -0
- package/dist/providers/csharp.js +292 -0
- package/dist/providers/csharp.js.map +1 -0
- package/dist/providers/dart.d.ts +3 -0
- package/dist/providers/dart.d.ts.map +1 -0
- package/dist/providers/dart.js +214 -0
- package/dist/providers/dart.js.map +1 -0
- package/dist/providers/definition-ids.d.ts +18 -0
- package/dist/providers/definition-ids.d.ts.map +1 -0
- package/dist/providers/definition-ids.js +23 -0
- package/dist/providers/definition-ids.js.map +1 -0
- package/dist/providers/extract-helpers.d.ts +60 -0
- package/dist/providers/extract-helpers.d.ts.map +1 -0
- package/dist/providers/extract-helpers.js +296 -0
- package/dist/providers/extract-helpers.js.map +1 -0
- package/dist/providers/extraction-types.d.ts +85 -0
- package/dist/providers/extraction-types.d.ts.map +1 -0
- package/dist/providers/extraction-types.js +13 -0
- package/dist/providers/extraction-types.js.map +1 -0
- package/dist/providers/go.d.ts +3 -0
- package/dist/providers/go.d.ts.map +1 -0
- package/dist/providers/go.js +359 -0
- package/dist/providers/go.js.map +1 -0
- package/dist/providers/http-detect.d.ts +44 -0
- package/dist/providers/http-detect.d.ts.map +1 -0
- package/dist/providers/http-detect.js +307 -0
- package/dist/providers/http-detect.js.map +1 -0
- package/dist/providers/index.d.ts +38 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +33 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/java.d.ts +3 -0
- package/dist/providers/java.d.ts.map +1 -0
- package/dist/providers/java.js +259 -0
- package/dist/providers/java.js.map +1 -0
- package/dist/providers/javascript.d.ts +3 -0
- package/dist/providers/javascript.d.ts.map +1 -0
- package/dist/providers/javascript.js +139 -0
- package/dist/providers/javascript.js.map +1 -0
- package/dist/providers/kotlin.d.ts +3 -0
- package/dist/providers/kotlin.d.ts.map +1 -0
- package/dist/providers/kotlin.js +175 -0
- package/dist/providers/kotlin.js.map +1 -0
- package/dist/providers/php.d.ts +3 -0
- package/dist/providers/php.d.ts.map +1 -0
- package/dist/providers/php.js +218 -0
- package/dist/providers/php.js.map +1 -0
- package/dist/providers/python-accesses.d.ts +9 -0
- package/dist/providers/python-accesses.d.ts.map +1 -0
- package/dist/providers/python-accesses.js +22 -0
- package/dist/providers/python-accesses.js.map +1 -0
- package/dist/providers/python.d.ts +3 -0
- package/dist/providers/python.d.ts.map +1 -0
- package/dist/providers/python.js +323 -0
- package/dist/providers/python.js.map +1 -0
- package/dist/providers/registry.d.ts +4 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +46 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/resolution/c3.d.ts +6 -0
- package/dist/providers/resolution/c3.d.ts.map +1 -0
- package/dist/providers/resolution/c3.js +76 -0
- package/dist/providers/resolution/c3.js.map +1 -0
- package/dist/providers/resolution/context.d.ts +38 -0
- package/dist/providers/resolution/context.d.ts.map +1 -0
- package/dist/providers/resolution/context.js +45 -0
- package/dist/providers/resolution/context.js.map +1 -0
- package/dist/providers/resolution/first-wins.d.ts +3 -0
- package/dist/providers/resolution/first-wins.d.ts.map +1 -0
- package/dist/providers/resolution/first-wins.js +27 -0
- package/dist/providers/resolution/first-wins.js.map +1 -0
- package/dist/providers/resolution/mro.d.ts +16 -0
- package/dist/providers/resolution/mro.d.ts.map +1 -0
- package/dist/providers/resolution/mro.js +14 -0
- package/dist/providers/resolution/mro.js.map +1 -0
- package/dist/providers/resolution/none.d.ts +3 -0
- package/dist/providers/resolution/none.d.ts.map +1 -0
- package/dist/providers/resolution/none.js +11 -0
- package/dist/providers/resolution/none.js.map +1 -0
- package/dist/providers/resolution/python-all-filter.d.ts +25 -0
- package/dist/providers/resolution/python-all-filter.d.ts.map +1 -0
- package/dist/providers/resolution/python-all-filter.js +64 -0
- package/dist/providers/resolution/python-all-filter.js.map +1 -0
- package/dist/providers/resolution/resolver-strategy.d.ts +42 -0
- package/dist/providers/resolution/resolver-strategy.d.ts.map +1 -0
- package/dist/providers/resolution/resolver-strategy.js +50 -0
- package/dist/providers/resolution/resolver-strategy.js.map +1 -0
- package/dist/providers/resolution/single-inheritance.d.ts +3 -0
- package/dist/providers/resolution/single-inheritance.d.ts.map +1 -0
- package/dist/providers/resolution/single-inheritance.js +21 -0
- package/dist/providers/resolution/single-inheritance.js.map +1 -0
- package/dist/providers/resolution/stack-graphs/__fixtures__/mock-tree.d.ts +16 -0
- package/dist/providers/resolution/stack-graphs/__fixtures__/mock-tree.d.ts.map +1 -0
- package/dist/providers/resolution/stack-graphs/__fixtures__/mock-tree.js +50 -0
- package/dist/providers/resolution/stack-graphs/__fixtures__/mock-tree.js.map +1 -0
- package/dist/providers/resolution/stack-graphs/glue.d.ts +15 -0
- package/dist/providers/resolution/stack-graphs/glue.d.ts.map +1 -0
- package/dist/providers/resolution/stack-graphs/glue.js +44 -0
- package/dist/providers/resolution/stack-graphs/glue.js.map +1 -0
- package/dist/providers/resolution/stack-graphs/node-edge-builder.d.ts +30 -0
- package/dist/providers/resolution/stack-graphs/node-edge-builder.d.ts.map +1 -0
- package/dist/providers/resolution/stack-graphs/node-edge-builder.js +366 -0
- package/dist/providers/resolution/stack-graphs/node-edge-builder.js.map +1 -0
- package/dist/providers/resolution/stack-graphs/partial-path-engine.d.ts +9 -0
- package/dist/providers/resolution/stack-graphs/partial-path-engine.d.ts.map +1 -0
- package/dist/providers/resolution/stack-graphs/partial-path-engine.js +152 -0
- package/dist/providers/resolution/stack-graphs/partial-path-engine.js.map +1 -0
- package/dist/providers/resolution/stack-graphs/rule-parser.d.ts +11 -0
- package/dist/providers/resolution/stack-graphs/rule-parser.d.ts.map +1 -0
- package/dist/providers/resolution/stack-graphs/rule-parser.js +247 -0
- package/dist/providers/resolution/stack-graphs/rule-parser.js.map +1 -0
- package/dist/providers/resolution/stack-graphs/types.d.ts +93 -0
- package/dist/providers/resolution/stack-graphs/types.d.ts.map +1 -0
- package/dist/providers/resolution/stack-graphs/types.js +11 -0
- package/dist/providers/resolution/stack-graphs/types.js.map +1 -0
- package/dist/providers/resolution/stack-graphs-python.d.ts +27 -0
- package/dist/providers/resolution/stack-graphs-python.d.ts.map +1 -0
- package/dist/providers/resolution/stack-graphs-python.js +104 -0
- package/dist/providers/resolution/stack-graphs-python.js.map +1 -0
- package/dist/providers/resolution/stack-graphs-ts.d.ts +134 -0
- package/dist/providers/resolution/stack-graphs-ts.d.ts.map +1 -0
- package/dist/providers/resolution/stack-graphs-ts.js +372 -0
- package/dist/providers/resolution/stack-graphs-ts.js.map +1 -0
- package/dist/providers/ruby.d.ts +3 -0
- package/dist/providers/ruby.d.ts.map +1 -0
- package/dist/providers/ruby.js +259 -0
- package/dist/providers/ruby.js.map +1 -0
- package/dist/providers/rust.d.ts +3 -0
- package/dist/providers/rust.d.ts.map +1 -0
- package/dist/providers/rust.js +318 -0
- package/dist/providers/rust.js.map +1 -0
- package/dist/providers/swift.d.ts +3 -0
- package/dist/providers/swift.d.ts.map +1 -0
- package/dist/providers/swift.js +177 -0
- package/dist/providers/swift.js.map +1 -0
- package/dist/providers/test-helpers.d.ts +24 -0
- package/dist/providers/test-helpers.d.ts.map +1 -0
- package/dist/providers/test-helpers.js +33 -0
- package/dist/providers/test-helpers.js.map +1 -0
- package/dist/providers/ts-shared.d.ts +30 -0
- package/dist/providers/ts-shared.d.ts.map +1 -0
- package/dist/providers/ts-shared.js +328 -0
- package/dist/providers/ts-shared.js.map +1 -0
- package/dist/providers/tsx.d.ts +7 -0
- package/dist/providers/tsx.d.ts.map +1 -0
- package/dist/providers/tsx.js +79 -0
- package/dist/providers/tsx.js.map +1 -0
- package/dist/providers/types.d.ts +166 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +7 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/providers/typescript-family-accesses.d.ts +14 -0
- package/dist/providers/typescript-family-accesses.d.ts.map +1 -0
- package/dist/providers/typescript-family-accesses.js +27 -0
- package/dist/providers/typescript-family-accesses.js.map +1 -0
- package/dist/providers/typescript.d.ts +9 -0
- package/dist/providers/typescript.d.ts.map +1 -0
- package/dist/providers/typescript.js +84 -0
- package/dist/providers/typescript.js.map +1 -0
- package/package.json +108 -0
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Complexity phase — annotate Function / Method / Constructor nodes with
|
|
3
|
+
* cyclomatic complexity, maximum nesting depth, NLOC, and Halstead volume.
|
|
4
|
+
*
|
|
5
|
+
* Runs after `parse` (it needs the definitions and their line ranges) and
|
|
6
|
+
* before any phase that depends on complexity-derived signals (none yet at
|
|
7
|
+
* MVP, but e.g. risk scoring will). The phase re-parses each source file
|
|
8
|
+
* once with the same grammar used by `parse`, then walks the subtree of each
|
|
9
|
+
* callable definition to compute:
|
|
10
|
+
*
|
|
11
|
+
* - `cyclomaticComplexity`: 1 + the number of decision points in the body
|
|
12
|
+
* (branches, loops, short-circuit boolean operators, catch clauses, etc.
|
|
13
|
+
* — per-language lists below).
|
|
14
|
+
* - `nestingDepth`: maximum depth of nested block/statement nodes inside
|
|
15
|
+
* the body. An unnested body reports 0.
|
|
16
|
+
* - `nloc`: count of non-blank, non-comment-only physical lines between
|
|
17
|
+
* the definition's `startLine` and `endLine` (inclusive).
|
|
18
|
+
* - `halsteadVolume`: (N1+N2) * log2(n1+n2) Halstead volume computed from
|
|
19
|
+
* leaf-token operator vs operand counts. Requires the provider to
|
|
20
|
+
* declare a `halsteadOperatorKinds` list; omitted when absent.
|
|
21
|
+
*
|
|
22
|
+
* The phase mutates the shared {@link KnowledgeGraph} by re-adding each
|
|
23
|
+
* callable node with the extra fields set; {@link KnowledgeGraph.addNode}
|
|
24
|
+
* keeps the entry with more defined fields, so the annotated version wins.
|
|
25
|
+
*
|
|
26
|
+
* Determinism:
|
|
27
|
+
* - Files are iterated in sorted order.
|
|
28
|
+
* - Within each file, definitions are iterated in (startLine, qualifiedName)
|
|
29
|
+
* order — identical to the tiebreak the parse phase uses.
|
|
30
|
+
* - Tree-sitter cursor walks are deterministic per grammar.
|
|
31
|
+
*
|
|
32
|
+
* Robustness:
|
|
33
|
+
* - A missing file, empty body, or re-parse error increments `skipped`
|
|
34
|
+
* rather than throwing.
|
|
35
|
+
* - Providers without a `complexityDefinitionKinds` table cause that
|
|
36
|
+
* language's callables to be skipped with a single debug note — no
|
|
37
|
+
* throw.
|
|
38
|
+
*/
|
|
39
|
+
import { promises as fs } from "node:fs";
|
|
40
|
+
import { createRequire } from "node:module";
|
|
41
|
+
import { loadGrammar } from "../../parse/grammar-registry.js";
|
|
42
|
+
import { getProvider } from "../../providers/registry.js";
|
|
43
|
+
import { PARSE_PHASE_NAME } from "./parse.js";
|
|
44
|
+
import { SCAN_PHASE_NAME } from "./scan.js";
|
|
45
|
+
export const COMPLEXITY_PHASE_NAME = "complexity";
|
|
46
|
+
export const complexityPhase = {
|
|
47
|
+
name: COMPLEXITY_PHASE_NAME,
|
|
48
|
+
deps: [PARSE_PHASE_NAME, SCAN_PHASE_NAME],
|
|
49
|
+
async run(ctx, deps) {
|
|
50
|
+
const parse = deps.get(PARSE_PHASE_NAME);
|
|
51
|
+
const scan = deps.get(SCAN_PHASE_NAME);
|
|
52
|
+
if (parse === undefined) {
|
|
53
|
+
throw new Error("complexity: parse output missing from dependency map");
|
|
54
|
+
}
|
|
55
|
+
if (scan === undefined) {
|
|
56
|
+
throw new Error("complexity: scan output missing from dependency map");
|
|
57
|
+
}
|
|
58
|
+
return runComplexity(ctx, parse, scan);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
// -------- module-local tree-sitter shim (main-thread, one parser per lang) --
|
|
62
|
+
const requireFn = createRequire(import.meta.url);
|
|
63
|
+
const parserCache = new Map();
|
|
64
|
+
let tsModuleCached;
|
|
65
|
+
let warnedComplexityDegraded = false;
|
|
66
|
+
function getTsModule() {
|
|
67
|
+
if (tsModuleCached !== undefined)
|
|
68
|
+
return tsModuleCached;
|
|
69
|
+
try {
|
|
70
|
+
tsModuleCached = requireFn("tree-sitter");
|
|
71
|
+
return tsModuleCached;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
if (!warnedComplexityDegraded) {
|
|
75
|
+
warnedComplexityDegraded = true;
|
|
76
|
+
process.stderr.write("[complexity] tree-sitter unavailable — complexity metrics degraded (set OCH_NATIVE_PARSER=1 on Node 22 to enable)\n");
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function getParser(lang) {
|
|
82
|
+
const cached = parserCache.get(lang);
|
|
83
|
+
if (cached !== undefined)
|
|
84
|
+
return cached;
|
|
85
|
+
const TS = getTsModule();
|
|
86
|
+
if (TS === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
const handle = await loadGrammar(lang);
|
|
89
|
+
const parser = new TS();
|
|
90
|
+
parser.setLanguage(handle.tsLanguage);
|
|
91
|
+
parserCache.set(lang, parser);
|
|
92
|
+
return parser;
|
|
93
|
+
}
|
|
94
|
+
// -------- decision-point + nesting tables ----------------------------------
|
|
95
|
+
/**
|
|
96
|
+
* Decision-point node-type sets. Each entry is the raw tree-sitter `type`
|
|
97
|
+
* string that contributes +1 to cyclomatic complexity inside a function body.
|
|
98
|
+
*
|
|
99
|
+
* Sourced from the grammars used by `@opencodehub/ingestion`. Where a node
|
|
100
|
+
* type represents a boolean short-circuit (`&&`, `||`), we further gate on
|
|
101
|
+
* operator text at walk time to avoid counting arithmetic or bitwise
|
|
102
|
+
* expressions that share the parent node kind.
|
|
103
|
+
*/
|
|
104
|
+
const DECISION_NODE_TYPES = {
|
|
105
|
+
typescript: new Set([
|
|
106
|
+
"if_statement",
|
|
107
|
+
"while_statement",
|
|
108
|
+
"do_statement",
|
|
109
|
+
"for_statement",
|
|
110
|
+
"for_in_statement",
|
|
111
|
+
"case_clause",
|
|
112
|
+
"catch_clause",
|
|
113
|
+
"ternary_expression",
|
|
114
|
+
"binary_expression",
|
|
115
|
+
]),
|
|
116
|
+
tsx: new Set([
|
|
117
|
+
"if_statement",
|
|
118
|
+
"while_statement",
|
|
119
|
+
"do_statement",
|
|
120
|
+
"for_statement",
|
|
121
|
+
"for_in_statement",
|
|
122
|
+
"case_clause",
|
|
123
|
+
"catch_clause",
|
|
124
|
+
"ternary_expression",
|
|
125
|
+
"binary_expression",
|
|
126
|
+
]),
|
|
127
|
+
javascript: new Set([
|
|
128
|
+
"if_statement",
|
|
129
|
+
"while_statement",
|
|
130
|
+
"do_statement",
|
|
131
|
+
"for_statement",
|
|
132
|
+
"for_in_statement",
|
|
133
|
+
"case_clause",
|
|
134
|
+
"catch_clause",
|
|
135
|
+
"ternary_expression",
|
|
136
|
+
"binary_expression",
|
|
137
|
+
]),
|
|
138
|
+
python: new Set([
|
|
139
|
+
"if_statement",
|
|
140
|
+
"elif_clause",
|
|
141
|
+
"while_statement",
|
|
142
|
+
"for_statement",
|
|
143
|
+
"except_clause",
|
|
144
|
+
"boolean_operator",
|
|
145
|
+
"conditional_expression",
|
|
146
|
+
"match_statement",
|
|
147
|
+
"case_clause",
|
|
148
|
+
]),
|
|
149
|
+
go: new Set([
|
|
150
|
+
"if_statement",
|
|
151
|
+
"for_statement",
|
|
152
|
+
"expression_case",
|
|
153
|
+
"type_case",
|
|
154
|
+
"communication_case",
|
|
155
|
+
"binary_expression",
|
|
156
|
+
]),
|
|
157
|
+
rust: new Set([
|
|
158
|
+
"if_expression",
|
|
159
|
+
"while_expression",
|
|
160
|
+
"for_expression",
|
|
161
|
+
"while_let_expression",
|
|
162
|
+
"loop_expression",
|
|
163
|
+
"match_arm",
|
|
164
|
+
"binary_expression",
|
|
165
|
+
"try_expression",
|
|
166
|
+
]),
|
|
167
|
+
java: new Set([
|
|
168
|
+
"if_statement",
|
|
169
|
+
"while_statement",
|
|
170
|
+
"do_statement",
|
|
171
|
+
"for_statement",
|
|
172
|
+
"enhanced_for_statement",
|
|
173
|
+
"switch_label",
|
|
174
|
+
"catch_clause",
|
|
175
|
+
"ternary_expression",
|
|
176
|
+
"binary_expression",
|
|
177
|
+
]),
|
|
178
|
+
csharp: new Set([
|
|
179
|
+
"if_statement",
|
|
180
|
+
"while_statement",
|
|
181
|
+
"do_statement",
|
|
182
|
+
"for_statement",
|
|
183
|
+
"for_each_statement",
|
|
184
|
+
"case_switch_label",
|
|
185
|
+
"catch_clause",
|
|
186
|
+
"conditional_expression",
|
|
187
|
+
"binary_expression",
|
|
188
|
+
"switch_expression_arm",
|
|
189
|
+
]),
|
|
190
|
+
};
|
|
191
|
+
/** Node types whose presence increases nesting depth by 1. */
|
|
192
|
+
const NESTING_NODE_TYPES = {
|
|
193
|
+
typescript: new Set([
|
|
194
|
+
"if_statement",
|
|
195
|
+
"while_statement",
|
|
196
|
+
"do_statement",
|
|
197
|
+
"for_statement",
|
|
198
|
+
"for_in_statement",
|
|
199
|
+
"switch_statement",
|
|
200
|
+
"try_statement",
|
|
201
|
+
"catch_clause",
|
|
202
|
+
]),
|
|
203
|
+
tsx: new Set([
|
|
204
|
+
"if_statement",
|
|
205
|
+
"while_statement",
|
|
206
|
+
"do_statement",
|
|
207
|
+
"for_statement",
|
|
208
|
+
"for_in_statement",
|
|
209
|
+
"switch_statement",
|
|
210
|
+
"try_statement",
|
|
211
|
+
"catch_clause",
|
|
212
|
+
]),
|
|
213
|
+
javascript: new Set([
|
|
214
|
+
"if_statement",
|
|
215
|
+
"while_statement",
|
|
216
|
+
"do_statement",
|
|
217
|
+
"for_statement",
|
|
218
|
+
"for_in_statement",
|
|
219
|
+
"switch_statement",
|
|
220
|
+
"try_statement",
|
|
221
|
+
"catch_clause",
|
|
222
|
+
]),
|
|
223
|
+
python: new Set([
|
|
224
|
+
"if_statement",
|
|
225
|
+
"elif_clause",
|
|
226
|
+
"else_clause",
|
|
227
|
+
"while_statement",
|
|
228
|
+
"for_statement",
|
|
229
|
+
"try_statement",
|
|
230
|
+
"except_clause",
|
|
231
|
+
"with_statement",
|
|
232
|
+
"match_statement",
|
|
233
|
+
]),
|
|
234
|
+
go: new Set([
|
|
235
|
+
"if_statement",
|
|
236
|
+
"for_statement",
|
|
237
|
+
"expression_switch_statement",
|
|
238
|
+
"type_switch_statement",
|
|
239
|
+
"select_statement",
|
|
240
|
+
]),
|
|
241
|
+
rust: new Set([
|
|
242
|
+
"if_expression",
|
|
243
|
+
"while_expression",
|
|
244
|
+
"while_let_expression",
|
|
245
|
+
"for_expression",
|
|
246
|
+
"loop_expression",
|
|
247
|
+
"match_expression",
|
|
248
|
+
"match_arm",
|
|
249
|
+
]),
|
|
250
|
+
java: new Set([
|
|
251
|
+
"if_statement",
|
|
252
|
+
"while_statement",
|
|
253
|
+
"do_statement",
|
|
254
|
+
"for_statement",
|
|
255
|
+
"enhanced_for_statement",
|
|
256
|
+
"switch_statement",
|
|
257
|
+
"try_statement",
|
|
258
|
+
"catch_clause",
|
|
259
|
+
]),
|
|
260
|
+
csharp: new Set([
|
|
261
|
+
"if_statement",
|
|
262
|
+
"while_statement",
|
|
263
|
+
"do_statement",
|
|
264
|
+
"for_statement",
|
|
265
|
+
"for_each_statement",
|
|
266
|
+
"switch_statement",
|
|
267
|
+
"try_statement",
|
|
268
|
+
"catch_clause",
|
|
269
|
+
]),
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Per-language definition-node lookup. Populated lazily from each
|
|
273
|
+
* provider's {@link LanguageProvider.complexityDefinitionKinds}. Missing
|
|
274
|
+
* providers return `undefined`; the phase skips those languages with a
|
|
275
|
+
* single debug note per language.
|
|
276
|
+
*/
|
|
277
|
+
const definitionTypeCache = new Map();
|
|
278
|
+
function definitionTypesFor(lang) {
|
|
279
|
+
if (definitionTypeCache.has(lang))
|
|
280
|
+
return definitionTypeCache.get(lang);
|
|
281
|
+
let provider;
|
|
282
|
+
try {
|
|
283
|
+
provider = getProvider(lang);
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
provider = undefined;
|
|
287
|
+
}
|
|
288
|
+
const kinds = provider?.complexityDefinitionKinds;
|
|
289
|
+
const set = kinds !== undefined && kinds.length > 0 ? new Set(kinds) : undefined;
|
|
290
|
+
definitionTypeCache.set(lang, set);
|
|
291
|
+
return set;
|
|
292
|
+
}
|
|
293
|
+
/** Per-language Halstead operator lookup — same semantics as above. */
|
|
294
|
+
const halsteadOperatorCache = new Map();
|
|
295
|
+
function halsteadOperatorsFor(lang) {
|
|
296
|
+
if (halsteadOperatorCache.has(lang))
|
|
297
|
+
return halsteadOperatorCache.get(lang);
|
|
298
|
+
let provider;
|
|
299
|
+
try {
|
|
300
|
+
provider = getProvider(lang);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
provider = undefined;
|
|
304
|
+
}
|
|
305
|
+
const kinds = provider?.halsteadOperatorKinds;
|
|
306
|
+
const set = kinds !== undefined && kinds.length > 0 ? new Set(kinds) : undefined;
|
|
307
|
+
halsteadOperatorCache.set(lang, set);
|
|
308
|
+
return set;
|
|
309
|
+
}
|
|
310
|
+
/** Single-line comment prefixes per language. Used by NLOC. */
|
|
311
|
+
const LINE_COMMENT_PREFIX = {
|
|
312
|
+
typescript: ["//"],
|
|
313
|
+
tsx: ["//"],
|
|
314
|
+
javascript: ["//"],
|
|
315
|
+
python: ["#"],
|
|
316
|
+
go: ["//"],
|
|
317
|
+
rust: ["//"],
|
|
318
|
+
java: ["//"],
|
|
319
|
+
csharp: ["//"],
|
|
320
|
+
};
|
|
321
|
+
// -------- traversal primitives ---------------------------------------------
|
|
322
|
+
/** Pre-order iterator over a tree-sitter subtree. */
|
|
323
|
+
function* walk(node) {
|
|
324
|
+
yield node;
|
|
325
|
+
const n = node.childCount;
|
|
326
|
+
for (let i = 0; i < n; i++) {
|
|
327
|
+
const child = node.child(i);
|
|
328
|
+
if (child !== null)
|
|
329
|
+
yield* walk(child);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function countDecisionsIn(body, lang) {
|
|
333
|
+
const decisions = DECISION_NODE_TYPES[lang];
|
|
334
|
+
const definitions = definitionTypesFor(lang);
|
|
335
|
+
if (decisions === undefined || definitions === undefined)
|
|
336
|
+
return 0;
|
|
337
|
+
let count = 0;
|
|
338
|
+
const stack = [{ node: body, skip: false }];
|
|
339
|
+
while (stack.length > 0) {
|
|
340
|
+
const frame = stack.pop();
|
|
341
|
+
if (frame === undefined)
|
|
342
|
+
break;
|
|
343
|
+
const { node, skip } = frame;
|
|
344
|
+
if (!skip && decisions.has(node.type)) {
|
|
345
|
+
if (contributesToCyclomatic(node, lang)) {
|
|
346
|
+
count += 1;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Do NOT descend into nested function/method/constructor definitions —
|
|
350
|
+
// their complexity belongs to their own node, not to ours.
|
|
351
|
+
const enteringNested = node !== body && definitions.has(node.type);
|
|
352
|
+
const nChildren = node.childCount;
|
|
353
|
+
for (let i = nChildren - 1; i >= 0; i--) {
|
|
354
|
+
const child = node.child(i);
|
|
355
|
+
if (child !== null) {
|
|
356
|
+
stack.push({ node: child, skip: enteringNested });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return count;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Only short-circuit boolean operators contribute to cyclomatic complexity.
|
|
364
|
+
* Tree-sitter exposes the operator text on a child node for `binary_expression`
|
|
365
|
+
* in the TS/JS/Go/Rust/Java/C# grammars; `boolean_operator` (Python) is
|
|
366
|
+
* structurally already boolean so every instance counts.
|
|
367
|
+
*/
|
|
368
|
+
function contributesToCyclomatic(node, lang) {
|
|
369
|
+
if (node.type !== "binary_expression")
|
|
370
|
+
return true;
|
|
371
|
+
// Find operator child; child(1) is typically the operator token.
|
|
372
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
373
|
+
const c = node.child(i);
|
|
374
|
+
if (c === null)
|
|
375
|
+
continue;
|
|
376
|
+
const t = c.type;
|
|
377
|
+
if (t === "&&" || t === "||")
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
// Rust uses the same `binary_expression` node for `&&` / `||`; the check
|
|
381
|
+
// above also covers it. Go's `&&`/`||` are tokens named by their literal.
|
|
382
|
+
void lang;
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
function maxNestingIn(body, lang) {
|
|
386
|
+
const nesting = NESTING_NODE_TYPES[lang];
|
|
387
|
+
const definitions = definitionTypesFor(lang);
|
|
388
|
+
if (nesting === undefined || definitions === undefined)
|
|
389
|
+
return 0;
|
|
390
|
+
let best = 0;
|
|
391
|
+
// Recursive walker with an explicit stack to avoid stack-overflow on huge
|
|
392
|
+
// functions. Entries: (node, currentDepth, skipSubtree).
|
|
393
|
+
const stack = [
|
|
394
|
+
{ node: body, depth: 0, skip: false },
|
|
395
|
+
];
|
|
396
|
+
while (stack.length > 0) {
|
|
397
|
+
const frame = stack.pop();
|
|
398
|
+
if (frame === undefined)
|
|
399
|
+
break;
|
|
400
|
+
const { node, depth, skip } = frame;
|
|
401
|
+
let nextDepth = depth;
|
|
402
|
+
if (!skip && node !== body && nesting.has(node.type)) {
|
|
403
|
+
nextDepth = depth + 1;
|
|
404
|
+
if (nextDepth > best)
|
|
405
|
+
best = nextDepth;
|
|
406
|
+
}
|
|
407
|
+
const enteringNested = node !== body && definitions.has(node.type);
|
|
408
|
+
for (let i = node.childCount - 1; i >= 0; i--) {
|
|
409
|
+
const child = node.child(i);
|
|
410
|
+
if (child !== null) {
|
|
411
|
+
stack.push({ node: child, depth: nextDepth, skip: enteringNested });
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return best;
|
|
416
|
+
}
|
|
417
|
+
// -------- NLOC --------------------------------------------------------------
|
|
418
|
+
function computeNloc(sourceText, startLine1, endLine1, lang) {
|
|
419
|
+
const lines = sourceText.split("\n");
|
|
420
|
+
const from = Math.max(0, startLine1 - 1);
|
|
421
|
+
const to = Math.min(lines.length - 1, endLine1 - 1);
|
|
422
|
+
const prefixes = LINE_COMMENT_PREFIX[lang] ?? [];
|
|
423
|
+
let count = 0;
|
|
424
|
+
let inPythonDocstring = false;
|
|
425
|
+
for (let i = from; i <= to; i++) {
|
|
426
|
+
const raw = lines[i] ?? "";
|
|
427
|
+
const trimmed = raw.trim();
|
|
428
|
+
if (trimmed.length === 0)
|
|
429
|
+
continue;
|
|
430
|
+
// Python: skip lines that are entirely a triple-quoted docstring range.
|
|
431
|
+
// We detect a simple toggle on `"""` or `'''`.
|
|
432
|
+
if (lang === "python") {
|
|
433
|
+
const opensClose = countTripleQuotes(trimmed);
|
|
434
|
+
if (inPythonDocstring) {
|
|
435
|
+
if (opensClose % 2 === 1)
|
|
436
|
+
inPythonDocstring = false;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if ((trimmed.startsWith('"""') || trimmed.startsWith("'''")) && opensClose % 2 === 1) {
|
|
440
|
+
inPythonDocstring = true;
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if ((trimmed.startsWith('"""') && trimmed.endsWith('"""') && trimmed.length > 3) ||
|
|
444
|
+
(trimmed.startsWith("'''") && trimmed.endsWith("'''") && trimmed.length > 3)) {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
let isCommentOnly = false;
|
|
449
|
+
for (const prefix of prefixes) {
|
|
450
|
+
if (trimmed.startsWith(prefix)) {
|
|
451
|
+
isCommentOnly = true;
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (isCommentOnly)
|
|
456
|
+
continue;
|
|
457
|
+
count += 1;
|
|
458
|
+
}
|
|
459
|
+
return count;
|
|
460
|
+
}
|
|
461
|
+
function countTripleQuotes(s) {
|
|
462
|
+
let n = 0;
|
|
463
|
+
for (let i = 0; i + 2 < s.length + 1; i++) {
|
|
464
|
+
if (s.startsWith('"""', i) || s.startsWith("'''", i)) {
|
|
465
|
+
n += 1;
|
|
466
|
+
i += 2;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return n;
|
|
470
|
+
}
|
|
471
|
+
// -------- main driver ------------------------------------------------------
|
|
472
|
+
const CALLABLE_KINDS = new Set([
|
|
473
|
+
"Function",
|
|
474
|
+
"Method",
|
|
475
|
+
"Constructor",
|
|
476
|
+
]);
|
|
477
|
+
async function runComplexity(ctx, parse, scan) {
|
|
478
|
+
const absByRel = new Map();
|
|
479
|
+
const langByRel = new Map();
|
|
480
|
+
for (const f of scan.files) {
|
|
481
|
+
if (f.language === undefined)
|
|
482
|
+
continue;
|
|
483
|
+
absByRel.set(f.relPath, f.absPath);
|
|
484
|
+
langByRel.set(f.relPath, f.language);
|
|
485
|
+
}
|
|
486
|
+
let annotated = 0;
|
|
487
|
+
let skipped = 0;
|
|
488
|
+
// Sorted file traversal for determinism.
|
|
489
|
+
const files = [...parse.definitionsByFile.keys()].sort();
|
|
490
|
+
for (const filePath of files) {
|
|
491
|
+
const defs = parse.definitionsByFile.get(filePath) ?? [];
|
|
492
|
+
const callableDefs = defs.filter((d) => CALLABLE_KINDS.has(d.kind));
|
|
493
|
+
if (callableDefs.length === 0)
|
|
494
|
+
continue;
|
|
495
|
+
const lang = langByRel.get(filePath);
|
|
496
|
+
const abs = absByRel.get(filePath);
|
|
497
|
+
if (lang === undefined || abs === undefined) {
|
|
498
|
+
skipped += callableDefs.length;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const parser = await getParser(lang);
|
|
502
|
+
if (parser === undefined) {
|
|
503
|
+
skipped += callableDefs.length;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
let sourceText;
|
|
507
|
+
try {
|
|
508
|
+
sourceText = await fs.readFile(abs, "utf8");
|
|
509
|
+
}
|
|
510
|
+
catch (err) {
|
|
511
|
+
ctx.onProgress?.({
|
|
512
|
+
phase: COMPLEXITY_PHASE_NAME,
|
|
513
|
+
kind: "warn",
|
|
514
|
+
message: `complexity: cannot read ${filePath}: ${err.message}`,
|
|
515
|
+
});
|
|
516
|
+
skipped += callableDefs.length;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
let tree;
|
|
520
|
+
try {
|
|
521
|
+
tree = parser.parse(sourceText);
|
|
522
|
+
}
|
|
523
|
+
catch (err) {
|
|
524
|
+
ctx.onProgress?.({
|
|
525
|
+
phase: COMPLEXITY_PHASE_NAME,
|
|
526
|
+
kind: "warn",
|
|
527
|
+
message: `complexity: parse failed for ${filePath}: ${err.message}`,
|
|
528
|
+
});
|
|
529
|
+
skipped += callableDefs.length;
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
const defTypesForLang = definitionTypesFor(lang);
|
|
533
|
+
if (defTypesForLang === undefined) {
|
|
534
|
+
// Provider did not declare `complexityDefinitionKinds`. Emit a single
|
|
535
|
+
// debug note per language (via the progress callback), skip the
|
|
536
|
+
// callables, and stay forward-compatible for future providers.
|
|
537
|
+
ctx.onProgress?.({
|
|
538
|
+
phase: COMPLEXITY_PHASE_NAME,
|
|
539
|
+
kind: "warn",
|
|
540
|
+
message: `complexity: language "${lang}" provider missing complexityDefinitionKinds; skipping`,
|
|
541
|
+
});
|
|
542
|
+
skipped += callableDefs.length;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const defNodes = collectDefinitionNodes(tree.rootNode, defTypesForLang);
|
|
546
|
+
// Sort for determinism.
|
|
547
|
+
const sortedDefs = [...callableDefs].sort((a, b) => {
|
|
548
|
+
if (a.startLine !== b.startLine)
|
|
549
|
+
return a.startLine - b.startLine;
|
|
550
|
+
if (a.qualifiedName < b.qualifiedName)
|
|
551
|
+
return -1;
|
|
552
|
+
if (a.qualifiedName > b.qualifiedName)
|
|
553
|
+
return 1;
|
|
554
|
+
return 0;
|
|
555
|
+
});
|
|
556
|
+
for (const def of sortedDefs) {
|
|
557
|
+
const subtree = matchSubtree(defNodes, def);
|
|
558
|
+
if (subtree === undefined) {
|
|
559
|
+
skipped += 1;
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
const body = selectBody(subtree) ?? subtree;
|
|
563
|
+
if (body.endPosition.row <= body.startPosition.row && body.childCount === 0) {
|
|
564
|
+
skipped += 1;
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
const decisions = countDecisionsIn(body, lang);
|
|
568
|
+
const cyclomaticComplexity = 1 + decisions;
|
|
569
|
+
const nestingDepth = maxNestingIn(body, lang);
|
|
570
|
+
const nloc = computeNloc(sourceText, def.startLine, def.endLine, lang);
|
|
571
|
+
const halsteadVolume = computeHalsteadVolume(body, lang);
|
|
572
|
+
const updated = annotateNode(ctx, def, cyclomaticComplexity, nestingDepth, nloc, halsteadVolume);
|
|
573
|
+
if (updated) {
|
|
574
|
+
annotated += 1;
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
skipped += 1;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return { symbolsAnnotated: annotated, skipped };
|
|
582
|
+
}
|
|
583
|
+
function collectDefinitionNodes(root, defTypes) {
|
|
584
|
+
const out = [];
|
|
585
|
+
for (const n of walk(root)) {
|
|
586
|
+
if (defTypes.has(n.type))
|
|
587
|
+
out.push(n);
|
|
588
|
+
}
|
|
589
|
+
return out;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Match a subtree to an {@link ExtractedDefinition} by line range. Tree-sitter
|
|
593
|
+
* `startPosition.row` is 0-indexed; ExtractedDefinition.startLine is 1-indexed.
|
|
594
|
+
* We compare the 1-indexed form on both sides.
|
|
595
|
+
*/
|
|
596
|
+
function matchSubtree(candidates, def) {
|
|
597
|
+
let best;
|
|
598
|
+
let bestRangeWidth = Number.POSITIVE_INFINITY;
|
|
599
|
+
for (const c of candidates) {
|
|
600
|
+
const cStart = c.startPosition.row + 1;
|
|
601
|
+
const cEnd = c.endPosition.row + 1;
|
|
602
|
+
if (cStart > def.startLine || cEnd < def.endLine)
|
|
603
|
+
continue;
|
|
604
|
+
// Prefer the tightest enclosing candidate.
|
|
605
|
+
const width = cEnd - cStart;
|
|
606
|
+
if (width < bestRangeWidth) {
|
|
607
|
+
bestRangeWidth = width;
|
|
608
|
+
best = c;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return best;
|
|
612
|
+
}
|
|
613
|
+
/** Pick the body node when the function itself is a declaration shell. */
|
|
614
|
+
function selectBody(def) {
|
|
615
|
+
if (def.childForFieldName !== undefined) {
|
|
616
|
+
const named = def.childForFieldName("body");
|
|
617
|
+
if (named !== null && named !== undefined)
|
|
618
|
+
return named;
|
|
619
|
+
}
|
|
620
|
+
// Fallback: last named child tends to be the block in most grammars.
|
|
621
|
+
for (let i = def.childCount - 1; i >= 0; i--) {
|
|
622
|
+
const c = def.child(i);
|
|
623
|
+
if (c === null)
|
|
624
|
+
continue;
|
|
625
|
+
if (c.type.includes("block") || c.type.includes("body"))
|
|
626
|
+
return c;
|
|
627
|
+
}
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
function annotateNode(ctx, def, cyclomaticComplexity, nestingDepth, nloc, halsteadVolume) {
|
|
631
|
+
const existing = findCallableNode(ctx, def);
|
|
632
|
+
if (existing === undefined)
|
|
633
|
+
return false;
|
|
634
|
+
const updated = withComplexity(existing, cyclomaticComplexity, nestingDepth, nloc, halsteadVolume);
|
|
635
|
+
ctx.graph.addNode(updated);
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
function findCallableNode(ctx, def) {
|
|
639
|
+
// Iterate graph nodes looking for an exact id/filePath/name match. The
|
|
640
|
+
// graph holds ~O(symbols) entries; this is O(N) per definition but the
|
|
641
|
+
// call cost is dominated by parse IO, so we stay simple.
|
|
642
|
+
for (const n of ctx.graph.nodes()) {
|
|
643
|
+
if (!CALLABLE_KINDS.has(n.kind))
|
|
644
|
+
continue;
|
|
645
|
+
if (n.filePath !== def.filePath)
|
|
646
|
+
continue;
|
|
647
|
+
if (n.name !== def.name)
|
|
648
|
+
continue;
|
|
649
|
+
if (n.kind !== def.kind)
|
|
650
|
+
continue;
|
|
651
|
+
// Only callable nodes carry startLine; the CALLABLE_KINDS guard above
|
|
652
|
+
// narrows `n` to a LocatedNode in practice, but TS cannot see it here
|
|
653
|
+
// because the GraphNode union also contains kinds without startLine.
|
|
654
|
+
const nodeWithLine = n;
|
|
655
|
+
if (nodeWithLine.startLine !== def.startLine)
|
|
656
|
+
continue;
|
|
657
|
+
return n;
|
|
658
|
+
}
|
|
659
|
+
return undefined;
|
|
660
|
+
}
|
|
661
|
+
function withComplexity(node, cyclomaticComplexity, nestingDepth, nloc, halsteadVolume) {
|
|
662
|
+
// Only callable kinds carry these fields; other kinds fall through
|
|
663
|
+
// unchanged, matching the optional-field contract in core-types.
|
|
664
|
+
if (node.kind === "Function" || node.kind === "Method" || node.kind === "Constructor") {
|
|
665
|
+
return {
|
|
666
|
+
...node,
|
|
667
|
+
cyclomaticComplexity,
|
|
668
|
+
nestingDepth,
|
|
669
|
+
nloc,
|
|
670
|
+
...(halsteadVolume !== undefined ? { halsteadVolume } : {}),
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
return node;
|
|
674
|
+
}
|
|
675
|
+
// -------- Halstead volume --------------------------------------------------
|
|
676
|
+
/**
|
|
677
|
+
* Compute the Halstead volume for a function body.
|
|
678
|
+
*
|
|
679
|
+
* Halstead's metric treats every token as either an "operator" or an
|
|
680
|
+
* "operand". Operators are the syntactic tokens that perform work
|
|
681
|
+
* (`+`, `&&`, `=`, `if`, `return`, …); operands are the identifiers and
|
|
682
|
+
* literals they act on.
|
|
683
|
+
*
|
|
684
|
+
* Volume V = (N1 + N2) * log2(n1 + n2), where:
|
|
685
|
+
* - n1 = unique operator count
|
|
686
|
+
* - n2 = unique operand count
|
|
687
|
+
* - N1 = total operator occurrences
|
|
688
|
+
* - N2 = total operand occurrences
|
|
689
|
+
*
|
|
690
|
+
* Returns `undefined` when the provider did not declare
|
|
691
|
+
* `halsteadOperatorKinds` or when the body contains no countable tokens.
|
|
692
|
+
*/
|
|
693
|
+
function computeHalsteadVolume(body, lang) {
|
|
694
|
+
const operators = halsteadOperatorsFor(lang);
|
|
695
|
+
if (operators === undefined)
|
|
696
|
+
return undefined;
|
|
697
|
+
const definitions = definitionTypesFor(lang);
|
|
698
|
+
if (definitions === undefined)
|
|
699
|
+
return undefined;
|
|
700
|
+
const operatorCounts = new Map();
|
|
701
|
+
const operandCounts = new Map();
|
|
702
|
+
// Iterative walk; avoids stack overflow on very large functions. We do
|
|
703
|
+
// not descend into nested function/method definitions — their tokens
|
|
704
|
+
// belong to their own volume computation.
|
|
705
|
+
const stack = [{ node: body, skip: false }];
|
|
706
|
+
while (stack.length > 0) {
|
|
707
|
+
const frame = stack.pop();
|
|
708
|
+
if (frame === undefined)
|
|
709
|
+
break;
|
|
710
|
+
const { node, skip } = frame;
|
|
711
|
+
if (skip)
|
|
712
|
+
continue;
|
|
713
|
+
const enteringNested = node !== body && definitions.has(node.type);
|
|
714
|
+
// Leaf = no children. A leaf's `type` holds either a token ("+") or a
|
|
715
|
+
// semantic name ("identifier", "number", "string"); we bucket by type
|
|
716
|
+
// string. Non-leaf internal nodes do not contribute tokens on their own.
|
|
717
|
+
if (node.childCount === 0) {
|
|
718
|
+
const t = node.type;
|
|
719
|
+
if (operators.has(t)) {
|
|
720
|
+
operatorCounts.set(t, (operatorCounts.get(t) ?? 0) + 1);
|
|
721
|
+
}
|
|
722
|
+
else if (isHalsteadOperand(t)) {
|
|
723
|
+
// Use the operand text so `x` and `y` are distinct identifiers.
|
|
724
|
+
// Fall back to the type string if the text is empty (guard against
|
|
725
|
+
// grammars that return empty leaves for synthesized tokens).
|
|
726
|
+
const key = node.text.length > 0 ? `${t}:${node.text}` : t;
|
|
727
|
+
operandCounts.set(key, (operandCounts.get(key) ?? 0) + 1);
|
|
728
|
+
}
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
for (let i = node.childCount - 1; i >= 0; i--) {
|
|
732
|
+
const child = node.child(i);
|
|
733
|
+
if (child !== null) {
|
|
734
|
+
stack.push({ node: child, skip: enteringNested });
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
const n1 = operatorCounts.size;
|
|
739
|
+
const n2 = operandCounts.size;
|
|
740
|
+
let N1 = 0;
|
|
741
|
+
let N2 = 0;
|
|
742
|
+
for (const v of operatorCounts.values())
|
|
743
|
+
N1 += v;
|
|
744
|
+
for (const v of operandCounts.values())
|
|
745
|
+
N2 += v;
|
|
746
|
+
const totalVocab = n1 + n2;
|
|
747
|
+
if (totalVocab === 0)
|
|
748
|
+
return undefined;
|
|
749
|
+
if (totalVocab === 1)
|
|
750
|
+
return 0;
|
|
751
|
+
const volume = (N1 + N2) * Math.log2(totalVocab);
|
|
752
|
+
return Number.isFinite(volume) ? volume : undefined;
|
|
753
|
+
}
|
|
754
|
+
/** Leaf-node type names we treat as Halstead operands. */
|
|
755
|
+
const HALSTEAD_OPERAND_TYPES = new Set([
|
|
756
|
+
"identifier",
|
|
757
|
+
"property_identifier",
|
|
758
|
+
"type_identifier",
|
|
759
|
+
"field_identifier",
|
|
760
|
+
"shorthand_property_identifier",
|
|
761
|
+
"namespace_identifier",
|
|
762
|
+
"package_identifier",
|
|
763
|
+
"simple_identifier",
|
|
764
|
+
"constant",
|
|
765
|
+
"number",
|
|
766
|
+
"integer",
|
|
767
|
+
"integer_literal",
|
|
768
|
+
"float",
|
|
769
|
+
"float_literal",
|
|
770
|
+
"string",
|
|
771
|
+
"string_literal",
|
|
772
|
+
"string_content",
|
|
773
|
+
"raw_string_literal",
|
|
774
|
+
"true",
|
|
775
|
+
"false",
|
|
776
|
+
"null",
|
|
777
|
+
"nil",
|
|
778
|
+
"undefined",
|
|
779
|
+
"none",
|
|
780
|
+
"None",
|
|
781
|
+
"boolean_literal",
|
|
782
|
+
"char_literal",
|
|
783
|
+
"character_literal",
|
|
784
|
+
"escape_sequence",
|
|
785
|
+
"regex",
|
|
786
|
+
"regex_pattern",
|
|
787
|
+
"self",
|
|
788
|
+
"super",
|
|
789
|
+
"this",
|
|
790
|
+
]);
|
|
791
|
+
function isHalsteadOperand(type) {
|
|
792
|
+
return HALSTEAD_OPERAND_TYPES.has(type);
|
|
793
|
+
}
|
|
794
|
+
//# sourceMappingURL=complexity.js.map
|