15
import { indentedBy, stringOrNone } from "../util"
16
import { IDLCallback, IDLConstructor, IDLEntry, IDLEnum, IDLInterface, IDLKind, IDLMethod, IDLModuleType, IDLParameter, IDLProperty, IDLType, IDLTypedef, getExtAttribute,
20
isClass, isConstructor, isContainerType, isEnum, isEnumType, isInterface, isMethod, isModuleType, isPrimitiveType, isProperty, isReferenceType, isTypeParameterType, isTypedef, isUnionType } from "../idl"
21
import * as webidl2 from "webidl2"
22
import { toIDLNode } from "./deserialize"
25
export class CustomPrintVisitor {
28
visit(node: IDLEntry) {
29
if (isInterface(node) || isClass(node)) {
31
} else if (isMethod(node) || isConstructor(node)) {
32
this.printMethod(node)
33
} else if (isProperty(node)) {
34
this.printProperty(node)
35
} else if (isTypedef(node)) {
36
this.printTypedef(node)
37
} else if (isEnum(node)) {
39
} else if (isCallback(node)) {
40
this.printCallback(node)
41
} else if (isModuleType(node)) {
42
this.printModuleType(node)
44
throw new Error(`Unexpected node kind: ${IDLKind[node.kind!]}`)
48
computeDeclaration(node: IDLInterface): string {
49
let keyword = hasExtAttribute(node, "Class") ? "class" : "interface"
50
let typeOrExtends = ""
51
if (hasExtAttribute(node, "Component")) {
52
if (node.name == "CommonMethod")
55
typeOrExtends = ` extends CommonMethod<${getExtAttribute(node, "Component")}Attribute>`
57
if (hasExtAttribute(node, "Parametrized")) {
58
typeOrExtends = `<${getExtAttribute(node, "Parametrized")}>`
60
return `${keyword} ${node.name}${typeOrExtends}`
63
currentInterface?: IDLInterface
65
printClass(node: IDLInterface) {
66
this.print(`declare ${this.computeDeclaration(node)} {`)
67
this.currentInterface = node
69
node.constructors.map(it => this.visit(it))
70
node.properties.map(it => this.visit(it))
71
node.methods.map(it => this.visit(it))
72
node.callables.map(it => this.visit(it))
73
let verbatim = getVerbatimDts(node)
77
.map(it => this.print(it))
82
if (hasExtAttribute(node, "Component")) {
83
let name = getExtAttribute(node, "Component")
84
this.print(`declare const ${name}Instance: ${name}Attribute;`)
85
this.print(`declare const ${name}: ${name}Interface;`)
89
printMethod(node: IDLMethod|IDLConstructor) {
90
let returnType = node.returnType ? `: ${printTypeForTS(node.returnType, true)}` : ""
91
let isStatic = isMethod(node) && node.isStatic
92
let name = isConstructor(node) ? "constructor" : node.name
93
if (hasExtAttribute(node, "CallSignature")) name = ""
94
this.print(`${isStatic ? "static " : ""}${name}(${node.parameters.map(p => this.paramText(p)).join(", ")})${returnType};`)
96
paramText(param: IDLParameter): string {
97
return `${param.name}${param.isOptional ? "?" : ""}: ${printTypeForTS(param.type)}`
99
printProperty(node: IDLProperty) {
100
const isCommonMethod = hasExtAttribute(node, "CommonMethod")
101
if (isCommonMethod) {
102
let returnType = this.currentInterface!.name == "CommonMethod" ? "T" : this.currentInterface!.name
103
this.print(`${node.name}(value: ${printTypeForTS(node.type)}): ${returnType};`)
105
this.print(`${node.isStatic ? "static " : ""}${node.isReadonly ? "readonly " : ""}${node.name}${node.isOptional ? "?" : ""}: ${printTypeForTS(node.type)};`)
109
printEnum(node: IDLEnum) {
110
this.print(`declare enum ${node.name} {`)
112
node.elements.forEach((it, index) => {
113
this.print(`${it.name}${it.initializer ? " = " + it.initializer : ""}${index < node.elements.length - 1 ? "," : ""}`)
118
printCallback(node: IDLCallback) {
120
this.print(`declare type ${(node.name)} = (${node.parameters.map(it => `${it.name}: ${printTypeForTS(it.type)}`).join(", ")}) => ${printTypeForTS(node.returnType)};`)
122
printTypedef(node: IDLTypedef) {
123
let text = getVerbatimDts(node) ?? printTypeForTS(node.type)
124
this.print(`declare type ${(node.name)} = ${text};`)
127
printModuleType(node: IDLModuleType) {
128
let text = getVerbatimDts(node) ?? ""
129
this.print(`${text}`)
132
checkVerbatim(node: IDLEntry) {
133
let verbatim = getExtAttribute(node, "VerbatimDts")
136
.substring(1, verbatim.length - 2)
138
.forEach(it => this.print(it))
143
indented(input: string): string {
144
return indentedBy(input, this.indent)
152
print(value: stringOrNone) {
153
if (value) this.output.push(this.indented(value))
157
export function idlToString(name: string, content: string): string {
158
let printer = new CustomPrintVisitor()
159
webidl2.parse(content)
160
.map(it => toIDLNode(name, it))
161
.map(it => printer.visit(it))
162
return printer.output.join("\n")
165
export function printTypeForTS(type: IDLType | undefined, undefinedToVoid?: boolean): string {
166
if (!type) throw new Error("Missing type")
167
if (type.name == "undefined" && undefinedToVoid) return "void"
168
if (type.name == "int32" || type.name == "float32") return "number"
169
if (type.name == "DOMString") return "string"
170
if (type.name == "this") return "T"
171
if (isPrimitiveType(type)) return type.name
172
if (isContainerType(type))
173
return `${mapContainerType(type.name)}<${type.elementType.map(it => printTypeForTS(it)).join("\n")}>`
174
if (isReferenceType(type)) return `${type.name}`
175
if (isUnionType(type)) return `(${type.types.map(it => printTypeForTS(it, undefinedToVoid)).join("|")})`
176
if (isEnumType(type)) return type.name
177
if (isTypeParameterType(type)) return type.name
178
throw new Error(`Cannot map type: ${IDLKind[type.kind]}`)
181
function mapContainerType(idlName: string): string {
183
case "sequence": return "Array"
184
case "record": return "Map"
185
case "Promise": return "Promise"
186
default: throw new Error(`Unmapped container type: ${idlName}`)