code-auditor-mcp 2.6.1 → 3.0.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/README.md +319 -147
- package/dist/analyzers/analyzerUtils.d.ts +13 -11
- package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
- package/dist/analyzers/analyzerUtils.js +22 -56
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/dataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/dataAccessAnalyzer.js +50 -24
- package/dist/analyzers/dataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts +4 -1
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +210 -111
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/dryAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/dryAnalyzer.js +190 -58
- package/dist/analyzers/dryAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts +20 -0
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/invariantsAnalyzer.js +148 -0
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +2 -2
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/schemaAnalyzer.d.ts +2 -0
- package/dist/analyzers/schemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/schemaAnalyzer.js +306 -147
- package/dist/analyzers/schemaAnalyzer.js.map +1 -1
- package/dist/analyzers/solidAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/solidAnalyzer.js +145 -119
- package/dist/analyzers/solidAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +3 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +41 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +8 -8
- 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 +6 -6
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/api/index.js +1 -0
- package/dist/api/index.js.map +1 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +367 -24
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +740 -18
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB-enhanced.d.ts +35 -13
- package/dist/codeIndexDB-enhanced.d.ts.map +1 -1
- package/dist/codeIndexDB-enhanced.js +311 -214
- package/dist/codeIndexDB-enhanced.js.map +1 -1
- package/dist/codeIndexDB.d.ts +222 -0
- package/dist/codeIndexDB.d.ts.map +1 -0
- package/dist/codeIndexDB.js +2042 -0
- package/dist/codeIndexDB.js.map +1 -0
- package/dist/codeIndexService.d.ts.map +1 -1
- package/dist/codeIndexService.js.map +1 -1
- package/dist/componentScanner.d.ts +14 -8
- package/dist/componentScanner.d.ts.map +1 -1
- package/dist/componentScanner.js +136 -113
- package/dist/componentScanner.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +10 -7
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/fingerprint.d.ts +15 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +32 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/functionScanner.d.ts +5 -0
- package/dist/functionScanner.d.ts.map +1 -1
- package/dist/functionScanner.js +417 -341
- package/dist/functionScanner.js.map +1 -1
- package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
- package/dist/generators/AiderConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.js.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
- package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
- package/dist/generators/JetBrainsConfigGenerator.d.ts.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
- package/dist/grammars/tree-sitter-go.wasm +0 -0
- package/dist/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/invariants/index.d.ts +9 -0
- package/dist/invariants/index.d.ts.map +1 -0
- package/dist/invariants/index.js +4 -0
- package/dist/invariants/index.js.map +1 -0
- package/dist/invariants/invariant-rules.schema.json +137 -0
- package/dist/invariants/ruleEngine.d.ts +32 -0
- package/dist/invariants/ruleEngine.d.ts.map +1 -0
- package/dist/invariants/ruleEngine.js +536 -0
- package/dist/invariants/ruleEngine.js.map +1 -0
- package/dist/invariants/ruleValidator.d.ts +17 -0
- package/dist/invariants/ruleValidator.d.ts.map +1 -0
- package/dist/invariants/ruleValidator.js +264 -0
- package/dist/invariants/ruleValidator.js.map +1 -0
- package/dist/invariants/types.d.ts +78 -0
- package/dist/invariants/types.d.ts.map +1 -0
- package/dist/invariants/types.js +5 -0
- package/dist/invariants/types.js.map +1 -0
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/LanguageRegistry.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +1 -1
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +102 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -0
- package/dist/languages/adapterBridge.js +382 -0
- package/dist/languages/adapterBridge.js.map +1 -0
- package/dist/languages/go/GoAdapter.d.ts +37 -26
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +560 -281
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/index.d.ts +5 -2
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +11 -9
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/tree-sitter/converter.d.ts +43 -0
- package/dist/languages/tree-sitter/converter.d.ts.map +1 -0
- package/dist/languages/tree-sitter/converter.js +150 -0
- package/dist/languages/tree-sitter/converter.js.map +1 -0
- package/dist/languages/tree-sitter/parser.d.ts +36 -0
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -0
- package/dist/languages/tree-sitter/parser.js +114 -0
- package/dist/languages/tree-sitter/parser.js.map +1 -0
- package/dist/languages/types.d.ts +11 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +92 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +951 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -0
- package/dist/mcp-tools/projectTasks.d.ts +80 -11
- package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
- package/dist/mcp-tools/projectTasks.js +279 -13
- package/dist/mcp-tools/projectTasks.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +21 -21
- package/dist/mcp-tools/whitelistTools.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +130 -126
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.js +1 -1
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +1455 -1132
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts +4 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +49 -2
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpDiagnostics.d.ts.map +1 -1
- package/dist/mcpDiagnostics.js +8 -5
- package/dist/mcpDiagnostics.js.map +1 -1
- package/dist/mcpToolErrors.d.ts.map +1 -1
- package/dist/mcpToolErrors.js.map +1 -1
- package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
- package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
- package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
- package/dist/native-bootstrap.d.ts +15 -0
- package/dist/native-bootstrap.d.ts.map +1 -0
- package/dist/native-bootstrap.js +75 -0
- package/dist/native-bootstrap.js.map +1 -0
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.js +4 -0
- package/dist/reporting/reportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts +39 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -0
- package/dist/reporting/sarifReportGenerator.js +229 -0
- package/dist/reporting/sarifReportGenerator.js.map +1 -0
- package/dist/search/QueryParser.d.ts +41 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +200 -19
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/services/CodeMapGenerator.d.ts.map +1 -1
- package/dist/services/CodeMapGenerator.js +4 -4
- package/dist/services/CodeMapGenerator.js.map +1 -1
- package/dist/services/ProjectTaskRepository.d.ts +34 -9
- package/dist/services/ProjectTaskRepository.d.ts.map +1 -1
- package/dist/services/ProjectTaskRepository.js +385 -10
- package/dist/services/ProjectTaskRepository.js.map +1 -1
- package/dist/services/SchemaParser.d.ts.map +1 -1
- package/dist/services/SchemaParser.js +5 -8
- package/dist/services/SchemaParser.js.map +1 -1
- package/dist/services/auditJobService.d.ts +1 -1
- package/dist/services/projectTaskHelpers.js.map +1 -1
- package/dist/services/whitelistService.d.ts.map +1 -1
- package/dist/services/whitelistService.js +1 -1
- package/dist/services/whitelistService.js.map +1 -1
- package/dist/tool-registry.d.ts +79 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +139 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/types/projectTask.d.ts +31 -0
- package/dist/types/projectTask.d.ts.map +1 -1
- package/dist/types.d.ts +26 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/ui/InteractivePrompts.d.ts.map +1 -1
- package/dist/ui/InteractivePrompts.js.map +1 -1
- package/dist/utils/astParser.d.ts +31 -38
- package/dist/utils/astParser.d.ts.map +1 -1
- package/dist/utils/astParser.js +138 -196
- package/dist/utils/astParser.js.map +1 -1
- package/dist/utils/astUtils.d.ts +87 -58
- package/dist/utils/astUtils.d.ts.map +1 -1
- package/dist/utils/astUtils.js +852 -581
- package/dist/utils/astUtils.js.map +1 -1
- package/dist/utils/componentPatterns.js.map +1 -1
- package/dist/utils/componentResponsibility.d.ts +13 -9
- package/dist/utils/componentResponsibility.d.ts.map +1 -1
- package/dist/utils/componentResponsibility.js +236 -206
- package/dist/utils/componentResponsibility.js.map +1 -1
- package/dist/utils/dependencyExtractor.d.ts +11 -6
- package/dist/utils/dependencyExtractor.d.ts.map +1 -1
- package/dist/utils/dependencyExtractor.js +200 -119
- package/dist/utils/dependencyExtractor.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +1 -1
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/jsDocParser.d.ts +27 -30
- package/dist/utils/jsDocParser.d.ts.map +1 -1
- package/dist/utils/jsDocParser.js +271 -207
- package/dist/utils/jsDocParser.js.map +1 -1
- package/dist/utils/reactDetection.d.ts +27 -12
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +331 -219
- package/dist/utils/reactDetection.js.map +1 -1
- package/dist/workers/auditWorker.js +17 -3
- package/dist/workers/auditWorker.js.map +1 -1
- package/package.json +54 -51
- package/dist/adapters/GoAdapter.d.ts +0 -53
- package/dist/adapters/GoAdapter.d.ts.map +0 -1
- package/dist/adapters/GoAdapter.js +0 -611
- package/dist/adapters/GoAdapter.js.map +0 -1
- package/dist/adapters/GoConfig.d.ts +0 -35
- package/dist/adapters/GoConfig.d.ts.map +0 -1
- package/dist/adapters/GoConfig.js +0 -155
- package/dist/adapters/GoConfig.js.map +0 -1
- package/dist/adapters/LanguageAdapter.d.ts +0 -135
- package/dist/adapters/LanguageAdapter.d.ts.map +0 -1
- package/dist/adapters/LanguageAdapter.js +0 -47
- package/dist/adapters/LanguageAdapter.js.map +0 -1
- package/dist/adapters/TypeScriptAdapter.d.ts +0 -43
- package/dist/adapters/TypeScriptAdapter.d.ts.map +0 -1
- package/dist/adapters/TypeScriptAdapter.js +0 -386
- package/dist/adapters/TypeScriptAdapter.js.map +0 -1
- package/dist/adapters/UniversalAnalyzer.d.ts +0 -42
- package/dist/adapters/UniversalAnalyzer.d.ts.map +0 -1
- package/dist/adapters/UniversalAnalyzer.js +0 -103
- package/dist/adapters/UniversalAnalyzer.js.map +0 -1
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts +0 -48
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts.map +0 -1
- package/dist/adapters/UniversalSOLIDAnalyzer.js +0 -178
- package/dist/adapters/UniversalSOLIDAnalyzer.js.map +0 -1
- package/dist/adapters/solidAnalyzerCompat.d.ts +0 -21
- package/dist/adapters/solidAnalyzerCompat.d.ts.map +0 -1
- package/dist/adapters/solidAnalyzerCompat.js +0 -78
- package/dist/adapters/solidAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/dataAccessAnalyzerCompat.js +0 -59
- package/dist/analyzers/dataAccessAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/documentationAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/documentationAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/documentationAnalyzerCompat.js +0 -52
- package/dist/analyzers/documentationAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/dryAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/dryAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/dryAnalyzerCompat.js +0 -44
- package/dist/analyzers/dryAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/schemaAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/schemaAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/schemaAnalyzerCompat.js +0 -69
- package/dist/analyzers/schemaAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/solidAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/solidAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/solidAnalyzerCompat.js +0 -78
- package/dist/analyzers/solidAnalyzerCompat.js.map +0 -1
- package/dist/codeIndexDb.d.ts +0 -394
- package/dist/codeIndexDb.d.ts.map +0 -1
- package/dist/codeIndexDb.js +0 -2077
- package/dist/codeIndexDb.js.map +0 -1
- package/dist/languages/typescript/TypeScriptAdapter.d.ts +0 -108
- package/dist/languages/typescript/TypeScriptAdapter.d.ts.map +0 -1
- package/dist/languages/typescript/TypeScriptAdapter.js +0 -567
- package/dist/languages/typescript/TypeScriptAdapter.js.map +0 -1
- package/dist/languages/typescript/test-extraction.d.ts +0 -2
- package/dist/languages/typescript/test-extraction.d.ts.map +0 -1
- package/dist/languages/typescript/test-extraction.js +0 -36
- package/dist/languages/typescript/test-extraction.js.map +0 -1
- package/dist/mcp-hybrid.d.ts +0 -10
- package/dist/mcp-hybrid.d.ts.map +0 -1
- package/dist/mcp-hybrid.js +0 -41
- package/dist/mcp-hybrid.js.map +0 -1
- package/dist/mcp-index.d.ts +0 -13
- package/dist/mcp-index.d.ts.map +0 -1
- package/dist/mcp-index.js +0 -123
- package/dist/mcp-index.js.map +0 -1
- package/dist/mcp-standalone.d.ts +0 -3
- package/dist/mcp-standalone.d.ts.map +0 -1
- package/dist/mcp-standalone.js +0 -1201
- package/dist/mcp-standalone.js.map +0 -1
- package/dist/mcp-ui-server.d.ts +0 -27
- package/dist/mcp-ui-server.d.ts.map +0 -1
- package/dist/mcp-ui-server.js +0 -484
- package/dist/mcp-ui-server.js.map +0 -1
- package/dist/test-multi-strategy-search.d.ts +0 -3
- package/dist/test-multi-strategy-search.d.ts.map +0 -1
- package/dist/test-multi-strategy-search.js +0 -170
- package/dist/test-multi-strategy-search.js.map +0 -1
|
@@ -1,8 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* React Component Detection Utilities
|
|
3
3
|
* Provides AST-based detection of React components and their properties
|
|
4
|
+
*
|
|
5
|
+
* ## Migration: TypeScript Compiler API → tree-sitter
|
|
6
|
+
*
|
|
7
|
+
* ### Capability regressions (documented per Spec 08 plan Step 2.5):
|
|
8
|
+
*
|
|
9
|
+
* | Check | TS API approach | tree-sitter approach | Honest outcome |
|
|
10
|
+
* |--------------------------|-------------------------------------|-----------------------------------|----------------|
|
|
11
|
+
* | Prop type from interface | `checker.getTypeAtLocation()` | Parse type annotation as string | Recovers for inline types; loses cross-file interface resolution |
|
|
12
|
+
* | `PropTypes.oneOf` | Type string from checker | Match call pattern textually | Full parity — doesn't need checker |
|
|
13
|
+
* | Symbol resolution | `checker.getSymbolAtLocation()` | Not possible without checker | Lost — falls through to parameter destructuring heuristics |
|
|
4
14
|
*/
|
|
5
|
-
import
|
|
15
|
+
import { findNodes, getNodeText, getLineAndColumn } from '../languages/adapterBridge.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Helpers
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/** Get raw text from a tree-sitter node (stored on ASTNode.raw). */
|
|
20
|
+
function rawText(node) {
|
|
21
|
+
return node.raw?.text ?? '';
|
|
22
|
+
}
|
|
23
|
+
/** Check if a tree-sitter node type is a function-like declaration. */
|
|
24
|
+
function isFunctionType(type) {
|
|
25
|
+
return type === 'function_declaration' || type === 'function_expression' || type === 'arrow_function';
|
|
26
|
+
}
|
|
27
|
+
/** Check if a tree-sitter node type is a JSX node. */
|
|
28
|
+
function isJsxType(type) {
|
|
29
|
+
return type === 'jsx_element' || type === 'jsx_fragment' || type === 'jsx_self_closing_element';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check if a name follows React component naming convention (PascalCase)
|
|
33
|
+
*/
|
|
34
|
+
function isComponentName(name) {
|
|
35
|
+
return /^[A-Z][a-zA-Z0-9]*$/.test(name);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if a hook name is a built-in React hook
|
|
39
|
+
*/
|
|
40
|
+
function isBuiltInHook(hookName) {
|
|
41
|
+
const builtInHooks = [
|
|
42
|
+
'useState', 'useEffect', 'useContext', 'useReducer', 'useCallback',
|
|
43
|
+
'useMemo', 'useRef', 'useImperativeHandle', 'useLayoutEffect',
|
|
44
|
+
'useDebugValue', 'useId', 'useDeferredValue', 'useTransition',
|
|
45
|
+
'useSyncExternalStore', 'useInsertionEffect'
|
|
46
|
+
];
|
|
47
|
+
return builtInHooks.includes(hookName);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Find the first child of a given type.
|
|
51
|
+
*/
|
|
52
|
+
function findChildOfType(node, type) {
|
|
53
|
+
return node.children?.find(c => c.type === type);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Find all children of a given type.
|
|
57
|
+
*/
|
|
58
|
+
function findChildrenOfType(node, type) {
|
|
59
|
+
return node.children?.filter(c => c.type === type) ?? [];
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Component detection
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
6
64
|
/**
|
|
7
65
|
* Check if a node is any type of React component
|
|
8
66
|
*/
|
|
@@ -13,17 +71,22 @@ export function isReactComponent(node) {
|
|
|
13
71
|
* Check if a node is a functional React component
|
|
14
72
|
*/
|
|
15
73
|
export function isFunctionalComponent(node) {
|
|
16
|
-
if (
|
|
74
|
+
if (node.type === 'function_declaration' || node.type === 'function_expression') {
|
|
17
75
|
return returnsJSX(node);
|
|
18
76
|
}
|
|
19
|
-
if (
|
|
77
|
+
if (node.type === 'arrow_function') {
|
|
20
78
|
return returnsJSX(node);
|
|
21
79
|
}
|
|
22
80
|
// Check for variable declarations with arrow functions
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
81
|
+
// tree-sitter: lexical_declaration / variable_declaration → variable_declarator
|
|
82
|
+
if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
|
|
83
|
+
const declarator = findChildOfType(node, 'variable_declarator');
|
|
84
|
+
if (declarator) {
|
|
85
|
+
// Find the value/initializer (non-identifier child)
|
|
86
|
+
const initializer = declarator.children?.find(c => c.type !== 'identifier' && c.type !== 'type_annotation');
|
|
87
|
+
if (initializer && initializer.type === 'arrow_function') {
|
|
88
|
+
return returnsJSX(initializer);
|
|
89
|
+
}
|
|
27
90
|
}
|
|
28
91
|
}
|
|
29
92
|
return false;
|
|
@@ -32,120 +95,157 @@ export function isFunctionalComponent(node) {
|
|
|
32
95
|
* Check if a node returns JSX elements
|
|
33
96
|
*/
|
|
34
97
|
export function returnsJSX(node) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
ts.isJsxSelfClosingElement(child.expression)) {
|
|
46
|
-
hasJSX = true;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (!hasJSX) {
|
|
50
|
-
ts.forEachChild(child, visit);
|
|
98
|
+
// Check for JSX elements anywhere in the subtree
|
|
99
|
+
const jsxNodes = findNodes(node, n => isJsxType(n.type));
|
|
100
|
+
if (jsxNodes.length > 0)
|
|
101
|
+
return true;
|
|
102
|
+
// Check for return statements containing JSX
|
|
103
|
+
const returnNodes = findNodes(node, n => n.type === 'return_statement');
|
|
104
|
+
for (const ret of returnNodes) {
|
|
105
|
+
for (const child of ret.children ?? []) {
|
|
106
|
+
if (isJsxType(child.type))
|
|
107
|
+
return true;
|
|
51
108
|
}
|
|
52
109
|
}
|
|
53
|
-
|
|
54
|
-
return hasJSX;
|
|
110
|
+
return false;
|
|
55
111
|
}
|
|
56
112
|
/**
|
|
57
113
|
* Check if a node is a class component extending React.Component
|
|
58
114
|
*/
|
|
59
115
|
export function isClassComponent(node) {
|
|
60
|
-
if (
|
|
116
|
+
if (node.type !== 'class_declaration')
|
|
61
117
|
return false;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
118
|
+
// Check heritage clauses for extends React.Component / PureComponent
|
|
119
|
+
for (const child of node.children ?? []) {
|
|
120
|
+
if (child.type !== 'heritage_clause')
|
|
121
|
+
continue;
|
|
122
|
+
// Check if this is an 'extends' clause (not 'implements')
|
|
123
|
+
const raw = child.raw;
|
|
124
|
+
if (!raw?.children)
|
|
125
|
+
continue;
|
|
126
|
+
const isExtends = raw.children.some(c => !c.isNamed && c.type === 'extends');
|
|
127
|
+
if (!isExtends)
|
|
128
|
+
continue;
|
|
129
|
+
// Check each type in the extends clause
|
|
130
|
+
for (const typeNode of child.children ?? []) {
|
|
131
|
+
if (typeNode.type === 'member_expression') {
|
|
132
|
+
// React.Component or React.PureComponent
|
|
133
|
+
const parts = typeNode.children ?? [];
|
|
134
|
+
if (parts.length >= 2) {
|
|
135
|
+
const obj = parts[0];
|
|
136
|
+
const prop = parts[parts.length - 1];
|
|
137
|
+
if (obj.type === 'identifier' && rawText(obj) === 'React' &&
|
|
138
|
+
prop.type === 'property_identifier' &&
|
|
139
|
+
(rawText(prop) === 'Component' || rawText(prop) === 'PureComponent')) {
|
|
81
140
|
return true;
|
|
82
141
|
}
|
|
83
142
|
}
|
|
84
143
|
}
|
|
144
|
+
if (typeNode.type === 'identifier') {
|
|
145
|
+
const name = rawText(typeNode);
|
|
146
|
+
if (name === 'Component' || name === 'PureComponent') {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
85
150
|
}
|
|
86
151
|
}
|
|
87
152
|
return false;
|
|
88
153
|
}
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Hook extraction
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
89
157
|
/**
|
|
90
158
|
* Extract hooks usage from a component node
|
|
91
159
|
*/
|
|
92
|
-
export function extractHooks(node
|
|
160
|
+
export function extractHooks(node) {
|
|
93
161
|
const hooks = [];
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
162
|
+
// Find all call expressions in the component
|
|
163
|
+
const callExprs = findNodes(node, n => n.type === 'call_expression');
|
|
164
|
+
for (const call of callExprs) {
|
|
165
|
+
// The expression being called is the first child (identifier or member_expression)
|
|
166
|
+
const callee = call.children?.[0];
|
|
167
|
+
if (!callee)
|
|
168
|
+
continue;
|
|
169
|
+
if (callee.type === 'identifier') {
|
|
170
|
+
const name = rawText(callee);
|
|
171
|
+
if (name.startsWith('use')) {
|
|
172
|
+
const { line } = getLineAndColumn(callee);
|
|
100
173
|
hooks.push({
|
|
101
|
-
name
|
|
174
|
+
name,
|
|
102
175
|
line,
|
|
103
|
-
customHook: !isBuiltInHook(
|
|
176
|
+
customHook: !isBuiltInHook(name)
|
|
104
177
|
});
|
|
105
178
|
}
|
|
106
179
|
}
|
|
107
|
-
ts.forEachChild(child, visit);
|
|
108
180
|
}
|
|
109
|
-
ts.forEachChild(node, visit);
|
|
110
181
|
return hooks;
|
|
111
182
|
}
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// Prop type extraction
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
112
186
|
/**
|
|
113
|
-
*
|
|
187
|
+
* Extract props from a TypeScript type literal / object_type node.
|
|
114
188
|
*/
|
|
115
|
-
function
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
'
|
|
121
|
-
|
|
122
|
-
|
|
189
|
+
function extractPropsFromTypeLiteral(typeLiteral) {
|
|
190
|
+
const props = [];
|
|
191
|
+
for (const member of typeLiteral.children ?? []) {
|
|
192
|
+
if (member.type !== 'property_signature')
|
|
193
|
+
continue;
|
|
194
|
+
const nameNode = member.children?.find(c => c.type === 'identifier' || c.type === 'property_identifier');
|
|
195
|
+
if (!nameNode)
|
|
196
|
+
continue;
|
|
197
|
+
const typeChild = member.children?.find(c => c.type === 'type_annotation');
|
|
198
|
+
const typeStr = typeChild ? getNodeText(typeChild, '') || rawText(typeChild) : 'any';
|
|
199
|
+
// Check for optional marker
|
|
200
|
+
const raw = member.raw;
|
|
201
|
+
const hasQuestion = raw?.children?.some(c => !c.isNamed && c.type === '?') ?? false;
|
|
202
|
+
props.push({
|
|
203
|
+
name: rawText(nameNode),
|
|
204
|
+
type: typeStr,
|
|
205
|
+
required: !hasQuestion,
|
|
206
|
+
hasDefault: false
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return props;
|
|
123
210
|
}
|
|
124
211
|
/**
|
|
125
212
|
* Extract prop types from a component (TypeScript interfaces/types)
|
|
213
|
+
*
|
|
214
|
+
* NOTE: Cross-file interface resolution via TypeChecker is NOT available
|
|
215
|
+
* with tree-sitter. We recover inline types and parameter destructuring,
|
|
216
|
+
* but type references (e.g. `React.FC<Props>`) without inline definition
|
|
217
|
+
* will fall through to parameter heuristics.
|
|
126
218
|
*/
|
|
127
|
-
export function extractPropTypes(node
|
|
219
|
+
export function extractPropTypes(node) {
|
|
128
220
|
const props = [];
|
|
129
221
|
// Handle variable declarations with type annotations (e.g., const Button: React.FC<Props>)
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
222
|
+
if (node.type === 'variable_declarator') {
|
|
223
|
+
const typeAnnot = findChildOfType(node, 'type_annotation');
|
|
224
|
+
const initializer = node.children?.find(c => c.type !== 'identifier' && c.type !== 'type_annotation');
|
|
225
|
+
// If there's a type annotation, extract props from destructured parameters
|
|
226
|
+
if (initializer && (initializer.type === 'arrow_function' || initializer.type === 'function_expression')) {
|
|
227
|
+
const params = findChildOfType(initializer, 'formal_parameters');
|
|
228
|
+
if (params) {
|
|
229
|
+
const firstParam = params.children?.[0];
|
|
230
|
+
if (firstParam) {
|
|
231
|
+
// Check for destructured parameter (object pattern)
|
|
232
|
+
if (firstParam.type === 'object_pattern' || firstParam.type === 'object_binding_pattern') {
|
|
233
|
+
for (const element of firstParam.children ?? []) {
|
|
234
|
+
if (element.type !== 'binding_element' && element.type !== 'pair_pattern' &&
|
|
235
|
+
element.type !== 'shorthand_property_identifier_pattern')
|
|
236
|
+
continue;
|
|
237
|
+
const nameNode = element.children?.find(c => c.type === 'identifier' || c.type === 'property_identifier');
|
|
238
|
+
if (!nameNode)
|
|
239
|
+
continue;
|
|
240
|
+
const raw = element.raw;
|
|
241
|
+
const hasRest = raw?.children?.some(c => !c.isNamed && c.type === '...') ?? false;
|
|
242
|
+
const hasDefault = element.children?.some(c => c.type !== 'identifier' && c.type !== 'property_identifier') ?? false;
|
|
243
|
+
props.push({
|
|
244
|
+
name: rawText(nameNode),
|
|
245
|
+
type: 'any',
|
|
246
|
+
required: !hasRest && !hasDefault,
|
|
247
|
+
hasDefault
|
|
248
|
+
});
|
|
149
249
|
}
|
|
150
250
|
}
|
|
151
251
|
}
|
|
@@ -153,57 +253,60 @@ export function extractPropTypes(node, sourceFile, typeChecker) {
|
|
|
153
253
|
}
|
|
154
254
|
}
|
|
155
255
|
// For functional components, check the first parameter
|
|
156
|
-
if (
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
|
|
256
|
+
if (isFunctionType(node.type)) {
|
|
257
|
+
const params = findChildOfType(node, 'formal_parameters');
|
|
258
|
+
if (params) {
|
|
259
|
+
const firstParam = params.children?.[0];
|
|
260
|
+
if (firstParam) {
|
|
261
|
+
// Check if parameter is destructured
|
|
262
|
+
if (firstParam.type === 'object_pattern' || firstParam.type === 'object_binding_pattern') {
|
|
263
|
+
for (const element of firstParam.children ?? []) {
|
|
264
|
+
if (element.type !== 'binding_element' && element.type !== 'shorthand_property_identifier_pattern')
|
|
265
|
+
continue;
|
|
266
|
+
const nameNode = element.children?.find(c => c.type === 'identifier' || c.type === 'property_identifier');
|
|
267
|
+
if (!nameNode)
|
|
268
|
+
continue;
|
|
269
|
+
const raw = element.raw;
|
|
270
|
+
const hasRest = raw?.children?.some(c => !c.isNamed && c.type === '...') ?? false;
|
|
271
|
+
const hasDefault = element.children?.some(c => c.type !== 'identifier' && c.type !== 'property_identifier') ?? false;
|
|
163
272
|
props.push({
|
|
164
|
-
name:
|
|
165
|
-
type: 'any',
|
|
166
|
-
required: !
|
|
167
|
-
hasDefault
|
|
273
|
+
name: rawText(nameNode),
|
|
274
|
+
type: 'any',
|
|
275
|
+
required: !hasRest && !hasDefault,
|
|
276
|
+
hasDefault
|
|
168
277
|
});
|
|
169
278
|
}
|
|
170
279
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
else if (ts.isTypeReferenceNode(firstParam.type) && typeChecker) {
|
|
178
|
-
// Handle interface references
|
|
179
|
-
const symbol = typeChecker.getSymbolAtLocation(firstParam.type.typeName);
|
|
180
|
-
if (symbol) {
|
|
181
|
-
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, firstParam.type);
|
|
182
|
-
const propSymbols = type.getProperties();
|
|
183
|
-
for (const propSymbol of propSymbols) {
|
|
184
|
-
const propType = typeChecker.getTypeOfSymbolAtLocation(propSymbol, propSymbol.valueDeclaration);
|
|
185
|
-
props.push({
|
|
186
|
-
name: propSymbol.getName(),
|
|
187
|
-
type: typeChecker.typeToString(propType),
|
|
188
|
-
required: !(propSymbol.flags & ts.SymbolFlags.Optional),
|
|
189
|
-
hasDefault: false // TODO: Detect default props
|
|
190
|
-
});
|
|
191
|
-
}
|
|
280
|
+
// Extract props from type annotation on parameter
|
|
281
|
+
const typeAnnot = findChildOfType(firstParam, 'type_annotation');
|
|
282
|
+
if (typeAnnot && typeAnnot.children?.[0]) {
|
|
283
|
+
const typeNode = typeAnnot.children[0];
|
|
284
|
+
if (typeNode.type === 'object_type' || typeNode.type === 'type_literal') {
|
|
285
|
+
props.push(...extractPropsFromTypeLiteral(typeNode));
|
|
192
286
|
}
|
|
287
|
+
// NOTE: type_reference (interface references) can't be resolved
|
|
288
|
+
// without TypeChecker — this is a known tree-sitter limitation.
|
|
193
289
|
}
|
|
194
290
|
}
|
|
195
291
|
}
|
|
196
292
|
}
|
|
197
|
-
// For class components, check Props
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
293
|
+
// For class components, check Props in extends clause
|
|
294
|
+
if (node.type === 'class_declaration') {
|
|
295
|
+
for (const child of node.children ?? []) {
|
|
296
|
+
if (child.type !== 'heritage_clause')
|
|
297
|
+
continue;
|
|
298
|
+
const raw = child.raw;
|
|
299
|
+
const isExtends = raw?.children?.some(c => !c.isNamed && c.type === 'extends') ?? false;
|
|
300
|
+
if (!isExtends)
|
|
301
|
+
continue;
|
|
302
|
+
for (const typeNode of child.children ?? []) {
|
|
303
|
+
if (typeNode.type === 'generic_type') {
|
|
304
|
+
// React.Component<Props> — type_arguments contain the props type
|
|
305
|
+
const typeArgs = findChildOfType(typeNode, 'type_arguments');
|
|
306
|
+
if (typeArgs?.children) {
|
|
307
|
+
const firstArg = typeArgs.children[0];
|
|
308
|
+
if (firstArg?.type === 'object_type' || firstArg?.type === 'type_literal') {
|
|
309
|
+
props.push(...extractPropsFromTypeLiteral(firstArg));
|
|
207
310
|
}
|
|
208
311
|
}
|
|
209
312
|
}
|
|
@@ -212,131 +315,140 @@ export function extractPropTypes(node, sourceFile, typeChecker) {
|
|
|
212
315
|
}
|
|
213
316
|
return props;
|
|
214
317
|
}
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
// Component import extraction
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
215
321
|
/**
|
|
216
|
-
* Extract
|
|
322
|
+
* Extract component imports from an AST.
|
|
217
323
|
*/
|
|
218
|
-
function
|
|
219
|
-
for (const member of typeLiteral.members) {
|
|
220
|
-
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
221
|
-
props.push({
|
|
222
|
-
name: member.name.text,
|
|
223
|
-
type: member.type ? member.type.getText() : 'any',
|
|
224
|
-
required: !member.questionToken,
|
|
225
|
-
hasDefault: false
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Extract component imports from a source file
|
|
232
|
-
*/
|
|
233
|
-
export function extractComponentImports(sourceFile) {
|
|
324
|
+
export function extractComponentImports(astRoot) {
|
|
234
325
|
const imports = [];
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
326
|
+
const importNodes = findNodes(astRoot, n => n.type === 'import_statement');
|
|
327
|
+
for (const imp of importNodes) {
|
|
328
|
+
// Find module specifier (string child)
|
|
329
|
+
const moduleNode = findChildOfType(imp, 'string');
|
|
330
|
+
if (!moduleNode)
|
|
331
|
+
continue;
|
|
332
|
+
const rawSpecifier = rawText(moduleNode);
|
|
333
|
+
const importPath = rawSpecifier.replace(/^["']|["']$/g, '');
|
|
334
|
+
// Only check local imports (not node_modules)
|
|
335
|
+
if (!importPath.startsWith('.') && !importPath.startsWith('/'))
|
|
336
|
+
continue;
|
|
337
|
+
const importClause = findChildOfType(imp, 'import_clause');
|
|
338
|
+
if (!importClause)
|
|
339
|
+
continue;
|
|
340
|
+
// Default import (identifier child of import_clause)
|
|
341
|
+
for (const child of importClause.children ?? []) {
|
|
342
|
+
if (child.type === 'identifier') {
|
|
343
|
+
const name = rawText(child);
|
|
344
|
+
if (isComponentName(name)) {
|
|
345
|
+
imports.push({ name, path: importPath, isDefault: true });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Named imports
|
|
349
|
+
if (child.type === 'named_imports') {
|
|
350
|
+
for (const spec of child.children ?? []) {
|
|
351
|
+
if (spec.type !== 'import_specifier')
|
|
352
|
+
continue;
|
|
353
|
+
// The last identifier in the specifier is the local name
|
|
354
|
+
const identifiers = spec.children?.filter(c => c.type === 'identifier') ?? [];
|
|
355
|
+
const localName = identifiers[identifiers.length - 1];
|
|
356
|
+
if (localName) {
|
|
357
|
+
const name = rawText(localName);
|
|
358
|
+
if (isComponentName(name)) {
|
|
359
|
+
imports.push({ name, path: importPath, isDefault: false });
|
|
267
360
|
}
|
|
268
361
|
}
|
|
269
362
|
}
|
|
270
363
|
}
|
|
271
364
|
}
|
|
272
|
-
ts.forEachChild(node, visit);
|
|
273
365
|
}
|
|
274
|
-
visit(sourceFile);
|
|
275
366
|
return imports;
|
|
276
367
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
function isComponentName(name) {
|
|
281
|
-
return /^[A-Z][a-zA-Z0-9]*$/.test(name);
|
|
282
|
-
}
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
// Component type detection
|
|
370
|
+
// ---------------------------------------------------------------------------
|
|
283
371
|
/**
|
|
284
372
|
* Detect the specific type of component (functional, class, memo, forwardRef)
|
|
285
373
|
*/
|
|
286
374
|
export function detectComponentType(node) {
|
|
287
|
-
// Check for memo
|
|
288
|
-
if (
|
|
289
|
-
const
|
|
290
|
-
if (
|
|
291
|
-
(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
375
|
+
// Check for call expression wrapping (memo, forwardRef)
|
|
376
|
+
if (node.type === 'call_expression') {
|
|
377
|
+
const callee = node.children?.[0];
|
|
378
|
+
if (callee) {
|
|
379
|
+
if (callee.type === 'identifier') {
|
|
380
|
+
const name = rawText(callee);
|
|
381
|
+
if (name === 'memo')
|
|
382
|
+
return 'memo';
|
|
383
|
+
if (name === 'forwardRef')
|
|
384
|
+
return 'forwardRef';
|
|
385
|
+
}
|
|
386
|
+
if (callee.type === 'member_expression') {
|
|
387
|
+
const parts = callee.children ?? [];
|
|
388
|
+
if (parts.length >= 2) {
|
|
389
|
+
const obj = parts[0];
|
|
390
|
+
const prop = parts[parts.length - 1];
|
|
391
|
+
if (obj.type === 'identifier' && rawText(obj) === 'React') {
|
|
392
|
+
const propName = rawText(prop);
|
|
393
|
+
if (propName === 'memo')
|
|
394
|
+
return 'memo';
|
|
395
|
+
if (propName === 'forwardRef')
|
|
396
|
+
return 'forwardRef';
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
302
400
|
}
|
|
303
401
|
}
|
|
304
|
-
if (isClassComponent(node))
|
|
402
|
+
if (isClassComponent(node))
|
|
305
403
|
return 'class';
|
|
306
|
-
|
|
307
|
-
if (isFunctionalComponent(node)) {
|
|
404
|
+
if (isFunctionalComponent(node))
|
|
308
405
|
return 'functional';
|
|
309
|
-
}
|
|
310
406
|
return null;
|
|
311
407
|
}
|
|
408
|
+
// ---------------------------------------------------------------------------
|
|
409
|
+
// Component name extraction
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
312
411
|
/**
|
|
313
412
|
* Get component name from various declaration patterns
|
|
314
413
|
*/
|
|
315
414
|
export function getComponentName(node) {
|
|
316
415
|
// Function declaration
|
|
317
|
-
if (
|
|
318
|
-
|
|
416
|
+
if (node.type === 'function_declaration') {
|
|
417
|
+
const nameNode = findChildOfType(node, 'identifier');
|
|
418
|
+
if (nameNode)
|
|
419
|
+
return rawText(nameNode);
|
|
319
420
|
}
|
|
320
421
|
// Class declaration
|
|
321
|
-
if (
|
|
322
|
-
|
|
422
|
+
if (node.type === 'class_declaration') {
|
|
423
|
+
const nameNode = findChildOfType(node, 'identifier');
|
|
424
|
+
if (nameNode)
|
|
425
|
+
return rawText(nameNode);
|
|
323
426
|
}
|
|
324
|
-
// Variable declaration
|
|
325
|
-
if (
|
|
326
|
-
const
|
|
327
|
-
if (
|
|
328
|
-
|
|
427
|
+
// Variable declaration / lexical declaration
|
|
428
|
+
if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
|
|
429
|
+
const declarator = findChildOfType(node, 'variable_declarator');
|
|
430
|
+
if (declarator) {
|
|
431
|
+
const nameNode = declarator.children?.find(c => c.type === 'identifier');
|
|
432
|
+
if (nameNode)
|
|
433
|
+
return rawText(nameNode);
|
|
329
434
|
}
|
|
330
435
|
}
|
|
331
|
-
// Variable
|
|
332
|
-
if (
|
|
333
|
-
|
|
436
|
+
// Variable declarator (direct)
|
|
437
|
+
if (node.type === 'variable_declarator') {
|
|
438
|
+
const nameNode = node.children?.find(c => c.type === 'identifier');
|
|
439
|
+
if (nameNode)
|
|
440
|
+
return rawText(nameNode);
|
|
334
441
|
}
|
|
335
442
|
// For memo/forwardRef wrapped components, try to extract from the argument
|
|
336
|
-
if (
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
339
|
-
|
|
443
|
+
if (node.type === 'call_expression') {
|
|
444
|
+
const args = findChildOfType(node, 'arguments');
|
|
445
|
+
if (args?.children) {
|
|
446
|
+
const firstArg = args.children[0];
|
|
447
|
+
if (firstArg?.type === 'function_expression') {
|
|
448
|
+
const nameNode = findChildOfType(firstArg, 'identifier');
|
|
449
|
+
if (nameNode)
|
|
450
|
+
return rawText(nameNode);
|
|
451
|
+
}
|
|
340
452
|
}
|
|
341
453
|
}
|
|
342
454
|
return 'AnonymousComponent';
|