code-auditor-mcp 3.1.1 → 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 -2
- 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 -3
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.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 -2
- 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 -53
- 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 +61 -4
- 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,847 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SOLID Principles Analyzer - Functional Implementation
|
|
3
|
-
* Detects violations of SOLID principles in TypeScript/JavaScript code
|
|
4
|
-
*/
|
|
5
|
-
import { ResponsibilityType } from '../types.js';
|
|
6
|
-
import { createViolation, getNodePosition, findNodesOfType, getNodeName, traverseAST, processFiles } from './analyzerUtils.js';
|
|
7
|
-
import { hasModifier } from '../languages/adapterBridge.js';
|
|
8
|
-
import { isReactComponent, getComponentName, detectComponentType, extractHooks, extractPropTypes } from '../utils/reactDetection.js';
|
|
9
|
-
import { detectComponentResponsibilities, areResponsibilitiesRelated } from '../utils/componentResponsibility.js';
|
|
10
|
-
import { detectComponentPattern, getUnrelatedResponsibilities } from '../utils/componentPatterns.js';
|
|
11
|
-
import { CodeIndexDB } from '../codeIndexDB.js';
|
|
12
|
-
import { WhitelistType } from '../types/whitelist.js';
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// Helpers
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
/** Get raw text from a tree-sitter node (stored on ASTNode.raw). */
|
|
17
|
-
function rawText(node) {
|
|
18
|
-
if (!node)
|
|
19
|
-
return '';
|
|
20
|
-
return node.raw?.text ?? '';
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Default configuration
|
|
24
|
-
*/
|
|
25
|
-
// Database instance for whitelist checking
|
|
26
|
-
let dbInstance = null;
|
|
27
|
-
async function getDB() {
|
|
28
|
-
if (!dbInstance) {
|
|
29
|
-
dbInstance = CodeIndexDB.getInstance();
|
|
30
|
-
await dbInstance.initialize();
|
|
31
|
-
}
|
|
32
|
-
return dbInstance;
|
|
33
|
-
}
|
|
34
|
-
const DEFAULT_CONFIG = {
|
|
35
|
-
maxMethodsPerClass: 15, // Increased from 10 - reasonable for service classes
|
|
36
|
-
maxLinesPerMethod: 50,
|
|
37
|
-
maxParametersPerMethod: 4,
|
|
38
|
-
maxClassComplexity: 50,
|
|
39
|
-
maxInterfaceMembers: 20, // Increased from 15 - configuration interfaces need more flexibility
|
|
40
|
-
checkDependencyInversion: false, // Disabled by default - DI is not standard pattern
|
|
41
|
-
checkInterfaceSegregation: true,
|
|
42
|
-
checkLiskovSubstitution: true,
|
|
43
|
-
// Component-specific defaults
|
|
44
|
-
enableComponentSRP: true,
|
|
45
|
-
componentPatterns: {
|
|
46
|
-
patterns: [],
|
|
47
|
-
customPatterns: undefined,
|
|
48
|
-
enablePatternDetection: true
|
|
49
|
-
},
|
|
50
|
-
maxUnrelatedResponsibilities: 3, // Increased from 2 - allow URL management + UI + state
|
|
51
|
-
contextAwareThresholds: true,
|
|
52
|
-
checkUnrelatedResponsibilities: true // Check for unrelated responsibilities by default
|
|
53
|
-
};
|
|
54
|
-
/**
|
|
55
|
-
* Helper: Check if import is from a framework/library
|
|
56
|
-
*/
|
|
57
|
-
function isFrameworkImport(importPath) {
|
|
58
|
-
const frameworkPatterns = [
|
|
59
|
-
// React ecosystem
|
|
60
|
-
/^(react|react-dom|react-router|react-redux)/,
|
|
61
|
-
// Next.js
|
|
62
|
-
/^(next|@next)\//,
|
|
63
|
-
// Vue ecosystem
|
|
64
|
-
/^(vue|vue-router|vuex|@vue)/,
|
|
65
|
-
// Angular
|
|
66
|
-
/^(@angular|angular)/,
|
|
67
|
-
// UI frameworks
|
|
68
|
-
/^@(mui|mantine|chakra-ui|ant-design|arco-design)/,
|
|
69
|
-
// Node frameworks
|
|
70
|
-
/^(express|fastify|koa|hapi|@nestjs)/,
|
|
71
|
-
// Auth libraries
|
|
72
|
-
/^(@clerk|@auth0|@stackframe|next-auth)/,
|
|
73
|
-
// Database clients
|
|
74
|
-
/^(pg|mongodb|@prisma|redis|ioredis)/,
|
|
75
|
-
// HTTP clients
|
|
76
|
-
/^(axios|node-fetch|got|ky)/,
|
|
77
|
-
// Testing
|
|
78
|
-
/^(@testing-library|jest|vitest|mocha|chai)/,
|
|
79
|
-
// Build tools
|
|
80
|
-
/^(webpack|vite|rollup|parcel|esbuild)/,
|
|
81
|
-
// Common utilities that shouldn't trigger DI violations
|
|
82
|
-
/^(lodash|underscore|ramda|date-fns|moment|dayjs)/,
|
|
83
|
-
// Node modules indicator
|
|
84
|
-
/node_modules/,
|
|
85
|
-
// Anything from npm scope
|
|
86
|
-
/^@[\w-]+\//
|
|
87
|
-
];
|
|
88
|
-
return frameworkPatterns.some(pattern => pattern.test(importPath));
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Helper: Find import declaration for an identifier
|
|
92
|
-
*/
|
|
93
|
-
function findImportForIdentifier(rootNode, identifier) {
|
|
94
|
-
let importPath;
|
|
95
|
-
traverseAST(rootNode, node => {
|
|
96
|
-
if (node.type !== 'import_statement')
|
|
97
|
-
return;
|
|
98
|
-
const stringNode = node.children?.find(c => c.type === 'string');
|
|
99
|
-
if (!stringNode)
|
|
100
|
-
return;
|
|
101
|
-
const moduleSpecifier = rawText(stringNode).replace(/^["']|["']$/g, '');
|
|
102
|
-
const importClause = node.children?.find(c => c.type === 'import_clause');
|
|
103
|
-
if (!importClause)
|
|
104
|
-
return;
|
|
105
|
-
// Check named imports
|
|
106
|
-
const namedImports = importClause.children?.find(c => c.type === 'named_imports');
|
|
107
|
-
if (namedImports) {
|
|
108
|
-
for (const spec of namedImports.children ?? []) {
|
|
109
|
-
if (spec.type !== 'import_specifier')
|
|
110
|
-
continue;
|
|
111
|
-
const identifiers = spec.children?.filter(c => c.type === 'identifier') ?? [];
|
|
112
|
-
for (const idNode of identifiers) {
|
|
113
|
-
if (rawText(idNode) === identifier) {
|
|
114
|
-
importPath = moduleSpecifier;
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// Check default import
|
|
121
|
-
const defaultImport = importClause.children?.find(c => c.type === 'identifier');
|
|
122
|
-
if (defaultImport && rawText(defaultImport) === identifier) {
|
|
123
|
-
importPath = moduleSpecifier;
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
return importPath;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Architectural layer detection for context-aware analysis
|
|
130
|
-
*/
|
|
131
|
-
var ArchitecturalLayer;
|
|
132
|
-
(function (ArchitecturalLayer) {
|
|
133
|
-
ArchitecturalLayer["Route"] = "route";
|
|
134
|
-
ArchitecturalLayer["Middleware"] = "middleware";
|
|
135
|
-
ArchitecturalLayer["Component"] = "component";
|
|
136
|
-
ArchitecturalLayer["Library"] = "library";
|
|
137
|
-
ArchitecturalLayer["Repository"] = "repository";
|
|
138
|
-
ArchitecturalLayer["Service"] = "service";
|
|
139
|
-
ArchitecturalLayer["Hook"] = "hook";
|
|
140
|
-
ArchitecturalLayer["Utility"] = "utility";
|
|
141
|
-
ArchitecturalLayer["Config"] = "config";
|
|
142
|
-
})(ArchitecturalLayer || (ArchitecturalLayer = {}));
|
|
143
|
-
/**
|
|
144
|
-
* Helper: Detect architectural layer from file path
|
|
145
|
-
*/
|
|
146
|
-
function detectArchitecturalLayer(filePath) {
|
|
147
|
-
const patterns = [
|
|
148
|
-
// Next.js App Router patterns
|
|
149
|
-
[ArchitecturalLayer.Route, /\/(app|pages)\/.*\/(route|page|layout|error|loading|not-found)\.(ts|tsx|js|jsx)$/],
|
|
150
|
-
[ArchitecturalLayer.Route, /\/api\/.*\/route\.(ts|js)$/],
|
|
151
|
-
// Middleware patterns
|
|
152
|
-
[ArchitecturalLayer.Middleware, /middleware\.(ts|js)$/],
|
|
153
|
-
[ArchitecturalLayer.Middleware, /\/(middleware|middlewares)\/.*\.(ts|js)$/],
|
|
154
|
-
// Component patterns
|
|
155
|
-
[ArchitecturalLayer.Component, /\/(components?|ui)\/.*\.(tsx|jsx)$/],
|
|
156
|
-
// Repository patterns
|
|
157
|
-
[ArchitecturalLayer.Repository, /\/(repositories|repository|repos?|dal|data-access)\/.*\.(ts|js)$/],
|
|
158
|
-
// Service patterns
|
|
159
|
-
[ArchitecturalLayer.Service, /\/(services?|use-cases|domain|business)\/.*\.(ts|js)$/],
|
|
160
|
-
// Hook patterns
|
|
161
|
-
[ArchitecturalLayer.Hook, /\/(hooks?)\/.*\.(ts|tsx|js|jsx)$/],
|
|
162
|
-
[ArchitecturalLayer.Hook, /use[A-Z]\w+\.(ts|tsx|js|jsx)$/],
|
|
163
|
-
// Utility patterns
|
|
164
|
-
[ArchitecturalLayer.Utility, /\/(utils?|utilities|helpers?|lib)\/.*\.(ts|js)$/],
|
|
165
|
-
// Config patterns
|
|
166
|
-
[ArchitecturalLayer.Config, /\/(config|constants)\/.*\.(ts|js)$/],
|
|
167
|
-
[ArchitecturalLayer.Config, /\.(config|constants)\.(ts|js)$/]
|
|
168
|
-
];
|
|
169
|
-
for (const [layer, pattern] of patterns) {
|
|
170
|
-
if (pattern.test(filePath)) {
|
|
171
|
-
return layer;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
// Default to library for unmatched files
|
|
175
|
-
return ArchitecturalLayer.Library;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Helper: Get context-aware thresholds based on architectural layer
|
|
179
|
-
*/
|
|
180
|
-
function getLayerThresholds(layer, config) {
|
|
181
|
-
const layerConfigs = {
|
|
182
|
-
[ArchitecturalLayer.Route]: {
|
|
183
|
-
maxMethodsPerClass: 20, // Routes can have more methods
|
|
184
|
-
maxUnrelatedResponsibilities: 4 // Routes often coordinate multiple concerns
|
|
185
|
-
},
|
|
186
|
-
[ArchitecturalLayer.Component]: {
|
|
187
|
-
maxUnrelatedResponsibilities: 3, // Components can manage UI + state + events
|
|
188
|
-
maxMethodsPerClass: 12
|
|
189
|
-
},
|
|
190
|
-
[ArchitecturalLayer.Repository]: {
|
|
191
|
-
maxMethodsPerClass: 25, // Repositories can have many data access methods
|
|
192
|
-
maxUnrelatedResponsibilities: 1 // Should focus on data access only
|
|
193
|
-
},
|
|
194
|
-
[ArchitecturalLayer.Service]: {
|
|
195
|
-
maxMethodsPerClass: 15,
|
|
196
|
-
maxUnrelatedResponsibilities: 2
|
|
197
|
-
},
|
|
198
|
-
[ArchitecturalLayer.Hook]: {
|
|
199
|
-
maxMethodsPerClass: 5,
|
|
200
|
-
maxUnrelatedResponsibilities: 1 // Hooks should have single purpose
|
|
201
|
-
},
|
|
202
|
-
[ArchitecturalLayer.Middleware]: {
|
|
203
|
-
maxMethodsPerClass: 8,
|
|
204
|
-
maxUnrelatedResponsibilities: 1
|
|
205
|
-
},
|
|
206
|
-
[ArchitecturalLayer.Utility]: {
|
|
207
|
-
maxMethodsPerClass: 20, // Utility modules can have many functions
|
|
208
|
-
maxUnrelatedResponsibilities: 3 // Utilities may group related functions
|
|
209
|
-
},
|
|
210
|
-
[ArchitecturalLayer.Config]: {
|
|
211
|
-
maxMethodsPerClass: 50, // Config files may export many settings
|
|
212
|
-
maxInterfaceMembers: 30 // Config interfaces need flexibility
|
|
213
|
-
},
|
|
214
|
-
[ArchitecturalLayer.Library]: {
|
|
215
|
-
// Use defaults from config
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
return config.contextAwareThresholds ? (layerConfigs[layer] || {}) : {};
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Helper: Check if file is a test file
|
|
222
|
-
*/
|
|
223
|
-
function isTestFile(filePath) {
|
|
224
|
-
const testPatterns = [
|
|
225
|
-
/\.test\.[jt]sx?$/,
|
|
226
|
-
/\.spec\.[jt]sx?$/,
|
|
227
|
-
/\.test-d\.ts$/,
|
|
228
|
-
/__tests__\//,
|
|
229
|
-
/test\//,
|
|
230
|
-
/tests\//,
|
|
231
|
-
/spec\//,
|
|
232
|
-
/\.e2e\.[jt]s$/,
|
|
233
|
-
];
|
|
234
|
-
return testPatterns.some(pattern => pattern.test(filePath));
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Analyze a file for SOLID violations
|
|
238
|
-
*/
|
|
239
|
-
const analyzeFile = async (filePath, ast, config) => {
|
|
240
|
-
const violations = [];
|
|
241
|
-
const isTest = isTestFile(filePath);
|
|
242
|
-
const rootNode = ast.root;
|
|
243
|
-
// Detect architectural layer for context-aware analysis
|
|
244
|
-
const layer = detectArchitecturalLayer(filePath);
|
|
245
|
-
const layerThresholds = getLayerThresholds(layer, config);
|
|
246
|
-
// Layer thresholds should not override explicit user configuration
|
|
247
|
-
// Only apply layer defaults if the user hasn't configured them
|
|
248
|
-
const effectiveConfig = { ...layerThresholds, ...config };
|
|
249
|
-
// Check Single Responsibility Principle for classes
|
|
250
|
-
const classes = findNodesOfType(rootNode, n => n.type === 'class_declaration');
|
|
251
|
-
classes.forEach(cls => {
|
|
252
|
-
violations.push(...checkSingleResponsibility(cls, filePath, effectiveConfig));
|
|
253
|
-
});
|
|
254
|
-
// Check Single Responsibility Principle for React components
|
|
255
|
-
if (config.enableComponentSRP) {
|
|
256
|
-
const processedComponents = new Set();
|
|
257
|
-
traverseAST(rootNode, node => {
|
|
258
|
-
if (isReactComponent(node)) {
|
|
259
|
-
// Skip if this is an arrow function that's part of a variable declaration
|
|
260
|
-
// that we've already processed
|
|
261
|
-
if (node.type === 'arrow_function' && node.parent?.type === 'variable_declarator' &&
|
|
262
|
-
processedComponents.has(node.parent)) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
// For variable declarations with arrow functions, process the declaration
|
|
266
|
-
// not the arrow function itself
|
|
267
|
-
if (node.type === 'variable_declarator') {
|
|
268
|
-
const initializer = node.children?.find(c => c.type !== 'identifier' && c.type !== 'type_annotation');
|
|
269
|
-
if (initializer?.type === 'arrow_function') {
|
|
270
|
-
if (!processedComponents.has(node)) {
|
|
271
|
-
processedComponents.add(node);
|
|
272
|
-
violations.push(...checkComponentSRP(node, filePath, effectiveConfig));
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
else if (!processedComponents.has(node)) {
|
|
277
|
-
processedComponents.add(node);
|
|
278
|
-
violations.push(...checkComponentSRP(node, filePath, effectiveConfig));
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
// Check Open/Closed Principle
|
|
284
|
-
traverseAST(rootNode, node => {
|
|
285
|
-
violations.push(...checkOpenClosed(node, filePath, effectiveConfig));
|
|
286
|
-
});
|
|
287
|
-
// Check Liskov Substitution Principle
|
|
288
|
-
if (effectiveConfig.checkLiskovSubstitution) {
|
|
289
|
-
violations.push(...checkLiskovSubstitution(rootNode, filePath, effectiveConfig));
|
|
290
|
-
}
|
|
291
|
-
// Check Interface Segregation Principle
|
|
292
|
-
if (effectiveConfig.checkInterfaceSegregation) {
|
|
293
|
-
const interfaces = findNodesOfType(rootNode, n => n.type === 'interface_declaration');
|
|
294
|
-
interfaces.forEach(iface => {
|
|
295
|
-
violations.push(...checkInterfaceSegregation(iface, filePath, effectiveConfig));
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
// Check Dependency Inversion Principle (skip for test files)
|
|
299
|
-
if (effectiveConfig.checkDependencyInversion && !isTest) {
|
|
300
|
-
violations.push(...await checkDependencyInversion(rootNode, filePath, effectiveConfig));
|
|
301
|
-
}
|
|
302
|
-
return violations;
|
|
303
|
-
};
|
|
304
|
-
/**
|
|
305
|
-
* Check Single Responsibility Principle
|
|
306
|
-
*/
|
|
307
|
-
function checkSingleResponsibility(node, filePath, config) {
|
|
308
|
-
const violations = [];
|
|
309
|
-
const className = getNodeName(node) || 'Anonymous';
|
|
310
|
-
// Count methods
|
|
311
|
-
const methods = findNodesOfType(node, n => n.type === 'method_definition');
|
|
312
|
-
const publicMethods = methods.filter(m => !hasModifier(m, 'private'));
|
|
313
|
-
if (publicMethods.length > config.maxMethodsPerClass) {
|
|
314
|
-
const { line, column } = getNodePosition(node);
|
|
315
|
-
violations.push(createViolation({
|
|
316
|
-
analyzer: 'solid',
|
|
317
|
-
file: filePath,
|
|
318
|
-
line,
|
|
319
|
-
column,
|
|
320
|
-
severity: 'warning',
|
|
321
|
-
message: `Class "${className}" has ${publicMethods.length} public methods (max: ${config.maxMethodsPerClass})`,
|
|
322
|
-
type: 'solid',
|
|
323
|
-
principle: 'single-responsibility',
|
|
324
|
-
recommendation: 'Consider splitting this class into smaller, more focused classes',
|
|
325
|
-
details: {
|
|
326
|
-
className,
|
|
327
|
-
methodCount: publicMethods.length,
|
|
328
|
-
threshold: config.maxMethodsPerClass
|
|
329
|
-
}
|
|
330
|
-
}));
|
|
331
|
-
}
|
|
332
|
-
// Check for multiple responsibilities
|
|
333
|
-
const responsibilities = detectResponsibilities(node);
|
|
334
|
-
if (responsibilities.length > 1) {
|
|
335
|
-
const { line, column } = getNodePosition(node);
|
|
336
|
-
violations.push(createViolation({
|
|
337
|
-
analyzer: 'solid',
|
|
338
|
-
file: filePath,
|
|
339
|
-
line,
|
|
340
|
-
column,
|
|
341
|
-
severity: 'warning',
|
|
342
|
-
message: `Class "${className}" appears to have multiple responsibilities`,
|
|
343
|
-
type: 'solid',
|
|
344
|
-
principle: 'single-responsibility',
|
|
345
|
-
recommendation: `Consider splitting based on: ${responsibilities.join(', ')}`,
|
|
346
|
-
details: {
|
|
347
|
-
className,
|
|
348
|
-
responsibilities
|
|
349
|
-
}
|
|
350
|
-
}));
|
|
351
|
-
}
|
|
352
|
-
return violations;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Check Open/Closed Principle
|
|
356
|
-
*/
|
|
357
|
-
function checkOpenClosed(node, filePath, config) {
|
|
358
|
-
const violations = [];
|
|
359
|
-
// Check for long if-else chains
|
|
360
|
-
if (node.type === 'if_statement') {
|
|
361
|
-
const chainLength = countIfElseChain(node);
|
|
362
|
-
if (chainLength > 3) {
|
|
363
|
-
const { line, column } = getNodePosition(node);
|
|
364
|
-
violations.push(createViolation({
|
|
365
|
-
analyzer: 'solid',
|
|
366
|
-
file: filePath,
|
|
367
|
-
line,
|
|
368
|
-
column,
|
|
369
|
-
severity: 'warning',
|
|
370
|
-
message: `Long if-else chain (${chainLength} branches) violates Open/Closed Principle`,
|
|
371
|
-
type: 'solid',
|
|
372
|
-
principle: 'open-closed',
|
|
373
|
-
recommendation: 'Consider using polymorphism, strategy pattern, or a mapping object',
|
|
374
|
-
details: {
|
|
375
|
-
chainLength
|
|
376
|
-
}
|
|
377
|
-
}));
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
// Check for switch statements with many cases
|
|
381
|
-
if (node.type === 'switch_statement') {
|
|
382
|
-
const cases = findNodesOfType(node, n => n.type === 'switch_case' || n.type === 'case_clause');
|
|
383
|
-
if (cases.length > 5) {
|
|
384
|
-
const { line, column } = getNodePosition(node);
|
|
385
|
-
violations.push(createViolation({
|
|
386
|
-
analyzer: 'solid',
|
|
387
|
-
file: filePath,
|
|
388
|
-
line,
|
|
389
|
-
column,
|
|
390
|
-
severity: 'warning',
|
|
391
|
-
message: `Switch statement with ${cases.length} cases violates Open/Closed Principle`,
|
|
392
|
-
type: 'solid',
|
|
393
|
-
principle: 'open-closed',
|
|
394
|
-
recommendation: 'Consider using polymorphism or a strategy pattern',
|
|
395
|
-
details: {
|
|
396
|
-
caseCount: cases.length
|
|
397
|
-
}
|
|
398
|
-
}));
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return violations;
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* Check Liskov Substitution Principle
|
|
405
|
-
*/
|
|
406
|
-
function checkLiskovSubstitution(rootNode, filePath, config) {
|
|
407
|
-
const violations = [];
|
|
408
|
-
// Find method overrides that throw errors
|
|
409
|
-
traverseAST(rootNode, node => {
|
|
410
|
-
if (node.type === 'method_definition' && hasModifier(node, 'override')) {
|
|
411
|
-
const hasThrow = findNodesOfType(node, n => n.type === 'throw_statement').length > 0;
|
|
412
|
-
const body = node.children?.find(c => c.type === 'statement_block');
|
|
413
|
-
const bodyChildren = body?.children ?? [];
|
|
414
|
-
const isNotImplemented = bodyChildren.length === 0 ||
|
|
415
|
-
(bodyChildren.length === 1 && bodyChildren[0].type === 'throw_statement');
|
|
416
|
-
if (hasThrow || isNotImplemented) {
|
|
417
|
-
const { line, column } = getNodePosition(node);
|
|
418
|
-
const methodName = getNodeName(node) || 'unknown';
|
|
419
|
-
violations.push(createViolation({
|
|
420
|
-
analyzer: 'solid',
|
|
421
|
-
file: filePath,
|
|
422
|
-
line,
|
|
423
|
-
column,
|
|
424
|
-
severity: 'critical',
|
|
425
|
-
message: 'Method override throws error or is not implemented',
|
|
426
|
-
type: 'solid',
|
|
427
|
-
principle: 'liskov-substitution',
|
|
428
|
-
recommendation: 'All overridden methods should maintain behavioral compatibility with base class',
|
|
429
|
-
details: {
|
|
430
|
-
methodName
|
|
431
|
-
}
|
|
432
|
-
}));
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
return violations;
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Check Interface Segregation Principle
|
|
440
|
-
*/
|
|
441
|
-
function checkInterfaceSegregation(node, filePath, config) {
|
|
442
|
-
const violations = [];
|
|
443
|
-
const interfaceName = getNodeName(node) || 'Anonymous';
|
|
444
|
-
// Count interface members (property_signature + method_signature children)
|
|
445
|
-
const memberCount = (node.children ?? []).filter(c => c.type === 'property_signature' || c.type === 'method_signature').length;
|
|
446
|
-
if (memberCount > config.maxInterfaceMembers) {
|
|
447
|
-
const { line, column } = getNodePosition(node);
|
|
448
|
-
violations.push(createViolation({
|
|
449
|
-
analyzer: 'solid',
|
|
450
|
-
file: filePath,
|
|
451
|
-
line,
|
|
452
|
-
column,
|
|
453
|
-
severity: 'critical',
|
|
454
|
-
message: `Interface "${interfaceName}" has too many members (${memberCount})`,
|
|
455
|
-
type: 'solid',
|
|
456
|
-
principle: 'interface-segregation',
|
|
457
|
-
recommendation: 'Split this interface into smaller, more focused interfaces',
|
|
458
|
-
details: {
|
|
459
|
-
interfaceName,
|
|
460
|
-
memberCount,
|
|
461
|
-
threshold: config.maxInterfaceMembers
|
|
462
|
-
}
|
|
463
|
-
}));
|
|
464
|
-
}
|
|
465
|
-
return violations;
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* Check Dependency Inversion Principle
|
|
469
|
-
*/
|
|
470
|
-
async function checkDependencyInversion(rootNode, filePath, config) {
|
|
471
|
-
const violations = [];
|
|
472
|
-
// Skip import checking - DI principle should focus on instantiation, not imports
|
|
473
|
-
// Imports are necessary and checking them creates too many false positives
|
|
474
|
-
// Check for 'new' expressions (direct instantiation)
|
|
475
|
-
const newExpressions = findNodesOfType(rootNode, n => n.type === 'new_expression');
|
|
476
|
-
for (const expr of newExpressions) {
|
|
477
|
-
// Find the class name identifier (first non-keyword named child)
|
|
478
|
-
const exprIdent = expr.children?.find(c => c.type === 'identifier');
|
|
479
|
-
if (!exprIdent)
|
|
480
|
-
continue;
|
|
481
|
-
const className = rawText(exprIdent);
|
|
482
|
-
// Skip if this is inside a singleton pattern
|
|
483
|
-
if (isInsideSingletonPattern(expr)) {
|
|
484
|
-
continue;
|
|
485
|
-
}
|
|
486
|
-
// Skip if this is inside a factory method
|
|
487
|
-
if (isInsideFactoryMethod(expr)) {
|
|
488
|
-
continue;
|
|
489
|
-
}
|
|
490
|
-
// Skip Error classes - they need to be instantiated
|
|
491
|
-
if (isErrorClass(className)) {
|
|
492
|
-
continue;
|
|
493
|
-
}
|
|
494
|
-
// Check if this class is imported from a framework/library
|
|
495
|
-
const importPath = findImportForIdentifier(rootNode, className);
|
|
496
|
-
if (importPath && isFrameworkImport(importPath)) {
|
|
497
|
-
continue; // Skip framework classes
|
|
498
|
-
}
|
|
499
|
-
if (await isConcreteClass(className)) {
|
|
500
|
-
const { line, column } = getNodePosition(expr);
|
|
501
|
-
violations.push(createViolation({
|
|
502
|
-
analyzer: 'solid',
|
|
503
|
-
file: filePath,
|
|
504
|
-
line,
|
|
505
|
-
column,
|
|
506
|
-
severity: 'warning',
|
|
507
|
-
message: `Direct instantiation of concrete class "${className}"`,
|
|
508
|
-
type: 'solid',
|
|
509
|
-
principle: 'dependency-inversion',
|
|
510
|
-
recommendation: 'Consider using dependency injection or factory pattern',
|
|
511
|
-
details: {
|
|
512
|
-
className,
|
|
513
|
-
importPath: importPath || 'local'
|
|
514
|
-
}
|
|
515
|
-
}));
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
return violations;
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Helper: Detect responsibilities in a class
|
|
522
|
-
*/
|
|
523
|
-
function detectResponsibilities(node) {
|
|
524
|
-
const responsibilities = new Set();
|
|
525
|
-
const methods = findNodesOfType(node, n => n.type === 'method_definition');
|
|
526
|
-
methods.forEach(method => {
|
|
527
|
-
const name = getNodeName(method)?.toLowerCase() || '';
|
|
528
|
-
// Detect different concerns
|
|
529
|
-
if (name.includes('save') || name.includes('load') || name.includes('persist')) {
|
|
530
|
-
responsibilities.add('data persistence');
|
|
531
|
-
}
|
|
532
|
-
if (name.includes('validate') || name.includes('check')) {
|
|
533
|
-
responsibilities.add('validation');
|
|
534
|
-
}
|
|
535
|
-
if (name.includes('render') || name.includes('display') || name.includes('show')) {
|
|
536
|
-
responsibilities.add('presentation');
|
|
537
|
-
}
|
|
538
|
-
if (name.includes('send') || name.includes('fetch') || name.includes('request')) {
|
|
539
|
-
responsibilities.add('communication');
|
|
540
|
-
}
|
|
541
|
-
if (name.includes('calculate') || name.includes('compute') || name.includes('process')) {
|
|
542
|
-
responsibilities.add('business logic');
|
|
543
|
-
}
|
|
544
|
-
if (name.includes('log') || name.includes('track') || name.includes('monitor')) {
|
|
545
|
-
responsibilities.add('logging/monitoring');
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
// Check for too many public methods as a responsibility
|
|
549
|
-
const publicMethods = methods.filter(m => !hasModifier(m, 'private'));
|
|
550
|
-
if (publicMethods.length > 10) {
|
|
551
|
-
responsibilities.add('too many methods');
|
|
552
|
-
}
|
|
553
|
-
return Array.from(responsibilities);
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Helper: Count if-else chain length
|
|
557
|
-
*/
|
|
558
|
-
function countIfElseChain(node) {
|
|
559
|
-
let count = 1;
|
|
560
|
-
let elseClause = node.children?.find(c => c.type === 'else_clause');
|
|
561
|
-
while (elseClause) {
|
|
562
|
-
count++;
|
|
563
|
-
const statement = elseClause.children?.find(c => c.type !== 'else');
|
|
564
|
-
if (statement?.type === 'if_statement') {
|
|
565
|
-
elseClause = statement.children?.find(c => c.type === 'else_clause');
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
break;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return count;
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Helper: Check if import is a concrete dependency
|
|
575
|
-
*/
|
|
576
|
-
async function isConcreteDependency(importPath) {
|
|
577
|
-
try {
|
|
578
|
-
const db = await getDB();
|
|
579
|
-
// Skip relative paths (they're project internal) - don't flag these
|
|
580
|
-
if (importPath.startsWith('.') || importPath.startsWith('/')) {
|
|
581
|
-
return false; // Internal modules are fine
|
|
582
|
-
}
|
|
583
|
-
// Check if it's whitelisted directly
|
|
584
|
-
if (db.isWhitelisted(importPath, WhitelistType.NodeBuiltin) ||
|
|
585
|
-
db.isWhitelisted(importPath, WhitelistType.SharedLibrary) ||
|
|
586
|
-
db.isWhitelisted(importPath, WhitelistType.ProjectDependency) ||
|
|
587
|
-
db.isWhitelisted(importPath, WhitelistType.FrameworkClass)) {
|
|
588
|
-
return false;
|
|
589
|
-
}
|
|
590
|
-
// For Node.js built-ins, also check the base module name
|
|
591
|
-
// e.g., "fs/promises" -> check "fs", "node:fs/promises" -> check "fs"
|
|
592
|
-
const baseModule = importPath.replace(/^node:/, '').split('/')[0];
|
|
593
|
-
if (baseModule !== importPath) {
|
|
594
|
-
if (db.isWhitelisted(baseModule, WhitelistType.NodeBuiltin)) {
|
|
595
|
-
return false;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
// If not whitelisted, it's potentially a concrete dependency
|
|
599
|
-
return true;
|
|
600
|
-
}
|
|
601
|
-
catch (error) {
|
|
602
|
-
// If database is not available, be conservative and don't flag
|
|
603
|
-
console.warn('Database not available for dependency check:', error);
|
|
604
|
-
return false;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
/**
|
|
608
|
-
* Helper: Check if class name is an Error class
|
|
609
|
-
*/
|
|
610
|
-
function isErrorClass(className) {
|
|
611
|
-
return className.endsWith('Error') ||
|
|
612
|
-
className.endsWith('Exception') ||
|
|
613
|
-
className === 'Error';
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Helper: Check if node is inside a singleton pattern
|
|
617
|
-
*/
|
|
618
|
-
function isInsideSingletonPattern(node) {
|
|
619
|
-
let parent = node.parent;
|
|
620
|
-
while (parent) {
|
|
621
|
-
// Check if we're inside a method named getInstance, instance, singleton, etc.
|
|
622
|
-
if (parent.type === 'method_definition' || parent.type === 'function_declaration') {
|
|
623
|
-
const methodName = getNodeName(parent)?.toLowerCase();
|
|
624
|
-
if (methodName && (methodName.includes('getinstance') ||
|
|
625
|
-
methodName === 'instance' ||
|
|
626
|
-
methodName === 'singleton' ||
|
|
627
|
-
(methodName === 'create' && parent.parent?.type === 'class_declaration'))) {
|
|
628
|
-
return true;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
// Check if we're inside a static property initialization
|
|
632
|
-
if (parent.type === 'public_field_definition' && hasModifier(parent, 'static')) {
|
|
633
|
-
const propName = getNodeName(parent)?.toLowerCase();
|
|
634
|
-
if (propName && (propName === 'instance' || propName === '_instance')) {
|
|
635
|
-
return true;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
parent = parent.parent;
|
|
639
|
-
}
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
/**
|
|
643
|
-
* Helper: Check if node is inside a factory method
|
|
644
|
-
*/
|
|
645
|
-
function isInsideFactoryMethod(node) {
|
|
646
|
-
let parent = node.parent;
|
|
647
|
-
while (parent) {
|
|
648
|
-
// Check if we're inside a method with 'create' or 'factory' in its name
|
|
649
|
-
if (parent.type === 'method_definition' || parent.type === 'function_declaration') {
|
|
650
|
-
const methodName = getNodeName(parent)?.toLowerCase();
|
|
651
|
-
if (methodName && (methodName.includes('create') ||
|
|
652
|
-
methodName.includes('factory') ||
|
|
653
|
-
methodName.includes('make') ||
|
|
654
|
-
methodName.includes('build'))) {
|
|
655
|
-
return true;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
// Check if we're inside a class with Factory in its name
|
|
659
|
-
if (parent.type === 'class_declaration') {
|
|
660
|
-
const className = getNodeName(parent);
|
|
661
|
-
if (className && className.toLowerCase().includes('factory')) {
|
|
662
|
-
return true;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
parent = parent.parent;
|
|
666
|
-
}
|
|
667
|
-
return false;
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* Helper: Check if class name represents concrete implementation
|
|
671
|
-
*/
|
|
672
|
-
async function isConcreteClass(className) {
|
|
673
|
-
try {
|
|
674
|
-
const db = await getDB();
|
|
675
|
-
// Check if it's whitelisted (platform API or framework class)
|
|
676
|
-
if (db.isWhitelisted(className, WhitelistType.PlatformAPI) ||
|
|
677
|
-
db.isWhitelisted(className, WhitelistType.FrameworkClass)) {
|
|
678
|
-
return false;
|
|
679
|
-
}
|
|
680
|
-
// Skip if it looks like a type/interface name
|
|
681
|
-
if (className.startsWith('I') && className.length > 2 && className[1] === className[1].toUpperCase()) {
|
|
682
|
-
return false; // e.g., IUserService
|
|
683
|
-
}
|
|
684
|
-
if (className.endsWith('Interface') || className.endsWith('Type') || className.endsWith('Schema')) {
|
|
685
|
-
return false;
|
|
686
|
-
}
|
|
687
|
-
return true;
|
|
688
|
-
}
|
|
689
|
-
catch (error) {
|
|
690
|
-
// If database is not available, be conservative and don't flag
|
|
691
|
-
console.warn('Database not available for class check:', error);
|
|
692
|
-
return false;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
/**
|
|
696
|
-
* Check Component Single Responsibility Principle
|
|
697
|
-
*/
|
|
698
|
-
function checkComponentSRP(node, filePath, config) {
|
|
699
|
-
const violations = [];
|
|
700
|
-
// Extract component metadata
|
|
701
|
-
const componentType = detectComponentType(node);
|
|
702
|
-
if (!componentType)
|
|
703
|
-
return violations;
|
|
704
|
-
const componentName = getComponentName(node) || 'AnonymousComponent';
|
|
705
|
-
const hooks = extractHooks(node);
|
|
706
|
-
const props = extractPropTypes(node);
|
|
707
|
-
const componentInfo = {
|
|
708
|
-
name: componentName,
|
|
709
|
-
filePath,
|
|
710
|
-
lineNumber: getNodePosition(node).line,
|
|
711
|
-
language: 'typescript',
|
|
712
|
-
dependencies: [],
|
|
713
|
-
purpose: '',
|
|
714
|
-
context: '',
|
|
715
|
-
entityType: 'component',
|
|
716
|
-
componentType,
|
|
717
|
-
hooks,
|
|
718
|
-
props,
|
|
719
|
-
isExported: true
|
|
720
|
-
};
|
|
721
|
-
// Detect component responsibilities
|
|
722
|
-
const responsibilities = detectComponentResponsibilities(node, componentInfo);
|
|
723
|
-
// Detect component pattern
|
|
724
|
-
const pattern = detectComponentPattern(componentName, filePath, componentInfo.props?.map(p => p.name) || [], componentInfo.hooks?.map(h => h.name) || [], [] // TODO: Extract imports if needed
|
|
725
|
-
);
|
|
726
|
-
// Apply context-aware thresholds
|
|
727
|
-
let effectiveMaxResponsibilities = config.maxUnrelatedResponsibilities;
|
|
728
|
-
if (config.contextAwareThresholds && pattern) {
|
|
729
|
-
// Check for pattern-specific override first
|
|
730
|
-
if (config.patternThresholds?.[pattern.name]?.maxResponsibilities) {
|
|
731
|
-
effectiveMaxResponsibilities = config.patternThresholds[pattern.name].maxResponsibilities;
|
|
732
|
-
}
|
|
733
|
-
else {
|
|
734
|
-
// Fall back to multiplier-based calculation
|
|
735
|
-
effectiveMaxResponsibilities = Math.ceil(config.maxUnrelatedResponsibilities * pattern.complexityMultiplier);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
// Get unrelated responsibilities
|
|
739
|
-
const uniqueTypes = [...new Set(responsibilities.map(r => r.type))];
|
|
740
|
-
const unrelatedGroups = [];
|
|
741
|
-
if (pattern) {
|
|
742
|
-
// Use pattern-specific analysis
|
|
743
|
-
const patternUnrelated = getUnrelatedResponsibilities(uniqueTypes, pattern);
|
|
744
|
-
unrelatedGroups.push(...patternUnrelated);
|
|
745
|
-
}
|
|
746
|
-
else {
|
|
747
|
-
// Generic analysis - check for unrelated responsibility pairs
|
|
748
|
-
for (let i = 0; i < uniqueTypes.length; i++) {
|
|
749
|
-
for (let j = i + 1; j < uniqueTypes.length; j++) {
|
|
750
|
-
if (!areResponsibilitiesRelated(uniqueTypes[i], uniqueTypes[j])) {
|
|
751
|
-
unrelatedGroups.push([uniqueTypes[i], uniqueTypes[j]]);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
// Create violations only if there are actually unrelated responsibilities
|
|
757
|
-
// Skip if there's only one responsibility or all responsibilities are related
|
|
758
|
-
// Filter out single-element groups which aren't violations
|
|
759
|
-
const realUnrelatedGroups = unrelatedGroups.filter(group => group.length >= 2);
|
|
760
|
-
// Only check for unrelated groups if enabled in config
|
|
761
|
-
if (config.checkUnrelatedResponsibilities && realUnrelatedGroups.length > 0 && uniqueTypes.length > 1) {
|
|
762
|
-
const { line, column } = getNodePosition(node);
|
|
763
|
-
// Main violation for mixed responsibilities
|
|
764
|
-
violations.push(createViolation({
|
|
765
|
-
analyzer: 'solid',
|
|
766
|
-
file: filePath,
|
|
767
|
-
line,
|
|
768
|
-
column,
|
|
769
|
-
severity: 'warning',
|
|
770
|
-
message: `Component "${componentName}" has ${uniqueTypes.length} responsibilities with ${realUnrelatedGroups.length} unrelated groups`,
|
|
771
|
-
type: 'solid',
|
|
772
|
-
principle: 'single-responsibility',
|
|
773
|
-
componentName,
|
|
774
|
-
recommendation: generateComponentRefactoringRecommendation(responsibilities, pattern),
|
|
775
|
-
details: {
|
|
776
|
-
componentName,
|
|
777
|
-
componentType: componentInfo.componentType,
|
|
778
|
-
pattern: pattern?.name,
|
|
779
|
-
responsibilities: uniqueTypes,
|
|
780
|
-
unrelatedGroups: realUnrelatedGroups,
|
|
781
|
-
hookCount: componentInfo.hooks?.length || 0,
|
|
782
|
-
effectCount: responsibilities.filter(r => r.type === ResponsibilityType.SideEffects).length
|
|
783
|
-
}
|
|
784
|
-
}));
|
|
785
|
-
}
|
|
786
|
-
// Check for too many responsibilities overall
|
|
787
|
-
if (uniqueTypes.length > effectiveMaxResponsibilities) {
|
|
788
|
-
const { line, column } = getNodePosition(node);
|
|
789
|
-
violations.push(createViolation({
|
|
790
|
-
analyzer: 'solid',
|
|
791
|
-
file: filePath,
|
|
792
|
-
line,
|
|
793
|
-
column,
|
|
794
|
-
severity: 'critical',
|
|
795
|
-
message: `Component "${componentName}" has too many responsibilities (${uniqueTypes.length} > ${effectiveMaxResponsibilities})`,
|
|
796
|
-
type: 'solid',
|
|
797
|
-
principle: 'single-responsibility',
|
|
798
|
-
componentName,
|
|
799
|
-
recommendation: 'Split this component into smaller, focused components',
|
|
800
|
-
details: {
|
|
801
|
-
componentName,
|
|
802
|
-
responsibilityCount: uniqueTypes.length,
|
|
803
|
-
threshold: effectiveMaxResponsibilities,
|
|
804
|
-
detectedPattern: pattern?.name
|
|
805
|
-
}
|
|
806
|
-
}));
|
|
807
|
-
}
|
|
808
|
-
return violations;
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Generate component-specific refactoring recommendations
|
|
812
|
-
*/
|
|
813
|
-
function generateComponentRefactoringRecommendation(responsibilities, pattern) {
|
|
814
|
-
const types = [...new Set(responsibilities.map(r => r.type))];
|
|
815
|
-
// Check for common anti-patterns
|
|
816
|
-
if (types.includes(ResponsibilityType.DataFetching) && types.includes(ResponsibilityType.UIState)) {
|
|
817
|
-
return 'Consider using container/presenter pattern: extract data fetching into a container component';
|
|
818
|
-
}
|
|
819
|
-
if (types.includes(ResponsibilityType.BusinessLogic) && types.includes(ResponsibilityType.FormHandling)) {
|
|
820
|
-
return 'Extract business logic into custom hooks or utility functions';
|
|
821
|
-
}
|
|
822
|
-
if (types.includes(ResponsibilityType.SideEffects) && types.includes(ResponsibilityType.UIState)) {
|
|
823
|
-
return 'Move side effects (analytics, logging) to a higher-level component or custom hook';
|
|
824
|
-
}
|
|
825
|
-
if (pattern?.name === 'Form' && types.includes(ResponsibilityType.DataFetching)) {
|
|
826
|
-
return 'Forms should not fetch data directly. Move data fetching to parent component';
|
|
827
|
-
}
|
|
828
|
-
if (pattern?.name === 'SimpleUI' && types.length > 2) {
|
|
829
|
-
return 'Simple UI components should only handle presentation. Extract logic to parent or hooks';
|
|
830
|
-
}
|
|
831
|
-
// Generic recommendation
|
|
832
|
-
return 'Consider splitting by responsibility: ' + types.join(', ');
|
|
833
|
-
}
|
|
834
|
-
/**
|
|
835
|
-
* SOLID analyzer definition
|
|
836
|
-
*/
|
|
837
|
-
export const solidAnalyzer = {
|
|
838
|
-
name: 'solid',
|
|
839
|
-
defaultConfig: DEFAULT_CONFIG,
|
|
840
|
-
analyze: async (files, config, options, progressCallback) => {
|
|
841
|
-
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
842
|
-
return processFiles(files, analyzeFile, 'solid', mergedConfig, progressCallback ?
|
|
843
|
-
(current, total, file) => progressCallback({ current, total, analyzer: 'solid', file }) :
|
|
844
|
-
undefined);
|
|
845
|
-
}
|
|
846
|
-
};
|
|
847
|
-
//# sourceMappingURL=solidAnalyzer.js.map
|