-
Notifications
You must be signed in to change notification settings - Fork 0
feat(graph): rework dependency traversal with graphology DAG #13
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 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import { | ||
| createSourceFileFromInput, | ||
| type InputOptions, | ||
| } from '@daxserver/validation-schema-codegen/input-handler' | ||
| import { TypeBoxPrinter } from '@daxserver/validation-schema-codegen/printer/typebox-printer' | ||
| import { DependencyTraversal } from '@daxserver/validation-schema-codegen/traverse/dependency-traversal' | ||
| import type { TraversedNode } from '@daxserver/validation-schema-codegen/traverse/types' | ||
| import { Project, SourceFile, ts } from 'ts-morph' | ||
|
|
||
| const createOutputFile = (hasGenericInterfaces: boolean) => { | ||
| const newSourceFile = new Project().createSourceFile('output.ts', '', { | ||
| overwrite: true, | ||
| }) | ||
|
|
||
| // Add imports | ||
| const namedImports = [ | ||
| 'Type', | ||
| { | ||
| name: 'Static', | ||
| isTypeOnly: true, | ||
| }, | ||
| ] | ||
|
|
||
| if (hasGenericInterfaces) { | ||
| namedImports.push({ | ||
| name: 'TSchema', | ||
| isTypeOnly: true, | ||
| }) | ||
| } | ||
|
|
||
| newSourceFile.addImportDeclaration({ | ||
| moduleSpecifier: '@sinclair/typebox', | ||
| namedImports, | ||
| }) | ||
|
|
||
| return newSourceFile | ||
| } | ||
|
|
||
| const printSortedNodes = ( | ||
| sortedTraversedNodes: TraversedNode[], | ||
| newSourceFile: SourceFile, | ||
| ) => { | ||
| const printer = new TypeBoxPrinter({ | ||
| newSourceFile, | ||
| printer: ts.createPrinter(), | ||
| }) | ||
|
|
||
| // Process nodes in topological order | ||
| for (const traversedNode of sortedTraversedNodes) { | ||
| printer.printNode(traversedNode) | ||
| } | ||
|
|
||
| return newSourceFile.getFullText() | ||
| } | ||
|
|
||
| export const generateCode = async ({ | ||
| sourceCode, | ||
| filePath, | ||
| ...options | ||
| }: InputOptions): Promise<string> => { | ||
| // Create source file from input | ||
| const sourceFile = createSourceFileFromInput({ | ||
| sourceCode, | ||
| filePath, | ||
| ...options, | ||
| }) | ||
|
|
||
| // Check if any interfaces have generic type parameters | ||
| const hasGenericInterfaces = sourceFile | ||
| .getInterfaces() | ||
| .some((i) => i.getTypeParameters().length > 0) | ||
|
|
||
| // Create output file with proper imports | ||
| const newSourceFile = createOutputFile(hasGenericInterfaces) | ||
|
|
||
| // Create dependency traversal and start traversal | ||
| const dependencyTraversal = new DependencyTraversal() | ||
| const traversedNodes = dependencyTraversal.startTraversal(sourceFile) | ||
|
|
||
| // Print sorted nodes to output | ||
| const result = printSortedNodes(traversedNodes, newSourceFile) | ||
|
|
||
| return result | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,17 +10,6 @@ import { | |
|
|
||
| export class InterfaceParser extends BaseParser { | ||
| parse(interfaceDecl: InterfaceDeclaration): void { | ||
| this.parseWithImportFlag(interfaceDecl, false) | ||
| } | ||
|
|
||
| parseWithImportFlag(interfaceDecl: InterfaceDeclaration, isImported: boolean): void { | ||
| this.parseInterfaceWithImportFlag(interfaceDecl, isImported) | ||
| } | ||
|
|
||
| private parseInterfaceWithImportFlag( | ||
| interfaceDecl: InterfaceDeclaration, | ||
| isImported: boolean, | ||
| ): void { | ||
| const interfaceName = interfaceDecl.getName() | ||
|
|
||
| if (this.processedTypes.has(interfaceName)) { | ||
|
|
@@ -30,17 +19,16 @@ export class InterfaceParser extends BaseParser { | |
| this.processedTypes.add(interfaceName) | ||
|
|
||
| const typeParameters = interfaceDecl.getTypeParameters() | ||
| const isExported = this.getIsExported(interfaceDecl, isImported) | ||
|
|
||
| // Check if interface has type parameters (generic) | ||
| if (typeParameters.length > 0) { | ||
| this.parseGenericInterface(interfaceDecl, isExported) | ||
| this.parseGenericInterface(interfaceDecl) | ||
| } else { | ||
| this.parseRegularInterface(interfaceDecl, isExported) | ||
| this.parseRegularInterface(interfaceDecl) | ||
| } | ||
| } | ||
|
|
||
| private parseRegularInterface(interfaceDecl: InterfaceDeclaration, isExported: boolean): void { | ||
| private parseRegularInterface(interfaceDecl: InterfaceDeclaration): void { | ||
| const interfaceName = interfaceDecl.getName() | ||
|
|
||
| // Generate TypeBox type definition | ||
|
|
@@ -52,7 +40,7 @@ export class InterfaceParser extends BaseParser { | |
| ) | ||
|
|
||
| this.newSourceFile.addVariableStatement({ | ||
| isExported, | ||
| isExported: true, | ||
| declarationKind: VariableDeclarationKind.Const, | ||
| declarations: [ | ||
| { | ||
|
|
@@ -67,11 +55,10 @@ export class InterfaceParser extends BaseParser { | |
| interfaceName, | ||
| this.newSourceFile.compilerNode, | ||
| this.printer, | ||
| isExported, | ||
| ) | ||
| } | ||
|
|
||
| private parseGenericInterface(interfaceDecl: InterfaceDeclaration, isExported: boolean): void { | ||
| private parseGenericInterface(interfaceDecl: InterfaceDeclaration): void { | ||
| const interfaceName = interfaceDecl.getName() | ||
| const typeParameters = interfaceDecl.getTypeParameters() | ||
|
|
||
|
|
@@ -85,7 +72,7 @@ export class InterfaceParser extends BaseParser { | |
|
|
||
| // Add the function declaration | ||
| this.newSourceFile.addVariableStatement({ | ||
| isExported, | ||
| isExported: true, | ||
| declarationKind: VariableDeclarationKind.Const, | ||
| declarations: [ | ||
| { | ||
|
|
@@ -96,13 +83,12 @@ export class InterfaceParser extends BaseParser { | |
| }) | ||
|
|
||
| // Add generic type alias: type A<T extends TSchema> = Static<ReturnType<typeof A<T>>> | ||
| this.addGenericTypeAlias(interfaceName, typeParameters, isExported) | ||
| this.addGenericTypeAlias(interfaceName, typeParameters) | ||
| } | ||
|
Comment on lines
85
to
87
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. 💡 Verification agent 🧩 Analysis chainPotentially invalid TypeScript: Creating a TypeQuery with type arguments ( Please run tsc to confirm. If invalid, switch to a safe pattern like:
I can produce a concrete patch once we confirm the preferred generic emission shape. Also applies to: 89-145 🏁 Script executed: #!/bin/bash
fd parse-interfaces.ts -t fLength of output: 61 🏁 Script executed: #!/bin/bash
sed -n '1,200p' src/parsers/parse-interfaces.tsLength of output: 4568 Replace invalid TypeScript type queries don’t support type arguments (queries accept only plain identifiers) (1library.net); emit a generic helper function (e.g. 🤖 Prompt for AI Agents |
||
|
|
||
| private addGenericTypeAlias( | ||
| name: string, | ||
| typeParameters: TypeParameterDeclaration[], | ||
| isExported: boolean, | ||
| ): void { | ||
| // Create type parameters for the type alias | ||
| const typeParamDeclarations = typeParameters.map((typeParam) => { | ||
|
|
@@ -152,12 +138,12 @@ export class InterfaceParser extends BaseParser { | |
| ) | ||
|
|
||
| this.newSourceFile.addTypeAlias({ | ||
| isExported, | ||
| isExported: true, | ||
| name, | ||
| typeParameters: typeParamDeclarations.map((tp) => | ||
| this.printer.printNode(ts.EmitHint.Unspecified, tp, this.newSourceFile.compilerNode), | ||
| ), | ||
| type: staticType, | ||
| ), | ||
| type: staticType, | ||
| }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,18 +6,12 @@ import { ts, TypeAliasDeclaration, VariableDeclarationKind } from 'ts-morph' | |
|
|
||
| export class TypeAliasParser extends BaseParser { | ||
| parse(typeAlias: TypeAliasDeclaration): void { | ||
| this.parseWithImportFlag(typeAlias, false) | ||
| this.parseWithImportFlag(typeAlias) | ||
| } | ||
|
|
||
| parseWithImportFlag(typeAlias: TypeAliasDeclaration, isImported: boolean): void { | ||
| parseWithImportFlag(typeAlias: TypeAliasDeclaration): void { | ||
| const typeName = typeAlias.getName() | ||
|
|
||
| if (this.processedTypes.has(typeName)) { | ||
| return | ||
| } | ||
|
|
||
| this.processedTypes.add(typeName) | ||
|
|
||
| const typeNode = typeAlias.getTypeNode() | ||
|
Comment on lines
+12
to
15
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. 💡 Verification agent 🧩 Analysis chainRemoved processedTypes dedup guard — verify no duplicate emissions. Run quick checks: 🏁 Script executed: #!/bin/bash
# Where else is processedTypes used?
rg -n "processedTypes" -C2
# Ensure TypeAliasParser no longer uses it
rg -n "class TypeAliasParser" -n -C5 src | sed -n '1,160p'Length of output: 4112 🏁 Script executed: #!/bin/bash
sed -n '1,200p' src/printer/typebox-printer.tsLength of output: 2165 🏁 Script executed: #!/bin/bash
sed -n '1,200p' src/parsers/parse-type-aliases.tsLength of output: 1379 Add processedTypes guard in TypeAliasParser if (this.processedTypes.has(typeName)) return;
this.processedTypes.add(typeName);🤖 Prompt for AI Agents |
||
| const typeboxTypeNode = typeNode ? getTypeBoxType(typeNode) : makeTypeCall('Any') | ||
| const typeboxType = this.printer.printNode( | ||
|
|
@@ -26,10 +20,8 @@ export class TypeAliasParser extends BaseParser { | |
| this.newSourceFile.compilerNode, | ||
| ) | ||
|
|
||
| const isExported = this.getIsExported(typeAlias, isImported) | ||
|
|
||
| this.newSourceFile.addVariableStatement({ | ||
| isExported, | ||
| isExported: true, | ||
| declarationKind: VariableDeclarationKind.Const, | ||
| declarations: [ | ||
| { | ||
|
|
@@ -44,7 +36,6 @@ export class TypeAliasParser extends BaseParser { | |
| typeName, | ||
| this.newSourceFile.compilerNode, | ||
| this.printer, | ||
| isExported, | ||
| ) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.