diff --git a/.ts-for-gir.gio.rc.js b/.ts-for-gir.gio.rc.js index c0141e040..bd10f825f 100644 --- a/.ts-for-gir.gio.rc.js +++ b/.ts-for-gir.gio.rc.js @@ -4,4 +4,7 @@ export default { girDirectories: ['./vala-girs/gir-1.0'], ignore: [], promisify: true, + verbose: true, + package: true, + outdir: './types', } diff --git a/packages/cli/src/commands/doc.ts b/packages/cli/src/commands/doc.ts index b62ee42a1..4a919d974 100644 --- a/packages/cli/src/commands/doc.ts +++ b/packages/cli/src/commands/doc.ts @@ -32,7 +32,7 @@ const handler = async (args: ConfigFlags) => { if (config.environments[i]) { const generateConfig = Config.getGenerateConfig(config, config.environments[i]) const moduleLoader = new ModuleLoader(generateConfig) - const { keep, grouped } = await moduleLoader.getModulesResolved( + const { keep } = await moduleLoader.getModulesResolved( config.modules, config.ignore || [], config.ignoreVersionConflicts, @@ -41,10 +41,11 @@ const handler = async (args: ConfigFlags) => { return Logger.error(ERROR_NO_MODULES_FOUND(config.girDirectories)) } const tsForGir = new GenerationHandler(generateConfig, GeneratorType.HTML_DOC) + const registry = moduleLoader.dependencyManager.registry await tsForGir.start( Array.from(keep).map((girModuleResolvedBy) => girModuleResolvedBy.module), - Object.values(grouped), + registry, ) } } diff --git a/packages/cli/src/commands/generate.ts b/packages/cli/src/commands/generate.ts index db10c4f77..6e9cea218 100644 --- a/packages/cli/src/commands/generate.ts +++ b/packages/cli/src/commands/generate.ts @@ -8,8 +8,10 @@ import { GeneratorType } from '@ts-for-gir/generator-base' import { GenerationHandler } from '../generation-handler.js' import { Config } from '../config.js' import { ModuleLoader } from '../module-loader.js' +import prettier from 'prettier' import type { ConfigFlags } from '@ts-for-gir/lib' +import { Formatter } from '@ts-for-gir/lib/lib/newlib/lib.js' const command = 'generate [modules..]' @@ -31,7 +33,7 @@ const handler = async (args: ConfigFlags) => { for (const env of config.environments) { const generateConfig = Config.getGenerateConfig(config, env) const moduleLoader = new ModuleLoader(generateConfig) - const { keep, grouped } = await moduleLoader.getModulesResolved( + const { keep } = await moduleLoader.getModulesResolved( config.modules, config.ignore || [], config.ignoreVersionConflicts, @@ -42,9 +44,10 @@ const handler = async (args: ConfigFlags) => { const tsForGir = new GenerationHandler(generateConfig, GeneratorType.TYPES) const girModules = Array.from(keep).map((girModuleResolvedBy) => girModuleResolvedBy.module) - const girModulesGrouped = Object.values(grouped) + // const girModulesGrouped = Object.values(grouped) - await tsForGir.start(girModules, girModulesGrouped) + moduleLoader.dependencyManager.registry.registerFormatter('dts', new TypeScriptFormatter()) + await tsForGir.start(girModules, moduleLoader.dependencyManager.registry) } } @@ -64,6 +67,22 @@ const examples: ReadonlyArray<[string, string?]> = [ ], ] +class TypeScriptFormatter extends Formatter { + format(input: string): Promise { + try { + return prettier.format(input, { + parser: 'typescript', + printWidth: 120, + tabWidth: 4, + }) + } catch (error) { + console.error('Failed to format output...') + console.error(input) + throw error + } + } +} + export const generate = { command, description, diff --git a/packages/cli/src/config.ts b/packages/cli/src/config.ts index b0a0f0e43..4f28ce1c4 100644 --- a/packages/cli/src/config.ts +++ b/packages/cli/src/config.ts @@ -331,6 +331,8 @@ export class Config { npmScope: config.npmScope, package: config.package, packageYarn: config.packageYarn, + noPrettyPrint: false, + noAdvancedVariants: true, } return generateConfig } diff --git a/packages/cli/src/generation-handler.ts b/packages/cli/src/generation-handler.ts index 5b7d70344..38f7d3360 100644 --- a/packages/cli/src/generation-handler.ts +++ b/packages/cli/src/generation-handler.ts @@ -10,13 +10,14 @@ import { } from '@ts-for-gir/lib' import { GeneratorType, Generator } from '@ts-for-gir/generator-base' import { TypeDefinitionGenerator } from '@ts-for-gir/generator-typescript' -import { HtmlDocGenerator } from '@ts-for-gir/generator-html-doc' +// import { HtmlDocGenerator } from '@ts-for-gir/generator-html-doc' -import type { InheritanceTable, GenerateConfig, GirModulesGrouped } from '@ts-for-gir/lib' +import type { InheritanceTable, GenerateConfig, NSRegistry } from '@ts-for-gir/lib' export class GenerationHandler { log: Logger generator: Generator + constructor( private readonly config: GenerateConfig, type: GeneratorType, @@ -27,9 +28,9 @@ export class GenerationHandler { case GeneratorType.TYPES: this.generator = new TypeDefinitionGenerator(config) break - case GeneratorType.HTML_DOC: - this.generator = new HtmlDocGenerator(config) - break + // case GeneratorType.HTML_DOC: + // this.generator = new HtmlDocGenerator(config) + // break default: throw new Error('Unknown Generator') } @@ -48,7 +49,7 @@ export class GenerationHandler { } } - public async start(girModules: GirModule[], girModulesGrouped: GirModulesGrouped[]): Promise { + public async start(girModules: GirModule[], registry: NSRegistry): Promise { this.log.info(START_MODULE(this.config.environment, this.config.buildType)) if (girModules.length == 0) { @@ -74,7 +75,13 @@ export class GenerationHandler { girModule.start(girModules) } - await this.generator.start(girModules, girModulesGrouped, inheritanceTable) + await this.generator.start(registry) + + for (const girModule of girModules) { + await this.generator.generate(registry, girModule) + } + + await this.generator.finish(registry) this.log.success(GENERATING_TYPES_DONE) } diff --git a/packages/cli/src/module-loader.ts b/packages/cli/src/module-loader.ts index 04cff26a5..88fdbda8a 100644 --- a/packages/cli/src/module-loader.ts +++ b/packages/cli/src/module-loader.ts @@ -386,7 +386,7 @@ export class ModuleLoader { this.log.log(`Parsing ${dependency.path}...`) const fileContents = await readFile(dependency.path, 'utf8') const result = parser.parseGir(fileContents) - const girModule = new GirModule(result, this.config) + const girModule = GirModule.load(result, this.config, this.dependencyManager.registry) // Figure out transitive module dependencies this.extendDependencyMapByGirModule(girModule) return girModule diff --git a/packages/generator-base/src/generator.ts b/packages/generator-base/src/generator.ts index 26157392e..ce13cb8f6 100644 --- a/packages/generator-base/src/generator.ts +++ b/packages/generator-base/src/generator.ts @@ -1,9 +1,7 @@ -import type { InheritanceTable, GirModulesGrouped, GirModule } from '@ts-for-gir/lib' +import type { GirModule, NSRegistry } from '@ts-for-gir/lib' export interface Generator { - start( - girModules: GirModule[], - girModulesGrouped: GirModulesGrouped[], - inheritanceTable: InheritanceTable, - ): Promise + start(registry: NSRegistry): Promise + generate(registry: NSRegistry, module: GirModule): Promise + finish(registry: NSRegistry): Promise } diff --git a/packages/generator-typescript/src/type-definition-generator.ts b/packages/generator-typescript/src/type-definition-generator.ts index 44b621dd2..1749eba0d 100644 --- a/packages/generator-typescript/src/type-definition-generator.ts +++ b/packages/generator-typescript/src/type-definition-generator.ts @@ -4,98 +4,239 @@ import { generateIndent, removeNamespace, removeClassModule, - girElementIsIntrospectable, - typesContainsOptional, - typesContainsNullable, Dependency, DependencyManager, NO_TSDATA, WARN_NOT_FOUND_DEPENDENCY_GIR_FILE, - WARN_IGNORE_MULTIPLE_CALLBACKS, - WARN_IGNORE_MULTIPLE_FUNC_DESC, + //WARN_IGNORE_MULTIPLE_CALLBACKS, + //WARN_IGNORE_MULTIPLE_FUNC_DESC, PackageData, + TypeExpression, + NSRegistry, + IntrospectedClass, + IntrospectedRecord, + IntrospectedInterface, + IntrospectedBaseClass, + IntrospectedField, + GirDirection, + TsDoc, } from '@ts-for-gir/lib' import { TemplateProcessor } from './template-processor.js' import { PackageDataParser } from './package-data-parser.js' - -import type { +import { writeFile, mkdir } from 'fs/promises' +import { dirname } from 'path' +import { GenerateConfig, - GirClassElement, - GirCallableParamElement, - GirSignalElement, - GirEnumElement, - GirAliasElement, - GirInterfaceElement, - GirUnionElement, - GirModulesGrouped, - GirRecordElement, - GirBitfieldElement, - GirInstanceParameter, GirModule, - TsGenericParameter, - TsType, - TsDoc, - TsFunction, - TsCallback, - TsSignal, - TsMember, - TsVar, - TsProperty, - TsParameter, + IntrospectedFunction, + IntrospectedCallback, + IntrospectedSignal, + IntrospectedProperty, + IntrospectedConstant, } from '@ts-for-gir/lib' +import { + IntrospectedClassCallback, + IntrospectedClassFunction, + IntrospectedConstructor, + IntrospectedDirectAllocationConstructor, + IntrospectedFunctionParameter, + IntrospectedStaticClassFunction, + IntrospectedVirtualClassFunction, +} from '@ts-for-gir/lib/lib/newlib/gir/function.js' +import { IntrospectedNamespaceMember } from '@ts-for-gir/lib/lib/newlib/gir/base.js' +import { + FormatGenerator, + Generic, + GirEnumMember, + IntrospectedAlias, + IntrospectedEnum, + IntrospectedSignalType, + NativeType, +} from '@ts-for-gir/lib/lib/newlib/lib.js' +import { IntrospectedError } from '@ts-for-gir/lib/lib/newlib/gir/enum.js' +import { isInvalid } from '@ts-for-gir/lib/lib/newlib/gir/util.js' + +function printGirDocComment(tsDoc: TsDoc, config: GenerateConfig, indentCount = 0) { + const desc: string[] = [] + const indent = generateIndent(indentCount) + if (config.noComments) { + return desc.join('\n') + } -export class TypeDefinitionGenerator implements Generator { - protected log: Logger - protected dependencyManager: DependencyManager - protected packageData?: PackageDataParser + const text = tsDoc.text + + if (text) { + desc.push(`${indent}/**`) + + if (text) { + const lines = text.split('\n') + if (lines.length) { + for (const line of lines) { + desc.push(`${indent} * ${line}`) + } + } + } + + for (const tag of tsDoc.tags) { + if (tag.paramName) { + desc.push(`${indent} * @${tag.tagName} ${tag.paramName} ${tag.text}`) + } else { + desc.push(`${indent} * @${tag.tagName} ${tag.text}`) + } + } + + desc.push(`${indent} */`) + } + return desc.join('\n') +} + +class ModuleGenerator extends FormatGenerator { + log: Logger + dependencyManager: DependencyManager + packageData?: PackageDataParser /** Override config, used to override the config temporarily to generate both ESM and CJS for NPM packages */ - protected overrideConfig: Partial = {} + overrideConfig: Partial = {} + + _config: GenerateConfig + moduleTemplateProcessor: TemplateProcessor /** Get the current config, including the override config */ - protected get config(): GenerateConfig { + get config(): GenerateConfig { return { ...this._config, ...this.overrideConfig } } /** * @param _config The config to use without the override config */ - constructor(protected readonly _config: GenerateConfig) { + constructor(namespace: GirModule, config: GenerateConfig) { + super(namespace, config) + + this._config = config + this.log = new Logger(this.config.environment, this.config.verbose, TypeDefinitionGenerator.name) this.dependencyManager = DependencyManager.getInstance(this.config) if (this.config.package) { this.packageData = new PackageDataParser(this.config) } + const girModule = namespace + let pkgData: PackageData | undefined + if (this.packageData) { + pkgData = this.packageData.get(girModule.packageName) + } + this.moduleTemplateProcessor = new TemplateProcessor( + { + name: girModule.namespace, + namespace: girModule.namespace, + version: girModule.version, + importName: girModule.importName, + girModule, + pkgData, + registry: this.dependencyManager.registry, + }, + girModule.packageName, + girModule.transitiveDependencies, + this.config, + ) } - /** - * - * @param namespace E.g. 'Gtk' - * @param packageName E.g. 'Gtk-3.0' - * @param asExternType Currently only used for node type imports - */ - protected generateModuleDependenciesImport(packageName: string): string[] { - const def: string[] = [] - const dep = this.dependencyManager.get(packageName) + generateClassCallback(node: IntrospectedClassCallback): string[] { + return this.generateCallback(node) + } + generateConstructor(node: IntrospectedConstructor): string[] { + const Parameters = this.generateParameters(node.parameters) - // if (this.config.package) { - // if (this.config.buildType === 'types') { - // // See https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html - // def.push(`/// `) - // } - // } else { - // if (this.config.buildType === 'types') { - // // See https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html - // def.push(`/// `) - // } - // } + return [`constructor(${Parameters});`] + } + generateDirectAllocationConstructor(node: IntrospectedDirectAllocationConstructor): string[] { + const ConstructorFields = node.parameters.map((param) => param.asField().asString(this)).join('\n') + + return [ + ` + constructor(properties?: Partial<{ + ${ConstructorFields} + }>);`, + ] + } + protected generateParameters(parameters: IntrospectedFunctionParameter[]): string { + return parameters + .flatMap((p) => { + return p.asString(this) + }) + .join(', ') + } - def.push(dep.importDef) + generateConstructorFunction(node: IntrospectedConstructor): string[] { + const { namespace, options } = this + + const Parameters = this.generateParameters(node.parameters) + + const invalid = isInvalid(node.name) + const name = invalid ? `["${node.name}"]` : node.name + const warning = node.getWarning() + return [ + `${warning ? `${warning}\n` : ''}`, + ...this.addGirDocComment(node.doc), + `static ${name}(${Parameters}): ${node + .return() + .resolve(namespace, options) + .rootPrint(namespace, options)};`, + ] + } + generateRecord(node: IntrospectedRecord): string[] { + return this.generateClass(node) + } + generateInterface(node: IntrospectedInterface): string[] { + return this.generateImplementationInterface(node) + } + generateError(node: IntrospectedError): string[] { + 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) + } - return def + generateSignal(node: IntrospectedSignal, type: IntrospectedSignalType = IntrospectedSignalType.CONNECT): string[] { + 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) + } } - protected generateExport(type: string, name: string, definition: string, indentCount = 0) { + generateStaticClassFunction(node: IntrospectedStaticClassFunction): string[] { + return this.generateClassFunction(node) + } + generateVirtualClassFunction(node: IntrospectedVirtualClassFunction): string[] { + return this.generateClassFunction(node) + } + + generateExport(type: string, name: string, definition: string, indentCount = 0) { const exp = !this.config.noNamespace ? '' : 'export ' const indent = generateIndent(indentCount) if (!definition.startsWith(':')) { @@ -104,44 +245,62 @@ export class TypeDefinitionGenerator implements Generator { return `${indent}${exp}${type} ${name}${definition}` } - protected generateProperty(tsProp: TsProperty, onlyStatic: boolean, namespace: string, indentCount = 0) { + generateProperty(tsProp: IntrospectedProperty, construct?: boolean, indentCount = 0) { + // if (!tsProp) { + // throw new Error('[generateProperty] Not all required properties set!') + // } + + const desc: string[] = [] + const isStatic = false //tsProp.isStatic + + // if ((isStatic && !onlyStatic) || (!isStatic && onlyStatic)) { + // return desc + // } + + desc.push(...this.addGirDocComment(tsProp.doc, indentCount)) + + const indent = generateIndent(indentCount) + const varDesc = this.generateVariable(tsProp) + const staticStr = isStatic ? 'static ' : '' + const readonly = !tsProp.writable ? 'readonly ' : '' + + // temporary solution, will be solved differently later + const commentOut = '' + + desc.push(`${indent}${commentOut}${staticStr}${readonly}${varDesc}`) + return desc + } + + generateField(tsProp: IntrospectedField, indentCount = 0) { if (!tsProp) { throw new Error('[generateProperty] Not all required properties set!') } const desc: string[] = [] - const isStatic = tsProp.isStatic + const isStatic = false //tsProp.isStatic - if ((isStatic && !onlyStatic) || (!isStatic && onlyStatic)) { - return desc - } + // if ((isStatic && !onlyStatic) || (!isStatic && onlyStatic)) { + // return desc + // } - if (!tsProp.hasUnresolvedConflict) { - desc.push(...this.addGirDocComment(tsProp.doc, indentCount)) - } + desc.push(...this.addGirDocComment(tsProp.doc, indentCount)) const indent = generateIndent(indentCount) - const varDesc = this.generateVariable(tsProp, namespace, 0, false) + const varDesc = this.generateVariable(tsProp, 0) const staticStr = isStatic ? 'static ' : '' - const readonly = tsProp.readonly ? 'readonly ' : '' + const readonly = !tsProp.writable ? 'readonly ' : '' // temporary solution, will be solved differently later - const commentOut = tsProp.hasUnresolvedConflict ? '// Has conflict: ' : '' + const commentOut = '' desc.push(`${indent}${commentOut}${staticStr}${readonly}${varDesc}`) return desc } - protected generateProperties( - tsProps: TsProperty[], - onlyStatic: boolean, - namespace: string, - comment: string, - indentCount = 0, - ) { + generateProperties(tsProps: IntrospectedProperty[], comment: string, indentCount = 0) { const def: string[] = [] for (const tsProp of tsProps) { - def.push(...this.generateProperty(tsProp, onlyStatic, namespace, indentCount)) + def.push(...this.generateProperty(tsProp, false, indentCount)) } if (def.length > 0) { @@ -151,201 +310,160 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateVariableCallbackType(tsType: TsType, namespace: string) { - // The type of a callback is a functions definition + generateFields(tsProps: IntrospectedField[], comment: string, indentCount = 0) { + const def: string[] = [] + for (const tsProp of tsProps) { + def.push(...this.generateField(tsProp)) + } + + if (def.length > 0) { + def.unshift(...this.addInfoComment(comment, indentCount)) + } - let typeStr = 'any' + return def + } - const { callbacks } = tsType + // generateVariableCallbackType(tsType: TypeExpression, namespace: string) { + // // The type of a callback is a functions definition - if (!callbacks.length) return typeStr + // let typeStr = 'any' - if (callbacks.length > 1) { - this.log.warn(WARN_IGNORE_MULTIPLE_CALLBACKS) - } + // const { callbacks } = tsType - const girCallback = callbacks[0] + // if (!callbacks.length) return typeStr - if (!girCallback._tsData) { - throw new Error(NO_TSDATA('generateVariableCallbackType')) - } + // if (callbacks.length > 1) { + // this.log.warn(WARN_IGNORE_MULTIPLE_CALLBACKS) + // } - const funcDesc = this.generateFunction(girCallback._tsData, false, namespace, 0) + // const girCallback = callbacks[0] - if (girCallback._tsData && funcDesc?.length) { - if (funcDesc.length > 1) { - this.log.warn(WARN_IGNORE_MULTIPLE_FUNC_DESC) - } - typeStr = funcDesc[0] - } + // if (!girCallback) { + // throw new Error(NO_TSDATA('generateVariableCallbackType')) + // } - return typeStr - } + // const funcDesc = this.generateFunction(girCallback, false, namespace, 0) - protected generateVariable(tsVar: TsProperty | TsVar, namespace: string, indentCount = 0, allowCommentOut = true) { + // if (girCallback && funcDesc?.length) { + // if (funcDesc.length > 1) { + // this.log.warn(WARN_IGNORE_MULTIPLE_FUNC_DESC) + // } + // typeStr = funcDesc[0] + // } + + // return typeStr + // } + + generateVariable(tsVar: IntrospectedProperty | IntrospectedConstant | IntrospectedField, indentCount = 0) { const indent = generateIndent(indentCount) const name = tsVar.name // Constants are not optional - const optional = tsVar.tsTypeName !== 'constant' && typesContainsOptional(tsVar.type) - - if (!name) { + const optional = false + const invalid = isInvalid(name) + const Name = + invalid && (tsVar instanceof IntrospectedProperty || tsVar instanceof IntrospectedField) + ? `"${name}"` + : name + + if (!Name) { throw new Error('[generateVariable] "name" not set!') } const affix = optional ? '?' : '' - const typeStr = this.generateTypes(tsVar.type, namespace) + const typeStr = this.generateTypes(tsVar.type) // temporary solution, will be solved differently later - const commentOut = allowCommentOut && tsVar.hasUnresolvedConflict ? '// Has conflict: ' : '' - - return `${indent}${commentOut}${name}${affix}: ${typeStr}` - } - - protected generateType(tsType: TsType, namespace: string, generateNullable = true) { - let typeName = removeNamespace(tsType.type, namespace) - - if (tsType.callbacks.length) { - typeName = this.generateVariableCallbackType(tsType, namespace) - } - - if (!typeName) { - throw new Error('[generateVariable] "typeName" not set!') - } - - let prefix = tsType.isArray ? '[]' : '' - if (generateNullable && tsType.nullable) { - prefix += ' | null' - } - - // We just use the generic values here - const genericStr = this.generateGenericValues(tsType, namespace) - - return `${typeName}${genericStr}${prefix}` - } + // TODO: const commentOut = allowCommentOut && tsVar.hasUnresolvedConflict ? '// Has conflict: ' : '' + const commentOut = '' - protected generateTypes(tsTypes: TsType[], namespace: string) { - let def = '' - for (const tsType of tsTypes) { - const separator = tsType.leftSeparator || '|' - const typeStr = this.generateType(tsType, namespace, false) - if (!def) { - def = typeStr - } else { - def += ` ${separator} ${typeStr}` - } - } - const hasNullable = typesContainsNullable(tsTypes) - if (hasNullable) { - if (tsTypes.length > 1) { - def = `(${def}) | null` - } else { - def += ' | null' - } - } - return def + return `${indent}${commentOut}${Name}${affix}: ${typeStr}` } - protected generateGenericValues(tsType: TsType, namespace: string) { - // We just use the generic values here - const genericValues = tsType.generics.map((g) => removeNamespace(g.value || '', namespace)).filter((g) => !!g) - const genericStr = tsType.generics.length ? `<${genericValues.join(', ')}>` : '' - return genericStr + generateType(tsType: TypeExpression) { + return tsType.print(this.namespace, this._config) } - /** - * Generates signals from all properties of a base class - * TODO: Build new `GirSignalElement`s in `gir-module.ts` instead of generate the strings directly - * @param girClass - * @param namespace - * @param indentCount - * @returns - */ - protected generateClassPropertySignals( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, - indentCount = 1, - ) { - const def: string[] = [] - - if (!girClass._tsData || !girClass._fullSymName || !girClass._module) { - throw new Error(NO_TSDATA('generateClassPropertySignals')) - } - - if (girClass._tsData?.isDerivedFromGObject) { - if (girClass._tsData.propertySignalMethods.length > 0) { - def.push( - ...this.addInfoComment( - `Class property signals of ${girClass._module?.packageName}.${girClass._fullSymName}`, - indentCount, - ), - ) - for (const tsSignalMethod of girClass._tsData.propertySignalMethods) { - if (!tsSignalMethod) { - continue - } - def.push(...this.generateFunction(tsSignalMethod, false, namespace, indentCount)) - } - } - } - return def + generateTypes(tsTypes: TypeExpression) { + return tsTypes.print(this.namespace, this._config) } - protected generateInParameters( - inParams: GirCallableParamElement[], - instanceParameters: GirInstanceParameter[], - namespace: string, - ) { + // TODO: + // generateGenericValues(tsType: TypeExpression, namespace: string) { + // // We just use the generic values here + // const genericValues = tsType.generics.map((g) => removeNamespace(g.value || '', namespace)).filter((g) => !!g) + // const genericStr = tsType.generics.length ? `<${genericValues.join(', ')}>` : '' + // return genericStr + // } + + // /** + // * Generates signals from all properties of a base class + // * TODO: Build new `GirSignalElement`s in `gir-module.ts` instead of generate the strings directly + // * @param girClass + // * @param namespace + // * @param indentCount + // * @returns + // */ + // generateClassPropertySignals( + // girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, + // namespace: string, + // indentCount = 1, + // ) { + // const def: string[] = [] + + // const isDerivedFromGObject = girClass.someParent( + // (p: IntrospectedBaseClass) => p.namespace.name === 'GObject' && p.name === 'Object', + // ) + + // if (isDerivedFromGObject) { + // if (girClass.propertySignalMethods.length > 0) { + // def.push( + // ...this.addInfoComment( + // `Class property signals of ${girClass._module?.packageName}.${girClass.name}`, + // indentCount, + // ), + // ) + // for (const tsSignalMethod of girClass.propertySignalMethods) { + // if (!tsSignalMethod) { + // continue + // } + // def.push(...this.generateFunction(tsSignalMethod, false, namespace, indentCount)) + // } + // } + // } + // return def + // } + + generateInParameters(inParams: IntrospectedFunctionParameter[]) { const inParamsDef: string[] = [] // TODO: Should use of a constructor, and even of an instance, be discouraged? - for (const instanceParameter of instanceParameters) { - if (instanceParameter._tsData) { - let { structFor } = instanceParameter._tsData - const { name } = instanceParameter._tsData - const gobject = namespace === 'GObject' || namespace === 'GLib' ? '' : 'GObject.' + // for (const instanceParameter of instanceParameters) { + // if (instanceParameter) { + // let { structFor } = instanceParameter + // const { name } = instanceParameter + // const gobject = namespace === 'GObject' || namespace === 'GLib' ? '' : 'GObject.' - structFor = removeNamespace(structFor, namespace) + // structFor = removeNamespace(structFor, namespace) - const returnTypes = [structFor, 'Function', `${gobject}GType`] - inParamsDef.push(`${name}: ${returnTypes.join(' | ')}`) - } - } + // const returnTypes = [structFor, 'Function', `${gobject}GType`] + // inParamsDef.push(`${name}: ${returnTypes.join(' | ')}`) + // } + // } for (const inParam of inParams) { - if (inParam._tsData) inParamsDef.push(...this.generateParameter(inParam._tsData, namespace)) + inParamsDef.push(...this.generateParameter(inParam)) } return inParamsDef } - protected generateSignals( - girSignals: GirSignalElement[], - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, - indentCount = 0, - ) { - const def: string[] = [] - - for (const girSignal of girSignals) { - if (girSignal._tsData?.tsMethods?.length) { - for (const tsSignalMethod of girSignal._tsData?.tsMethods) { - if (!tsSignalMethod) { - continue - } - def.push(...this.generateFunction(tsSignalMethod, false, namespace, indentCount)) - } - } - } - return def - } - /** * Adds documentation comments * @see https://github.com/microsoft/tsdoc * @param lines * @param indentCount */ - protected addTSDocCommentLines(lines: string[], indentCount = 0): string[] { + addTSDocCommentLines(lines: string[], indentCount = 0): string[] { const def: string[] = [] const indent = generateIndent(indentCount) def.push(`${indent}/**`) @@ -364,15 +482,14 @@ export class TypeDefinitionGenerator implements Generator { * @param overwriteDoc * @returns */ - protected addGirDocComment(tsDoc: TsDoc | undefined, indentCount = 0, overwriteDoc?: Partial) { + addGirDocComment(tsDoc: string | null | undefined, indentCount = 0) { const desc: string[] = [] const indent = generateIndent(indentCount) if (this.config.noComments) { return desc } - const text = overwriteDoc?.text || tsDoc?.text - const tags = overwriteDoc?.tags || tsDoc?.tags || [] + const text = tsDoc if (text) { desc.push(`${indent}/**`) @@ -386,13 +503,13 @@ export class TypeDefinitionGenerator implements Generator { } } - for (const tag of tags) { - if (tag.paramName) { - desc.push(`${indent} * @${tag.tagName} ${tag.paramName} ${tag.text}`) - } else { - desc.push(`${indent} * @${tag.tagName} ${tag.text}`) - } - } + // for (const tag of tags) { + // if (tag.paramName) { + // desc.push(`${indent} * @${tag.tagName} ${tag.paramName} ${tag.text}`) + // } else { + // desc.push(`${indent} * @${tag.tagName} ${tag.text}`) + // } + // } desc.push(`${indent} */`) } return desc @@ -404,7 +521,7 @@ export class TypeDefinitionGenerator implements Generator { * @param indentCount * @returns */ - protected addInfoComment(comment?: string, indentCount = 0) { + addInfoComment(comment?: string, indentCount = 0) { const def: string[] = [] if (this.config.noDebugComments) { return def @@ -424,7 +541,7 @@ export class TypeDefinitionGenerator implements Generator { * @param indentCount * @returns */ - protected addInlineInfoComment(comment?: string, indentCount = 0) { + addInlineInfoComment(comment?: string, indentCount = 0) { const def: string[] = [] if (this.config.noDebugComments) { return def @@ -436,7 +553,7 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected mergeDescs(descs: string[], comment?: string, indentCount = 1) { + mergeDescs(descs: string[], comment?: string, indentCount = 1) { const def: string[] = [] const indent = generateIndent(indentCount) @@ -451,17 +568,17 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateParameter(tsParam: TsParameter, namespace: string) { + generateParameter(tsParam: IntrospectedFunctionParameter) { if (typeof tsParam?.name !== 'string') { throw new Error(NO_TSDATA('generateParameter')) } const types = tsParam.type const name = tsParam.name - const typeStr = this.generateTypes(types, namespace) - const optional = typesContainsOptional(types) && !tsParam.isRest + const typeStr = this.generateTypes(types) + const optional = tsParam.isOptional && !tsParam.isVarArgs const affix = optional ? '?' : '' - const prefix = tsParam.isRest ? '...' : '' + const prefix = tsParam.isVarArgs ? '...' : '' return [`${prefix}${name}${affix}: ${typeStr}`] } @@ -472,112 +589,87 @@ export class TypeDefinitionGenerator implements Generator { * @param isOut If this generic parameters are out do only generate the type parameter names * @returns */ - protected generateGenericParameters(tsGenerics?: TsGenericParameter[], isOut = false) { - const desc: string[] = [] - if (!tsGenerics?.length) { - return '' - } + generateGenericParameters(nodes: Generic[], withDefaults = true) { + const { namespace, options } = this - for (const tsGeneric of tsGenerics) { - if (!tsGeneric.name) { - continue - } - let genericStr = `${tsGeneric.name}` - if (!isOut && tsGeneric.extends) { - genericStr += ` extends ${tsGeneric.extends}` - } - if (!isOut && tsGeneric.value) { - genericStr += ` = ${tsGeneric.value}` + const list = nodes.map((generic) => { + const Type = generic.type.rootPrint(namespace, options) + + if (generic.defaultType && withDefaults) { + const defaultType = generic.defaultType.rootPrint(namespace, options) + + if (generic.constraint) { + const constraint = generic.constraint.rootPrint(namespace, options) + return `${Type} extends ${constraint} = ${defaultType}` + } + + return `${Type} = ${defaultType}` + } else if (generic.constraint && withDefaults) { + const constraint = generic.constraint.rootPrint(namespace, options) + return `${Type} extends ${constraint}` + } else { + return `${Type}` } - desc.push(genericStr) + }) + + if (list.length > 0) { + return `<${list.join(', ')}>` } - return `<${desc.join(', ')}>` + return '' } - protected generateOutParameterReturn(girParam: GirCallableParamElement, namespace: string) { - const desc: string[] = [] + // generateOutParameterReturn(girParam: GirCallableParamElement, namespace: string) { + // const desc: string[] = [] - if (!girParam._tsData) { - this.log.warn(NO_TSDATA('generateOutParameterReturn')) - return desc - } + // if (!girParam) { + // this.log.warn(NO_TSDATA('generateOutParameterReturn')) + // return desc + // } - const { name } = girParam._tsData - const typeStr = this.generateTypes(girParam._tsData.type, namespace) + // const { name } = girParam + // const typeStr = this.generateTypes(girParam.type, namespace) - desc.push(`/* ${name} */ ${typeStr}`) - return desc - } + // desc.push(`/* ${name} */ ${typeStr}`) + // return desc + // } - protected generateFunctionReturn(tsFunction: TsFunction | TsCallback | TsSignal, namespace: string) { + generateFunctionReturn( + tsFunction: IntrospectedFunction | IntrospectedClassFunction | IntrospectedClassCallback | IntrospectedCallback, + ) { if (tsFunction.name === 'constructor') { return '' } - const overrideReturnType = tsFunction.overrideReturnType - const outParams = tsFunction.outParams - const retTypeIsVoid = tsFunction.retTypeIsVoid - const isPromise = tsFunction.isPromise || false - const typeStr = this.generateTypes(tsFunction.returnTypes, namespace) - - let desc = typeStr - - if (overrideReturnType) { - desc = removeNamespace(overrideReturnType, namespace) - } - // TODO: Transform the outParams to `tsFunction.returnTypes` to move this logic to `gir-module.ts` - else if (outParams.length + (retTypeIsVoid ? 0 : 1) > 1) { - const outParamsDesc: string[] = [] - - if (!retTypeIsVoid) { - outParamsDesc.push(`/* returnType */ ${typeStr}`) - } - - for (const outParam of outParams) { - outParamsDesc.push(...this.generateOutParameterReturn(outParam, namespace)) - } + const typeStr = this.generateType(tsFunction.return()) - desc = outParamsDesc.join(', ') - desc = `[ ${desc} ]` - } else if (outParams.length === 1 && retTypeIsVoid) { - desc = this.generateOutParameterReturn(outParams[0], namespace).join(' ') - } - - if (isPromise) { - desc = `globalThis.Promise<${desc}>` - } + return typeStr + } - return desc + generateClassFunction(node: IntrospectedClassFunction): string[] { + return this.generateFunction(node) } - protected generateFunction( - tsFunction: TsFunction | TsCallback | TsSignal | undefined, - /** If true only generate static functions otherwise generate only non static functions */ - onlyStatic: boolean, - namespace: string, - indentCount = 1, - overloads = true, + generateFunction( + tsFunction: IntrospectedClassFunction | IntrospectedFunction | IntrospectedCallback, + indentCount = 0, ) { const def: string[] = [] const indent = generateIndent(indentCount) - if (!tsFunction) { - this.log.warn(NO_TSDATA('generateFunction')) - return def - } - let { name } = tsFunction - const { isStatic } = tsFunction + const isStatic = tsFunction instanceof IntrospectedStaticClassFunction + const isGlobal = !(tsFunction instanceof IntrospectedClassFunction) + const isArrowType = + tsFunction instanceof IntrospectedCallback || tsFunction instanceof IntrospectedClassCallback - const { isArrowType, isGlobal, inParams, instanceParameters } = tsFunction + const { parameters: inParams } = tsFunction - if ((isStatic && !onlyStatic) || (!isStatic && onlyStatic)) { - return def - } + // if ((isStatic && !onlyStatic) || (!isStatic && onlyStatic)) { + // return def + // } - if (tsFunction.doc && !tsFunction.hasUnresolvedConflict) - def.push(...this.addGirDocComment(tsFunction.doc, indentCount)) + if (tsFunction.doc) def.push(...this.addGirDocComment(tsFunction.doc, indentCount)) const staticStr = isStatic && tsFunction.name !== 'constructor' ? 'static ' : '' @@ -585,7 +677,7 @@ export class TypeDefinitionGenerator implements Generator { const genericStr = this.generateGenericParameters(tsFunction.generics) // temporary solution, will be solved differently later - const commentOut = tsFunction.hasUnresolvedConflict ? '// Has conflict: ' : '' + const commentOut = '' let exportStr = '' // `tsType === 'function'` are a global methods which can be exported @@ -593,7 +685,7 @@ export class TypeDefinitionGenerator implements Generator { exportStr = !this.config.noNamespace ? '' : 'export ' } - const returnType = this.generateFunctionReturn(tsFunction, namespace) + const returnType = this.generateFunctionReturn(tsFunction) let retSep = '' if (returnType) { @@ -605,7 +697,7 @@ export class TypeDefinitionGenerator implements Generator { } } - const inParamsDef: string[] = this.generateInParameters(inParams, instanceParameters, namespace) + const inParamsDef: string[] = this.generateInParameters(inParams) def.push( `${indent}${commentOut}${exportStr}${staticStr}${globalStr}${name}${genericStr}(${inParamsDef.join( @@ -614,27 +706,25 @@ export class TypeDefinitionGenerator implements Generator { ) // Add overloaded methods - if (overloads && tsFunction.overloads.length > 0) { - def.push(...this.addInfoComment(`Overloads of ${name}`, indentCount)) - for (const func of tsFunction.overloads) { - def.push(...this.generateFunction(func, onlyStatic, namespace, indentCount, false)) - } - } + // if (overloads && tsFunction.overloads.length > 0) { + // def.push(...this.addInfoComment(`Overloads of ${name}`, indentCount)) + // for (const func of tsFunction.overloads) { + // def.push(...this.generateFunction(func, onlyStatic, namespace, indentCount, false)) + // } + // } return def } - protected generateFunctions( - tsFunctions: TsFunction[], - onlyStatic: boolean, - namespace: string, + generateFunctions( + tsFunctions: IntrospectedFunction[] | IntrospectedClassFunction[], indentCount = 1, comment?: string, ) { const def: string[] = [] for (const girFunction of tsFunctions) { - def.push(...this.generateFunction(girFunction, onlyStatic, namespace, indentCount)) + def.push(...this.generateFunction(girFunction, indentCount)) } if (def.length > 0) { @@ -644,34 +734,28 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateCallbackInterface( - tsCallback: TsCallback | TsSignal, - namespace: string, + generateCallback( + tsCallback: IntrospectedCallback | IntrospectedClassCallback, indentCount = 0, classModuleName?: string, ) { const def: string[] = [] - if (!tsCallback?.tsCallbackInterface) { - this.log.warn(NO_TSDATA('generateCallbackInterface')) - return def - } - - def.push(...this.addGirDocComment(tsCallback.doc, indentCount, tsCallback.tsCallbackInterface.overwriteDoc)) + def.push(...this.addGirDocComment(tsCallback.doc, indentCount)) const indent = generateIndent(indentCount) const indentBody = generateIndent(indentCount + 1) - const { inParams, instanceParameters } = tsCallback - const returnTypeStr = this.generateTypes(tsCallback.returnTypes, namespace) + const { parameters: inParams } = tsCallback + const returnTypeStr = this.generateTypes(tsCallback.return()) // Get name, remove namespace and remove module class name prefix - let { name } = tsCallback.tsCallbackInterface - const { generics } = tsCallback.tsCallbackInterface - name = removeNamespace(name, namespace) + let { name } = tsCallback + const generics = tsCallback.generics + name = removeNamespace(name, tsCallback.namespace.name) if (classModuleName) name = removeClassModule(name, classModuleName) const genericParameters = this.generateGenericParameters(generics) - const inParamsDef: string[] = this.generateInParameters(inParams, instanceParameters, namespace) + const inParamsDef: string[] = this.generateInParameters(inParams) const interfaceHead = `${name}${genericParameters}` @@ -682,9 +766,8 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateCallbackInterfaces( - tsCallbacks: Array, - namespace: string, + generateCallbackInterfaces( + tsCallbacks: Array, indentCount = 0, classModuleName: string, comment?: string, @@ -692,7 +775,7 @@ export class TypeDefinitionGenerator implements Generator { const def: string[] = [] for (const tsCallback of tsCallbacks) { - def.push(...this.generateCallbackInterface(tsCallback, namespace, indentCount, classModuleName), '') + def.push(...this.generateCallback(tsCallback, indentCount, classModuleName), '') } if (def.length > 0) { @@ -702,39 +785,35 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateEnumeration(girEnum: GirEnumElement | GirBitfieldElement, indentCount = 0) { + generateEnum(girEnum: IntrospectedEnum, indentCount = 0) { const desc: string[] = [] - if (!girElementIsIntrospectable(girEnum)) { - return desc - } - - if (!girEnum._tsData) { + if (!girEnum) { this.log.warn(NO_TSDATA('generateEnumeration')) return desc } - desc.push(...this.addGirDocComment(girEnum._tsData.doc, indentCount)) + desc.push(...this.addGirDocComment(girEnum.doc, indentCount)) - const { name } = girEnum._tsData + const { name } = girEnum desc.push(this.generateExport('enum', name, '{', indentCount)) - if (girEnum.member) { - for (const girEnumMember of girEnum.member) { - if (!girEnumMember._tsData) continue - desc.push(...this.generateEnumerationMember(girEnumMember._tsData, indentCount + 1)) + if (girEnum.members) { + for (const girEnumMember of girEnum.members.values()) { + if (!girEnumMember) continue + desc.push(...this.generateEnumMember(girEnumMember, indentCount + 1)) } } desc.push('}') return desc } - protected generateEnumerationMember(tsMember: TsMember, indentCount = 1) { + generateEnumMember(tsMember: GirEnumMember, indentCount = 1) { const desc: string[] = [] - if (!tsMember) { - this.log.warn(NO_TSDATA('generateEnumerationMember')) - return desc - } + // if (!tsMember) { + // this.log.warn(NO_TSDATA('generateEnumerationMember')) + // return desc + // } desc.push(...this.addGirDocComment(tsMember.doc, indentCount)) @@ -743,83 +822,70 @@ export class TypeDefinitionGenerator implements Generator { return desc } - protected generateConstant(tsConst: TsVar, namespace: string, indentCount = 0) { + generateConst(tsConst: IntrospectedConstant, indentCount = 0) { const desc: string[] = [] - if (!tsConst.hasUnresolvedConflict) { - desc.push(...this.addGirDocComment(tsConst.doc, indentCount)) - } + // if (!tsConst.hasUnresolvedConflict) { + // desc.push(...this.addGirDocComment(tsConst.doc, indentCount)) + // } const indent = generateIndent(indentCount) const exp = !this.config.noNamespace ? '' : 'export ' - const varDesc = this.generateVariable(tsConst, namespace, 0) + const varDesc = this.generateVariable(tsConst, 0) desc.push(`${indent}${exp}const ${varDesc}`) return desc } - protected generateAlias(girAlias: GirAliasElement, namespace: string, indentCount = 0) { + generateAlias(girAlias: IntrospectedAlias, indentCount = 0) { const desc: string[] = [] - if (!girElementIsIntrospectable(girAlias)) { - return '' - } - - if (!girAlias._tsData) { - this.log.warn(NO_TSDATA('generateAlias')) - return desc - } const indent = generateIndent(indentCount) const exp = !this.config.noNamespace ? '' : 'export ' - const type = removeNamespace(girAlias._tsData.type, namespace) - desc.push(`${indent}${exp}type ${girAlias._tsData.name} = ${type}`) + desc.push(`${indent}${exp}type ${girAlias.name} = ${girAlias.type.print(this.namespace, this._config)}`) return desc } - protected generateConstructPropsInterface( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, + generateConstructPropsInterface( + girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 0, ) { const def: string[] = [] - if (!girClass._tsData || !girClass._fullSymName || !girClass._module) { - throw new Error(NO_TSDATA('generateConstructPropsInterface')) - } - - if (!girClass._tsData.isDerivedFromGObject) { + if (!girClass.someParent((p: IntrospectedBaseClass) => p.namespace.name === 'GObject' && p.name === 'Object')) { return def } const indent = generateIndent(indentCount) const exp = !this.config.noNamespace ? '' : 'export ' let ext = '' + const resolution = girClass.resolveParents() + const superType = resolution.node + const iSuperTypes = 'implements' in resolution ? resolution.implements() : [] - if (girClass._tsData.inheritConstructPropInterfaceNames.length) { - const constructPropInterfaceNames = girClass._tsData.inheritConstructPropInterfaceNames.map((n) => - removeNamespace(n, namespace), - ) - ext = `extends ${constructPropInterfaceNames.join(', ')} ` + if (superType || iSuperTypes.length > 0) { + ext = `extends ${[ + superType.getType().print(this.namespace, this.config), + ...iSuperTypes.map((i) => i.node.getType().print(this.namespace, this.config)), + ].join(', ')} ` } // Remove namespace and class module name const constructPropInterfaceName = removeClassModule( - removeNamespace(girClass._tsData.constructPropInterfaceName, namespace), - girClass._tsData.name, + removeNamespace(`${girClass.name}ConstructorProps`, girClass.namespace.name), + girClass.name, ) def.push(...this.addInfoComment('Constructor properties interface', indentCount)) // START BODY - { + if (girClass.mainConstructor) { def.push(`${indent}${exp}interface ${constructPropInterfaceName} ${ext}{`) def.push( - ...this.generateProperties( - girClass._tsData.constructProps.map((cp) => cp._tsData).filter((cp) => !!cp) as TsProperty[], - false, - namespace, - `Own constructor properties of ${girClass._module.packageName}.${girClass._fullSymName}`, + ...this.generateFields( + girClass.mainConstructor.parameters.map((param) => param.asField()), + `Own constructor properties of ${girClass.namespace.packageName}.${girClass.name}`, indentCount + 1, ), ) @@ -830,23 +896,13 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateClassFields( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - onlyStatic: boolean, - namespace: string, - indentCount = 1, - ) { + generateClassFields(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 1) { const def: string[] = [] - if (!girClass._tsData || !girClass._fullSymName || !girClass._module) { - throw new Error(NO_TSDATA('generateClassFields')) - } def.push( - ...this.generateProperties( - girClass._tsData.fields.map((f) => f._tsData).filter((f) => !!f) as TsProperty[], - onlyStatic, - namespace, - `Own fields of ${girClass._module.packageName}.${girClass._fullSymName}`, + ...this.generateFields( + girClass.fields, + `Own fields of ${girClass.namespace.packageName}.${girClass.name}`, indentCount, ), ) @@ -854,34 +910,13 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateClassProperties( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - onlyStatic: boolean, - namespace: string, - indentCount = 1, - ) { + generateClassProperties(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 1) { const def: string[] = [] - if (!girClass._tsData || !girClass._fullSymName || !girClass._module) { - throw new Error(NO_TSDATA('generateClassProperties')) - } - - def.push( - ...this.generateProperties( - girClass._tsData.properties.map((p) => p._tsData).filter((p) => !!p) as TsProperty[], - onlyStatic, - namespace, - `Own properties of ${girClass._module.packageName}.${girClass._fullSymName}`, - indentCount, - ), - ) - def.push( ...this.generateProperties( - girClass._tsData.conflictProperties, - onlyStatic, - namespace, - `Conflicting properties`, + girClass.props.filter((p) => !!p), + `Own properties of ${girClass.namespace.packageName}.${girClass.name}`, indentCount, ), ) @@ -889,88 +924,63 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateClassMethods( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - onlyStatic: boolean, - namespace: string, - indentCount = 1, - ) { + generateClassMethods(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 1) { const def: string[] = [] - if (!girClass._tsData || !girClass._fullSymName || !girClass._module) { - throw new Error(NO_TSDATA('generateClassMethods')) - } - + //${onlyStatic ? 'static ' : ''} def.push( ...this.generateFunctions( - girClass._tsData.methods.map((girFunc) => girFunc._tsData).filter((tsFunc) => !!tsFunc) as TsFunction[], - onlyStatic, - namespace, + girClass.members, indentCount, - `Owm ${onlyStatic ? 'static ' : ''}methods of ${girClass._module.packageName}.${girClass._fullSymName}`, + `Owm methods of ${girClass.namespace.packageName}.${girClass.name}`, ), ) - def.push( - ...this.generateFunctions( - girClass._tsData.conflictMethods, - onlyStatic, - namespace, - indentCount, - `Conflicting ${onlyStatic ? 'static ' : ''}methods`, - ), - ) + // def.push( + // ...this.generateFunctions( + // girClass.conflictMethods, + // onlyStatic, + // namespace, + // indentCount, + // `Conflicting ${onlyStatic ? 'static ' : ''}methods`, + // ), + // ) return def } - protected generateClassConstructors( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, + generateClassConstructors( + girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 1, ) { const def: string[] = [] - if (!girClass._tsData || !girClass._fullSymName || !girClass._module) { - throw new Error(NO_TSDATA('generateClassConstructors')) - } + // if (!girClass || !girClass.name || !girClass._module) { + // throw new Error(NO_TSDATA('generateClassConstructors')) + // } // Constructors - def.push( - ...this.generateFunctions( - girClass._tsData.constructors - .map((girFunc) => girFunc._tsData) - .filter((tsFunc) => !!tsFunc) as TsFunction[], - true, - namespace, - indentCount, - ), - ) + if (girClass.mainConstructor instanceof IntrospectedDirectAllocationConstructor) + def.push(...this.generateDirectAllocationConstructor(girClass.mainConstructor)) + else if (girClass.mainConstructor instanceof IntrospectedConstructor) + def.push(...this.generateConstructor(girClass.mainConstructor)) + // _init method - def.push( - ...this.generateFunctions( - girClass._tsData.constructors - .map((girFunc) => girFunc._tsData) - .filter((tsFunc) => !!tsFunc) as TsFunction[], - false, - namespace, - indentCount, - ), - ) - // Pseudo constructors - def.push( - ...this.generateFunctions( - girClass._tsData.staticFunctions - .map((girFunc) => girFunc._tsData) - .filter((tsFunc) => !!tsFunc) as TsFunction[], - true, - namespace, - indentCount, - ), - ) + def.push(...girClass.constructors.flatMap((constructor) => this.generateConstructorFunction(constructor))) + // // Pseudo constructors + // def.push( + // ...this.generateFunctions( + // girClass.staticFunctions + // .map((girFunc) => girFunc) + // .filter((tsFunc) => !!tsFunc) as IntrospectedFunction[], + // true, + // namespace, + // indentCount, + // ), + // ) if (def.length) { def.unshift( ...this.addInfoComment( - `Constructors of ${girClass._module.packageName}.${girClass._fullSymName}`, + `Constructors of ${girClass.namespace.packageName}.${girClass.name}`, indentCount, ), ) @@ -983,51 +993,43 @@ export class TypeDefinitionGenerator implements Generator { * Instance methods, vfunc_ prefix * @param girClass */ - protected generateClassVirtualMethods( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, + generateClassVirtualMethods( + girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 1, ) { const def: string[] = [] - if (!girClass._tsData || !girClass._fullSymName || !girClass._module) { - throw new Error(NO_TSDATA('generateClassVirtualMethods')) - } def.push( ...this.generateFunctions( - girClass._tsData.virtualMethods - .map((girFunc) => girFunc._tsData) - .filter((tsFunc) => !!tsFunc) as TsFunction[], - false, - namespace, + [...girClass.members.values()], indentCount, - `Own virtual methods of ${girClass._module.packageName}.${girClass._fullSymName}`, + `Own virtual methods of ${girClass.namespace.packageName}.${girClass.name}`, ), ) return def } - protected generateClassSignalInterfaces( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, - indentCount = 0, - ) { + generateClassSignalInterfaces(girClass: IntrospectedClass, indentCount = 0) { const def: string[] = [] - if (!girClass._tsData) { + if (!girClass) { throw new Error(NO_TSDATA('generateClassSignalInterface')) } - const tsSignals = girClass._tsData.signals - .map((signal) => signal._tsData) - .filter((signal) => !!signal) as TsSignal[] + const tsSignals = girClass.signals.map((signal) => signal).filter((signal) => !!signal) def.push( ...this.generateCallbackInterfaces( - tsSignals, - namespace, + tsSignals.map((signal) => { + return new IntrospectedClassCallback({ + name: signal.name, + parameters: signal.parameters, + return_type: signal.return_type, + parent: signal.parent, + }) + }), indentCount, - girClass._tsData.name, + girClass.name, 'Signal callback interfaces', ), ) @@ -1035,45 +1037,36 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected generateClassSignals( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, - ) { - const def: string[] = [] - if (!girClass._tsData || !girClass._fullSymName || !girClass._module) { - throw new Error(NO_TSDATA('generateClassSignals')) - } + // generateClassSignals(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface) { + // const def: string[] = [] + // if (!girClass || !girClass.name || !girClass._module) { + // throw new Error(NO_TSDATA('generateClassSignals')) + // } - const signalDescs = this.generateSignals(girClass._tsData.signals, girClass, namespace, 0) + // const signalDescs = this.generateSignals(girClass, girClass, 0) - def.push( - ...this.mergeDescs( - signalDescs, - `Own signals of ${girClass._module.packageName}.${girClass._fullSymName}`, - 1, - ), - ) + // def.push( + // ...this.mergeDescs(signalDescs, `Own signals of ${girClass.namespace.packageName}.${girClass.name}`, 1), + // ) - return def - } + // return def + // } - protected generateClassModules( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, - indentCount = 0, - ) { + generateClassModules(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 0) { const def: string[] = [] const bodyDef: string[] = [] - if (!girClass._tsData) return def + if (!girClass) return def const indent = generateIndent(indentCount) const exp = !this.config.noNamespace ? '' : 'export ' - // Signal interfaces - bodyDef.push(...this.generateClassSignalInterfaces(girClass, namespace, indentCount + 1)) + if ('signals' in girClass) { + // Signal interfaces + bodyDef.push(...this.generateClassSignalInterfaces(girClass, indentCount + 1)) + } // Properties interface for construction - bodyDef.push(...this.generateConstructPropsInterface(girClass, namespace, indentCount + 1)) + bodyDef.push(...this.generateConstructPropsInterface(girClass, indentCount + 1)) if (!bodyDef.length) { return [] @@ -1081,7 +1074,7 @@ export class TypeDefinitionGenerator implements Generator { // START BODY { - def.push(`${indent}${exp}module ${girClass._tsData.name} {`) + def.push(`${indent}${exp}module ${girClass.name} {`) // Properties interface for construction def.push(...bodyDef) @@ -1099,20 +1092,19 @@ export class TypeDefinitionGenerator implements Generator { * @param girClass * @param namespace */ - protected generateImplementationInterface( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, - ) { + generateImplementationInterface(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface) { const def: string[] = [] - if (!girClass._tsData) return def + if (!girClass) return def - const genericParameters = this.generateGenericParameters(girClass._tsData.generics) - const implementationNames = girClass._tsData.parents - .filter((implementation) => implementation.type !== 'parent') - .map((implementation) => implementation.localParentName) + const genericParameters = this.generateGenericParameters(girClass.generics) + const resolution = girClass.resolveParents() + const implementationNames = + 'implements' in resolution + ? resolution.implements().map((i) => i.node.getType().print(this.namespace, this.config)) + : [] const ext = implementationNames.length ? ` extends ${implementationNames.join(', ')}` : '' - const interfaceHead = `${girClass._tsData.name}${genericParameters}${ext}` + const interfaceHead = `${girClass.name}${genericParameters}${ext}` // START INTERFACE { @@ -1121,22 +1113,22 @@ export class TypeDefinitionGenerator implements Generator { // START BODY { // Properties - def.push(...this.generateClassProperties(girClass, false, namespace)) + def.push(...this.generateClassProperties(girClass)) // Fields - def.push(...this.generateClassFields(girClass, false, namespace)) + def.push(...this.generateClassFields(girClass)) // Methods - def.push(...this.generateClassMethods(girClass, false, namespace)) + def.push(...this.generateClassMethods(girClass)) // Virtual methods - def.push(...this.generateClassVirtualMethods(girClass, namespace)) + def.push(...this.generateClassVirtualMethods(girClass)) // Signals - def.push(...this.generateClassSignals(girClass, namespace)) + // TODO: def.push(...this.generateClassSignals(girClass)) // TODO: Generate `GirSignalElement`s instead of generate the signal definition strings directly - def.push(...this.generateClassPropertySignals(girClass, namespace)) + // TODO: def.push(...this.generateClassPropertySignals(girClass)) } // END BODY @@ -1148,33 +1140,45 @@ export class TypeDefinitionGenerator implements Generator { return def } + protected extends(node: IntrospectedBaseClass) { + const { namespace: ns, options } = this + if (node.superType) { + const ResolvedType = node.superType.resolveIdentifier(ns, options) + const Type = ResolvedType?.print(ns, options) + + if (Type) { + return ` extends ${Type}` + } + + throw new Error( + `Unable to resolve type: ${node.superType.name} from ${node.superType.namespace} in ${node.namespace.name} ${node.namespace.version}`, + ) + } + + return '' + } + /** * Represents a record, GObject class or interface as a Typescript class * @param girClass * @param namespace */ - protected generateClass( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - namespace: string, - ) { + generateClass(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface) { const def: string[] = [] - if (!girClass._tsData) return def - - def.push(...this.generateClassModules(girClass, namespace)) + def.push(...this.generateClassModules(girClass)) - def.push(...this.generateImplementationInterface(girClass, namespace)) + def.push(...this.generateImplementationInterface(girClass)) - def.push(...this.addGirDocComment(girClass._tsData.doc, 0)) + def.push(...this.addGirDocComment(girClass.doc, 0)) - const genericParameters = this.generateGenericParameters(girClass._tsData.generics) - const parentName = girClass._tsData.parents.find((parent) => parent.type === 'parent')?.localParentName - const ext = parentName ? ` extends ${parentName}` : '' - const classHead = `${girClass._tsData.name}${genericParameters}${ext}` + const genericParameters = this.generateGenericParameters(girClass.generics) + const ext = this.extends(girClass) + const classHead = `${girClass.name}${genericParameters}${ext}` // START CLASS { - if (girClass._tsData.isAbstract) { + if (girClass instanceof IntrospectedClass && girClass.isAbstract) { def.push(this.generateExport('abstract class', classHead, '{')) } else { def.push(this.generateExport('class', classHead, '{')) @@ -1183,16 +1187,16 @@ export class TypeDefinitionGenerator implements Generator { // START BODY { // Static Properties - def.push(...this.generateClassProperties(girClass, true, namespace)) + def.push(...this.generateClassProperties(girClass)) // Static Fields - def.push(...this.generateClassFields(girClass, true, namespace)) + def.push(...this.generateClassFields(girClass)) // Constructors - def.push(...this.generateClassConstructors(girClass, namespace)) + def.push(...this.generateClassConstructors(girClass)) // Static Methods - def.push(...this.generateClassMethods(girClass, true, namespace)) + def.push(...this.generateClassMethods(girClass)) } // END BODY @@ -1204,7 +1208,11 @@ export class TypeDefinitionGenerator implements Generator { return def } - protected async exportModuleJS(moduleTemplateProcessor: TemplateProcessor, girModule: GirModule): Promise { + async stringifyNamespace(node: GirModule): Promise { + return (await this.generateNamespace(node))?.join('\n') ?? null + } + + async exportModuleJS(girModule: GirModule): Promise { const template = 'module.js' let target = `${girModule.importName}.js` @@ -1217,7 +1225,7 @@ export class TypeDefinitionGenerator implements Generator { } if (this.config.outdir) { - await moduleTemplateProcessor.create( + await this.moduleTemplateProcessor.create( template, this.config.outdir, target, @@ -1227,15 +1235,12 @@ export class TypeDefinitionGenerator implements Generator { this.config, ) } else { - const { append, prepend } = await moduleTemplateProcessor.load(template, {}, this.config) + const { append, prepend } = await this.moduleTemplateProcessor.load(template, {}, this.config) this.log.log(append + prepend) } } - protected async exportModuleAmbientTS( - moduleTemplateProcessor: TemplateProcessor, - girModule: GirModule, - ): Promise { + async exportModuleAmbientTS(girModule: GirModule): Promise { const template = 'module-ambient.d.ts' let target = `${girModule.importName}-ambient.d.ts` @@ -1248,7 +1253,7 @@ export class TypeDefinitionGenerator implements Generator { } if (this.config.outdir) { - await moduleTemplateProcessor.create( + await this.moduleTemplateProcessor.create( template, this.config.outdir, target, @@ -1258,15 +1263,12 @@ export class TypeDefinitionGenerator implements Generator { this.config, ) } else { - const { append, prepend } = await moduleTemplateProcessor.load(template, {}, this.config) + const { append, prepend } = await this.moduleTemplateProcessor.load(template, {}, this.config) this.log.log(append + prepend) } } - protected async exportModuleImportTS( - moduleTemplateProcessor: TemplateProcessor, - girModule: GirModule, - ): Promise { + async exportModuleImportTS(girModule: GirModule): Promise { const template = 'module-import.d.ts' let target = `${girModule.importName}-import.d.ts` @@ -1279,7 +1281,7 @@ export class TypeDefinitionGenerator implements Generator { } if (this.config.outdir) { - await moduleTemplateProcessor.create( + await this.moduleTemplateProcessor.create( template, this.config.outdir, target, @@ -1289,14 +1291,22 @@ export class TypeDefinitionGenerator implements Generator { this.config, ) } else { - const { append, prepend } = await moduleTemplateProcessor.load(template, {}, this.config) + const { append, prepend } = await this.moduleTemplateProcessor.load(template, {}, this.config) this.log.log(append + prepend) } } - protected async exportModuleTS(moduleTemplateProcessor: TemplateProcessor, girModule: GirModule): Promise { - const template = 'module.d.ts' - const explicitTemplate = `${girModule.importName}.d.ts` + async exportModuleTS(): Promise { + const { namespace: girModule } = this + const output = await this.generateNamespace(girModule) + + if (!output) { + this.log.error('Failed to generate') + return + } + + // const template = 'module.d.ts' + // const explicitTemplate = `${girModule.importName}.d.ts` let target = `${girModule.importName}.d.ts` @@ -1308,6 +1318,69 @@ export class TypeDefinitionGenerator implements Generator { } } + const formatter = this.dependencyManager.registry.getFormatter('dts') + + let contents!: string + try { + contents = this.config.noPrettyPrint ? output.join('\n') : await formatter.format(output.join('\n')) + } catch (error) { + console.error(error) + contents = output.join('\n') + } + + if (this.config.outdir) { + const outputPath = this.moduleTemplateProcessor.getOutputPath(this.config.outdir, target) + console.log(outputPath, target) + // write template result file + await mkdir(dirname(outputPath), { recursive: true }) + await writeFile(outputPath, contents, { encoding: 'utf8', flag: 'w' }) + } else { + this.log.log(contents) + } + } + + /** + * + * @param namespace E.g. 'Gtk' + * @param packageName E.g. 'Gtk-3.0' + * @param asExternType Currently only used for node type imports + */ + protected generateModuleDependenciesImport(packageName: string): string[] { + const def: string[] = [] + const dep = this.dependencyManager.get(packageName) + + // if (this.config.package) { + // if (this.config.buildType === 'types') { + // // See https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html + // def.push(`/// `) + // } + // } else { + // if (this.config.buildType === 'types') { + // // See https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html + // def.push(`/// `) + // } + // } + + def.push(dep.importDef) + + return def + } + + async generateNamespace(girModule: GirModule): Promise { + const moduleTemplateProcessor = this.moduleTemplateProcessor + const template = 'module.d.ts' + const explicitTemplate = `${girModule.importName}.d.ts` + + // let target = `${girModule.importName}.d.ts` + + // if (this.overrideConfig.moduleType) { + // if (this.overrideConfig.moduleType === 'cjs') { + // target = `${girModule.importName}.d.cts` + // } else { + // target = `${girModule.importName}.d.mts` + // } + // } + const out: string[] = [] out.push(...this.addTSDocCommentLines([girModule.packageName])) @@ -1342,138 +1415,73 @@ export class TypeDefinitionGenerator implements Generator { // Newline out.push('') - if (girModule.ns.enumeration) - for (const girEnum of girModule.ns.enumeration) out.push(...this.generateEnumeration(girEnum)) - - if (girModule.ns.bitfield) - for (const girBitfield of girModule.ns.bitfield) out.push(...this.generateEnumeration(girBitfield)) - - if (girModule.ns.constant) - for (const girConst of girModule.ns.constant) { - if (!girConst._tsData) continue - out.push(...this.generateConstant(girConst._tsData, girModule.namespace, 0)) - } - - if (girModule.ns.function) - for (const girFunc of girModule.ns.function) { - if (!girFunc._tsData) { - // this.log.warn(NO_TSDATA('exportModuleTS functions')) - continue - } - out.push(...this.generateFunction(girFunc._tsData, false, girModule.namespace, 0)) - } - - if (girModule.ns.callback) - for (const girCallback of girModule.ns.callback) { - if (!girCallback._tsData) continue - out.push(...this.generateCallbackInterface(girCallback._tsData, girModule.namespace)) - } - - if (girModule.ns.interface) - for (const girIface of girModule.ns.interface) - if (girIface._module) { - out.push(...this.generateClass(girIface, girIface._module.namespace)) - } + if (girModule.members) + for (const m of girModule.members.values()) + out.push( + ...(Array.isArray(m) ? m : [m]).flatMap( + (m) => (m as IntrospectedNamespaceMember).asString(this as FormatGenerator) ?? [], + ), + ) // Extra interfaces if a template with the module name (e.g. '../templates/gobject-2-0.d.ts') is found // E.g. used for GObject-2.0 to help define GObject classes in js; // these aren't part of gi. if (moduleTemplateProcessor.exists(explicitTemplate)) { - const { append, prepend } = await moduleTemplateProcessor.load(explicitTemplate, {}, this.config) + const { append, prepend } = await this.moduleTemplateProcessor.load(explicitTemplate, {}, this.config) // TODO push prepend and append to the right position out.push(append + prepend) } - if (girModule.ns.class) - for (const gitClass of girModule.ns.class) - if (gitClass._module) { - out.push(...this.generateClass(gitClass, gitClass._module.namespace)) - } - - if (girModule.ns.record) - for (const girRecord of girModule.ns.record) - if (girRecord._module) { - out.push(...this.generateClass(girRecord, girRecord._module.namespace)) - } - - if (girModule.ns.union) - for (const girUnion of girModule.ns.union) - if (girUnion._module) { - out.push(...this.generateClass(girUnion, girUnion._module.namespace)) - } - - if (girModule.ns.alias) - // GType is not a number in GJS - for (const girAlias of girModule.ns.alias) - if (girModule.packageName !== 'GObject-2.0' || girAlias.$.name !== 'Type') - out.push(...this.generateAlias(girAlias, girModule.namespace, 1)) - if (this.config.environment === 'gjs') { // Properties added to every GIRepositoryNamespace // https://gitlab.gnome.org/GNOME/gjs/-/blob/master/gi/ns.cpp#L186-190 out.push( - ...this.generateConstant( - { - doc: { - text: 'Name of the imported GIR library', - tags: [ - { - text: 'https://gitlab.gnome.org/GNOME/gjs/-/blob/master/gi/ns.cpp#L188', - tagName: 'see', - paramName: '', - }, - ], - }, - name: '__name__', - type: [ + ...this.generateConst( + new IntrospectedConstant({ + // TODO: + doc: printGirDocComment( { - type: 'string', - optional: false, - nullable: false, - callbacks: [], - generics: [], - isArray: false, - isFunction: false, - isCallback: false, + text: 'Name of the imported GIR library', + tags: [ + { + text: 'https://gitlab.gnome.org/GNOME/gjs/-/blob/master/gi/ns.cpp#L188', + tagName: 'see', + paramName: '', + }, + ], }, - ], - isInjected: false, - tsTypeName: 'constant', - girTypeName: 'constant', - }, - girModule.namespace, + this.config, + ), + namespace: this.namespace, + value: null, + name: '__name__', + type: new NativeType('string'), + // isInjected: false, + // tsTypeName: 'constant', + // girTypeName: 'constant', + }), 0, ), - ...this.generateConstant( - { - doc: { - text: 'Version of the imported GIR library', - tags: [ - { - text: 'https://gitlab.gnome.org/GNOME/gjs/-/blob/master/gi/ns.cpp#L189', - tagName: 'see', - paramName: '', - }, - ], - }, - name: '__version__', - type: [ + ...this.generateConst( + new IntrospectedConstant({ + doc: printGirDocComment( { - type: 'string', - optional: false, - nullable: false, - callbacks: [], - generics: [], - isArray: false, - isFunction: false, - isCallback: false, + text: 'Version of the imported GIR library', + tags: [ + { + text: 'https://gitlab.gnome.org/GNOME/gjs/-/blob/master/gi/ns.cpp#L189', + tagName: 'see', + paramName: '', + }, + ], }, - ], - isInjected: false, - tsTypeName: 'constant', - girTypeName: 'constant', - }, - girModule.namespace, + this.config, + ), + namespace: this.namespace, + name: '__version__', + type: new NativeType('string'), + value: null, + }), 0, ), ) @@ -1486,27 +1494,22 @@ export class TypeDefinitionGenerator implements Generator { out.push(`export default ${girModule.namespace};`) } - const outResult = out.join('\n') // End of file + const { append, prepend } = await this.moduleTemplateProcessor.load(template, {}, this.config) - if (this.config.outdir) { - await moduleTemplateProcessor.create(template, this.config.outdir, target, true, outResult, {}, this.config) - } else { - const { append, prepend } = await moduleTemplateProcessor.load(template, {}, this.config) - this.log.log(append + '\n' + outResult + prepend) - } + return [append, ...out, prepend] } - protected async exportNPMPackage(moduleTemplateProcessor: TemplateProcessor, girModuleImportName: string) { - await this.exportNPMPackageJson(moduleTemplateProcessor) - await this.exportNPMReadme(moduleTemplateProcessor, girModuleImportName) - await this.exportTSConfig(moduleTemplateProcessor) - await this.exportTypeDoc(moduleTemplateProcessor) + async exportNPMPackage(girModuleImportName: string) { + await this.exportNPMPackageJson() + await this.exportNPMReadme(girModuleImportName) + await this.exportTSConfig() + await this.exportTypeDoc() } - protected async exportNPMPackageJson(moduleTemplateProcessor: TemplateProcessor) { + async exportNPMPackageJson() { const template = 'package.json' if (this.config.outdir) { - await moduleTemplateProcessor.create( + await this.moduleTemplateProcessor.create( template, this.config.outdir, template, // output filename @@ -1516,22 +1519,22 @@ export class TypeDefinitionGenerator implements Generator { this.config, ) } else { - const { append, prepend } = await moduleTemplateProcessor.load(template, {}, this.config) + const { append, prepend } = await this.moduleTemplateProcessor.load(template, {}, this.config) this.log.log(append + prepend) } } - protected async exportNPMReadme(moduleTemplateProcessor: TemplateProcessor, girModuleImportName: string) { + async exportNPMReadme(girModuleImportName: string) { // E.g. `README-GJS.md` or `README-GTK-4.0.md` let template = girModuleImportName ? `README-${girModuleImportName.toUpperCase()}.md` : 'README.md' const outputFilename = 'README.md' - if (!moduleTemplateProcessor.exists(template)) { + if (!this.moduleTemplateProcessor.exists(template)) { template = 'README.md' } if (this.config.outdir) { - await moduleTemplateProcessor.create( + await this.moduleTemplateProcessor.create( template, this.config.outdir, outputFilename, @@ -1541,15 +1544,15 @@ export class TypeDefinitionGenerator implements Generator { this.config, ) } else { - const { append, prepend } = await moduleTemplateProcessor.load(template, {}, this.config) + const { append, prepend } = await this.moduleTemplateProcessor.load(template, {}, this.config) this.log.log(append + prepend) } } - protected async exportTSConfig(moduleTemplateProcessor: TemplateProcessor) { + async exportTSConfig() { const template = 'tsconfig.json' if (this.config.outdir) { - await moduleTemplateProcessor.create( + await this.moduleTemplateProcessor.create( template, this.config.outdir, template, // output filename @@ -1559,15 +1562,15 @@ export class TypeDefinitionGenerator implements Generator { this.config, ) } else { - const { append, prepend } = await moduleTemplateProcessor.load(template, {}, this.config) + const { append, prepend } = await this.moduleTemplateProcessor.load(template, {}, this.config) this.log.log(append + prepend) } } - protected async exportTypeDoc(moduleTemplateProcessor: TemplateProcessor) { + async exportTypeDoc() { const template = 'typedoc.json' if (this.config.outdir) { - await moduleTemplateProcessor.create( + await this.moduleTemplateProcessor.create( template, this.config.outdir, template, // output filename @@ -1577,58 +1580,34 @@ export class TypeDefinitionGenerator implements Generator { this.config, ) } else { - const { append, prepend } = await moduleTemplateProcessor.load(template, {}, this.config) + const { append, prepend } = await this.moduleTemplateProcessor.load(template, {}, this.config) this.log.log(append + prepend) } } - protected async exportModule( - girModule: GirModule, - girModules: GirModule[], - girModulesGrouped: GirModulesGrouped[], - ) { - let pkgData: PackageData | undefined - if (this.packageData) { - pkgData = this.packageData.get(girModule.packageName) - } - const moduleTemplateProcessor = new TemplateProcessor( - { - name: girModule.namespace, - namespace: girModule.namespace, - version: girModule.version, - importName: girModule.importName, - girModule, - girModules, - girModulesGrouped, - pkgData, - }, - girModule.packageName, - girModule.transitiveDependencies, - this.config, - ) - - await this.exportModuleTS(moduleTemplateProcessor, girModule) + async exportModule(registry: NSRegistry, girModule: GirModule) { + await this.exportModuleTS() if (this.config.environment === 'gjs') { - await this.exportModuleAmbientTS(moduleTemplateProcessor, girModule) + await this.exportModuleAmbientTS(girModule) } - await this.exportModuleImportTS(moduleTemplateProcessor, girModule) + await this.exportModuleImportTS(girModule) if (this.config.buildType === 'lib') { - await this.exportModuleJS(moduleTemplateProcessor, girModule) + await this.exportModuleJS(girModule) } if (this.config.package) { this.setOverrideConfigForOtherModuleType() - await this.exportModuleTS(moduleTemplateProcessor, girModule) + await this.exportModuleTS() if (this.config.buildType === 'lib') { - await this.exportModuleJS(moduleTemplateProcessor, girModule) + await this.exportModuleJS(girModule) } this.resetOverrideConfig() } if (this.config.package) { - await this.exportNPMPackage(moduleTemplateProcessor, girModule.importName) + await this.exportNPMPackage(girModule.importName) } } @@ -1636,7 +1615,7 @@ export class TypeDefinitionGenerator implements Generator { * We build both module types if we build an NPM package, * so we need to switch the module type and use the default noNamespace value for the module type */ - protected setOverrideConfigForOtherModuleType() { + setOverrideConfigForOtherModuleType() { if (this.config.moduleType === 'esm') { this.overrideConfig.moduleType = 'cjs' this.overrideConfig.noNamespace = true @@ -1646,24 +1625,68 @@ export class TypeDefinitionGenerator implements Generator { } } - protected resetOverrideConfig() { + resetOverrideConfig() { this.overrideConfig = {} } +} - protected async exportGjs( - dependencies: Dependency[], - girModules: GirModule[], - girModulesGrouped: GirModulesGrouped[], - ) { +export class TypeDefinitionGenerator implements Generator { + log: Logger + dependencyManager: DependencyManager + packageData?: PackageDataParser + + /** Override config, used to override the config temporarily to generate both ESM and CJS for NPM packages */ + overrideConfig: Partial = {} + module!: ModuleGenerator + + /** Get the current config, including the override config */ + get config(): GenerateConfig { + return { ...this._config, ...this.overrideConfig } + } + + /** + * @param _config The config to use without the override config + */ + constructor(readonly _config: GenerateConfig) { + this.log = new Logger(this.config.environment, this.config.verbose, TypeDefinitionGenerator.name) + this.dependencyManager = DependencyManager.getInstance(this.config) + if (this.config.package) { + this.packageData = new PackageDataParser(this.config) + } + } + + /** + * + * @param namespace E.g. 'Gtk' + * @param packageName E.g. 'Gtk-3.0' + * @param asExternType Currently only used for node type imports + */ + generateModuleDependenciesImport(packageName: string): string[] { + const def: string[] = [] + const dep = this.dependencyManager.get(packageName) + + // if (this.config.package) { + // if (this.config.buildType === 'types') { + // // See https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html + // def.push(`/// `) + // } + // } else { + // if (this.config.buildType === 'types') { + // // See https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html + // def.push(`/// `) + // } + // } + + def.push(dep.importDef) + + return def + } + + async exportGjs(dependencies: Dependency[], registry: NSRegistry) { if (!this.config.outdir) return const packageName = 'Gjs' - const templateProcessor = new TemplateProcessor( - { girModules: girModules, girModulesGrouped }, - packageName, - dependencies, - this.config, - ) + const templateProcessor = new TemplateProcessor(registry, packageName, dependencies, this.config) // TS await templateProcessor.create('gjs.d.ts', this.config.outdir, 'gjs.d.ts') @@ -1681,7 +1704,7 @@ export class TypeDefinitionGenerator implements Generator { // If you build an NPM package, we also build the Gjs module for the other module type if (this.config.package) { - this.setOverrideConfigForOtherModuleType() + this.module.setOverrideConfigForOtherModuleType() // TS await templateProcessor.create( 'gjs.d.ts', @@ -1759,7 +1782,7 @@ export class TypeDefinitionGenerator implements Generator { this.config, ) } - this.resetOverrideConfig() + this.module.resetOverrideConfig() } // Import ambient types @@ -1781,24 +1804,28 @@ export class TypeDefinitionGenerator implements Generator { // Package if (this.config.package) { - await this.exportNPMPackage(templateProcessor, 'Gjs') + await this.module.exportNPMPackage('Gjs') } } - public async start(girModules: GirModule[], girModulesGrouped: GirModulesGrouped[] = []) { - this.dependencyManager.addAll(girModules) + public async generate(registry: NSRegistry, module: GirModule) { + this.module = new ModuleGenerator(module, this.config) + console.log('generating...' + module.name) + await this.module.exportModuleTS() + } + + public async start() { + // this.dependencyManager.addAll(girModules) if (this.config.package && this.packageData) { await this.packageData.start() } + } - for (const girModule of girModules) { - await this.exportModule(girModule, girModules, girModulesGrouped) - } - + public async finish(registry: NSRegistry) { if (this.config.environment === 'gjs') { // GJS internal stuff - await this.exportGjs(this.dependencyManager.core(), girModules, girModulesGrouped) + await this.exportGjs(this.dependencyManager.core(), registry) } } } diff --git a/packages/lib/src/dependency-manager.ts b/packages/lib/src/dependency-manager.ts index 94b382f5e..9b4593c14 100644 --- a/packages/lib/src/dependency-manager.ts +++ b/packages/lib/src/dependency-manager.ts @@ -4,10 +4,12 @@ import { Transformation } from './transformation.js' import type { Dependency, GenerateConfig, GirInclude } from './types/index.js' import type { GirModule } from './gir-module.js' +import { GirNSRegistry } from './newlib/lib.js' export class DependencyManager { protected log: Logger protected transformation: Transformation + registry = new GirNSRegistry() cache: { [packageName: string]: Dependency } = {} diff --git a/packages/lib/src/gir-module.ts b/packages/lib/src/gir-module.ts index 726bc9ed1..52dc0c702 100644 --- a/packages/lib/src/gir-module.ts +++ b/packages/lib/src/gir-module.ts @@ -1,91 +1,68 @@ // TODO move this class into a web-worker? https://www.npmjs.com/package/web-worker - -import { - Transformation, - FULL_TYPE_MAP, - PRIMITIVE_TYPE_MAP, - ARRAY_TYPE_MAP, - IGNORE_GIR_TYPE_TS_DOC_TYPES, -} from './transformation.js' -import { STATIC_NAME_ALREADY_EXISTS, MAX_CLASS_PARENT_DEPTH } from './constants.js' +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Transformation, IGNORE_GIR_TYPE_TS_DOC_TYPES } from './transformation.js' import { Logger } from './logger.js' import { Injector } from './injection/injector.js' import { GirFactory } from './gir-factory.js' import { ConflictResolver } from './conflict-resolver.js' import { DependencyManager } from './dependency-manager.js' -import { - NO_TSDATA, - WARN_NOT_FOUND_TYPE, - WARN_CONSTANT_ALREADY_EXPORTED, - WARN_DUPLICATE_SYMBOL, - WARN_DUPLICATE_PARAMETER, - WARN_DUPLICATE_ENUM_IDENTIFIER, -} from './messages.js' -import { isEqual, find, clone, girBool, removeNamespace, addNamespace, girElementIsIntrospectable } from './utils.js' +import { NO_TSDATA } from './messages.js' +import { find, girBool } from './utils.js' import { SymTable } from './symtable.js' import { LibraryVersion } from './library-version.js' -import { GirDirection } from './types/index.js' import type { Dependency, GirRepository, - GirNamespace, GirAliasElement, - GirEnumElement, - GirMemberElement, GirFunctionElement, - GirClassElement, GirArrayType, GirType, - GirCallableParams, GirCallableParamElement, GirVirtualMethodElement, GirSignalElement, GirCallableReturn, - GirRecordElement, GirCallbackElement, GirConstantElement, - GirBitfieldElement, GirFieldElement, GirMethodElement, GirPropertyElement, - GirAnyElement, - GirUnionElement, - GirInstanceParameter, - GirInterfaceElement, GirConstructorElement, GirDocElement, - TypeGirVariable, - TypeGirClass, - TypeGirEnumerationMember, - LocalNameCheck, - LocalNameType, - LocalName, - LocalNames, TsDoc, TsDocTag, - TsClass, - TsMethod, - TsFunction, - TsProperty, - TsVar, - TsParameter, - TsInstanceParameter, - TsCallbackInterface, - TsMember, - TsEnum, - TsAlias, - TsType, - TsGenericParameter, - TsCallback, InheritanceTable, ParsedGir, + GirInfoAttrs, GenerateConfig, - ClassParent, - InjectionParameter, - TsSignal, - PromisifyFunc, + GirNSMember, } from './types/index.js' +import { + ClosureType, + TypeIdentifier, + PromiseType, + VoidType, + BooleanType, + TupleType, + BinaryType, + NullableType, + ObjectType, +} from './newlib/gir.js' +import { IntrospectedAlias } from './newlib/gir/alias.js' +import { IntrospectedBase, IntrospectedNamespaceMember } from './newlib/gir/base.js' +import { + IntrospectedBaseClass, + IntrospectedClass, + IntrospectedRecord, + IntrospectedInterface, +} from './newlib/gir/class.js' +import { IntrospectedConstant } from './newlib/gir/const.js' +import { IntrospectedEnum, IntrospectedError } from './newlib/gir/enum.js' +import { IntrospectedFunction, IntrospectedCallback, IntrospectedClassCallback } from './newlib/gir/function.js' +import { NSRegistry } from './newlib/gir/registry.js' +import { isPrimitiveType } from './newlib/gir/util.js' +import { LoadOptions } from './newlib/types.js' +import { GirVisitor } from './newlib/visitor.js' export class GirModule { /** @@ -95,28 +72,28 @@ export class GirModule { /** * E.g. 'Gtk' */ - namespace: string + namespace!: string /** * E.g. '4.0' */ - version = '0.0' + version: string /** * E.g. 'Gtk-4.0' */ - packageName: string + packageName!: string /** * E.g. 'Gtk40' * Is used in the generated index.d.ts, for example: `import * as Gtk40 from "./Gtk-4.0.js";` */ - importNamespace: string + importNamespace!: string - importName: string + importName!: string /** * The version of the library as an object. * E.g. `{ major: 4, minor: 0, patch: 0 }` or as string `4.0.0`' */ - libraryVersion: LibraryVersion + libraryVersion!: LibraryVersion dependencies: Dependency[] = [] private _transitiveDependencies: Dependency[] = [] @@ -133,24 +110,24 @@ export class GirModule { return [...new Set([...this.dependencies, ...this.transitiveDependencies])] } - repo: GirRepository - ns: GirNamespace = { $: { name: '', version: '0.0' } } + repo!: GirRepository + ns: GirModule /** * Used to find namespaces that are used in other modules */ - symTable: SymTable + symTable!: SymTable - transformation: Transformation + transformation!: Transformation girFactory = new GirFactory() dependencyManager: DependencyManager - conflictResolver: ConflictResolver + conflictResolver!: ConflictResolver - log: Logger + log!: Logger - inject: Injector + inject!: Injector extends?: string @@ -160,30 +137,33 @@ export class GirModule { */ constNames: { [varName: string]: GirConstantElement } = {} - constructor( - xml: ParsedGir, - private readonly config: GenerateConfig, - ) { - this.repo = xml.repository[0] + readonly name: string + readonly c_prefixes: string[] - if (!this.repo.namespace || !this.repo.namespace.length) { - throw new Error(`Namespace not found!`) - } + private imports: Map = new Map() + default_imports: Map = new Map() + + private _members?: Map + private _enum_constants?: Map + private _resolve_names: Map = new Map() + __dts__references?: string[] + + package_version!: readonly [string, string] | readonly [string, string, string] + parent!: NSRegistry + config: GenerateConfig + + constructor(repo: GirRepository, name: string, version: string, prefixes: string[], config: GenerateConfig) { + this.name = name + this.version = version + this.c_prefixes = [...prefixes] + this.package_version = ['0', '0'] + this.config = config + this.repo = repo this.dependencyManager = DependencyManager.getInstance(this.config) this.dependencies = this.dependencyManager.fromGirIncludes(this.repo.include || []) - this.ns = this.repo.namespace[0] - this.namespace = this.ns.$.name - this.version = this.ns.$.version - this.packageName = `${this.namespace}-${this.version}` - this.libraryVersion = new LibraryVersion(this.ns.constant, this.version) - this.transformation = new Transformation(config) - this.log = new Logger(config.environment, config.verbose, this.packageName || 'GirModule') - this.conflictResolver = new ConflictResolver(config.environment, config.verbose) - this.inject = new Injector(this.config.environment) - this.importNamespace = this.transformation.transformModuleNamespaceName(this.packageName) - this.importName = this.transformation.transformImportName(this.packageName) - this.symTable = new SymTable(this.config, this.packageName, this.namespace) + + this.ns = this } private checkTransitiveDependencies(transitiveDependencies: Dependency[]) { @@ -354,32 +334,6 @@ export class GirModule { } } - /** - * Functions and methods of a class - */ - private annotateMethods( - girClass: GirClassElement | GirRecordElement | GirInterfaceElement, - girFuncs: - | GirMethodElement[] - | GirFunctionElement[] - | GirConstructorElement[] - | GirVirtualMethodElement[] - | GirSignalElement[], - ): void { - if (Array.isArray(girFuncs)) - for (const girFunc of girFuncs) { - if (girFunc?.$?.name) { - // girFunc._girType = girType - // girFunc._tsType = tsType - girFunc._class = girClass - const nsName = girClass ? girClass._fullSymName : this.namespace - if (nsName) girFunc._fullSymName = `${nsName}.${girFunc.$.name}` - this.annotateFunctionArguments(girFunc) - this.annotateFunctionReturn(girFunc) - } - } - } - /** * Variables which are not part of a class */ @@ -392,380 +346,118 @@ export class GirModule { } } - private annotateFields( - girClass: GirClassElement | GirRecordElement | GirInterfaceElement | null, - girVars: GirPropertyElement[], - ): void - - private annotateFields( - girClass: GirClassElement | GirRecordElement | GirInterfaceElement | null, - girVars: GirFieldElement[], - ): void - - /** - * Fields are variables which are part of a class - * @see https://www.typescriptlang.org/docs/handbook/2/classes.html#fields - */ - private annotateFields( - girClass: GirClassElement | GirRecordElement | GirInterfaceElement, - girVars: GirPropertyElement[] | GirFieldElement[], - ): void { - for (const girVar of girVars) { - const nsName = girClass ? girClass._fullSymName : this.namespace - girVar._module = this - if (girClass) { - girVar._class = girClass - } - - if (girVar.$ && girVar.$.name && nsName) { - girVar._fullSymName = `${nsName}.${girVar.$.name}` - } - } - } - - private annotateClass(girClass: GirClassElement, girTypeName: 'class'): void - private annotateClass(girClass: GirRecordElement, girTypeName: 'record'): void - private annotateClass(girClass: GirInterfaceElement, girTypeName: 'interface'): void - - private annotateClass(girClass: GirClassElement | GirRecordElement | GirInterfaceElement) { - girClass._module = this - girClass._fullSymName = `${this.namespace}.${girClass.$.name}` - - const constructors = Array.isArray(girClass.constructor) ? girClass.constructor : [] - const signals = ((girClass as GirClassElement | GirInterfaceElement).signal || - girClass['glib:signal'] || - []) as GirSignalElement[] - const functions = girClass.function || [] - const methods = girClass.method || [] - const vMethods = (girClass as GirClassElement)['virtual-method'] || new Array() - const properties = girClass.property - const fields = girClass.field - - this.annotateMethods(girClass, constructors) - this.annotateMethods(girClass, functions) - this.annotateMethods(girClass, methods) - this.annotateMethods(girClass, vMethods) - this.annotateMethods(girClass, signals) - if (properties) this.annotateFields(girClass, properties) - if (fields) this.annotateFields(girClass, fields) - } - - /** - * Annotates the custom `_module`, `_fullSymName` and `_girType` properties. - * Also registers the element on the `symTable`. - * @param girElements - * @param girType - */ - private annotateAndRegisterGirElement(girElements: GirAnyElement[]): void { - for (const girElement of girElements) { - if (girElement?.$ && girElement.$.name) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument - if (!girElementIsIntrospectable(girElement as any)) continue - - girElement._module = this - girElement._fullSymName = `${this.namespace}.${girElement.$.name}` - if (this.symTable.get(this.allDependencies, girElement._fullSymName)) { - this.log.warn(WARN_DUPLICATE_SYMBOL(girElement._fullSymName)) - } - - this.symTable.set(this.allDependencies, girElement._fullSymName, girElement) - } - } - } - - /** - * TODO: find better name for this method - * @param girVar - * @param fullTypeName - * @returns e.g. - * ```ts - * { - * namespace: "Gtk", - * resValue: "Gtk.Widget" - * } - * - */ - private fullTypeLookup( - girVar: - | GirCallableParamElement - | GirCallableReturn - | GirFieldElement - | GirAliasElement - | GirPropertyElement - | GirConstantElement, - fullTypeName: string | null, - ) { - let resValue = '' - let namespace = '' - - if (!fullTypeName) { - return { - value: resValue, - fullTypeName, - namespace, - } - } - - // Fully qualify our type name - if (!fullTypeName.includes('.')) { - let tryFullTypeName = '' - - if (!resValue && girVar._module && girVar._module !== this) { - tryFullTypeName = `${girVar._module.namespace}.${fullTypeName}` - resValue = this.fullTypeLookupWithNamespace(tryFullTypeName).value - if (resValue) { - fullTypeName = tryFullTypeName - namespace = girVar._module.namespace - } - } - - if (!resValue) { - tryFullTypeName = `${this.namespace}.${fullTypeName}` - resValue = this.fullTypeLookupWithNamespace(tryFullTypeName).value - if (resValue) { - fullTypeName = tryFullTypeName - namespace = this.namespace - } - } - - const girClass = ( - girVar as - | GirCallableParamElement - | GirCallableReturn - | GirFieldElement - | GirAliasElement - | GirPropertyElement - )._class as GirClassElement | undefined - - if (!resValue && girClass?._module?.namespace && girClass._module !== this) { - tryFullTypeName = `${girClass._module.namespace}.${fullTypeName}` - resValue = this.fullTypeLookupWithNamespace(tryFullTypeName).value - if (resValue) { - fullTypeName = tryFullTypeName - namespace = girClass?._module?.namespace - } - } - } - - if (!resValue && fullTypeName) { - resValue = this.fullTypeLookupWithNamespace(fullTypeName).value - } - - return { - value: resValue, - namespace, - } - } + // /** + // * Annotates the custom `_module`, `_fullSymName` and `_girType` properties. + // * Also registers the element on the `symTable`. + // * @param girElements + // * @param girType + // */ + // private annotateAndRegisterGirElement(girElements: GirAnyElement[]): void { + // for (const girElement of girElements) { + // if (girElement?.$ && girElement.$.name) { + // // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument + // if (!girElementIsIntrospectable(girElement as any)) continue + + // girElement._module = this + // girElement._fullSymName = `${this.namespace}.${girElement.$.name}` + // if (this.symTable.get(this.allDependencies, girElement._fullSymName)) { + // this.log.warn(WARN_DUPLICATE_SYMBOL(girElement._fullSymName)) + // } + + // this.symTable.set(this.allDependencies, girElement._fullSymName, girElement) + // } + // } + // } + + // /** + // * TODO: find better name for this method + // * @param girVar + // * @param fullTypeName + // * @returns e.g. + // * ```ts + // * { + // * namespace: "Gtk", + // * resValue: "Gtk.Widget" + // * } + // * + // */ + // private fullTypeLookup( + // girVar: + // | GirCallableParamElement + // | GirCallableReturn + // | GirFieldElement + // | GirAliasElement + // | GirPropertyElement + // | GirConstantElement, + // fullTypeName: string | null, + // ) { + // let resValue = '' + // let namespace = '' + + // if (!fullTypeName) { + // return { + // value: resValue, + // fullTypeName, + // namespace, + // } + // } + + // // Fully qualify our type name + // if (!fullTypeName.includes('.')) { + // let tryFullTypeName = '' + + // if (!resValue && girVar._module && girVar._module !== this) { + // tryFullTypeName = `${girVar._module.namespace}.${fullTypeName}` + // resValue = this.fullTypeLookupWithNamespace(tryFullTypeName).value + // if (resValue) { + // fullTypeName = tryFullTypeName + // namespace = girVar._module.namespace + // } + // } + + // if (!resValue) { + // tryFullTypeName = `${this.namespace}.${fullTypeName}` + // resValue = this.fullTypeLookupWithNamespace(tryFullTypeName).value + // if (resValue) { + // fullTypeName = tryFullTypeName + // namespace = this.namespace + // } + // } + + // const girClass = ( + // girVar as + // | GirCallableParamElement + // | GirCallableReturn + // | GirFieldElement + // | GirAliasElement + // | GirPropertyElement + // )._class as GirClassElement | undefined + + // if (!resValue && girClass?._module?.namespace && girClass._module !== this) { + // tryFullTypeName = `${girClass._module.namespace}.${fullTypeName}` + // resValue = this.fullTypeLookupWithNamespace(tryFullTypeName).value + // if (resValue) { + // fullTypeName = tryFullTypeName + // namespace = girClass?._module?.namespace + // } + // } + // } + + // if (!resValue && fullTypeName) { + // resValue = this.fullTypeLookupWithNamespace(fullTypeName).value + // } + + // return { + // value: resValue, + // namespace, + // } + // } /** * this method needs to be refactored, an array can also be an array of an array for example * @param girVar * @returns */ - getArrayData( - girVar: - | GirCallableReturn - | GirAliasElement - | GirFieldElement - | GirCallableParamElement - | GirPropertyElement - | GirConstantElement, - ) { - let arrayType: GirType | null = null - let arrCType: string | undefined - let isArray = false - let overrideTypeName: string | undefined - let typeArray: GirType[] | undefined - - let collection: GirArrayType[] | GirType[] | undefined - - if ((girVar as GirCallableReturn | GirFieldElement).array) { - collection = (girVar as GirCallableReturn | GirFieldElement).array - } else if (/^GLib.S?List$/.test(girVar.type?.[0].$?.name || '')) { - // This converts GLib.List / GLib.SList to T[] - collection = girVar.type - } - - if (collection && collection.length > 0) { - isArray = true - typeArray = collection[0].type - if (collection[0].$) { - const ea = collection[0].$ - arrCType = ea['c:type'] - } - } - - if (typeArray && typeArray?.length > 0) { - arrayType = typeArray[0] - } - - if (this.config.environment == 'gjs' && isArray && arrayType?.$?.name && ARRAY_TYPE_MAP[arrayType.$.name]) { - isArray = false - overrideTypeName = ARRAY_TYPE_MAP[arrayType.$.name] as string | undefined - } - - return { - arrCType, - arrayType, - isArray, - overrideTypeName, - } - } - - /** - * Get the typescript type of a GirElement like a `GirPropertyElement` or `GirCallableReturn` - * @param girVar - */ - private getTsType( - girVar: - | GirCallableReturn - | GirAliasElement - | GirFieldElement - | GirCallableParamElement - | GirPropertyElement - | GirConstantElement, - tsClass: TsClass | null, - defaults: Partial = {}, - ) { - let type: GirType | undefined = girVar.type?.[0] - let fullTypeName: string | null = null - let typeName = defaults.type || '' - let isFunction = defaults.isFunction || false - let isCallback = defaults.isCallback || false - const nullable = this.typeIsNullable(girVar) || defaults.nullable || false - const optional = this.typeIsOptional(girVar) || defaults.optional || false - - const girCallbacks: GirCallbackElement[] = [] - const array = this.getArrayData(girVar) - - if (array.overrideTypeName) { - typeName = array.overrideTypeName - } - - if (array.arrayType) { - type = array.arrayType - } - - const cType = type?.$ ? type.$['c:type'] : array.arrCType - fullTypeName = type?.$?.name || null - const callbacks = (girVar as GirFieldElement).callback - - if (!typeName && callbacks?.length) { - for (const girCallback of callbacks) { - if (!girElementIsIntrospectable(girCallback)) continue - - if (!girCallback._tsData) { - const tsCallback = this.getFunctionTsData(girCallback, 'callback', tsClass, { - isStatic: false, - isArrowType: true, - isGlobal: false, - isVirtual: false, - returnType: null, - generics: [], - }) - - if (!tsCallback) continue - - girCallback._tsData = { - ...tsCallback, - girTypeName: 'callback', - tsTypeName: this.girFactory.girTypeNameToTsTypeName('callback', false), - tsCallbackInterface: this.getCallbackInterfaceTsData(girCallback), - doc: this.getTsDoc(girCallback), - overloads: [], - } - } - - if (girCallback._tsData) { - girCallbacks.push(girCallback) - isFunction = true - isCallback = true - } - } - } - - if (!isFunction) { - const res = this.fullTypeLookup(girVar, fullTypeName) - if (res.value) { - typeName = res.value - fullTypeName = typeName - } - } - - if (!typeName && type?.$?.name && PRIMITIVE_TYPE_MAP[type.$.name]) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - typeName = PRIMITIVE_TYPE_MAP[type.$.name] - } - - if (cType) { - const parsedCType = PRIMITIVE_TYPE_MAP[cType] as string | undefined - if (!typeName && parsedCType) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - typeName = parsedCType - } - } - - if (!typeName) { - typeName = 'any' - const logName = cType || fullTypeName || girVar.$.name || '' - this.log.warn(WARN_NOT_FOUND_TYPE(logName)) - } - - // TODO: transform array to type with generics? - const tsType = this.girFactory.newTsType({ - ...defaults, - type: typeName, - optional, - nullable, - callbacks: girCallbacks, - isArray: array.isArray, - isFunction, - isCallback, - }) - - return tsType - } - - /** - * - * @param girFunc - * @returns - */ - private getReturnType( - girFunc: - | GirMethodElement - | GirFunctionElement - | GirConstructorElement - | GirCallbackElement - | GirSignalElement - | GirVirtualMethodElement, - tsClass: TsClass | null, - generics: TsGenericParameter[] = [], - ) { - let outArrayLengthIndex = -1 - - if (girFunc['return-value'] && girFunc['return-value'].length > 1) { - throw new Error('Several return values found!') - } - - // There are no multiple return values, so we always use index 0 - const girVar = girFunc['return-value']?.[0] || null - - // We still use an array to allow multiple return values for later - const returnTypes: TsType[] = [] - - if (girVar) { - returnTypes.push(this.getTsType(girVar, tsClass, { generics })) - - outArrayLengthIndex = girVar.array && girVar.array[0].$?.length ? Number(girVar.array[0].$.length) : -1 - } else { - returnTypes.push(this.girFactory.newTsType({ type: 'void', generics })) - } - - const retTypeIsVoid = returnTypes.length === 1 && returnTypes[0].type === 'void' - - return { returnTypes, outArrayLengthIndex, retTypeIsVoid } - } private arrayLengthIndexLookup(girVar: GirCallableParamElement): number { if (!girVar.array) return -1 @@ -866,2084 +558,857 @@ export class GirModule { } } - private setParameterTsData( - girParam: GirCallableParamElement, - girParams: GirCallableParamElement[], - paramNames: string[], - skip: GirCallableParamElement[], - parent: TsFunction | TsSignal, - ) { - // I think it's safest to force inout params to have the - // same type for in and out - const tsType = this.getTsType(girParam, parent.parent) - // const optDirection = girParam.$.direction - - if (girParam._tsData) { - // this.log.warn('[setParameterTsData] _tsData already set!') - return girParam._tsData - } + // private setParameterTsData( + // girParam: GirCallableParamElement, + // girParams: GirCallableParamElement[], + // paramNames: string[], + // skip: GirCallableParamElement[], + // parent: TsFunction | TsSignal, + // ) { + // // I think it's safest to force inout params to have the + // // same type for in and out + // const tsType = this.getTsType(girParam, parent.parent) + // // const optDirection = girParam.$.direction + + // if (girParam._tsData) { + // // this.log.warn('[setParameterTsData] _tsData already set!') + // return girParam._tsData + // } + + // if (tsType.isCallback) { + // throw new Error('Callback type is not implemented here') + // } + + // let paramName = this.transformation.transformParameterName(girParam, false) + + // if (paramNames.includes(paramName)) { + // this.log.warn(WARN_DUPLICATE_PARAMETER(paramName, girParam._fullSymName)) + // paramName += '_' + // } + // paramNames.push(paramName) + + // // In Typescript no optional parameters are allowed if the following ones are not optional + // if (tsType.optional) { + // const index = girParams.indexOf(girParam) + // const following = girParams + // .slice(index) + // .filter(() => skip.indexOf(girParam) === -1) + // .filter((p) => p.$.direction !== GirDirection.Out) + + // if (following.some((p) => !this.typeIsOptional(p))) { + // tsType.optional = false + // } + // } + + // const tsParam: TsParameter = { + // name: paramName, + // type: [tsType], + // isRest: false, + // girTypeName: 'callable-param', + // doc: this.getTsDoc(girParam), + // parent, + // } + + // girParam._tsData = tsParam + + // // // TODO: remove this, wee need a special solution for Gio.AsyncReadyCallback instead + // girParam = this.inject.toParameterType(girParam) + + // return girParam._tsData + // } + + // private setParametersTsData( + // outArrayLengthIndex: number, + // girParams: GirCallableParams[] = [], + // parent: TsFunction | TsCallback, + // ) { + // const outParams: GirCallableParamElement[] = [] + // const inParams: GirCallableParamElement[] = [] + // const paramNames: string[] = [] + // const instanceParameters: GirInstanceParameter[] = [] + + // if (girParams && girParams.length > 0) { + // for (const girParam of girParams) { + // const params = girParam?.parameter || [] + + // // Instance parameter needs to be exposed for class methods (see comment above getClassMethods()) + // const instanceParameter = girParams[0]['instance-parameter']?.[0] + // if (instanceParameter) { + // } + // if (params.length) { + // const skip = outArrayLengthIndex === -1 ? [] : [params[outArrayLengthIndex]] + + // this.processParams(params, skip, (girVar) => this.arrayLengthIndexLookup(girVar)) + // this.processParams(params, skip, (girVar) => this.closureDataIndexLookup(girVar)) + // this.processParams(params, skip, (girVar) => this.destroyDataIndexLookup(girVar)) + + // for (const param of params) { + // if (skip.includes(param)) { + // continue + // } + + // param._tsData = this.setParameterTsData(param, params, paramNames, skip, parent) + + // const optDirection = param.$.direction + // if ( + // optDirection === GirDirection.Out || + // optDirection === GirDirection.Inout || + // optDirection === GirDirection.InOut + // ) { + // outParams.push(param) + // if (optDirection === GirDirection.Out) continue + // } + // inParams.push(param) + // } + // } + // } + // } + + // return { outParams, paramNames, inParams, instanceParameters } + // } + + // overloadPromisifiedFunctions(girFunctions: GirFunctionElement[]): void { + // if (!this.config.promisify) return + + // const promisifyAsyncReturn = ['Gio.AsyncReadyCallback', 'AsyncReadyCallback'] + // const promisifyFuncMap = {} as { [name: string]: PromisifyFunc } + + // // Find the functions that can be promisified + // for (const girFunction of girFunctions) { + // const tsFunction = girFunction._tsData + // if (!tsFunction) continue + + // // Check if function name satisfies async,finish scheme + // const isAsync = tsFunction.name.endsWith('_async') || tsFunction.name.endsWith('_begin') + // const isFinish = tsFunction.name.endsWith('_finish') + // if (!isAsync && !isFinish) continue + + // // Handle async functions + // if (isAsync) { + // if (tsFunction.inParams.length === 0) continue + // const lastParam = tsFunction.inParams[tsFunction.inParams.length - 1] + // if (lastParam.type && lastParam.type.length > 0) { + // const type = lastParam.type[0].$.name + // if (type && promisifyAsyncReturn.includes(type)) { + // if (!(tsFunction.name in promisifyFuncMap)) promisifyFuncMap[tsFunction.name] = {} + // promisifyFuncMap[tsFunction.name].asyncFn = tsFunction + // } + // } + // } + + // // Handle finish functions + // if (isFinish) { + // if (tsFunction.returnTypes.length === 0) continue + // let name = `${tsFunction.name.replace(/(_finish)$/, '')}_async` + // if (!(name in promisifyFuncMap)) name = `${tsFunction.name.replace(/(_finish)$/, '')}_begin` + // if (!(name in promisifyFuncMap)) promisifyFuncMap[name] = {} + // promisifyFuncMap[name].finishFn = tsFunction + // } + // } + + // // Generate TsFunctions for promisify-able functions and add to the array + // for (const [, func] of Object.entries(promisifyFuncMap)) { + // if (!func.asyncFn || !func.finishFn) continue + + // const inParams = this.girFactory.newGirCallableParamElements( + // func.asyncFn.inParams.slice(0, -1), + // func.asyncFn, + // ) + + // const outParams = this.girFactory.newGirCallableParamElements(func.finishFn.outParams, func.asyncFn) + + // const returnTypes = this.girFactory.newTsTypes(outParams.length > 0 ? [] : func.finishFn.returnTypes) + + // let docReturnText = func.finishFn.doc.tags.find((tag) => tag.tagName === 'returns')?.text || '' + // if (docReturnText) { + // docReturnText = `A Promise of: ${docReturnText}` + // } else { + // docReturnText = `A Promise of the result of {@link ${func.asyncFn.name}}` + // } + + // const docText = `Promisified version of {@link ${func.asyncFn.name}}\n\n${func.asyncFn.doc.text}` + + // const docTags = func.asyncFn.doc.tags.filter( + // (tag) => tag.paramName !== 'callback' && tag.paramName !== 'returns', + // ) + + // docTags.push({ + // tagName: 'returns', + // text: docReturnText, + // paramName: '', + // }) + + // const doc = this.girFactory.newTsDoc({ + // text: docText, + // tags: docTags, + // }) + + // const promisifyFn = this.girFactory.newTsFunction( + // { + // ...func.asyncFn, + // inParams, + // outParams, + // returnTypes, + // isPromise: true, + // doc, + // }, + // func.asyncFn.parent, + // ) + + // func.asyncFn.overloads.push(promisifyFn) + // } + // } + + // private getConstantTsData(girConst: GirConstantElement, tsClass: TsClass | null) { + // if (!girElementIsIntrospectable(girConst)) return undefined + + // if (girConst._tsData) { + // // this.log.warn('[getConstantTsData] _tsData already set!') + // return girConst._tsData + // } + + // let tsData: TsVar | undefined = this.getVariableTsData( + // girConst, + // 'constant', + // 'constant', + // tsClass, + // false, + // false, + // false, + // ) + // if (tsData?.name) { + // if (!this.constNames[tsData.name]) { + // this.constNames[tsData.name] = girConst + // } else { + // this.log.warn(WARN_CONSTANT_ALREADY_EXPORTED(tsData.name)) + // tsData = undefined + // } + // } + // return tsData + // } + + // private getClassConstructPropsTsData( + // girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, + // constructPropNames: LocalNames, + // ) { + // const constructProps: GirPropertyElement[] = [] + // const girProperties = (girClass as GirClassElement | GirInterfaceElement).property + // if (!girProperties?.length) { + // return constructProps + // } + // for (const girProp of girProperties) { + // if (!girElementIsIntrospectable(girProp) || !girProp.$.name) continue + // // Do not modify the original girProp, create a new one by clone `girProp` to `girConstrProp` + // const girConstrProp = clone(girProp) + + // if (!girClass._tsData) continue + + // if (!girConstrProp._tsData) { + // girConstrProp._tsData = this.getPropertyTsData( + // girConstrProp, + // 'property', + // 'constructor-property', + // girClass._tsData, + // true, + // true, + // true, + // 0, + // ) + // } + + // if (!girConstrProp._tsData) { + // continue + // } + + // const localName = this.checkOrSetLocalName(girConstrProp, constructPropNames, 'property') + + // if (!localName?.added) { + // continue + // } + + // if (girConstrProp._fullSymName) + // this.symTable.set(this.allDependencies, girConstrProp._fullSymName, girConstrProp) + // constructProps.push(girConstrProp) + // } + + // return constructProps + // } + + // /** + // * Some class/static methods are defined in a separate record which is not + // * exported, but the methods are available as members of the JS constructor. + // * In gjs one can use an instance of the object, a JS constructor or a GType + // * as the method's instance-parameter. + // * @see https://discourse.gnome.org/t/using-class-methods-like-gtk-widget-class-get-css-name-from-gjs/4001 + // * @param girClass + // */ + // private getClassRecordMethods( + // girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, + // ): GirMethodElement[] { + // const girMethods: GirMethodElement[] = [] + + // if (!girClass.$.name) return girMethods + // const fName = girClass.$.name + 'Class' + // let rec = this.ns.record?.find((r) => r.$.name == fName) + // if (!rec || !this.isGtypeStructFor(girClass, rec)) { + // rec = this.ns.record?.find((r) => this.isGtypeStructFor(girClass, r)) + // fName == rec?.$.name + // } + // if (!rec) return girMethods + + // // Record methods + // const methods = rec.method || [] + + // for (const girMethod of methods) { + // if (!girElementIsIntrospectable(girMethod) || !girClass._tsData) continue + + // if (!girMethod._tsData) + // girMethod._tsData = this.getFunctionTsData(girMethod, 'static-function', girClass._tsData, { + // isStatic: true, + // isArrowType: false, + // isGlobal: false, + // isVirtual: false, + // returnType: null, + // generics: [], + // }) + + // if (!girMethod._tsData) continue + + // if (girMethod._tsData) { + // if (girMethod._fullSymName) this.symTable.set(this.allDependencies, girMethod._fullSymName, girMethod) + // girMethods.push(girMethod) + // } + // } + + // this.overloadPromisifiedFunctions(methods) + + // return girMethods + // } + + // private getGObjectProperties(girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement) { + // const girProperties: GirPropertyElement[] = [] + // if (girClass._fullSymName && !STATIC_NAME_ALREADY_EXISTS.includes(girClass._fullSymName)) { + // // Records, classes and interfaces all have a static name + // const type = this.girFactory.newTsType({ type: 'string' }) + // const staticNameProp = this.girFactory.newGirProperty({ + // isStatic: true, + // name: 'name', + // type: [type], + // girTypeName: 'property', + // }) + // girProperties.push(staticNameProp) + // } + + // if (girClass._tsData?.isDerivedFromGObject && girClass._module) { + // if (this.config.environment === 'gjs') { + // const type = this.girFactory.newTsType({ + // // TODO: Type not as string + // type: 'GObject.GType', + // generics: this.girFactory.newGenerics([ + // { + // value: girClass._tsData.name, + // }, + // ]), + // }) + // const staticGTypeProp = this.girFactory.newGirProperty({ + // isStatic: true, + // name: '$gtype', + // type: [type], + // girTypeName: 'property', + // }) + // girProperties.push(staticGTypeProp) + // } + // } + + // return girProperties + // } + + getClassParentObject(parentName: string, namespace: string, type: 'parent' | 'prerequisite' | 'implements') { + let qualifiedParentName: string + let parentModName: string - if (tsType.isCallback) { - throw new Error('Callback type is not implemented here') + // WORKAROUND: Fix wrong parent names + if ( + (this.packageName === 'GstAudio-0.10' || this.packageName === 'ClutterGst-1.0') && + (parentName === 'GstBase.BaseTransform' || + parentName === 'GstBase.BaseSink' || + parentName === 'GstBase.PushSrc') + ) { + const rename = parentName.replace('GstBase.', 'Gst.') + this.log.warn(`[getClassParentObject] Rename parent class "${parentName}" -> "${rename}"`) + parentName = rename } - let paramName = this.transformation.transformParameterName(girParam, false) - - if (paramNames.includes(paramName)) { - this.log.warn(WARN_DUPLICATE_PARAMETER(paramName, girParam._fullSymName)) - paramName += '_' - } - paramNames.push(paramName) - - // In Typescript no optional parameters are allowed if the following ones are not optional - if (tsType.optional) { - const index = girParams.indexOf(girParam) - const following = girParams - .slice(index) - .filter(() => skip.indexOf(girParam) === -1) - .filter((p) => p.$.direction !== GirDirection.Out) - - if (following.some((p) => !this.typeIsOptional(p))) { - tsType.optional = false - } + if (parentName === 'GraniteServicesSettingsSerializable') { + parentName = 'ServicesSettingsSerializable' + this.log.warn( + `[getClassParentObject] Rename parent class "GraniteServicesSettingsSerializable" -> "ServicesSettingsSerializable"`, + ) } - const tsParam: TsParameter = { - name: paramName, - type: [tsType], - isRest: false, - girTypeName: 'callable-param', - doc: this.getTsDoc(girParam), - parent, + if (parentName.indexOf('.') < 0) { + qualifiedParentName = namespace + '.' + parentName + parentModName = namespace + } else { + qualifiedParentName = parentName + const split = parentName.split('.') + parentName = split[split.length - 1] + parentModName = split.slice(0, split.length - 1).join('.') } + const localParentName = parentModName == namespace ? parentName : qualifiedParentName - girParam._tsData = tsParam + const dependencyExists = !!this.symTable.get(this.allDependencies, qualifiedParentName) - // // TODO: remove this, wee need a special solution for Gio.AsyncReadyCallback instead - girParam = this.inject.toParameterType(girParam) + // todo + } - return girParam._tsData + registerResolveName(resolveName: string, namespace: string, name: string) { + this._resolve_names.set(resolveName, new TypeIdentifier(name, namespace)) } - private getInstanceParameterTsData(instanceParameter: GirInstanceParameter): TsInstanceParameter | undefined { - const typeName = instanceParameter.type?.[0]?.$?.name || undefined - const rec = typeName ? this.ns.record?.find((r) => r.$.name == typeName) : undefined - const structFor = rec?.$['glib:is-gtype-struct-for'] - if (structFor && instanceParameter.$.name) { - // TODO: Should use of a constructor, and even of an instance, be discouraged? - return { - name: instanceParameter.$.name, - structFor, - } + get members(): Map { + if (!this._members) { + this._members = new Map() } - return undefined + + return this._members } - private setParametersTsData( - outArrayLengthIndex: number, - girParams: GirCallableParams[] = [], - parent: TsFunction | TsCallback, - ) { - const outParams: GirCallableParamElement[] = [] - const inParams: GirCallableParamElement[] = [] - const paramNames: string[] = [] - const instanceParameters: GirInstanceParameter[] = [] - - if (girParams && girParams.length > 0) { - for (const girParam of girParams) { - const params = girParam?.parameter || [] - - // Instance parameter needs to be exposed for class methods (see comment above getClassMethods()) - const instanceParameter = girParams[0]['instance-parameter']?.[0] - if (instanceParameter) { - instanceParameter._tsData = this.getInstanceParameterTsData(instanceParameter) - if (instanceParameter._tsData) { - instanceParameters.push(instanceParameter) - } - } - if (params.length) { - const skip = outArrayLengthIndex === -1 ? [] : [params[outArrayLengthIndex]] + get enum_constants(): Map { + if (!this._enum_constants) { + this._enum_constants = new Map() + } - this.processParams(params, skip, (girVar) => this.arrayLengthIndexLookup(girVar)) - this.processParams(params, skip, (girVar) => this.closureDataIndexLookup(girVar)) - this.processParams(params, skip, (girVar) => this.destroyDataIndexLookup(girVar)) + return this._enum_constants + } - for (const param of params) { - if (skip.includes(param)) { - continue - } + accept(visitor: GirVisitor) { + for (const key of [...this.members.keys()]) { + const member = this.members.get(key) - param._tsData = this.setParameterTsData(param, params, paramNames, skip, parent) + if (!member) continue - const optDirection = param.$.direction - if ( - optDirection === GirDirection.Out || - optDirection === GirDirection.Inout || - optDirection === GirDirection.InOut - ) { - outParams.push(param) - if (optDirection === GirDirection.Out) continue - } - inParams.push(param) - } - } + if (Array.isArray(member)) { + this.members.set( + key, + member.map((m) => { + return m.accept(visitor) + }), + ) + } else { + this.members.set(key, member.accept(visitor)) } } - return { outParams, paramNames, inParams, instanceParameters } + return this } - private getVariableTsData( - girVar: GirPropertyElement, - girTypeName: 'property', - tsTypeName: 'property' | 'constructor-property' | 'static-property', - tsClass: TsClass | null, - optional: boolean, - nullable: boolean, - allowQuotes: boolean, - ): GirPropertyElement['_tsData'] - - private getVariableTsData( - girVar: GirConstantElement, - girTypeName: 'constant', - tsTypeName: 'constant', - tsClass: TsClass | null, - optional: boolean, - nullable: boolean, - allowQuotes: boolean, - ): GirConstantElement['_tsData'] - - private getVariableTsData( - girVar: GirFieldElement, - girTypeName: 'field', - tsTypeName: 'property' | 'static-property', - tsClass: TsClass | null, - optional: boolean, - nullable: boolean, - allowQuotes: boolean, - ): GirFieldElement['_tsData'] - - private getVariableTsData( - girVar: GirPropertyElement | GirFieldElement | GirConstantElement, - girTypeName: 'property' | TypeGirVariable | 'field', - tsTypeName: 'constant' | 'property' | 'constructor-property' | 'static-property', - tsClass: TsClass | null, - optional = false, - nullable = false, - allowQuotes = false, - generics: TsGenericParameter[] = [], - ) { - if (!girVar.$.name) return undefined - if ( - !girVar || - !girVar.$ || - !girBool(girVar.$.introspectable, true) || - girBool((girVar as GirFieldElement).$.private) - ) { - return undefined - } + getImportsForCPrefix(c_prefix: string): GirModule[] { + return this.parent.namespacesForPrefix(c_prefix) + } - if (girVar._tsData) { - // this.log.warn('[getVariableTsData] _tsData already set!') - return girVar._tsData - } + hasImport(name: string): boolean { + return this.default_imports.has(name) || this.imports.has(name) + } - let name = girVar.$.name - - switch (girTypeName) { - case 'property': - name = this.transformation.transformPropertyName(girVar.$.name, allowQuotes) - break - case 'constant': - name = this.transformation.transformConstantName(girVar.$.name, allowQuotes) - break - case 'field': - name = this.transformation.transformFieldName(girVar.$.name, allowQuotes) - break - } - // Use the out type because it can be a union which isn't appropriate - // for a property - const tsType = this.getTsType(girVar, tsClass, { optional, nullable, generics }) - - const tsData: TsProperty | TsVar = { - name, - type: [tsType], - isInjected: false, - girTypeName, - tsTypeName, - doc: this.getTsDoc(girVar), - } + private _getImport(name: string): GirModule | null { + let version = this.default_imports.get(name) ?? this.imports.get(name) - tsData.doc.tags.push(...this.getTsDocGirElementTags(tsData.tsTypeName, tsData.girTypeName)) + if (name === this.name) { + return this + } - return tsData - } + // TODO: Clean this up, but essentially try to resolve import versions + // using transitive imports (e.g. use GtkSource to find the version of Gtk) + if (!version) { + const entries = [...this.default_imports.entries()].flatMap(([_name]) => { + const namespace = this._getImport(_name) - private getPropertyTsData( - girProp: GirPropertyElement, - girTypeName: 'property', - tsTypeName: 'property' | 'constructor-property' | 'static-property', - tsClass: TsClass, - construct?: boolean, - optional?: boolean, - nullable?: boolean, - indentCount?: number, - ): TsProperty | undefined - - private getPropertyTsData( - girProp: GirFieldElement, - girTypeName: 'field', - tsTypeName: 'property' | 'static-property', - tsClass: TsClass, - construct?: boolean, - optional?: boolean, - nullable?: boolean, - indentCount?: number, - ): TsVar | undefined + return [...(namespace?.default_imports.entries() ?? [])] + }) - /** - * - * @param girProp - * @param girTypeName - * @param tsTypeName - * @param construct construct means include the property even if it's construct-only, - * @param optional optional means if it's construct-only it will also be marked optional (?) - * @param nullable - * @returns - */ - private getPropertyTsData( - girProp: GirPropertyElement | GirFieldElement, - girTypeName: 'property' | 'field', - tsTypeName: 'constructor-property' | 'property' | 'static-property', - tsClass: TsClass, - construct = false, - optional?: boolean, - nullable?: boolean, - ): TsProperty | undefined { - if (!girBool(girProp.$.writable) && construct) return undefined - if (girBool((girProp as GirFieldElement).$.private)) return undefined - - if (girProp._tsData) { - // this.log.warn('[getPropertyTsData] _tsData already set!') - return girProp._tsData as TsProperty + version = Object.fromEntries(entries)[name] } - if (optional === undefined) optional = this.typeIsOptional(girProp) - if (nullable === undefined) nullable = this.typeIsNullable(girProp) - - const readonly = - !girBool(girProp.$.writable) || (!construct && girBool((girProp as GirPropertyElement).$['construct-only'])) - - let tsData: TsProperty | undefined - - switch (girTypeName) { - case 'property': - tsData = this.getVariableTsData( - girProp as GirPropertyElement, - girTypeName, - tsTypeName, - tsClass, - construct && optional, - construct && nullable, - true, - ) as TsProperty - break - case 'field': - if (tsTypeName !== 'property') { - throw new Error(`Wrong tsType: "${tsTypeName}" for girType: "${girTypeName}`) - } - tsData = this.getVariableTsData( - girProp as GirFieldElement, - girTypeName, - tsTypeName, - tsClass, - construct && optional, - construct && nullable, - true, - ) as TsProperty - break - default: - throw new Error(`Unknown property type: ${girTypeName as string}`) + if (!version) { + version = this.parent.assertDefaultVersionOf(name) } - if (!tsData?.name) { - return undefined - } + const namespace = this.parent.namespace(name, version) - tsData = { - ...tsData, - readonly, + if (namespace) { + if (!this.imports.has(namespace.name)) { + this.imports.set(namespace.name, namespace.version) + } } - return tsData + + return namespace } - /** - * - * @param girFunc - * @param prefix E.g. vfunc - * @param overrideReturnType - * @param isArrowType - * @param indentCount - */ - private getFunctionTsData( - girFunc: - | GirMethodElement - | GirFunctionElement - | GirConstructorElement - | GirCallbackElement - | GirVirtualMethodElement, - girTypeName: 'virtual' | 'method' | 'constructor' | 'function' | 'callback' | 'static-function', - tsClass: TsClass | null, - overwrite: { - isStatic: boolean - isArrowType: boolean - isGlobal: boolean - isVirtual: boolean - isInjected?: boolean - returnType: string | null - generics: TsGenericParameter[] - }, - ): TsFunction | undefined { - if (!girFunc || !girFunc.$ || !girBool(girFunc.$.introspectable, true) || girFunc.$['shadowed-by']) { - return undefined - } + getInstalledImport(name: string): GirModule | null { + let version = this.default_imports.get(name) ?? this.imports.get(name) - let hasUnresolvedConflict: boolean | undefined + if (name === this.name) { + return this + } - // TODO: Fix that we overwrite tsData every time seems wrong to me, but if I just return the already defined `_tsData` leads to problems with the overload methods - if (girFunc._tsData) { - hasUnresolvedConflict = girFunc._tsData?.hasUnresolvedConflict // WORKAROUND do not overwrite conflicts + if (!version) { + version = this.parent.defaultVersionOf(name) ?? undefined } - let name = girFunc.$.name - const { returnTypes, outArrayLengthIndex, retTypeIsVoid } = this.getReturnType(girFunc, tsClass) + if (!version) { + return null + } - const shadows = (girFunc as GirMethodElement).$.shadows + const namespace = this.parent.namespace(name, version) - if (shadows) { - name = shadows + if (namespace) { + if (!this.imports.has(namespace.name)) { + this.imports.set(namespace.name, namespace.version) + } } - // Overwrites - overwrite.isStatic = overwrite.isStatic || girTypeName === 'static-function' || girTypeName === 'constructor' - overwrite.isGlobal = overwrite.isGlobal || girTypeName === 'function' - overwrite.isVirtual = overwrite.isVirtual || girTypeName === 'virtual' - overwrite.isInjected = overwrite.isInjected || false - - // Function name transformation by environment - name = this.transformation.transformFunctionName(name, overwrite.isVirtual) - - const tsData: TsFunction = { - isArrowType: overwrite.isArrowType, - isStatic: overwrite.isStatic, - isGlobal: overwrite.isGlobal, - isVirtual: overwrite.isVirtual, - isInjected: overwrite.isInjected, - returnTypes, - retTypeIsVoid, - name, - overrideReturnType: overwrite.returnType || undefined, - inParams: [], - outParams: [], - instanceParameters: [], - generics: overwrite.generics, - hasUnresolvedConflict, - girTypeName, - tsTypeName: this.girFactory.girTypeNameToTsTypeName(girTypeName, overwrite.isStatic), - doc: this.getTsDoc(girFunc as GirDocElement), - overloads: [], - parent: tsClass, - } + return namespace + } - const { inParams, outParams, instanceParameters } = this.setParametersTsData( - outArrayLengthIndex, - girFunc.parameters, - tsData, - ) + assertInstalledImport(name: string): GirModule { + const namespace = this._getImport(name) - tsData.inParams.push(...inParams) - tsData.outParams.push(...outParams) - tsData.instanceParameters.push(...instanceParameters) + if (!namespace) { + throw new Error(`Failed to import ${name} in ${this.name}, not installed or accessible.`) + } - tsData.doc.tags.push(...this.getTsDocGirElementTags(tsData.tsTypeName, tsData.girTypeName)) - tsData.doc.tags.push(...this.getTsDocInParamTags(tsData?.inParams)) - tsData.doc.tags.push(...this.getTsDocReturnTags(girFunc)) + return namespace + } - return tsData + getImports(): [string, string][] { + return [...this.imports.entries()].sort(([[a], [b]]) => a.localeCompare(b)) } - overloadPromisifiedFunctions(girFunctions: GirFunctionElement[]): void { - if (!this.config.promisify) return - - const promisifyAsyncReturn = ['Gio.AsyncReadyCallback', 'AsyncReadyCallback'] - const promisifyFuncMap = {} as { [name: string]: PromisifyFunc } - - // Find the functions that can be promisified - for (const girFunction of girFunctions) { - const tsFunction = girFunction._tsData - if (!tsFunction) continue - - // Check if function name satisfies async,finish scheme - const isAsync = tsFunction.name.endsWith('_async') || tsFunction.name.endsWith('_begin') - const isFinish = tsFunction.name.endsWith('_finish') - if (!isAsync && !isFinish) continue - - // Handle async functions - if (isAsync) { - if (tsFunction.inParams.length === 0) continue - const lastParam = tsFunction.inParams[tsFunction.inParams.length - 1] - if (lastParam.type && lastParam.type.length > 0) { - const type = lastParam.type[0].$.name - if (type && promisifyAsyncReturn.includes(type)) { - if (!(tsFunction.name in promisifyFuncMap)) promisifyFuncMap[tsFunction.name] = {} - promisifyFuncMap[tsFunction.name].asyncFn = tsFunction - } - } - } + addImport(ns_name: string) { + this._getImport(ns_name) + } - // Handle finish functions - if (isFinish) { - if (tsFunction.returnTypes.length === 0) continue - let name = `${tsFunction.name.replace(/(_finish)$/, '')}_async` - if (!(name in promisifyFuncMap)) name = `${tsFunction.name.replace(/(_finish)$/, '')}_begin` - if (!(name in promisifyFuncMap)) promisifyFuncMap[name] = {} - promisifyFuncMap[name].finishFn = tsFunction - } + getMembers(name: string): IntrospectedNamespaceMember[] { + const members = this.members.get(name) + + if (Array.isArray(members)) { + return [...members] } + return members ? [members] : [] + } - // Generate TsFunctions for promisify-able functions and add to the array - for (const [, func] of Object.entries(promisifyFuncMap)) { - if (!func.asyncFn || !func.finishFn) continue + getMemberWithoutOverrides(name: string) { + if (this.members.has(name)) { + const member = this.members.get(name) - const inParams = this.girFactory.newGirCallableParamElements( - func.asyncFn.inParams.slice(0, -1), - func.asyncFn, - ) + if (!Array.isArray(member)) { + return member + } - const outParams = this.girFactory.newGirCallableParamElements(func.finishFn.outParams, func.asyncFn) + return null + } - const returnTypes = this.girFactory.newTsTypes(outParams.length > 0 ? [] : func.finishFn.returnTypes) + const resolvedName = this._resolve_names.get(name) + if (resolvedName) { + const member = this.members.get(resolvedName.name) - let docReturnText = func.finishFn.doc.tags.find((tag) => tag.tagName === 'returns')?.text || '' - if (docReturnText) { - docReturnText = `A Promise of: ${docReturnText}` - } else { - docReturnText = `A Promise of the result of {@link ${func.asyncFn.name}}` + if (!Array.isArray(member)) { + return member } + } - const docText = `Promisified version of {@link ${func.asyncFn.name}}\n\n${func.asyncFn.doc.text}` + return null + } - const docTags = func.asyncFn.doc.tags.filter( - (tag) => tag.paramName !== 'callback' && tag.paramName !== 'returns', - ) + assertClass(name: string): IntrospectedBaseClass { + const clazz = this.getClass(name) - docTags.push({ - tagName: 'returns', - text: docReturnText, - paramName: '', - }) + if (!clazz) { + throw new Error(`Class ${name} does not exist in namespace ${this.name}.`) + } - const doc = this.girFactory.newTsDoc({ - text: docText, - tags: docTags, - }) + return clazz + } - const promisifyFn = this.girFactory.newTsFunction( - { - ...func.asyncFn, - inParams, - outParams, - returnTypes, - isPromise: true, - doc, - }, - func.asyncFn.parent, - ) + getClass(name: string): IntrospectedBaseClass | null { + const member = this.getMemberWithoutOverrides(name) - func.asyncFn.overloads.push(promisifyFn) + if (member instanceof IntrospectedBaseClass) { + return member } + return null } - private getCallbackInterfaceTsData(girCallback: GirCallbackElement | GirConstructorElement) { - if (!girElementIsIntrospectable(girCallback)) return undefined + getEnum(name: string): IntrospectedEnum | null { + const member = this.getMemberWithoutOverrides(name) - const namespace = this.namespace - - const tsDataInterface: TsCallbackInterface = { - name: `${namespace}.${girCallback.$.name}`, - generics: [], + if (member instanceof IntrospectedEnum) { + return member } - - return tsDataInterface + return null } - private setCallbackTsData(girCallback: GirCallbackElement, tsClass: TsClass | null) { - const tsFunction = this.getFunctionTsData(girCallback, 'callback', tsClass, { - isStatic: false, - isArrowType: true, - isGlobal: false, - isVirtual: false, - returnType: null, - generics: [], - }) - if (tsFunction) { - const tsCallback: TsCallback = { - ...tsFunction, - girTypeName: 'callback', - tsTypeName: this.girFactory.girTypeNameToTsTypeName('callback', false), - tsCallbackInterface: this.getCallbackInterfaceTsData(girCallback), - overloads: [], - } - girCallback._tsData = tsCallback + getAlias(name: string): IntrospectedAlias | null { + const member = this.getMemberWithoutOverrides(name) - this.inject.toCallback(girCallback) + if (member instanceof IntrospectedAlias) { + return member } - return girCallback._tsData + return null } - private getSignalCallbackInterfaceTsData( - girCallback: GirSignalElement, - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ) { - if (!girElementIsIntrospectable(girCallback)) return undefined + hasSymbol(name: string) { + return this.members.has(name) + } + + resolveSymbolFromTypeName(name: string): string | null { + const resolvedName = this._resolve_names.get(name) - if (!girClass._tsData || !girClass._module) { - throw new Error(NO_TSDATA('getSignalCallbackTsData')) + if (!resolvedName) { + return null } - const className = girClass._tsData.name - const signalName = girCallback.$.name - const signalInterfaceName = this.transformation.transformSignalInterfaceName(signalName) - const namespace = girClass._module.namespace - - const tsDataInterface: TsCallbackInterface = { - name: `${namespace}.${className}.${signalInterfaceName}SignalCallback`, - generics: [], - overwriteDoc: { - text: `Signal callback interface for \`${signalName}\``, - tags: [], - }, + const member = this.members.get(resolvedName.name) + if (member instanceof IntrospectedBase) { + return member.name } - return tsDataInterface + return null } - private getConstructorFunctionTsData(parentClass: TsClass, girConstructor: GirConstructorElement) { - if (!girElementIsIntrospectable(girConstructor)) return - - const constructorTypeName = addNamespace(parentClass.name, parentClass.namespace) - - return this.getFunctionTsData(girConstructor, 'constructor', parentClass, { - isStatic: true, - isArrowType: false, - isGlobal: false, - isVirtual: false, - returnType: constructorTypeName, - generics: [], - }) - } - - private getSignalCallbackTsData( - girSignalFunc: GirSignalElement, - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ) { - if (!girClass._tsData) { - throw new Error(NO_TSDATA('getSignalCallbackTsData')) - } - - if (girSignalFunc._tsData) { - return girSignalFunc._tsData - } - - // Leads to errors here - // if (!girElementIsIntrospectable(girSignalFunc)) return undefined - - const name = this.transformation.transform('signalName', girSignalFunc.$.name) - - const { returnTypes, outArrayLengthIndex, retTypeIsVoid } = this.getReturnType(girSignalFunc, girClass._tsData) - - const tsCallback: TsCallback = { - name, // TODO: 'callback'? - returnTypes, - isArrowType: true, - isStatic: false, - isGlobal: false, - isVirtual: false, - isInjected: false, - retTypeIsVoid, - inParams: [], - instanceParameters: [], - outParams: [], - generics: [], - girTypeName: 'callback', - tsTypeName: this.girFactory.girTypeNameToTsTypeName('callback', false), - doc: this.getTsDoc(girSignalFunc), - overloads: [], - parent: girClass._tsData, - } - - const { inParams, outParams, instanceParameters } = this.setParametersTsData( - outArrayLengthIndex, - girSignalFunc.parameters, - tsCallback, - ) - - if (this.config.environment === 'gjs') { - inParams.unshift( - this.girFactory.newGirCallableParamElement( - { - name: '$obj', - type: [ - { - type: girClass._tsData.name, - }, - ], - }, - tsCallback, - ), - ) - } - - tsCallback.inParams.push(...inParams) - tsCallback.outParams.push(...outParams) - tsCallback.instanceParameters.push(...instanceParameters) - - tsCallback.doc.tags.push(...this.getTsDocGirElementTags(tsCallback.tsTypeName, tsCallback.girTypeName)) - tsCallback.doc.tags.push(...this.getTsDocInParamTags(tsCallback?.inParams)) - tsCallback.doc.tags.push(...this.getTsDocReturnTags(girSignalFunc)) - - return tsCallback - } - - /** - * Generates signal methods like `connect`, `connect_after` and `emit` on Gjs or `connect`, `on`, `once`, `off` and `emit` an node-gtk - * for a default gir signal element - * @param girSignal - * @returns - */ - private getClassSignalMethodsTsData(girSignal: GirSignalElement, parentClass: TsClass) { - if (!girSignal._tsData) { - throw new Error(NO_TSDATA('getClassSignalMethodsTsData')) - } - - const inParams = girSignal._tsData.inParams.slice(1).map((inParam) => { - const injectParam: InjectionParameter = { - ...inParam._tsData, - type: inParam._tsData?.type || [], - name: inParam._tsData?.name || 'unknown', - } - return injectParam - }) - - return this.girFactory.newTsSignalMethods( - girSignal._tsData?.name, - girSignal._tsData?.tsCallbackInterface?.name, - inParams, - parentClass, - this.config.environment, - false, + findClassCallback(name: string): [string | null, string] { + const clazzes = Array.from(this.members.values()).filter( + (m): m is IntrospectedBaseClass => m instanceof IntrospectedBaseClass, ) - } - - /** - * Generates signal methods for the GObject properties of a gir class element - * @param girClass - */ - private getClassPropertySignalsMethods( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ) { - if (!girClass._tsData) { - throw new Error(NO_TSDATA('getClassPropertySignalsMethods')) - } - - const tsMethods: TsMethod[] = [] - - const propertyNames = this.getClassNonStaticPropertyNames(girClass) - const namespacePrefix = this.namespace === 'GObject' ? '' : 'GObject.' - - // TODO: Signals: Generate SignalMethods instead of direct types - for (const propertyName of propertyNames) { - let callbackType = 'any' - if (this.config.environment === 'gjs') { - const objParam = `$obj: ${girClass._tsData.name}` - // TODO: create arrowType object instead of a pure string type, see Pango-1.0.Pango.FontMapClass.load_font for an example - callbackType = `((${objParam}, pspec: ${namespacePrefix}ParamSpec) => void)` - } - tsMethods.push( - ...this.girFactory.newTsSignalMethods( - `notify::${propertyName}`, - callbackType, - [], - girClass._tsData, - this.config.environment, - ), - ) - } - - return tsMethods - } - - private getGeneralSignalsMethods(parentClass: TsClass) { - return this.girFactory.newTsSignalMethods(undefined, undefined, [], parentClass, this.config.environment, true) - } - - private setSignalTsData( - girSignal: GirSignalElement, - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ) { - if (!girClass._tsData) { - throw NO_TSDATA('setSignalTsData') - } - - if (!girSignal._tsData) { - girSignal._tsData = { - ...this.getSignalCallbackTsData(girSignal, girClass), - tsCallbackInterface: this.getSignalCallbackInterfaceTsData(girSignal, girClass), - tsMethods: [], - girTypeName: 'signal', - tsTypeName: this.girFactory.girTypeNameToTsTypeName('signal', false), - doc: this.getTsDoc(girSignal), - overloads: [], - } - - girSignal._tsData.doc.tags.push( - ...this.getTsDocGirElementTags(girSignal._tsData.tsTypeName, girSignal._tsData.girTypeName), - ) - // TODO: this are the callback parameters - girSignal._tsData.doc.tags.push(...this.getTsDocInParamTags(girSignal._tsData?.inParams)) - girSignal._tsData.doc.tags.push(...this.getTsDocReturnTags(girSignal)) - - if (!girSignal._tsData) { - throw NO_TSDATA('setSignalTsData') - } - - girSignal._tsData.tsMethods = this.getClassSignalMethodsTsData(girSignal, girClass._tsData) - } - - return girSignal._tsData - } - - private fixEnumerationDuplicateIdentifier(girEnum: GirEnumElement | GirBitfieldElement) { - if (!girElementIsIntrospectable(girEnum)) return girEnum - - if (!girEnum._tsData) { - throw new Error(NO_TSDATA('fixEnumerationDuplicateIdentifier')) - } - - if (!girEnum.member?.length) { - return girEnum - } - - const memberNames: string[] = [] - - for (const girEnumMember of girEnum.member) { - if (!girEnumMember._tsData) { - throw new Error(NO_TSDATA('fixEnumerationDuplicateIdentifier')) - } - if (memberNames.find((name) => name === girEnumMember._tsData?.name)) { - const renamed = '_' + girEnumMember._tsData.name - this.log.warn(WARN_DUPLICATE_ENUM_IDENTIFIER(girEnumMember._tsData.name, renamed)) - girEnumMember._tsData.name = renamed - } - memberNames.push(girEnumMember._tsData.name) - } - return girEnum - } - - private getEnumerationMemberTsData(girEnumMember: GirMemberElement, girTypeName: TypeGirEnumerationMember) { - const memberName = girEnumMember.$.name || girEnumMember.$['glib:nick'] || girEnumMember.$['c:identifier'] - if (!girElementIsIntrospectable(girEnumMember, memberName)) return undefined - - if (girEnumMember._tsData) { - // this.log.warn('[getEnumerationMemberTsData] _tsData already set!') - return girEnumMember._tsData - } - - const name = this.transformation.transformEnumMember(memberName) - const tsData: TsMember = { - name, - girTypeName, - tsTypeName: this.girFactory.girTypeNameToTsTypeName(girTypeName, false), - doc: this.getTsDoc(girEnumMember), - } - - tsData.doc.tags.push(...this.getTsDocGirElementTags(tsData.tsTypeName, tsData.girTypeName)) - - return tsData - } - - private getEnumerationTsData(girEnum: GirEnumElement | GirBitfieldElement, girTypeName: 'bitfield' | 'enum') { - if (!girElementIsIntrospectable(girEnum)) return undefined - - if (girEnum._tsData) { - // this.log.warn('[getEnumerationMemberTsData] _tsData already set!') - return girEnum._tsData - } - - // E.g. the NetworkManager-1.0 has enum names starting with 80211 - const name = this.transformation.transformEnumName(girEnum) - - const tsData: TsEnum = { - name, - girTypeName, - tsTypeName: this.girFactory.girTypeNameToTsTypeName(girTypeName, false), - doc: this.getTsDoc(girEnum), - } - - tsData.doc.tags.push(...this.getTsDocGirElementTags(tsData.tsTypeName, tsData.girTypeName)) - - return tsData - } - - private getAliasTsData(girAlias: GirAliasElement, tsClass: TsClass | null) { - if (!girElementIsIntrospectable(girAlias)) return undefined - - if (girAlias._tsData) { - // this.log.warn('[getEnumerationMemberTsData] _tsData already set!') - return girAlias._tsData - } - - const { type: typeName } = this.getTsType(girAlias, tsClass) - const name = girAlias.$.name - const tsData: TsAlias = { - name, - type: typeName, - girTypeName: 'alias', - tsTypeName: this.girFactory.girTypeNameToTsTypeName('alias', false), - } - return tsData - } - - private getConstantTsData(girConst: GirConstantElement, tsClass: TsClass | null) { - if (!girElementIsIntrospectable(girConst)) return undefined - - if (girConst._tsData) { - // this.log.warn('[getConstantTsData] _tsData already set!') - return girConst._tsData - } - - let tsData: TsVar | undefined = this.getVariableTsData( - girConst, - 'constant', - 'constant', - tsClass, - false, - false, - false, - ) - if (tsData?.name) { - if (!this.constNames[tsData.name]) { - this.constNames[tsData.name] = girConst - } else { - this.log.warn(WARN_CONSTANT_ALREADY_EXPORTED(tsData.name)) - tsData = undefined - } - } - return tsData - } - - private getClassConstructPropsTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - constructPropNames: LocalNames, - ) { - const constructProps: GirPropertyElement[] = [] - const girProperties = (girClass as GirClassElement | GirInterfaceElement).property - if (!girProperties?.length) { - return constructProps - } - for (const girProp of girProperties) { - if (!girElementIsIntrospectable(girProp) || !girProp.$.name) continue - // Do not modify the original girProp, create a new one by clone `girProp` to `girConstrProp` - const girConstrProp = clone(girProp) - - if (!girClass._tsData) continue - - if (!girConstrProp._tsData) { - girConstrProp._tsData = this.getPropertyTsData( - girConstrProp, - 'property', - 'constructor-property', - girClass._tsData, - true, - true, - true, - 0, - ) - } - - if (!girConstrProp._tsData) { - continue - } - - const localName = this.checkOrSetLocalName(girConstrProp, constructPropNames, 'property') - - if (!localName?.added) { - continue - } - - if (girConstrProp._fullSymName) - this.symTable.set(this.allDependencies, girConstrProp._fullSymName, girConstrProp) - constructProps.push(girConstrProp) - } - - return constructProps - } - - /** - * Some class/static methods are defined in a separate record which is not - * exported, but the methods are available as members of the JS constructor. - * In gjs one can use an instance of the object, a JS constructor or a GType - * as the method's instance-parameter. - * @see https://discourse.gnome.org/t/using-class-methods-like-gtk-widget-class-get-css-name-from-gjs/4001 - * @param girClass - */ - private getClassRecordMethods( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ): GirMethodElement[] { - const girMethods: GirMethodElement[] = [] - - if (!girClass.$.name) return girMethods - const fName = girClass.$.name + 'Class' - let rec = this.ns.record?.find((r) => r.$.name == fName) - if (!rec || !this.isGtypeStructFor(girClass, rec)) { - rec = this.ns.record?.find((r) => this.isGtypeStructFor(girClass, r)) - fName == rec?.$.name - } - if (!rec) return girMethods - - // Record methods - const methods = rec.method || [] - - for (const girMethod of methods) { - if (!girElementIsIntrospectable(girMethod) || !girClass._tsData) continue - - if (!girMethod._tsData) - girMethod._tsData = this.getFunctionTsData(girMethod, 'static-function', girClass._tsData, { - isStatic: true, - isArrowType: false, - isGlobal: false, - isVirtual: false, - returnType: null, - generics: [], - }) - - if (!girMethod._tsData) continue - - if (girMethod._tsData) { - if (girMethod._fullSymName) this.symTable.set(this.allDependencies, girMethod._fullSymName, girMethod) - girMethods.push(girMethod) - } + const res = clazzes + .map<[IntrospectedBaseClass, IntrospectedClassCallback | undefined]>((m) => [ + m, + m.callbacks.find((c) => c.name === name || c.resolve_names.includes(name)), + ]) + .find((r): r is [IntrospectedBaseClass, IntrospectedClassCallback] => r[1] != null) + + if (res) { + return [res[0].name, res[1].name] + } else { + return [null, name] } - - this.overloadPromisifiedFunctions(methods) - - return girMethods } /** - * Instance methods - * @param girClass - * @param localNames + * This is an internal method to add TypeScript + * comments when overrides use parts of the TypeScript standard + * libraries that are newer than default. */ - private getClassMethodsTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - localNames: LocalNames, - ) { - const girMethods: GirMethodElement[] = [] - if (girClass.method) { - for (const girMethod of girClass.method) { - if (!girElementIsIntrospectable(girMethod) || !girClass._tsData) continue - - if (!girMethod._tsData) - girMethod._tsData = this.getFunctionTsData(girMethod, 'method', girClass._tsData, { - isStatic: false, - isArrowType: false, - isGlobal: false, - isVirtual: false, - returnType: null, - generics: [], - }) - - if (!girMethod._tsData) continue - - const localName = this.checkOrSetLocalName(girMethod, localNames, 'method') - if (localName?.added && localName.method) { - if (girMethod._fullSymName) - this.symTable.set(this.allDependencies, girMethod._fullSymName, girMethod) - girMethods.push(localName.method) - } - } - - this.overloadPromisifiedFunctions(girClass.method) - } - return girMethods + ___dts___addReference(reference: string) { + this.__dts__references ??= [] + this.__dts__references.push(reference) } - private getClassFieldsTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - localNames: LocalNames, - ) { - const girFields: GirFieldElement[] = [] - if (!girClass._tsData) { - this.log.warn(NO_TSDATA('setClassFieldsTsData')) - return girFields - } - - if (girClass.field) { - for (const girField of girClass.field) { - if (!girElementIsIntrospectable(girField)) continue - if (!girField._tsData) - girField._tsData = this.getVariableTsData( - girField, - 'field', - 'property', - girClass._tsData, - false, - false, - false, - ) - - if (!girField._tsData) { - continue - } - - const localName = this.checkOrSetLocalName(girField, localNames, 'field') - if (localName?.added && localName.field) { - if (girField._fullSymName) this.symTable.set(this.allDependencies, girField._fullSymName, girField) - girFields.push(localName.field) - } - } - } - - return girFields - } - - private getGObjectProperties(girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement) { - const girProperties: GirPropertyElement[] = [] - if (girClass._fullSymName && !STATIC_NAME_ALREADY_EXISTS.includes(girClass._fullSymName)) { - // Records, classes and interfaces all have a static name - const type = this.girFactory.newTsType({ type: 'string' }) - const staticNameProp = this.girFactory.newGirProperty({ - isStatic: true, - name: 'name', - type: [type], - girTypeName: 'property', - }) - girProperties.push(staticNameProp) - } - - if (girClass._tsData?.isDerivedFromGObject && girClass._module) { - if (this.config.environment === 'gjs') { - const type = this.girFactory.newTsType({ - // TODO: Type not as string - type: 'GObject.GType', - generics: this.girFactory.newGenerics([ - { - value: girClass._tsData.name, - }, - ]), - }) - const staticGTypeProp = this.girFactory.newGirProperty({ - isStatic: true, - name: '$gtype', - type: [type], - girTypeName: 'property', - }) - girProperties.push(staticGTypeProp) - } - } - - return girProperties - } + static load(repo: ParsedGir, config: GenerateConfig, registry: NSRegistry): GirModule { + const ns = repo.repository[0]?.namespace?.[0] - private getClassPropertiesTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - localNames: LocalNames, - ) { - const girProperties: GirPropertyElement[] = [] - const properties = (girClass as GirClassElement | GirInterfaceElement).property - if (properties) { - for (const girProperty of properties) { - if (!girElementIsIntrospectable(girProperty) || !girClass._tsData) continue - - girProperty._tsData = this.getPropertyTsData(girProperty, 'property', 'property', girClass._tsData) - if (!girProperty._tsData) continue - - const localName = this.checkOrSetLocalName(girProperty, localNames, 'property') - if (localName?.added && localName.property) { - if (girProperty._fullSymName) - this.symTable.set(this.allDependencies, girProperty._fullSymName, girProperty) - girProperties.push(localName.property) - } - } - } - return girProperties - } + if (!ns) throw new Error(`Missing namespace in ${repo.repository[0].package[0].$.name}`) - private getClassNonStaticPropertyNames( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ) { - const propertyNames: string[] = [] + const modName = ns.$['name'] + let version = ns.$['version'] - if (!girClass._tsData) { - return propertyNames + // Hardcode harfbuzz version for now... + if (modName === 'HarfBuzz' && version === '0.0') { + version = '2.0' } - const girProperties = girClass._tsData.properties - - if (girProperties.length > 0) { - for (const girProperty of girProperties) { - if (!girElementIsIntrospectable(girProperty)) continue - if ( - girProperty.$.name && - !propertyNames.includes(girProperty.$.name) && - !girProperty._tsData?.isStatic - ) { - propertyNames.push(girProperty.$.name) - } - } + const options: LoadOptions = { + loadDocs: !config.noComments, + propertyCase: 'both', + environment: 'gjs', + verbose: config.verbose, } - // Extended property names - for (const fullSymName of Object.keys(girClass._tsData.inherit)) { - const girProperties = girClass._tsData.inherit[fullSymName]?.class.properties - if (girProperties.length > 0) { - for (const girProperty of girProperties) { - if (!girElementIsIntrospectable(girProperty)) continue - if ( - girProperty.$.name && - !propertyNames.includes(girProperty.$.name) && - !girProperty._tsData?.isStatic - ) { - propertyNames.push(girProperty.$.name) - } - } - } + if (!modName) { + throw new Error('Invalid GIR file: no namespace name specified.') } - // Implemented property names - for (const fullSymName of Object.keys(girClass._tsData.implements)) { - const girProperties = girClass._tsData.implements[fullSymName]?.interface.properties - if (girProperties.length > 0) { - for (const girProperty of girProperties) { - if (!girElementIsIntrospectable(girProperty)) continue - if ( - girProperty._tsData && - girProperty.$.name && - !propertyNames.includes(girProperty.$.name) && - !girProperty._tsData?.isStatic - ) { - propertyNames.push(girProperty.$.name) - } - } - } + if (!version) { + throw new Error('Invalid GIR file: no version name specified.') } - return propertyNames - } + const c_prefix = ns.$?.['c:symbol-prefixes']?.split(',') ?? [] - private getClassConstructorsTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ) { - if (!girClass._tsData) { - throw new Error(NO_TSDATA('getClassConstructorsTsData')) + if (options.verbose) { + console.debug(`Parsing ${modName}...`) } - const girConstructors: GirConstructorElement[] = [] - - // JS constructor(s) - if (girClass._tsData?.isDerivedFromGObject) { - const constructorInParam: InjectionParameter = { - name: 'config', - type: [ - { - optional: true, - type: girClass._tsData.constructPropInterfaceName, - }, - ], - } - const realConstructor = this.girFactory.newGirFunction( - { - name: 'constructor', - isStatic: true, - inParams: [constructorInParam], - girTypeName: 'constructor', - }, - girClass._tsData, - ) - const initConstructor = this.girFactory.newGirFunction( - { - name: '_init', - inParams: [constructorInParam], - girTypeName: 'method', - }, - girClass._tsData, - ) - girConstructors.push(realConstructor, initConstructor) - } - - if (Array.isArray(girClass.constructor)) { - for (const girConstructor of girClass.constructor) { - if (!girElementIsIntrospectable(girConstructor)) continue - - girConstructor._tsData = this.getConstructorFunctionTsData(girClass._tsData, girConstructor) - if (!girConstructor._tsData?.name) continue + const building = new GirModule(repo.repository[0], modName, version, c_prefix, config) + building.parent = registry + // Set the namespace object here to prevent re-parsing the namespace if + // another namespace imports it. + registry.mapping.set(modName, version, building) - // Also add `new` pseudo constructors - const ADD_NEW_PSEUDO_CONSTRUCTOR = true + const includes = repo.repository[0].include || [] - // Inject an additional real constructor if static new(...) exists - if (girConstructor._tsData.name === 'new') { - const realConstructor = clone(girConstructor) - realConstructor._tsData = clone(realConstructor._tsData) - - if (realConstructor._tsData) { - realConstructor._tsData.overloads = [] - realConstructor.$.name = 'constructor' - realConstructor._tsData.name = 'constructor' - girConstructors.push(realConstructor) + includes + .map((i) => [i.$.name, i.$.version] as const) + .forEach(([name, version]) => { + if (version) { + if (options.verbose) { + console.debug(`Adding dependency ${name} ${version}...`) } - if (ADD_NEW_PSEUDO_CONSTRUCTOR) { - girConstructors.push(girConstructor) - } - } else { - girConstructors.push(girConstructor) + building.default_imports.set(name, version) } - } - } - - return girConstructors - } - - private getClassVirtualMethodsTsData( - girClass: GirClassElement | GirInterfaceElement | GirUnionElement | GirRecordElement, - ) { - const methods: GirVirtualMethodElement[] = - (girClass as GirClassElement | GirInterfaceElement)['virtual-method'] || [] - const girMethods: GirVirtualMethodElement[] = [] - - for (const girVMethod of methods) { - if (!girElementIsIntrospectable(girVMethod) || !girClass._tsData) continue - - girVMethod._tsData = this.getFunctionTsData(girVMethod, 'virtual', girClass._tsData, { - isStatic: false, - isArrowType: false, - isGlobal: false, - isVirtual: true, - returnType: null, - generics: [], }) - if (!girVMethod._tsData) continue - - if (girVMethod?._tsData?.name) { - girMethods.push(girVMethod) - } - } - return girMethods - } - - /** - * - * @param girClass This is the class / interface the `parentClass` implements signals from - * @returns - */ - private getClassSignalsTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ) { - const girSignals: GirSignalElement[] = [] - - const signals: GirSignalElement[] = - (girClass as GirClassElement | GirInterfaceElement).signal || - (girClass as GirClassElement | GirInterfaceElement)['glib:signal'] || - [] - if (signals) { - for (const girSignal of signals) { - girSignal._tsData = this.setSignalTsData(girSignal, girClass) - if (!girSignal._tsData) continue - if (girSignal._fullSymName) this.symTable.set(this.allDependencies, girSignal._fullSymName, girSignal) - - girSignals.push(girSignal) - } - } - return girSignals - } - - getClassParentObject( - parentName: string, - namespace: string, - type: 'parent' | 'prerequisite' | 'implements', - ): ClassParent { - let qualifiedParentName: string - let parentModName: string - - // WORKAROUND: Fix wrong parent names - if ( - (this.packageName === 'GstAudio-0.10' || this.packageName === 'ClutterGst-1.0') && - (parentName === 'GstBase.BaseTransform' || - parentName === 'GstBase.BaseSink' || - parentName === 'GstBase.PushSrc') - ) { - const rename = parentName.replace('GstBase.', 'Gst.') - this.log.warn(`[getClassParentObject] Rename parent class "${parentName}" -> "${rename}"`) - parentName = rename - } - - if (parentName === 'GraniteServicesSettingsSerializable') { - parentName = 'ServicesSettingsSerializable' - this.log.warn( - `[getClassParentObject] Rename parent class "GraniteServicesSettingsSerializable" -> "ServicesSettingsSerializable"`, - ) - } - - if (parentName.indexOf('.') < 0) { - qualifiedParentName = namespace + '.' + parentName - parentModName = namespace - } else { - qualifiedParentName = parentName - const split = parentName.split('.') - parentName = split[split.length - 1] - parentModName = split.slice(0, split.length - 1).join('.') - } - const localParentName = parentModName == namespace ? parentName : qualifiedParentName - - const dependencyExists = !!this.symTable.get(this.allDependencies, qualifiedParentName) - - const cls = this.getClassParent({ - qualifiedParentName, - parentName, - }) - - return { - qualifiedParentName, - localParentName, - type, - parentName, - cls, - dependencyExists, - // TODO: are there other types that can be inherited or implemented? - girTypeName: type === 'parent' ? 'class' : 'interface', - } - } - - private getClassParents(girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement) { - const parents: ClassParent[] = [] - - if (!girClass._module?.namespace) { - throw new Error('Namespace not found!') - } - - if (!girClass._tsData) { - throw new Error(NO_TSDATA('getClassParents')) - } - - const prerequisites = (girClass as GirInterfaceElement)?.prerequisite - const implmts = (girClass as GirInterfaceElement)?.implements - - if (implmts) { - for (const implement of implmts) { - const parentName = implement.$?.name - if (!parentName) continue - const parent = this.getClassParentObject(parentName, girClass._module.namespace, 'implements') - if (parent.dependencyExists) { - parents.push(parent) - } - } + const importConflicts = (el: IntrospectedConstant | IntrospectedBaseClass | IntrospectedFunction) => { + return !building.hasImport(el.name) } - if (prerequisites) { - for (const prerequisite of prerequisites) { - const parentName = prerequisite.$?.name - if (!parentName) continue - const parent = this.getClassParentObject(parentName, girClass._module.namespace, 'prerequisite') - if (parent.dependencyExists) { - parents.push(parent) - } - } - } - - if ((girClass as GirClassElement).$.parent) { - const parentName = (girClass as GirClassElement).$.parent - if (parentName) { - const parent = this.getClassParentObject(parentName, girClass._module.namespace, 'parent') - if (parent.dependencyExists) { - parents.push(parent) - } - } - } - - // Please reply: Do all interfaces always inherit from GObject.Object? - // If this is a interface and GObject.Object is not in the parents array, add GObject.Object to the parents - if (girClass._tsData.girTypeName === 'interface' && girClass._fullSymName !== 'GObject.Object') { - if (!parents.find((parent) => parent.qualifiedParentName === 'GObject.Object')) { - // TODO make sure this class exists in symTable - const gObjectObjectCls = - this.symTable.getByHand('GObject-2.0.GObject.Object') || undefined - const parent: ClassParent = { - qualifiedParentName: 'GObject.Object', - localParentName: girClass._module.namespace === 'GObject' ? 'Object' : 'GObject.Object', - type: 'parent', - parentName: 'Object', - cls: gObjectObjectCls, - dependencyExists: !!gObjectObjectCls, - girTypeName: 'class', - } - if (parent.dependencyExists) { - parents.push(parent) - } - } - } - - return parents - } - - private setClassBaseTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - girTypeName: TypeGirClass, - ) { - if (!girClass?.$?.name) return undefined - - const girModule: GirModule = girClass._module ? girClass._module : this - let className = this.transformation.transformClassName(girClass.$.name) - /** - * E.g. 'Gtk' - */ - const namespace = girModule.namespace - /** - * E.g. '3.0' - */ - const version = girModule.version - - let qualifiedName: string - if (!className.includes('.')) { - qualifiedName = namespace + '.' + className - } else { - qualifiedName = className - const split = className.split('.') - className = split[split.length - 1] - } - - girClass._tsData = { - name: className, - qualifiedName, - parents: [], - namespace, - version, - isAbstract: this.isAbstractClass(girClass), - localNames: {}, - constructPropNames: {}, - inheritConstructPropInterfaceNames: [], - constructPropInterfaceName: `${namespace}.${className}.ConstructorProperties`, - fields: [], - properties: [], - conflictProperties: [], - constructProps: [], - propertySignalMethods: [], - methods: [], - conflictMethods: [], - virtualMethods: [], - constructors: [], - staticFunctions: [], - signals: [], - generics: [], - inherit: {}, - implements: {}, - girTypeName, - tsTypeName: this.girFactory.girTypeNameToTsTypeName(girTypeName, false), - doc: this.getTsDoc(girClass), - } - - girClass._tsData.doc.tags.push( - ...this.getTsDocGirElementTags(girClass._tsData.tsTypeName, girClass._tsData.girTypeName), - ) - - girClass._tsData.parents = this.getClassParents(girClass) - - if (girClass._tsData.parents.length) { - for (const parent of girClass._tsData.parents) { - girClass._tsData.inheritConstructPropInterfaceNames.push( - `${parent.qualifiedParentName}.ConstructorProperties`, - ) - } - } - - girClass._tsData.isDerivedFromGObject = this.isDerivedFromGObject(girClass) - - return girClass._tsData - } - - private setClassTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - girTypeName: TypeGirClass, - ): TsClass | undefined { - if (!girClass?.$?.name) return undefined - - if (girClass._tsData) { - return girClass._tsData - } - - girClass._tsData = this.setClassBaseTsData(girClass, girTypeName) - if (!girClass._tsData) { - return undefined - } - - // BASE - - if (girClass._tsData.isDerivedFromGObject) { - girClass._tsData.constructProps.push( - ...this.getClassConstructPropsTsData(girClass, girClass._tsData.constructPropNames), - ) - } - - girClass._tsData.constructors.push(...this.getClassConstructorsTsData(girClass)) - girClass._tsData.staticFunctions.push(...this.getClassStaticFunctionsTsData(girClass, girClass._tsData)) - - girClass._tsData.fields.push(...this.getClassFieldsTsData(girClass, girClass._tsData.localNames)) - - girClass._tsData.properties.push(...this.getClassPropertiesTsData(girClass, girClass._tsData.localNames)) - girClass._tsData.methods.push(...this.getClassMethodsTsData(girClass, girClass._tsData.localNames)) - girClass._tsData.virtualMethods.push(...this.getClassVirtualMethodsTsData(girClass)) - girClass._tsData.signals.push(...this.getClassSignalsTsData(girClass)) - - girClass._tsData.properties.push(...this.getGObjectProperties(girClass)) - - // Copy fields, properties, methods, virtual methods and signals from inheritance tree - this.traverseInheritanceTree(girClass, girClass._tsData.girTypeName, (extendsCls, depth) => { - if (!girClass._tsData || !extendsCls._tsData || !extendsCls._fullSymName || !extendsCls._module) { - return - } - - if (girClass._fullSymName === extendsCls._fullSymName) { - return - } - - const key = extendsCls._module.packageName + '.' + extendsCls._fullSymName - if (girClass._tsData.inherit[key]) return - - girClass._tsData.inherit[key] = { - depth, - class: extendsCls._tsData, - } - }) - - // Copy properties, methods and signals from implemented interface - this.forEachInterface(girClass, girClass._tsData.girTypeName, (iface, depth) => { - if (!girClass._tsData || !iface._tsData || !iface._fullSymName || !iface._module) { - return - } - - if (girClass._fullSymName === iface._fullSymName) { - return - } - - const key = iface._module.packageName + '.' + iface._fullSymName - if (girClass._tsData.implements[key]) return - - girClass._tsData.implements[key] = { - depth, - interface: iface._tsData, - } - }) - - this.inject.toClass(girClass) - - girClass._tsData.propertySignalMethods.push( - ...this.getClassPropertySignalsMethods(girClass), - ...this.getGeneralSignalsMethods(girClass._tsData), - ) - - if (this.config.fixConflicts) { - this.conflictResolver.repairClass(girClass) - } - - return girClass._tsData - } - - private isDerivedFromGObject( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ): boolean { - if (typeof girClass._tsData?.isDerivedFromGObject === 'boolean') return girClass._tsData.isDerivedFromGObject - let ret = false - - const onClassOrInterface = ( - cls: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ) => { - if (cls._tsData?.isDerivedFromGObject === true || cls._fullSymName === 'GObject.Object') { - ret = true - } - } - if (!girClass._tsData) throw new Error(NO_TSDATA('isDerivedFromGObject')) - this.traverseInheritanceTree(girClass, girClass._tsData.tsTypeName, onClassOrInterface) - this.forEachInterface(girClass, girClass._tsData.tsTypeName, onClassOrInterface) - return ret - } - - private getClassParent(parent: Pick) { - let parentPtr: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement | undefined = - undefined - if (this.symTable.get(this.allDependencies, parent.qualifiedParentName)) { - parentPtr = - (this.symTable.get(this.allDependencies, parent.qualifiedParentName) as GirClassElement) || undefined - } - - if (!parentPtr && parent.parentName == 'Object') { - parentPtr = (this.symTable.getByHand('GObject-2.0.GObject.Object') as GirClassElement) || undefined - } - - return parentPtr - } - - private traverseInheritanceTree( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - gitTypeName: TypeGirClass, - callback: ( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - depth: number, - ) => void, - depth = 0, - recursive = true, - ): void { - if (!girClass.$.name) return - if (!girClass._tsData) girClass._tsData = this.setClassTsData(girClass, gitTypeName) - if (!girClass._tsData) return - - callback(girClass, depth) - - const parents = girClass._tsData.parents - if (recursive && parents.length) { - ++depth - - if (depth >= MAX_CLASS_PARENT_DEPTH) { - this.log.error('[traverseInheritanceTree] Maximum recursion depth reached') - return - } - - for (const parent of parents) { - if (!parent.parentName || parent.type !== 'parent') { - continue - } - - if (parent.cls) { - if (parent.cls === girClass) { - this.log.warn('[traverseInheritanceTree] A class cannot inherit itself') - continue - } - - this.traverseInheritanceTree(parent.cls, parent.girTypeName, callback, depth, recursive) - } - } - } - } - - private forEachInterface( - girIface: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - girTypeName: TypeGirClass, - callback: ( - cls: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - depth: number, - ) => void, - recursive = true, - depth = 0, - ): void { - if (!girIface.$.name) return - if (!girIface._tsData) girIface._tsData = this.setClassTsData(girIface, girTypeName) - if (!girIface._tsData) { - throw new Error(NO_TSDATA('forEachInterface')) - } - - const parents = girIface._tsData.parents - if (parents.length) { - ++depth - for (const parent of parents) { - if (!parent.parentName || parent.type === 'parent') { - continue - } - - if (parent.cls) { - callback(parent.cls as GirInterfaceElement, depth) - // iface's prerequisite is also an interface, or it's - // a class and we also want to recurse classes - - if (recursive) - this.forEachInterface( - parent.cls as GirInterfaceElement, - parent.girTypeName, - callback, - recursive, - depth, - ) - } - } - } - } - - /** - * - * @param girElement - * @param localNames Can be (constructor) properties, fields or methods - * @param type - */ - private checkOrSetLocalName( - girElement: - | GirMethodElement - | GirPropertyElement - | GirFieldElement - | GirConstructorElement - | GirFunctionElement, - localNames: LocalNames, - type: LocalNameType, - ): LocalNameCheck | null { - let isOverloadable = false - - if (!girElement._tsData) { - return null - } - - const name = girElement._tsData?.name - - if (!name) { - return null - } - - // Methods are overloadable by typescript - // TODO Add support for properties - if (type === 'method') { - isOverloadable = true - } - - // Only names of the same type are overloadable - if (localNames[name]?.type && localNames[name].type !== type) { - // In GIO there are some methods and properties with the same name - // E.g. on `WebKit2.WebView.isLoading` and `WebKit2.WebView.isLoading()` - // See Gjs doc https://gjs-docs.gnome.org/webkit240~4.0_api/webkit2.webview#property-is_loading - // TODO prefer functions over properties (Overwrite the properties with the functions if they have the same name) - - return null - } - - // If name is found in localNames this variable was already defined - if (localNames?.[name]?.[type]?._tsData) { - // Ignore duplicates with the same type - // TODO should we use `this.functionSignaturesMatch` here? - - if (type === 'method') { - const tsMethod1 = (girElement as GirMethodElement)._tsData - const tsMethod2 = (localNames[name][type] as GirMethodElement)._tsData - if (!tsMethod1 || !tsMethod2) { - return null - } - // if (ConflictResolver.functionConflict(tsMethod1, tsMethod2)) { - // return null - // } - } else { - // TODO better handling of property and field - if (isEqual(localNames[name][type]?._tsData, girElement._tsData)) { - return null - } - } - - // Ignore if current method is not overloadable - if (!isOverloadable) { - return null - } - } - - localNames[name] = localNames[name] || {} - const localName: LocalName = { - ...localNames[name], - [type]: girElement, - type, - } - - localNames[name] = localName - return { ...localName, added: true } - } - - private isGtypeStructFor( - e: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - rec: GirRecordElement, - ) { - const isFor = rec.$['glib:is-gtype-struct-for'] - return isFor && isFor == e.$.name - } - - /** - * E.g GObject.ObjectClass is a abstract class and required by UPowerGlib-1.0, UDisks-2.0 and others - * @param girClass - * @returns `true` if this is this a abstract class. - */ - private isAbstractClass(girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement) { - return girClass.$?.['glib:is-gtype-struct-for'] ? true : false - } - - /** - * - * @param girClass - * @returns - */ - private getOtherStaticFunctions( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - ): GirFunctionElement[] { - const girFunctions: GirFunctionElement[] = [] - - if (!girClass._tsData) return girFunctions - - if (girClass.function?.length) { - for (const girFunction of girClass.function) { - girFunction._tsData = this.getFunctionTsData(girFunction, 'static-function', girClass._tsData, { - isStatic: true, - isArrowType: false, - isGlobal: false, - isVirtual: false, - returnType: null, - generics: [], - }) - - if (!girFunction._tsData?.name) continue - if (girFunction._tsData.name.startsWith('new')) continue - - girFunctions.push(girFunction) - } - } - return girFunctions - } - - private getStaticNewFunctions( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - parentClass: TsClass, - ): Array { - const girFunctions: Array = [] - if (girClass.function?.length) { - for (const girFunction of girClass.function) { - girFunction._tsData = this.getConstructorFunctionTsData(parentClass, girFunction) - if (!girFunction._tsData?.name.startsWith('new')) continue - girFunctions.push(girFunction) - } - } - return girFunctions - } - - /** - * Static methods, and - * @param girClass - * @param constructorTypeName Used to overwrite the constructor return type - * @param useReference This value should be `false` for inherited and implemented classes / interfaces. - * Otherwise other modules would overwrite the return value of the constructor methods - */ - private getClassStaticFunctionsTsData( - girClass: GirClassElement | GirUnionElement | GirInterfaceElement | GirRecordElement, - parentClass: TsClass, - ) { - const girStaticFuncs: Array = [] - - girStaticFuncs.push(...this.getStaticNewFunctions(girClass, parentClass)) - girStaticFuncs.push(...this.getOtherStaticFunctions(girClass)) - girStaticFuncs.push(...this.getClassRecordMethods(girClass)) - - return girStaticFuncs - } - - private setModuleTsData() { - if (this.ns.enumeration) { - for (const girEnum of this.ns.enumeration) { - if (girEnum.member) { - for (const girEnumMember of girEnum.member) { - girEnumMember._tsData = this.getEnumerationMemberTsData(girEnumMember, 'enum-member') - if (!girEnumMember._tsData) continue - } - } - girEnum._tsData = this.getEnumerationTsData(girEnum, 'enum') - this.fixEnumerationDuplicateIdentifier(girEnum) - if (!girEnum._tsData) continue - } - } - - if (this.ns.bitfield) - for (const girBitfield of this.ns.bitfield) { - if (girBitfield.member) - for (const girEnumMember of girBitfield.member) { - girEnumMember._tsData = this.getEnumerationMemberTsData(girEnumMember, 'bitfield-member') + if (ns.enumeration) { + // Get the requested enums + ns.enumeration + ?.map((enumeration) => { + if (enumeration.$['glib:error-domain']) { + return IntrospectedError.fromXML(enumeration, building, options) + } else { + return IntrospectedEnum.fromXML(enumeration, building, options) } - girBitfield._tsData = this.getEnumerationTsData(girBitfield, 'bitfield') - if (!girBitfield._tsData) continue - this.fixEnumerationDuplicateIdentifier(girBitfield) - } - - if (this.ns.constant) - for (const girConst of this.ns.constant) { - girConst._tsData = this.getConstantTsData(girConst, null) - if (!girConst._tsData) continue - } - - if (this.ns.function) { - for (const girFunc of this.ns.function) { - girFunc._tsData = this.getFunctionTsData(girFunc, 'function', null, { - isStatic: false, - isArrowType: false, - isGlobal: true, - isVirtual: false, - returnType: null, - generics: [], }) - if (!girFunc._tsData) continue - } - - this.overloadPromisifiedFunctions(this.ns.function) - } - - if (this.ns.callback) - for (const girCallback of this.ns.callback) { - girCallback._tsData = this.setCallbackTsData(girCallback, null) - } - - if (this.ns.interface) - for (const girIface of this.ns.interface) { - girIface._tsData = this.setClassTsData(girIface, 'interface') - } - - if (this.ns.class) - for (const girClass of this.ns.class) { - girClass._tsData = this.setClassTsData(girClass, 'class') - if (!girClass._tsData) continue - } - - if (this.ns.record) - for (const girRecord of this.ns.record) { - girRecord._tsData = this.setClassTsData(girRecord, 'record') - if (!girRecord._tsData) continue - } - - if (this.ns.union) - for (const girUnion of this.ns.union) { - girUnion._tsData = this.setClassTsData(girUnion, 'union') - if (!girUnion._tsData) continue - } - - if (this.ns.alias) { - for (const girAlias of this.ns.alias) { - // GType is not a number in GJS - if (this.packageName !== 'GObject-2.0' || girAlias.$.name !== 'Type') - girAlias._tsData = this.getAliasTsData(girAlias, null) - if (!girAlias._tsData) continue - } - } - } - - /** - * TODO: find better name for this method - * @param fullTypeName - * @returns - */ - private fullTypeLookupWithNamespace(fullTypeName: string) { - let resValue = '' - let namespace = '' - - // Check overwrites first - if (!resValue && fullTypeName && FULL_TYPE_MAP(this.config.environment, fullTypeName)) { - resValue = FULL_TYPE_MAP(this.config.environment, fullTypeName) || '' - } - - // Only use the fullTypeName as the type if it is found in the symTable - if (!resValue && this.symTable.get(this.allDependencies, fullTypeName)) { - if (fullTypeName.startsWith(this.namespace + '.')) { - resValue = removeNamespace(fullTypeName, this.namespace) - resValue = this.transformation.transformTypeName(resValue) - // TODO: check if resValue is a class, enum or interface before transformClassName - resValue = this.transformation.transformClassName(resValue) - namespace = this.namespace - resValue = namespace + '.' + resValue - } else { - const resValues = fullTypeName.split('.') - resValues.map((name) => this.transformation.transformTypeName(name)) - // TODO: check if resValues[resValues.length - 1] is a class, enum, interface before transformClassName - resValues[resValues.length - 1] = this.transformation.transformClassName( - resValues[resValues.length - 1], + .forEach((c) => building.members.set(c.name, c)) + } + + // Constants + if (ns.constant) { + ns.constant + ?.filter(isIntrospectable) + .map((constant) => IntrospectedConstant.fromXML(constant, building, options)) + .filter(importConflicts) + .forEach((c) => building.members.set(c.name, c)) + } + + // Get the requested functions + if (ns.function) { + ns.function + ?.filter(isIntrospectable) + .map((func) => IntrospectedFunction.fromXML(func, building, options)) + .filter(importConflicts) + .forEach((c) => building.members.set(c.name, c)) + } + + if (ns.callback) { + ns.callback + ?.filter(isIntrospectable) + .map((callback) => IntrospectedCallback.fromXML(callback, building, options)) + .filter(importConflicts) + .forEach((c) => building.members.set(c.name, c)) + } + + if (ns['glib:boxed']) { + ns['glib:boxed'] + ?.filter(isIntrospectable) + .map( + (boxed) => + new IntrospectedAlias({ + name: boxed.$['glib:name'], + namespace: building, + type: new NullableType(ObjectType), + }), ) - resValue = resValues.join('.') - namespace = resValues[0] - } - } - - return { - value: resValue, - namespace, + .forEach((c) => building.members.set(c.name, c)) } - } - - private loadInheritance(inheritanceTable: InheritanceTable): void { - // Class hierarchy - for (const girClass of this.ns.class ? this.ns.class : []) { - let parent: string | null = null - if (girClass.$ && girClass.$.parent) parent = girClass.$.parent - if (!parent) continue - if (!girClass._fullSymName) continue - - if (!parent.includes('.')) { - parent = addNamespace(parent, this.namespace) - } - const className = girClass._fullSymName - - const arr: string[] = inheritanceTable[className] || [] - arr.push(parent) - inheritanceTable[className] = arr + // Bitfield is a type of enum + if (ns.bitfield) { + ns.bitfield + ?.filter(isIntrospectable) + .map((field) => IntrospectedEnum.fromXML(field, building, options, true)) + .forEach((c) => building.members.set(c.name, c)) } - // Class interface implementations - for (const girClass of this.ns.class ? this.ns.class : []) { - if (!girClass._fullSymName) continue - - const names: string[] = [] - - if (girClass.implements) { - for (const girImplements of girClass.implements) { - if (girImplements.$.name) { - let name: string = girImplements.$.name - if (!name.includes('.')) { - name = girClass._fullSymName.substring(0, girClass._fullSymName.indexOf('.') + 1) + name - } - names.push(name) - } - } - } + // The `enum_constants` map maps the C identifiers (GTK_BUTTON_TYPE_Y) + // to the name of the enum (Button) to resolve references (Gtk.Button.Y) + Array.from(building.members.values()) + .filter((m): m is IntrospectedEnum => m instanceof IntrospectedEnum) + .forEach((m) => { + m.members.forEach((member) => { + building.enum_constants.set(member.c_identifier, [m.name, member.name] as const) + }) + }) - if (names.length > 0) { - const className = girClass._fullSymName - const arr: string[] = inheritanceTable[className] || [] - inheritanceTable[className] = arr.concat(names) - } + // Get the requested classes + if (ns.class) { + ns.class + ?.filter(isIntrospectable) + .map((klass) => IntrospectedClass.fromXML(klass, building, options)) + .filter(importConflicts) + .forEach((c) => building.members.set(c.name, c)) + } + + if (ns.record) { + ns.record + ?.filter(isIntrospectable) + .map((record) => IntrospectedRecord.fromXML(record, building, options)) + .filter(importConflicts) + .forEach((c) => building.members.set(c.name, c)) + } + + if (ns.union) { + ns.union + ?.filter(isIntrospectable) + .map((union) => IntrospectedRecord.fromXML(union, building, options)) + .filter(importConflicts) + .forEach((c) => building.members.set(c.name, c)) + } + + if (ns.interface) { + ns.interface + ?.map((inter) => IntrospectedInterface.fromXML(inter, building, options)) + .filter(importConflicts) + .forEach((c) => building.members.set(c.name, c)) + } + + if (ns.alias) { + type NamedType = GirType & { $: { name: string } } + + ns.alias + ?.filter(isIntrospectable) + // Avoid attempting to alias non-introspectable symbols. + .map((b) => { + b.type = b.type + ?.filter((t): t is NamedType => !!(t && t.$.name)) + .map((t) => { + if ( + t.$.name && + !building.hasSymbol(t.$.name) && + !isPrimitiveType(t.$.name) && + !t.$.name.includes('.') + ) { + return { $: { name: 'unknown', 'c:type': 'unknown' } } as GirType + } + + return t + }) + + return b + }) + .map((alias) => IntrospectedAlias.fromXML(alias, building, options)) + .filter((alias): alias is IntrospectedAlias => alias != null) + .forEach((c) => building.members.set(c.name, c)) } - } - private loadTypes(): void { - if (this.ns.bitfield) this.annotateAndRegisterGirElement(this.ns.bitfield) - if (this.ns.callback) this.annotateAndRegisterGirElement(this.ns.callback) - if (this.ns.class) this.annotateAndRegisterGirElement(this.ns.class) - if (this.ns.constant) this.annotateAndRegisterGirElement(this.ns.constant) - if (this.ns.enumeration) this.annotateAndRegisterGirElement(this.ns.enumeration) - if (this.ns.function) this.annotateAndRegisterGirElement(this.ns.function) - if (this.ns.interface) this.annotateAndRegisterGirElement(this.ns.interface) - if (this.ns.record) this.annotateAndRegisterGirElement(this.ns.record) - if (this.ns.union) this.annotateAndRegisterGirElement(this.ns.union) - if (this.ns.alias) this.annotateAndRegisterGirElement(this.ns.alias) - - if (this.ns.callback) for (const girCallback of this.ns.callback) this.annotateFunctionArguments(girCallback) - - for (const girClass of this.ns.class || []) { - this.annotateClass(girClass, 'class') - } - for (const girClass of this.ns.record || []) { - this.annotateClass(girClass, 'record') - } - for (const girClass of this.ns.interface || []) { - this.annotateClass(girClass, 'interface') - } + building.namespace = building.ns.name + building.version = building.ns.version + building.packageName = `${building.namespace}-${building.version}` + building.libraryVersion = new LibraryVersion(ns.constant, building.version) + building.transformation = new Transformation(config) - if (this.ns.function) this.annotateFunctions(this.ns.function) - if (this.ns.callback) this.annotateFunctions(this.ns.callback) + building.log = new Logger(config.environment, config.verbose, building.packageName || 'GirModule') + building.conflictResolver = new ConflictResolver(config.environment, config.verbose) + building.inject = new Injector(building.config.environment) + building.importNamespace = building.transformation.transformModuleNamespaceName(building.packageName) + building.importName = building.transformation.transformImportName(building.packageName) + building.symTable = new SymTable(building.config, building.packageName, building.namespace) - if (this.ns.constant) this.annotateVariables(this.ns.constant) + return building } /** @@ -2951,8 +1416,8 @@ export class GirModule { * This is done in the `GenerationHandler`. */ public init(inheritanceTable: InheritanceTable) { - this.loadTypes() - this.loadInheritance(inheritanceTable) + // this.loadTypes() + // this.loadInheritance(inheritanceTable) } /** @@ -2967,6 +1432,70 @@ export class GirModule { } } - this.setModuleTsData() + // this.setModuleTsData() } } + +export const isIntrospectable = (e: { $?: GirInfoAttrs }) => + !e || !e.$ || !e.$.introspectable || e.$.introspectable === '1' +export const isDeprecated = (e: { $: GirInfoAttrs }) => e && e.$ && e.$.deprecated === '1' +export const deprecatedVersion = (e: { $: GirInfoAttrs }) => e?.$?.['deprecated-version'] +export const introducedVersion = (e: { $: GirInfoAttrs }) => e?.$?.version + +export function promisifyNamespaceFunctions(namespace: GirModule) { + return namespace.members.forEach((node) => { + if (!(node instanceof IntrospectedFunction)) return + + if (node.parameters.length < 1) return + + const last_param = node.parameters[node.parameters.length - 1] + + if (!last_param) return + + const last_param_unwrapped = last_param.type.unwrap() + + if (!(last_param_unwrapped instanceof ClosureType)) return + + const internal = last_param_unwrapped.type + + if (internal instanceof TypeIdentifier && internal.is('Gio', 'AsyncReadyCallback')) { + const async_res = [ + ...Array.from(namespace.members.values()).filter( + (m): m is IntrospectedFunction => m instanceof IntrospectedFunction, + ), + ].find((m) => m.name === `${node.name.replace(/_async$/, '')}_finish` || m.name === `${node.name}_finish`) + + if (async_res) { + const async_parameters = node.parameters.slice(0, -1).map((p) => p.copy()) + const sync_parameters = node.parameters.map((p) => p.copy({ isOptional: false })) + const output_parameters = async_res.output_parameters + + let async_return = new PromiseType(async_res.return()) + + if (output_parameters.length > 0) { + const raw_return = async_res.return() + if (raw_return.equals(VoidType) || raw_return.equals(BooleanType)) { + const [output_type, ...output_types] = output_parameters.map((op) => op.type) + async_return = new PromiseType(new TupleType(output_type, ...output_types)) + } else { + const [...output_types] = output_parameters.map((op) => op.type) + async_return = new PromiseType(new TupleType(raw_return, ...output_types)) + } + } + + namespace.members.set(node.name, [ + node.copy({ + parameters: async_parameters, + return_type: async_return, + }), + node.copy({ + parameters: sync_parameters, + }), + node.copy({ + return_type: new BinaryType(async_return, node.return()), + }), + ]) + } + } + }) +} diff --git a/packages/lib/src/newlib/formatters/default.ts b/packages/lib/src/newlib/formatters/default.ts index fe08c4415..77c756fc8 100644 --- a/packages/lib/src/newlib/formatters/default.ts +++ b/packages/lib/src/newlib/formatters/default.ts @@ -1,7 +1,7 @@ import { Formatter } from "./formatter.js"; export class DefaultFormatter extends Formatter { - format(source: string): string { - return source; + format(source: string): Promise { + return Promise.resolve(source); } } diff --git a/packages/lib/src/newlib/formatters/formatter.ts b/packages/lib/src/newlib/formatters/formatter.ts index ee7c36d4b..f889904e0 100644 --- a/packages/lib/src/newlib/formatters/formatter.ts +++ b/packages/lib/src/newlib/formatters/formatter.ts @@ -1,3 +1,3 @@ export abstract class Formatter { - abstract format(source: string): string; + abstract format(source: string): Promise; } diff --git a/packages/lib/src/newlib/formatters/json.ts b/packages/lib/src/newlib/formatters/json.ts index f38765413..41ddf6b90 100644 --- a/packages/lib/src/newlib/formatters/json.ts +++ b/packages/lib/src/newlib/formatters/json.ts @@ -1,7 +1,7 @@ import { Formatter } from "./formatter.js"; export class JSONFormatter extends Formatter { - format(source: string): string { - return JSON.stringify(JSON.parse(source), null, 4); + format(source: string): Promise { + return Promise.resolve(JSON.stringify(JSON.parse(source), null, 4)); } } diff --git a/packages/lib/src/newlib/generators/dts-inline.ts b/packages/lib/src/newlib/generators/dts-inline.ts index 54c44c80d..84c650662 100644 --- a/packages/lib/src/newlib/generators/dts-inline.ts +++ b/packages/lib/src/newlib/generators/dts-inline.ts @@ -1,12 +1,12 @@ import { IntrospectedNamespace, promisifyNamespaceFunctions } from "../gir/namespace.js"; -import { GirBase } from "../gir.js"; import { GenerationOptions } from "../types.js"; import { override as overrideGLib } from "./dts/glib.js"; import { override as overrideGObject } from "./dts/gobject.js"; import { override as overrideGio } from "./dts/gio.js"; import { DtsGenerator } from "./dts.js"; +import { IntrospectedNamespaceMember } from "../gir/base.js"; export class DtsInlineGenerator extends DtsGenerator { constructor(namespace: IntrospectedNamespace, options: GenerationOptions) { @@ -51,17 +51,18 @@ export class DtsInlineGenerator extends DtsGenerator { const content = Array.from(node.members.values()) .map(m => { return `${(Array.isArray(m) ? m : [m]) - .map(m => (m.emit ? (m as GirBase).asString(this) : "")) + .map(m => (m.emit ? (m as IntrospectedNamespaceMember).asString(this) : "")) .join("\n")}`; }) .join("\n"); + const versionedImports = true; // TODO: options.versionedImports // Resolve imports after we stringify everything else, sometimes we have to ad-hoc add an import. const imports = Array.from(node.getImports()) .map( ([i, version]) => - `import * as ${i} from "${options.importPrefix}${i.toLowerCase()}${ - options.versionedImports ? version.toLowerCase().split(".")[0] : "" + `import * as ${i} from "${options.npmScope}${i.toLowerCase()}${ + versionedImports ? version.toLowerCase().split(".")[0] : "" }";` ) .join(`${"\n"}`); diff --git a/packages/lib/src/newlib/generators/dts-modules.ts b/packages/lib/src/newlib/generators/dts-modules.ts index 9f41ba6e5..ac142bf8b 100644 --- a/packages/lib/src/newlib/generators/dts-modules.ts +++ b/packages/lib/src/newlib/generators/dts-modules.ts @@ -1,12 +1,12 @@ import { IntrospectedNamespace, promisifyNamespaceFunctions } from "../gir/namespace.js"; -import { GirBase } from "../gir.js"; import { GenerationOptions } from "../types.js"; import { override as overrideGLib } from "./dts/glib.js"; import { override as overrideGObject } from "./dts/gobject.js"; import { override as overrideGio } from "./dts/gio.js"; import { DtsGenerator, versionImportFormat } from "./dts.js"; +import { IntrospectedNamespaceMember } from "../gir/base.js"; export class DtsModuleGenerator extends DtsGenerator { constructor(namespace: IntrospectedNamespace, options: GenerationOptions) { @@ -51,21 +51,28 @@ export class DtsModuleGenerator extends DtsGenerator { const content = Array.from(node.members.values()) .map(m => { return `${(Array.isArray(m) ? m : [m]) - .map(m => (m.emit ? (m as GirBase).asString(this) : "")) + .map(m => { + const content = (m as IntrospectedNamespaceMember).asString(this); + + return m.emit ? content : ""; + }) .join("\n")}`; }) .join("\n"); - const pathSuffix = options.outputFormat === "folder" ? "/index.d.ts" : ".d.ts"; - const referenceType = options.importPrefix.startsWith(".") ? "path" : "types"; + const versionedImports = true; // TODO options.versionedImports + const pathSuffix = options.buildType === "lib" ? "/index.d.ts" : ".d.ts"; + const referenceType = + /*options.importPrefix.startsWith(".")*/ options.buildType === "lib" ? "path" : "types"; const references = [ ...(node.__dts__references ?? []), ...Array.from(node.getImports()).map( ([i, version]) => - `/// - `import ${i} from 'gi://${i}${options.versionedImports ? `?version=${version}` : ""}';` - ) + .map(([i, version]) => `import ${i} from 'gi://${i}${versionedImports ? `?version=${version}` : ""}';`) .join("\n"); const metadata = ` diff --git a/packages/lib/src/newlib/generators/dts.ts b/packages/lib/src/newlib/generators/dts.ts index fc6de3947..37cb366d8 100644 --- a/packages/lib/src/newlib/generators/dts.ts +++ b/packages/lib/src/newlib/generators/dts.ts @@ -13,14 +13,15 @@ import { } from "../gir/class.js"; import { IntrospectedConstant } from "../gir/const.js"; import { IntrospectedEnum, IntrospectedError, GirEnumMember } from "../gir/enum.js"; -import { GirProperty, Field } from "../gir/property.js"; +import { IntrospectedProperty, IntrospectedField } from "../gir/property.js"; import { IntrospectedSignal, IntrospectedSignalType } from "../gir/signal.js"; import { IntrospectedFunction, IntrospectedConstructor, IntrospectedFunctionParameter, IntrospectedCallback, - IntrospectedDirectAllocationConstructor + IntrospectedDirectAllocationConstructor, + IntrospectedClassCallback } from "../gir/function.js"; import { IntrospectedClassFunction, @@ -40,12 +41,12 @@ import { Generic, ConflictType, TypeConflict, - BinaryType, - GirBase + BinaryType } from "../gir.js"; import { GirDirection } from "@gi.ts/parser"; import { IntrospectedAlias } from "../gir/alias.js"; -import { GenerationOptions } from "../types.js"; +import { AnyIntrospectedType } from "../gir/base.js"; +import { GenerateConfig } from "../../types/generate-config.js"; export function versionImportFormat(versionFormat: string, namespace: string, version: string) { const versionSlug = version.toLowerCase().split(".")[0]; @@ -59,7 +60,7 @@ export function versionImportFormat(versionFormat: string, namespace: string, ve } export abstract class DtsGenerator extends FormatGenerator { - constructor(namespace: IntrospectedNamespace, options: GenerationOptions) { + constructor(namespace: IntrospectedNamespace, options: GenerateConfig) { super(namespace, options); } @@ -101,7 +102,7 @@ export abstract class DtsGenerator extends FormatGenerator { return ""; } - generateCallbackType(node: IntrospectedCallback): [string, string] { + generateCallbackType(node: IntrospectedCallback | IntrospectedClassCallback): [string, string] { const { namespace, options } = this; const Parameters = this.generateParameters(node.parameters); @@ -117,10 +118,14 @@ export abstract class DtsGenerator extends FormatGenerator { return ["", `(${Parameters}) => ${node.return().resolve(namespace, options).print(namespace, options)}`]; } - generateCallback(node: IntrospectedCallback): string { + generateCallback(node: IntrospectedCallback | IntrospectedClassCallback): string { return `${this.docString(node)}export type ${node.name}${this.generateCallbackType(node).join(" = ")};`; } + generateClassCallback(node: IntrospectedClassCallback): string { + return this.generateCallback(node); + } + generateReturn(return_type: TypeExpression, output_parameters: IntrospectedFunctionParameter[]) { const { namespace, options } = this; @@ -189,11 +194,12 @@ ${this.docString(node)}export enum ${node.name} { const GLib = namespace.assertInstalledImport("GLib"); const GLibError = GLib.assertClass("Error"); - clazz.parent = GLibError.getType(); + clazz.superType = GLibError.getType(); // Manually construct a GLib.Error constructor. clazz.mainConstructor = new IntrospectedConstructor({ name: "new", + parent: clazz, parameters: [ new IntrospectedFunctionParameter({ name: "options", @@ -244,8 +250,8 @@ ${this.docString(node)}export enum ${node.name} { protected extends(node: IntrospectedBaseClass) { const { namespace: ns, options } = this; - if (node.parent) { - const ResolvedType = node.parent.resolveIdentifier(ns, options); + if (node.superType) { + const ResolvedType = node.superType.resolveIdentifier(ns, options); const Type = ResolvedType?.print(ns, options); if (Type) { @@ -253,7 +259,7 @@ ${this.docString(node)}export enum ${node.name} { } throw new Error( - `Unable to resolve type: ${node.parent.name} from ${node.parent.namespace} in ${node.namespace.name} ${node.namespace.version}` + `Unable to resolve type: ${node.superType.name} from ${node.superType.namespace} in ${node.namespace.name} ${node.namespace.version}` ); } @@ -438,11 +444,12 @@ ${this.docString(node)}export class ${name}${Generics}${Extends} {${ } else { MainConstructor = `\nconstructor(properties?: Partial<${name}.ConstructorProperties${GenericTypes}>, ...args: any[]);\n`; - if (!options.noInitTypes) { - MainConstructor += `_init(properties?: Partial<${name}.ConstructorProperties${GenericTypes}>, ...args: any[]): void;\n`; - } else { - MainConstructor += "_init(...args: any[]): void;\n"; - } + // TODO: options migration + //if (!options.noInitTypes) { + // MainConstructor += `_init(properties?: Partial<${name}.ConstructorProperties${GenericTypes}>, ...args: any[]): void;\n`; + //} else { + MainConstructor += "_init(...args: any[]): void;\n"; + //} } const ConstructorProps = filterConflicts( @@ -642,7 +649,7 @@ ${this.docString(node)}export class ${name}${Generics}${Extends} {${ }`; } - generateField(node: Field): string { + generateField(node: IntrospectedField): string { const { namespace, options } = this; const { name, computed } = node; const invalid = isInvalid(name); @@ -670,7 +677,7 @@ ${this.docString(node)}export class ${name}${Generics}${Extends} {${ .rootPrint(namespace, options)};`; } - generateProperty(node: GirProperty, construct: boolean = false): string { + generateProperty(node: IntrospectedProperty, construct: boolean = false): string { const { namespace, options } = this; const invalid = isInvalid(node.name); @@ -781,9 +788,9 @@ ${this.docString(node)}export class ${name}${Generics}${Extends} {${ } } - docString(node: GirBase) { + docString(node: AnyIntrospectedType) { // TODO: Support node.doc not being a string? - return typeof node.doc === "string" && this.options.withDocs + return typeof node.doc === "string" && !this.options.noComments ? `/** ${node.doc .split("\n") @@ -832,7 +839,7 @@ ${node.doc } generateDirectAllocationConstructor(node: IntrospectedDirectAllocationConstructor): string { - const ConstructorFields = node.fields.map(field => field.asString(this)).join("\n"); + const ConstructorFields = node.parameters.map(param => param.asField().asString(this)).join("\n"); return ` constructor(properties?: Partial<{ diff --git a/packages/lib/src/newlib/generators/generator.ts b/packages/lib/src/newlib/generators/generator.ts index 1098be7c7..485b5763f 100644 --- a/packages/lib/src/newlib/generators/generator.ts +++ b/packages/lib/src/newlib/generators/generator.ts @@ -2,21 +2,22 @@ import { IntrospectedNamespace } from "../gir/namespace.js"; import { IntrospectedClass, IntrospectedRecord, IntrospectedInterface } from "../gir/class.js"; import { IntrospectedConstant } from "../gir/const.js"; import { IntrospectedEnum, IntrospectedError, GirEnumMember } from "../gir/enum.js"; -import { GirProperty, Field } from "../gir/property.js"; +import { IntrospectedProperty, IntrospectedField } from "../gir/property.js"; import { IntrospectedSignal, IntrospectedSignalType } from "../gir/signal.js"; import { IntrospectedFunction, IntrospectedFunctionParameter, IntrospectedConstructor, IntrospectedCallback, - IntrospectedDirectAllocationConstructor + IntrospectedDirectAllocationConstructor, + IntrospectedClassCallback } from "../gir/function.js"; import { IntrospectedClassFunction } from "../gir/function.js"; import { IntrospectedStaticClassFunction } from "../gir/function.js"; import { IntrospectedVirtualClassFunction } from "../gir/function.js"; import { IntrospectedAlias } from "../gir/alias.js"; import { TypeExpression } from "../gir.js"; -import { GenerationOptions } from "../types.js"; +import { GenerateConfig } from "../../types/generate-config.js"; export interface GenericDescriptor { type: TypeExpression; @@ -25,9 +26,9 @@ export interface GenericDescriptor { export abstract class FormatGenerator { protected namespace: IntrospectedNamespace; - protected options: GenerationOptions; + protected options: GenerateConfig; - constructor(namespace: IntrospectedNamespace, options: GenerationOptions) { + constructor(namespace: IntrospectedNamespace, options: GenerateConfig) { this.namespace = namespace; this.options = options; } @@ -36,6 +37,7 @@ export abstract class FormatGenerator { abstract stringifyNamespace(node: IntrospectedNamespace): Promise; abstract generateCallback(node: IntrospectedCallback): T; + abstract generateClassCallback(node: IntrospectedClassCallback): T; abstract generateAlias(node: IntrospectedAlias): T; abstract generateConstructor(node: IntrospectedConstructor): T; abstract generateDirectAllocationConstructor(node: IntrospectedDirectAllocationConstructor): T; @@ -48,8 +50,8 @@ export abstract class FormatGenerator { abstract generateConst(node: IntrospectedConstant): T; abstract generateClass(node: IntrospectedClass): T; abstract generateParameter(node: IntrospectedFunctionParameter): T; - abstract generateProperty(node: GirProperty, construct?: boolean): T; - abstract generateField(node: Field): T; + abstract generateProperty(node: IntrospectedProperty, construct?: boolean): T; + abstract generateField(node: IntrospectedField): T; abstract generateSignal(node: IntrospectedSignal, type?: IntrospectedSignalType): T; abstract generateFunction(node: IntrospectedFunction): T; abstract generateClassFunction(node: IntrospectedClassFunction): T; diff --git a/packages/lib/src/newlib/generators/json.ts b/packages/lib/src/newlib/generators/json.ts index c4637b6a6..c4b70949b 100644 --- a/packages/lib/src/newlib/generators/json.ts +++ b/packages/lib/src/newlib/generators/json.ts @@ -4,14 +4,15 @@ import { IntrospectedNamespace } from "../gir/namespace.js"; import { IntrospectedBaseClass, IntrospectedRecord, IntrospectedInterface, IntrospectedClass } from "../gir/class.js"; import { IntrospectedConstant } from "../gir/const.js"; import { IntrospectedEnum, IntrospectedError, GirEnumMember } from "../gir/enum.js"; -import { GirProperty, Field } from "../gir/property.js"; +import { IntrospectedProperty, IntrospectedField } from "../gir/property.js"; import { IntrospectedSignal, IntrospectedSignalType } from "../gir/signal.js"; import { IntrospectedFunction, IntrospectedConstructor, IntrospectedFunctionParameter, IntrospectedCallback, - IntrospectedDirectAllocationConstructor + IntrospectedDirectAllocationConstructor, + IntrospectedClassCallback } from "../gir/function.js"; import { IntrospectedClassFunction, @@ -32,14 +33,14 @@ import { TupleType, NullableType, ClosureType, - GirBase, AnyFunctionType, TypeConflict, - GirMetadata + IntrospectedMetadata } from "../gir.js"; import { GirDirection } from "@gi.ts/parser"; import { IntrospectedAlias } from "../gir/alias.js"; import { GenerationOptions } from "../types.js"; +import { AnyIntrospectedType, IntrospectedNamespaceMember } from "../gir/base.js"; export const enum NodeKind { class = "class", @@ -336,7 +337,10 @@ export class JsonGenerator extends FormatGenerator { private generateDoc(doc: string): string { const { namespace } = this; - function resolveClass(ns: IntrospectedNamespace, className: string): readonly [GirBase | null, boolean] { + function resolveClass( + ns: IntrospectedNamespace, + className: string + ): readonly [IntrospectedNamespaceMember | null, boolean] { let classes = ns.getMembers(className); let plural = false; @@ -592,7 +596,7 @@ export class JsonGenerator extends FormatGenerator { }); } - private generateMetadata(metadata: GirMetadata): MetadataJson { + private generateMetadata(metadata: IntrospectedMetadata): MetadataJson { return { ...metadata } as MetadataJson; } @@ -612,7 +616,7 @@ export class JsonGenerator extends FormatGenerator { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - generateCallbackType(node: IntrospectedCallback): [Json, Json] { + generateCallbackType(node: IntrospectedCallback | IntrospectedClassCallback): [Json, Json] { return [{}, {}]; } @@ -631,6 +635,21 @@ export class JsonGenerator extends FormatGenerator { }; } + generateClassCallback(node: IntrospectedClassCallback): CallbackJson { + const { namespace, options } = this; + + const parameters = this.generateParameters(node.parameters); + + return { + kind: NodeKind.callback, + name: node.name, + type: this.generateCallbackType(node), + parameters, + returnType: generateType(node.return().resolve(namespace, options)), + ...this._generateDocAndMetadata(node) + }; + } + generateReturn( return_type: TypeExpression, output_parameters: IntrospectedFunctionParameter[] @@ -670,11 +689,12 @@ export class JsonGenerator extends FormatGenerator { const GLib = namespace.assertInstalledImport("GLib"); const GLibError = GLib.assertClass("Error"); - clazz.parent = GLibError.getType(); + clazz.superType = GLibError.getType(); // Manually construct a GLib.Error constructor. clazz.mainConstructor = new IntrospectedConstructor({ name: "new", + parent: clazz, parameters: [ new IntrospectedFunctionParameter({ name: "options", @@ -691,10 +711,10 @@ export class JsonGenerator extends FormatGenerator { }; } - _generateDocAndMetadata(node: GirBase) { + _generateDocAndMetadata(node: AnyIntrospectedType) { const { options } = this; - if (options.withDocs) { + if (!options.noComments) { return { private: node.isPrivate, doc: this.generateDoc(node.doc ?? "") ?? null, @@ -735,8 +755,8 @@ export class JsonGenerator extends FormatGenerator { private extends(node: IntrospectedBaseClass): TypeIdentifier | null { const { namespace: ns, options } = this; - if (node.parent) { - return node.parent.resolveIdentifier(ns, options); + if (node.superType) { + return node.superType.resolveIdentifier(ns, options); } return null; @@ -745,7 +765,7 @@ export class JsonGenerator extends FormatGenerator { generateInterface(node: IntrospectedInterface): InterfaceJson { const { namespace } = this; // If an interface does not list a prerequisite type, we fill it with GObject.Object - if (node.parent == null) { + if (node.superType == null) { const gobject = namespace.assertInstalledImport("GObject"); // TODO Optimize GObject.Object @@ -761,7 +781,7 @@ export class JsonGenerator extends FormatGenerator { ); } - node.parent = GObject.getType(); + node.superType = GObject.getType(); } const { name } = node; @@ -1002,7 +1022,7 @@ export class JsonGenerator extends FormatGenerator { }; } - generateField(node: Field): FieldJson { + generateField(node: IntrospectedField): FieldJson { const { namespace, options } = this; const { name, computed } = node; const invalid = isInvalid(name); @@ -1021,7 +1041,7 @@ export class JsonGenerator extends FormatGenerator { }; } - generateProperty(node: GirProperty, construct: boolean = false): PropertyJson { + generateProperty(node: IntrospectedProperty, construct: boolean = false): PropertyJson { const { namespace, options } = this; const invalid = isInvalid(node.name); @@ -1148,17 +1168,7 @@ export class JsonGenerator extends FormatGenerator { return { name: node.name, kind: NodeKind.constructor, - parameters: this.generateParameters( - node.fields.map( - field => - new IntrospectedFunctionParameter({ - name: field.name, - direction: GirDirection.In, - type: field.type, - isOptional: true - }) - ) - ), + parameters: this.generateParameters(node.parameters), ...this._generateDocAndMetadata(node) }; } @@ -1217,7 +1227,7 @@ export class JsonGenerator extends FormatGenerator { } generateNamespace(node: IntrospectedNamespace): Promise { - function shouldGenerate(node: GirBase) { + function shouldGenerate(node: AnyIntrospectedType) { return node.emit; } diff --git a/packages/lib/src/newlib/generics/meta.ts b/packages/lib/src/newlib/generics/meta.ts index 2007381cf..5cbb63c8f 100644 --- a/packages/lib/src/newlib/generics/meta.ts +++ b/packages/lib/src/newlib/generics/meta.ts @@ -15,10 +15,10 @@ export default { const BackgroundContent = namespace.assertClass("BackgroundContent"); const BackgroundActor = namespace.assertClass("BackgroundActor"); - const parent = BackgroundActor.parent; + const parent = BackgroundActor.superType; if (parent) { - BackgroundActor.parent = new GenerifiedTypeIdentifier(parent.name, parent.namespace, [ + BackgroundActor.superType = new GenerifiedTypeIdentifier(parent.name, parent.namespace, [ LayoutManager.getType(), BackgroundContent.getType() ]); diff --git a/packages/lib/src/newlib/generics/st.ts b/packages/lib/src/newlib/generics/st.ts index 4ab9ae7cb..ec150e07c 100644 --- a/packages/lib/src/newlib/generics/st.ts +++ b/packages/lib/src/newlib/generics/st.ts @@ -61,10 +61,12 @@ export default { constraint: Actor.getType() }); - if (StBoxLayout.parent) { - StBoxLayout.parent = new GenerifiedTypeIdentifier(StBoxLayout.parent.name, StBoxLayout.parent.namespace, [ - ClutterBoxLayout.getType() - ]); + if (StBoxLayout.superType) { + StBoxLayout.superType = new GenerifiedTypeIdentifier( + StBoxLayout.superType.name, + StBoxLayout.superType.namespace, + [ClutterBoxLayout.getType()] + ); } Bin.addGeneric({ diff --git a/packages/lib/src/newlib/generics/visitor.ts b/packages/lib/src/newlib/generics/visitor.ts index 1ab844e03..9b59b4a2f 100644 --- a/packages/lib/src/newlib/generics/visitor.ts +++ b/packages/lib/src/newlib/generics/visitor.ts @@ -80,7 +80,7 @@ export class GenericVisitor extends GirVisitor { const { namespace } = _node; - const resolvedParent = node.parent ? resolveTypeIdentifier(namespace, node.parent) : null; + const resolvedParent = node.superType ? resolveTypeIdentifier(namespace, node.superType) : null; const derivatives = node.generics.filter(generic => generic.parent != null); if (node instanceof IntrospectedClass) { @@ -142,15 +142,15 @@ export class GenericVisitor extends GirVisitor { }); } - if (node.parent) { - const parentType = node.parent; + if (node.superType) { + const parentType = node.superType; const generic = derivatives.filter(d => d.parent?.is(parentType.namespace, parentType.name)); - if (node.parent instanceof GenerifiedTypeIdentifier) { + if (node.superType instanceof GenerifiedTypeIdentifier) { // Do nothing } else if (generic.length > 0) { - node.parent = new GenerifiedTypeIdentifier( + node.superType = new GenerifiedTypeIdentifier( parentType.name, parentType.namespace, generic.map(g => g.type) @@ -167,7 +167,7 @@ export class GenericVisitor extends GirVisitor { ); if (constrainedGeneric) { - node.parent = new GenerifiedTypeIdentifier(resolved.name, resolved.namespace.name, [ + node.superType = new GenerifiedTypeIdentifier(resolved.name, resolved.namespace.name, [ constrainedGeneric.type ]); } else { @@ -183,7 +183,7 @@ export class GenericVisitor extends GirVisitor { const firstGeneric = node.generics[node.generics.length - 1]; - node.parent = new GenerifiedTypeIdentifier(resolved.name, resolved.namespace.name, [ + node.superType = new GenerifiedTypeIdentifier(resolved.name, resolved.namespace.name, [ firstGeneric.type ]); } else if ( @@ -191,7 +191,7 @@ export class GenericVisitor extends GirVisitor { c => generic.defaultType && c.identifier.equals(generic.defaultType) ) ) { - node.parent = new GenerifiedTypeIdentifier(resolved.name, resolved.namespace.name, [ + node.superType = new GenerifiedTypeIdentifier(resolved.name, resolved.namespace.name, [ node.getType() ]); } @@ -201,7 +201,7 @@ export class GenericVisitor extends GirVisitor { c => generic.defaultType && c.identifier.equals(generic.defaultType) ) ) { - node.parent = new GenerifiedTypeIdentifier(resolved.name, resolved.namespace.name, [ + node.superType = new GenerifiedTypeIdentifier(resolved.name, resolved.namespace.name, [ node.getType() ]); } diff --git a/packages/lib/src/newlib/gir.ts b/packages/lib/src/newlib/gir.ts index 86fb9867e..9f66fceb5 100644 --- a/packages/lib/src/newlib/gir.ts +++ b/packages/lib/src/newlib/gir.ts @@ -1,9 +1,10 @@ import { IntrospectedNamespace } from "./gir/namespace.js"; -import { GirProperty, Field } from "./gir/property.js"; +import { IntrospectedProperty, IntrospectedField } from "./gir/property.js"; import { GenerationOptions } from "./types.js"; import { sanitizeIdentifierName } from "./gir/util.js"; -export { IntrospectedBase as GirBase, Options as GirOptions, Metadata as GirMetadata } from "./gir/base.js"; +export { IntrospectedBase, Options as IntrospectedOptions, Metadata as IntrospectedMetadata } from "./gir/base.js"; +export * from "./gir/nodes.js"; export abstract class TypeExpression { isPointer = false; @@ -750,4 +751,4 @@ export const VoidType = new NativeType("void"); export const UnknownType = new NativeType("unknown"); export const AnyFunctionType = new NativeType("(...args: any[]) => any"); -export type GirClassField = GirProperty | Field; +export type GirClassField = IntrospectedProperty | IntrospectedField; diff --git a/packages/lib/src/newlib/gir/alias.ts b/packages/lib/src/newlib/gir/alias.ts index b1ebd03b5..340855833 100644 --- a/packages/lib/src/newlib/gir/alias.ts +++ b/packages/lib/src/newlib/gir/alias.ts @@ -1,5 +1,5 @@ import { TypeExpression } from "../gir.js"; -import { IntrospectedBase as IntrospectedBase, Options } from "./base.js"; +import { IntrospectedNamespaceMember, Options } from "./base.js"; import { GirAliasElement } from "../../index.js"; import { IntrospectedNamespace, isIntrospectable } from "./namespace.js"; @@ -8,21 +8,23 @@ import { FormatGenerator, GenericDescriptor } from "../generators/generator.js"; import { LoadOptions } from "../types.js"; import { GirVisitor } from "../visitor.js"; -export class IntrospectedAlias extends IntrospectedBase { +export class IntrospectedAlias extends IntrospectedNamespaceMember { readonly type: TypeExpression; readonly generics: GenericDescriptor[]; constructor({ + namespace, name, type, generics = [], ...args }: Options<{ + namespace: IntrospectedNamespace; name: string; type: TypeExpression; generics?: GenericDescriptor[]; }>) { - super(name, { ...args }); + super(name, namespace, { ...args }); this.type = type; this.generics = generics; @@ -37,9 +39,9 @@ export class IntrospectedAlias extends IntrospectedBase { } copy(options?: { parent?: undefined; type?: TypeExpression }): IntrospectedAlias { - const { name, type } = this; + const { name, namespace, type } = this; - return new IntrospectedAlias({ name, type: options?.type ?? type })._copyBaseProperties(this); + return new IntrospectedAlias({ name, namespace, type: options?.type ?? type })._copyBaseProperties(this); } asString>(generator: T): ReturnType { @@ -47,26 +49,25 @@ export class IntrospectedAlias extends IntrospectedBase { } static fromXML( - modName: string, + element: GirAliasElement, ns: IntrospectedNamespace, - options: LoadOptions, - _parent, - m: GirAliasElement + options: LoadOptions ): IntrospectedAlias | null { - if (!m.$.name) { - console.error(`Alias in ${modName} lacks name.`); + if (!element.$.name) { + console.error(`Alias in ${ns.name} lacks name.`); return null; } const alias = new IntrospectedAlias({ - name: sanitizeIdentifierName(ns.name, m.$.name), - type: getAliasType(modName, ns, m), - isIntrospectable: isIntrospectable(m) + namespace: ns, + name: sanitizeIdentifierName(ns.name, element.$.name), + type: getAliasType(ns.name, ns, element), + isIntrospectable: isIntrospectable(element) }); if (options.loadDocs) { - alias.doc = parseDoc(m); - alias.metadata = parseMetadata(m); + alias.doc = parseDoc(element); + alias.metadata = parseMetadata(element); } return alias; diff --git a/packages/lib/src/newlib/gir/base.ts b/packages/lib/src/newlib/gir/base.ts index e7e548de7..b68037219 100644 --- a/packages/lib/src/newlib/gir/base.ts +++ b/packages/lib/src/newlib/gir/base.ts @@ -2,6 +2,7 @@ import { FormatGenerator } from "../generators/index.js"; import { LoadOptions } from "../types.js"; import { GirVisitor } from "../visitor.js"; import { IntrospectedNamespace } from "./namespace.js"; +import type { IntrospectedBaseClass } from "./nodes.js"; export interface Metadata { deprecated?: boolean; @@ -13,11 +14,15 @@ export interface Metadata { export interface BaseOptions { isPrivate?: boolean; isIntrospectable?: boolean; + doc?: string | null; } export type Options = BaseOptions & T; -export abstract class IntrospectedBase { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AnyIntrospectedType = IntrospectedBase; + +export abstract class IntrospectedBase { name: string; doc?: string | null; metadata?: Metadata; @@ -27,12 +32,19 @@ export abstract class IntrospectedBase { private _commentWarning?: string; private _isPrivate: boolean; private _isIntrospectable: boolean; + private _parent: Parent; - constructor(name: string, options: BaseOptions = {}) { + constructor(name: string, parent: Parent, options: BaseOptions = {}) { this.name = name; + this._parent = parent; this._isPrivate = options.isPrivate ?? false; this._isIntrospectable = options.isIntrospectable ?? true; + this.doc = options.doc ?? null; + } + + get parent(): Parent { + return this._parent; } /** @@ -49,6 +61,8 @@ export abstract class IntrospectedBase { return this._commentWarning; } + abstract get namespace(): IntrospectedNamespace; + get isIntrospectable() { return this._isIntrospectable; } @@ -83,24 +97,56 @@ export abstract class IntrospectedBase { return this; } - abstract copy(options?: { parent?: IntrospectedBase }): IntrospectedBase; + abstract copy(options?: { parent?: Parent }): IntrospectedBase; - abstract accept(visitor: GirVisitor): IntrospectedBase; + abstract accept(visitor: GirVisitor): IntrospectedBase; static fromXML( + // eslint-disable-next-line + element: Record, // eslint-disable-next-line @typescript-eslint/no-unused-vars - modName: string, + parent: IntrospectedNamespace | AnyIntrospectedType, // eslint-disable-next-line @typescript-eslint/no-unused-vars - ns: IntrospectedNamespace, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - options: LoadOptions, + options: LoadOptions + ): AnyIntrospectedType | null { + throw new Error("GirBase cannot be instantiated"); + } + + abstract asString>( + generator: T + ): (T extends FormatGenerator ? R : never) | null; + abstract asString>(generator: T): unknown; +} + +export abstract class IntrospectedNamespaceMember extends IntrospectedBase { + constructor(name: string, namespace: IntrospectedNamespace, options: BaseOptions = {}) { + super(name, namespace, options); + } + + get namespace() { + return this.parent; + } + + static fromXML( + // eslint-disable-next-line + element: Record, // eslint-disable-next-line @typescript-eslint/no-unused-vars - parent: IntrospectedBase | null, + parent: IntrospectedNamespace, // eslint-disable-next-line @typescript-eslint/no-unused-vars - gir: object - ): IntrospectedBase | null { + options: LoadOptions + ): IntrospectedNamespaceMember | null { throw new Error("GirBase cannot be instantiated"); } +} - abstract asString, K>(generator: T): K | null; +export abstract class IntrospectedClassMember< + Parent extends IntrospectedBaseClass | null = IntrospectedBaseClass | null +> extends IntrospectedBase { + get namespace() { + if (!this.parent) { + throw new Error(`Failed to get namespace for ${this.name}`); + } + + return this.parent.namespace; + } } diff --git a/packages/lib/src/newlib/gir/class.ts b/packages/lib/src/newlib/gir/class.ts index e1ba01b15..2f41a96ac 100644 --- a/packages/lib/src/newlib/gir/class.ts +++ b/packages/lib/src/newlib/gir/class.ts @@ -17,20 +17,20 @@ import { TypeConflict } from "../gir.js"; import { TypeExpression } from "../gir.js"; -import { IntrospectedBase } from "./base.js"; +import { IntrospectedBase, IntrospectedClassMember, IntrospectedNamespaceMember } from "./base.js"; import { GirInterfaceElement, GirClassElement, GirRecordElement, GirDirection, GirUnionElement } from "../../index.js"; import { IntrospectedClassFunction, IntrospectedVirtualClassFunction, IntrospectedStaticClassFunction, - IntrospectedCallback, IntrospectedFunction, IntrospectedConstructor, IntrospectedFunctionParameter, - IntrospectedDirectAllocationConstructor + IntrospectedDirectAllocationConstructor, + IntrospectedClassCallback } from "./function.js"; -import { GirProperty, Field } from "./property.js"; +import { IntrospectedProperty, IntrospectedField } from "./property.js"; import { IntrospectedNamespace } from "./namespace.js"; import { sanitizeIdentifierName, @@ -52,7 +52,7 @@ export enum FilterBehavior { PRESERVE } -export function filterConflicts( +export function filterConflicts( ns: IntrospectedNamespace, c: IntrospectedBaseClass, elements: T[], @@ -65,12 +65,12 @@ export function filterConflicts( const field_conflicts = c.findParentMap(resolved_parent => { return findMap([...resolved_parent.fields], p => { if (p.name && p.name == next.name) { - if (next instanceof GirProperty) { + if (next instanceof IntrospectedProperty) { return ConflictType.ACCESSOR_PROPERTY_CONFLICT; } if ( - next instanceof Field && + next instanceof IntrospectedField && !isSubtypeOf(ns, thisType, resolved_parent.getType(), next.type, p.type) ) { return ConflictType.FIELD_NAME_CONFLICT; @@ -85,12 +85,12 @@ export function filterConflicts( ? c.findParentMap(resolved_parent => { return findMap([...resolved_parent.props], p => { if (p.name && p.name == next.name) { - if (next instanceof Field) { + if (next instanceof IntrospectedField) { return ConflictType.PROPERTY_ACCESSOR_CONFLICT; } if ( - next instanceof GirProperty && + next instanceof IntrospectedProperty && !isSubtypeOf(ns, thisType, resolved_parent.getType(), next.type, p.type) ) { console.log( @@ -125,7 +125,7 @@ export function filterConflicts( const conflict = field_conflicts || prop_conflicts || function_conflicts; if (conflict) { if (behavior === FilterBehavior.PRESERVE) { - if (next instanceof Field || next instanceof GirProperty) { + if (next instanceof IntrospectedField || next instanceof IntrospectedProperty) { prev.push( next.copy({ type: new TypeConflict(next.type, conflict) @@ -238,6 +238,7 @@ export function filterFunctionConflict< const neverOptions = { name: next.name, + parent: base, parameters: [never_param], return_type: AnyType }; @@ -364,14 +365,14 @@ export const enum ClassInjectionMember { } export interface ClassDefinition { - parent: TypeIdentifier; + superType: TypeIdentifier; interfaces: TypeIdentifier[]; mainConstructor: IntrospectedConstructor; constructors: IntrospectedConstructor[]; members: IntrospectedClassFunction[]; - props: GirProperty[]; - fields: Field[]; - callbacks: IntrospectedCallback[]; + props: IntrospectedProperty[]; + fields: IntrospectedField[]; + callbacks: IntrospectedClassCallback[]; } export interface ResolutionNode { @@ -395,17 +396,16 @@ export interface RecordResolution extends ResolutionNode, Iterable c.copy())]; @@ -449,9 +448,9 @@ export abstract class IntrospectedBaseClass extends IntrospectedBase { parent?: undefined; constructors?: IntrospectedConstructor[]; members?: IntrospectedClassFunction[]; - props?: GirProperty[]; - fields?: Field[]; - callbacks?: IntrospectedCallback[]; + props?: IntrospectedProperty[]; + fields?: IntrospectedField[]; + callbacks?: IntrospectedClassCallback[]; }): IntrospectedBaseClass; getGenericName = GenericNameGenerator.new(); @@ -484,15 +483,11 @@ export abstract class IntrospectedBaseClass extends IntrospectedBase { static fromXML( // eslint-disable-next-line @typescript-eslint/no-unused-vars - modName: string, + element: GirClassElement | GirInterfaceElement | GirRecordElement, // eslint-disable-next-line @typescript-eslint/no-unused-vars ns: IntrospectedNamespace, // eslint-disable-next-line @typescript-eslint/no-unused-vars - options: LoadOptions, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - parent, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - klass: GirClassElement | GirInterfaceElement | GirRecordElement + options: LoadOptions ): IntrospectedBaseClass { throw new Error("fromXML is not implemented on GirBaseClass"); } @@ -500,6 +495,8 @@ export abstract class IntrospectedBaseClass extends IntrospectedBase { abstract asString(generator: FormatGenerator): T; } +type ClassMember = IntrospectedClassMember | IntrospectedClassFunction | IntrospectedProperty; + export class IntrospectedClass extends IntrospectedBaseClass { signals: IntrospectedSignal[] = []; interfaces: TypeIdentifier[] = []; @@ -523,7 +520,7 @@ export class IntrospectedClass extends IntrospectedBaseClass { return visitor.visitClass?.(node) ?? node; } - hasInstanceSymbol(s: IntrospectedBase): boolean { + hasInstanceSymbol(s: S): boolean { return ( this.members.some(p => s.name === p.name && !(p instanceof IntrospectedStaticClassFunction)) || this.props.some(p => s.name === p.name) || @@ -593,15 +590,15 @@ export class IntrospectedClass extends IntrospectedBaseClass { return findMap(interfaces, i => i.findParentMap(predicate)); } - implementedProperties(potentialConflicts: IntrospectedBase[] = []) { + implementedProperties(potentialConflicts: IntrospectedBase[] = []) { const resolution = this.resolveParents(); const implemented_on_parent = [...(resolution.extends() ?? [])] .map(r => r.implements()) .flat() .map(i => i.identifier); - const properties = new Map(); + const properties = new Map(); - const validateProp = (prop: GirProperty) => + const validateProp = (prop: IntrospectedProperty) => !this.hasInstanceSymbol(prop) && !properties.has(prop.name) && potentialConflicts.every(p => prop.name !== p.name); @@ -638,7 +635,7 @@ export class IntrospectedClass extends IntrospectedBaseClass { return [...properties.values()]; } - implementedMethods(potentialConflicts: IntrospectedBase[] = []) { + implementedMethods(potentialConflicts: ClassMember[] = []) { const resolution = this.resolveParents(); const implemented_on_parent = [...(resolution.extends() ?? [])].map(r => r.implements()).flat(); const methods = new Map(); @@ -738,7 +735,7 @@ export class IntrospectedClass extends IntrospectedBaseClass { } resolveParents(): ClassResolution { - const { namespace, parent, interfaces } = this; + const { namespace, superType, interfaces } = this; return { *[Symbol.iterator]() { @@ -758,7 +755,7 @@ export class IntrospectedClass extends IntrospectedBaseClass { return z; }, extends() { - const parentType = parent; + const parentType = superType; const resolved_parent = parentType && resolveTypeIdentifier(namespace, parentType); if (resolved_parent instanceof IntrospectedClass) return resolved_parent.resolveParents(); return undefined; @@ -774,15 +771,15 @@ export class IntrospectedClass extends IntrospectedBaseClass { signals?: IntrospectedSignal[]; constructors?: IntrospectedConstructor[]; members?: IntrospectedClassFunction[]; - props?: GirProperty[]; - fields?: Field[]; - callbacks?: IntrospectedCallback[]; + props?: IntrospectedProperty[]; + fields?: IntrospectedField[]; + callbacks?: IntrospectedClassCallback[]; } = {} ): IntrospectedClass { const { name, namespace, - parent, + superType, interfaces, members, constructors, @@ -800,8 +797,8 @@ export class IntrospectedClass extends IntrospectedBaseClass { clazz._copyBaseProperties(this); - if (parent) { - clazz.parent = parent; + if (superType) { + clazz.superType = superType; } clazz._staticDefinition = _staticDefinition; @@ -826,45 +823,39 @@ export class IntrospectedClass extends IntrospectedBaseClass { return this._staticDefinition; } - static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, - _parent, - klass: GirClassElement - ): IntrospectedClass { - const name = sanitizeIdentifierName(ns.name, klass.$.name); + static fromXML(element: GirClassElement, ns: IntrospectedNamespace, options: LoadOptions): IntrospectedClass { + const name = sanitizeIdentifierName(ns.name, element.$.name); if (options.verbose) { - console.debug(` >> GirClass: Parsing definition ${klass.$.name} (${name})...`); + console.debug(` >> GirClass: Parsing definition ${element.$.name} (${name})...`); } const clazz = new IntrospectedClass(name, ns); if (options.loadDocs) { - clazz.doc = parseDoc(klass); - clazz.metadata = parseMetadata(klass); + clazz.doc = parseDoc(element); + clazz.metadata = parseMetadata(element); } - if (klass.$["glib:type-name"]) { - clazz.resolve_names.push(klass.$["glib:type-name"]); + if (element.$["glib:type-name"]) { + clazz.resolve_names.push(element.$["glib:type-name"]); - ns.registerResolveName(klass.$["glib:type-name"], ns.name, name); + ns.registerResolveName(element.$["glib:type-name"], ns.name, name); } - if (klass.$["glib:type-struct"]) { + if (element.$["glib:type-struct"]) { clazz.resolve_names.push(); - ns.registerResolveName(klass.$["glib:type-struct"], ns.name, name); + ns.registerResolveName(element.$["glib:type-struct"], ns.name, name); } - if (klass.$["c:type"]) { - clazz.resolve_names.push(klass.$["c:type"]); + if (element.$["c:type"]) { + clazz.resolve_names.push(element.$["c:type"]); - ns.registerResolveName(klass.$["c:type"], ns.name, name); + ns.registerResolveName(element.$["c:type"], ns.name, name); } - const typeStruct = klass.$["glib:type-struct"]; + const typeStruct = element.$["glib:type-struct"]; if (typeStruct) { clazz.registerStaticDefinition(typeStruct); @@ -875,34 +866,32 @@ export class IntrospectedClass extends IntrospectedBaseClass { try { // Setup parent type if this is an interface or class. - if (klass.$.parent) { - clazz.parent = parseTypeIdentifier(modName, klass.$.parent); + if (element.$.parent) { + clazz.superType = parseTypeIdentifier(ns.name, element.$.parent); } - if (klass.$.abstract) { + if (element.$.abstract) { clazz.isAbstract = true; } - if (Array.isArray(klass.constructor)) { + if (Array.isArray(element.constructor)) { clazz.constructors.push( - ...klass.constructor.map(constructor => - IntrospectedConstructor.fromXML(modName, ns, options, clazz, constructor) + ...element.constructor.map(constructor => + IntrospectedConstructor.fromXML(constructor, clazz, options) ) ); } - if (klass["glib:signal"]) { + if (element["glib:signal"]) { clazz.signals.push( - ...klass["glib:signal"].map(signal => - IntrospectedSignal.fromXML(modName, ns, options, clazz, signal) - ) + ...element["glib:signal"].map(signal => IntrospectedSignal.fromXML(signal, clazz, options)) ); } // Properties - if (klass.property) { - klass.property.forEach(prop => { - const property = GirProperty.fromXML(modName, ns, options, clazz, prop); + if (element.property) { + element.property.forEach(prop => { + const property = IntrospectedProperty.fromXML(prop, clazz, options); switch (options.propertyCase) { case "both": clazz.props.push(property); @@ -926,33 +915,31 @@ export class IntrospectedClass extends IntrospectedBaseClass { } // Instance Methods - if (klass.method) { + if (element.method) { clazz.members.push( - ...klass.method.map(method => - IntrospectedClassFunction.fromXML(modName, ns, options, clazz, method) - ) + ...element.method.map(method => IntrospectedClassFunction.fromXML(method, clazz, options)) ); } // Fields - if (klass.field) { - klass.field + if (element.field) { + element.field .filter(field => !("callback" in field)) .forEach(field => { - const f = Field.fromXML(modName, ns, options, null, field); + const f = IntrospectedField.fromXML(field, clazz); clazz.fields.push(f); }); } - if (klass.implements) { - klass.implements.forEach(implementee => { + if (element.implements) { + element.implements.forEach(implementee => { const name = implementee.$.name; - const type = parseTypeIdentifier(modName, name); + const type = parseTypeIdentifier(ns.name, name); // Sometimes namespaces will implicitly import // other namespaces like Atk via interface implements. - if (type && type.namespace && type.namespace !== modName && !ns.hasImport(type.namespace)) { + if (type && type.namespace && type.namespace !== ns.name && !ns.hasImport(type.namespace)) { ns.addImport(type.namespace); } @@ -963,33 +950,31 @@ export class IntrospectedClass extends IntrospectedBaseClass { } // Callback Types - if (klass.callback) { + if (element.callback) { clazz.callbacks.push( - ...klass.callback.map(callback => { + ...element.callback.map(callback => { if (options.verbose) { - console.debug(`Adding callback ${callback.$.name} for ${modName}`); + console.debug(`Adding callback ${callback.$.name} for ${ns.name}`); } - return IntrospectedCallback.fromXML(modName, ns, options, clazz, callback); + return IntrospectedClassCallback.fromXML(callback, clazz, options); }) ); } // Virtual Methods - if (klass["virtual-method"]) { + if (element["virtual-method"]) { clazz.members.push( - ...klass["virtual-method"].map(method => - IntrospectedVirtualClassFunction.fromXML(modName, ns, options, clazz, method) + ...element["virtual-method"].map(method => + IntrospectedVirtualClassFunction.fromXML(method, clazz, options) ) ); } // Static methods (functions) - if (klass.function) { + if (element.function) { clazz.members.push( - ...klass.function.map(func => - IntrospectedStaticClassFunction.fromXML(modName, ns, options, clazz, func) - ) + ...element.function.map(func => IntrospectedStaticClassFunction.fromXML(func, clazz, options)) ); } } catch (e) { @@ -1078,7 +1063,7 @@ export class IntrospectedRecord extends IntrospectedBaseClass { } resolveParents(): RecordResolution { - const { namespace, parent } = this; + const { namespace, superType } = this; return { *[Symbol.iterator]() { @@ -1090,8 +1075,7 @@ export class IntrospectedRecord extends IntrospectedBaseClass { } }, extends() { - const parentType = parent; - const resolved_parent = parentType ? resolveTypeIdentifier(namespace, parentType) : undefined; + const resolved_parent = superType ? resolveTypeIdentifier(namespace, superType) : undefined; if (resolved_parent instanceof IntrospectedRecord) return resolved_parent.resolveParents(); return undefined; @@ -1106,15 +1090,15 @@ export class IntrospectedRecord extends IntrospectedBaseClass { parent?: undefined; constructors?: IntrospectedConstructor[]; members?: IntrospectedClassFunction[]; - props?: GirProperty[]; - fields?: Field[]; - callbacks?: IntrospectedCallback[]; + props?: IntrospectedProperty[]; + fields?: IntrospectedField[]; + callbacks?: IntrospectedClassCallback[]; } = {} ): IntrospectedRecord { const { name, namespace, - parent, + superType, members, constructors, _isForeign, @@ -1130,8 +1114,8 @@ export class IntrospectedRecord extends IntrospectedBaseClass { clazz._copyBaseProperties(this); - if (parent) { - clazz.parent = parent; + if (superType) { + clazz.superType; } clazz._structFor = _structFor; @@ -1154,92 +1138,87 @@ export class IntrospectedRecord extends IntrospectedBaseClass { } static fromXML( - modName: string, + element: GirRecordElement | GirUnionElement, namespace: IntrospectedNamespace, - options: LoadOptions, - klass: GirRecordElement | GirUnionElement + options: LoadOptions ): IntrospectedRecord { - if (!klass.$.name) { + if (!element.$.name) { throw new Error("Invalid GIR File: No name provided for union."); } - const name = sanitizeIdentifierName(namespace.name, klass.$.name); + const name = sanitizeIdentifierName(namespace.name, element.$.name); if (options.verbose) { - console.debug(` >> GirRecord: Parsing definition ${klass.$.name} (${name})...`); + console.debug(` >> GirRecord: Parsing definition ${element.$.name} (${name})...`); } const clazz = new IntrospectedRecord({ name, namespace }); clazz.setPrivate( - klass.$.name.startsWith("_") || - ("disguised" in klass.$ && klass.$.disguised === "1") || + element.$.name.startsWith("_") || + ("disguised" in element.$ && element.$.disguised === "1") || // Don't generate records for structs - (typeof klass.$["glib:is-gtype-struct-for"] === "string" && !!klass.$["glib:is-gtype-struct-for"]) + (typeof element.$["glib:is-gtype-struct-for"] === "string" && !!element.$["glib:is-gtype-struct-for"]) ); - if (typeof klass.$["glib:is-gtype-struct-for"] === "string" && !!klass.$["glib:is-gtype-struct-for"]) { + if (typeof element.$["glib:is-gtype-struct-for"] === "string" && !!element.$["glib:is-gtype-struct-for"]) { clazz.noEmit(); // This let's us replace these references when generating. - clazz._structFor = parseTypeIdentifier(modName, klass.$["glib:is-gtype-struct-for"]); + clazz._structFor = parseTypeIdentifier(namespace.name, element.$["glib:is-gtype-struct-for"]); } else { - if (klass.$["glib:type-name"]) { - clazz.resolve_names.push(klass.$["glib:type-name"]); + if (element.$["glib:type-name"]) { + clazz.resolve_names.push(element.$["glib:type-name"]); - namespace.registerResolveName(klass.$["glib:type-name"], namespace.name, name); + namespace.registerResolveName(element.$["glib:type-name"], namespace.name, name); } - if (klass.$["c:type"]) { - clazz.resolve_names.push(klass.$["c:type"]); + if (element.$["c:type"]) { + clazz.resolve_names.push(element.$["c:type"]); - namespace.registerResolveName(klass.$["c:type"], namespace.name, name); + namespace.registerResolveName(element.$["c:type"], namespace.name, name); } } if (options.loadDocs) { - clazz.doc = parseDoc(klass); - clazz.metadata = parseMetadata(klass); + clazz.doc = parseDoc(element); + clazz.metadata = parseMetadata(element); } try { // Instance Methods - if (klass.method) { + if (element.method) { clazz.members.push( - ...klass.method.map(method => - IntrospectedClassFunction.fromXML(modName, namespace, options, clazz, method) - ) + ...element.method.map(method => IntrospectedClassFunction.fromXML(method, clazz, options)) ); } // Constructors - if (Array.isArray(klass.constructor)) { - klass.constructor.forEach(constructor => { - const c = IntrospectedConstructor.fromXML(modName, namespace, options, clazz, constructor); + if (Array.isArray(element.constructor)) { + element.constructor.forEach(constructor => { + const c = IntrospectedConstructor.fromXML(constructor, clazz, options); clazz.constructors.push(c); }); } // Static methods (functions) - if (klass.function) { + if (element.function) { clazz.members.push( - ...klass.function.map(func => - IntrospectedStaticClassFunction.fromXML(modName, namespace, options, clazz, func) - ) + ...element.function.map(func => IntrospectedStaticClassFunction.fromXML(func, clazz, options)) ); } // Is this a foreign type? (don't allow construction if foreign) - clazz._isForeign = "foreign" in klass.$ && klass.$.foreign === "1"; + clazz._isForeign = "foreign" in element.$ && element.$.foreign === "1"; // Fields (for "non-class" records) - if (klass.field) { + if (element.field) { clazz.fields.push( - ...klass.field + ...element.field .filter(field => !("callback" in field)) - .map(field => Field.fromXML(modName, namespace, options, null, field)) + .map(field => IntrospectedField.fromXML(field, clazz)) ); } } catch (e) { @@ -1408,15 +1387,15 @@ export class IntrospectedInterface extends IntrospectedBaseClass { noParent?: boolean; constructors?: IntrospectedConstructor[]; members?: IntrospectedClassFunction[]; - props?: GirProperty[]; - fields?: Field[]; - callbacks?: IntrospectedCallback[]; + props?: IntrospectedProperty[]; + fields?: IntrospectedField[]; + callbacks?: IntrospectedClassCallback[]; } = {} ): IntrospectedInterface { const { name, namespace, - parent, + superType, noParent, members, constructors, @@ -1433,8 +1412,8 @@ export class IntrospectedInterface extends IntrospectedBaseClass { clazz.noParent = noParent; - if (parent) { - clazz.parent = parent; + if (superType) { + clazz.superType = superType; } clazz.props = (options.props ?? props).map(p => p.copy({ parent: clazz })); @@ -1490,7 +1469,7 @@ export class IntrospectedInterface extends IntrospectedBaseClass { } resolveParents(): InterfaceResolution { - const { namespace, parent } = this; + const { namespace, superType } = this; return { *[Symbol.iterator]() { let current = this.extends(); @@ -1501,8 +1480,8 @@ export class IntrospectedInterface extends IntrospectedBaseClass { } }, extends() { - if (!parent) return undefined; - const resolved = resolveTypeIdentifier(namespace, parent); + if (!superType) return undefined; + const resolved = resolveTypeIdentifier(namespace, superType); if (resolved && (resolved instanceof IntrospectedClass || resolved instanceof IntrospectedInterface)) return resolved.resolveParents(); return undefined; @@ -1525,66 +1504,63 @@ export class IntrospectedInterface extends IntrospectedBaseClass { } static fromXML( - modName: string, + element: GirInterfaceElement, namespace: IntrospectedNamespace, - options: LoadOptions, - klass: GirInterfaceElement + options: LoadOptions ): IntrospectedInterface { - const name = sanitizeIdentifierName(namespace.name, klass.$.name); + const name = sanitizeIdentifierName(namespace.name, element.$.name); if (options.verbose) { - console.debug(` >> GirInterface: Parsing definition ${klass.$.name} (${name})...`); + console.debug(` >> GirInterface: Parsing definition ${element.$.name} (${name})...`); } const clazz = new IntrospectedInterface({ name, namespace }); if (options.loadDocs) { - clazz.doc = parseDoc(klass); - clazz.metadata = parseMetadata(klass); + clazz.doc = parseDoc(element); + clazz.metadata = parseMetadata(element); } - if (klass.$["glib:type-name"]) { - clazz.resolve_names.push(klass.$["glib:type-name"]); + if (element.$["glib:type-name"]) { + clazz.resolve_names.push(element.$["glib:type-name"]); - namespace.registerResolveName(klass.$["glib:type-name"], namespace.name, name); + namespace.registerResolveName(element.$["glib:type-name"], namespace.name, name); } - if (klass.$["glib:type-struct"]) { + if (element.$["glib:type-struct"]) { clazz.resolve_names.push(); - namespace.registerResolveName(klass.$["glib:type-struct"], namespace.name, name); + namespace.registerResolveName(element.$["glib:type-struct"], namespace.name, name); } - if (klass.$["c:type"]) { - clazz.resolve_names.push(klass.$["c:type"]); + if (element.$["c:type"]) { + clazz.resolve_names.push(element.$["c:type"]); - namespace.registerResolveName(klass.$["c:type"], namespace.name, name); + namespace.registerResolveName(element.$["c:type"], namespace.name, name); } try { // Setup the "parent" (prerequisite) for this interface. - if (klass.prerequisite && klass.prerequisite[0]) { - const [prerequisite] = klass.prerequisite; + if (element.prerequisite && element.prerequisite[0]) { + const [prerequisite] = element.prerequisite; if (prerequisite.$.name) { - clazz.parent = parseTypeIdentifier(modName, prerequisite.$.name); + clazz.superType = parseTypeIdentifier(namespace.name, prerequisite.$.name); } } - if (Array.isArray(klass.constructor)) { - for (const constructor of klass.constructor) { - clazz.constructors.push( - IntrospectedConstructor.fromXML(modName, namespace, options, clazz, constructor) - ); + if (Array.isArray(element.constructor)) { + for (const constructor of element.constructor) { + clazz.constructors.push(IntrospectedConstructor.fromXML(constructor, clazz, options)); } } // Properties - if (klass.property) { + if (element.property) { clazz.props.push( - ...klass.property + ...element.property - .map(prop => GirProperty.fromXML(modName, namespace, options, clazz, prop)) + .map(prop => IntrospectedProperty.fromXML(prop, clazz, options)) .map(prop => { switch (options.propertyCase) { case "both": @@ -1607,40 +1583,36 @@ export class IntrospectedInterface extends IntrospectedBaseClass { } // Instance Methods - if (klass.method) { - for (const method of klass.method) { - const m = IntrospectedClassFunction.fromXML(modName, namespace, options, clazz, method); + if (element.method) { + for (const method of element.method) { + const m = IntrospectedClassFunction.fromXML(method, clazz, options); clazz.members.push(m); } } // Virtual Methods - if (klass["virtual-method"]) { - for (const method of klass["virtual-method"]) { - clazz.members.push( - IntrospectedVirtualClassFunction.fromXML(modName, namespace, options, clazz, method) - ); + if (element["virtual-method"]) { + for (const method of element["virtual-method"]) { + clazz.members.push(IntrospectedVirtualClassFunction.fromXML(method, clazz, options)); } } // Callback Types - if (klass.callback) { - for (const callback of klass.callback) { + if (element.callback) { + for (const callback of element.callback) { if (options.verbose) { - console.debug(`Adding callback ${callback.$.name} for ${modName}`); + console.debug(`Adding callback ${callback.$.name} for ${namespace.name}`); } - clazz.callbacks.push(IntrospectedCallback.fromXML(modName, namespace, options, clazz, callback)); + clazz.callbacks.push(IntrospectedClassCallback.fromXML(callback, clazz, options)); } } // Static methods (functions) - if (klass.function) { - for (const func of klass.function) { - clazz.members.push( - IntrospectedStaticClassFunction.fromXML(modName, namespace, options, clazz, func) - ); + if (element.function) { + for (const func of element.function) { + clazz.members.push(IntrospectedStaticClassFunction.fromXML(func, clazz, options)); } } } catch (e) { diff --git a/packages/lib/src/newlib/gir/const.ts b/packages/lib/src/newlib/gir/const.ts index 8f2211a7e..7b9e71040 100644 --- a/packages/lib/src/newlib/gir/const.ts +++ b/packages/lib/src/newlib/gir/const.ts @@ -1,5 +1,5 @@ import { TypeExpression } from "../gir.js"; -import { IntrospectedBase } from "./base.js"; +import { IntrospectedNamespaceMember, Options } from "./base.js"; import { GirConstantElement } from "../../index.js"; import { IntrospectedNamespace } from "./namespace.js"; @@ -8,21 +8,24 @@ import { FormatGenerator } from "../generators/generator.js"; import { LoadOptions } from "../types.js"; import { GirVisitor } from "../visitor.js"; -export class IntrospectedConstant extends IntrospectedBase { +export class IntrospectedConstant extends IntrospectedNamespaceMember { type: TypeExpression; value: string | null; constructor({ name, type, - value - }: { + namespace, + value, + ...options + }: Options<{ name: string; type: TypeExpression; + namespace: IntrospectedNamespace; value: string | null; isIntrospectable?: boolean; - }) { - super(name); + }>) { + super(name, namespace, options); this.type = type; this.value = value; @@ -38,7 +41,7 @@ export class IntrospectedConstant extends IntrospectedBase { copy( options: { - parent?: undefined; + parent?: IntrospectedNamespace; type?: TypeExpression; } = {} ): IntrospectedConstant { @@ -46,27 +49,23 @@ export class IntrospectedConstant extends IntrospectedBase { return new IntrospectedConstant({ name, + namespace: options.parent ?? this.namespace, type: options.type ?? type, value })._copyBaseProperties(this); } - static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, - _parent, - constant: GirConstantElement - ): IntrospectedConstant { + static fromXML(element: GirConstantElement, ns: IntrospectedNamespace, options: LoadOptions): IntrospectedConstant { const c = new IntrospectedConstant({ - name: sanitizeIdentifierName(ns.name, constant.$.name), - type: getType(modName, ns, constant), - value: constant.$.value ?? null + name: sanitizeIdentifierName(ns.name, element.$.name), + namespace: ns, + type: getType(ns, element), + value: element.$.value ?? null }); if (options.loadDocs) { - c.doc = parseDoc(constant); - c.metadata = parseMetadata(constant); + c.doc = parseDoc(element); + c.metadata = parseMetadata(element); } return c; diff --git a/packages/lib/src/newlib/gir/enum.ts b/packages/lib/src/newlib/gir/enum.ts index e1a7f34c7..42b87acfb 100644 --- a/packages/lib/src/newlib/gir/enum.ts +++ b/packages/lib/src/newlib/gir/enum.ts @@ -1,10 +1,10 @@ import { NumberType, TypeIdentifier } from "../gir.js"; -import { IntrospectedBase } from "./base.js"; +import { IntrospectedBase, IntrospectedNamespaceMember } from "./base.js"; import { GirMemberElement, GirEnumElement, GirBitfieldElement } from "../../index.js"; import { GirComplexRecord, IntrospectedRecord } from "./class.js"; -import { Field } from "./property.js"; +import { IntrospectedField } from "./property.js"; import { IntrospectedStaticClassFunction } from "./function.js"; import { IntrospectedNamespace } from "./namespace.js"; import { parseDoc, parseMetadata, sanitizeIdentifierName, sanitizeMemberName } from "./util.js"; @@ -12,15 +12,14 @@ import { FormatGenerator } from "../generators/generator.js"; import { LoadOptions } from "../types.js"; import { GirVisitor } from "../visitor.js"; -export class IntrospectedEnum extends IntrospectedBase { +export class IntrospectedEnum extends IntrospectedNamespaceMember { members = new Map(); flags: boolean = false; - namespace: IntrospectedNamespace; ns: string; constructor(name: string, namespace: IntrospectedNamespace, options: { isIntrospectable?: boolean } = {}) { - super(sanitizeIdentifierName(namespace.name, name), options); - this.namespace = namespace; + super(sanitizeIdentifierName(namespace.name, name), namespace, options); + this.ns = namespace.name; } @@ -72,8 +71,9 @@ export class IntrospectedEnum extends IntrospectedBase { clazz.fields.push( ...Array.from(this.members.values()).map(m => { - const field = new Field({ + const field = new IntrospectedField({ name: m.name, + parent: clazz, type: NumberType, writable: true, isStatic: true @@ -90,38 +90,36 @@ export class IntrospectedEnum extends IntrospectedBase { } static fromXML( - modName: string, + element: GirEnumElement | GirBitfieldElement, ns: IntrospectedNamespace, options: LoadOptions, - _parent, - m: GirEnumElement | GirBitfieldElement, flags = false ): IntrospectedEnum { - const em = new IntrospectedEnum(sanitizeMemberName(m.$.name), ns); + const em = new IntrospectedEnum(sanitizeMemberName(element.$.name), ns); - if (m.$["glib:type-name"]) { - em.resolve_names.push(m.$["glib:type-name"]); + if (element.$["glib:type-name"]) { + em.resolve_names.push(element.$["glib:type-name"]); - ns.registerResolveName(m.$["glib:type-name"], ns.name, em.name); + ns.registerResolveName(element.$["glib:type-name"], ns.name, em.name); } - if (m.$["c:type"]) { - em.resolve_names.push(m.$["c:type"]); + if (element.$["c:type"]) { + em.resolve_names.push(element.$["c:type"]); - ns.registerResolveName(m.$["c:type"], ns.name, em.name); + ns.registerResolveName(element.$["c:type"], ns.name, em.name); } if (options.loadDocs) { - em.doc = parseDoc(m); - em.metadata = parseMetadata(m); + em.doc = parseDoc(element); + em.metadata = parseMetadata(element); } - if (!m.member) { + if (!element.member) { return em; } - m.member.forEach(m => { - const member = GirEnumMember.fromXML(modName, ns, options, em, m); + element.member.forEach(m => { + const member = GirEnumMember.fromXML(m, em, options); em.members.set(member.name, member); }); @@ -132,42 +130,40 @@ export class IntrospectedEnum extends IntrospectedBase { } } -export class GirEnumMember extends IntrospectedBase { +export class GirEnumMember extends IntrospectedBase { value: string; c_identifier: string; - constructor(name: string, value: string, c_identifier: string) { - super(name); + constructor(name: string, value: string, parent: IntrospectedEnum, c_identifier: string) { + super(name, parent); this.value = value; this.c_identifier = c_identifier; } + get namespace() { + return this.parent.namespace; + } + accept(visitor: GirVisitor): GirEnumMember { const node = this.copy(); return visitor.visitEnumMember?.(node) ?? node; } copy(): GirEnumMember { - const { value, name, c_identifier } = this; + const { value, name, parent, c_identifier } = this; - return new GirEnumMember(name, value, c_identifier)._copyBaseProperties(this); + return new GirEnumMember(name, value, parent, c_identifier)._copyBaseProperties(this); } - static fromXML( - _: string, - _ns: IntrospectedNamespace, - options: LoadOptions, - _parent, - m: GirMemberElement - ): GirEnumMember { - const upper = m.$.name.toUpperCase(); - const c_identifier = m.$["c:identifier"]; + static fromXML(element: GirMemberElement, parent: IntrospectedEnum, options: LoadOptions): GirEnumMember { + const upper = element.$.name.toUpperCase(); + const c_identifier = element.$["c:identifier"]; - const enumMember = new GirEnumMember(upper, m.$.value, c_identifier); + const enumMember = new GirEnumMember(upper, element.$.value, parent, c_identifier); if (options.loadDocs) { - enumMember.doc = parseDoc(m); - enumMember.metadata = parseMetadata(m); + enumMember.doc = parseDoc(element); + enumMember.metadata = parseMetadata(element); } return enumMember; @@ -213,41 +209,39 @@ export class IntrospectedError extends IntrospectedEnum { } static fromXML( - modName: string, + element: GirEnumElement | GirBitfieldElement, ns: IntrospectedNamespace, - options: LoadOptions, - parent, - m: GirEnumElement | GirBitfieldElement + options: LoadOptions ): IntrospectedEnum { - const err = new IntrospectedError(sanitizeMemberName(m.$.name), ns); + const err = new IntrospectedError(sanitizeMemberName(element.$.name), ns); - if (m.$["glib:type-name"]) { - err.resolve_names.push(m.$["glib:type-name"]); + if (element.$["glib:type-name"]) { + err.resolve_names.push(element.$["glib:type-name"]); - ns.registerResolveName(m.$["glib:type-name"], ns.name, err.name); + ns.registerResolveName(element.$["glib:type-name"], ns.name, err.name); } - if (m.$["c:type"]) { - err.resolve_names.push(m.$["c:type"]); + if (element.$["c:type"]) { + err.resolve_names.push(element.$["c:type"]); - ns.registerResolveName(m.$["c:type"], ns.name, err.name); + ns.registerResolveName(element.$["c:type"], ns.name, err.name); } if (options.loadDocs) { - err.doc = parseDoc(m); - err.metadata = parseMetadata(m); + err.doc = parseDoc(element); + err.metadata = parseMetadata(element); } - if (m.member) { - m.member.forEach(m => { - const member = GirEnumMember.fromXML(modName, ns, options, parent, m); + if (element.member) { + element.member.forEach(m => { + const member = GirEnumMember.fromXML(m, err, options); err.members.set(member.name, member); }); } - if (isEnumElement(m) && m.function) { - m.function.forEach(f => { - const func = IntrospectedStaticClassFunction.fromXML(modName, ns, options, err, f); + if (isEnumElement(element) && element.function) { + element.function.forEach(f => { + const func = IntrospectedStaticClassFunction.fromXML(f, err, options); err.functions.set(func.name, func); }); } diff --git a/packages/lib/src/newlib/gir/function.ts b/packages/lib/src/newlib/gir/function.ts index 2852f0c41..c1d765313 100644 --- a/packages/lib/src/newlib/gir/function.ts +++ b/packages/lib/src/newlib/gir/function.ts @@ -10,7 +10,7 @@ import { FunctionType } from "../gir.js"; -import { IntrospectedBase, Options } from "./base.js"; +import { IntrospectedBase, IntrospectedClassMember, IntrospectedNamespaceMember, Options } from "./base.js"; import { GirFunctionElement, GirMethodElement, @@ -18,24 +18,26 @@ import { GirCallableParamElement, GirCallbackElement, GirVirtualMethodElement, - GirConstructorElement + GirConstructorElement, + GirModule } from "../../index.js"; import { IntrospectedNamespace, isIntrospectable } from "./namespace.js"; import { getType, isInvalid, sanitizeMemberName, sanitizeIdentifierName, parseDoc, parseMetadata } from "./util.js"; -import { IntrospectedBaseClass, IntrospectedClass } from "./class.js"; +import { IntrospectedClass } from "./class.js"; import { IntrospectedEnum } from "./enum.js"; import { IntrospectedSignal } from "./signal.js"; import { FormatGenerator } from "../generators/generator.js"; import { LoadOptions } from "../types.js"; import { GirVisitor } from "../visitor.js"; -import { Field } from "./property.js"; +import { IntrospectedField } from "./property.js"; +import { IntrospectedBaseClass } from "./nodes.js"; function hasShadow(obj: GirFunctionElement | GirMethodElement): obj is GirFunctionElement & { $: { shadows: string } } { return obj.$["shadows"] != null; } -export class IntrospectedFunction extends IntrospectedBase { +export class IntrospectedFunction extends IntrospectedNamespaceMember { readonly parameters: IntrospectedFunctionParameter[]; readonly output_parameters: IntrospectedFunctionParameter[]; readonly return_type: TypeExpression; @@ -49,6 +51,7 @@ export class IntrospectedFunction extends IntrospectedBase { return_type = UnknownType, parameters = [], output_parameters = [], + namespace, ...args }: Options<{ name: string; @@ -56,8 +59,9 @@ export class IntrospectedFunction extends IntrospectedBase { return_type?: TypeExpression; parameters?: IntrospectedFunctionParameter[]; output_parameters?: IntrospectedFunctionParameter[]; + namespace: GirModule; }>) { - super(name, { ...args }); + super(name, namespace, { ...args }); this.raw_name = raw_name; this.parameters = parameters.map(p => p.copy({ parent: this })); @@ -65,17 +69,21 @@ export class IntrospectedFunction extends IntrospectedBase { this.return_type = return_type; } - copy({ - outputParameters, - parameters, - return_type - }: { - parent?: undefined; - parameters?: IntrospectedFunctionParameter[]; - outputParameters?: IntrospectedFunctionParameter[]; - return_type?: TypeExpression; - } = {}): IntrospectedFunction { + copy( + { + parent = this.parent, + outputParameters, + parameters, + return_type + }: { + parent?: GirModule; + parameters?: IntrospectedFunctionParameter[]; + outputParameters?: IntrospectedFunctionParameter[]; + return_type?: TypeExpression; + } = { parent: this.parent } + ): IntrospectedFunction { const fn = new IntrospectedFunction({ + namespace: parent ?? this.namespace, raw_name: this.raw_name, name: this.name, return_type: return_type ?? this.return_type, @@ -90,6 +98,7 @@ export class IntrospectedFunction extends IntrospectedBase { accept(visitor: GirVisitor): IntrospectedFunction { const node = this.copy({ + parent: this.parent, parameters: this.parameters.map(p => { return p.accept(visitor); }), @@ -103,43 +112,47 @@ export class IntrospectedFunction extends IntrospectedBase { } static fromXML( - modName: string, + element: GirFunctionElement | GirMethodElement, ns: IntrospectedNamespace, - options: LoadOptions, - _parent, - func: GirFunctionElement | GirMethodElement + options: LoadOptions ): IntrospectedFunction { - let raw_name = func.$.name; + let raw_name = element.$.name; let name = sanitizeIdentifierName(null, raw_name); - if (hasShadow(func)) { - raw_name = func.$["shadows"]; - name = sanitizeIdentifierName(null, func.$["shadows"]); + if (hasShadow(element)) { + raw_name = element.$["shadows"]; + name = sanitizeIdentifierName(null, element.$["shadows"]); } let return_type: TypeExpression; - if ("return-value" in func && func["return-value"] && func["return-value"].length > 0) { - const value = func["return-value"][0]; + if ("return-value" in element && element["return-value"] && element["return-value"].length > 0) { + const value = element["return-value"][0]; - return_type = getType(modName, ns, value); + return_type = getType(ns, value); } else { return_type = VoidType; } let parameters: IntrospectedFunctionParameter[] = []; - if (func.parameters) { - const param = func.parameters[0].parameter; + // TODO: Don't create a useless function object here + let fn = new IntrospectedFunction({ + name, + raw_name, + namespace: ns, + isIntrospectable: isIntrospectable(element) + }); + + if (element.parameters) { + const param = element.parameters[0].parameter; if (param) { const inputs = param.filter((p): p is typeof p & { $: { name: string } } => { return !!p.$.name; }); - parameters.push( - ...inputs.map(i => IntrospectedFunctionParameter.fromXML(modName, ns, options, null, i)) - ); + parameters.push(...inputs.map(i => IntrospectedFunctionParameter.fromXML(i, fn, options))); const unwrapped = return_type.unwrap(); @@ -214,18 +227,16 @@ export class IntrospectedFunction extends IntrospectedBase { ) .map(parameter => parameter.copy({ isOptional: false })); - const fn = new IntrospectedFunction({ + fn = fn.copy({ + parent: ns, parameters: input_parameters, - output_parameters, - return_type, - name, - raw_name, - isIntrospectable: isIntrospectable(func) + outputParameters: output_parameters, + return_type }); if (options.loadDocs) { - fn.doc = parseDoc(func); - fn.metadata = parseMetadata(func); + fn.doc = parseDoc(element); + fn.metadata = parseMetadata(element); } return fn; @@ -236,9 +247,10 @@ export class IntrospectedFunction extends IntrospectedBase { } asCallback(): IntrospectedCallback { - const { raw_name, name, output_parameters, parameters, return_type } = this; + const { name, raw_name, namespace, output_parameters, parameters, return_type } = this; return new IntrospectedCallback({ + namespace, raw_name, name, output_parameters, @@ -294,17 +306,33 @@ export class IntrospectedFunction extends IntrospectedBase { } } -export class IntrospectedDirectAllocationConstructor extends IntrospectedBase { - fields: Field[]; +export class IntrospectedDirectAllocationConstructor extends IntrospectedClassMember { + parameters: IntrospectedFunctionParameter[]; - constructor(fields: Field[]) { - super("new", { isPrivate: false, isIntrospectable: true }); + constructor(parameters: IntrospectedFunctionParameter[], parent: IntrospectedBaseClass | null) { + super("new", parent, { isPrivate: false, isIntrospectable: true }); - this.fields = fields + this.parameters = parameters.map(parameter => parameter.copy({ parent: this })); + } + + static fromFields(fields: IntrospectedField[], parent: IntrospectedBaseClass) { + const building = new IntrospectedDirectAllocationConstructor([], parent); + + building.parameters = fields .filter(field => { return !field.isStatic && !field.isPrivate && !field.type.isPointer; }) - .map(field => field.copy({ parent: this })); + .map( + field => + new IntrospectedFunctionParameter({ + parent: building, + type: field.type, + name: field.name, + direction: GirDirection.In + }) + ); + + return building; } asString>(generator: T): ReturnType { @@ -314,9 +342,14 @@ export class IntrospectedDirectAllocationConstructor extends IntrospectedBase { } copy( - options?: { parent?: IntrospectedBase | undefined; fields: Field[] } | undefined + options?: + | { parent?: IntrospectedBaseClass | undefined; parameters?: IntrospectedFunctionParameter[] } + | undefined ): IntrospectedDirectAllocationConstructor { - const copy = new IntrospectedDirectAllocationConstructor(options?.fields ?? this.fields); + const copy = new IntrospectedDirectAllocationConstructor( + options?.parameters ?? this.parameters, + options?.parent ?? this.parent + ); copy._copyBaseProperties(this); @@ -325,8 +358,8 @@ export class IntrospectedDirectAllocationConstructor extends IntrospectedBase { accept(visitor: GirVisitor): IntrospectedDirectAllocationConstructor { const node = this.copy({ - fields: this.fields.map(field => { - return field.accept(visitor); + parameters: this.parameters.map(parameters => { + return parameters.accept(visitor); }) }); @@ -334,7 +367,7 @@ export class IntrospectedDirectAllocationConstructor extends IntrospectedBase { } } -export class IntrospectedConstructor extends IntrospectedBase { +export class IntrospectedConstructor extends IntrospectedClassMember { readonly parameters: IntrospectedFunctionParameter[] = []; readonly return_type: TypeExpression = UnknownType; @@ -342,40 +375,42 @@ export class IntrospectedConstructor extends IntrospectedBase { name, parameters = [], return_type, + parent, ...args }: Options<{ name: string; + parent: IntrospectedBaseClass | null; parameters?: IntrospectedFunctionParameter[]; return_type: TypeExpression; }>) { - super(name, { ...args }); + super(name, parent, { ...args }); this.return_type = return_type; this.parameters = parameters.map(p => p.copy({ parent: this })); } copy({ + parent, parameters, return_type }: { - parent?: undefined; + parent?: IntrospectedBaseClass; parameters?: IntrospectedFunctionParameter[]; return_type?: TypeExpression; } = {}): IntrospectedConstructor { return new IntrospectedConstructor({ name: this.name, + parent: parent ?? this.parent ?? null, return_type: return_type ?? this.return_type, parameters: parameters ?? this.parameters })._copyBaseProperties(this); } static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, + m: GirConstructorElement, parent: IntrospectedBaseClass, - m: GirConstructorElement + options: LoadOptions ): IntrospectedConstructor { - return IntrospectedClassFunction.fromXML(modName, ns, options, parent, m as GirFunctionElement).asConstructor(); + return IntrospectedClassFunction.fromXML(m as GirFunctionElement, parent, options).asConstructor(); } accept(visitor: GirVisitor): IntrospectedConstructor { @@ -398,13 +433,19 @@ export class IntrospectedConstructor extends IntrospectedBase { } } -export class IntrospectedFunctionParameter extends IntrospectedBase { +export class IntrospectedFunctionParameter extends IntrospectedBase< + | IntrospectedClassFunction + | IntrospectedFunction + | IntrospectedSignal + | IntrospectedConstructor + | IntrospectedDirectAllocationConstructor + | null +> { readonly type: TypeExpression; readonly direction: GirDirection; readonly isVarArgs: boolean = false; readonly isOptional: boolean = false; readonly isNullable: boolean = false; - readonly parent?: IntrospectedClassFunction | IntrospectedFunction | IntrospectedSignal | IntrospectedConstructor; constructor({ name, @@ -418,7 +459,13 @@ export class IntrospectedFunctionParameter extends IntrospectedBase { ...args }: Options<{ name: string; - parent?: IntrospectedClassFunction | IntrospectedFunction | IntrospectedSignal | IntrospectedConstructor; + parent?: + | IntrospectedClassFunction + | IntrospectedFunction + | IntrospectedSignal + | IntrospectedConstructor + | IntrospectedDirectAllocationConstructor + | null; type: TypeExpression; direction: GirDirection; doc?: string | null; @@ -426,9 +473,8 @@ export class IntrospectedFunctionParameter extends IntrospectedBase { isOptional?: boolean; isNullable?: boolean; }>) { - super(name, { ...args }); + super(name, parent ?? null, { ...args }); - this.parent = parent; this.type = type; this.direction = direction; this.doc = doc; @@ -437,9 +483,31 @@ export class IntrospectedFunctionParameter extends IntrospectedBase { this.isNullable = isNullable; } + get namespace() { + if (!this.parent) throw new Error(`Failed to get namespace for ${this.name}`); + + return this.parent.namespace; + } + + asField() { + const { name, parent, isOptional: optional, type } = this; + + if (!(parent instanceof IntrospectedBaseClass)) { + throw new Error("Can't convert parameter of non-class function to field"); + } + + return new IntrospectedField({ name, parent, optional, type }); + } + copy( options: { - parent?: IntrospectedClassFunction | IntrospectedFunction | IntrospectedSignal | IntrospectedConstructor; + parent?: + | IntrospectedClassFunction + | IntrospectedFunction + | IntrospectedSignal + | IntrospectedConstructor + | IntrospectedDirectAllocationConstructor + | null; type?: TypeExpression; isOptional?: boolean; isNullable?: boolean; @@ -473,13 +541,14 @@ export class IntrospectedFunctionParameter extends IntrospectedBase { return generator.generateParameter(this); } - static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, - parent: IntrospectedSignal | IntrospectedClassFunction | IntrospectedFunction | IntrospectedConstructor | null, - parameter: GirCallableParamElement & { $: { name: string } } + static fromXML< + Parent extends IntrospectedSignal | IntrospectedClassFunction | IntrospectedFunction | IntrospectedConstructor + >( + parameter: GirCallableParamElement & { $: { name: string } }, + parent: Parent, + options: LoadOptions ): IntrospectedFunctionParameter { + const ns = parent.namespace; let name = sanitizeMemberName(parameter.$.name); if (isInvalid(name)) { @@ -518,10 +587,10 @@ export class IntrospectedFunctionParameter extends IntrospectedBase { isNullable = true; } - type = getType(modName, ns, parameter); + type = getType(ns, parameter); } else if (parameter.$.direction === GirDirection.Out) { direction = parameter.$.direction; - type = getType(modName, ns, parameter); + type = getType(ns, parameter); } else { throw new Error(`Unknown parameter direction: ${parameter.$.direction as string}`); } @@ -546,13 +615,12 @@ export class IntrospectedFunctionParameter extends IntrospectedBase { } } -export class IntrospectedClassFunction extends IntrospectedBase { +export class IntrospectedClassFunction extends IntrospectedBase { readonly parameters: IntrospectedFunctionParameter[]; protected readonly return_type: TypeExpression; readonly output_parameters: IntrospectedFunctionParameter[]; protected _anyify: boolean = false; protected _generify: boolean = false; - parent: IntrospectedBaseClass | IntrospectedEnum; interfaceParent: IntrospectedBaseClass | IntrospectedEnum | null = null; generics: Generic[] = []; @@ -573,22 +641,38 @@ export class IntrospectedClassFunction extends IntrospectedBase { parent: IntrospectedBaseClass | IntrospectedEnum; doc?: string | null; }>) { - super(name, { ...args }); + super(name, parent, { ...args }); this.parameters = parameters.map(p => p.copy({ parent: this })); this.output_parameters = output_parameters.map(p => p.copy({ parent: this })); this.return_type = return_type; - this.parent = parent; this.doc = doc; } + get namespace() { + return this.parent.namespace; + } + + asCallback(): IntrospectedClassCallback { + const { name, parent, output_parameters, parameters, return_type } = this; + + return new IntrospectedClassCallback({ + parent, + name, + output_parameters, + parameters, + return_type + }); + } + asConstructor(): IntrospectedConstructor { - const { name, parameters } = this; + const { name, parent, parameters } = this; - if (this.parent instanceof IntrospectedBaseClass) { + if (parent instanceof IntrospectedBaseClass) { // Always force constructors to have the correct return type. return new IntrospectedConstructor({ name, + parent, parameters, return_type: this.parent.getType(), isIntrospectable: this.isIntrospectable @@ -596,7 +680,7 @@ export class IntrospectedClassFunction extends IntrospectedBase { } throw new Error( - `Attempted to convert GirClassFunction into GirConstructor from invalid parent: ${this.parent.name}` + `Attempted to convert GirClassFunction into GirConstructor from invalid enum parent: ${this.parent.name}` ); } @@ -678,13 +762,11 @@ export class IntrospectedClassFunction extends IntrospectedBase { } static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, + element: GirFunctionElement | GirMethodElement, parent: IntrospectedBaseClass | IntrospectedEnum, - m: GirFunctionElement | GirMethodElement + options: LoadOptions ): IntrospectedClassFunction { - const fn = IntrospectedFunction.fromXML(modName, ns, options, null, m); + const fn = IntrospectedFunction.fromXML(element, parent.namespace, options); return fn.asClassFunction(parent); } @@ -744,13 +826,11 @@ export class IntrospectedVirtualClassFunction extends IntrospectedClassFunction } static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, + m: GirVirtualMethodElement, parent: IntrospectedBaseClass, - m: GirVirtualMethodElement + options: LoadOptions ): IntrospectedVirtualClassFunction { - const fn = IntrospectedFunction.fromXML(modName, ns, options, parent, m); + const fn = IntrospectedFunction.fromXML(m, parent.namespace, options); return fn.asVirtualClassFunction(parent); } @@ -780,13 +860,11 @@ export class IntrospectedStaticClassFunction extends IntrospectedClassFunction { } static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, + m: GirFunctionElement, parent: IntrospectedBaseClass | IntrospectedEnum, - m: GirFunctionElement + options: LoadOptions ): IntrospectedStaticClassFunction { - const fn = IntrospectedFunction.fromXML(modName, ns, options, parent, m); + const fn = IntrospectedFunction.fromXML(m, parent.namespace, options); return fn.asStaticClassFunction(parent); } @@ -803,9 +881,10 @@ export class IntrospectedCallback extends IntrospectedFunction { copy({ parameters, returnType, - outputParameters + outputParameters, + parent }: { - parent?: undefined; + parent?: GirModule; parameters?: IntrospectedFunctionParameter[]; outputParameters?: IntrospectedFunctionParameter[]; returnType?: TypeExpression; @@ -815,7 +894,8 @@ export class IntrospectedCallback extends IntrospectedFunction { raw_name: this.raw_name, return_type: returnType ?? this.return_type, parameters: parameters ?? this.parameters, - output_parameters: outputParameters ?? this.output_parameters + output_parameters: outputParameters ?? this.output_parameters, + namespace: parent ?? this.parent })._copyBaseProperties(this); cb.generics = [...this.generics]; @@ -837,26 +917,21 @@ export class IntrospectedCallback extends IntrospectedFunction { return visitor.visitCallback?.(node) ?? node; } - static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, - _parent, - func: GirCallbackElement - ): IntrospectedCallback { - const cb = IntrospectedFunction.fromXML(modName, ns, options, null, func).asCallback(); - - const glibTypeName = func.$["glib:type-name"]; - if (typeof glibTypeName === "string" && func.$["glib:type-name"]) { + static fromXML(element: GirCallbackElement, namespace: GirModule, options: LoadOptions): IntrospectedCallback { + const ns = namespace; + const cb = IntrospectedFunction.fromXML(element, ns, options).asCallback(); + + const glibTypeName = element.$["glib:type-name"]; + if (typeof glibTypeName === "string" && element.$["glib:type-name"]) { cb.resolve_names.push(glibTypeName); ns.registerResolveName(glibTypeName, ns.name, cb.name); } - if (func.$["c:type"]) { - cb.resolve_names.push(func.$["c:type"]); + if (element.$["c:type"]) { + cb.resolve_names.push(element.$["c:type"]); - ns.registerResolveName(func.$["c:type"], ns.name, cb.name); + ns.registerResolveName(element.$["c:type"], ns.name, cb.name); } return cb; @@ -866,3 +941,78 @@ export class IntrospectedCallback extends IntrospectedFunction { return generator.generateCallback(this) as ReturnType; } } + +export class IntrospectedClassCallback extends IntrospectedClassFunction { + asFunctionType(): FunctionType { + return new FunctionType( + Object.fromEntries(this.parameters.map(p => [p.name, p.type] as const)), + this.return_type + ); + } + + copy({ + parameters, + returnType, + outputParameters, + parent + }: { + parent?: IntrospectedBaseClass; + parameters?: IntrospectedFunctionParameter[]; + outputParameters?: IntrospectedFunctionParameter[]; + returnType?: TypeExpression; + } = {}): IntrospectedClassCallback { + const cb = new IntrospectedClassCallback({ + name: this.name, + return_type: returnType ?? this.return_type, + parameters: parameters ?? this.parameters, + output_parameters: outputParameters ?? this.output_parameters, + parent: parent ?? this.parent + })._copyBaseProperties(this); + + cb.generics = [...this.generics]; + + return cb; + } + + accept(visitor: GirVisitor): IntrospectedClassCallback { + const node = this.copy({ + parameters: this.parameters.map(p => { + return p.accept(visitor); + }), + outputParameters: this.output_parameters.map(p => { + return p.accept(visitor); + }), + returnType: visitor.visitType?.(this.return_type) + }); + + return visitor.visitClassCallback?.(node) ?? node; + } + + static fromXML( + element: GirCallbackElement, + parent: IntrospectedBaseClass, + options: LoadOptions + ): IntrospectedClassCallback { + const ns = parent.namespace; + const cb = IntrospectedClassFunction.fromXML(element, parent, options).asCallback(); + + const glibTypeName = element.$["glib:type-name"]; + if (typeof glibTypeName === "string" && element.$["glib:type-name"]) { + cb.resolve_names.push(glibTypeName); + + ns.registerResolveName(glibTypeName, ns.name, cb.name); + } + + if (element.$["c:type"]) { + cb.resolve_names.push(element.$["c:type"]); + + ns.registerResolveName(element.$["c:type"], ns.name, cb.name); + } + + return cb; + } + + asString>(generator: T): ReturnType { + return generator.generateClassCallback(this) as ReturnType; + } +} diff --git a/packages/lib/src/newlib/gir/namespace.ts b/packages/lib/src/newlib/gir/namespace.ts index b2c1e7f35..2c57825ee 100644 --- a/packages/lib/src/newlib/gir/namespace.ts +++ b/packages/lib/src/newlib/gir/namespace.ts @@ -1,29 +1,13 @@ -import { - NullableType, - ObjectType, - BinaryType, - VoidType, - PromiseType, - BooleanType, - TupleType, - TypeIdentifier, - ClosureType -} from "../gir.js"; +import { BinaryType, VoidType, PromiseType, BooleanType, TupleType, TypeIdentifier, ClosureType } from "../gir.js"; -import { IntrospectedBase } from "./base.js"; -import { ParsedGir, GirInfoAttrs, GirType } from "../../index.js"; -import { isPrimitiveType } from "./util.js"; +import { GirInfoAttrs, GirModule } from "../../index.js"; -import { IntrospectedClass, IntrospectedInterface, IntrospectedRecord, IntrospectedBaseClass } from "./class.js"; -import { IntrospectedFunction, IntrospectedCallback } from "./function.js"; -import { IntrospectedEnum, IntrospectedError } from "./enum.js"; +import { IntrospectedBaseClass } from "./class.js"; +import { IntrospectedFunction } from "./function.js"; +import { IntrospectedEnum } from "./enum.js"; import { IntrospectedConstant } from "./const.js"; import { IntrospectedAlias } from "./alias.js"; -import { LoadOptions } from "../types.js"; -import { NSRegistry } from "./registry.js"; -import { GirVisitor } from "../visitor.js"; - export type GirNSMember = | IntrospectedBaseClass | IntrospectedFunction @@ -37,7 +21,7 @@ export const isDeprecated = (e: { $: GirInfoAttrs }) => e && e.$ && e.$.deprecat export const deprecatedVersion = (e: { $: GirInfoAttrs }) => e?.$?.["deprecated-version"]; export const introducedVersion = (e: { $: GirInfoAttrs }) => e?.$?.version; -export function promisifyNamespaceFunctions(namespace: IntrospectedNamespace) { +export function promisifyNamespaceFunctions(namespace: GirModule) { return namespace.members.forEach(node => { if (!(node instanceof IntrospectedFunction)) return; @@ -95,487 +79,4 @@ export function promisifyNamespaceFunctions(namespace: IntrospectedNamespace) { }); } -export class IntrospectedNamespace { - readonly name: string; - readonly version: string; - readonly c_prefixes: string[]; - - private imports: Map = new Map(); - default_imports: Map = new Map(); - - private _members?: Map; - private _enum_constants?: Map; - private _resolve_names: Map = new Map(); - __dts__references?: string[]; - - package_version!: readonly [string, string] | readonly [string, string, string]; - parent!: NSRegistry; - - constructor(name: string, version: string, prefixes: string[]) { - this.name = name; - this.version = version; - this.c_prefixes = [...prefixes]; - this.package_version = ["0", "0"]; - } - - registerResolveName(resolveName: string, namespace: string, name: string) { - this._resolve_names.set(resolveName, new TypeIdentifier(name, namespace)); - } - - get members(): Map { - if (!this._members) { - this._members = new Map(); - } - - return this._members; - } - - get enum_constants(): Map { - if (!this._enum_constants) { - this._enum_constants = new Map(); - } - - return this._enum_constants; - } - - accept(visitor: GirVisitor) { - for (const key of [...this.members.keys()]) { - const member = this.members.get(key); - - if (!member) continue; - - if (Array.isArray(member)) { - this.members.set( - key, - member.map(m => { - return m.accept(visitor); - }) - ); - } else { - this.members.set(key, member.accept(visitor)); - } - } - } - - getImportsForCPrefix(c_prefix: string): IntrospectedNamespace[] { - return this.parent.namespacesForPrefix(c_prefix); - } - - hasImport(name: string): boolean { - return this.default_imports.has(name) || this.imports.has(name); - } - - private _getImport(name: string): IntrospectedNamespace | null { - let version = this.default_imports.get(name) ?? this.imports.get(name); - - if (name === this.name) { - return this; - } - - // TODO: Clean this up, but essentially try to resolve import versions - // using transitive imports (e.g. use GtkSource to find the version of Gtk) - if (!version) { - const entries = [...this.default_imports.entries()].flatMap(([_name]) => { - const namespace = this._getImport(_name); - - return [...(namespace?.default_imports.entries() ?? [])]; - }); - - version = Object.fromEntries(entries)[name]; - } - - if (!version) { - version = this.parent.assertDefaultVersionOf(name); - } - - const namespace = this.parent.namespace(name, version); - - if (namespace) { - if (!this.imports.has(namespace.name)) { - this.imports.set(namespace.name, namespace.version); - } - } - - return namespace; - } - - getInstalledImport(name: string): IntrospectedNamespace | null { - let version = this.default_imports.get(name) ?? this.imports.get(name); - - if (name === this.name) { - return this; - } - - if (!version) { - version = this.parent.defaultVersionOf(name) ?? undefined; - } - - if (!version) { - return null; - } - - const namespace = this.parent.namespace(name, version); - - if (namespace) { - if (!this.imports.has(namespace.name)) { - this.imports.set(namespace.name, namespace.version); - } - } - - return namespace; - } - - assertInstalledImport(name: string): IntrospectedNamespace { - const namespace = this._getImport(name); - - if (!namespace) { - throw new Error(`Failed to import ${name} in ${this.name}, not installed or accessible.`); - } - - return namespace; - } - - getImports(): [string, string][] { - return [...this.imports.entries()].sort(([[a], [b]]) => a.localeCompare(b)); - } - - addImport(ns_name: string) { - this._getImport(ns_name); - } - - getMembers(name: string): IntrospectedBase[] { - const members = this.members.get(name); - - if (Array.isArray(members)) { - return [...members]; - } - return members ? [members] : []; - } - - getMemberWithoutOverrides(name: string) { - if (this.members.has(name)) { - const member = this.members.get(name); - - if (!Array.isArray(member)) { - return member; - } - - return null; - } - - const resolvedName = this._resolve_names.get(name); - if (resolvedName) { - const member = this.members.get(resolvedName.name); - - if (!Array.isArray(member)) { - return member; - } - } - - return null; - } - - assertClass(name: string): IntrospectedBaseClass { - const clazz = this.getClass(name); - - if (!clazz) { - throw new Error(`Class ${name} does not exist in namespace ${this.name}.`); - } - - return clazz; - } - - getClass(name: string): IntrospectedBaseClass | null { - const member = this.getMemberWithoutOverrides(name); - - if (member instanceof IntrospectedBaseClass) { - return member; - } - return null; - } - - getEnum(name: string): IntrospectedEnum | null { - const member = this.getMemberWithoutOverrides(name); - - if (member instanceof IntrospectedEnum) { - return member; - } - return null; - } - - getAlias(name: string): IntrospectedAlias | null { - const member = this.getMemberWithoutOverrides(name); - - if (member instanceof IntrospectedAlias) { - return member; - } - return null; - } - - hasSymbol(name: string) { - return this.members.has(name); - } - - resolveSymbolFromTypeName(name: string): string | null { - const resolvedName = this._resolve_names.get(name); - - if (!resolvedName) { - return null; - } - - const member = this.members.get(resolvedName.name); - if (member instanceof IntrospectedBase) { - return member.name; - } - - return null; - } - - findClassCallback(name: string): [string | null, string] { - const clazzes = Array.from(this.members.values()).filter( - (m): m is IntrospectedBaseClass => m instanceof IntrospectedBaseClass - ); - const res = clazzes - .map<[IntrospectedBaseClass, IntrospectedCallback | undefined]>(m => [ - m, - m.callbacks.find(c => c.name === name || c.resolve_names.includes(name)) - ]) - .find((r): r is [IntrospectedBaseClass, IntrospectedCallback] => r[1] != null); - - if (res) { - return [res[0].name, res[1].name]; - } else { - return [null, name]; - } - } - - /** - * This is an internal method to add TypeScript - * comments when overrides use parts of the TypeScript standard - * libraries that are newer than default. - */ - ___dts___addReference(reference: string) { - this.__dts__references ??= []; - this.__dts__references.push(reference); - } - - static fromXML(repo: ParsedGir, options: LoadOptions, registry: NSRegistry): IntrospectedNamespace { - const ns = repo.repository[0]?.namespace?.[0]; - - if (!ns) throw new Error(`Missing namespace in ${repo.repository[0].package[0].$.name}`); - - const modName = ns.$["name"]; - let version = ns.$["version"]; - - // Hardcode harfbuzz version for now... - if (modName === "HarfBuzz" && version === "0.0") { - version = "2.0"; - } - - if (!modName) { - throw new Error("Invalid GIR file: no namespace name specified."); - } - - if (!version) { - throw new Error("Invalid GIR file: no version name specified."); - } - - const c_prefix = ns.$?.["c:symbol-prefixes"]?.split(",") ?? []; - - if (options.verbose) { - console.debug(`Parsing ${modName}...`); - } - - const building = new IntrospectedNamespace(modName, version, c_prefix); - building.parent = registry; - // Set the namespace object here to prevent re-parsing the namespace if - // another namespace imports it. - registry.mapping.set(modName, version, building); - - const includes = repo.repository[0].include || []; - - includes - .map(i => [i.$.name, i.$.version] as const) - .forEach(([name, version]) => { - if (version) { - if (options.verbose) { - console.debug(`Adding dependency ${name} ${version}...`); - } - - building.default_imports.set(name, version); - } - }); - - const importConflicts = (el: IntrospectedConstant | IntrospectedBaseClass | IntrospectedFunction) => { - return !building.hasImport(el.name); - }; - - if (ns.enumeration) { - // Get the requested enums - ns.enumeration - ?.map(enumeration => { - if (enumeration.$["glib:error-domain"]) { - return IntrospectedError.fromXML(modName, building, options, null, enumeration); - } else { - return IntrospectedEnum.fromXML(modName, building, options, null, enumeration); - } - }) - .forEach(c => building.members.set(c.name, c)); - } - - // Constants - if (ns.constant) { - ns.constant - ?.filter(isIntrospectable) - .map(constant => IntrospectedConstant.fromXML(modName, building, options, null, constant)) - .filter(importConflicts) - .forEach(c => building.members.set(c.name, c)); - } - - // Get the requested functions - if (ns.function) { - ns.function - ?.filter(isIntrospectable) - .map(func => IntrospectedFunction.fromXML(modName, building, options, null, func)) - .filter(importConflicts) - .forEach(c => building.members.set(c.name, c)); - } - - if (ns.callback) { - ns.callback - ?.filter(isIntrospectable) - .map(callback => IntrospectedCallback.fromXML(modName, building, options, null, callback)) - .filter(importConflicts) - .forEach(c => building.members.set(c.name, c)); - } - - if (ns["glib:boxed"]) { - ns["glib:boxed"] - ?.filter(isIntrospectable) - .map(boxed => new IntrospectedAlias({ name: boxed.$["glib:name"], type: new NullableType(ObjectType) })) - .forEach(c => building.members.set(c.name, c)); - } - - // Bitfield is a type of enum - if (ns.bitfield) { - ns.bitfield - ?.filter(isIntrospectable) - .map(field => IntrospectedEnum.fromXML(modName, building, options, building, field, true)) - .forEach(c => building.members.set(c.name, c)); - } - - // The `enum_constants` map maps the C identifiers (GTK_BUTTON_TYPE_Y) - // to the name of the enum (Button) to resolve references (Gtk.Button.Y) - Array.from(building.members.values()) - .filter((m): m is IntrospectedEnum => m instanceof IntrospectedEnum) - .forEach(m => { - m.members.forEach(member => { - building.enum_constants.set(member.c_identifier, [m.name, member.name] as const); - }); - }); - - // Get the requested classes - if (ns.class) { - ns.class - ?.filter(isIntrospectable) - .map(klass => IntrospectedClass.fromXML(modName, building, options, building, klass)) - .filter(importConflicts) - .forEach(c => building.members.set(c.name, c)); - } - - if (ns.record) { - ns.record - ?.filter(isIntrospectable) - .map(record => IntrospectedRecord.fromXML(modName, building, options, record)) - .filter(importConflicts) - .forEach(c => building.members.set(c.name, c)); - } - - if (ns.union) { - ns.union - ?.filter(isIntrospectable) - .map(union => IntrospectedRecord.fromXML(modName, building, options, union)) - .filter(importConflicts) - .forEach(c => building.members.set(c.name, c)); - } - - if (ns.interface) { - ns.interface - ?.map(inter => IntrospectedInterface.fromXML(modName, building, options, inter)) - .filter(importConflicts) - .forEach(c => building.members.set(c.name, c)); - } - - if (ns.alias) { - type NamedType = GirType & { $: { name: string } }; - - ns.alias - ?.filter(isIntrospectable) - // Avoid attempting to alias non-introspectable symbols. - .map(b => { - b.type = b.type - ?.filter((t): t is NamedType => !!(t && t.$.name)) - .map(t => { - if ( - t.$.name && - !building.hasSymbol(t.$.name) && - !isPrimitiveType(t.$.name) && - !t.$.name.includes(".") - ) { - return { $: { name: "unknown", "c:type": "unknown" } } as GirType; - } - - return t; - }); - - return b; - }) - .map(alias => IntrospectedAlias.fromXML(modName, building, options, null, alias)) - .filter((alias): alias is IntrospectedAlias => alias != null) - .forEach(c => building.members.set(c.name, c)); - } - - try { - let [major = null] = building.getMembers("MAJOR_VERSION"); - let [minor = null] = building.getMembers("MINOR_VERSION"); - let [micro = null] = building.getMembers("MICRO_VERSION"); - - if (!major) { - major = building.getMembers("VERSION_MAJOR")[0] ?? null; - } - - if (!minor) { - minor = building.getMembers("VERSION_MINOR")[0] ?? null; - } - - if (!micro) { - micro = building.getMembers("VERSION_MICRO")[0] ?? null; - } - - if ( - major instanceof IntrospectedConstant && - minor instanceof IntrospectedConstant && - major.value && - minor.value - ) { - if (micro instanceof IntrospectedConstant && micro.value) { - building.package_version = [major.value, minor.value, micro.value] as const; - } else { - building.package_version = [major.value, minor.value] as const; - } - } else { - const [major = "", minor = "0", micro] = building.version.split(".").filter(v => v != ""); - - if (micro) { - building.package_version = [major, minor, micro]; - } else { - building.package_version = [major, minor]; - } - } - } catch (error) { - console.error(`Failed to parse package version for ${building.name} with version ${building.version}`); - } - - return building; - } -} +export { GirModule as IntrospectedNamespace } from "../../gir-module.js"; diff --git a/packages/lib/src/newlib/gir/nodes.ts b/packages/lib/src/newlib/gir/nodes.ts index aeebaa06d..f215db9c2 100644 --- a/packages/lib/src/newlib/gir/nodes.ts +++ b/packages/lib/src/newlib/gir/nodes.ts @@ -1,25 +1,47 @@ -export { IntrospectedAlias as GirAlias } from "./alias.js"; +export { IntrospectedAlias } from "./alias.js"; export { - IntrospectedClass as GirClass, - IntrospectedInterface as GirInterface, - IntrospectedRecord as GirRecord, + IntrospectedClass, + IntrospectedInterface, + IntrospectedRecord, GirComplexRecord, - IntrospectedBaseClass as GirBaseClass + IntrospectedBaseClass } from "./class.js"; -export { IntrospectedConstant as GirConst } from "./const.js"; -export { IntrospectedEnum as GirEnum, IntrospectedError as GirError, GirEnumMember } from "./enum.js"; +export { IntrospectedConstant } from "./const.js"; +export { IntrospectedEnum, GirEnumMember } from "./enum.js"; export { - IntrospectedFunction as GirFunction, - IntrospectedClassFunction as GirClassFunction, - IntrospectedCallback as GirCallback, - IntrospectedConstructor as GirConstructor, - IntrospectedStaticClassFunction as GirStaticClassFunction, - IntrospectedVirtualClassFunction as GirVirtualClassFunction, - IntrospectedFunctionParameter as GirFunctionParameter, - IntrospectedDirectAllocationConstructor as GirDirectAllocationConstructor + IntrospectedFunction, + IntrospectedClassFunction, + IntrospectedCallback, + IntrospectedConstructor, + IntrospectedStaticClassFunction, + IntrospectedVirtualClassFunction, + IntrospectedFunctionParameter, + IntrospectedDirectAllocationConstructor as IntrospectedDirectAllocationConstructor } from "./function.js"; -export { IntrospectedNamespace as GirNamespace, GirNSMember } from "./namespace.js"; -export { NSRegistry as GirNSRegistry, NSLoader as GirNSLoader } from "./registry.js"; -export { Field as GirField, GirProperty } from "./property.js"; -export { IntrospectedSignal as GirSignal, IntrospectedSignalType as GirSignalType } from "./signal.js"; +export { IntrospectedNamespace, GirNSMember } from "./namespace.js"; +export { NSRegistry, NSLoader } from "./registry.js"; +export { IntrospectedField, IntrospectedProperty } from "./property.js"; +export { IntrospectedSignal, IntrospectedSignalType } from "./signal.js"; +export { + GenericType, + TypeExpression, + TypeIdentifier, + OrType, + NativeType, + TypeConflict, + AnyType, + ThisType, + TupleType, + GTypeType, + NeverType, + BinaryType, + NumberType, + ObjectType, + StringType, + ClosureType, + PromiseType, + UnknownType, + ConflictType +} from "../gir.js"; + export * as nodeUtils from "./util.js"; diff --git a/packages/lib/src/newlib/gir/property.ts b/packages/lib/src/newlib/gir/property.ts index be9909c9a..2d2738ef2 100644 --- a/packages/lib/src/newlib/gir/property.ts +++ b/packages/lib/src/newlib/gir/property.ts @@ -1,16 +1,16 @@ import { TypeExpression } from "../gir.js"; -import { IntrospectedBase, Options } from "./base.js"; +import { IntrospectedBase, IntrospectedClassMember, Options } from "./base.js"; import { GirFieldElement, GirPropertyElement } from "../../index.js"; import { getType, parseDoc, parseMetadata } from "./util.js"; -import { IntrospectedNamespace, isIntrospectable } from "./namespace.js"; +import { isIntrospectable } from "./namespace.js"; import { FormatGenerator } from "../generators/generator.js"; import { LoadOptions } from "../types.js"; import { GirVisitor } from "../visitor.js"; import { IntrospectedBaseClass } from "./class.js"; import { IntrospectedEnum } from "./enum.js"; -export class Field extends IntrospectedBase { +export class IntrospectedField extends IntrospectedClassMember { type: TypeExpression; // TODO: Make these properties readonly @@ -20,11 +20,12 @@ export class Field extends IntrospectedBase { writable: boolean; isNative: boolean = false; - copy(options?: { parent?: IntrospectedBase; type?: TypeExpression; isStatic?: boolean }): Field { - const { type, name, optional, computed, isStatic, writable } = this; + copy(options?: { parent?: IntrospectedBaseClass; type?: TypeExpression; isStatic?: boolean }): IntrospectedField { + const { type, name, parent, optional, computed, isStatic, writable } = this; - return new Field({ + return new IntrospectedField({ name, + parent, type: options?.type ?? type, optional, computed, @@ -35,6 +36,7 @@ export class Field extends IntrospectedBase { constructor({ name, + parent, type, computed = false, optional = false, @@ -43,13 +45,14 @@ export class Field extends IntrospectedBase { ...args }: Options<{ name: string; + parent?: IntrospectedBaseClass | null; type: TypeExpression; computed?: boolean; optional?: boolean; isStatic?: boolean; writable?: boolean; }>) { - super(name, { ...args }); + super(name, parent ?? null, { ...args }); this.type = type; this.computed = computed; @@ -62,7 +65,7 @@ export class Field extends IntrospectedBase { return generator.generateField(this) as ReturnType; } - accept(visitor: GirVisitor): Field { + accept(visitor: GirVisitor): IntrospectedField { const node = this.copy({ type: visitor.visitType?.(this.type) ?? this.type }); @@ -70,18 +73,14 @@ export class Field extends IntrospectedBase { return visitor.visitField?.(node) ?? node; } - static fromXML( - namespace: string, - ns: IntrospectedNamespace, - _options: LoadOptions, - _parent, - field: GirFieldElement - ): Field { + static fromXML(field: GirFieldElement, parent: IntrospectedBaseClass): IntrospectedField { + const namespace = parent.namespace; const name = field.$["name"]; const _name = name.replace(/[-]/g, "_"); - const f = new Field({ + const f = new IntrospectedField({ name: _name, - type: getType(namespace, ns, field), + parent, + type: getType(namespace, field), isPrivate: field.$.private === "1", isIntrospectable: isIntrospectable(field) }); @@ -90,27 +89,29 @@ export class Field extends IntrospectedBase { } } -export class JSField extends Field { +export class JSField extends IntrospectedField { isNative = true; } -export class GirProperty extends IntrospectedBase { +export class IntrospectedProperty extends IntrospectedBase { type: TypeExpression; readonly writable: boolean = false; readonly readable: boolean = true; readonly constructOnly: boolean; - parent: IntrospectedBaseClass | IntrospectedEnum; + get namespace() { + return this.parent.namespace; + } copy(options?: { name?: string; parent?: IntrospectedBaseClass | IntrospectedEnum; type?: TypeExpression; - }): GirProperty { + }): IntrospectedProperty { const { name, writable, readable, type, constructOnly, parent } = this; - return new GirProperty({ + return new IntrospectedProperty({ name: options?.name ?? name, writable, readable, @@ -120,7 +121,7 @@ export class GirProperty extends IntrospectedBase { })._copyBaseProperties(this); } - accept(visitor: GirVisitor): GirProperty { + accept(visitor: GirVisitor): IntrospectedProperty { const node = this.copy({ parent: this.parent, type: visitor.visitType?.(this.type) ?? this.type @@ -145,13 +146,12 @@ export class GirProperty extends IntrospectedBase { constructOnly: boolean; parent: IntrospectedBaseClass | IntrospectedEnum; }>) { - super(name, { ...args }); + super(name, parent, { ...args }); this.type = type; this.writable = writable; this.readable = readable; this.constructOnly = constructOnly; - this.parent = parent; } asString>(generator: T, construct?: boolean): ReturnType { @@ -177,27 +177,26 @@ export class GirProperty extends IntrospectedBase { } static fromXML( - namespace: string, - ns: IntrospectedNamespace, - options: LoadOptions, + element: GirPropertyElement, parent: IntrospectedBaseClass | IntrospectedEnum, - prop: GirPropertyElement - ): GirProperty { - const name = prop.$["name"]; + options: LoadOptions + ): IntrospectedProperty { + const ns = parent.namespace; + const name = element.$["name"]; const _name = name.replace(/[-]/g, "_"); - const property = new GirProperty({ + const property = new IntrospectedProperty({ name: _name, - writable: prop.$?.writable === "1", - readable: prop.$?.readable !== "0", - constructOnly: prop.$?.["construct-only"] === "1", - type: getType(namespace, ns, prop), + writable: element.$?.writable === "1", + readable: element.$?.readable !== "0", + constructOnly: element.$?.["construct-only"] === "1", + type: getType(ns, element), parent, - isIntrospectable: isIntrospectable(prop) + isIntrospectable: isIntrospectable(element) }); if (options.loadDocs) { - property.doc = parseDoc(prop); - property.metadata = parseMetadata(prop); + property.doc = parseDoc(element); + property.metadata = parseMetadata(element); } return property; diff --git a/packages/lib/src/newlib/gir/registry.ts b/packages/lib/src/newlib/gir/registry.ts index dec54b606..5bec9e897 100644 --- a/packages/lib/src/newlib/gir/registry.ts +++ b/packages/lib/src/newlib/gir/registry.ts @@ -15,6 +15,7 @@ import { IntrospectedNamespace } from "./namespace.js"; import { DtsModuleGenerator } from "../generators/dts-modules.js"; import { DtsInlineGenerator } from "../generators/dts-inline.js"; import { ParsedGir } from "../../types/parsed-gir.js"; +import { GenerateConfig } from "../../types/generate-config.js"; export interface NSLoader { load(namespace: string, version: string): ParsedGir | null; @@ -210,7 +211,7 @@ export class NSRegistry { } load(gir: ParsedGir, options: LoadOptions): IntrospectedNamespace { - const namespace = IntrospectedNamespace.fromXML(gir, options, this); + const namespace = IntrospectedNamespace.load(gir, options as LoadOptions & GenerateConfig, this); this.mapping.set(namespace.name, namespace.version, namespace); diff --git a/packages/lib/src/newlib/gir/signal.ts b/packages/lib/src/newlib/gir/signal.ts index 089e4708e..7210f89d2 100644 --- a/packages/lib/src/newlib/gir/signal.ts +++ b/packages/lib/src/newlib/gir/signal.ts @@ -8,10 +8,10 @@ import { NullableType, ArrayType } from "../gir.js"; -import { IntrospectedBase, Options } from "./base.js"; -import { IntrospectedNamespace, isIntrospectable } from "./namespace.js"; +import { IntrospectedClassMember, Options } from "./base.js"; +import { isIntrospectable } from "./namespace.js"; import { IntrospectedClass } from "./class.js"; -import { IntrospectedClassFunction, IntrospectedFunctionParameter, IntrospectedCallback } from "./function.js"; +import { IntrospectedClassFunction, IntrospectedFunctionParameter, IntrospectedClassCallback } from "./function.js"; import { GirSignalElement, GirDirection, GirCallableParamElement } from "@gi.ts/parser"; import { getType, parseDoc, parseMetadata } from "./util.js"; import { FormatGenerator } from "../generators/generator.js"; @@ -24,10 +24,9 @@ export enum IntrospectedSignalType { EMIT } -export class IntrospectedSignal extends IntrospectedBase { +export class IntrospectedSignal extends IntrospectedClassMember { parameters: IntrospectedFunctionParameter[]; return_type: TypeExpression; - parent: IntrospectedClass; constructor({ name, @@ -41,11 +40,10 @@ export class IntrospectedSignal extends IntrospectedBase { return_type?: TypeExpression; parent: IntrospectedClass; }>) { - super(name, { ...args }); + super(name, parent, { ...args }); this.parameters = parameters.map(p => p.copy({ parent: this })); this.return_type = return_type; - this.parent = parent; } accept(visitor: GirVisitor): IntrospectedSignal { @@ -76,24 +74,19 @@ export class IntrospectedSignal extends IntrospectedBase { })._copyBaseProperties(this); } - static fromXML( - modName: string, - ns: IntrospectedNamespace, - options: LoadOptions, - parent: IntrospectedClass, - sig: GirSignalElement - ): IntrospectedSignal { + static fromXML(element: GirSignalElement, parent: IntrospectedClass, options: LoadOptions): IntrospectedSignal { + const ns = parent.namespace; const signal = new IntrospectedSignal({ - name: sig.$.name, + name: element.$.name, parent, - isIntrospectable: isIntrospectable(sig) + isIntrospectable: isIntrospectable(element) }); - if (sig.parameters && sig.parameters[0].parameter) { + if (element.parameters && element.parameters[0].parameter) { signal.parameters.push( - ...sig.parameters[0].parameter + ...element.parameters[0].parameter .filter((p): p is GirCallableParamElement & { $: { name: string } } => !!p.$.name) - .map(p => IntrospectedFunctionParameter.fromXML(modName, ns, options, signal, p)) + .map(p => IntrospectedFunctionParameter.fromXML(p, signal, options)) ); } @@ -148,11 +141,11 @@ export class IntrospectedSignal extends IntrospectedBase { .params.reverse() .filter((p): p is IntrospectedFunctionParameter => p != null); - signal.return_type = getType(modName, ns, sig["return-value"]?.[0]); + signal.return_type = getType(ns, element["return-value"]?.[0]); if (options.loadDocs) { - signal.doc = parseDoc(sig); - signal.metadata = parseMetadata(sig); + signal.doc = parseDoc(element); + signal.metadata = parseMetadata(element); } return signal; @@ -192,12 +185,16 @@ export class IntrospectedSignal extends IntrospectedBase { const name = after ? "connect_after" : "connect"; const parent = this.parent; - const cb = new IntrospectedCallback({ - raw_name: "callback", + const cb = new IntrospectedClassCallback({ + parent, name: "callback", output_parameters: [], parameters: [ - new IntrospectedFunctionParameter({ name: "_source", type: ThisType, direction: GirDirection.In }), + new IntrospectedFunctionParameter({ + name: "_source", + type: ThisType, + direction: GirDirection.In + }), ...connect.parameters.map(p => p.copy()) ], return_type: connect.return_type @@ -222,7 +219,7 @@ export class IntrospectedSignal extends IntrospectedBase { return_type, parameters, name, - parent + parent: this.parent }); } diff --git a/packages/lib/src/newlib/gir/util.ts b/packages/lib/src/newlib/gir/util.ts index 996ed5851..872fc9e11 100644 --- a/packages/lib/src/newlib/gir/util.ts +++ b/packages/lib/src/newlib/gir/util.ts @@ -141,10 +141,11 @@ function isPointerType(types: GirType[] | undefined) { /* Decode the type */ export function getType( - modName: string, - _ns: IntrospectedNamespace, + ns: IntrospectedNamespace, param?: GirConstantElement | GirCallableReturn | GirFieldElement ): TypeExpression { + const modName = ns.name; + if (!param) return VoidType; let name = ""; diff --git a/packages/lib/src/newlib/injections/gio.ts b/packages/lib/src/newlib/injections/gio.ts index 72363c0fb..1530d8bc8 100644 --- a/packages/lib/src/newlib/injections/gio.ts +++ b/packages/lib/src/newlib/injections/gio.ts @@ -20,7 +20,7 @@ import { Generic } from "../gir.js"; import { GirDirection } from "@gi.ts/parser"; -import { Field, JSField } from "../gir/property.js"; +import { IntrospectedField, JSField } from "../gir/property.js"; import { IntrospectedClass, IntrospectedInterface } from "../gir/class.js"; export default { @@ -36,6 +36,7 @@ export default { DBusNodeInfo.constructors.push( new IntrospectedConstructor({ name: "new_for_xml", + parent: DBusNodeInfo, parameters: [ new IntrospectedFunctionParameter({ name: "info", @@ -54,6 +55,7 @@ export default { DBusInterfaceInfo.constructors.push( new IntrospectedConstructor({ name: "new_for_xml", + parent: DBusInterfaceInfo, parameters: [ new IntrospectedFunctionParameter({ name: "info", @@ -70,8 +72,9 @@ export default { const ListStore = namespace.assertClass("ListStore"); ListStore.fields.push( - new Field({ + new IntrospectedField({ name: "Symbol.iterator", + parent: ListStore, computed: true, type: new FunctionType( {}, @@ -87,6 +90,7 @@ export default { SettingsSchema.fields.push( new JSField({ name: "_realGetKey", + parent: SettingsSchema, type: new NativeType("typeof SettingsSchema.prototype.get_key") }) ); @@ -98,18 +102,22 @@ export default { Settings.fields.push( new JSField({ name: "_realInit", + parent: Settings, type: AnyFunctionType }), new JSField({ name: "_realMethods", + parent: Settings, type: new NativeType("typeof Settings.prototype") }), new JSField({ name: "_keys", + parent: Settings, type: new ArrayType(StringType) }), new JSField({ name: "_children", + parent: Settings, type: new ArrayType(StringType) }) ); @@ -286,12 +294,14 @@ export default { new JSField({ isStatic: true, name: "session", + parent: DBus, type: DBusConnection.getType(), writable: false }), new JSField({ isStatic: true, name: "system", + parent: DBus, type: DBusConnection.getType(), writable: false }) diff --git a/packages/lib/src/newlib/injections/glib.ts b/packages/lib/src/newlib/injections/glib.ts index 71da10eed..4d0c2bc1d 100644 --- a/packages/lib/src/newlib/injections/glib.ts +++ b/packages/lib/src/newlib/injections/glib.ts @@ -31,6 +31,7 @@ export default { new IntrospectedFunction({ name: "log_structured", raw_name: "log_structured", + namespace: namespace, parameters: [ new IntrospectedFunctionParameter({ name: "logDomain", @@ -59,6 +60,7 @@ export default { new IntrospectedFunction({ name: "strstrip", raw_name: "strstrip", + namespace: namespace, parameters: [ new IntrospectedFunctionParameter({ name: "str", @@ -132,6 +134,7 @@ export default { ]; const VariantConstructor = new IntrospectedConstructor({ name: "new", + parent: Variant, parameters: VariantParams.map(vp => vp.copy()), return_type: Variant.getType() }); @@ -144,6 +147,7 @@ export default { // static _new_internal: (sig: any, value: any) => any;, new IntrospectedConstructor({ name: "_new_internal", + parent: Variant, parameters: VariantParams.map(vp => vp.copy()), return_type: AnyType }) diff --git a/packages/lib/src/newlib/injections/gobject.ts b/packages/lib/src/newlib/injections/gobject.ts index a318af7f6..0e29e0b1e 100644 --- a/packages/lib/src/newlib/injections/gobject.ts +++ b/packages/lib/src/newlib/injections/gobject.ts @@ -25,7 +25,7 @@ import { GenerifiedTypeIdentifier } from "../gir.js"; import { GirDirection } from "@gi.ts/parser"; -import { Field } from "../gir/property.js"; +import { IntrospectedField } from "../gir/property.js"; import { IntrospectedAlias } from "../gir/alias.js"; import { IntrospectedInterface } from "../gir/class.js"; @@ -50,19 +50,19 @@ export default { name: "SignalMatch", namespace, fields: [ - new Field({ + new IntrospectedField({ name: "signalId", type: StringType, isStatic: false, writable: true }), - new Field({ + new IntrospectedField({ name: "detail", type: StringType, isStatic: false, writable: true }), - new Field({ + new IntrospectedField({ name: "func", type: AnyFunctionType, isStatic: false, @@ -78,6 +78,7 @@ export default { const GType = new IntrospectedAlias({ name: "GType", + namespace, type: new NativeType("any") }); namespace.members.set("GType", GType); @@ -132,7 +133,7 @@ export default { } ParamSpec.fields.push( - new Field({ + new IntrospectedField({ name: "override", isStatic: true, type: AnyType, @@ -254,6 +255,7 @@ export default { "Closure", new IntrospectedAlias({ name: "Closure", + namespace, type: NativeType.of("(...args: P[]) => R"), generics: [ { @@ -394,6 +396,7 @@ export default { "signal_handlers_block_by_func", new IntrospectedFunction({ name: "signal_handlers_block_by_func", + namespace, raw_name: "signal_handlers_block_by_func", parameters: [ new IntrospectedFunctionParameter({ @@ -417,6 +420,7 @@ export default { "signal_handlers_unblock_by_func", new IntrospectedFunction({ name: "signal_handlers_unblock_by_func", + namespace, raw_name: "signal_handlers_unblock_by_func", parameters: [ new IntrospectedFunctionParameter({ @@ -440,6 +444,7 @@ export default { "signal_handlers_disconnect_by_func", new IntrospectedFunction({ name: "signal_handlers_disconnect_by_func", + namespace, raw_name: "signal_handlers_disconnect_by_func", parameters: [ new IntrospectedFunctionParameter({ @@ -533,6 +538,7 @@ export default { function originalFunc(name: string) { return new IntrospectedFunction({ name, + namespace, raw_name: name, return_type: NumberType, parameters: originalArgs.map(a => a.copy()) @@ -545,6 +551,7 @@ export default { // [name](...args: [Object, SignalMatch] | [Object, SignalMatchType, number, GLib.Quark, Closure | null, object | null, object | null]): number; new IntrospectedFunction({ name, + namespace, raw_name: name, return_type: NumberType, parameters: [args] @@ -552,6 +559,7 @@ export default { // export function [name](instance: Object, match: SignalMatch): number; new IntrospectedFunction({ name, + namespace, raw_name: name, return_type: NumberType, parameters: modifiedArgs.map(a => a.copy()) diff --git a/packages/lib/src/newlib/lib.ts b/packages/lib/src/newlib/lib.ts index 54969e80e..d51d9b94b 100644 --- a/packages/lib/src/newlib/lib.ts +++ b/packages/lib/src/newlib/lib.ts @@ -38,11 +38,13 @@ export async function generateModule( ): Promise { const ns = registry.namespace(name, version); + const format = "dts"; // TODO: options.format; + if (ns) { - const Generator = await registry.getGenerator(options.format); + const Generator = await registry.getGenerator(format); if (!Generator) { - throw new Error(`Invalid output format selected: ${options.format}.`); + throw new Error(`Invalid output format selected: ${format}.`); } const generator = new Generator(ns, options); @@ -70,8 +72,8 @@ export async function generateModule( imports: Object.fromEntries(ns.getImports()) }; - const formatter = registry.getFormatter(options.format); - const formatted = !options.noPrettyPrint ? formatter.format(generated) : generated; + const formatter = registry.getFormatter(format); + const formatted = !options.noPrettyPrint ? await formatter.format(generated) : generated; return { formattedOutput: formatted, diff --git a/packages/lib/src/newlib/types.ts b/packages/lib/src/newlib/types.ts index 31b683593..f9d9dbf4c 100644 --- a/packages/lib/src/newlib/types.ts +++ b/packages/lib/src/newlib/types.ts @@ -1,7 +1,10 @@ +import { GenerateConfig } from "../types"; + export type PropertyCase = "both" | "camel" | "underscore"; export type Format = "dts" | "json"; export interface Options { + environment: "gjs"; verbose: boolean; } @@ -16,28 +19,28 @@ export interface TransformOptions extends Options { export type OutputFormat = "file" | "folder"; -export interface GenerationOptions extends Options { - [key: string]: boolean | string | number | undefined; - outputFormat?: string; - outputPath?: string; - promisify: boolean; - withDocs: boolean; - format: string; - versionedOutput: boolean; - versionedImports: boolean; - /** - * A format for versioned imports - * - * @example - * "{namespace}{version}{version-slug}" - */ - versionFormat?: string; - importPrefix: string; - emitMetadata: boolean; - noAdvancedVariants: boolean; - noPrettyPrint: boolean; - noInitTypes: boolean; -} +export type GenerationOptions = GenerateConfig; +// [key: string]: boolean | string | number | undefined; +// outputFormat?: string; +// outputPath?: string; +// promisify: boolean; +// withDocs: boolean; +// format: string; +// versionedOutput: boolean; +// versionedImports: boolean; +// /** +// * A format for versioned imports +// * +// * @example +// * "{namespace}{version}{version-slug}" +// */ +// versionFormat?: string; +// importPrefix: string; +// emitMetadata: boolean; +// noAdvancedVariants: boolean; +// noPrettyPrint: boolean; +// noInitTypes: boolean; +// } export interface Metadata { name: string; diff --git a/packages/lib/src/newlib/validators/class.ts b/packages/lib/src/newlib/validators/class.ts index d5ccae0d4..9c735c318 100644 --- a/packages/lib/src/newlib/validators/class.ts +++ b/packages/lib/src/newlib/validators/class.ts @@ -60,10 +60,10 @@ const filterConflictingNamedClassMembers = (nod * @returns */ const fixParamSpecSubtypes = (node: T): T => { - if (node.parent?.namespace === "GObject" && node.parent.name.startsWith("ParamSpec")) { + if (node.superType?.namespace === "GObject" && node.superType.name.startsWith("ParamSpec")) { // We don't assert this import because we don't want to force libraries // to unnecessarily import GObject. - node.parent = new TypeIdentifier("ParamSpec", "GObject"); + node.superType = new TypeIdentifier("ParamSpec", "GObject"); node.noEmit(); } @@ -82,11 +82,11 @@ const fixParamSpecSubtypes = (node: T): T => { const fixMissingParent = (node: T): T => { const { namespace } = node; - if (node.parent == null) { + if (node.superType == null) { const isGObject = node.someParent(p => p.getType().is("GObject", "Object")); if (isGObject) { - node.parent = namespace.assertInstalledImport("GObject").assertClass("Object").getType(); + node.superType = namespace.assertInstalledImport("GObject").assertClass("Object").getType(); } } @@ -170,7 +170,7 @@ const resolveMainConstructor = (node: IntrospectedRecord): IntrospectedRecord => } if (zeroArgsConstructor || node.isSimpleWithoutPointers()) { - node.mainConstructor = new IntrospectedDirectAllocationConstructor(node.fields); + node.mainConstructor = IntrospectedDirectAllocationConstructor.fromFields(node.fields, node); return node; } @@ -181,7 +181,7 @@ const resolveMainConstructor = (node: IntrospectedRecord): IntrospectedRecord => } if (node.isSimple()) { - node.mainConstructor = new IntrospectedDirectAllocationConstructor(node.fields); + node.mainConstructor = IntrospectedDirectAllocationConstructor.fromFields(node.fields, node); return node; } diff --git a/packages/lib/src/newlib/validators/interface.ts b/packages/lib/src/newlib/validators/interface.ts index c4dddce76..d946773df 100644 --- a/packages/lib/src/newlib/validators/interface.ts +++ b/packages/lib/src/newlib/validators/interface.ts @@ -4,7 +4,7 @@ import { GirVisitor } from "../visitor.js"; export class InterfaceVisitor extends GirVisitor { visitInterface = (node: IntrospectedInterface): IntrospectedInterface => { // If an interface does not list a prerequisite type, we fill it with GObject.Object - if (!node.noParent && node.parent == null) { + if (!node.noParent && node.superType == null) { const gobject = node.namespace.assertInstalledImport("GObject"); const GObject = gobject.assertClass("Object"); @@ -14,7 +14,7 @@ export class InterfaceVisitor extends GirVisitor { ); } - node.parent = GObject.getType(); + node.superType = GObject.getType(); } return node; diff --git a/packages/lib/src/newlib/visitor.ts b/packages/lib/src/newlib/visitor.ts index 9a44ba2ad..f9c4b0987 100644 --- a/packages/lib/src/newlib/visitor.ts +++ b/packages/lib/src/newlib/visitor.ts @@ -11,15 +11,17 @@ import { IntrospectedClassFunction, IntrospectedStaticClassFunction, IntrospectedVirtualClassFunction, - IntrospectedDirectAllocationConstructor + IntrospectedDirectAllocationConstructor, + IntrospectedClassCallback } from "./gir/function.js"; import { IntrospectedNamespace } from "./gir/namespace.js"; -import { GirProperty, Field } from "./gir/property.js"; +import { IntrospectedProperty, IntrospectedField } from "./gir/property.js"; import { IntrospectedSignal, IntrospectedSignalType } from "./gir/signal.js"; export abstract class GirVisitor { visitType?: (node: TypeExpression) => TypeExpression; visitCallback?: (node: IntrospectedCallback) => IntrospectedCallback; + visitClassCallback?: (node: IntrospectedClassCallback) => IntrospectedClassCallback; visitAlias?: (node: IntrospectedAlias) => IntrospectedAlias; visitConstructor?: (node: IntrospectedConstructor) => IntrospectedConstructor; visitDirectAllocationConstructor?: ( @@ -34,8 +36,8 @@ export abstract class GirVisitor { visitConst?: (node: IntrospectedConstant) => IntrospectedConstant; visitClass?: (node: IntrospectedClass) => IntrospectedClass; visitParameter?: (node: IntrospectedFunctionParameter) => IntrospectedFunctionParameter; - visitProperty?: (node: GirProperty) => GirProperty; - visitField?: (node: Field) => Field; + visitProperty?: (node: IntrospectedProperty) => IntrospectedProperty; + visitField?: (node: IntrospectedField) => IntrospectedField; visitSignal?: (node: IntrospectedSignal, type?: IntrospectedSignalType) => IntrospectedSignal; visitFunction?: (node: IntrospectedFunction) => IntrospectedFunction; visitClassFunction?: (node: IntrospectedClassFunction) => IntrospectedClassFunction; diff --git a/packages/lib/src/types/generate-config.ts b/packages/lib/src/types/generate-config.ts index b9809a99c..7227bd9e1 100644 --- a/packages/lib/src/types/generate-config.ts +++ b/packages/lib/src/types/generate-config.ts @@ -39,4 +39,8 @@ export interface GenerateConfig { package: boolean /** Adds Yarn workspace support to the NPM packages */ packageYarn: boolean + /** Disable pretty printing the output */ + noPrettyPrint: boolean + /** Disable GLib.Variant class with string parsing */ + noAdvancedVariants: boolean } diff --git a/packages/lib/src/types/index.ts b/packages/lib/src/types/index.ts index 907c7393a..4757266dd 100644 --- a/packages/lib/src/types/index.ts +++ b/packages/lib/src/types/index.ts @@ -125,3 +125,4 @@ export * from './type-ts-function.js' export * from './type-ts-property.js' export * from './user-config-load-result.js' export * from './user-config.js' +export * from '../newlib/gir.js'