@report-toolkit/cli
Version:
See docs at [https://ibm.github.io/report-toolkit](https://ibm.github.io/report-toolkit)
220 lines (207 loc) • 5.95 kB
JavaScript
import {
_,
constants,
createDebugger,
createDebugPipe,
observable
} from '@report-toolkit/common';
import {observable as observableAPI} from '@report-toolkit/core';
import {toObjectFromFilepath} from '@report-toolkit/fs';
import {terminalColumns} from '../console-utils.js';
const debug = createDebugPipe('cli', 'commands', 'common');
const {
compatibleTransformers,
builtinTransformerIds,
toReportFromObject
} = observableAPI;
const {fromAny, share} = observable;
const toUniqueArray = _.pipe(_.castArray, _.uniq);
export const GROUPS = {
FILTER: 'Filter:',
FILTER_TRANSFORM: '"filter" Transform Options:',
JSON_TRANSFORM: '"json" Transform Options:',
OUTPUT: 'Output:',
TABLE_TRANSFORM: '"table" Transform Options:'
};
export const OPTIONS = {
FILTER_TRANSFORM: {
exclude: {
coerce: toUniqueArray,
/** @type {string[]} */
default: [],
description: 'Exclude properties (keypaths allowed)',
group: GROUPS.FILTER_TRANSFORM,
nargs: 1,
type: 'string'
},
include: {
coerce: toUniqueArray,
/** @type {string[]} */
default: [],
description: 'Include properties (keypaths allowed)',
group: GROUPS.FILTER_TRANSFORM,
nargs: 1,
type: 'string'
}
},
JSON_TRANSFORM: {
pretty: {
description: 'Pretty-print JSON output',
group: GROUPS.JSON_TRANSFORM,
type: 'boolean'
}
},
OUTPUT: {
output: {
alias: 'o',
description: 'Output to file instead of STDOUT',
group: GROUPS.OUTPUT,
normalize: true,
requiresArg: true,
type: 'string'
},
'show-secrets-unsafe': {
description: 'Live dangerously & do not automatically redact secrets',
group: GROUPS.OUTPUT,
type: 'boolean'
}
},
TABLE_TRANSFORM: {
'max-width': {
default: terminalColumns,
defaultDescription: 'terminal width',
description: 'Set maximum output width; ignored if --no-truncate used',
group: GROUPS.TABLE_TRANSFORM,
type: 'number'
},
truncate: {
default: true,
description: 'Truncate & word-wrap output',
group: GROUPS.TABLE_TRANSFORM,
type: 'boolean'
}
},
TRANSFORM: {
// @todo list transform aliases
alias: 't',
choices: builtinTransformerIds,
coerce: toUniqueArray,
default: constants.DEFAULT_TRANSFORMER,
description: 'Transform(s) to apply',
group: GROUPS.OUTPUT,
nargs: 1,
type: 'string'
}
};
/**
* Yes, yes, I know.
* @typedef {Object} GetTransformerOptionsOptions
* @property {'report'|'object'} sourceType - Whether the transformer source is a report or an object
* @property {string} defaultTransformer - Name of default transformer
* @property {object} extra - Merge these into the result; use this to override specific values (e.g., default behavior of a transformer)
* @property {string[]} omit - List of transformers to explicitly omit, if any
*/
/**
* Get all transformer options for a command
* @param {Partial<GetTransformerOptionsOptions>} opts - Options
*/
export const getTransformerOptions = ({
sourceType = 'report',
defaultTransformer = constants.DEFAULT_TRANSFORMER,
omit = [],
extra = {}
} = {}) => {
const transformerNames = _.filter(
transformerName => !omit.includes(transformerName),
compatibleTransformers(sourceType)
);
return _.defaultsDeep(
_.reduce(
(acc, transformName) => {
const transformSpecificOptions =
OPTIONS[
/** @type {keyof OPTIONS} */ (`${transformName.toUpperCase()}_TRANSFORM`)
];
if (transformSpecificOptions) {
acc = {...acc, ...transformSpecificOptions};
}
return acc;
},
{
transform: {
...OPTIONS.TRANSFORM,
choices: transformerNames,
default: defaultTransformer
}
},
transformerNames
),
extra
);
};
/**
*
* @param {string[]|string} filepaths
* @param {Partial<import('@report-toolkit/core/src/observable').ToReportFromObjectOptions>} opts
*/
export function fromFilepathsToReports(filepaths, opts = {}) {
return fromAny(filepaths).pipe(
debug(() => `attempting to load filepath(s): ${filepaths}`),
toObjectFromFilepath(),
toReportFromObject(opts),
debug(() => `loaded filepath(s): ${filepaths}`),
share()
);
}
/**
* Compute a configuration for a particular command. Command-specific
* config overrides whatever the top-level config is.
* @param {string} commandName - Name of command
* @param {object} [argv] - Command-line args
* @param {object} [defaultConfig] - Default command configuration
* @returns {object} Resulting config with command-specific stuff on top
*/
export const mergeCommandConfig = (
commandName,
argv = {},
defaultConfig = {}
) => {
const config = _.omit(
'commands',
_.mergeAll([
// overwrite default config with more-specific default command config, if it exists.
_.defaultsDeep(
defaultConfig,
_.getOr({}, `commands.${commandName}`, defaultConfig)
),
// overwrite transformer default with arg-supplied transformer config
{
transformers: _.mapValues(
transformerConfig =>
_.defaultsDeep(
transformerConfig,
_.omit(['$0', 'config', '_'], argv)
),
_.getOr({}, 'transformers', defaultConfig)
)
},
// overwrite config with command-specific config
_.defaultsDeep(
_.getOr({}, 'config', argv),
_.getOr({}, `config.commands.${commandName}`, argv)
),
_.omit(['$0', 'config', '_'], argv)
])
);
// @ts-ignore
if (_.isEmpty(config.transformers)) {
// @ts-ignore
delete config.transformers;
}
createDebugger(
'cli',
'commands',
'common'
)(`computed config for command "${commandName}": %O`, config);
return config;
};