• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}