codeguardian-mcp 1.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/LICENSE +21 -0
- package/README.md +348 -0
- package/dist/agent/agentTools.d.ts +26 -0
- package/dist/agent/agentTools.d.ts.map +1 -0
- package/dist/agent/agentTools.js +699 -0
- package/dist/agent/agentTools.js.map +1 -0
- package/dist/agent/autoValidator.d.ts +110 -0
- package/dist/agent/autoValidator.d.ts.map +1 -0
- package/dist/agent/autoValidator.js +964 -0
- package/dist/agent/autoValidator.js.map +1 -0
- package/dist/agent/fileWatcher.d.ts +28 -0
- package/dist/agent/fileWatcher.d.ts.map +1 -0
- package/dist/agent/fileWatcher.js +88 -0
- package/dist/agent/fileWatcher.js.map +1 -0
- package/dist/agent/guardianPersistence.d.ts +98 -0
- package/dist/agent/guardianPersistence.d.ts.map +1 -0
- package/dist/agent/guardianPersistence.js +296 -0
- package/dist/agent/guardianPersistence.js.map +1 -0
- package/dist/agent/mcpNotifications.d.ts +38 -0
- package/dist/agent/mcpNotifications.d.ts.map +1 -0
- package/dist/agent/mcpNotifications.js +81 -0
- package/dist/agent/mcpNotifications.js.map +1 -0
- package/dist/analyzers/aiPatterns.d.ts +16 -0
- package/dist/analyzers/aiPatterns.d.ts.map +1 -0
- package/dist/analyzers/aiPatterns.js +103 -0
- package/dist/analyzers/aiPatterns.js.map +1 -0
- package/dist/analyzers/antiPatterns.d.ts +60 -0
- package/dist/analyzers/antiPatterns.d.ts.map +1 -0
- package/dist/analyzers/antiPatterns.js +198 -0
- package/dist/analyzers/antiPatterns.js.map +1 -0
- package/dist/analyzers/builtinTypes.d.ts +18 -0
- package/dist/analyzers/builtinTypes.d.ts.map +1 -0
- package/dist/analyzers/builtinTypes.js +1275 -0
- package/dist/analyzers/builtinTypes.js.map +1 -0
- package/dist/analyzers/complexity.d.ts +14 -0
- package/dist/analyzers/complexity.d.ts.map +1 -0
- package/dist/analyzers/complexity.js +610 -0
- package/dist/analyzers/complexity.js.map +1 -0
- package/dist/analyzers/findingVerifier.d.ts +59 -0
- package/dist/analyzers/findingVerifier.d.ts.map +1 -0
- package/dist/analyzers/findingVerifier.js +1169 -0
- package/dist/analyzers/findingVerifier.js.map +1 -0
- package/dist/analyzers/impactAnalyzer.d.ts +53 -0
- package/dist/analyzers/impactAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/impactAnalyzer.js +152 -0
- package/dist/analyzers/impactAnalyzer.js.map +1 -0
- package/dist/analyzers/languageDetector.d.ts +48 -0
- package/dist/analyzers/languageDetector.d.ts.map +1 -0
- package/dist/analyzers/languageDetector.js +404 -0
- package/dist/analyzers/languageDetector.js.map +1 -0
- package/dist/analyzers/parsers/incrementalParser.d.ts +53 -0
- package/dist/analyzers/parsers/incrementalParser.d.ts.map +1 -0
- package/dist/analyzers/parsers/incrementalParser.js +193 -0
- package/dist/analyzers/parsers/incrementalParser.js.map +1 -0
- package/dist/analyzers/parsers/scopeResolver.d.ts +92 -0
- package/dist/analyzers/parsers/scopeResolver.d.ts.map +1 -0
- package/dist/analyzers/parsers/scopeResolver.js +324 -0
- package/dist/analyzers/parsers/scopeResolver.js.map +1 -0
- package/dist/analyzers/parsers/semanticIndex.d.ts +127 -0
- package/dist/analyzers/parsers/semanticIndex.d.ts.map +1 -0
- package/dist/analyzers/parsers/semanticIndex.js +429 -0
- package/dist/analyzers/parsers/semanticIndex.js.map +1 -0
- package/dist/analyzers/parsers/sessionDiffAnalyzer.d.ts +42 -0
- package/dist/analyzers/parsers/sessionDiffAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/parsers/sessionDiffAnalyzer.js +233 -0
- package/dist/analyzers/parsers/sessionDiffAnalyzer.js.map +1 -0
- package/dist/analyzers/parsers/treeSitterParser.d.ts +76 -0
- package/dist/analyzers/parsers/treeSitterParser.d.ts.map +1 -0
- package/dist/analyzers/parsers/treeSitterParser.js +709 -0
- package/dist/analyzers/parsers/treeSitterParser.js.map +1 -0
- package/dist/analyzers/relevanceScorer.d.ts +43 -0
- package/dist/analyzers/relevanceScorer.d.ts.map +1 -0
- package/dist/analyzers/relevanceScorer.js +200 -0
- package/dist/analyzers/relevanceScorer.js.map +1 -0
- package/dist/analyzers/standardLibrary.d.ts +22 -0
- package/dist/analyzers/standardLibrary.d.ts.map +1 -0
- package/dist/analyzers/standardLibrary.js +211 -0
- package/dist/analyzers/standardLibrary.js.map +1 -0
- package/dist/analyzers/symbolGraph.d.ts +30 -0
- package/dist/analyzers/symbolGraph.d.ts.map +1 -0
- package/dist/analyzers/symbolGraph.js +380 -0
- package/dist/analyzers/symbolGraph.js.map +1 -0
- package/dist/analyzers/symbolTable.d.ts +18 -0
- package/dist/analyzers/symbolTable.d.ts.map +1 -0
- package/dist/analyzers/symbolTable.js +176 -0
- package/dist/analyzers/symbolTable.js.map +1 -0
- package/dist/analyzers/typeChecker.d.ts +13 -0
- package/dist/analyzers/typeChecker.d.ts.map +1 -0
- package/dist/analyzers/typeChecker.js +580 -0
- package/dist/analyzers/typeChecker.js.map +1 -0
- package/dist/analyzers/usagePatterns.d.ts +42 -0
- package/dist/analyzers/usagePatterns.d.ts.map +1 -0
- package/dist/analyzers/usagePatterns.js +75 -0
- package/dist/analyzers/usagePatterns.js.map +1 -0
- package/dist/api-contract/context/backend.d.ts +19 -0
- package/dist/api-contract/context/backend.d.ts.map +1 -0
- package/dist/api-contract/context/backend.js +64 -0
- package/dist/api-contract/context/backend.js.map +1 -0
- package/dist/api-contract/context/contract.d.ts +34 -0
- package/dist/api-contract/context/contract.d.ts.map +1 -0
- package/dist/api-contract/context/contract.js +306 -0
- package/dist/api-contract/context/contract.js.map +1 -0
- package/dist/api-contract/context/frontend.d.ts +19 -0
- package/dist/api-contract/context/frontend.d.ts.map +1 -0
- package/dist/api-contract/context/frontend.js +64 -0
- package/dist/api-contract/context/frontend.js.map +1 -0
- package/dist/api-contract/detector.d.ts +28 -0
- package/dist/api-contract/detector.d.ts.map +1 -0
- package/dist/api-contract/detector.js +393 -0
- package/dist/api-contract/detector.js.map +1 -0
- package/dist/api-contract/extractors/python.d.ts +32 -0
- package/dist/api-contract/extractors/python.d.ts.map +1 -0
- package/dist/api-contract/extractors/python.js +521 -0
- package/dist/api-contract/extractors/python.js.map +1 -0
- package/dist/api-contract/extractors/pythonAstUtils.d.ts +44 -0
- package/dist/api-contract/extractors/pythonAstUtils.d.ts.map +1 -0
- package/dist/api-contract/extractors/pythonAstUtils.js +489 -0
- package/dist/api-contract/extractors/pythonAstUtils.js.map +1 -0
- package/dist/api-contract/extractors/tsAstUtils.d.ts +47 -0
- package/dist/api-contract/extractors/tsAstUtils.d.ts.map +1 -0
- package/dist/api-contract/extractors/tsAstUtils.js +173 -0
- package/dist/api-contract/extractors/tsAstUtils.js.map +1 -0
- package/dist/api-contract/extractors/typescript.d.ts +32 -0
- package/dist/api-contract/extractors/typescript.d.ts.map +1 -0
- package/dist/api-contract/extractors/typescript.js +666 -0
- package/dist/api-contract/extractors/typescript.js.map +1 -0
- package/dist/api-contract/index.d.ts +104 -0
- package/dist/api-contract/index.d.ts.map +1 -0
- package/dist/api-contract/index.js +232 -0
- package/dist/api-contract/index.js.map +1 -0
- package/dist/api-contract/types.d.ts +151 -0
- package/dist/api-contract/types.d.ts.map +1 -0
- package/dist/api-contract/types.js +19 -0
- package/dist/api-contract/types.js.map +1 -0
- package/dist/api-contract/validators/endpoint.d.ts +21 -0
- package/dist/api-contract/validators/endpoint.d.ts.map +1 -0
- package/dist/api-contract/validators/endpoint.js +224 -0
- package/dist/api-contract/validators/endpoint.js.map +1 -0
- package/dist/api-contract/validators/index.d.ts +40 -0
- package/dist/api-contract/validators/index.d.ts.map +1 -0
- package/dist/api-contract/validators/index.js +875 -0
- package/dist/api-contract/validators/index.js.map +1 -0
- package/dist/api-contract/validators/parameter.d.ts +17 -0
- package/dist/api-contract/validators/parameter.d.ts.map +1 -0
- package/dist/api-contract/validators/parameter.js +250 -0
- package/dist/api-contract/validators/parameter.js.map +1 -0
- package/dist/api-contract/validators/type.d.ts +38 -0
- package/dist/api-contract/validators/type.d.ts.map +1 -0
- package/dist/api-contract/validators/type.js +244 -0
- package/dist/api-contract/validators/type.js.map +1 -0
- package/dist/context/apiContract/complexTypeSupport.d.ts +83 -0
- package/dist/context/apiContract/complexTypeSupport.d.ts.map +1 -0
- package/dist/context/apiContract/complexTypeSupport.js +665 -0
- package/dist/context/apiContract/complexTypeSupport.js.map +1 -0
- package/dist/context/apiContract/graphqlSupport.d.ts +105 -0
- package/dist/context/apiContract/graphqlSupport.d.ts.map +1 -0
- package/dist/context/apiContract/graphqlSupport.js +671 -0
- package/dist/context/apiContract/graphqlSupport.js.map +1 -0
- package/dist/context/apiContract/index.d.ts +14 -0
- package/dist/context/apiContract/index.d.ts.map +1 -0
- package/dist/context/apiContract/index.js +17 -0
- package/dist/context/apiContract/index.js.map +1 -0
- package/dist/context/apiContract/webSocketSupport.d.ts +104 -0
- package/dist/context/apiContract/webSocketSupport.d.ts.map +1 -0
- package/dist/context/apiContract/webSocketSupport.js +465 -0
- package/dist/context/apiContract/webSocketSupport.js.map +1 -0
- package/dist/context/apiContractContext.d.ts +15 -0
- package/dist/context/apiContractContext.d.ts.map +1 -0
- package/dist/context/apiContractContext.js +979 -0
- package/dist/context/apiContractContext.js.map +1 -0
- package/dist/context/apiContractExtraction.d.ts +52 -0
- package/dist/context/apiContractExtraction.d.ts.map +1 -0
- package/dist/context/apiContractExtraction.js +438 -0
- package/dist/context/apiContractExtraction.js.map +1 -0
- package/dist/context/contextLineage.d.ts +79 -0
- package/dist/context/contextLineage.d.ts.map +1 -0
- package/dist/context/contextLineage.js +259 -0
- package/dist/context/contextLineage.js.map +1 -0
- package/dist/context/contextOrchestrator.d.ts +57 -0
- package/dist/context/contextOrchestrator.d.ts.map +1 -0
- package/dist/context/contextOrchestrator.js +162 -0
- package/dist/context/contextOrchestrator.js.map +1 -0
- package/dist/context/intentTracker.d.ts +73 -0
- package/dist/context/intentTracker.d.ts.map +1 -0
- package/dist/context/intentTracker.js +168 -0
- package/dist/context/intentTracker.js.map +1 -0
- package/dist/context/projectContext.d.ts +219 -0
- package/dist/context/projectContext.d.ts.map +1 -0
- package/dist/context/projectContext.js +1984 -0
- package/dist/context/projectContext.js.map +1 -0
- package/dist/prompts/index.d.ts +17 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +260 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/library.d.ts +51 -0
- package/dist/prompts/library.d.ts.map +1 -0
- package/dist/prompts/library.js +65 -0
- package/dist/prompts/library.js.map +1 -0
- package/dist/prompts/templates.d.ts +44 -0
- package/dist/prompts/templates.d.ts.map +1 -0
- package/dist/prompts/templates.js +97 -0
- package/dist/prompts/templates.js.map +1 -0
- package/dist/queue/jobPersistence.d.ts +46 -0
- package/dist/queue/jobPersistence.d.ts.map +1 -0
- package/dist/queue/jobPersistence.js +158 -0
- package/dist/queue/jobPersistence.js.map +1 -0
- package/dist/queue/jobQueue.d.ts +116 -0
- package/dist/queue/jobQueue.d.ts.map +1 -0
- package/dist/queue/jobQueue.js +275 -0
- package/dist/queue/jobQueue.js.map +1 -0
- package/dist/queue/validationJob.d.ts +69 -0
- package/dist/queue/validationJob.d.ts.map +1 -0
- package/dist/queue/validationJob.js +435 -0
- package/dist/queue/validationJob.js.map +1 -0
- package/dist/resources/index.d.ts +15 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +328 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/validationReportStore.d.ts +170 -0
- package/dist/resources/validationReportStore.d.ts.map +1 -0
- package/dist/resources/validationReportStore.js +515 -0
- package/dist/resources/validationReportStore.js.map +1 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +102 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/asyncValidation.d.ts +19 -0
- package/dist/tools/asyncValidation.d.ts.map +1 -0
- package/dist/tools/asyncValidation.js +346 -0
- package/dist/tools/asyncValidation.js.map +1 -0
- package/dist/tools/buildContext.d.ts +17 -0
- package/dist/tools/buildContext.d.ts.map +1 -0
- package/dist/tools/buildContext.js +188 -0
- package/dist/tools/buildContext.js.map +1 -0
- package/dist/tools/getDependencyGraph.d.ts +16 -0
- package/dist/tools/getDependencyGraph.d.ts.map +1 -0
- package/dist/tools/getDependencyGraph.js +436 -0
- package/dist/tools/getDependencyGraph.js.map +1 -0
- package/dist/tools/incrementalValidation.d.ts +71 -0
- package/dist/tools/incrementalValidation.d.ts.map +1 -0
- package/dist/tools/incrementalValidation.js +203 -0
- package/dist/tools/incrementalValidation.js.map +1 -0
- package/dist/tools/index.d.ts +24 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +106 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/validateCode.d.ts +17 -0
- package/dist/tools/validateCode.d.ts.map +1 -0
- package/dist/tools/validateCode.js +368 -0
- package/dist/tools/validateCode.js.map +1 -0
- package/dist/tools/validateCodeLite.d.ts +2 -0
- package/dist/tools/validateCodeLite.d.ts.map +1 -0
- package/dist/tools/validateCodeLite.js +2 -0
- package/dist/tools/validateCodeLite.js.map +1 -0
- package/dist/tools/validation/builtins.d.ts +92 -0
- package/dist/tools/validation/builtins.d.ts.map +1 -0
- package/dist/tools/validation/builtins.js +2184 -0
- package/dist/tools/validation/builtins.js.map +1 -0
- package/dist/tools/validation/contextualNaming.d.ts +99 -0
- package/dist/tools/validation/contextualNaming.d.ts.map +1 -0
- package/dist/tools/validation/contextualNaming.js +959 -0
- package/dist/tools/validation/contextualNaming.js.map +1 -0
- package/dist/tools/validation/deadCode.d.ts +115 -0
- package/dist/tools/validation/deadCode.d.ts.map +1 -0
- package/dist/tools/validation/deadCode.js +861 -0
- package/dist/tools/validation/deadCode.js.map +1 -0
- package/dist/tools/validation/extractors/index.d.ts +131 -0
- package/dist/tools/validation/extractors/index.d.ts.map +1 -0
- package/dist/tools/validation/extractors/index.js +233 -0
- package/dist/tools/validation/extractors/index.js.map +1 -0
- package/dist/tools/validation/extractors/javascript.d.ts +73 -0
- package/dist/tools/validation/extractors/javascript.d.ts.map +1 -0
- package/dist/tools/validation/extractors/javascript.js +1841 -0
- package/dist/tools/validation/extractors/javascript.js.map +1 -0
- package/dist/tools/validation/extractors/python.d.ts +93 -0
- package/dist/tools/validation/extractors/python.d.ts.map +1 -0
- package/dist/tools/validation/extractors/python.js +799 -0
- package/dist/tools/validation/extractors/python.js.map +1 -0
- package/dist/tools/validation/manifest.d.ts +45 -0
- package/dist/tools/validation/manifest.d.ts.map +1 -0
- package/dist/tools/validation/manifest.js +719 -0
- package/dist/tools/validation/manifest.js.map +1 -0
- package/dist/tools/validation/parser.d.ts +58 -0
- package/dist/tools/validation/parser.d.ts.map +1 -0
- package/dist/tools/validation/parser.js +232 -0
- package/dist/tools/validation/parser.js.map +1 -0
- package/dist/tools/validation/registry.d.ts +15 -0
- package/dist/tools/validation/registry.d.ts.map +1 -0
- package/dist/tools/validation/registry.js +169 -0
- package/dist/tools/validation/registry.js.map +1 -0
- package/dist/tools/validation/scoring.d.ts +54 -0
- package/dist/tools/validation/scoring.d.ts.map +1 -0
- package/dist/tools/validation/scoring.js +242 -0
- package/dist/tools/validation/scoring.js.map +1 -0
- package/dist/tools/validation/types.d.ts +120 -0
- package/dist/tools/validation/types.d.ts.map +1 -0
- package/dist/tools/validation/types.js +11 -0
- package/dist/tools/validation/types.js.map +1 -0
- package/dist/tools/validation/unusedLocals.d.ts +36 -0
- package/dist/tools/validation/unusedLocals.d.ts.map +1 -0
- package/dist/tools/validation/unusedLocals.js +333 -0
- package/dist/tools/validation/unusedLocals.js.map +1 -0
- package/dist/tools/validation/validation.d.ts +98 -0
- package/dist/tools/validation/validation.d.ts.map +1 -0
- package/dist/tools/validation/validation.js +1837 -0
- package/dist/tools/validation/validation.js.map +1 -0
- package/dist/types/codeGraph.d.ts +163 -0
- package/dist/types/codeGraph.d.ts.map +1 -0
- package/dist/types/codeGraph.js +9 -0
- package/dist/types/codeGraph.js.map +1 -0
- package/dist/types/symbolGraph.d.ts +68 -0
- package/dist/types/symbolGraph.d.ts.map +1 -0
- package/dist/types/symbolGraph.js +10 -0
- package/dist/types/symbolGraph.js.map +1 -0
- package/dist/types/tools.d.ts +43 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +7 -0
- package/dist/types/tools.js.map +1 -0
- package/dist/utils/fileFilter.d.ts +37 -0
- package/dist/utils/fileFilter.d.ts.map +1 -0
- package/dist/utils/fileFilter.js +91 -0
- package/dist/utils/fileFilter.js.map +1 -0
- package/dist/utils/gitUtils.d.ts +28 -0
- package/dist/utils/gitUtils.d.ts.map +1 -0
- package/dist/utils/gitUtils.js +81 -0
- package/dist/utils/gitUtils.js.map +1 -0
- package/dist/utils/logger.d.ts +15 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +38 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/serialization.d.ts +25 -0
- package/dist/utils/serialization.d.ts.map +1 -0
- package/dist/utils/serialization.js +53 -0
- package/dist/utils/serialization.js.map +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Contract Guardian - Complex TypeScript Type Handling
|
|
3
|
+
*
|
|
4
|
+
* Handles advanced TypeScript types including:
|
|
5
|
+
* - Union types (string | number)
|
|
6
|
+
* - Generic types (Array<T>, Promise<T>)
|
|
7
|
+
* - Intersection types (Type1 & Type2)
|
|
8
|
+
* - Mapped types (Partial<T>, Required<T>, etc.)
|
|
9
|
+
* - Conditional types
|
|
10
|
+
* - Type aliases
|
|
11
|
+
* - Cross-file type resolution
|
|
12
|
+
*
|
|
13
|
+
* @format
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from "fs/promises";
|
|
16
|
+
import * as path from "path";
|
|
17
|
+
import { logger } from "../../utils/logger.js";
|
|
18
|
+
import { getParser } from "../../tools/validation/parser.js";
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Type Parser
|
|
21
|
+
// ============================================================================
|
|
22
|
+
/**
|
|
23
|
+
* Parse a complex TypeScript type string into structured info
|
|
24
|
+
*/
|
|
25
|
+
export function parseComplexType(typeString, file, line) {
|
|
26
|
+
const trimmed = typeString.trim();
|
|
27
|
+
// Check for union types: string | number | null
|
|
28
|
+
if (trimmed.includes("|")) {
|
|
29
|
+
return parseUnionType(trimmed, file, line);
|
|
30
|
+
}
|
|
31
|
+
// Check for intersection types: Type1 & Type2
|
|
32
|
+
if (trimmed.includes("&")) {
|
|
33
|
+
return parseIntersectionType(trimmed, file, line);
|
|
34
|
+
}
|
|
35
|
+
// Check for mapped types FIRST: Partial<T>, Required<T>, etc.
|
|
36
|
+
// These look like generics but are special TypeScript utility types
|
|
37
|
+
const mappedTypeMatch = trimmed.match(/^(Partial|Required|Readonly|Pick|Omit|Record|Exclude|Extract)<(.+)>$/);
|
|
38
|
+
if (mappedTypeMatch) {
|
|
39
|
+
return parseMappedType(mappedTypeMatch[1], mappedTypeMatch[2], file, line);
|
|
40
|
+
}
|
|
41
|
+
// Check for generic types: Array<T>, Promise<T>, Map<K, V>
|
|
42
|
+
if (trimmed.includes("<") && trimmed.includes(">")) {
|
|
43
|
+
return parseGenericType(trimmed, file, line);
|
|
44
|
+
}
|
|
45
|
+
// Check for conditional types: T extends U ? X : Y
|
|
46
|
+
if (trimmed.includes("extends") && trimmed.includes("?")) {
|
|
47
|
+
return parseConditionalType(trimmed, file, line);
|
|
48
|
+
}
|
|
49
|
+
// Primitive type
|
|
50
|
+
return {
|
|
51
|
+
name: trimmed,
|
|
52
|
+
kind: "primitive",
|
|
53
|
+
file,
|
|
54
|
+
line,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function parseUnionType(typeString, file, line) {
|
|
58
|
+
const parts = splitTypeString(typeString, "|");
|
|
59
|
+
const constituents = parts.map(p => parseComplexType(p.trim(), file, line));
|
|
60
|
+
// Check if it's a nullable type (includes null or undefined)
|
|
61
|
+
const isNullable = constituents.some(c => c.name === "null" || c.name === "undefined");
|
|
62
|
+
return {
|
|
63
|
+
name: typeString,
|
|
64
|
+
kind: "union",
|
|
65
|
+
constituents,
|
|
66
|
+
file,
|
|
67
|
+
line,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function parseIntersectionType(typeString, file, line) {
|
|
71
|
+
const parts = splitTypeString(typeString, "&");
|
|
72
|
+
const constituents = parts.map(p => parseComplexType(p.trim(), file, line));
|
|
73
|
+
return {
|
|
74
|
+
name: typeString,
|
|
75
|
+
kind: "intersection",
|
|
76
|
+
constituents,
|
|
77
|
+
file,
|
|
78
|
+
line,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function parseGenericType(typeString, file, line) {
|
|
82
|
+
// Match: TypeName<T1, T2, ...>
|
|
83
|
+
const match = typeString.match(/^(\w+)<(.+)>$/);
|
|
84
|
+
if (!match) {
|
|
85
|
+
return {
|
|
86
|
+
name: typeString,
|
|
87
|
+
kind: "primitive",
|
|
88
|
+
file,
|
|
89
|
+
line,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const baseType = match[1];
|
|
93
|
+
const paramsString = match[2];
|
|
94
|
+
// Split type parameters (handle nested generics)
|
|
95
|
+
const typeParameters = splitTypeParams(paramsString);
|
|
96
|
+
return {
|
|
97
|
+
name: typeString,
|
|
98
|
+
kind: "generic",
|
|
99
|
+
baseType,
|
|
100
|
+
typeParameters,
|
|
101
|
+
file,
|
|
102
|
+
line,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function parseMappedType(mappedType, typeArg, file, line) {
|
|
106
|
+
const mappedTypeMap = {
|
|
107
|
+
Partial: "partial",
|
|
108
|
+
Required: "required",
|
|
109
|
+
Readonly: "readonly",
|
|
110
|
+
Pick: "pick",
|
|
111
|
+
Omit: "omit",
|
|
112
|
+
Record: "record",
|
|
113
|
+
Exclude: "exclude",
|
|
114
|
+
Extract: "extract",
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
name: `${mappedType}<${typeArg}>`,
|
|
118
|
+
kind: "mapped",
|
|
119
|
+
mappedType: mappedTypeMap[mappedType],
|
|
120
|
+
mappedTypeArgument: typeArg,
|
|
121
|
+
file,
|
|
122
|
+
line,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function parseConditionalType(typeString, file, line) {
|
|
126
|
+
// Match: T extends U ? X : Y
|
|
127
|
+
const match = typeString.match(/^(.+?)\s+extends\s+(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/);
|
|
128
|
+
if (!match) {
|
|
129
|
+
return {
|
|
130
|
+
name: typeString,
|
|
131
|
+
kind: "primitive",
|
|
132
|
+
file,
|
|
133
|
+
line,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
name: typeString,
|
|
138
|
+
kind: "conditional",
|
|
139
|
+
condition: {
|
|
140
|
+
checkType: match[1].trim(),
|
|
141
|
+
extendsType: match[2].trim(),
|
|
142
|
+
trueType: match[3].trim(),
|
|
143
|
+
falseType: match[4].trim(),
|
|
144
|
+
},
|
|
145
|
+
file,
|
|
146
|
+
line,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Split a type string by delimiter, respecting nested angle brackets
|
|
151
|
+
*/
|
|
152
|
+
function splitTypeString(typeString, delimiter) {
|
|
153
|
+
const parts = [];
|
|
154
|
+
let current = "";
|
|
155
|
+
let depth = 0;
|
|
156
|
+
for (const char of typeString) {
|
|
157
|
+
if (char === "<")
|
|
158
|
+
depth++;
|
|
159
|
+
if (char === ">")
|
|
160
|
+
depth--;
|
|
161
|
+
if (char === delimiter && depth === 0) {
|
|
162
|
+
parts.push(current.trim());
|
|
163
|
+
current = "";
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
current += char;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (current.trim()) {
|
|
170
|
+
parts.push(current.trim());
|
|
171
|
+
}
|
|
172
|
+
return parts;
|
|
173
|
+
}
|
|
174
|
+
function splitTypeParams(paramsString) {
|
|
175
|
+
const params = [];
|
|
176
|
+
let current = "";
|
|
177
|
+
let depth = 0;
|
|
178
|
+
for (const char of paramsString) {
|
|
179
|
+
if (char === "<")
|
|
180
|
+
depth++;
|
|
181
|
+
if (char === ">")
|
|
182
|
+
depth--;
|
|
183
|
+
if (char === "," && depth === 0) {
|
|
184
|
+
params.push(current.trim());
|
|
185
|
+
current = "";
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
current += char;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (current.trim()) {
|
|
192
|
+
params.push(current.trim());
|
|
193
|
+
}
|
|
194
|
+
return params;
|
|
195
|
+
}
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Type Resolution
|
|
198
|
+
// ============================================================================
|
|
199
|
+
/**
|
|
200
|
+
* Resolve a complex type to its base type
|
|
201
|
+
* Expands type aliases, evaluates conditional types, etc.
|
|
202
|
+
*/
|
|
203
|
+
export function resolveComplexType(typeInfo, typeDefinitions, context) {
|
|
204
|
+
// Check for circular references
|
|
205
|
+
if (context.pendingResolutions.has(typeInfo.name)) {
|
|
206
|
+
context.errors.push({
|
|
207
|
+
typeName: typeInfo.name,
|
|
208
|
+
file: typeInfo.file,
|
|
209
|
+
message: "Circular type reference detected",
|
|
210
|
+
line: typeInfo.line,
|
|
211
|
+
});
|
|
212
|
+
return typeInfo;
|
|
213
|
+
}
|
|
214
|
+
// Check if already resolved
|
|
215
|
+
if (context.resolvedTypes.has(typeInfo.name)) {
|
|
216
|
+
return context.resolvedTypes.get(typeInfo.name);
|
|
217
|
+
}
|
|
218
|
+
context.pendingResolutions.add(typeInfo.name);
|
|
219
|
+
let resolved;
|
|
220
|
+
switch (typeInfo.kind) {
|
|
221
|
+
case "union":
|
|
222
|
+
resolved = resolveUnionType(typeInfo, typeDefinitions, context);
|
|
223
|
+
break;
|
|
224
|
+
case "intersection":
|
|
225
|
+
resolved = resolveIntersectionType(typeInfo, typeDefinitions, context);
|
|
226
|
+
break;
|
|
227
|
+
case "generic":
|
|
228
|
+
resolved = resolveGenericType(typeInfo, typeDefinitions, context);
|
|
229
|
+
break;
|
|
230
|
+
case "mapped":
|
|
231
|
+
resolved = resolveMappedType(typeInfo, typeDefinitions, context);
|
|
232
|
+
break;
|
|
233
|
+
case "conditional":
|
|
234
|
+
resolved = resolveConditionalType(typeInfo, typeDefinitions, context);
|
|
235
|
+
break;
|
|
236
|
+
case "alias":
|
|
237
|
+
resolved = resolveAliasType(typeInfo, typeDefinitions, context);
|
|
238
|
+
break;
|
|
239
|
+
default:
|
|
240
|
+
resolved = typeInfo;
|
|
241
|
+
}
|
|
242
|
+
context.pendingResolutions.delete(typeInfo.name);
|
|
243
|
+
context.resolvedTypes.set(typeInfo.name, resolved);
|
|
244
|
+
return resolved;
|
|
245
|
+
}
|
|
246
|
+
function resolveUnionType(typeInfo, typeDefinitions, context) {
|
|
247
|
+
if (!typeInfo.constituents)
|
|
248
|
+
return typeInfo;
|
|
249
|
+
const resolvedConstituents = typeInfo.constituents.map(c => resolveComplexType(c, typeDefinitions, context));
|
|
250
|
+
// Flatten nested unions
|
|
251
|
+
const flattened = [];
|
|
252
|
+
for (const c of resolvedConstituents) {
|
|
253
|
+
if (c.kind === "union" && c.constituents) {
|
|
254
|
+
flattened.push(...c.constituents);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
flattened.push(c);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
...typeInfo,
|
|
262
|
+
constituents: flattened,
|
|
263
|
+
resolvedType: flattened.map(c => c.resolvedType || c.name).join(" | "),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function resolveIntersectionType(typeInfo, typeDefinitions, context) {
|
|
267
|
+
if (!typeInfo.constituents)
|
|
268
|
+
return typeInfo;
|
|
269
|
+
const resolvedConstituents = typeInfo.constituents.map(c => resolveComplexType(c, typeDefinitions, context));
|
|
270
|
+
return {
|
|
271
|
+
...typeInfo,
|
|
272
|
+
constituents: resolvedConstituents,
|
|
273
|
+
resolvedType: resolvedConstituents.map(c => c.resolvedType || c.name).join(" & "),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function resolveGenericType(typeInfo, typeDefinitions, context) {
|
|
277
|
+
if (!typeInfo.typeParameters)
|
|
278
|
+
return typeInfo;
|
|
279
|
+
const resolvedParams = typeInfo.typeParameters.map(p => {
|
|
280
|
+
const parsed = parseComplexType(p, typeInfo.file, typeInfo.line);
|
|
281
|
+
return resolveComplexType(parsed, typeDefinitions, context);
|
|
282
|
+
});
|
|
283
|
+
// Handle built-in generic types
|
|
284
|
+
if (typeInfo.baseType === "Array" || typeInfo.baseType === "Promise") {
|
|
285
|
+
return {
|
|
286
|
+
...typeInfo,
|
|
287
|
+
resolvedType: `${typeInfo.baseType}<${resolvedParams[0]?.resolvedType || resolvedParams[0]?.name}>`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
...typeInfo,
|
|
292
|
+
resolvedType: `${typeInfo.baseType}<${resolvedParams.map(p => p.resolvedType || p.name).join(", ")}>`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function resolveMappedType(typeInfo, typeDefinitions, context) {
|
|
296
|
+
if (!typeInfo.mappedType || !typeInfo.mappedTypeArgument)
|
|
297
|
+
return typeInfo;
|
|
298
|
+
const baseType = typeDefinitions.get(typeInfo.mappedTypeArgument);
|
|
299
|
+
if (!baseType) {
|
|
300
|
+
return {
|
|
301
|
+
...typeInfo,
|
|
302
|
+
resolvedType: typeInfo.name,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const resolvedBase = resolveComplexType(baseType, typeDefinitions, context);
|
|
306
|
+
// Apply mapped type transformation
|
|
307
|
+
switch (typeInfo.mappedType) {
|
|
308
|
+
case "partial":
|
|
309
|
+
return {
|
|
310
|
+
...typeInfo,
|
|
311
|
+
resolvedType: `Partial<${resolvedBase.resolvedType || resolvedBase.name}>`,
|
|
312
|
+
};
|
|
313
|
+
case "required":
|
|
314
|
+
return {
|
|
315
|
+
...typeInfo,
|
|
316
|
+
resolvedType: `Required<${resolvedBase.resolvedType || resolvedBase.name}>`,
|
|
317
|
+
};
|
|
318
|
+
case "readonly":
|
|
319
|
+
return {
|
|
320
|
+
...typeInfo,
|
|
321
|
+
resolvedType: `Readonly<${resolvedBase.resolvedType || resolvedBase.name}>`,
|
|
322
|
+
};
|
|
323
|
+
default:
|
|
324
|
+
return {
|
|
325
|
+
...typeInfo,
|
|
326
|
+
resolvedType: typeInfo.name,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function resolveConditionalType(typeInfo, typeDefinitions, context) {
|
|
331
|
+
if (!typeInfo.condition)
|
|
332
|
+
return typeInfo;
|
|
333
|
+
const { checkType, extendsType, trueType, falseType } = typeInfo.condition;
|
|
334
|
+
// Simple case: check if checkType extends extendsType
|
|
335
|
+
// In a real implementation, this would be more sophisticated
|
|
336
|
+
const resolvedCheck = parseComplexType(checkType, typeInfo.file, typeInfo.line);
|
|
337
|
+
const resolvedExtends = parseComplexType(extendsType, typeInfo.file, typeInfo.line);
|
|
338
|
+
// For now, assume the condition is true if types match
|
|
339
|
+
const conditionResult = resolvedCheck.name === resolvedExtends.name;
|
|
340
|
+
const resultType = conditionResult ? trueType : falseType;
|
|
341
|
+
return {
|
|
342
|
+
...typeInfo,
|
|
343
|
+
resolvedType: resultType,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function resolveAliasType(typeInfo, typeDefinitions, context) {
|
|
347
|
+
const aliasedType = typeDefinitions.get(typeInfo.name);
|
|
348
|
+
if (aliasedType) {
|
|
349
|
+
return resolveComplexType(aliasedType, typeDefinitions, context);
|
|
350
|
+
}
|
|
351
|
+
return typeInfo;
|
|
352
|
+
}
|
|
353
|
+
// ============================================================================
|
|
354
|
+
// Type Compatibility Checking
|
|
355
|
+
// ============================================================================
|
|
356
|
+
/**
|
|
357
|
+
* Check if a TypeScript type is compatible with a Python type
|
|
358
|
+
*/
|
|
359
|
+
export function checkTypeCompatibility(tsType, pyType) {
|
|
360
|
+
const issues = [];
|
|
361
|
+
let score = 100;
|
|
362
|
+
// Normalize Python type
|
|
363
|
+
const normalizedPyType = normalizePythonType(pyType);
|
|
364
|
+
// Handle union types
|
|
365
|
+
if (tsType.kind === "union" && tsType.constituents) {
|
|
366
|
+
// Check if any constituent matches
|
|
367
|
+
const matches = tsType.constituents.some(c => {
|
|
368
|
+
const result = checkTypeCompatibility(c, pyType);
|
|
369
|
+
return result.compatible;
|
|
370
|
+
});
|
|
371
|
+
if (!matches) {
|
|
372
|
+
issues.push({
|
|
373
|
+
severity: "error",
|
|
374
|
+
message: `Union type '${tsType.name}' is not compatible with Python type '${pyType}'`,
|
|
375
|
+
frontendType: tsType.name,
|
|
376
|
+
backendType: pyType,
|
|
377
|
+
});
|
|
378
|
+
score = 0;
|
|
379
|
+
}
|
|
380
|
+
return { compatible: matches, score, issues };
|
|
381
|
+
}
|
|
382
|
+
// Check primitive type compatibility
|
|
383
|
+
const tsPrimitive = getPrimitiveTypeName(tsType);
|
|
384
|
+
const compatibility = getTypeCompatibility(tsPrimitive, normalizedPyType);
|
|
385
|
+
if (!compatibility.compatible) {
|
|
386
|
+
issues.push({
|
|
387
|
+
severity: "error",
|
|
388
|
+
message: `Type mismatch: TypeScript '${tsType.name}' is not compatible with Python '${pyType}'`,
|
|
389
|
+
frontendType: tsType.name,
|
|
390
|
+
backendType: pyType,
|
|
391
|
+
});
|
|
392
|
+
score = 0;
|
|
393
|
+
}
|
|
394
|
+
else if (compatibility.warning) {
|
|
395
|
+
issues.push({
|
|
396
|
+
severity: "warning",
|
|
397
|
+
message: compatibility.warning,
|
|
398
|
+
frontendType: tsType.name,
|
|
399
|
+
backendType: pyType,
|
|
400
|
+
});
|
|
401
|
+
score -= 20;
|
|
402
|
+
}
|
|
403
|
+
return { compatible: compatibility.compatible, score: Math.max(0, score), issues };
|
|
404
|
+
}
|
|
405
|
+
function normalizePythonType(pyType) {
|
|
406
|
+
return pyType
|
|
407
|
+
.replace(/Optional\[(.+?)\]/, "$1 | null")
|
|
408
|
+
.replace(/List\[(.+?)\]/, "Array<$1>")
|
|
409
|
+
.replace(/Dict\[(.+?),\s*(.+?)\]/, "Record<$1, $2>")
|
|
410
|
+
.replace(/Union\[(.+?)\]/, "$1")
|
|
411
|
+
.trim();
|
|
412
|
+
}
|
|
413
|
+
function getPrimitiveTypeName(typeInfo) {
|
|
414
|
+
if (typeInfo.resolvedType) {
|
|
415
|
+
return typeInfo.resolvedType;
|
|
416
|
+
}
|
|
417
|
+
// Handle generic types
|
|
418
|
+
if (typeInfo.kind === "generic" && typeInfo.baseType) {
|
|
419
|
+
if (typeInfo.baseType === "Array") {
|
|
420
|
+
return "array";
|
|
421
|
+
}
|
|
422
|
+
if (typeInfo.baseType === "Promise") {
|
|
423
|
+
return "promise";
|
|
424
|
+
}
|
|
425
|
+
if (typeInfo.baseType === "Record") {
|
|
426
|
+
return "record";
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return typeInfo.name.toLowerCase();
|
|
430
|
+
}
|
|
431
|
+
function getTypeCompatibility(tsType, pyType) {
|
|
432
|
+
// TypeScript to Python type mapping
|
|
433
|
+
const typeMap = {
|
|
434
|
+
string: ["str", "string", "text"],
|
|
435
|
+
number: ["int", "float", "decimal", "number"],
|
|
436
|
+
boolean: ["bool", "boolean"],
|
|
437
|
+
null: ["none", "null"],
|
|
438
|
+
undefined: ["none", "null"],
|
|
439
|
+
any: ["any"],
|
|
440
|
+
unknown: ["any"],
|
|
441
|
+
array: ["list", "array", "sequence"],
|
|
442
|
+
record: ["dict", "mapping", "object"],
|
|
443
|
+
date: ["date", "datetime"],
|
|
444
|
+
bigint: ["int"],
|
|
445
|
+
symbol: [],
|
|
446
|
+
object: ["dict", "object"],
|
|
447
|
+
};
|
|
448
|
+
const normalizedTs = tsType.toLowerCase();
|
|
449
|
+
const normalizedPy = pyType.toLowerCase();
|
|
450
|
+
// Direct match
|
|
451
|
+
if (normalizedTs === normalizedPy) {
|
|
452
|
+
return { compatible: true };
|
|
453
|
+
}
|
|
454
|
+
// Check mapping
|
|
455
|
+
const compatibleTypes = typeMap[normalizedTs] || [];
|
|
456
|
+
if (compatibleTypes.includes(normalizedPy)) {
|
|
457
|
+
return { compatible: true };
|
|
458
|
+
}
|
|
459
|
+
// Special case: array types
|
|
460
|
+
if (normalizedTs === "array" && normalizedPy.startsWith("array<")) {
|
|
461
|
+
return { compatible: true };
|
|
462
|
+
}
|
|
463
|
+
// Special cases
|
|
464
|
+
if (normalizedTs === "string" && normalizedPy === "int") {
|
|
465
|
+
return {
|
|
466
|
+
compatible: false,
|
|
467
|
+
warning: "Type mismatch: string cannot be assigned to int",
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
if (normalizedTs === "number" && normalizedPy === "str") {
|
|
471
|
+
return {
|
|
472
|
+
compatible: true,
|
|
473
|
+
warning: "Implicit conversion: number to string",
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
if (normalizedTs === "array" && !["list", "array", "sequence"].includes(normalizedPy)) {
|
|
477
|
+
return {
|
|
478
|
+
compatible: false,
|
|
479
|
+
warning: "Type mismatch: array type requires Python List",
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
return { compatible: false };
|
|
483
|
+
}
|
|
484
|
+
// ============================================================================
|
|
485
|
+
// Import Resolution
|
|
486
|
+
// ============================================================================
|
|
487
|
+
/**
|
|
488
|
+
* Extract all type definitions from a TypeScript file
|
|
489
|
+
* Including imported types
|
|
490
|
+
*/
|
|
491
|
+
export async function extractAllTypeDefinitions(filePath, projectPath) {
|
|
492
|
+
const typeMap = new Map();
|
|
493
|
+
try {
|
|
494
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
495
|
+
const parser = getParser("typescript");
|
|
496
|
+
const tree = parser.parse(content);
|
|
497
|
+
// Extract local type definitions
|
|
498
|
+
extractTypeDefinitionsFromAST(tree.rootNode, content, filePath, typeMap);
|
|
499
|
+
// Extract imports and resolve them
|
|
500
|
+
const imports = extractTypeImportsFromAST(tree.rootNode, content);
|
|
501
|
+
for (const importInfo of imports) {
|
|
502
|
+
const resolvedPath = resolveImportPath(importInfo.path, filePath, projectPath);
|
|
503
|
+
if (resolvedPath) {
|
|
504
|
+
try {
|
|
505
|
+
const importedTypes = await extractAllTypeDefinitions(resolvedPath, projectPath);
|
|
506
|
+
for (const [name, typeInfo] of importedTypes) {
|
|
507
|
+
if (!typeMap.has(name)) {
|
|
508
|
+
typeMap.set(name, typeInfo);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
// Ignore import resolution errors
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
logger.debug(`Failed to extract type definitions from ${filePath}: ${err}`);
|
|
520
|
+
}
|
|
521
|
+
return typeMap;
|
|
522
|
+
}
|
|
523
|
+
function extractTypeDefinitionsFromAST(node, content, filePath, typeMap) {
|
|
524
|
+
if (!node)
|
|
525
|
+
return;
|
|
526
|
+
// Type alias: type MyType = ...
|
|
527
|
+
if (node.type === "type_alias_declaration") {
|
|
528
|
+
const nameNode = node.childForFieldName("name");
|
|
529
|
+
const valueNode = node.childForFieldName("value");
|
|
530
|
+
if (nameNode && valueNode) {
|
|
531
|
+
const name = getNodeText(nameNode, content);
|
|
532
|
+
const value = getNodeText(valueNode, content);
|
|
533
|
+
const line = node.startPosition.row + 1;
|
|
534
|
+
const typeInfo = parseComplexType(value, filePath, line);
|
|
535
|
+
typeInfo.name = name;
|
|
536
|
+
typeInfo.kind = "alias";
|
|
537
|
+
typeMap.set(name, typeInfo);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// Interface declaration
|
|
541
|
+
if (node.type === "interface_declaration") {
|
|
542
|
+
const nameNode = node.childForFieldName("name");
|
|
543
|
+
if (nameNode) {
|
|
544
|
+
const name = getNodeText(nameNode, content);
|
|
545
|
+
const line = node.startPosition.row + 1;
|
|
546
|
+
typeMap.set(name, {
|
|
547
|
+
name,
|
|
548
|
+
kind: "primitive",
|
|
549
|
+
file: filePath,
|
|
550
|
+
line,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
// Recursively traverse children
|
|
555
|
+
for (const child of node.children || []) {
|
|
556
|
+
extractTypeDefinitionsFromAST(child, content, filePath, typeMap);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function extractTypeImportsFromAST(node, content) {
|
|
560
|
+
const imports = [];
|
|
561
|
+
if (!node)
|
|
562
|
+
return imports;
|
|
563
|
+
if (node.type === "import_statement" || node.type === "import_declaration") {
|
|
564
|
+
const sourceNode = node.childForFieldName("source");
|
|
565
|
+
if (sourceNode) {
|
|
566
|
+
const importPath = getNodeText(sourceNode, content).replace(/['"]/g, "");
|
|
567
|
+
// Extract imported names
|
|
568
|
+
const clauseNode = node.childForFieldName("clause") || node.childForFieldName("importClause");
|
|
569
|
+
if (clauseNode) {
|
|
570
|
+
const names = [];
|
|
571
|
+
for (const child of clauseNode.children || []) {
|
|
572
|
+
if (child.type === "identifier" || child.type === "type_identifier") {
|
|
573
|
+
names.push(getNodeText(child, content));
|
|
574
|
+
}
|
|
575
|
+
if (child.type === "named_imports") {
|
|
576
|
+
for (const spec of child.children || []) {
|
|
577
|
+
if (spec.type === "import_specifier") {
|
|
578
|
+
const nameNode = spec.childForFieldName("name");
|
|
579
|
+
if (nameNode) {
|
|
580
|
+
names.push(getNodeText(nameNode, content));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
imports.push({ names, path: importPath });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
// Recursively traverse children
|
|
591
|
+
for (const child of node.children || []) {
|
|
592
|
+
imports.push(...extractTypeImportsFromAST(child, content));
|
|
593
|
+
}
|
|
594
|
+
return imports;
|
|
595
|
+
}
|
|
596
|
+
function resolveImportPath(importPath, fromFile, projectPath) {
|
|
597
|
+
// Handle relative imports
|
|
598
|
+
if (importPath.startsWith("./") || importPath.startsWith("../")) {
|
|
599
|
+
const fromDir = path.dirname(fromFile);
|
|
600
|
+
let resolvedPath = path.resolve(fromDir, importPath);
|
|
601
|
+
// Try common extensions
|
|
602
|
+
const extensions = [".ts", ".tsx", ".d.ts", "/index.ts", "/index.tsx"];
|
|
603
|
+
for (const ext of extensions) {
|
|
604
|
+
const fullPath = resolvedPath + ext;
|
|
605
|
+
try {
|
|
606
|
+
fsSync.accessSync(fullPath);
|
|
607
|
+
return fullPath;
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
// Try next extension
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// Handle path aliases (simplified - would need tsconfig.json parsing)
|
|
615
|
+
if (importPath.startsWith("@/") || importPath.startsWith("~/")) {
|
|
616
|
+
// Try to resolve from project root
|
|
617
|
+
const relativePath = importPath.slice(2); // Remove @/ or ~/
|
|
618
|
+
const possiblePath = path.join(projectPath, "src", relativePath);
|
|
619
|
+
const extensions = [".ts", ".tsx", ".d.ts", "/index.ts", "/index.tsx"];
|
|
620
|
+
for (const ext of extensions) {
|
|
621
|
+
const fullPath = possiblePath + ext;
|
|
622
|
+
try {
|
|
623
|
+
fsSync.accessSync(fullPath);
|
|
624
|
+
return fullPath;
|
|
625
|
+
}
|
|
626
|
+
catch {
|
|
627
|
+
// Try next extension
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
import * as fsSync from "fs";
|
|
634
|
+
function getNodeText(node, content) {
|
|
635
|
+
if (!node)
|
|
636
|
+
return "";
|
|
637
|
+
return content.slice(node.startIndex, node.endIndex);
|
|
638
|
+
}
|
|
639
|
+
// ============================================================================
|
|
640
|
+
// Main Entry Point
|
|
641
|
+
// ============================================================================
|
|
642
|
+
/**
|
|
643
|
+
* Enhance ApiTypeDefinition with complex type information
|
|
644
|
+
*/
|
|
645
|
+
export async function enhanceTypeWithComplexInfo(typeDef, projectPath) {
|
|
646
|
+
const typeMap = await extractAllTypeDefinitions(typeDef.file, projectPath);
|
|
647
|
+
const context = {
|
|
648
|
+
resolvedTypes: new Map(),
|
|
649
|
+
pendingResolutions: new Set(),
|
|
650
|
+
errors: [],
|
|
651
|
+
};
|
|
652
|
+
const enhancedFields = typeDef.fields.map(field => {
|
|
653
|
+
const complexType = parseComplexType(field.type, typeDef.file, typeDef.line);
|
|
654
|
+
const resolvedType = resolveComplexType(complexType, typeMap, context);
|
|
655
|
+
return {
|
|
656
|
+
...field,
|
|
657
|
+
complexType: resolvedType,
|
|
658
|
+
};
|
|
659
|
+
});
|
|
660
|
+
return {
|
|
661
|
+
...typeDef,
|
|
662
|
+
complexFields: enhancedFields,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
//# sourceMappingURL=complexTypeSupport.js.map
|