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 | // @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 | }
|
327 | exports.UX = UX;
|
328 | /**
|
329 | * Collection of warnings that can be accessed and manipulated later.
|
330 | *
|
331 | * @type {Set<string>}
|
332 | */
|
333 | UX.warnings = new Set();
|
334 | //# sourceMappingURL=ux.js.map |
\ | No newline at end of file |