1import * as ts from "@koalaui/ets-tsc" 2import { sourceStructName } from "./ApiUtils" 3import { adaptorClassName, isDefined, prependMemoComment, throwError } from "./utils" 4import { PropertyTranslatorContext, classifyProperty } from "./PropertyTranslators" 5import { Importer } from "./Importer" 6import { ImportExport } from "./import-export"; 7 8export class OptionDescriptor { 9 constructor( 10 public name: ts.Identifier, 11 public type: ts.TypeNode, 12 public isBuilderParam: boolean = false 13 ) {} 14} 15 16export class StructOptions { 17 private importExport: ImportExport 18 constructor( 19 private context: PropertyTranslatorContext, 20 private typechecker: ts.TypeChecker 21 ) { 22 this.importExport = new ImportExport(this.typechecker) 23 } 24 25 private implProperties(node: ts.StructDeclaration): OptionDescriptor[] { 26 return node.members 27 .map(it => classifyProperty(it, this.context)) 28 .filter(isDefined) 29 .flatMap(it => it.implField()) 30 } 31 32 private structOptionsName(node: ts.StructDeclaration | ts.Identifier) { 33 return `${this.structName(node)}Options` 34 } 35 36 private structName(node: ts.StructDeclaration | ts.Identifier) { 37 if (ts.isStructDeclaration(node)) { 38 node = sourceStructName(node) 39 } 40 return ts.idText(node) 41 } 42 43 public createDeclaration(node: ts.StructDeclaration) { 44 return ts.factory.createInterfaceDeclaration( 45 [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], 46 this.structOptionsName(node), 47 [], 48 [], 49 this.implProperties(node) 50 .map(descriptor => { 51 const property = ts.factory.createPropertySignature( 52 undefined, 53 ts.idText(descriptor.name), 54 ts.factory.createToken(ts.SyntaxKind.QuestionToken), 55 descriptor.type 56 ) 57 if (descriptor.isBuilderParam) { 58 return prependMemoComment(property) 59 } else { 60 return property 61 } 62 }) 63 ) 64 } 65 66 public updatedInitializersValue(node: ts.StructDeclaration, instance: ts.Identifier) { 67 return ts.factory.createObjectLiteralExpression( 68 node.members 69 .map(it => classifyProperty(it, this.context)) 70 .filter(isDefined) 71 .flatMap(it => it.toInitialization(instance)), 72 true 73 ) 74 } 75 76 public createTypeReference(node: ts.StructDeclaration | ts.Identifier) { 77 if (ts.isStructDeclaration(node)) { 78 node = sourceStructName(node) 79 } 80 81 return ts.factory.createTypeReferenceNode( 82 this.structOptionsName(node) 83 ) 84 } 85 86 public addImport(node: ts.ImportDeclaration): ts.ImportDeclaration { 87 /* 88 TODO: only named imports supported for now 89 */ 90 if (node.importClause === undefined) return node 91 const namedImports = node.importClause.namedBindings 92 if (namedImports === undefined) return node 93 if (!ts.isNamedImports(namedImports)) return node 94 95 const structOptionsImports = namedImports.elements 96 .filter(it => { 97 const real = this.importExport.findRealDeclaration(it.name) 98 return real !== undefined && ts.isStructDeclaration(real) 99 }) 100 .map(it => 101 ts.factory.createImportSpecifier( 102 false, 103 undefined, 104 ts.factory.createIdentifier(this.structOptionsName(it.name)) 105 ) 106 ) 107 108 return ts.factory.updateImportDeclaration( 109 node, 110 node.modifiers, 111 ts.factory.updateImportClause( 112 node.importClause, 113 node.importClause.isTypeOnly, 114 node.importClause.name, 115 ts.factory.updateNamedImports( 116 namedImports, 117 namedImports.elements.concat(structOptionsImports) 118 ) 119 ), 120 node.moduleSpecifier, 121 node.assertClause 122 ) 123 } 124}