@ts-for-gir/lib
Version:
Typescript .d.ts generator from GIR for gjs
866 lines • 36 kB
JavaScript
import { Logger } from "../logger.js";
import { FormatGenerator } from "./generator.js";
import { IntrospectedBaseClass, IntrospectedRecord, IntrospectedInterface, IntrospectedClass } from "../gir/class.js";
import { IntrospectedConstant } from "../gir/const.js";
import { IntrospectedEnum, IntrospectedError } from "../gir/enum.js";
import { IntrospectedSignalType } from "../gir/signal.js";
import { IntrospectedFunction, IntrospectedConstructor, IntrospectedFunctionParameter, IntrospectedCallback } from "../gir/function.js";
import { IntrospectedClassFunction, IntrospectedStaticClassFunction, IntrospectedVirtualClassFunction } from "../gir/function.js";
import { sanitizeIdentifierName, isInvalid, resolveDirectedType } from "../gir/util.js";
import { NativeType, AnyType, VoidType, StringType, NumberType, ArrayType, TypeIdentifier, OrType, TupleType, NullableType, ClosureType, AnyFunctionType, TypeConflict } from "../gir.js";
import { GirDirection } from "@gi.ts/parser";
import { IntrospectedAlias } from "../gir/alias.js";
function generateType(type) {
if (type instanceof TypeIdentifier) {
return {
kind: "identifier" /* TypeKind.identifier */,
name: type.name,
namespace: type.namespace
};
}
else if (type instanceof NativeType) {
return {
kind: "native" /* TypeKind.native */,
type: type.expression()
};
}
else if (type instanceof ClosureType) {
return {
kind: "closure" /* TypeKind.closure */,
type: generateType(type.type),
user_data: type.user_data
};
}
else if (type instanceof ArrayType) {
return {
kind: "array" /* TypeKind.array */,
type: generateType(type.type),
depth: type.arrayDepth
};
}
else if (type instanceof NullableType) {
return {
kind: "null" /* TypeKind.nulled */,
type: generateType(type.type)
};
}
else if (type instanceof TypeConflict) {
// Type conflicts aren't considered in JSON outputs.
return generateType(type.type);
}
else if (type instanceof TupleType) {
return {
kind: "tuple" /* TypeKind.tuple */,
types: type.types.map(t => generateType(t))
};
}
else if (type instanceof OrType) {
return {
kind: "or" /* TypeKind.or */,
types: type.types.map(t => generateType(t))
};
}
else {
return {
kind: "native" /* TypeKind.native */,
type: "any"
};
}
}
function capitalize(str) {
if (str.length === 0) {
return "";
}
if (str.length === 1) {
return str[0].toUpperCase();
}
return str[0].toUpperCase() + str.substring(1).toLowerCase();
}
export class JsonGenerator extends FormatGenerator {
log;
constructor(namespace, options) {
super(namespace, options);
this.log = new Logger(options.verbose, JsonGenerator.name);
}
/**
* Intelligently reformats # and () references
* to handle c-prefixes and namespacing.
*
* @param doc
*/
generateDoc(doc) {
const { namespace } = this;
function resolveClass(ns, className) {
let classes = ns.getMembers(className);
let plural = false;
if (classes.length === 0 && className.endsWith("Class")) {
classes = ns.getMembers(className.slice(0, -5));
}
if (classes.length === 0 && className.endsWith("Iface")) {
classes = ns.getMembers(className.slice(0, -5));
}
if (classes.length === 0 && className.endsWith("Interface")) {
classes = ns.getMembers(className.slice(0, -9));
}
if (classes.length === 0 && className.endsWith("s")) {
plural = true;
classes = ns.getMembers(className.slice(0, -1));
}
return [classes[0] ?? null, plural];
}
function formatReference(identifier, member_name, punc) {
const parts = identifier
.split(/([A-Z])/)
.filter(p => p != "")
.reduce((prev, next) => {
if (next.toUpperCase() === next) {
prev.push(`${next}`);
}
else {
const lastCapital = prev.pop();
prev.push(`${lastCapital}${next}`);
}
return prev;
}, []);
const [base_part] = parts;
const [, , namespaces, className] = parts.slice(1).reduce(([underscore, camel, ns, selected], next) => {
const next_underscore = [underscore, next.toLowerCase()].join("_");
const namespaces = namespace.getImportsForCPrefix(next_underscore);
const nextCamel = camel + capitalize(next);
if (namespaces.length > 0) {
return [next_underscore, nextCamel, namespaces, capitalize(next)];
}
return [next_underscore, nextCamel, ns, selected + capitalize(next)];
}, [
base_part.toLowerCase(),
capitalize(base_part),
namespace.getImportsForCPrefix(base_part.toLowerCase()),
""
]);
let ns = namespaces.find(n => n.hasSymbol(className));
if (!ns) {
ns = namespaces.find(n => {
const [c] = resolveClass(n, className);
return c != null;
});
}
if (ns) {
const is_prop = punc === ":";
const modified_name = is_prop ? member_name.replace(/[\-]/g, "_") : member_name;
const [clazz, plural] = resolveClass(ns, className);
if (clazz instanceof IntrospectedBaseClass || clazz instanceof IntrospectedEnum) {
const r = `#${plural ? "{" : ""}${ns.namespace}.${clazz.name}${punc ? `${punc}${modified_name}` : ""}${plural ? "}s" : ""}`;
return r;
}
return `#${ns.namespace}${punc ? ` (${punc}${modified_name})` : ""}`;
}
else {
return null;
}
}
function formatFunctionReference(func, upper = false) {
// namespace_class_do_thing()
const parts = func.toLowerCase().split("_");
// ['namespace', 'class', 'do', 'thing']
const [base_part] = parts;
// ['namespace']
const namespaceBase = [
base_part.toLowerCase(),
capitalize(base_part),
namespace.getImportsForCPrefix(base_part.toLowerCase()),
0
];
// ['namespace', 'Namespace', { Namespace }, -1]
const [, , namespaces, i] = parts.slice(1).reduce(([prev, camel, currentNamespaces, selected], next, i) => {
const underscore = [prev, next.toLowerCase()].join("_");
const namespaces = namespace.getImportsForCPrefix(underscore);
const identifier = camel + capitalize(next);
// We've found namespace(s) which matches the c_prefix
if (namespaces.length > 0) {
return [underscore, identifier, namespaces, i];
}
return [underscore, identifier, currentNamespaces, selected];
}, namespaceBase);
// If no namespaces are found for the function's c_prefix, we return the original reference.
if (namespaces.length === 0) {
return null;
}
// ['class', 'do', 'thing']
const nameParts = parts.slice(i + 1);
// 'class_do_thing'
const functionName = nameParts.join("_");
const functionNamespace = namespaces.find(n => n.hasSymbol(functionName.toLowerCase()));
const constNamespace = namespaces.find(n => n.hasSymbol(functionName.toUpperCase()));
const enumNamespace = namespaces.find(n => n.enum_constants.has(func.toUpperCase()));
if (functionNamespace) {
const [member = null] = functionNamespace.getMembers(functionName.toLowerCase());
if (member instanceof IntrospectedFunction) {
return `${functionNamespace.namespace}.${member.name}`;
}
return null;
}
else if (constNamespace) {
const [member = null] = constNamespace.getMembers(functionName.toUpperCase());
if (member instanceof IntrospectedConstant) {
return `${constNamespace.namespace}.${member.name}`;
}
return null;
}
else if (enumNamespace) {
const constantInfo = enumNamespace.enum_constants.get(func.toUpperCase());
if (constantInfo) {
const [enumName, memberName] = constantInfo;
const [klass = null] = enumNamespace.getMembers(enumName);
if (klass instanceof IntrospectedEnum) {
return `${enumNamespace.namespace}.${klass.name}.${memberName.toUpperCase()}`;
}
}
return null;
}
else {
// ['class', 'do', 'thing']
const { selectedClassName, resolvedNamespace, selectedIndex } = parts.slice(i + 1).reduce(({ className, selectedClassName, resolvedNamespace, selectedIndex }, next, i) => {
// Class
const identifier = `${className}${capitalize(next)}`;
const withSymbol = namespaces.find(n => n.hasSymbol(identifier));
if (withSymbol) {
// { className: Class, resolvedNamespace: {Namespace}, selectedIndex: 0 }
return {
className: identifier,
selectedClassName: identifier,
resolvedNamespace: withSymbol,
selectedIndex: i
};
}
return { className: identifier, selectedClassName, resolvedNamespace, selectedIndex };
}, {
className: "",
selectedClassName: "",
resolvedNamespace: null,
selectedIndex: -1
});
if (resolvedNamespace && selectedIndex >= 0) {
const nextIndex = i + selectedIndex + 1 /* (slice omits first index) */ + 1; /* (the next index) */
const functionName = parts.slice(nextIndex).join("_");
const [klass] = resolveClass(resolvedNamespace, selectedClassName);
if (klass instanceof IntrospectedBaseClass || klass instanceof IntrospectedEnum) {
return `${resolvedNamespace.namespace}.${klass.name}.${upper ? functionName.toUpperCase() : functionName}`;
}
return `${resolvedNamespace.namespace}.${selectedClassName}.${upper ? functionName.toUpperCase() : functionName}`;
}
}
return null;
}
return `${doc}`
.replace(/[#]{0,1}([A-Z][A-z]+)\.([a-z_]+)\(\)/g, (original, identifier, member_name) => {
const resolved = formatReference(identifier, member_name, ".");
return resolved != null ? `${resolved}()` : original;
})
.replace(/#([A-Z][A-z]*)(([:]{1,2})([a-z\-]+)){0,1}/g, (original, identifier, _, punc, member_name) => {
const resolved = formatReference(identifier, member_name, punc);
return resolved != null ? resolved : original;
})
.replace(/([A-Z][A-z]*)(([:]{1,2})([a-z\-]+))/g, (original, identifier, _, punc, member_name) => {
const resolved = formatReference(identifier, member_name, punc);
return resolved != null ? resolved : original;
})
.replace(/(\s)([a-z_]+)\(\)/g, (original, w, func) => {
const resolved = formatFunctionReference(func);
return resolved != null ? `${w}${resolved}()` : original;
})
.replace(/%([A-Z_]+)/g, (original, identifier) => {
const resolved = formatFunctionReference(identifier.toLowerCase(), true);
return resolved != null ? `%${resolved}` : original;
})
.replace(/#([A-Z_]+)/g, (original, identifier) => {
const resolved = formatFunctionReference(identifier.toLowerCase(), true);
return resolved != null ? `#${resolved}` : original;
});
}
generateMetadata(metadata) {
return { ...metadata };
}
generateParameters(parameters) {
const { namespace, options } = this;
return parameters.map(p => ({
kind: "parameter" /* NodeKind.parameter */,
direction: p.direction,
optional: p.isOptional,
varargs: p.isVarArgs,
name: p.name,
resoleNames: p.resolve_names,
type: generateType(p.type.resolve(namespace, options)),
...this._generateDocAndMetadata(p)
}));
}
generateCallbackType(node) {
return [{}, {}];
}
generateCallback(node) {
const { namespace, options } = this;
const parameters = this.generateParameters(node.parameters);
return {
kind: "callback" /* NodeKind.callback */,
name: node.name,
type: this.generateCallbackType(node),
parameters,
returnType: generateType(node.return().resolve(namespace, options)),
...this._generateDocAndMetadata(node)
};
}
generateClassCallback(node) {
const { namespace, options } = this;
const parameters = this.generateParameters(node.parameters);
return {
kind: "callback" /* NodeKind.callback */,
name: node.name,
type: this.generateCallbackType(node),
parameters,
returnType: generateType(node.return().resolve(namespace, options)),
...this._generateDocAndMetadata(node)
};
}
generateReturn(return_type, output_parameters) {
const { namespace, options } = this;
const type = return_type.resolve(namespace, options);
if (output_parameters.length > 0) {
const exclude_first = type.equals(VoidType);
const returns = [
...(exclude_first ? [] : [type]),
...output_parameters.map(op => op.type.resolve(namespace, options))
];
return returns.map(r => generateType(r));
}
return generateType(type);
}
generateEnum(node) {
return {
kind: "enum" /* NodeKind.enum */,
name: node.name,
members: Array.from(node.members.values()).map(member => member.asString(this)),
...this._generateDocAndMetadata(node)
};
}
generateError(node) {
const { namespace } = this;
const clazz = node.asClass();
clazz.members = [];
clazz.members.push(...Array.from(node.functions.values()));
const GLib = namespace.assertInstalledImport("GLib");
const GLibError = GLib.assertClass("Error");
clazz.superType = GLibError.getType();
// Manually construct a GLib.Error constructor.
clazz.mainConstructor = new IntrospectedConstructor({
name: "new",
parent: clazz,
parameters: [
new IntrospectedFunctionParameter({
name: "options",
type: NativeType.of("{ message: string, code: number}"),
direction: GirDirection.In
})
],
return_type: clazz.getType()
});
return {
...clazz.asString(this),
kind: "error" /* NodeKind.error */
};
}
_generateDocAndMetadata(node) {
const { options } = this;
if (!options.noComments) {
return {
private: node.isPrivate,
doc: this.generateDoc(node.doc ?? "") ?? null,
metadata: this.generateMetadata(node.metadata ?? {}) ?? null
};
}
return {
private: false,
doc: null,
metadata: null
};
}
generateConst(node) {
const { namespace, options } = this;
return {
kind: "constant" /* NodeKind.constant */,
name: node.name,
type: generateType(node.type.resolve(namespace, options)),
...this._generateDocAndMetadata(node)
};
}
implements(node) {
const { namespace, options } = this;
if (node.interfaces.length > 0) {
return node.interfaces
.map(i => i.resolveIdentifier(namespace, options))
.filter((i) => i != null);
}
return [];
}
extends(node) {
const { namespace: ns, options } = this;
if (node.superType) {
return node.superType.resolveIdentifier(ns, options);
}
return null;
}
generateInterface(node) {
const { namespace } = this;
// If an interface does not list a prerequisite type, we fill it with GObject.Object
if (node.superType == null) {
const gobject = namespace.assertInstalledImport("GObject");
// TODO Optimize GObject.Object
if (!gobject) {
throw new Error("GObject not generated, all interfaces extend from GObject.Object!");
}
const GObject = gobject.getClass("Object");
if (!GObject) {
throw new Error(`GObject.Object could not be found while generating ${node.namespace.namespace}.${node.name}`);
}
node.superType = GObject.getType();
}
const { name } = node;
const Extends = this.extends(node);
const Properties = node.props.map(v => v && v.asString(this));
const Methods = node.members
.filter(m => !(m instanceof IntrospectedStaticClassFunction) && !(m instanceof IntrospectedVirtualClassFunction))
.map(v => v && v.asString(this));
const StaticMethods = node.members
.filter((m) => m instanceof IntrospectedStaticClassFunction)
.map(v => v && v.asString(this));
const VirtualMethods = node.members
.filter((m) => m instanceof IntrospectedVirtualClassFunction)
.map(v => v && v.asString(this));
return {
kind: "interface" /* NodeKind.interface */,
name,
type: generateType(node.getType()),
extends: Extends ? generateType(Extends) : null,
props: Properties,
methods: Methods,
staticMethods: StaticMethods,
virtualMethods: VirtualMethods,
...this._generateDocAndMetadata(node)
};
}
generateRecord(node) {
const { name } = node;
const Extends = this.extends(node);
const Properties = node.props.map(v => v && v.asString(this));
const Fields = node.fields.map(v => v && v.asString(this));
const Constructors = node.constructors.map(v => v && this.generateConstructorFunction(v));
const Methods = node.members
.filter(m => !(m instanceof IntrospectedStaticClassFunction) && !(m instanceof IntrospectedVirtualClassFunction))
.map(v => v && v.asString(this));
const StaticMethods = node.members
.filter((m) => m instanceof IntrospectedStaticClassFunction)
.map(v => v && v.asString(this));
const VirtualMethods = node.members
.filter((m) => m instanceof IntrospectedVirtualClassFunction)
.map(v => v && v.asString(this));
const Callbacks = node.callbacks.map(c => c && c.asString(this));
return {
kind: "record" /* NodeKind.record */,
name,
type: generateType(node.getType()),
mainConstructor: node.mainConstructor?.asString(this) ?? null,
extends: Extends ? generateType(Extends) : null,
implements: [],
props: Properties,
fields: Fields,
constructors: Constructors,
methods: Methods,
staticMethods: StaticMethods,
virtualMethods: VirtualMethods,
callbacks: Callbacks,
...this._generateDocAndMetadata(node)
};
}
generateClass(node) {
const Extends = this.extends(node);
const Implements = this.implements(node);
let MainConstructor = null;
const ConstructorProps = node.props.map(v => this.generateProperty(v, true));
if (node.mainConstructor) {
MainConstructor = this.generateConstructor(node.mainConstructor);
}
else {
MainConstructor = {
kind: "properties_constructor" /* NodeKind.propertiesConstructor */,
name: "new",
private: false,
properties: ConstructorProps.map(p => ({
kind: "parameter" /* NodeKind.parameter */,
private: p.private,
varargs: false,
name: p.name,
type: p.type,
doc: p.doc,
metadata: p.metadata,
optional: true
})),
doc: null,
metadata: null
};
}
const Properties = node.props.map(v => v.asString(this));
const Fields = node.fields.map(v => v.asString(this));
const Constructors = node.constructors.map(v => this.generateConstructorFunction(v));
const Methods = node.members
.filter(m => !(m instanceof IntrospectedStaticClassFunction) && !(m instanceof IntrospectedVirtualClassFunction))
.map(v => v && v.asString(this));
const StaticMethods = node.members
.filter((m) => m instanceof IntrospectedStaticClassFunction)
.map(v => v && v.asString(this));
const VirtualMethods = node.members
.filter((m) => m instanceof IntrospectedVirtualClassFunction)
.map(v => v && v.asString(this));
// TODO Move these to a cleaner place.
const Connect = new IntrospectedClassFunction({
name: "connect",
parent: node,
parameters: [
new IntrospectedFunctionParameter({
name: "id",
type: StringType,
direction: GirDirection.In
}),
new IntrospectedFunctionParameter({
name: "callback",
type: AnyFunctionType,
direction: GirDirection.In
})
],
return_type: NumberType
});
const ConnectAfter = new IntrospectedClassFunction({
name: "connect_after",
parent: node,
parameters: [
new IntrospectedFunctionParameter({
name: "id",
type: StringType,
direction: GirDirection.In
}),
new IntrospectedFunctionParameter({
name: "callback",
type: AnyFunctionType,
direction: GirDirection.In
})
],
return_type: NumberType
});
const Emit = new IntrospectedClassFunction({
name: "emit",
parent: node,
parameters: [
new IntrospectedFunctionParameter({
name: "id",
type: StringType,
direction: GirDirection.In
}),
new IntrospectedFunctionParameter({
name: "args",
isVarArgs: true,
type: new ArrayType(AnyType),
direction: GirDirection.In
})
],
return_type: VoidType
});
const default_signals = [];
let hasConnect, hasConnectAfter, hasEmit;
if (node.signals.length > 0) {
hasConnect = node.members.some(m => m.name === "connect");
hasConnectAfter = node.members.some(m => m.name === "connect_after");
hasEmit = node.members.some(m => m.name === "emit");
if (!hasConnect) {
default_signals.push(Connect);
}
if (!hasConnectAfter) {
default_signals.push(ConnectAfter);
}
if (!hasEmit) {
default_signals.push(Emit);
}
hasConnect = !default_signals.some(s => s.name === "connect");
hasConnectAfter = !default_signals.some(s => s.name === "connect_after");
hasEmit = !default_signals.some(s => s.name === "emit");
}
const SignalsList = [
...default_signals.map(s => s.asString(this)),
...node.signals
.map(s => {
const methods = [];
if (!hasConnect)
methods.push(s.asString(this, IntrospectedSignalType.CONNECT));
if (!hasConnectAfter)
methods.push(s.asString(this, IntrospectedSignalType.CONNECT_AFTER));
if (!hasEmit)
methods.push(s.asString(this, IntrospectedSignalType.EMIT));
return methods;
})
.flat()
];
const Signals = SignalsList;
return {
kind: "class" /* NodeKind.class */,
abstract: node.isAbstract,
type: generateType(node.getType()),
name: node.name,
mainConstructor: MainConstructor,
signals: Signals,
extends: Extends ? generateType(Extends) : null,
implements: Implements.map(i => generateType(i)),
props: Properties,
fields: Fields,
constructors: Constructors,
methods: Methods,
staticMethods: StaticMethods,
virtualMethods: VirtualMethods,
...this._generateDocAndMetadata(node)
};
}
generateField(node) {
const { namespace, options } = this;
const { name, computed } = node;
const invalid = isInvalid(name);
const Static = node.isStatic;
const ReadOnly = node.writable;
return {
kind: "field" /* NodeKind.field */,
readonly: ReadOnly,
static: Static,
computed,
type: generateType(node.type.resolve(namespace, options)),
name: invalid ? `"${name}"` : name,
...this._generateDocAndMetadata(node)
};
}
generateProperty(node, construct = false) {
const { namespace, options } = this;
const invalid = isInvalid(node.name);
const ReadOnly = node.writable || construct;
const Name = invalid ? `"${node.name}"` : node.name;
const Type = generateType(node.type.resolve(namespace, options));
return {
kind: "prop" /* NodeKind.prop */,
readonly: ReadOnly,
constructOnly: node.constructOnly,
readable: node.readable,
writable: node.writable,
static: false,
type: Type,
name: Name,
...this._generateDocAndMetadata(node)
};
}
generateSignal(node, type = IntrospectedSignalType.CONNECT) {
switch (type) {
case IntrospectedSignalType.CONNECT:
return node.asConnect(false).asString(this);
case IntrospectedSignalType.CONNECT_AFTER:
return node.asConnect(true).asString(this);
case IntrospectedSignalType.EMIT:
return node.asEmit().asString(this);
}
}
generateEnumMember(node) {
const invalid = isInvalid(node.name);
let enumMember;
if (node.value != null &&
!Number.isNaN(Number.parseInt(node.value, 10)) &&
Number.isNaN(Number.parseInt(node.name, 10)) &&
node.name !== "NaN") {
enumMember = { name: invalid ? `"${node.name}"` : `${node.name}`, value: `${node.value}` };
}
else {
enumMember = { name: invalid ? `"${node.name}"` : `${node.name}`, value: null };
}
return {
kind: "enum_member" /* NodeKind.enumMember */,
...enumMember,
...this._generateDocAndMetadata(node)
};
}
generateParameter(node) {
const { namespace, options } = this;
const type = generateType(resolveDirectedType(node.type, node.direction)?.resolve(namespace, options) ??
node.type.resolve(namespace, options));
return {
kind: "parameter" /* NodeKind.parameter */,
name: node.name,
type,
varargs: node.isVarArgs,
optional: node.isOptional,
...this._generateDocAndMetadata(node)
};
}
generateFunction(node) {
const { namespace } = this;
// Register our identifier with the sanitized identifiers.
// We avoid doing this in fromXML because other class-level function classes
// depends on that code.
sanitizeIdentifierName(namespace.namespace, node.raw_name);
const Parameters = this.generateParameters(node.parameters);
const ReturnType = this.generateReturn(node.return(), node.output_parameters);
return {
kind: "function" /* NodeKind.function */,
name: node.name,
parameters: Parameters,
returnType: ReturnType,
...this._generateDocAndMetadata(node)
};
}
generateConstructorFunction(node) {
const { namespace, options } = this;
const Parameters = this.generateParameters(node.parameters);
return {
kind: "class_function" /* NodeKind.classFunction */,
static: true,
name: node.name,
parameters: Parameters,
returnType: generateType(node.return().resolve(namespace, options)),
...this._generateDocAndMetadata(node)
};
}
generateConstructor(node) {
return {
name: node.name,
kind: "constructor" /* NodeKind.constructor */,
parameters: this.generateParameters(node.parameters),
...this._generateDocAndMetadata(node)
};
}
generateDirectAllocationConstructor(node) {
return {
name: node.name,
kind: "constructor" /* NodeKind.constructor */,
parameters: this.generateParameters(node.parameters),
...this._generateDocAndMetadata(node)
};
}
generateClassFunction(node) {
const parameters = node.parameters.map(p => this.generateParameter(p));
const output_parameters = node.output_parameters;
const return_type = node.return();
const ReturnType = this.generateReturn(return_type, output_parameters);
return {
kind: "class_function" /* NodeKind.classFunction */,
name: node.name,
parameters,
returnType: ReturnType,
...this._generateDocAndMetadata(node)
};
}
generateStaticClassFunction(node) {
const parameters = node.parameters.map(p => this.generateParameter(p));
const output_parameters = node.output_parameters;
const return_type = node.return();
const ReturnType = this.generateReturn(return_type, output_parameters);
return {
kind: "static_class_function" /* NodeKind.staticClassFunction */,
name: node.name,
parameters,
returnType: ReturnType,
...this._generateDocAndMetadata(node)
};
}
generateAlias(node) {
const { namespace, options } = this;
const type = node.type.resolve(namespace, options);
const { name } = node;
return {
kind: "alias" /* NodeKind.alias */,
name,
type: generateType(type.resolve(namespace, options)),
...this._generateDocAndMetadata(node)
};
}
generateVirtualClassFunction(node) {
return {
...this.generateClassFunction(node),
kind: "virtual_class_function" /* NodeKind.virtualClassFunction */
};
}
generateNamespace(node) {
function shouldGenerate(node) {
return node.emit;
}
const { namespace, version } = node.dependency;
const members = Array.from(node.members.values())
.flatMap(m => m)
.filter(shouldGenerate);
const classes = members
.filter((m) => m instanceof IntrospectedClass)
.map(m => m.asString(this));
const interfaces = members
.filter((m) => m instanceof IntrospectedInterface)
.map(m => m.asString(this));
const records = members
.filter((m) => m instanceof IntrospectedRecord)
.map(m => m.asString(this));
const constants = members
.filter((m) => m instanceof IntrospectedConstant)
.map(m => m.asString(this));
const callbacks = members
.filter((m) => m instanceof IntrospectedCallback)
.map(m => m.asString(this));
// Functions can have overrides.
const functions = [
...members
.filter((m) => !(m instanceof IntrospectedCallback) && m instanceof IntrospectedFunction)
.reduce((prev, next) => {
if (!prev.has(next.name))
prev.set(next.name, next.asString(this));
return prev;
}, new Map())
.values()
];
const errors = members
.filter((m) => m instanceof IntrospectedError)
.map(m => m.asString(this));
const enums = members
.filter((m) => !(m instanceof IntrospectedError) && m instanceof IntrospectedEnum)
.map(m => m.asString(this));
const alias = members
.filter((m) => m instanceof IntrospectedAlias)
.map(m => m.asString(this));
// Resolve imports after we stringify everything else, sometimes we have to ad-hoc add an import.
const imports = [];
return Promise.resolve({
kind: "namespace" /* NodeKind.namespace */,
name: namespace,
version,
imports: Object.fromEntries(imports),
classes,
interfaces,
records,
constants,
functions,
callbacks,
errors,
enums,
alias
});
}
async stringifyNamespace(node) {
const { namespace } = this;
this.log.debug(`Resolving the types of ${namespace.namespace}...`);
try {
const output = await this.generateNamespace(node);
this.log.debug(`Printing ${namespace.namespace}...`);
if (!output)
return null;
return JSON.stringify(output, null, 4);
}
catch (err) {
this.log.error(`Failed to generate namespace: "${node.namespace}"`, err);
return null;
}
}
}
//# sourceMappingURL=json.js.map