UNPKG

hb-lib-tools

Version:

homebridge-lib Command-Line Tools`

178 lines (149 loc) 5.53 kB
// hb-lib-tools/lib/JsonTool.js // // Library for Homebridge plugins. // Copyright © 2018-2025 Erik Baauw. All rights reserved. // // JSON formatter. import { readFile } from 'node:fs/promises' import { promisify } from 'node:util' import { unzip } from 'node:zlib' import { CommandLineParser } from 'hb-lib-tools/CommandLineParser' import { CommandLineTool } from 'hb-lib-tools/CommandLineTool' import { JsonFormatter } from 'hb-lib-tools/JsonFormatter' import { OptionParser } from 'hb-lib-tools/OptionParser' const gunzip = promisify(unzip) const { b, u } = CommandLineTool const usage = `${b('json')} [${b('-hVsnjuatlkv')}] [${b('-p')} path] [${b('-d')} depth] [${b('-c')} ${u('string')}]... [${u('file')}]...` const help = `JSON formatter. Usage: ${usage} By default, ${b('json')} reads JSON from stdin, formats it, and prints it to stdout. Parameters: ${b('-h')}, ${b('--help')} Print this help and exit. ${b('-V')}, ${b('--version')} Print version and exit. ${b('-s')}, ${b('--sortKeys')} Sort object key/value pairs alphabetically on key. ${b('-n')}, ${b('--noWhiteSpace')} Do not include spaces nor newlines in output. ${b('-j')}, ${b('--jsonArray')} Output JSON array of objects for each key/value pair. Each object contains two key/value pairs: - key ${b('keys')} with an array of keys as value; - key ${b('value')} with the value as value. ${b('-u')}, ${b('--joinKeys')} Output JSON array of objects for each key/value pair. Each object contains one key/value pair: the path (concatenated keys separated by ${b('/')} as key and the value as value. ${b('-a')}, ${b('--ascii')} Output ${u('path')}${b(':')}${u('value')} in plain text instead of JSON. ${b('-t')}, ${b('--topOnly')} Limit output to top-level key/values. ${b('-p')} ${u('path')}, ${b('--fromPath=')}${u('path')} Limit output to key/values under ${u('path')}. Set top level below ${u('path')}. ${b('-d')} ${u('depth')}, ${b('--maxDepth=')}${u('depth')} Limit output to levels above ${u('depth')}. ${b('-l')}, ${b('--leavesOnly')} Limit output to leaf (non-array, non-object) key/values. ${b('-k')}, ${b('--keysOnly')} Limit output to keys. With ${b('-u')} output JSON array of paths. ${b('-v')}, ${b('--valuesOnly')} Limit output to values. With ${b('-u')} output JSON array of values. ${b('-c')} ${u('string')}, ${b('--string=')}${u('string')} Read JSON from ${u('string')} instead of from stdin. ${u('file')} Read JSON from ${u('file')} instead of from stdin. When the file name ends in ${b('.gz')}, it is assumed to be a gzip file and uncompressed automatically.` class JsonTool extends CommandLineTool { constructor (pkgJson) { super() this.usage = usage this.options = {} this.pkgJson = pkgJson this.stringList = [] this.fileList = [] } parseArguments () { const parser = new CommandLineParser(this.pkgJson) parser .help('h', 'help', help) .version('V', 'version') .flag('s', 'sortKeys', () => { this.options.sortKeys = true }) .flag('n', 'noWhiteSpace', () => { this.options.noWhiteSpace = true }) .flag('j', 'jsonArray', () => { this.options.jsonArray = true }) .flag('u', 'joinKeys', () => { this.options.joinKeys = true }) .flag('a', 'ascii', () => { this.options.ascii = true }) .flag('t', 'topOnly', () => { this.options.topOnly = true }) .option('d', 'maxDepth', (value, option) => { this.options.maxDepth = OptionParser.toInt( 'maxDepth', value, 0, undefined, true) }) .option('p', 'fromPath', (value, option) => { this.options.fromPath = OptionParser.toPath( 'fromPath', value, true ) }) .flag('l', 'leavesOnly', () => { this.options.leavesOnly = true }) .flag('k', 'keysOnly', () => { this.options.keysOnly = true }) .flag('v', 'valuesOnly', () => { this.options.valuesOnly = true }) .option('c', 'string', (value) => { this.stringList.push(value) }) .remaining((list) => { this.fileList = list }) .parse() } processString (s) { let value try { value = JSON.parse(s) } catch (error) { throw new Error(error.message) // Convert SyntaxError to Error. } const output = this.jsonFormatter.stringify(value) if (this.n++ > 0) { this.print('------') } if (output !== '') { this.print(output) } } async readStdin () { return new Promise((resolve, reject) => { let s = '' process.stdin.setEncoding('utf8') process.stdin.on('data', (data) => { s += data }) process.stdin.on('end', () => { resolve(s) }) }) } async main () { try { this.parseArguments() this.jsonFormatter = new JsonFormatter(this.options) if (this.fileList.length === 0 && this.stringList.length === 0) { this.fileList = ['-'] } this.n = 0 this.stringList.forEach((s) => { try { this.processString(s) } catch (error) { this.error(error) } }) this.fileList.forEach(async (file) => { try { const s = file === '-' ? await this.readStdin() : file.endsWith('.gz') ? await gunzip(await readFile(file)) : await readFile(file, 'utf8') this.processString(s) } catch (error) { this.error(error) } }) } catch (error) { await this.fatal(error) } } } export { JsonTool }