UNPKG

nihilqui

Version:

Typescript .d.ts generator from GIR for gjs and node-gtk

260 lines (223 loc) 8.49 kB
import { findFileInDirs, splitModuleName, pascalCase } from './utils.js' import { Logger } from './logger.js' import { Transformation } from './transformation.js' import type { Dependency, GenerateConfig, GirInclude } from './types/index.js' import type { GirModule } from './gir-module.js' export class DependencyManager { protected log: Logger protected transformation: Transformation cache: { [packageName: string]: Dependency } = {} static instances: { [env: string]: DependencyManager } = {} protected constructor(protected readonly config: GenerateConfig) { this.transformation = new Transformation(config) this.log = new Logger(config.environment, config.verbose, 'DependencyManager') } /** * Get the DependencyManager singleton instance */ static getInstance(config: GenerateConfig): DependencyManager { if (this.instances[config.environment]) { return this.instances[config.environment] } this.instances[config.environment] = new DependencyManager(config) return this.instances[config.environment] } protected parseArgs(namespaceOrPackageName: string, version?: string) { let packageName: string let namespace: string if (version) { namespace = namespaceOrPackageName packageName = `${namespace}-${version}` } else { packageName = namespaceOrPackageName const { namespace: _namespace, version: _version } = splitModuleName(packageName) namespace = _namespace version = _version } return { packageName, namespace, version } } /** * Get all dependencies in the cache * @returns All dependencies in the cache */ all(): Dependency[] { return Object.values(this.cache) } /** * Get the core dependencies * @returns */ core(): Dependency[] { return [this.get('GObject-2.0'), this.get('GLib-2.0')] } createImportProperties(namespace: string, packageName: string) { const importPath = this.createImportPath(packageName) const importDef = this.createImportDef(namespace, importPath) return { importPath, importDef, } } createImportPath(packageName: string): string { const importName = this.transformation.transformImportName(packageName) const importPath = this.config.package ? `${this.config.npmScope}/${importName}` : `./${importName}.js` return importPath } createImportDef(namespace: string, importPath: string): string { return this.config.noNamespace ? `import type * as ${namespace} from '${importPath}'` : `import type ${namespace} from '${importPath}';` } /** * Get the dependency object by packageName * @param packageName The package name (with version affix) of the dependency * @returns The dependency object */ get(packageName: string): Dependency /** * Get the dependency object by namespace and version * @param namespace The namespace of the dependency * @param version The version of the dependency * @returns The dependency object */ get(namespace: string, version: string): Dependency get(namespaceOrPackageName: string, version?: string): Dependency { // Special case for Gjs if (namespaceOrPackageName === 'Gjs') { return this.getGjs() } else if (namespaceOrPackageName === 'node-gtk') { return this.getNodeGtk() } const args = this.parseArgs(namespaceOrPackageName, version) version = args.version const packageName = args.packageName const namespace = args.namespace if (this.cache[packageName]) { const dep = this.cache[packageName] return dep } const filename = `${packageName}.gir` const { exists, path } = findFileInDirs(this.config.girDirectories, filename) const dependency: Dependency = { namespace, exists, filename, path, packageName, importName: this.transformation.transformImportName(packageName), version, ...this.createImportProperties(namespace, packageName), } this.cache[packageName] = dependency return dependency } /** * Get girModule for dependency * @param girModules * @param packageName */ getModule(girModules: GirModule[], dep: Dependency): GirModule | undefined { return girModules.find( (m) => m.packageName === dep.packageName && m.namespace === dep.namespace && m.version === dep.version, ) } /** * Add all dependencies from an array of gir modules * @param girModules */ addAll(girModules: GirModule[]): Dependency[] { for (const girModule of girModules) { this.get(girModule.namespace, girModule.version || '0.0') } return this.all() } /** * Transforms a gir include object array to a dependency object array * @param girIncludes - Array of gir includes * @returns Array of dependencies */ fromGirIncludes(girIncludes: GirInclude[]): Dependency[] { const dependencies: Dependency[] = [] for (const i of girIncludes) { dependencies.unshift(this.get(i.$.name, i.$.version || '0.0')) } return dependencies } getAllPackageNames(): string[] { return Object.keys(this.cache) } /** * Check if multiple dependencies with the given namespace exist in the cache * @param namespace The namespace of the dependency * @returns */ hasConflict(namespace: string): boolean { const packageNames = this.getAllPackageNames() const candidates = packageNames.filter((packageName) => { return packageName.startsWith(`${namespace}-`) && this.cache[packageName].namespace === namespace }) return candidates.length > 1 } /** * Check if the given version is the latest version of the dependency * @param namespace The namespace of the dependency * @param version The version of the dependency * @returns */ isLatestVersion(namespace: string, version: string): boolean { const hasConflict = this.hasConflict(namespace) if (!hasConflict) { return true } const latestVersion = this.find(namespace) return latestVersion?.version === version } /** * Find a dependency by it's namespace from the cache, if multiple versions are found, the latest version is returned * @param namespace The namespace of the dependency * @returns The dependency object or null if not found */ find(namespace: string): Dependency | null { // Special case for Gjs if (namespace === 'Gjs') { return this.getGjs() } else if (namespace === 'node-gtk') { return this.getNodeGtk() } const packageNames = this.getAllPackageNames() const candidates = packageNames.filter((packageName) => { return packageName.startsWith(`${namespace}-`) && this.cache[packageName].namespace === namespace }) if (candidates.length > 1) { this.log.warn(`Found multiple versions of ${namespace}: ${candidates.join(', ')}`) } const latestVersion = candidates.sort().pop() if (latestVersion && this.cache[latestVersion]) { const dep = this.cache[latestVersion] return dep } return null } protected getPseudoPackage(packageName: string): Dependency { if (this.cache[packageName]) { return this.cache[packageName] } const dep: Dependency = { namespace: pascalCase(packageName), exists: true, filename: '', path: '', packageName: packageName, importName: this.transformation.transformImportName(packageName), version: '0.0', ...this.createImportProperties(packageName, packageName), } return dep } getGjs(): Dependency { return this.getPseudoPackage('Gjs') } getNodeGtk(): Dependency { return this.getPseudoPackage('node-gtk') } }