UNPKG

node-gtk

Version:

GNOME Gtk+ bindings for NodeJS

138 lines (113 loc) 3.64 kB
/* * module.js */ const fs = require('fs') const util = require('util') const readdir = util.promisify(fs.readdir) const internal = require('./native.js') const { GI, defineLazyInfos } = require('./bootstrap.js') const moduleCache = internal.GetModuleCache(); module.exports = { require: giRequire, isLoaded, prependSearchPath, prependLibraryPath, listAvailableModules, } /** * Requires a module. Automatically loads dependencies. * @param {string} ns - namespace to load * @param {string} [version=null] - version to load (null for latest) * @returns {Object} the loaded module */ function giRequire(ns, version) { if (moduleCache[ns]) return moduleCache[ns] const module = moduleCache[ns] = Object.create(null) const repo = GI.Repository_get_default() GI.Repository_require.call(repo, ns, version || null, 0) version = version || GI.Repository_get_version.call(repo, ns) loadDependencies(ns, version) // Top-level infos materialize on first access (see defineLazyInfos in // bootstrap.js): building every class of a namespace and its dependencies // eagerly used to cost ~160ms for Gtk-4.0, for the few dozen classes a // typical app touches. defineLazyInfos(module, ns) // Apply overrides, if present let override try { override = require.resolve(`./overrides/${[ns, version].join('-')}.js`) } catch (e) { try { override = require.resolve(`./overrides/${ns}.js`) } catch (e) {} } if (override) require(override).apply(module) return module } /** * Loads dependencies of a library */ function loadDependencies(ns, version) { const repo = GI.Repository_get_default() const dependencies = GI.Repository_get_dependencies.call(repo, ns, version) dependencies.forEach(dependency => { const [name, version] = dependency.split('-') giRequire(name, version) }) } /** * Check if module version is loaded */ function isLoaded(ns, version) { const namespace = moduleCache[ns] || (moduleCache[ns] = Object.create(null)); version = version || null; if (namespace[version]) return true; if (version == null && namespace.length > 0) return true; return false; } /** * Prepends a path to GObject-Introspection search path (for typelibs) * @param {string} path */ function prependSearchPath(path) { GI.Repository_prepend_search_path(path) } /** * Prepends a path to GObject-Introspection library path (for shared libraries) * @param {string} path */ function prependLibraryPath(path) { GI.Repository_prepend_library_path(path) } /** * @typedef ModuleDescription * @type {object} * @property {string} name * @property {string} version */ /** * Returns a list of available modules * @returns {Promise<ModuleDescription[]>} */ function listAvailableModules() { const paths = GI.Repository_get_search_path() return Promise.all(paths.map(path => readdir(path).catch(err => Promise.resolve([])) )) .then(results => results.reduce((acc, cur) => acc.concat(cur))) .then(filenames => filenames.filter(filename => filename.endsWith('.typelib'))) .then(filenames => filenames.map(parseModuleFilename)) } // Helpers function parseModuleFilename(filename) { // Typelibs are named `Name-Version.typelib`; the version is the trailing // `-X.Y`, so split on the LAST dash — namespaces themselves may contain dashes // (e.g. `GUPnP-DLNA-2.0` -> name `GUPnP-DLNA`, version `2.0`). const base = filename.replace(/\.typelib$/, '') const dash = base.lastIndexOf('-') if (dash === -1) return { name: base, version: '' } return { name: base.slice(0, dash), version: base.slice(dash + 1) } }