@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