@salesforce/command
Version:
Salesforce CLI base command class
335 lines • 11.7 kB
JavaScript
"use strict";
/*
* Copyright (c) 2020, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.UX = void 0;
// would be willing to change this, but don't want to change types on public methods (object => Record<string, undefined>))
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable no-console */
const kit_1 = require("@salesforce/kit");
const ts_types_1 = require("@salesforce/ts-types");
/**
* A table option configuration type that can be the TableOptions as defined by
* [oclif/cli-ux](https://github.com/oclif/cli-ux/blob/master/src/styled/table.ts) or a string array of table keys to be used as table headers
* for simple tables.
*
* @typedef {object} SfdxTableOptions
* @property {TableOptions | string[]} options
*/
/**
* A prompt option configuration as defined by
* [oclif/cli-ux](https://github.com/oclif/cli-ux/blob/master/src/prompt.ts).
*
* @typedef {object} IPromptOptions
* @property {string} prompt The prompt string displayed to the user.
* @property {'normal' | 'mask' | 'hide'} type `Normal` does not hide the user input, `mask` hides the user input after the user presses `ENTER`, and `hide` hides the user input as it is being typed.
*/
/**
* An action option configuration as defined by
* [oclif/cli-ux](https://github.com/oclif/cli-ux/blob/master/src/action/base.ts).
*
* @typedef {object} OclifActionOptions
* @property {boolean} stdout The option to display to stdout or not.
*/
const core_1 = require("@salesforce/core");
const chalk_1 = require("chalk");
const core_2 = require("@oclif/core");
/**
* @deprecated Use Ux from `@salesforce/sf-plugins-core` instead
* Utilities for interacting with terminal I/O.
*/
class UX {
/**
* Do not directly construct instances of this class -- use {@link UX.create} instead.
*/
constructor(logger, isOutputEnabled, ux) {
this.logger = logger;
this.cli = ux ?? core_2.CliUx;
if ((0, ts_types_1.isBoolean)(isOutputEnabled)) {
this.isOutputEnabled = isOutputEnabled;
}
else {
// Respect the --json flag and SFDX_CONTENT_TYPE for consumers who don't explicitly check
const isContentTypeJSON = kit_1.env.getString('SFDX_CONTENT_TYPE', '').toUpperCase() === 'JSON';
this.isOutputEnabled = !(process.argv.find((arg) => arg === '--json') ?? isContentTypeJSON);
}
}
/**
* Formats a deprecation warning for display to `stderr`, `stdout`, and/or logs.
*
* @param {DeprecationDefinition} def The definition for the deprecated object.
* @returns {string} The formatted deprecation message.
*/
static formatDeprecationWarning(def) {
let msg;
if ((0, ts_types_1.has)(def, 'version')) {
const version = (0, ts_types_1.isString)(def.version) ? parseInt(def.version, 10) : def.version || 0;
// @ts-ignore
const type = (0, ts_types_1.ensure)(def.type);
// @ts-ignore
const name = (0, ts_types_1.ensure)(def.name);
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
msg = `The ${type} "${name}" has been deprecated and will be removed in v${version + 1}.0 or later.`;
}
else {
msg = def.messageOverride;
}
if (def.to) {
msg += ` Use "${def.to}" instead.`;
}
if (def.message) {
msg += ` ${def.message}`;
}
return msg;
}
/**
* Create a `UX` instance.
*
* @returns {Promise<UX>} A `Promise` of the created `UX` instance.
*/
static async create() {
return new UX(await core_1.Logger.child('UX'));
}
/**
* Logs at `INFO` level and conditionally writes to `stdout` if stream output is enabled.
*
* @param {...any[]} args The messages or objects to log.
* @returns {UX}
*/
log(...args) {
if (this.isOutputEnabled) {
this.cli.ux.log(...args);
}
// log to sfdx.log after the console as log filtering mutates the args.
this.logger.info(...args);
return this;
}
/**
* Log JSON to stdout and to the log file with log level info.
*
* @param {object} obj The object to log -- must be serializable as JSON.
* @returns {UX}
* @throws {TypeError} If the object is not JSON-serializable.
*/
logJson(obj) {
this.cli.ux.styledJSON(obj);
// log to sfdx.log after the console as log filtering mutates the args.
this.logger.info(obj);
return this;
}
/**
* Prompt the user for input.
*
* @param {string} name The string that the user sees when prompted for information.
* @param {IPromptOptions} options A prompt option configuration.
* @returns {Promise<string>} The user input to the prompt.
*/
async prompt(name, options = {}) {
return this.cli.ux.prompt(name, options);
}
/**
* Prompt the user for confirmation.
*
* @param {string} message The message displayed to the user.
* @returns {Promise<boolean>} Returns `true` if the user inputs 'y' or 'yes', and `false` if the user inputs 'n' or 'no'.
*/
async confirm(message) {
return this.cli.ux.confirm(message);
}
/**
* Start a spinner action after displaying the given message.
*
* @param {string} message The message displayed to the user.
* @param {string} status The status displayed to the user.
* @param {OclifActionOptions} opts The options to select whereas spinner will output to stderr or stdout.
*/
startSpinner(message, status, opts = {}) {
if (this.isOutputEnabled) {
this.cli.ux.action.start(message, status, opts);
}
}
/**
* Pause the spinner and call the given function.
*
* @param {function} fn The function to be called in the pause.
* @param {string} icon The string displayed to the user.
* @returns {T} The result returned by the passed in function.
*/
pauseSpinner(fn, icon) {
if (this.isOutputEnabled) {
return this.cli.ux.action.pause(fn, icon);
}
}
/**
* Update the spinner status.
*
* @param {string} status The message displayed to the user.
*/
setSpinnerStatus(status) {
if (this.isOutputEnabled) {
this.cli.ux.action.status = status;
}
}
/**
* Get the spinner status.
*
* @returns {Optional<string>}
*/
getSpinnerStatus() {
if (this.isOutputEnabled) {
return this.cli.ux.action.status;
}
}
/**
* Stop the spinner action.
*
* @param {string} message The message displayed to the user.
*/
stopSpinner(message) {
if (this.isOutputEnabled) {
this.cli.ux.action.stop(message);
}
}
/**
* Logs a warning as `WARN` level and conditionally writes to `stderr` if the log
* level is `WARN` or above and stream output is enabled. The message is added
* to the static {@link UX.warnings} set if stream output is _not_ enabled, for later
* consumption and manipulation.
*
* @param {string} message The warning message to output.
* @returns {UX}
* @see UX.warnings
*/
warn(message) {
const warning = chalk_1.default.yellow('WARNING:');
// Necessarily log to sfdx.log.
this.logger.warn(warning, message);
if (this.logger.shouldLog(core_1.LoggerLevel.WARN)) {
if (!this.isOutputEnabled) {
UX.warnings.add(message);
}
else {
console.warn(`${warning} ${message}`);
}
}
return this;
}
/**
* Logs an error at `ERROR` level and conditionally writes to `stderr` if stream
* output is enabled.
*
* @param {...any[]} args The errors to log.
* @returns {UX}
*/
error(...args) {
if (this.isOutputEnabled) {
console.error(...args);
}
this.logger.error(...args);
return this;
}
/**
* Logs an object as JSON at `ERROR` level and to `stderr`.
*
* @param {object} obj The error object to log -- must be serializable as JSON.
* @returns {UX}
* @throws {TypeError} If the object is not JSON-serializable.
*/
errorJson(obj) {
const err = JSON.stringify(obj, null, 4);
console.error(err);
this.logger.error(err);
return this;
}
/**
* Logs at `INFO` level and conditionally writes to `stdout` in a table format if
* stream output is enabled.
*
* @param {object[]} rows The rows of data to be output in table format.
* @param columns Table column options
* @param {SfdxTableOptions} options The {@link SfdxTableOptions} to use for formatting.
* @returns {UX}
*/
table(
// (allow any because matches oclif)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
rows, columns = {}, options = { 'no-truncate': true }) {
if (this.isOutputEnabled) {
// This is either an array of column names or an already built Partial<OclifTableOptions>
if ((0, ts_types_1.isArray)(columns)) {
const tableColumns = {};
for (const col of columns) {
tableColumns[col] = {
header: col
.split(/(?=[A-Z])|[-_\s]/)
.map((w) => w.toUpperCase())
.join(' '),
};
}
this.cli.ux.table(rows, tableColumns, options);
}
else {
this.cli.ux.table(rows, columns, options);
}
}
// Log after table output as log filtering mutates data.
this.logger.info(rows);
return this;
}
/**
* Logs at `INFO` level and conditionally writes to `stdout` in a styled object format if
* stream output is enabled.
*
* @param {object} obj The object to be styled for stdout.
* @param {string[]} [keys] The object keys to be written to stdout.
* @returns {UX}
*/
styledObject(obj, keys) {
this.logger.info(obj);
if (this.isOutputEnabled) {
this.cli.ux.styledObject(obj, keys);
}
return this;
}
/**
* Log at `INFO` level and conditionally write to `stdout` in styled JSON format if
* stream output is enabled.
*
* @param {object} obj The object to be styled for stdout.
* @returns {UX}
*/
styledJSON(obj) {
this.logger.info(obj);
if (this.isOutputEnabled) {
this.cli.ux.styledJSON(obj);
}
return this;
}
/**
* Logs at `INFO` level and conditionally writes to `stdout` in a styled header format if
* stream output is enabled.
*
* @param {string} header The header to be styled.
* @returns {UX}
*/
styledHeader(header) {
this.logger.info(header);
if (this.isOutputEnabled) {
this.cli.ux.styledHeader(header);
}
return this;
}
}
exports.UX = UX;
/**
* Collection of warnings that can be accessed and manipulated later.
*
* @type {Set<string>}
*/
UX.warnings = new Set();
//# sourceMappingURL=ux.js.map