UNPKG

easy-cli-framework

Version:

A framework for building CLI applications that are robust and easy to maintain. Supports theming, configuration files, interactive prompts, and more.

254 lines (250 loc) 9.93 kB
'use strict'; var chalk = require('chalk'); var index = require('./logger/index.js'); var themedTable = require('./themed-table.js'); var themedSpinner = require('./themed-spinner.js'); var simpleProgress = require('./progress/simple-progress.js'); require('cli-progress'); var progressWithStatus = require('./progress/progress-with-status.js'); /** @packageDocumentation Support for theming for command line applications, it includes support for verbosity, themed text display, spinners, and progress bars. */ /** * A theme for the CLI, that allows for customizing the look and feel of the CLI, generating logs, tables, spinners, and progress bars. * * @class EasyCLITheme * * @property {number} verbosity The verbosity level of the theme * @property {Record<NamedDisplayOptions, StringDisplayOptions>} namedDisplayOptions The named display options for the theme * @property {EasyCLITheme} setVerbosity Sets the verbosity level of the theme * @property {EasyCLITheme} setNamedDisplayOption Sets the named display options for the theme * @property {EasyCLILogger} getLogger Gets a logger with the theme * @property {ThemedTable} getTable Gets a table with the theme * @property {ThemedSpinner} getSpinner Gets a spinner with the theme * @property {ThemedSimpleProgressBar} getSimpleProgressBar Gets a simple progress bar with the theme * @property {ThemedStatusProgressBar} getStatusProgressBar Gets a status progress bar with the theme * * @example * ```typescript * const theme = new EasyCLITheme( * 0, // Set the verbosity level to 0 * { * log: { color: '#F5F5F5' }, // Update the log color * error: { color: '#FF5555', bold: true }, // Update the error color and make it bold * custom: { color: '#55FF55' }, // Add a custom named display option * } * ); * const logger = theme.getLogger(); * logger.log('Hello, world!'); * ``` */ class EasyCLITheme { /** * Creates an instance of EasyCLITheme. * * @param {number} [verbosity=0] The verbosity level of the theme * @param {Record<NamedDisplayOptions, StringDisplayOptions>} [namedDisplayOptions] The named display options for the theme */ constructor(verbosity = 0, namedDisplayOptions) { this.verbosity = 0; this.namedDisplayOptions = { log: {}, error: { color: '#FF5555', bold: true }, warn: { color: '#FFFF55' }, info: { color: '#F5F5F5' }, success: { color: '#55FF55' }, default: {}, }; this.verbosity = verbosity; if (namedDisplayOptions) { this.namedDisplayOptions = { ...this.namedDisplayOptions, ...namedDisplayOptions, }; } } /** * An internal method to merge display options, allowing for multiple display options to be combined * ie. ['info', { bold: true }] => { color: '#F5F5F5', bold: true } * * @param {DisplayOptions} options The display options to merge * * @returns {StringDisplayOptions} A single display options object */ mergeDisplayOptions(options) { var _a, _b, _c; // If it's a string, we can just return the named display options if (typeof options === 'string') { return { ...this.namedDisplayOptions.default, ...((_b = (_a = this.namedDisplayOptions) === null || _a === undefined ? undefined : _a[options]) !== null && _b !== undefined ? _b : {}), }; } // If it's an object, we can just return the object if (!Array.isArray(options)) { return { ...this.namedDisplayOptions.default, ...options, }; } // Otherwise combine them in reverse order return [(_c = this.namedDisplayOptions.default) !== null && _c !== undefined ? _c : {}, ...options.reverse()] .map(option => { var _a, _b; return typeof option === 'string' ? (_b = (_a = this.namedDisplayOptions) === null || _a === undefined ? undefined : _a[option]) !== null && _b !== undefined ? _b : {} : option; }) .reduce((acc, option) => ({ ...acc, ...option }), {}); } /** * Formats a string with the display options * * @param {string} string The string to format * @param {DisplayOptions} options The display options to use * * @returns {string} The formatted string * * @example * ```typescript * const theme = new EasyCLITheme(); * const formatted = theme.formattedString('Hello, world!', ['info', { bold: true }]); * console.log(formatted); * ``` */ formattedString(string, options) { let formatter = chalk; let formatOptions = this.mergeDisplayOptions(options); if (formatOptions.bold) formatter = formatter.bold; if (formatOptions.italic) formatter = formatter.italic; if (formatOptions.underline) formatter = formatter.underline; if (formatOptions.strikethrough) formatter = formatter.strikethrough; if (formatOptions.color) formatter = formatter.hex(formatOptions.color); if (formatOptions.bgColor) formatter = formatter.bgHex(formatOptions.bgColor); return formatter(string); } /** * Sets the verbosity level of the theme * * @param {number} verbosity The verbosity level to set * * @returns {EasyCLITheme} The theme with the verbosity level, useful for optional chaining */ setVerbosity(verbosity) { this.verbosity = verbosity; return this; } /** * Sets a named display options for the theme * * @param {NamedDisplayOptions} name The name of the display options * @param {StringDisplayOptions} options The display options to set * * @returns {EasyCLITheme} The theme with the named display options set for use with optional chaining. */ setNamedDisplayOption(name, options) { this.namedDisplayOptions[name] = options; return this; } /** * Gets a logger instance with the theme. * * @returns {EasyCLILogger} The logger with the theme */ getLogger() { return new index.EasyCLILogger({ theme: this, verbosity: this.verbosity }); // TODO: Considering generating a singleton logger } /** * Gets a themed table using this theme * @template TItem The datatype for the items in the table. * * @param {ThemedTableColumn<TItem>[]} [columns=[]] The columns for the table * @param {number} [totalWidth=120] The total width of the table * * @returns {ThemedTable<TItem>} The themed table instance * * @example * ```typescript * const theme = new EasyCLITheme(); * const table = theme.getTable([ * { name: 'Name', data: item => item.name }, * { name: 'Age', data: item => item.age }, * ]); * * table.render([ * { name: 'Alice', age: 25 }, * { name: 'Bob', age: 30 }, * ]); * ``` */ getTable(columns = [], totalWidth = 120) { return new themedTable.ThemedTable({ theme: this, columns, totalWidth }); // TODO: Add verbosity and other options } /** * Gets a spinner using this theme * * @param {DisplayOptions} [format='default'] The format for the spinner * * @returns {ThemedSpinner} The themed spinner instance * * @example * ```typescript * const theme = new EasyCLITheme(); * const spinner = theme.getSpinner('default'); * spinner.start('Loading...'); * ``` */ getSpinner(format = 'default') { return new themedSpinner.ThemedSpinner(this, format); // TODO: Add other options } /** * Gets a simple progress bar using this theme * * @param {string} name The name of the progress bar * @param {DisplayOptions} [format='default'] The format for the progress bar * @param {ThemedSimpleProgressBarOptions} [options={}] The options for the progress bar * * @returns {ThemedSimpleProgressBar} The themed simple progress bar instance * @example * ```typescript * const theme = new EasyCLITheme(); * const progressBar = theme.getSimpleProgressBar('progress', 'default', { * showCurrentRecord: true, * currentRecordDisplayOptions: 'info', * }); * ``` */ getSimpleProgressBar(name, format = 'default', options = {}) { return new simpleProgress.ThemedSimpleProgressBar(this, name, format, options); // TODO: Add Other Options } /** * Gets a status progress bar using this theme * * @param {string} name The name of the progress bar * @param {DisplayOptions} [format='default'] The format for the progress bar * @param {ThemedStatusProgressBarOptions} [options={}] The options for the progress bar * * @returns {ThemedStatusProgressBar} The themed status progress bar instance * @example * ```typescript * const theme = new EasyCLITheme(); * const progressBar = theme.getStatusProgressBar('Task', 'Task in progress', { * showCurrentRecord: true, * }); * ``` */ getStatusProgressBar(name, format = 'default', options = {}) { return new progressWithStatus.ThemedStatusProgressBar(this, name, format, options); // TODO: Add Other Options } } exports.EasyCLILogger = index.EasyCLILogger; exports.EasyCLILoggerResponse = index.EasyCLILoggerResponse; exports.ThemedTable = themedTable.ThemedTable; exports.ThemedSpinner = themedSpinner.ThemedSpinner; exports.ThemedSimpleProgressBar = simpleProgress.ThemedSimpleProgressBar; exports.ThemedStatusProgressBar = progressWithStatus.ThemedStatusProgressBar; exports.EasyCLITheme = EasyCLITheme;