UNPKG

11.5 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright (c) 2020, salesforce.com, inc.
4 * All rights reserved.
5 * Licensed under the BSD 3-Clause license.
6 * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7 */
8Object.defineProperty(exports, "__esModule", { value: true });
9exports.UX = void 0;
10// would be willing to change this, but don't want to change types on public methods (object => Record<string, undefined>))
11/* eslint-disable @typescript-eslint/ban-types */
12/* eslint-disable no-console */
13const kit_1 = require("@salesforce/kit");
14const ts_types_1 = require("@salesforce/ts-types");
15/**
16 * A table option configuration type that can be the TableOptions as defined by
17 * [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
18 * for simple tables.
19 *
20 * @typedef {object} SfdxTableOptions
21 * @property {TableOptions | string[]} options
22 */
23/**
24 * A prompt option configuration as defined by
25 * [oclif/cli-ux](https://github.com/oclif/cli-ux/blob/master/src/prompt.ts).
26 *
27 * @typedef {object} IPromptOptions
28 * @property {string} prompt The prompt string displayed to the user.
29 * @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.
30 */
31/**
32 * An action option configuration as defined by
33 * [oclif/cli-ux](https://github.com/oclif/cli-ux/blob/master/src/action/base.ts).
34 *
35 * @typedef {object} OclifActionOptions
36 * @property {boolean} stdout The option to display to stdout or not.
37 */
38const core_1 = require("@salesforce/core");
39const chalk_1 = require("chalk");
40const core_2 = require("@oclif/core");
41/**
42 * Utilities for interacting with terminal I/O.
43 */
44class UX {
45 /**
46 * Do not directly construct instances of this class -- use {@link UX.create} instead.
47 */
48 constructor(logger, isOutputEnabled, ux) {
49 this.logger = logger;
50 this.cli = ux || core_2.CliUx;
51 if ((0, ts_types_1.isBoolean)(isOutputEnabled)) {
52 this.isOutputEnabled = isOutputEnabled;
53 }
54 else {
55 // Respect the --json flag and SFDX_CONTENT_TYPE for consumers who don't explicitly check
56 const isContentTypeJSON = kit_1.env.getString('SFDX_CONTENT_TYPE', '').toUpperCase() === 'JSON';
57 this.isOutputEnabled = !(process.argv.find((arg) => arg === '--json') || isContentTypeJSON);
58 }
59 }
60 /**
61 * Formats a deprecation warning for display to `stderr`, `stdout`, and/or logs.
62 *
63 * @param {DeprecationDefinition} def The definition for the deprecated object.
64 * @returns {string} The formatted deprecation message.
65 */
66 static formatDeprecationWarning(def) {
67 let msg;
68 if ((0, ts_types_1.has)(def, 'version')) {
69 const version = (0, ts_types_1.isString)(def.version) ? parseInt(def.version, 10) : def.version || 0;
70 const type = (0, ts_types_1.ensure)(def.type);
71 const name = (0, ts_types_1.ensure)(def.name);
72 msg = `The ${type} "${name}" has been deprecated and will be removed in v${version + 1}.0 or later.`;
73 }
74 else {
75 msg = def.messageOverride;
76 }
77 if (def.to) {
78 msg += ` Use "${def.to}" instead.`;
79 }
80 if (def.message) {
81 msg += ` ${def.message}`;
82 }
83 return msg;
84 }
85 /**
86 * Create a `UX` instance.
87 *
88 * @returns {Promise<UX>} A `Promise` of the created `UX` instance.
89 */
90 static async create() {
91 return new UX(await core_1.Logger.child('UX'));
92 }
93 /**
94 * Logs at `INFO` level and conditionally writes to `stdout` if stream output is enabled.
95 *
96 * @param {...any[]} args The messages or objects to log.
97 * @returns {UX}
98 */
99 log(...args) {
100 if (this.isOutputEnabled) {
101 this.cli.ux.log(...args);
102 }
103 // log to sfdx.log after the console as log filtering mutates the args.
104 this.logger.info(...args);
105 return this;
106 }
107 /**
108 * Log JSON to stdout and to the log file with log level info.
109 *
110 * @param {object} obj The object to log -- must be serializable as JSON.
111 * @returns {UX}
112 * @throws {TypeError} If the object is not JSON-serializable.
113 */
114 logJson(obj) {
115 this.cli.ux.styledJSON(obj);
116 // log to sfdx.log after the console as log filtering mutates the args.
117 this.logger.info(obj);
118 return this;
119 }
120 /**
121 * Prompt the user for input.
122 *
123 * @param {string} name The string that the user sees when prompted for information.
124 * @param {IPromptOptions} options A prompt option configuration.
125 * @returns {Promise<string>} The user input to the prompt.
126 */
127 async prompt(name, options = {}) {
128 return this.cli.ux.prompt(name, options);
129 }
130 /**
131 * Prompt the user for confirmation.
132 *
133 * @param {string} message The message displayed to the user.
134 * @returns {Promise<boolean>} Returns `true` if the user inputs 'y' or 'yes', and `false` if the user inputs 'n' or 'no'.
135 */
136 async confirm(message) {
137 return this.cli.ux.confirm(message);
138 }
139 /**
140 * Start a spinner action after displaying the given message.
141 *
142 * @param {string} message The message displayed to the user.
143 * @param {string} status The status displayed to the user.
144 * @param {OclifActionOptions} opts The options to select whereas spinner will output to stderr or stdout.
145 */
146 startSpinner(message, status, opts = {}) {
147 if (this.isOutputEnabled) {
148 this.cli.ux.action.start(message, status, opts);
149 }
150 }
151 /**
152 * Pause the spinner and call the given function.
153 *
154 * @param {function} fn The function to be called in the pause.
155 * @param {string} icon The string displayed to the user.
156 * @returns {T} The result returned by the passed in function.
157 */
158 pauseSpinner(fn, icon) {
159 if (this.isOutputEnabled) {
160 return this.cli.ux.action.pause(fn, icon);
161 }
162 }
163 /**
164 * Update the spinner status.
165 *
166 * @param {string} status The message displayed to the user.
167 */
168 setSpinnerStatus(status) {
169 if (this.isOutputEnabled) {
170 this.cli.ux.action.status = status;
171 }
172 }
173 /**
174 * Get the spinner status.
175 *
176 * @returns {Optional<string>}
177 */
178 getSpinnerStatus() {
179 if (this.isOutputEnabled) {
180 return this.cli.ux.action.status;
181 }
182 }
183 /**
184 * Stop the spinner action.
185 *
186 * @param {string} message The message displayed to the user.
187 */
188 stopSpinner(message) {
189 if (this.isOutputEnabled) {
190 this.cli.ux.action.stop(message);
191 }
192 }
193 /**
194 * Logs a warning as `WARN` level and conditionally writes to `stderr` if the log
195 * level is `WARN` or above and stream output is enabled. The message is added
196 * to the static {@link UX.warnings} set if stream output is _not_ enabled, for later
197 * consumption and manipulation.
198 *
199 * @param {string} message The warning message to output.
200 * @returns {UX}
201 * @see UX.warnings
202 */
203 warn(message) {
204 const warning = chalk_1.default.yellow('WARNING:');
205 // Necessarily log to sfdx.log.
206 this.logger.warn(warning, message);
207 if (this.logger.shouldLog(core_1.LoggerLevel.WARN)) {
208 if (!this.isOutputEnabled) {
209 UX.warnings.add(message);
210 }
211 else {
212 console.warn(`${warning} ${message}`);
213 }
214 }
215 return this;
216 }
217 /**
218 * Logs an error at `ERROR` level and conditionally writes to `stderr` if stream
219 * output is enabled.
220 *
221 * @param {...any[]} args The errors to log.
222 * @returns {UX}
223 */
224 error(...args) {
225 if (this.isOutputEnabled) {
226 console.error(...args);
227 }
228 this.logger.error(...args);
229 return this;
230 }
231 /**
232 * Logs an object as JSON at `ERROR` level and to `stderr`.
233 *
234 * @param {object} obj The error object to log -- must be serializable as JSON.
235 * @returns {UX}
236 * @throws {TypeError} If the object is not JSON-serializable.
237 */
238 errorJson(obj) {
239 const err = JSON.stringify(obj, null, 4);
240 console.error(err);
241 this.logger.error(err);
242 return this;
243 }
244 /**
245 * Logs at `INFO` level and conditionally writes to `stdout` in a table format if
246 * stream output is enabled.
247 *
248 * @param {object[]} rows The rows of data to be output in table format.
249 * @param columns Table column options
250 * @param {SfdxTableOptions} options The {@link SfdxTableOptions} to use for formatting.
251 * @returns {UX}
252 */
253 table(
254 // (allow any because matches oclif)
255 // eslint-disable-next-line @typescript-eslint/no-explicit-any
256 rows, columns = {}, options = { 'no-truncate': true }) {
257 if (this.isOutputEnabled) {
258 // This is either an array of column names or an already built Partial<OclifTableOptions>
259 if ((0, ts_types_1.isArray)(columns)) {
260 const tableColumns = {};
261 for (const col of columns) {
262 tableColumns[col] = {
263 header: col
264 .split(/(?=[A-Z])|[-_\s]/)
265 .map((w) => w.toUpperCase())
266 .join(' '),
267 };
268 }
269 this.cli.ux.table(rows, tableColumns, options);
270 }
271 else {
272 this.cli.ux.table(rows, columns, options);
273 }
274 }
275 // Log after table output as log filtering mutates data.
276 this.logger.info(rows);
277 return this;
278 }
279 /**
280 * Logs at `INFO` level and conditionally writes to `stdout` in a styled object format if
281 * stream output is enabled.
282 *
283 * @param {object} obj The object to be styled for stdout.
284 * @param {string[]} [keys] The object keys to be written to stdout.
285 * @returns {UX}
286 */
287 styledObject(obj, keys) {
288 this.logger.info(obj);
289 if (this.isOutputEnabled) {
290 this.cli.ux.styledObject(obj, keys);
291 }
292 return this;
293 }
294 /**
295 * Log at `INFO` level and conditionally write to `stdout` in styled JSON format if
296 * stream output is enabled.
297 *
298 * @param {object} obj The object to be styled for stdout.
299 * @returns {UX}
300 */
301 styledJSON(obj) {
302 this.logger.info(obj);
303 if (this.isOutputEnabled) {
304 this.cli.ux.styledJSON(obj);
305 }
306 return this;
307 }
308 /**
309 * Logs at `INFO` level and conditionally writes to `stdout` in a styled header format if
310 * stream output is enabled.
311 *
312 * @param {string} header The header to be styled.
313 * @returns {UX}
314 */
315 styledHeader(header) {
316 this.logger.info(header);
317 if (this.isOutputEnabled) {
318 this.cli.ux.styledHeader(header);
319 }
320 return this;
321 }
322}
323exports.UX = UX;
324/**
325 * Collection of warnings that can be accessed and manipulated later.
326 *
327 * @type {Set<string>}
328 */
329UX.warnings = new Set();
330//# sourceMappingURL=ux.js.map
\No newline at end of file