1 | ;
|
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 | */
|
8 | Object.defineProperty(exports, "__esModule", { value: true });
|
9 | exports.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 */
|
13 | const kit_1 = require("@salesforce/kit");
|
14 | const 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 | */
|
38 | const core_1 = require("@salesforce/core");
|
39 | const chalk_1 = require("chalk");
|
40 | const core_2 = require("@oclif/core");
|
41 | /**
|
42 | * Utilities for interacting with terminal I/O.
|
43 | */
|
44 | class 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 | }
|
323 | exports.UX = UX;
|
324 | /**
|
325 | * Collection of warnings that can be accessed and manipulated later.
|
326 | *
|
327 | * @type {Set<string>}
|
328 | */
|
329 | UX.warnings = new Set();
|
330 | //# sourceMappingURL=ux.js.map |
\ | No newline at end of file |