-
Notifications
You must be signed in to change notification settings - Fork 0
feat(traverse): improve type dependency analysis #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { BaseTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/base-type-handler' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getTypeBoxType } from '@daxserver/validation-schema-codegen/utils/typebox-call' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { makeTypeCall } from '@daxserver/validation-schema-codegen/utils/typebox-codegen-utils' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Node, ts, TypeReferenceNode } from 'ts-morph' | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -9,9 +10,23 @@ export class TypeReferenceHandler extends BaseTypeHandler { | |||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| handle(node: TypeReferenceNode): ts.Expression { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const referencedType = node.getTypeName() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeArguments = node.getTypeArguments() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Node.isIdentifier(referencedType)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeName = referencedType.getText() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // If there are type arguments, create a function call | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeArguments.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeBoxArgs = typeArguments.map((arg) => getTypeBoxType(arg)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return ts.factory.createCallExpression( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ts.factory.createIdentifier(typeName), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| typeBoxArgs, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Handle qualified names (e.g., ns.Vector) when emitting calls. The current path only supports identifiers. Qualified names will fall through or be emitted incorrectly if coerced into an Identifier. Build a property access chain from the EntityName for robust handling. - if (typeArguments.length > 0) {
- const typeBoxArgs = typeArguments.map((arg) => getTypeBoxType(arg))
-
- return ts.factory.createCallExpression(
- ts.factory.createIdentifier(typeName),
- undefined,
- typeBoxArgs,
- )
- }
+ if (typeArguments.length > 0) {
+ const typeBoxArgs = typeArguments.map((arg) => getTypeBoxType(arg))
+ const entity = node.getTypeName()
+ const toExpr = (e: typeof entity): ts.Expression => {
+ const cn = e.compilerNode
+ if (ts.isIdentifier(cn)) return ts.factory.createIdentifier(cn.text)
+ // QualifiedName: recursively build ns.prop.prop...
+ const left = toExpr((e as any).getLeft())
+ const right = (e as any).getRight().getText()
+ return ts.factory.createPropertyAccessExpression(left, right)
+ }
+ return ts.factory.createCallExpression(toExpr(entity as any), undefined, typeBoxArgs)
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // No type arguments, just return the identifier | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return ts.factory.createIdentifier(typeName) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,111 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { InterfaceDeclaration, Node, TypeAliasDeclaration, TypeReferenceNode } from 'ts-morph' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * AST traversal patterns used in dependency analysis | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class ASTTraversal { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static cache = new Map<string, string[]>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Extract interface names referenced by a type alias | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static extractInterfaceReferences( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| typeAlias: TypeAliasDeclaration, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interfaces: Map<string, InterfaceDeclaration>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): string[] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeNode = typeAlias.getTypeNode() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!typeNode) return [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cacheKey = `interface_refs_${typeNode.getText()}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cached = ASTTraversal.cache.get(cacheKey) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (cached) return cached | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const references: string[] = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const visited = new Set<Node>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const traverse = (node: Node): void => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (visited.has(node)) return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| visited.add(node) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle type references | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Node.isTypeReference(node)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeRefNode = node as TypeReferenceNode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeName = typeRefNode.getTypeName().getText() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (interfaces.has(typeName)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| references.push(typeName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Continue traversing to handle type arguments in generic instantiations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use forEachChild for better performance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node.forEachChild(traverse) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| traverse(typeNode) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Cache the result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ASTTraversal.cache.set(cacheKey, references) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return references | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Deduplicate references and store copies in cache. Traversals can encounter the same referenced name multiple times (e.g., repeated fields, unions). Use a Set to ensure uniqueness and store/return copies to maintain cache integrity. - const references: string[] = []
+ const references = new Set<string>()
const visited = new Set<Node>()
@@
- if (Node.isTypeReference(node)) {
+ if (Node.isTypeReference(node)) {
const typeRefNode = node as TypeReferenceNode
const typeName = typeRefNode.getTypeName().getText()
- if (interfaces.has(typeName)) {
- references.push(typeName)
- }
+ if (interfaces.has(typeName)) {
+ references.add(typeName)
+ }
// Continue traversing to handle type arguments in generic instantiations
}
@@
- ASTTraversal.cache.set(cacheKey, references)
- return references
+ const out = Array.from(references)
+ ASTTraversal.cache.set(cacheKey, [...out])
+ return out📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Extract type alias names referenced by an interface (e.g., in type parameter constraints) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static extractTypeAliasReferences( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interfaceDecl: InterfaceDeclaration, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| typeAliases: Map<string, TypeAliasDeclaration>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): string[] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cacheKey = `type_alias_refs_${interfaceDecl.getName()}_${interfaceDecl.getText()}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cached = ASTTraversal.cache.get(cacheKey) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (cached) return cached | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const references: string[] = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const visited = new Set<Node>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const traverse = (node: Node): void => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (visited.has(node)) return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| visited.add(node) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle type references | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Node.isTypeReference(node)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeRefNode = node as TypeReferenceNode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeName = typeRefNode.getTypeName().getText() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeAliases.has(typeName)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| references.push(typeName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return // No need to traverse children of type references | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+66
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t early-return on TypeReference; you’ll miss aliases in type arguments. In extractTypeAliasReferences, returning immediately after handling a TypeReference prevents traversal into its type arguments. You’ll miss cases like Wrapper. - if (Node.isTypeReference(node)) {
+ if (Node.isTypeReference(node)) {
const typeRefNode = node as TypeReferenceNode
const typeName = typeRefNode.getTypeName().getText()
if (typeAliases.has(typeName)) {
references.push(typeName)
}
- return // No need to traverse children of type references
+ // Intentionally continue to traverse children to catch nested references
}Also consider switching to a Set here and returning a unique list, mirroring extractInterfaceReferences. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use forEachChild for better performance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node.forEachChild(traverse) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check type parameters for constraints | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const typeParam of interfaceDecl.getTypeParameters()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const constraint = typeParam.getConstraint() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (constraint) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| traverse(constraint) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check heritage clauses | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const heritageClause of interfaceDecl.getHeritageClauses()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const typeNode of heritageClause.getTypeNodes()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| traverse(typeNode) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Cache the result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ASTTraversal.cache.set(cacheKey, references) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return references | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Clear the internal cache | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static clearCache(): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ASTTraversal.cache.clear() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| import { ASTTraversal } from '@daxserver/validation-schema-codegen/traverse/ast-traversal' | ||
| import { InterfaceDeclaration, TypeAliasDeclaration } from 'ts-morph' | ||
|
|
||
| /** | ||
| * Dependency analyzer for determining processing order | ||
| */ | ||
| export class DependencyAnalyzer { | ||
| /** | ||
| * Extract interface names referenced by a type alias | ||
| */ | ||
| extractInterfaceReferences( | ||
| typeAlias: TypeAliasDeclaration, | ||
| interfaces: Map<string, InterfaceDeclaration>, | ||
| ): string[] { | ||
| return ASTTraversal.extractInterfaceReferences(typeAlias, interfaces) | ||
| } | ||
|
|
||
| /** | ||
| * Extract type alias names referenced by an interface | ||
| */ | ||
| extractTypeAliasReferences( | ||
| interfaceDecl: InterfaceDeclaration, | ||
| typeAliases: Map<string, TypeAliasDeclaration>, | ||
| ): string[] { | ||
| return ASTTraversal.extractTypeAliasReferences(interfaceDecl, typeAliases) | ||
| } | ||
|
|
||
| /** | ||
| * Check if any type aliases reference interfaces | ||
| */ | ||
| hasInterfaceReferences( | ||
| typeAliases: TypeAliasDeclaration[], | ||
| interfaces: InterfaceDeclaration[], | ||
| ): boolean { | ||
| const interfaceMap = new Map<string, InterfaceDeclaration>() | ||
| for (const iface of interfaces) { | ||
| interfaceMap.set(iface.getName(), iface) | ||
| } | ||
|
|
||
| for (const typeAlias of typeAliases) { | ||
| const references = this.extractInterfaceReferences(typeAlias, interfaceMap) | ||
| if (references.length > 0) { | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| } | ||
|
|
||
| /** | ||
| * Get type aliases that reference interfaces, ordered by their dependencies | ||
| */ | ||
| getTypeAliasesReferencingInterfaces( | ||
| typeAliases: TypeAliasDeclaration[], | ||
| interfaces: InterfaceDeclaration[], | ||
| ): { typeAlias: TypeAliasDeclaration; referencedInterfaces: string[] }[] { | ||
| const interfaceMap = new Map<string, InterfaceDeclaration>() | ||
| for (const iface of interfaces) { | ||
| interfaceMap.set(iface.getName(), iface) | ||
| } | ||
|
|
||
| const result: { typeAlias: TypeAliasDeclaration; referencedInterfaces: string[] }[] = [] | ||
|
|
||
| for (const typeAlias of typeAliases) { | ||
| const references = this.extractInterfaceReferences(typeAlias, interfaceMap) | ||
| if (references.length > 0) { | ||
| result.push({ | ||
| typeAlias, | ||
| referencedInterfaces: references, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| return result | ||
| } | ||
|
|
||
| /** | ||
| * Determine the correct processing order for interfaces and type aliases | ||
| * Returns an object indicating which should be processed first | ||
| */ | ||
| analyzeProcessingOrder( | ||
| typeAliases: TypeAliasDeclaration[], | ||
| interfaces: InterfaceDeclaration[], | ||
| ): { | ||
| processInterfacesFirst: boolean | ||
| typeAliasesDependingOnInterfaces: string[] | ||
| interfacesDependingOnTypeAliases: string[] | ||
| } { | ||
| const typeAliasMap = new Map<string, TypeAliasDeclaration>() | ||
| const interfaceMap = new Map<string, InterfaceDeclaration>() | ||
|
|
||
| for (const typeAlias of typeAliases) { | ||
| typeAliasMap.set(typeAlias.getName(), typeAlias) | ||
| } | ||
|
|
||
| for (const interfaceDecl of interfaces) { | ||
| interfaceMap.set(interfaceDecl.getName(), interfaceDecl) | ||
| } | ||
|
|
||
| const typeAliasesDependingOnInterfaces: string[] = [] | ||
| const interfacesDependingOnTypeAliases: string[] = [] | ||
|
|
||
| // Check type aliases that depend on interfaces | ||
| for (const typeAlias of typeAliases) { | ||
| const interfaceRefs = this.extractInterfaceReferences(typeAlias, interfaceMap) | ||
| if (interfaceRefs.length > 0) { | ||
| typeAliasesDependingOnInterfaces.push(typeAlias.getName()) | ||
| } | ||
| } | ||
|
|
||
| // Check interfaces that depend on type aliases | ||
| for (const interfaceDecl of interfaces) { | ||
| const typeAliasRefs = this.extractTypeAliasReferences(interfaceDecl, typeAliasMap) | ||
| if (typeAliasRefs.length > 0) { | ||
| interfacesDependingOnTypeAliases.push(interfaceDecl.getName()) | ||
| } | ||
| } | ||
|
|
||
|
Comment on lines
+111
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incomplete interface→type-alias reference extraction misses interface members
Follow-up: extend // In src/traverse/ast-traversal.ts
static extractTypeAliasReferences(
interfaceDecl: InterfaceDeclaration,
typeAliases: Map<string, TypeAliasDeclaration>,
): string[] {
const cacheKey = `type_alias_refs_${interfaceDecl.getName()}_${interfaceDecl.getText()}`
const cached = ASTTraversal.cache.get(cacheKey)
if (cached) return cached
const references = new Set<string>()
const visit = (node: Node): void => {
if (Node.isTypeReference(node)) {
const typeName = (node as TypeReferenceNode).getTypeName().getText()
if (typeAliases.has(typeName)) references.add(typeName)
}
node.forEachChild(visit)
}
// 1) Type parameters (constraints)
for (const tp of interfaceDecl.getTypeParameters()) {
const c = tp.getConstraint()
if (c) visit(c)
}
// 2) Heritage clauses
for (const hc of interfaceDecl.getHeritageClauses()) {
for (const t of hc.getTypeNodes()) visit(t)
}
// 3) Members: properties, methods, index signatures, call signatures
for (const m of interfaceDecl.getMembers()) {
if (Node.isPropertySignature(m) || Node.isMethodSignature(m) || Node.isIndexSignatureDeclaration(m)) {
const t = m.getTypeNode()
if (t) visit(t)
}
// Also inspect method parameters/return types
if (Node.isMethodSignature(m)) {
for (const p of m.getParameters()) {
const pt = p.getTypeNode()
if (pt) visit(pt)
}
const rt = m.getReturnTypeNode()
if (rt) visit(rt)
}
}
const result = Array.from(references)
ASTTraversal.cache.set(cacheKey, result)
return result
}Additionally, consider de-duplicating reference arrays returned by traversal to avoid repeated work downstream. 🤖 Prompt for AI Agents |
||
| // Determine processing order: | ||
| // If interfaces depend on type aliases, process type aliases first | ||
| // If only type aliases depend on interfaces, process interfaces first | ||
| // If both have dependencies, process type aliases that interfaces depend on first, | ||
| // then interfaces, then type aliases that depend on interfaces | ||
| const processInterfacesFirst = | ||
| interfacesDependingOnTypeAliases.length === 0 && typeAliasesDependingOnInterfaces.length > 0 | ||
|
|
||
| return { | ||
| processInterfacesFirst, | ||
| typeAliasesDependingOnInterfaces, | ||
| interfacesDependingOnTypeAliases, | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Clear internal caches | ||
| */ | ||
| clearCache(): void { | ||
| ASTTraversal.clearCache() | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix class name:
DependencyAnalyzer(notInterfaceTypeDependencyAnalyzer) and clarify scenariosThe documented name doesn’t exist in the codebase and can mislead readers. Also tighten the bullet wording.
📝 Committable suggestion
🧰 Tools
🪛 LanguageTool
[grammar] ~119-~119: There might be a mistake here.
Context: ...l Processing**: Handles three scenarios: - Interfaces depending on type aliases onl...
(QB_NEW_EN)
[style] ~121-~121: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ... - Type aliases depending on interfaces only - Both dependencies present (three-p...
(ADVERB_REPETITION_PREMIUM)
🤖 Prompt for AI Agents