UNPKG

11.6 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 // @ts-ignore
71 const type = (0, ts_types_1.ensure)(def.type);
72 // @ts-ignore
73 const name = (0, ts_types_1.ensure)(def.name);
74 // @ts-ignore
75 // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
76 msg = `The ${type} "${name}" has been deprecated and will be removed in v${version + 1}.0 or later.`;
77 }
78 else {
79 msg = def.messageOverride;
80 }
81 if (def.to) {
82 msg += ` Use "${def.to}" instead.`;
83 }
84 if (def.message) {
85 msg += ` ${def.message}`;
86 }
87 return msg;
88 }
89 /**
90 * Create a `UX` instance.
91 *
92 * @returns {Promise<UX>} A `Promise` of the created `UX` instance.
93 */
94 static async create() {
95 return new UX(await core_1.Logger.child('UX'));
96 }
97 /**
98 * Logs at `INFO` level and conditionally writes to `stdout` if stream output is enabled.
99 *
100 * @param {...any[]} args The messages or objects to log.
101 * @returns {UX}
102 */
103 log(...args) {
104 if (this.isOutputEnabled) {
105 this.cli.ux.log(...args);
106 }
107 // log to sfdx.log after the console as log filtering mutates the args.
108 this.logger.info(...args);
109 return this;
110 }
111 /**
112 * Log JSON to stdout and to the log file with log level info.
113 *
114 * @param {object} obj The object to log -- must be serializable as JSON.
115 * @returns {UX}
116 * @throws {TypeError} If the object is not JSON-serializable.
117 */
118 logJson(obj) {
119 this.cli.ux.styledJSON(obj);
120 // log to sfdx.log after the console as log filtering mutates the args.
121 this.logger.info(obj);
122 return this;
123 }
124 /**
125 * Prompt the user for input.
126 *
127 * @param {string} name The string that the user sees when prompted for information.
128 * @param {IPromptOptions} options A prompt option configuration.
129 * @returns {Promise<string>} The user input to the prompt.
130 */
131 async prompt(name, options = {}) {
132 return this.cli.ux.prompt(name, options);
133 }
134 /**
135 * Prompt the user for confirmation.
136 *
137 * @param {string} message The message displayed to the user.
138 * @returns {Promise<boolean>} Returns `true` if the user inputs 'y' or 'yes', and `false` if the user inputs 'n' or 'no'.
139 */
140 async confirm(message) {
141 return this.cli.ux.confirm(message);
142 }
143 /**
144 * Start a spinner action after displaying the given message.
145 *
146 * @param {string} message The message displayed to the user.
147 * @param {string} status The status displayed to the user.
148 * @param {OclifActionOptions} opts The options to select whereas spinner will output to stderr or stdout.
149 */
150 startSpinner(message, status, opts = {}) {
151 if (this.isOutputEnabled) {
152 this.cli.ux.action.start(message, status, opts);
153 }
154 }
155 /**
156 * Pause the spinner and call the given function.
157 *
158 * @param {function} fn The function to be called in the pause.
159 * @param {string} icon The string displayed to the user.
160 * @returns {T} The result returned by the passed in function.
161 */
162 pauseSpinner(fn, icon) {
163 if (this.isOutputEnabled) {
164 return this.cli.ux.action.pause(fn, icon);
165 }
166 }
167 /**
168 * Update the spinner status.
169 *
170 * @param {string} status The message displayed to the user.
171 */
172 setSpinnerStatus(status) {
173 if (this.isOutputEnabled) {
174 this.cli.ux.action.status = status;
175 }
176 }
177 /**
178 * Get the spinner status.
179 *
180 * @returns {Optional<string>}
181 */
182 getSpinnerStatus() {
183 if (this.isOutputEnabled) {
184 return this.cli.ux.action.status;
185 }
186 }
187 /**
188 * Stop the spinner action.
189 *
190 * @param {string} message The message displayed to the user.
191 */
192 stopSpinner(message) {
193 if (this.isOutputEnabled) {
194 this.cli.ux.action.stop(message);
195 }
196 }
197 /**
198 * Logs a warning as `WARN` level and conditionally writes to `stderr` if the log
199 * level is `WARN` or above and stream output is enabled. The message is added
200 * to the static {@link UX.warnings} set if stream output is _not_ enabled, for later
201 * consumption and manipulation.
202 *
203 * @param {string} message The warning message to output.
204 * @returns {UX}
205 * @see UX.warnings
206 */
207 warn(message) {
208 const warning = chalk_1.default.yellow('WARNING:');
209 // Necessarily log to sfdx.log.
210 this.logger.warn(warning, message);
211 if (this.logger.shouldLog(core_1.LoggerLevel.WARN)) {
212 if (!this.isOutputEnabled) {
213 UX.warnings.add(message);
214 }
215 else {
216 console.warn(`${warning} ${message}`);
217 }
218 }
219 return this;
220 }
221 /**
222 * Logs an error at `ERROR` level and conditionally writes to `stderr` if stream
223 * output is enabled.
224 *
225 * @param {...any[]} args The errors to log.
226 * @returns {UX}
227 */
228 error(...args) {
229 if (this.isOutputEnabled) {
230 console.error(...args);
231 }
232 this.logger.error(...args);
233 return this;
234 }
235 /**
236 * Logs an object as JSON at `ERROR` level and to `stderr`.
237 *
238 * @param {object} obj The error object to log -- must be serializable as JSON.
239 * @returns {UX}
240 * @throws {TypeError} If the object is not JSON-serializable.
241 */
242 errorJson(obj) {
243 const err = JSON.stringify(obj, null, 4);
244 console.error(err);
245 this.logger.error(err);
246 return this;
247 }
248 /**
249 * Logs at `INFO` level and conditionally writes to `stdout` in a table format if
250 * stream output is enabled.
251 *
252 * @param {object[]} rows The rows of data to be output in table format.
253 * @param columns Table column options
254 * @param {SfdxTableOptions} options The {@link SfdxTableOptions} to use for formatting.
255 * @returns {UX}
256 */
257 table(
258 // (allow any because matches oclif)
259 // eslint-disable-next-line @typescript-eslint/no-explicit-any
260 rows, columns = {}, options = { 'no-truncate': true }) {
261 if (this.isOutputEnabled) {
262 // This is either an array of column names or an already built Partial<OclifTableOptions>
263 if ((0, ts_types_1.isArray)(columns)) {
264 const tableColumns = {};
265 for (const col of columns) {
266 tableColumns[col] = {
267 header: col
268 .split(/(?=[A-Z])|[-_\s]/)
269 .map((w) => w.toUpperCase())
270 .join(' '),
271 };
272 }
273 this.cli.ux.table(rows, tableColumns, options);
274 }
275 else {
276 this.cli.ux.table(rows, columns, options);
277 }
278 }
279 // Log after table output as log filtering mutates data.
280 this.logger.info(rows);
281 return this;
282 }
283 /**
284 * Logs at `INFO` level and conditionally writes to `stdout` in a styled object format if
285 * stream output is enabled.
286 *
287 * @param {object} obj The object to be styled for stdout.
288 * @param {string[]} [keys] The object keys to be written to stdout.
289 * @returns {UX}
290 */
291 styledObject(obj, keys) {
292 this.logger.info(obj);
293 if (this.isOutputEnabled) {
294 this.cli.ux.styledObject(obj, keys);
295 }
296 return this;
297 }
298 /**
299 * Log at `INFO` level and conditionally write to `stdout` in styled JSON format if
300 * stream output is enabled.
301 *
302 * @param {object} obj The object to be styled for stdout.
303 * @returns {UX}
304 */
305 styledJSON(obj) {
306 this.logger.info(obj);
307 if (this.isOutputEnabled) {
308 this.cli.ux.styledJSON(obj);
309 }
310 return this;
311 }
312 /**
313 * Logs at `INFO` level and conditionally writes to `stdout` in a styled header format if
314 * stream output is enabled.
315 *
316 * @param {string} header The header to be styled.
317 * @returns {UX}
318 */
319 styledHeader(header) {
320 this.logger.info(header);
321 if (this.isOutputEnabled) {
322 this.cli.ux.styledHeader(header);
323 }
324 return this;
325 }
326}
327exports.UX = UX;
328/**
329 * Collection of warnings that can be accessed and manipulated later.
330 *
331 * @type {Set<string>}
332 */
333UX.warnings = new Set();
334//# sourceMappingURL=ux.js.map
\No newline at end of file