code-auditor-mcp 3.1.0 → 3.4.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +665 -1
- package/README.md +85 -11
- package/dist/__test_tmp__/spec18_test.d.ts +2 -0
- package/dist/__test_tmp__/spec18_test.d.ts.map +1 -0
- package/dist/__test_tmp__/spec18_test.js +5 -0
- package/dist/__test_tmp__/spec18_test.js.map +1 -0
- package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +6 -0
- package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +1 -0
- package/dist/__tests__/fixtures/spec-20/scripts/tool.js +12 -0
- package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +1 -0
- package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +7 -0
- package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +1 -0
- package/dist/__tests__/fixtures/spec-20/src/demo-module.js +13 -0
- package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +1 -0
- package/dist/analyzers/analyzerUtils.js +2 -2
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +2 -4
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts +3 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js +3 -5
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +0 -3
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +2 -5
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +83 -0
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +611 -0
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -0
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +8 -4
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/orm/adapterRegistry.d.ts +41 -0
- package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -0
- package/dist/analyzers/orm/adapterRegistry.js +73 -0
- package/dist/analyzers/orm/adapterRegistry.js.map +1 -0
- package/dist/analyzers/orm/drizzleAdapter.d.ts +42 -0
- package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -0
- package/dist/analyzers/orm/drizzleAdapter.js +166 -0
- package/dist/analyzers/orm/drizzleAdapter.js.map +1 -0
- package/dist/analyzers/orm/index.d.ts +14 -0
- package/dist/analyzers/orm/index.d.ts.map +1 -0
- package/dist/analyzers/orm/index.js +24 -0
- package/dist/analyzers/orm/index.js.map +1 -0
- package/dist/analyzers/orm/prismaAdapter.d.ts +45 -0
- package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -0
- package/dist/analyzers/orm/prismaAdapter.js +170 -0
- package/dist/analyzers/orm/prismaAdapter.js.map +1 -0
- package/dist/analyzers/orm/types.d.ts +63 -0
- package/dist/analyzers/orm/types.d.ts.map +1 -0
- package/dist/analyzers/orm/types.js +10 -0
- package/dist/analyzers/orm/types.js.map +1 -0
- package/dist/analyzers/provenance.d.ts +133 -0
- package/dist/analyzers/provenance.d.ts.map +1 -0
- package/dist/analyzers/provenance.js +1041 -0
- package/dist/analyzers/provenance.js.map +1 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +117 -1
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.d.ts +38 -0
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -0
- package/dist/analyzers/ruleRegistry.js +123 -0
- package/dist/analyzers/ruleRegistry.js.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +58 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +445 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +52 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +108 -26
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +64 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +259 -103
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +5 -15
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +10 -10
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +25 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +250 -59
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +103 -0
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +750 -0
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +483 -3
- package/dist/auditRunner.js.map +1 -1
- package/dist/baseline.d.ts +98 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +184 -0
- package/dist/baseline.js.map +1 -0
- package/dist/churn/churnExtractor.d.ts +32 -0
- package/dist/churn/churnExtractor.d.ts.map +1 -0
- package/dist/churn/churnExtractor.js +261 -0
- package/dist/churn/churnExtractor.js.map +1 -0
- package/dist/cli.js +1215 -11
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB-enhanced.js +1 -1
- package/dist/codeIndexDB-enhanced.js.map +1 -1
- package/dist/codeIndexDB.d.ts +101 -0
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +661 -5
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +95 -1
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +72 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +146 -15
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/pathProfiles.d.ts +38 -0
- package/dist/config/pathProfiles.d.ts.map +1 -0
- package/dist/config/pathProfiles.js +47 -0
- package/dist/config/pathProfiles.js.map +1 -0
- package/dist/conventions/conventionMiner.d.ts +63 -0
- package/dist/conventions/conventionMiner.d.ts.map +1 -0
- package/dist/conventions/conventionMiner.js +696 -0
- package/dist/conventions/conventionMiner.js.map +1 -0
- package/dist/coverage/coverageService.d.ts +49 -0
- package/dist/coverage/coverageService.d.ts.map +1 -0
- package/dist/coverage/coverageService.js +117 -0
- package/dist/coverage/coverageService.js.map +1 -0
- package/dist/coverage/istanbulParser.d.ts +84 -0
- package/dist/coverage/istanbulParser.d.ts.map +1 -0
- package/dist/coverage/istanbulParser.js +120 -0
- package/dist/coverage/istanbulParser.js.map +1 -0
- package/dist/coverage/lcovParser.d.ts +40 -0
- package/dist/coverage/lcovParser.d.ts.map +1 -0
- package/dist/coverage/lcovParser.js +168 -0
- package/dist/coverage/lcovParser.js.map +1 -0
- package/dist/fingerprint.d.ts +17 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +42 -0
- package/dist/fingerprint.js.map +1 -1
- package/dist/grammars/tree-sitter-css.wasm +0 -0
- package/dist/graph/blastRadius.d.ts +27 -0
- package/dist/graph/blastRadius.d.ts.map +1 -0
- package/dist/graph/blastRadius.js +150 -0
- package/dist/graph/blastRadius.js.map +1 -0
- package/dist/graph/callGraph.d.ts +78 -0
- package/dist/graph/callGraph.d.ts.map +1 -0
- package/dist/graph/callGraph.js +484 -0
- package/dist/graph/callGraph.js.map +1 -0
- package/dist/graph/importGraph.d.ts +91 -0
- package/dist/graph/importGraph.d.ts.map +1 -0
- package/dist/graph/importGraph.js +602 -0
- package/dist/graph/importGraph.js.map +1 -0
- package/dist/graph/outputFormatter.d.ts +55 -0
- package/dist/graph/outputFormatter.d.ts.map +1 -0
- package/dist/graph/outputFormatter.js +283 -0
- package/dist/graph/outputFormatter.js.map +1 -0
- package/dist/hooks/codex.d.ts +36 -1
- package/dist/hooks/codex.d.ts.map +1 -1
- package/dist/hooks/codex.js +88 -54
- package/dist/hooks/codex.js.map +1 -1
- package/dist/hotspots/hotspotScorer.d.ts +20 -0
- package/dist/hotspots/hotspotScorer.d.ts.map +1 -0
- package/dist/hotspots/hotspotScorer.js +202 -0
- package/dist/hotspots/hotspotScorer.js.map +1 -0
- package/dist/invariants/invariant-rules.schema.json +42 -1
- package/dist/invariants/ruleEngine.d.ts +4 -1
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +141 -5
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/invariants/ruleValidator.d.ts.map +1 -1
- package/dist/invariants/ruleValidator.js +62 -1
- package/dist/invariants/ruleValidator.js.map +1 -1
- package/dist/invariants/types.d.ts +23 -2
- package/dist/invariants/types.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +59 -24
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/index.d.ts +1 -0
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +3 -0
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +59 -0
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -0
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +236 -0
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -0
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
- package/dist/languages/tree-sitter/parser.js +2 -0
- package/dist/languages/tree-sitter/parser.js.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +61 -10
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/ledger.d.ts +128 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +345 -0
- package/dist/ledger.js.map +1 -0
- package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
- package/dist/mcp-tools/projectTasks.js +9 -14
- package/dist/mcp-tools/projectTasks.js.map +1 -1
- package/dist/mcp.js +0 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +3 -0
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +7 -1
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.js +5 -1
- package/dist/reporting/htmlReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +10 -2
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +17 -17
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/scripts/runBench.d.ts +94 -0
- package/dist/scripts/runBench.d.ts.map +1 -0
- package/dist/scripts/runBench.js +1559 -0
- package/dist/scripts/runBench.js.map +1 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +51 -4
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/services/CodeMapGenerator.d.ts +33 -0
- package/dist/services/CodeMapGenerator.d.ts.map +1 -1
- package/dist/services/CodeMapGenerator.js +404 -0
- package/dist/services/CodeMapGenerator.js.map +1 -1
- package/dist/styles/normalizer.d.ts +37 -0
- package/dist/styles/normalizer.d.ts.map +1 -0
- package/dist/styles/normalizer.js +301 -0
- package/dist/styles/normalizer.js.map +1 -0
- package/dist/styles/styleExtractor.d.ts +38 -0
- package/dist/styles/styleExtractor.d.ts.map +1 -0
- package/dist/styles/styleExtractor.js +630 -0
- package/dist/styles/styleExtractor.js.map +1 -0
- package/dist/styles/styleIndexer.d.ts +35 -0
- package/dist/styles/styleIndexer.d.ts.map +1 -0
- package/dist/styles/styleIndexer.js +266 -0
- package/dist/styles/styleIndexer.js.map +1 -0
- package/dist/styles/tailwindConfigLoader.d.ts +38 -0
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -0
- package/dist/styles/tailwindConfigLoader.js +374 -0
- package/dist/styles/tailwindConfigLoader.js.map +1 -0
- package/dist/styles/tailwindExpander.d.ts +38 -0
- package/dist/styles/tailwindExpander.d.ts.map +1 -0
- package/dist/styles/tailwindExpander.js +637 -0
- package/dist/styles/tailwindExpander.js.map +1 -0
- package/dist/styles/types.d.ts +92 -0
- package/dist/styles/types.d.ts.map +1 -0
- package/dist/styles/types.js +10 -0
- package/dist/styles/types.js.map +1 -0
- package/dist/symbols.d.ts +21 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/symbols.js +31 -0
- package/dist/symbols.js.map +1 -0
- package/dist/types.d.ts +326 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -5
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/scripts/hook-audit.sh +5 -1
- package/plugin/skills/code-auditor/SKILL.md +51 -0
- package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts +0 -21
- package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js +0 -21
- package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js +0 -12
- package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts +0 -11
- package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js +0 -22
- package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts +0 -9
- package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js +0 -18
- package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js +0 -16
- package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts +0 -14
- package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js +0 -18
- package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js +0 -22
- package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js +0 -20
- package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts +0 -18
- package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js +0 -28
- package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts +0 -11
- package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js +0 -31
- package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts +0 -11
- package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js +0 -18
- package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js +0 -61
- package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js +0 -15
- package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts +0 -11
- package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js +0 -49
- package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js +0 -19
- package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts +0 -37
- package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js +0 -73
- package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts +0 -12
- package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js +0 -82
- package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js +0 -15
- package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js.map +0 -1
- package/dist/analyzers/dataAccessAnalyzer.d.ts +0 -46
- package/dist/analyzers/dataAccessAnalyzer.d.ts.map +0 -1
- package/dist/analyzers/dataAccessAnalyzer.js +0 -474
- package/dist/analyzers/dataAccessAnalyzer.js.map +0 -1
- package/dist/analyzers/dryAnalyzer.d.ts +0 -31
- package/dist/analyzers/dryAnalyzer.d.ts.map +0 -1
- package/dist/analyzers/dryAnalyzer.js +0 -734
- package/dist/analyzers/dryAnalyzer.js.map +0 -1
- package/dist/analyzers/schemaAnalyzer.d.ts +0 -23
- package/dist/analyzers/schemaAnalyzer.d.ts.map +0 -1
- package/dist/analyzers/schemaAnalyzer.js +0 -564
- package/dist/analyzers/schemaAnalyzer.js.map +0 -1
- package/dist/analyzers/solidAnalyzer.d.ts +0 -33
- package/dist/analyzers/solidAnalyzer.d.ts.map +0 -1
- package/dist/analyzers/solidAnalyzer.js +0 -847
- package/dist/analyzers/solidAnalyzer.js.map +0 -1
|
@@ -1,734 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DRY (Don't Repeat Yourself) Analyzer (Functional)
|
|
3
|
-
* Detects code duplication across the entire codebase
|
|
4
|
-
*
|
|
5
|
-
* Uses a code index to efficiently find:
|
|
6
|
-
* - Exact code duplicates
|
|
7
|
-
* - Similar code patterns
|
|
8
|
-
* - Repeated string literals
|
|
9
|
-
* - Duplicate imports
|
|
10
|
-
*/
|
|
11
|
-
import * as crypto from 'crypto';
|
|
12
|
-
import * as fs from 'fs/promises';
|
|
13
|
-
import { parseFile, walkAST, findNodes, getNodeText, getLineAndColumn, getNodeName, } from '../languages/adapterBridge.js';
|
|
14
|
-
/**
|
|
15
|
-
* Debug logger
|
|
16
|
-
*/
|
|
17
|
-
class DebugLogger {
|
|
18
|
-
logs = [];
|
|
19
|
-
enabled;
|
|
20
|
-
constructor(enabled = false) {
|
|
21
|
-
this.enabled = enabled;
|
|
22
|
-
}
|
|
23
|
-
log(message, data) {
|
|
24
|
-
if (!this.enabled)
|
|
25
|
-
return;
|
|
26
|
-
const timestamp = new Date().toISOString();
|
|
27
|
-
const logEntry = `[${timestamp}] ${message}`;
|
|
28
|
-
if (data !== undefined) {
|
|
29
|
-
this.logs.push(`${logEntry}\n${JSON.stringify(data, null, 2)}`);
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
this.logs.push(logEntry);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async writeToFile(filePath) {
|
|
36
|
-
if (!this.enabled || this.logs.length === 0)
|
|
37
|
-
return;
|
|
38
|
-
const content = this.logs.join('\n\n');
|
|
39
|
-
await fs.writeFile(filePath, content, 'utf-8');
|
|
40
|
-
}
|
|
41
|
-
getLogs() {
|
|
42
|
-
return this.logs;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Default configuration
|
|
47
|
-
*/
|
|
48
|
-
const DEFAULT_CONFIG = {
|
|
49
|
-
minLineThreshold: 5,
|
|
50
|
-
similarityThreshold: 0.85,
|
|
51
|
-
excludePatterns: ['**/*.test.ts', '**/*.spec.ts'],
|
|
52
|
-
checkImports: true,
|
|
53
|
-
checkStrings: true,
|
|
54
|
-
checkUnusedImports: true,
|
|
55
|
-
ignoreComments: true,
|
|
56
|
-
ignoreWhitespace: true,
|
|
57
|
-
debug: false,
|
|
58
|
-
debugLogPath: './dry-analyzer-debug.log'
|
|
59
|
-
};
|
|
60
|
-
/**
|
|
61
|
-
* Build a code index from all files
|
|
62
|
-
*/
|
|
63
|
-
async function buildCodeIndex(files, config, logger) {
|
|
64
|
-
const index = {
|
|
65
|
-
blocks: [],
|
|
66
|
-
hashMap: new Map(),
|
|
67
|
-
stringLiterals: new Map(),
|
|
68
|
-
imports: new Map(),
|
|
69
|
-
unusedImports: new Map()
|
|
70
|
-
};
|
|
71
|
-
logger.log(`Building code index for ${files.length} files`);
|
|
72
|
-
for (const file of files) {
|
|
73
|
-
// Skip excluded patterns
|
|
74
|
-
if (config.excludePatterns?.some(pattern => file.includes(pattern.replace('**/', '').replace('*', '')))) {
|
|
75
|
-
logger.log(`Skipping excluded file: ${file}`);
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
const content = await fs.readFile(file, 'utf-8');
|
|
80
|
-
const ast = parseFile(file, content);
|
|
81
|
-
if (!ast) {
|
|
82
|
-
logger.log(`Failed to parse file: ${file}`);
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
logger.log(`Processing file: ${file}`);
|
|
86
|
-
// Extract code blocks
|
|
87
|
-
const blocks = extractCodeBlocks(ast.root, content, file, config, logger);
|
|
88
|
-
logger.log(`Extracted ${blocks.length} code blocks from ${file}`);
|
|
89
|
-
for (const block of blocks) {
|
|
90
|
-
index.blocks.push(block);
|
|
91
|
-
// Add to hash map for O(1) duplicate lookup
|
|
92
|
-
if (!index.hashMap.has(block.hash)) {
|
|
93
|
-
index.hashMap.set(block.hash, []);
|
|
94
|
-
}
|
|
95
|
-
index.hashMap.get(block.hash).push(block);
|
|
96
|
-
}
|
|
97
|
-
// Extract string literals if enabled
|
|
98
|
-
if (config.checkStrings) {
|
|
99
|
-
const stringCount = index.stringLiterals.size;
|
|
100
|
-
extractStringLiteralsFromAST(ast.root, content, file, index.stringLiterals, logger);
|
|
101
|
-
logger.log(`Found ${index.stringLiterals.size - stringCount} new string literals`);
|
|
102
|
-
}
|
|
103
|
-
// Extract imports if enabled
|
|
104
|
-
if (config.checkImports) {
|
|
105
|
-
const importCount = index.imports.size;
|
|
106
|
-
extractImportsFromAST(ast.root, content, file, index.imports, logger);
|
|
107
|
-
logger.log(`Found ${index.imports.size - importCount} new import patterns`);
|
|
108
|
-
}
|
|
109
|
-
// Extract unused imports if enabled
|
|
110
|
-
if (config.checkUnusedImports) {
|
|
111
|
-
const unusedImportCount = index.unusedImports.size;
|
|
112
|
-
extractUnusedImportsFromAST(ast.root, content, file, index.unusedImports, logger);
|
|
113
|
-
logger.log(`Found ${index.unusedImports.size - unusedImportCount} new unused imports`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
logger.log(`Error processing ${file}: ${error}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
logger.log('Code index built', {
|
|
121
|
-
totalBlocks: index.blocks.length,
|
|
122
|
-
uniqueHashes: index.hashMap.size,
|
|
123
|
-
stringLiterals: index.stringLiterals.size,
|
|
124
|
-
importPatterns: index.imports.size,
|
|
125
|
-
unusedImports: index.unusedImports.size
|
|
126
|
-
});
|
|
127
|
-
return index;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Extract code blocks from an AST
|
|
131
|
-
*/
|
|
132
|
-
function extractCodeBlocks(ast, sourceCode, filePath, config, logger) {
|
|
133
|
-
const blocks = [];
|
|
134
|
-
// Extract functions
|
|
135
|
-
const functions = findNodes(ast, n => n.type === 'function_declaration');
|
|
136
|
-
for (const func of functions) {
|
|
137
|
-
const block = createCodeBlock(sourceCode, func, filePath, 'function', config);
|
|
138
|
-
if (block && isBlockLargeEnough(block, config)) {
|
|
139
|
-
blocks.push(block);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
// Extract arrow functions
|
|
143
|
-
const arrowFunctions = findNodes(ast, n => n.type === 'arrow_function' || n.type === 'function_expression');
|
|
144
|
-
for (const arrow of arrowFunctions) {
|
|
145
|
-
const block = createCodeBlock(sourceCode, arrow, filePath, 'function', config);
|
|
146
|
-
if (block && isBlockLargeEnough(block, config)) {
|
|
147
|
-
blocks.push(block);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// Extract methods
|
|
151
|
-
const methods = findNodes(ast, n => n.type === 'method_definition');
|
|
152
|
-
for (const method of methods) {
|
|
153
|
-
const block = createCodeBlock(sourceCode, method, filePath, 'method', config);
|
|
154
|
-
if (block && isBlockLargeEnough(block, config)) {
|
|
155
|
-
blocks.push(block);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// Extract class declarations
|
|
159
|
-
const classes = findNodes(ast, n => n.type === 'class_declaration');
|
|
160
|
-
for (const cls of classes) {
|
|
161
|
-
const block = createCodeBlock(sourceCode, cls, filePath, 'class', config);
|
|
162
|
-
if (block && isBlockLargeEnough(block, config)) {
|
|
163
|
-
blocks.push(block);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Extract block statements (if/else, loops, etc.)
|
|
167
|
-
const blockStatements = findNodes(ast, n => n.type === 'statement_block');
|
|
168
|
-
for (const blockStmt of blockStatements) {
|
|
169
|
-
// Only consider substantial blocks
|
|
170
|
-
const block = createCodeBlock(sourceCode, blockStmt, filePath, 'block', config);
|
|
171
|
-
if (block && isBlockLargeEnough(block, config)) {
|
|
172
|
-
blocks.push(block);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return blocks;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Create a code block from a node
|
|
179
|
-
*/
|
|
180
|
-
function createCodeBlock(sourceCode, node, filePath, type, config) {
|
|
181
|
-
const text = getNodeText(node, sourceCode);
|
|
182
|
-
const normalizedText = normalizeCode(text, config);
|
|
183
|
-
if (!normalizedText.trim()) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
const { line: startLine } = getLineAndColumn(node);
|
|
187
|
-
const endLine = node.location.end.line + 1;
|
|
188
|
-
// Get component/property name if available
|
|
189
|
-
const name = getNodeName(node) ?? undefined;
|
|
190
|
-
return {
|
|
191
|
-
file: filePath,
|
|
192
|
-
startLine,
|
|
193
|
-
endLine,
|
|
194
|
-
text,
|
|
195
|
-
normalizedText,
|
|
196
|
-
hash: hashCode(normalizedText),
|
|
197
|
-
type,
|
|
198
|
-
name
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Normalize code for comparison
|
|
203
|
-
*/
|
|
204
|
-
function normalizeCode(code, config) {
|
|
205
|
-
let normalized = code;
|
|
206
|
-
if (config.ignoreComments) {
|
|
207
|
-
// Remove single-line comments
|
|
208
|
-
normalized = normalized.replace(/\/\/.*$/gm, '');
|
|
209
|
-
// Remove multi-line comments
|
|
210
|
-
normalized = normalized.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
211
|
-
}
|
|
212
|
-
if (config.ignoreWhitespace) {
|
|
213
|
-
// Normalize whitespace
|
|
214
|
-
normalized = normalized.replace(/\s+/g, ' ').trim();
|
|
215
|
-
}
|
|
216
|
-
// Remove variable names to detect similar patterns
|
|
217
|
-
normalized = normalized.replace(/\b(?:const|let|var)\s+(\w+)/g, 'VAR $1');
|
|
218
|
-
return normalized;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Hash code for fast comparison
|
|
222
|
-
*/
|
|
223
|
-
function hashCode(text) {
|
|
224
|
-
return crypto.createHash('md5').update(text).digest('hex');
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Check if a block is large enough to be considered
|
|
228
|
-
*/
|
|
229
|
-
function isBlockLargeEnough(block, config) {
|
|
230
|
-
const lineCount = block.endLine - block.startLine + 1;
|
|
231
|
-
return lineCount >= (config.minLineThreshold || 5);
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Get string value (strip quotes from string literal)
|
|
235
|
-
*/
|
|
236
|
-
function getStringValue(node, sourceCode) {
|
|
237
|
-
const text = getNodeText(node, sourceCode);
|
|
238
|
-
if ((text.startsWith("'") && text.endsWith("'")) ||
|
|
239
|
-
(text.startsWith('"') && text.endsWith('"'))) {
|
|
240
|
-
return text.slice(1, -1);
|
|
241
|
-
}
|
|
242
|
-
if (text.startsWith('`') && text.endsWith('`')) {
|
|
243
|
-
return text.slice(1, -1);
|
|
244
|
-
}
|
|
245
|
-
return text;
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Extract string literals from AST
|
|
249
|
-
*/
|
|
250
|
-
function extractStringLiteralsFromAST(ast, sourceCode, filePath, stringMap, logger) {
|
|
251
|
-
// Find string and template_string nodes
|
|
252
|
-
const stringLiterals = findNodes(ast, n => n.type === 'string' || n.type === 'string_fragment' || n.type === 'template_string');
|
|
253
|
-
for (const literal of stringLiterals) {
|
|
254
|
-
// Only process actual string content (skip quote-only nodes that might be children of template_string)
|
|
255
|
-
const text = getStringValue(literal, sourceCode);
|
|
256
|
-
// Skip short strings and common ones
|
|
257
|
-
if (text.length < 10 || isCommonString(text)) {
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
const { line } = getLineAndColumn(literal);
|
|
261
|
-
if (!stringMap.has(text)) {
|
|
262
|
-
stringMap.set(text, []);
|
|
263
|
-
}
|
|
264
|
-
stringMap.get(text).push({ file: filePath, line });
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Check if a string is too common to track
|
|
269
|
-
*/
|
|
270
|
-
function isCommonString(str) {
|
|
271
|
-
const common = [
|
|
272
|
-
'use strict',
|
|
273
|
-
'default',
|
|
274
|
-
'exports',
|
|
275
|
-
'undefined',
|
|
276
|
-
'null',
|
|
277
|
-
'true',
|
|
278
|
-
'false',
|
|
279
|
-
'',
|
|
280
|
-
' ',
|
|
281
|
-
'\n',
|
|
282
|
-
'\t'
|
|
283
|
-
];
|
|
284
|
-
return common.includes(str) || /^[\s\d]+$/.test(str);
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Extract all import names from an import statement node
|
|
288
|
-
*/
|
|
289
|
-
function extractImportNames(node, sourceCode) {
|
|
290
|
-
const names = [];
|
|
291
|
-
walkAST(node, (child) => {
|
|
292
|
-
if (child.type === 'import_specifier') {
|
|
293
|
-
// named import: "import { foo } from 'bar'"
|
|
294
|
-
for (const c of (child.children || [])) {
|
|
295
|
-
if (c.type === 'identifier') {
|
|
296
|
-
names.push(getNodeText(c, sourceCode));
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
if (child.type === 'namespace_import') {
|
|
301
|
-
// "import * as foo from 'bar'"
|
|
302
|
-
for (const c of (child.children || [])) {
|
|
303
|
-
if (c.type === 'identifier') {
|
|
304
|
-
names.push('* as ' + getNodeText(c, sourceCode));
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
// Also check for default import: "import foo from 'bar'"
|
|
310
|
-
for (const child of (node.children || [])) {
|
|
311
|
-
if (child.type === 'import_clause') {
|
|
312
|
-
for (const c of (child.children || [])) {
|
|
313
|
-
if (c.type === 'identifier') {
|
|
314
|
-
const name = getNodeText(c, sourceCode);
|
|
315
|
-
if (!names.includes(name)) {
|
|
316
|
-
names.push(name);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return names;
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Extract imports from AST
|
|
326
|
-
*/
|
|
327
|
-
function extractImportsFromAST(ast, sourceCode, filePath, importMap, logger) {
|
|
328
|
-
const importNodes = findNodes(ast, n => n.type === 'import_statement');
|
|
329
|
-
for (const node of importNodes) {
|
|
330
|
-
let moduleSpecifier = '';
|
|
331
|
-
const importedNames = [];
|
|
332
|
-
// Find module specifier string
|
|
333
|
-
for (const child of (node.children || [])) {
|
|
334
|
-
if (child.type === 'string') {
|
|
335
|
-
moduleSpecifier = getNodeText(child, sourceCode).replace(/['"]/g, '');
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (!moduleSpecifier)
|
|
339
|
-
continue;
|
|
340
|
-
// Extract imported names
|
|
341
|
-
const names = extractImportNames(node, sourceCode);
|
|
342
|
-
importedNames.push(...names);
|
|
343
|
-
const { line } = getLineAndColumn(node);
|
|
344
|
-
const key = `${moduleSpecifier}:${importedNames.sort().join(',')}`;
|
|
345
|
-
if (!importMap.has(key)) {
|
|
346
|
-
importMap.set(key, []);
|
|
347
|
-
}
|
|
348
|
-
importMap.get(key).push({
|
|
349
|
-
file: filePath,
|
|
350
|
-
line,
|
|
351
|
-
modules: importedNames
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Extract detailed per-name import information
|
|
357
|
-
*/
|
|
358
|
-
function getImportDetails(ast, sourceCode) {
|
|
359
|
-
const details = [];
|
|
360
|
-
const importNodes = findNodes(ast, n => n.type === 'import_statement');
|
|
361
|
-
for (const node of importNodes) {
|
|
362
|
-
let moduleSpecifier = '';
|
|
363
|
-
const { line } = getLineAndColumn(node);
|
|
364
|
-
// Find module specifier string
|
|
365
|
-
for (const child of (node.children || [])) {
|
|
366
|
-
if (child.type === 'string') {
|
|
367
|
-
moduleSpecifier = getNodeText(child, sourceCode).replace(/['"]/g, '');
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
// Check for side-effect import (no import clause, no names)
|
|
371
|
-
let hasImportClause = false;
|
|
372
|
-
for (const child of (node.children || [])) {
|
|
373
|
-
if (child.type === 'import_clause') {
|
|
374
|
-
hasImportClause = true;
|
|
375
|
-
break;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
if (!hasImportClause) {
|
|
379
|
-
details.push({
|
|
380
|
-
importType: 'side-effect',
|
|
381
|
-
localName: moduleSpecifier,
|
|
382
|
-
modulePath: moduleSpecifier,
|
|
383
|
-
line
|
|
384
|
-
});
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
// Extract per-name imports
|
|
388
|
-
const names = extractImportNames(node, sourceCode);
|
|
389
|
-
for (const name of names) {
|
|
390
|
-
details.push({
|
|
391
|
-
importType: 'static',
|
|
392
|
-
localName: name,
|
|
393
|
-
modulePath: moduleSpecifier,
|
|
394
|
-
line
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
// If no names extracted but has import clause, it might be a namespace or default import not captured
|
|
398
|
-
if (names.length === 0 && moduleSpecifier) {
|
|
399
|
-
details.push({
|
|
400
|
-
importType: 'static',
|
|
401
|
-
localName: moduleSpecifier,
|
|
402
|
-
modulePath: moduleSpecifier,
|
|
403
|
-
line
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return details;
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Extract identifier usage map from AST
|
|
411
|
-
* Returns a Map of identifier name -> locations where it's used
|
|
412
|
-
*/
|
|
413
|
-
function extractIdentifierUsageFromAST(ast, sourceCode, targetNames) {
|
|
414
|
-
const usageMap = new Map();
|
|
415
|
-
// Find all identifiers in the AST
|
|
416
|
-
const identifiers = findNodes(ast, n => n.type === 'identifier');
|
|
417
|
-
for (const id of identifiers) {
|
|
418
|
-
const name = getNodeText(id, sourceCode);
|
|
419
|
-
if (targetNames.has(name)) {
|
|
420
|
-
if (!usageMap.has(name)) {
|
|
421
|
-
usageMap.set(name, []);
|
|
422
|
-
}
|
|
423
|
-
usageMap.get(name).push({ line: getLineAndColumn(id).line });
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return usageMap;
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* Extract unused imports from an AST
|
|
430
|
-
*/
|
|
431
|
-
function extractUnusedImportsFromAST(ast, sourceCode, filePath, unusedImportsMap, logger) {
|
|
432
|
-
// Get detailed imports
|
|
433
|
-
const detailedImports = getImportDetails(ast, sourceCode);
|
|
434
|
-
const importNames = new Set(detailedImports
|
|
435
|
-
.filter(imp => imp.importType !== 'side-effect')
|
|
436
|
-
.map(imp => imp.localName));
|
|
437
|
-
// Extract identifier usage across the entire file
|
|
438
|
-
const usageMap = extractIdentifierUsageFromAST(ast, sourceCode, importNames);
|
|
439
|
-
// Find unused imports
|
|
440
|
-
for (const imp of detailedImports) {
|
|
441
|
-
// Skip side-effect imports - they're never "unused"
|
|
442
|
-
if (imp.importType === 'side-effect')
|
|
443
|
-
continue;
|
|
444
|
-
if (!usageMap.has(imp.localName)) {
|
|
445
|
-
const key = `${filePath}:${imp.localName}`;
|
|
446
|
-
if (!unusedImportsMap.has(key)) {
|
|
447
|
-
unusedImportsMap.set(key, []);
|
|
448
|
-
}
|
|
449
|
-
unusedImportsMap.get(key).push({
|
|
450
|
-
file: filePath,
|
|
451
|
-
line: imp.line,
|
|
452
|
-
importName: imp.localName,
|
|
453
|
-
moduleSpecifier: imp.modulePath
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Find duplicates in the code index
|
|
460
|
-
*/
|
|
461
|
-
function findDuplicates(index, config, logger) {
|
|
462
|
-
const violations = [];
|
|
463
|
-
// Find exact duplicates
|
|
464
|
-
logger.log(`Checking ${index.hashMap.size} unique hashes for duplicates`);
|
|
465
|
-
for (const [hash, blocks] of index.hashMap) {
|
|
466
|
-
if (blocks.length > 1) {
|
|
467
|
-
logger.log(`Hash ${hash} has ${blocks.length} blocks`);
|
|
468
|
-
// Group by actual code (not just hash) to avoid false positives
|
|
469
|
-
const groups = groupByExactCode(blocks);
|
|
470
|
-
for (const group of groups) {
|
|
471
|
-
if (group.length > 1) {
|
|
472
|
-
logger.log(`Found exact duplicate with ${group.length} instances`, {
|
|
473
|
-
files: group.map(b => ({ file: b.file, line: b.startLine, type: b.type })),
|
|
474
|
-
preview: group[0].text.substring(0, 100) + '...'
|
|
475
|
-
});
|
|
476
|
-
violations.push(createDuplicateViolation(group, 'exact-duplicate'));
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
// Find similar code blocks
|
|
482
|
-
const similarViolations = findSimilarBlocks(index.blocks, config, logger);
|
|
483
|
-
violations.push(...similarViolations);
|
|
484
|
-
// Find duplicate strings
|
|
485
|
-
if (config.checkStrings) {
|
|
486
|
-
for (const [str, locations] of index.stringLiterals) {
|
|
487
|
-
if (locations.length > 2) { // More than 2 occurrences
|
|
488
|
-
violations.push(createStringDuplicateViolation(str, locations));
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
// Find duplicate imports
|
|
493
|
-
if (config.checkImports) {
|
|
494
|
-
for (const [importKey, locations] of index.imports) {
|
|
495
|
-
if (locations.length > 3) { // More than 3 files with same imports
|
|
496
|
-
violations.push(createImportDuplicateViolation(importKey, locations));
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
// Find unused imports
|
|
501
|
-
if (config.checkUnusedImports) {
|
|
502
|
-
for (const [importKey, locations] of index.unusedImports) {
|
|
503
|
-
if (locations.length > 0) {
|
|
504
|
-
violations.push(createUnusedImportViolation(locations[0]));
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return violations;
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* Group blocks by exact code content
|
|
512
|
-
*/
|
|
513
|
-
function groupByExactCode(blocks) {
|
|
514
|
-
const groups = new Map();
|
|
515
|
-
for (const block of blocks) {
|
|
516
|
-
const key = block.normalizedText;
|
|
517
|
-
if (!groups.has(key)) {
|
|
518
|
-
groups.set(key, []);
|
|
519
|
-
}
|
|
520
|
-
groups.get(key).push(block);
|
|
521
|
-
}
|
|
522
|
-
return Array.from(groups.values());
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Find similar (but not exact) code blocks
|
|
526
|
-
*/
|
|
527
|
-
function findSimilarBlocks(blocks, config, logger) {
|
|
528
|
-
const violations = [];
|
|
529
|
-
const threshold = config.similarityThreshold || 0.85;
|
|
530
|
-
const processed = new Set();
|
|
531
|
-
for (let i = 0; i < blocks.length; i++) {
|
|
532
|
-
const block1 = blocks[i];
|
|
533
|
-
const key1 = `${block1.file}:${block1.startLine}`;
|
|
534
|
-
if (processed.has(key1))
|
|
535
|
-
continue;
|
|
536
|
-
const similar = [block1];
|
|
537
|
-
for (let j = i + 1; j < blocks.length; j++) {
|
|
538
|
-
const block2 = blocks[j];
|
|
539
|
-
const key2 = `${block2.file}:${block2.startLine}`;
|
|
540
|
-
if (processed.has(key2))
|
|
541
|
-
continue;
|
|
542
|
-
// Skip if same hash (already handled in exact duplicates)
|
|
543
|
-
if (block1.hash === block2.hash)
|
|
544
|
-
continue;
|
|
545
|
-
const similarity = calculateSimilarity(block1.normalizedText, block2.normalizedText);
|
|
546
|
-
if (similarity >= threshold) {
|
|
547
|
-
similar.push(block2);
|
|
548
|
-
processed.add(key2);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
if (similar.length > 1) {
|
|
552
|
-
violations.push(createDuplicateViolation(similar, 'similar-logic', calculateAverageSimilarity(similar)));
|
|
553
|
-
similar.forEach(b => processed.add(`${b.file}:${b.startLine}`));
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
return violations;
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* Calculate similarity between two strings (0-1)
|
|
560
|
-
*/
|
|
561
|
-
function calculateSimilarity(str1, str2) {
|
|
562
|
-
// Simple token-based similarity
|
|
563
|
-
const tokens1 = tokenize(str1);
|
|
564
|
-
const tokens2 = tokenize(str2);
|
|
565
|
-
const set1 = new Set(tokens1);
|
|
566
|
-
const set2 = new Set(tokens2);
|
|
567
|
-
const intersection = new Set([...set1].filter(x => set2.has(x)));
|
|
568
|
-
const union = new Set([...set1, ...set2]);
|
|
569
|
-
// Jaccard similarity
|
|
570
|
-
const jaccard = intersection.size / union.size;
|
|
571
|
-
// Length similarity
|
|
572
|
-
const lengthSim = Math.min(str1.length, str2.length) / Math.max(str1.length, str2.length);
|
|
573
|
-
// Combined similarity
|
|
574
|
-
return (jaccard * 0.7 + lengthSim * 0.3);
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Tokenize code for similarity comparison
|
|
578
|
-
*/
|
|
579
|
-
function tokenize(code) {
|
|
580
|
-
return code
|
|
581
|
-
.split(/\s+/)
|
|
582
|
-
.filter(token => token.length > 0)
|
|
583
|
-
.map(token => token.toLowerCase());
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Calculate average similarity for a group
|
|
587
|
-
*/
|
|
588
|
-
function calculateAverageSimilarity(blocks) {
|
|
589
|
-
if (blocks.length < 2)
|
|
590
|
-
return 1;
|
|
591
|
-
let totalSim = 0;
|
|
592
|
-
let count = 0;
|
|
593
|
-
for (let i = 0; i < blocks.length - 1; i++) {
|
|
594
|
-
for (let j = i + 1; j < blocks.length; j++) {
|
|
595
|
-
totalSim += calculateSimilarity(blocks[i].normalizedText, blocks[j].normalizedText);
|
|
596
|
-
count++;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return totalSim / count;
|
|
600
|
-
}
|
|
601
|
-
/**
|
|
602
|
-
* Create a duplicate violation
|
|
603
|
-
*/
|
|
604
|
-
function createDuplicateViolation(blocks, type, similarity) {
|
|
605
|
-
const primary = blocks[0];
|
|
606
|
-
const locations = blocks.map(b => ({ file: b.file, line: b.startLine }));
|
|
607
|
-
const totalLines = blocks.reduce((sum, b) => sum + (b.endLine - b.startLine + 1), 0);
|
|
608
|
-
return {
|
|
609
|
-
analyzer: 'dry',
|
|
610
|
-
file: primary.file,
|
|
611
|
-
line: primary.startLine,
|
|
612
|
-
severity: type === 'exact-duplicate' ? 'warning' : 'suggestion',
|
|
613
|
-
type,
|
|
614
|
-
message: `${type === 'exact-duplicate' ? 'Exact duplicate' : 'Similar'} code found in ${blocks.length} locations`,
|
|
615
|
-
recommendation: `Consider extracting this ${primary.type} into a shared utility function`,
|
|
616
|
-
locations,
|
|
617
|
-
similarity,
|
|
618
|
-
metrics: {
|
|
619
|
-
duplicateLines: totalLines - (primary.endLine - primary.startLine + 1),
|
|
620
|
-
totalLines
|
|
621
|
-
},
|
|
622
|
-
estimatedEffort: 'medium'
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
/**
|
|
626
|
-
* Create a string duplicate violation
|
|
627
|
-
*/
|
|
628
|
-
function createStringDuplicateViolation(str, locations) {
|
|
629
|
-
return {
|
|
630
|
-
analyzer: 'dry',
|
|
631
|
-
file: locations[0].file,
|
|
632
|
-
line: locations[0].line,
|
|
633
|
-
severity: 'suggestion',
|
|
634
|
-
type: 'exact-duplicate',
|
|
635
|
-
message: `String literal "${str.substring(0, 50)}${str.length > 50 ? '...' : ''}" appears ${locations.length} times`,
|
|
636
|
-
recommendation: 'Consider extracting this string into a named constant',
|
|
637
|
-
locations,
|
|
638
|
-
estimatedEffort: 'small'
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Create an import duplicate violation
|
|
643
|
-
*/
|
|
644
|
-
function createImportDuplicateViolation(importKey, locations) {
|
|
645
|
-
const [moduleSpec] = importKey.split(':');
|
|
646
|
-
return {
|
|
647
|
-
analyzer: 'dry',
|
|
648
|
-
file: locations[0].file,
|
|
649
|
-
line: locations[0].line,
|
|
650
|
-
severity: 'suggestion',
|
|
651
|
-
type: 'pattern-duplication',
|
|
652
|
-
message: `Same import pattern from "${moduleSpec}" used in ${locations.length} files`,
|
|
653
|
-
recommendation: 'Consider creating a barrel export or shared import module',
|
|
654
|
-
locations: locations.map(l => ({ file: l.file, line: l.line })),
|
|
655
|
-
estimatedEffort: 'small'
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* Create an unused import violation
|
|
660
|
-
*/
|
|
661
|
-
function createUnusedImportViolation(location) {
|
|
662
|
-
return {
|
|
663
|
-
analyzer: 'dry',
|
|
664
|
-
file: location.file,
|
|
665
|
-
line: location.line,
|
|
666
|
-
severity: 'suggestion',
|
|
667
|
-
type: 'pattern-duplication',
|
|
668
|
-
message: `Unused import '${location.importName}' from '${location.moduleSpecifier}'`,
|
|
669
|
-
recommendation: `Remove this unused import to keep the codebase clean and reduce bundle size`,
|
|
670
|
-
estimatedEffort: 'small'
|
|
671
|
-
};
|
|
672
|
-
}
|
|
673
|
-
/**
|
|
674
|
-
* DRY Analyzer definition
|
|
675
|
-
*/
|
|
676
|
-
export const dryAnalyzer = {
|
|
677
|
-
name: 'dry',
|
|
678
|
-
defaultConfig: DEFAULT_CONFIG,
|
|
679
|
-
analyze: async (files, config, options, progressCallback) => {
|
|
680
|
-
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
681
|
-
const logger = new DebugLogger(mergedConfig.debug || false);
|
|
682
|
-
logger.log('DRY Analyzer started', {
|
|
683
|
-
filesCount: files.length,
|
|
684
|
-
config: mergedConfig
|
|
685
|
-
});
|
|
686
|
-
// Report initial progress
|
|
687
|
-
if (progressCallback) {
|
|
688
|
-
progressCallback({
|
|
689
|
-
current: 0,
|
|
690
|
-
total: files.length,
|
|
691
|
-
analyzer: 'dry',
|
|
692
|
-
phase: 'indexing'
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
// Build code index
|
|
696
|
-
const startTime = Date.now();
|
|
697
|
-
const index = await buildCodeIndex(files, mergedConfig, logger);
|
|
698
|
-
// Report analysis progress
|
|
699
|
-
if (progressCallback) {
|
|
700
|
-
progressCallback({
|
|
701
|
-
current: files.length / 2,
|
|
702
|
-
total: files.length,
|
|
703
|
-
analyzer: 'dry',
|
|
704
|
-
phase: 'analyzing'
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
// Find duplicates
|
|
708
|
-
const violations = findDuplicates(index, mergedConfig, logger);
|
|
709
|
-
logger.log('DRY Analysis complete', {
|
|
710
|
-
violationsFound: violations.length,
|
|
711
|
-
executionTime: Date.now() - startTime
|
|
712
|
-
});
|
|
713
|
-
// Write debug log to file
|
|
714
|
-
if (mergedConfig.debug && mergedConfig.debugLogPath) {
|
|
715
|
-
await logger.writeToFile(mergedConfig.debugLogPath);
|
|
716
|
-
}
|
|
717
|
-
// Complete
|
|
718
|
-
if (progressCallback) {
|
|
719
|
-
progressCallback({
|
|
720
|
-
current: files.length,
|
|
721
|
-
total: files.length,
|
|
722
|
-
analyzer: 'dry',
|
|
723
|
-
phase: 'complete'
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
return {
|
|
727
|
-
violations,
|
|
728
|
-
filesProcessed: files.length,
|
|
729
|
-
executionTime: Date.now() - startTime,
|
|
730
|
-
analyzerName: 'dry'
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
};
|
|
734
|
-
//# sourceMappingURL=dryAnalyzer.js.map
|