UNPKG

@ts-for-gir/lib

Version:

Typescript .d.ts generator from GIR for gjs

639 lines 20.9 kB
import { Logger } from './logger.js'; import { isInvalid, sanitizeIdentifierName, sanitizeNamespace } from './gir/util.js'; export { sanitizeMemberName, isInvalid } from './gir/util.js'; export { IntrospectedBase, IntrospectedNamespaceMember, IntrospectedClassMember, } from './gir/base.js'; export { filterConflicts, filterFunctionConflict, FilterBehavior } from './gir/class.js'; export { promisifyFunctions } from './gir/promisify.js'; export { resolveDirectedType, resolvePrimitiveType } from './gir/util.js'; export * from './gir/nodes.js'; export class TypeExpression { isPointer = false; deepUnwrap() { return this.unwrap(); } rootPrint(namespace, options) { return this.print(namespace, options); } } export class TypeIdentifier extends TypeExpression { name; namespace; log; constructor(name, namespace) { super(); this.name = name; this.namespace = namespace; this.log = new Logger(true, `TypeIdentifier(${this.namespace}.${name})`); } equals(type) { return type instanceof TypeIdentifier && type.name === this.name && type.namespace === this.namespace; } is(namespace, name) { return this.namespace === namespace && this.name === name; } unwrap() { return this; } rewrap(type) { return type; } /** * TODO: gi.ts didn't deal with sanitizing types but probably should have to avoid * invalid names such as "3gppProfile" */ sanitize() { return new TypeIdentifier(sanitizeIdentifierName(this.namespace, this.name), sanitizeNamespace(this.namespace)); } _resolve(namespace, options) { const name = sanitizeIdentifierName(null, this.name); const unresolvedNamespaceName = this.namespace; const ns = namespace.assertInstalledImport(unresolvedNamespaceName); if (ns.hasSymbol(name)) { const c = ns.getClass(name); // Some records are structs for other class types. // GirRecord.prototype.getType resolves this relationship. if (c) return c.getType(); return new TypeIdentifier(name, ns.namespace); } // Handle "class callback" types (they're in a definition-merged module) let [cb, corrected_name] = ns.findClassCallback(name); let resolved_name = null; if (!cb) { resolved_name = ns.resolveSymbolFromTypeName(name); } let c_resolved_name = null; if (!c_resolved_name) { c_resolved_name = ns.resolveSymbolFromTypeName(`${unresolvedNamespaceName}${name}`); } if (!c_resolved_name) { c_resolved_name = ns.resolveSymbolFromTypeName(`${ns.namespace}${name}`); } if (!cb && !resolved_name && !c_resolved_name) { // Don't warn if a missing import is at fault, this will be dealt with later. if (namespace.namespace === ns.namespace) { this.log.warn(`Attempting to fall back on c:type inference for ${ns.namespace}.${name}.`); } ; [cb, corrected_name] = ns.findClassCallback(`${ns.namespace}${name}`); if (cb) { this.log.warn(`Falling back on c:type inference for ${ns.namespace}.${name} and found ${ns.namespace}.${corrected_name}.`); } } if (cb) { if (options.verbose) { this.log.debug(`Callback found: ${cb}.${corrected_name}`); } return new ModuleTypeIdentifier(corrected_name, cb, ns.namespace); } else if (resolved_name) { return new TypeIdentifier(resolved_name, ns.namespace); } else if (c_resolved_name) { this.log.warn(`Fall back on c:type inference for ${ns.namespace}.${name} and found ${ns.namespace}.${corrected_name}.`); return new TypeIdentifier(c_resolved_name, ns.namespace); } else if (namespace.namespace === ns.namespace) { this.log.error(`Unable to resolve type ${this.name} in same namespace ${ns.namespace}!`); return null; } this.log.error(`Type ${this.name} could not be resolved in ${namespace.namespace} ${namespace.version}`); return null; } resolveIdentifier(namespace, options) { return this._resolve(namespace, options); } resolve(namespace, options) { const resolved = this._resolve(namespace, options); // Generally if we can't resolve a type it is not introspectable, // thus we annotate it as "never". return resolved ?? NeverType; } static new({ name, namespace }) { return new TypeIdentifier(name, namespace); } print(namespace, _options) { if (namespace.hasSymbol(this.namespace) && this.namespace !== namespace.namespace) { // TODO: Move to TypeScript generator... // Libraries like zbar have classes named things like "Gtk" return `${this.namespace}__.${this.name}`; } if (namespace.namespace === this.namespace) { return `${this.name}`; } else { return `${this.namespace}.${this.name}`; } } } export class ModuleTypeIdentifier extends TypeIdentifier { moduleName; namespace; constructor(name, moduleName, namespace) { super(name, namespace); this.moduleName = moduleName; this.namespace = namespace; } equals(type) { return super.equals(type) && type instanceof ModuleTypeIdentifier && this.moduleName === type.moduleName; } is(namespace, moduleName, name) { return (this.namespace === namespace && this.moduleName === moduleName && this.name === name && name !== undefined); } unwrap() { return this; } rewrap(type) { return type; } sanitize() { return new ModuleTypeIdentifier(sanitizeIdentifierName(this.namespace, this.name), sanitizeIdentifierName(this.namespace, this.moduleName), sanitizeNamespace(this.namespace)); } _resolve(namespace, options) { return this; } print(namespace, options) { if (namespace.namespace === this.namespace) { return `${this.moduleName}.${this.name}`; } else { return `${this.namespace}.${this.moduleName}.${this.name}`; } } } /** * This class overrides the default printing for types */ export class ClassStructTypeIdentifier extends TypeIdentifier { constructor(name, namespace) { super(name, namespace); } equals(type) { return type instanceof ClassStructTypeIdentifier && super.equals(type); } print(namespace, options) { if (namespace.namespace === this.namespace) { // TODO: Mapping to invalid names should happen at the generator level... return `typeof ${isInvalid(this.name) ? `__${this.name}` : this.name}`; } else { return `typeof ${this.namespace}.${isInvalid(this.name) ? `__${this.name}` : this.name}`; } } } export class GenerifiedTypeIdentifier extends TypeIdentifier { generics; constructor(name, namespace, generics = []) { super(name, namespace); this.generics = generics; } print(namespace, options) { const Generics = this.generics.map((generic) => generic.print(namespace, options)).join(', '); if (namespace.namespace === this.namespace) { return `${this.name}${this.generics.length > 0 ? `<${Generics}>` : ''}`; } else { return `${this.namespace}.${this.name}${this.generics.length > 0 ? `<${Generics}>` : ''}`; } } _resolve(namespace, options) { const iden = super._resolve(namespace, options); if (iden) { return new GenerifiedTypeIdentifier(iden.name, iden.namespace, [...this.generics]); } return iden; } } export class NativeType extends TypeExpression { expression; constructor(expression) { super(); this.expression = typeof expression === 'string' ? () => expression : expression; } rewrap(type) { return type; } resolve() { return this; } print(_namespace, options) { return this.expression(options); } equals(type, options) { return type instanceof NativeType && this.expression(options) === type.expression(options); } unwrap() { return this; } static withGenerator(generator) { return new NativeType(generator); } static of(nativeType) { return new NativeType(nativeType); } } export class OrType extends TypeExpression { types; constructor(type, ...types) { super(); this.types = [type, ...types]; } rewrap(type) { return type; } unwrap() { return this; } resolve(namespace, options) { const [type, ...types] = this.types; return new OrType(type.resolve(namespace, options), ...types.map((t) => t.resolve(namespace, options))); } print(namespace, options) { return `(${this.types.map((t) => t.print(namespace, options)).join(' | ')})`; } rootPrint(namespace, options) { return `${this.types.map((t) => t.print(namespace, options)).join(' | ')}`; } equals(type) { if (type instanceof OrType) { return this.types.every((t) => type.types.some((type) => type.equals(t))); } else { return false; } } } export class TupleType extends OrType { print(namespace, options) { return `[${this.types.map((t) => t.print(namespace, options)).join(', ')}]`; } rootPrint(namespace, options) { return this.print(namespace, options); } resolve(namespace, options) { const [type, ...types] = this.types; return new TupleType(type.resolve(namespace, options), ...types.map((t) => t.resolve(namespace, options))); } equals(type) { if (type instanceof TupleType) { return this.types.length === type.types.length && this.types.every((t, i) => type.types[i].equals(t)); } else { return false; } } } export class BinaryType extends OrType { constructor(primary, secondary) { super(primary, secondary); } unwrap() { return this; } resolve(namespace, options) { return new BinaryType(this.a.resolve(namespace, options), this.b.resolve(namespace, options)); } is() { return false; } get a() { return this.types[0]; } get b() { return this.types[1]; } } export class FunctionType extends TypeExpression { parameterTypes; returnType; constructor(parameters, returnType) { super(); this.parameterTypes = parameters; this.returnType = returnType; } equals(type) { if (type instanceof FunctionType) { return (Object.values(this.parameterTypes).every((t) => Object.values(type.parameterTypes).some((tp) => t.equals(tp))) && Object.values(type.parameterTypes).every((t) => Object.values(this.parameterTypes).some((tp) => t.equals(tp))) && this.returnType.equals(type.returnType)); } return false; } rewrap(type) { return type; } unwrap() { return this; } resolve(namespace, options) { return new FunctionType(Object.fromEntries(Object.entries(this.parameterTypes).map(([k, p]) => { return [k, p.resolve(namespace, options)]; })), this.returnType.resolve(namespace, options)); } rootPrint(namespace, options) { const Parameters = Object.entries(this.parameterTypes) .map(([k, v]) => { return `${k}: ${v.rootPrint(namespace, options)}`; }) .join(', '); return `(${Parameters}) => ${this.returnType.print(namespace, options)}`; } print(namespace, options) { return `(${this.rootPrint(namespace, options)})`; } } export class Generic { _deriveFrom; _genericType; _defaultType; _constraint; _propagate; constructor(genericType, defaultType, deriveFrom, constraint, propagate = true) { this._genericType = genericType; this._defaultType = defaultType ?? null; this._deriveFrom = deriveFrom ?? null; this._constraint = constraint ?? null; this._propagate = propagate; } unwrap() { return this.type; } get propagate() { return this._propagate; } get type() { return this._genericType; } get defaultType() { return this._defaultType; } get constraint() { return this._constraint; } get parent() { return this._deriveFrom; } } export class GenerifiedType extends TypeExpression { type; generic; constructor(type, generic) { super(); this.type = type; this.generic = generic; } resolve(namespace, options) { return new GenerifiedType(this.type.resolve(namespace, options), this.generic.resolve()); } unwrap() { return this.type; } rootPrint(namespace, options) { return `${this.type.print(namespace, options)}<${this.generic.print()}>`; } print(namespace, options) { return `${this.type.print(namespace, options)}<${this.generic.print()}>`; } equals(type) { if (type instanceof GenerifiedType) { return type.type.equals(this.type) && type.generic.equals(this.generic); } return false; } rewrap(type) { return new GenerifiedType(this.type.rewrap(type), this.generic); } } export class GenericType extends TypeExpression { identifier; replacedType; constructor(identifier, replacedType) { super(); this.identifier = identifier; this.replacedType = replacedType; } equals(type) { if (type instanceof GenericType) { return type.identifier === this.identifier; } return false; } unwrap() { return this; } rewrap(type) { return type; } resolve() { return this; } print() { return `${this.identifier}`; } } export class NullableType extends BinaryType { constructor(type) { super(type, NullType); } unwrap() { return this.type; } rewrap(type) { return new NullableType(this.type.rewrap(type)); } get type() { return this.a; } } export class PromiseType extends TypeExpression { type; constructor(type) { super(); this.type = type; } equals(type) { return type instanceof PromiseType && type.type.equals(this.type); } unwrap() { return this.type; } rewrap(type) { return new PromiseType(this.type.rewrap(type)); } resolve(namespace, options) { return new PromiseType(this.type.resolve(namespace, options)); } print(namespace, options) { // TODO: Optimize this check. if (!namespace.hasSymbol('Promise')) { return `Promise<${this.type.print(namespace, options)}>`; } return `globalThis.Promise<${this.type.print(namespace, options)}>`; } } /** * A list of possible type conflicts. * * The format is CHILD_PARENT_CONFLICT so * ACCESSOR_PROPERTY_CONFLICT means there * is an accessor on a child class and a * property on the parent class, which is a * conflict. * * Starts at '1' because the value is often * used as truthy. */ export var ConflictType; (function (ConflictType) { ConflictType[ConflictType["PROPERTY_NAME_CONFLICT"] = 1] = "PROPERTY_NAME_CONFLICT"; ConflictType[ConflictType["FIELD_NAME_CONFLICT"] = 2] = "FIELD_NAME_CONFLICT"; ConflictType[ConflictType["FUNCTION_NAME_CONFLICT"] = 3] = "FUNCTION_NAME_CONFLICT"; ConflictType[ConflictType["ACCESSOR_PROPERTY_CONFLICT"] = 4] = "ACCESSOR_PROPERTY_CONFLICT"; ConflictType[ConflictType["PROPERTY_ACCESSOR_CONFLICT"] = 5] = "PROPERTY_ACCESSOR_CONFLICT"; })(ConflictType || (ConflictType = {})); /** * This is one of the more interesting usages of our type * system. To handle type conflicts we wrap conflicting types * in this class with a ConflictType to denote why they are a * conflict. * * TypeConflict will throw if it is printed or resolved, so generators * must unwrap it and "resolve" the conflict. Some generators like JSON * just disregard this info, other generators like DTS attempt to * resolve the conflicts so the typing stays sound. */ export class TypeConflict extends TypeExpression { conflictType; type; constructor(type, conflictType) { super(); this.type = type; this.conflictType = conflictType; } rewrap(type) { return new TypeConflict(this.type.rewrap(type), this.conflictType); } unwrap() { return this.type; } // TODO: This constant "true" is a remnant of the Anyified type. equals() { return true; } resolve(namespace, options) { throw new Error(`Type conflict was not resolved for ${this.type.resolve(namespace, options).print(namespace, options)} in ${namespace.namespace}`); } print(namespace, options) { throw new Error(`Type conflict was not resolved for ${this.type.resolve(namespace, options).print(namespace, options)} in ${namespace.namespace}`); } } export class ClosureType extends TypeExpression { type; user_data = null; constructor(type) { super(); this.type = type; } equals(type) { if (type instanceof ClosureType) { const closureType = type; return this.type.equals(closureType.type); } return false; } deepUnwrap() { return this.type; } rewrap(type) { const closure = new ClosureType(this.type.rewrap(type)); closure.user_data = this.user_data; return closure; } unwrap() { return this; } resolve(namespace, options) { const { user_data, type } = this; return ClosureType.new({ user_data, type: type.resolve(namespace, options), }); } print(namespace, options) { return this.type.print(namespace, options); } static new({ type, user_data = null }) { const vt = new ClosureType(type); vt.user_data = user_data; return vt; } } export class ArrayType extends TypeExpression { type; arrayDepth = 1; length = null; constructor(type) { super(); this.type = type; } deepUnwrap() { return this.type; } unwrap() { return this; } rewrap(type) { const array = new ArrayType(this.type.rewrap(type)); array.arrayDepth = this.arrayDepth; array.length = this.length; return array; } equals(type) { if (type instanceof ArrayType) { const arrayType = type; return arrayType.type.equals(this.type) && type.arrayDepth === this.arrayDepth; } return false; } resolve(namespace, options) { const { type, arrayDepth, length } = this; return ArrayType.new({ type: type.resolve(namespace, options), arrayDepth, length, }); } print(namespace, options) { const depth = this.arrayDepth; let typeSuffix = ''; if (depth === 0) { typeSuffix = ''; } else if (depth === 1) { typeSuffix = '[]'; } else { typeSuffix = ''.padStart(2 * depth, '[]'); } return `${this.type.print(namespace, options)}${typeSuffix}`; } static new({ type, arrayDepth = 1, length = null, }) { const vt = new ArrayType(type); vt.length = length; vt.arrayDepth = arrayDepth; return vt; } } export const ThisType = new NativeType('this'); export const ObjectType = new NativeType('object'); export const AnyType = new NativeType('any'); export const NeverType = new NativeType('never'); export const Uint8ArrayType = new NativeType('Uint8Array'); export const BooleanType = new NativeType('boolean'); export const StringType = new NativeType('string'); export const NumberType = new NativeType('number'); export const NullType = new NativeType('null'); export const VoidType = new NativeType('void'); export const UnknownType = new NativeType('unknown'); export const AnyFunctionType = new NativeType('(...args: any[]) => any'); //# sourceMappingURL=gir.js.map