diff --git a/CHANGELOG.md b/CHANGELOG.md index 29ddb9a74..eec357b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,14 @@ ### New Features * Added `examples`, `positional`, and `usage` `yargs` parsing to incomings tasks +* Added new `oclif` style tabling option ### Internals * Added `yargs` to task middleware * Added `appConfig` to `lando` for more powerful tasks * Added `primary` to `appConfig` for more powerful tasks +* Added `otable` to `formatData` * Updated to `yargs@13.3.0` for better `--` handling ## v3.21.2 - [June 20, 2024](https://github.com/lando/cli/releases/tag/v3.21.2) diff --git a/lib/cli.js b/lib/cli.js index e4c90037b..94bededa2 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -108,7 +108,7 @@ module.exports = class Cli { */ confirm(message = 'Are you sure?') { return { - describe: 'Auto answer yes to prompts', + describe: 'Answers yes to prompts', alias: ['y'], default: false, boolean: true, diff --git a/lib/formatters.js b/lib/formatters.js index c0a17772e..c86e63695 100644 --- a/lib/formatters.js +++ b/lib/formatters.js @@ -9,17 +9,17 @@ const util = require('util'); const formats = ['default', 'json', 'table']; const formatOpts = { format: { - describe: `Output in given format: ${formats.join(', ')}`, + describe: `Outputs in given format: ${formats.join(', ')}`, choices: formats, string: true, }, path: { - describe: 'Only return the value at the given path', + describe: 'Returns the value at the given path', default: null, string: true, }, filter: { - describe: 'Filter data by "key=value"', + describe: 'Filters data by "key=value"', array: true, }, }; @@ -40,7 +40,25 @@ exports.formatData = (data, {path = '', format = 'default', filter = []} = {}, o switch (format) { case 'json': return JSON.stringify(data); - break; + case 'otable': + const ux = require('@oclif/core').ux; + // rows + const rows = require('../utils/get-object-keys')(data, {expandArrays: false}).map(key => ({key, value: _.get(data, key)})); + // columes + const columns = {key: {}, value: {get: row => require('../utils/prettify')(row.value)}}; + + // in order to keep this API consistent with return we need to hack console.log + // so we can get the table output in a string + let output = ''; + const ogcl = console.log; + console.log = data => output += `${data}\n`; + + // print table + ux.ux.table(_.sortBy(rows, 'key'), columns); + // restore + console.log = ogcl; + // return + return output; case 'table': const Table = require('./table'); if (!_.isArray(data)) { @@ -52,7 +70,6 @@ exports.formatData = (data, {path = '', format = 'default', filter = []} = {}, o .map(table => table.toString()) .thru(data => data.join(os.EOL)) .value(); - break; default: return util.inspect(data, { colors: process.stdout.isTTY, diff --git a/utils/get-object-keys.js b/utils/get-object-keys.js new file mode 100644 index 000000000..779f9a744 --- /dev/null +++ b/utils/get-object-keys.js @@ -0,0 +1,22 @@ +'use strict'; + +/* + * Returns an array of all keys, nested or otherwise, as "." separated paths but does not expand arrays + * @TODO: implement depth? this is needed for upstream things like get-object-size? + */ +module.exports = (data, {prefix = '', expandArrays = true, separator = '.'} = {}) => { + return Object.keys(data).reduce((keys, key) => { + // if we have a primitive then return the path + if (!data[key] || typeof data[key] !== 'object' || Object.keys(data[key]).length === 0) { + return !key.includes(separator) ? [...keys, `${prefix}${key}`] : [...keys, `${prefix}["${key}"]`]; + } + + // if we arent expanding arrays then dont return paths with array indexes + if (!expandArrays && Array.isArray(data[key])) { + return [...keys, `${prefix}${key}`]; + } + + // otherwise cycle through again + return [...keys, ...module.exports(data[key], {expandArrays, prefix: `${prefix}${key}${separator}`})]; + }, []); +};