node-gtk
Version:
GNOME Gtk+ bindings for NodeJS
126 lines (106 loc) • 4.84 kB
JavaScript
/*
* list-libraries.js — list the GObject-Introspection libraries (typelibs)
* available on this machine, grouped by namespace with their versions.
*
* Driven by the CLI: `node-gtk list [filter] [options]`. The names/versions it
* prints are exactly what you pass to `require()` / the `gi:` import scheme /
* `generate-types` (e.g. `Gtk-4.0`).
*/
const gi = require('../lib/index.js')
const GI = gi._GIRepository
// ---------------------------------------------------------------------------
// data
// ---------------------------------------------------------------------------
// Compare dotted versions numerically, ascending ("3.0" < "4.0" < "10.0"),
// falling back to a string compare for non-numeric segments.
function compareVersions(a, b) {
const pa = a.split('.'), pb = b.split('.')
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
const x = pa[i] ?? '', y = pb[i] ?? ''
const nx = Number(x), ny = Number(y)
if (Number.isNaN(nx) || Number.isNaN(ny)) { if (x !== y) return x < y ? -1 : 1 }
else if (nx !== ny) return nx - ny
}
return 0
}
// Group the flat [{name, version}] from listAvailableModules() into a sorted
// list of { name, versions } (names A→Z, versions ascending, deduped).
async function collect(filter) {
const modules = await gi.listAvailableModules()
const byName = new Map()
for (const { name, version } of modules) {
if (filter && !name.toLowerCase().includes(filter.toLowerCase())) continue
if (!byName.has(name)) byName.set(name, new Set())
if (version) byName.get(name).add(version)
}
return [...byName.entries()]
.map(([name, versions]) => ({ name, versions: [...versions].sort(compareVersions) }))
.sort((a, b) => a.name.localeCompare(b.name))
}
// ---------------------------------------------------------------------------
// output
// ---------------------------------------------------------------------------
const useColor = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR
const ansi = (open, close) => (s) => useColor ? `\x1b[${open}m${s}\x1b[${close}m` : s
const bold = ansi(1, 22)
const dim = ansi(2, 22)
const cyan = ansi(36, 39)
function printTable(libs) {
if (libs.length === 0) {
process.stdout.write(`${dim('No libraries found.')}\n`)
return
}
const width = libs.reduce((w, l) => Math.max(w, l.name.length), 0)
for (const { name, versions } of libs)
process.stdout.write(` ${bold(name.padEnd(width))} ${dim(versions.join(', '))}\n`)
}
// ---------------------------------------------------------------------------
// cli — `node-gtk list [filter] [options]`
// ---------------------------------------------------------------------------
const HELP = `node-gtk list — list the GObject-Introspection libraries available on this machine
Usage:
node-gtk list [filter] [options]
Arguments:
filter Only show libraries whose name contains this (case-insensitive)
Options:
--json Output as JSON: [{ "name", "versions": [...] }]
-h, --help Show this help
Examples:
node-gtk list
node-gtk list gtk
node-gtk list --json
`
function run(argv) {
// Don't crash when the output is piped into a reader that closes early
// (e.g. `node-gtk list | head`).
process.stdout.on('error', (err) => { if (err.code === 'EPIPE') process.exit(0); throw err })
const opts = { json: false }
for (const arg of argv) {
if (arg === '-h' || arg === '--help') { process.stdout.write(HELP); return }
else if (arg === '--json') opts.json = true
else if (arg.startsWith('-')) { process.stderr.write(`node-gtk list: unknown option '${arg}'\n\n${HELP}`); process.exit(1) }
else if (opts.filter === undefined) opts.filter = arg
else { process.stderr.write(`node-gtk list: unexpected argument '${arg}'\n\n${HELP}`); process.exit(1) }
}
collect(opts.filter).then((libs) => {
if (opts.json) {
process.stdout.write(JSON.stringify(libs, null, 2) + '\n')
return
}
const what = opts.filter ? `matching ${JSON.stringify(opts.filter)}` : 'available'
process.stdout.write(`\n${bold(`${libs.length}`)} ${libs.length === 1 ? 'library' : 'libraries'} ${what}:\n\n`)
printTable(libs)
if (libs.length) {
const first = libs.find(l => l.versions.length) || libs[0]
const example = first.versions.length ? `${first.name}-${first.versions[first.versions.length - 1]}` : first.name
process.stdout.write(`\n${dim('Use one with, e.g.:')} ${cyan(`node-gtk generate-types ${example}`)}\n\n`)
} else {
const paths = (GI.Repository_get_search_path() || [])
process.stdout.write(`\n${dim('Searched: ' + paths.join(', '))}\n`)
}
}).catch((err) => {
process.stderr.write(`node-gtk list: ${err.message}\n`)
process.exit(1)
})
}
module.exports = { run, collect, compareVersions }