UNPKG

@jayree/sfdx-plugin-org

Version:

A Salesforce CLI plugin containing commands to configure State and Country/Territory Picklists and other org settings.

518 lines 24.4 kB
/* * Copyright 2026, jayree * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { EOL } from 'node:os'; import { LISTR_DEFAULT_RENDERER_STYLE, ListrDefaultRendererLogLevels, ListrRendererError } from 'listr2'; import { ListrEventType, ListrTaskEventType, ListrTaskState } from 'listr2'; import { PRESET_TIMER } from 'listr2'; import { ListrLogLevels, ListrLogger, ProcessOutputBuffer, Spinner, assertFunctionOrSelf, cleanseAnsi, color, indent, } from 'listr2'; function startTimeSpan() { const start = process.hrtime.bigint(); return { getTimeSpan: () => Number(process.hrtime.bigint() - start) / 1_000_000_000 }; } export class MyDefaultRenderer { tasks; options; events; static nonTTY = false; static rendererOptions = { indentation: 2, clearOutput: false, showSubtasks: true, collapseSubtasks: true, collapseSkips: true, showSkipMessage: true, suffixSkips: false, collapseErrors: true, showErrorMessage: true, suffixRetries: true, lazy: false, removeEmptyLines: true, formatOutput: 'wrap', pausedTimer: { ...PRESET_TIMER, format: () => color.yellowBright, }, maxSubTasks: 10, hideAfterSeconds: 5, }; static rendererTaskOptions; bottom = new Map(); prompt; activePrompt; spinner; logger; updater; truncate; wrap; cache = { render: new Map(), rendererOptions: new Map(), rendererTaskOptions: new Map(), }; taskTime = {}; currentTasks = {}; hiddenTasks = {}; constructor(tasks, options, events) { this.tasks = tasks; this.options = options; this.events = events; this.options = { ...MyDefaultRenderer.rendererOptions, ...this.options, icon: { ...LISTR_DEFAULT_RENDERER_STYLE.icon, ...(options?.icon ?? {}), }, color: { ...LISTR_DEFAULT_RENDERER_STYLE.color, ...(options?.color ?? {}), }, }; this.spinner = this.options.spinner ?? new Spinner(); this.logger = this.options.logger ?? new ListrLogger({ useIcons: true, toStderr: [] }); if (this.logger.options) { this.logger.options.icon = this.options.icon; this.logger.options.color = this.options.color; } } isBottomBar(task) { const bottomBar = this.cache.rendererTaskOptions.get(task.id)?.bottomBar; return ((typeof bottomBar === 'number' && bottomBar !== 0) || (typeof bottomBar === 'boolean' && bottomBar !== false) || !task.hasTitle()); } async render() { const { createLogUpdate } = await import('log-update'); const { default: truncate } = await import('cli-truncate'); const { default: wrap } = await import('wrap-ansi'); this.updater = createLogUpdate(this.logger.process.stdout); this.truncate = truncate; this.wrap = wrap; // this.logger.process.hijack(); /* istanbul ignore if */ if (!this.options?.lazy) { this.spinner.start(() => { this.update(); }); } this.events.on(ListrEventType.SHOULD_REFRESH_RENDER, () => { this.update(); }); } update() { this.updater(this.create()); } end() { this.spinner.stop(); // clear log updater this.updater.clear(); this.updater.done(); // directly write to process.stdout, since logupdate only can update the seen height of terminal if (!this.options.clearOutput) { this.logger.process.toStdout(this.create({ prompt: false })); } this.logger.process.release(); } create(options) { options = { tasks: true, bottomBar: true, prompt: true, ...options, }; const render = []; const renderTasks = this.renderer(this.tasks); const renderBottomBar = this.renderBottomBar(); const renderPrompt = this.renderPrompt(); if (options.tasks && renderTasks.length > 0) { render.push(...renderTasks); } if (options.bottomBar && renderBottomBar.length > 0) { if (render.length > 0) { render.push(''); } render.push(...renderBottomBar); } if (options.prompt && renderPrompt.length > 0) { if (render.length > 0) { render.push(''); } render.push(...renderPrompt); } return render.join(EOL); } // eslint-disable-next-line complexity style(task, output = false) { const rendererOptions = this.cache.rendererOptions.get(task.id); if (task.isSkipped()) { if (output || rendererOptions?.collapseSkips) { return this.logger.icon(ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE); } else if (rendererOptions?.collapseSkips === false) { return this.logger.icon(ListrDefaultRendererLogLevels.SKIPPED_WITHOUT_COLLAPSE); } } if (output) { if (this.isBottomBar(task)) { return this.logger.icon(ListrDefaultRendererLogLevels.OUTPUT_WITH_BOTTOMBAR); } return this.logger.icon(ListrDefaultRendererLogLevels.OUTPUT); } if (task.hasSubtasks()) { if (task.isStarted() || (task.isPrompt() && rendererOptions?.showSubtasks !== false && !task.subtasks.every((subtask) => !subtask.hasTitle()))) { return this.logger.icon(ListrDefaultRendererLogLevels.PENDING); } else if (task.isCompleted() && task.subtasks.some((subtask) => subtask.hasFailed())) { return this.logger.icon(ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SUBTASKS); } else if (task.hasFailed()) { return this.logger.icon(ListrDefaultRendererLogLevels.FAILED_WITH_FAILED_SUBTASKS); } } if (task.isStarted() || task.isPrompt()) { return this.logger.icon(ListrDefaultRendererLogLevels.PENDING, !this.options?.lazy && this.spinner.fetch()); } else if (task.isCompleted()) { return this.logger.icon(ListrDefaultRendererLogLevels.COMPLETED); } else if (task.isRetrying()) { return this.logger.icon(ListrDefaultRendererLogLevels.RETRY, !this.options?.lazy && this.spinner.fetch()); } else if (task.isRollingBack()) { return this.logger.icon(ListrDefaultRendererLogLevels.ROLLING_BACK, !this.options?.lazy && this.spinner.fetch()); } else if (task.hasRolledBack()) { return this.logger.icon(ListrDefaultRendererLogLevels.ROLLED_BACK); } else if (task.hasFailed()) { return this.logger.icon(ListrDefaultRendererLogLevels.FAILED); } else if (task.isPaused()) { return this.logger.icon(ListrDefaultRendererLogLevels.PAUSED); } return this.logger.icon(ListrDefaultRendererLogLevels.WAITING); } format(message, icon, level) { // we dont like empty data around here if (message.trim() === '') { return []; } if (icon) { message = icon + ' ' + message; } let parsed; const columns = (process.stdout.columns ?? 80) - level * this.options.indentation - 2; switch (this.options.formatOutput) { case 'truncate': parsed = message.split(EOL).map((s, i) => { return this.truncate(this.indent(s, i), columns); }); break; case 'wrap': parsed = this.wrap(message, columns, { hard: true }) .split(EOL) .map((s, i) => this.indent(s, i)); break; default: throw new ListrRendererError('Format option for the renderer is wrong.'); } // this removes the empty lines if (this.options.removeEmptyLines) { parsed = parsed.filter(Boolean); } return parsed.map((str) => indent(str, level * this.options.indentation)); } renderer(tasks, id = 'root', level = 0) { const preOutput = []; const postOutput = []; if (!this.taskTime[id]) { this.taskTime[id] = {}; } if (!this.hiddenTasks[id] || this.currentTasks[id].length > this.options.maxSubTasks) { this.hiddenTasks[id] = tasks.filter((t) => level > 0 && typeof this.taskTime[id][t.id] !== 'undefined' && this.taskTime[id][t.id].getTimeSpan() > this.options.hideAfterSeconds); } if (!this.currentTasks[id] || this.currentTasks[id].length > this.options.maxSubTasks) { this.currentTasks[id] = tasks.filter((t) => level > 0 && (typeof this.taskTime[id][t.id] === 'undefined' || this.taskTime[id][t.id].getTimeSpan() <= this.options.hideAfterSeconds)); } if (this.hiddenTasks[id].length > 0 && tasks.filter((t) => t.isPending()).length !== 0) { const completed = this.hiddenTasks[id].filter((t) => t.isCompleted()); if (completed.length > 0) { preOutput.push(...this.format(`... completed (${completed.length})`, this.logger.icon(ListrDefaultRendererLogLevels.COMPLETED), level)); } const failed = this.hiddenTasks[id].filter((t) => t.hasFailed()); if (failed.length > 0) { preOutput.push(...this.format(`... failed (${failed.length})`, this.logger.icon(ListrDefaultRendererLogLevels.FAILED), level)); } const skipped = this.hiddenTasks[id].filter((t) => t.isSkipped()); if (skipped.length > 0) { preOutput.push(...this.format(`... skipped (${skipped.length})`, this.logger.icon(ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE), level)); } } if (level > 0 && this.currentTasks[id].length - this.options.maxSubTasks > 0 && tasks.filter((t) => t.isPending()).length !== 0) { postOutput.push(...this.format(`... waiting (${this.currentTasks[id].length - this.options.maxSubTasks})`, this.logger.icon(ListrDefaultRendererLogLevels.WAITING), level)); } return [ ...preOutput, // eslint-disable-next-line complexity ...tasks.flatMap((task) => { const output = []; const idx = this.currentTasks[id].findIndex((x) => x.title === task.title); if ((idx >= 0 && idx <= this.options.maxSubTasks - 1) || level === 0 || tasks.filter((t) => t.isPending() || typeof t.state === 'undefined').length === 0) { if (!task.isEnabled()) { return []; } // if this is already cached return the cache if (this.cache.render.has(task.id)) { return this.cache.render.get(task.id); } this.calculate(task); const rendererOptions = this.cache.rendererOptions.get(task.id); const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id); if (task.isPrompt()) { if (this.activePrompt && this.activePrompt !== task.id) { throw new ListrRendererError('Only one prompt can be active at the given time, please re-evaluate your task design.'); } else if (!this.activePrompt) { task.on(ListrTaskEventType.PROMPT, (prompt) => { const cleansed = cleanseAnsi(prompt); if (cleansed) { this.prompt = cleansed; } }); task.on(ListrTaskEventType.STATE, (state) => { if (state === ListrTaskState.PROMPT_COMPLETED || task.hasFinalized() || task.hasReset()) { this.prompt = null; this.activePrompt = null; task.off(ListrTaskEventType.PROMPT); } }); this.activePrompt = task.id; } } // Current Task Title if (task.hasTitle()) { if (!(tasks.some( // eslint-disable-next-line @typescript-eslint/no-shadow (task) => task.hasFailed()) && !task.hasFailed() && task.options.exitOnError !== false && !(task.isCompleted() || task.isSkipped()))) { // if task is skipped if (task.hasFailed() && rendererOptions?.collapseErrors) { // current task title and skip change the title output.push(...this.format(!task.hasSubtasks() && task.message.error && rendererOptions?.showErrorMessage ? task.message.error : task.title, this.style(task), level)); } else if (task.isSkipped() && rendererOptions?.collapseSkips) { // current task title and skip change the title output.push(...this.format(this.logger.suffix(task.message.skip && rendererOptions.showSkipMessage ? task.message.skip : task.title, { field: ListrLogLevels.SKIPPED, condition: rendererOptions.suffixSkips, format: () => color.dim, }), this.style(task), level)); } else if (task.isRetrying()) { output.push(...this.format(this.logger.suffix(task.title, { field: `${ListrLogLevels.RETRY}:${task.message.retry?.count}`, format: () => color.yellow, condition: rendererOptions?.suffixRetries, }), this.style(task), level)); } else if (task.isCompleted() && task.hasTitle() && assertFunctionOrSelf(rendererTaskOptions?.timer?.condition, task.message.duration)) { // task with timer output.push(...this.format(this.logger.suffix(task?.title, { ...rendererTaskOptions?.timer, args: [task.message.duration], }), this.style(task), level)); } else if (task.isPaused()) { output.push(...this.format(this.logger.suffix(task.title, { ...rendererOptions?.pausedTimer, args: [task.message.paused - Date.now()], }), this.style(task), level)); } else { // normal state output.push(...this.format(task.title, this.style(task), level)); } } else { // some sibling task but self has failed and this has stopped output.push(...this.format(task.title, this.logger.icon(ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SISTER_TASKS), level)); } } // task should not have subtasks since subtasks will handle the error already // maybe it is a better idea to show the error or skip messages when show subtasks is disabled. if (!task.hasSubtasks() || !rendererOptions?.showSubtasks) { // without the collapse option for skip and errors if (task.hasFailed() && rendererOptions?.collapseErrors === false && (rendererOptions?.showErrorMessage ?? !rendererOptions?.showSubtasks)) { // show skip data if collapsing is not defined output.push(...this.dump(task, level, ListrLogLevels.FAILED)); } else if (task.isSkipped() && rendererOptions?.collapseSkips === false && (rendererOptions?.showSkipMessage ?? !rendererOptions?.showSubtasks)) { // show skip data if collapsing is not defined output.push(...this.dump(task, level, ListrLogLevels.SKIPPED)); } } // Current Task Output if (task?.output) { if (this.isBottomBar(task)) { // create new if there is no persistent storage created for bottom bar if (!this.bottom.has(task.id)) { this.bottom.set(task.id, new ProcessOutputBuffer({ limit: typeof rendererTaskOptions?.bottomBar === 'boolean' ? 1 : rendererTaskOptions?.bottomBar, })); // eslint-disable-next-line @typescript-eslint/no-shadow task.on(ListrTaskEventType.OUTPUT, (output) => { const data = this.dump(task, -1, ListrLogLevels.OUTPUT, output); this.bottom.get(task.id)?.write(data.join(EOL)); }); } } else if (task.isPending() || rendererTaskOptions?.persistentOutput) { // keep output if persistent output is set output.push(...this.dump(task, level)); } } // render subtasks, some complicated conditionals going on if ( // check if renderer option is on first rendererOptions?.showSubtasks !== false && // if it doesnt have subtasks no need to check task.hasSubtasks() && (task.isPending() || (task.hasFinalized() && !task.hasTitle()) || // have to be completed and have subtasks (task.isCompleted() && rendererOptions?.collapseSubtasks === false && !task.subtasks.some((subtask) => subtask.rendererOptions.collapseSubtasks === true)) || // if any of the subtasks have the collapse option of task.subtasks.some((subtask) => subtask.rendererOptions.collapseSubtasks === false) || // if any of the subtasks has failed task.subtasks.some((subtask) => subtask.hasFailed()) || // if any of the subtasks rolled back task.subtasks.some((subtask) => subtask.hasRolledBack()))) { // set level const subtaskLevel = !task.hasTitle() ? level : level + 1; // render the subtasks as in the same way const subtaskRender = this.renderer(task.subtasks, task.id, subtaskLevel); output.push(...subtaskRender); } // after task is finished actions if (task.hasFinalized()) { if (!this.taskTime[id][task.id]) { this.taskTime[id][task.id] = startTimeSpan(); } // clean up bottom bar items if not indicated otherwise if (!rendererTaskOptions?.persistentOutput) { this.bottom.delete(task.id); } } } if (task.isClosed()) { this.cache.render.set(task.id, output); this.reset(task); } return output; }), ...postOutput, ]; } renderBottomBar() { // parse through all objects return only the last mentioned items if (this.bottom.size === 0) { return []; } return Array.from(this.bottom.values()) .flatMap((output) => output.all) .sort((a, b) => a.time - b.time) .map((output) => output.entry); } renderPrompt() { if (!this.prompt) { return []; } return [this.prompt]; } calculate(task) { if (this.cache.rendererOptions.has(task.id) && this.cache.rendererTaskOptions.has(task.id)) { return; } const rendererOptions = { ...this.options, ...task.rendererOptions, }; this.cache.rendererOptions.set(task.id, rendererOptions); this.cache.rendererTaskOptions.set(task.id, { ...MyDefaultRenderer.rendererTaskOptions, timer: rendererOptions.timer, ...task.rendererTaskOptions, }); } reset(task) { this.cache.rendererOptions.delete(task.id); this.cache.rendererTaskOptions.delete(task.id); } dump(task, level, source = ListrLogLevels.OUTPUT, data) { if (!data) { switch (source) { case ListrLogLevels.OUTPUT: data = task.output; break; case ListrLogLevels.SKIPPED: data = task.message.skip; break; case ListrLogLevels.FAILED: data = task.message.error; break; } } // dont return anything on some occasions if ((task.hasTitle() && source === ListrLogLevels.FAILED && data === task.title) || typeof data !== 'string') { return []; } if (source === ListrLogLevels.OUTPUT) { data = cleanseAnsi(data); } return this.format(data, this.style(task, true), level + 1); } indent(str, i) { return i > 0 ? indent(str.trim(), this.options.indentation) : str.trim(); } } //# sourceMappingURL=renderer.js.map