UNPKG

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