UNPKG

listr2

Version:

Terminal task list reborn! Create beautiful CLI interfaces via easy and logical to implement task lists that feel alive and interactive.

1,462 lines (1,422 loc) 70.3 kB
import EventEmitter from "eventemitter3"; import { inspect, styleText } from "node:util"; import { format } from "util"; import { EOL } from "os"; import { StringDecoder } from "string_decoder"; import { Writable } from "stream"; import rfdc from "rfdc"; import { randomUUID } from "crypto"; //#region src/constants/ansi-escape-codes.constants.ts /** * Indicates an UNICODE characters is coming up. */ const ANSI_ESCAPE = "\x1B["; /** * Generic ANSI escape characters for terminal based operations. */ const ANSI_ESCAPE_CODES = { CURSOR_HIDE: ANSI_ESCAPE + "?25l", CURSOR_SHOW: ANSI_ESCAPE + "?25h" }; //#endregion //#region src/constants/environment-variables.constants.ts /** * Environment variables for Listr. */ let ListrEnvironmentVariables = /* @__PURE__ */ function(ListrEnvironmentVariables) { ListrEnvironmentVariables["FORCE_UNICODE"] = "LISTR_FORCE_UNICODE"; ListrEnvironmentVariables["FORCE_TTY"] = "LISTR_FORCE_TTY"; ListrEnvironmentVariables["DISABLE_COLOR"] = "NO_COLOR"; ListrEnvironmentVariables["FORCE_COLOR"] = "FORCE_COLOR"; return ListrEnvironmentVariables; }({}); //#endregion //#region src/constants/listr-error.constants.ts /** * The actual error type that is collected and to help identify where the error is triggered from. */ let ListrErrorTypes = /* @__PURE__ */ function(ListrErrorTypes) { /** Task has failed and will try to retry. */ ListrErrorTypes["WILL_RETRY"] = "WILL_RETRY"; /** Task has failed and will try to rollback. */ ListrErrorTypes["WILL_ROLLBACK"] = "WILL_ROLLBACK"; /** Task has failed, ran the rollback action but the rollback action itself has failed. */ ListrErrorTypes["HAS_FAILED_TO_ROLLBACK"] = "HAS_FAILED_TO_ROLLBACK"; /** Task has failed. */ ListrErrorTypes["HAS_FAILED"] = "HAS_FAILED"; /** Task has failed, but exitOnError is set to false, so will ignore this error. */ ListrErrorTypes["HAS_FAILED_WITHOUT_ERROR"] = "HAS_FAILED_WITHOUT_ERROR"; return ListrErrorTypes; }({}); //#endregion //#region src/constants/listr-events.constants.ts /** * Events that are triggered by Listr. * * These are stateful and singleton events by being attached to the main Listr class and propagating to the subtasks. * * @see {@link https://listr2.kilic.dev/listr/events.html} */ let ListrEventType = /* @__PURE__ */ function(ListrEventType) { /** Indicates that underlying renderer should refresh the current render. */ ListrEventType["SHOULD_REFRESH_RENDER"] = "SHOUD_REFRESH_RENDER"; return ListrEventType; }({}); //#endregion //#region src/constants/listr-renderer.constants.ts let ListrRendererSelection = /* @__PURE__ */ function(ListrRendererSelection) { ListrRendererSelection["PRIMARY"] = "PRIMARY"; ListrRendererSelection["SECONDARY"] = "SECONDARY"; ListrRendererSelection["SILENT"] = "SILENT"; return ListrRendererSelection; }({}); //#endregion //#region src/constants/listr-task-events.constants.ts /** * Internal events that are fired from the Task. * * @see {@link https://listr2.kilic.dev/task/events.html} */ let ListrTaskEventType = /* @__PURE__ */ function(ListrTaskEventType) { /** Title has changed for the current Task. */ ListrTaskEventType["TITLE"] = "TITLE"; /** * State has changed for the current Task. * * @see {@link module:listr2.ListrTaskState} */ ListrTaskEventType["STATE"] = "STATE"; /** The current Task has been marked as enabled. */ ListrTaskEventType["ENABLED"] = "ENABLED"; /** The current Task is currently processing subtasks. */ ListrTaskEventType["SUBTASK"] = "SUBTASK"; /** The current Task is now processing a prompt. */ ListrTaskEventType["PROMPT"] = "PROMPT"; /** The current Task is now dumping output. */ ListrTaskEventType["OUTPUT"] = "OUTPUT"; /** * The current Task is now dumping a message. * * @see {module:Listr2.ListrTaskMessage} */ ListrTaskEventType["MESSAGE"] = "MESSAGE"; /** The current Task is closed and no further action in expected. */ ListrTaskEventType["CLOSED"] = "CLOSED"; return ListrTaskEventType; }({}); //#endregion //#region src/constants/listr-task-state.constants.ts /** * Tasks can be in various states during the execution. * * Whenever a state change occurs, the task will emit a {@link module:listr2.ListrTaskEventType.STATE} with the appropriate state. */ let ListrTaskState = /* @__PURE__ */ function(ListrTaskState) { /** Task has not started yet, waiting for pick-up. */ ListrTaskState["WAITING"] = "WAITING"; /** Task has started. */ ListrTaskState["STARTED"] = "STARTED"; /** Task has been completed. */ ListrTaskState["COMPLETED"] = "COMPLETED"; /** Task has failed. */ ListrTaskState["FAILED"] = "FAILED"; /** Task has been skipped. */ ListrTaskState["SKIPPED"] = "SKIPPED"; /** Task is currently trying to rollback. */ ListrTaskState["ROLLING_BACK"] = "ROLLING_BACK"; /** Task has rolledback successfully after failing. */ ListrTaskState["ROLLED_BACK"] = "ROLLED_BACK"; /** Task is currently retrying. */ ListrTaskState["RETRY"] = "RETRY"; /** Task is currently paused. */ ListrTaskState["PAUSED"] = "PAUSED"; /** Task is currently trying to process a prompt. */ ListrTaskState["PROMPT"] = "PROMPT"; /** Task has successfully processed the prompt. */ ListrTaskState["PROMPT_COMPLETED"] = "PROMPT_COMPLETED"; /** Task has failed to process the prompt. */ ListrTaskState["PROMPT_FAILED"] = "PROMPT_FAILED"; return ListrTaskState; }({}); //#endregion //#region src/lib/event-manager.ts var EventManager = class { emitter = new EventEmitter(); emit(dispatch, args) { this.emitter.emit(dispatch, args); } on(dispatch, handler) { this.emitter.addListener(dispatch, handler); } once(dispatch, handler) { this.emitter.once(dispatch, handler); } off(dispatch, handler) { this.emitter.off(dispatch, handler); } complete() { this.emitter.removeAllListeners(); } }; //#endregion //#region src/interfaces/event.interface.ts /** * Give event map a set of indexes to not make it go crazy when some events are missing from it. * They are optional after all. */ var BaseEventMap = class {}; //#endregion //#region src/utils/environment/is-observable.ts /** * Tests to see if the object is an RxJS {@link Observable} * @param obj the object to test */ function isObservable(obj) { return !!obj && typeof obj === "object" && typeof obj.subscribe === "function"; } //#endregion //#region src/utils/environment/is-readable.ts /** * Tests to see if the object is an Readable or NodeJS.ReadableStream {@link Readable, NodeJS.ReadableStream} * @param obj the object to test */ function isReadable(obj) { return !!obj && typeof obj === "object" && obj.readable === true && typeof obj.read === "function" && typeof obj.on === "function"; } //#endregion //#region src/utils/environment/is-unicode-supported.ts function isUnicodeSupported() { /* istanbul ignore next */ return !!process.env[ListrEnvironmentVariables.FORCE_UNICODE] || process.platform !== "win32" || !!process.env.CI || !!process.env.WT_SESSION || process.env.TERM_PROGRAM === "vscode" || process.env.TERM === "xterm-256color" || process.env.TERM === "alacritty"; } //#endregion //#region src/utils/format/cleanse-ansi.constants.ts const CLEAR_LINE_REGEX = "(?:\\u001b|\\u009b)\\[[\\=><~/#&.:=?%@~_-]*[0-9]*[\\a-ln-tqyz=><~/#&.:=?%@~_-]+"; const BELL_REGEX = /\u0007/; //#endregion //#region src/utils/format/cleanse-ansi.ts function cleanseAnsi(chunk) { return String(chunk).replace(new RegExp(CLEAR_LINE_REGEX, "gmi"), "").replace(new RegExp(BELL_REGEX, "gmi"), "").trim(); } //#endregion //#region src/utils/format/color.ts /** * Color palette. */ const color = Object.fromEntries(Object.keys(inspect.colors).map((color) => [color, (text) => styleText(color, String(text))])); //#endregion //#region src/utils/format/indent.ts function indent(string, count) { return string.replace(/^(?!\s*$)/gm, " ".repeat(count)); } //#endregion //#region src/utils/format/figures.ts const FIGURES_MAIN = { warning: "⚠", cross: "✖", arrowDown: "↓", tick: "✔", arrowRight: "→", pointer: "❯", checkboxOn: "☒", arrowLeft: "←", squareSmallFilled: "◼", pointerSmall: "›" }; const FIGURES_FALLBACK = { ...FIGURES_MAIN, warning: "‼", cross: "×", tick: "√", pointer: ">", checkboxOn: "[×]", squareSmallFilled: "■" }; const figures = isUnicodeSupported() ? FIGURES_MAIN : FIGURES_FALLBACK; //#endregion //#region src/utils/format/splat.ts function splat(message, ...splat) { return format(String(message), ...splat); } //#endregion //#region src/utils/logger/logger.constants.ts /** Default ListrLogLevels for the logger */ let ListrLogLevels = /* @__PURE__ */ function(ListrLogLevels) { ListrLogLevels["STARTED"] = "STARTED"; ListrLogLevels["COMPLETED"] = "COMPLETED"; ListrLogLevels["FAILED"] = "FAILED"; ListrLogLevels["SKIPPED"] = "SKIPPED"; ListrLogLevels["OUTPUT"] = "OUTPUT"; ListrLogLevels["TITLE"] = "TITLE"; ListrLogLevels["ROLLBACK"] = "ROLLBACK"; ListrLogLevels["RETRY"] = "RETRY"; ListrLogLevels["PROMPT"] = "PROMPT"; ListrLogLevels["PAUSED"] = "PAUSED"; return ListrLogLevels; }({}); const LISTR_LOGGER_STYLE = { icon: { [ListrLogLevels.STARTED]: figures.pointer, [ListrLogLevels.FAILED]: figures.cross, [ListrLogLevels.SKIPPED]: figures.arrowDown, [ListrLogLevels.COMPLETED]: figures.tick, [ListrLogLevels.OUTPUT]: figures.pointerSmall, [ListrLogLevels.TITLE]: figures.arrowRight, [ListrLogLevels.RETRY]: figures.warning, [ListrLogLevels.ROLLBACK]: figures.arrowLeft, [ListrLogLevels.PAUSED]: figures.squareSmallFilled }, color: { [ListrLogLevels.STARTED]: color.yellow, [ListrLogLevels.FAILED]: color.red, [ListrLogLevels.SKIPPED]: color.yellow, [ListrLogLevels.COMPLETED]: color.green, [ListrLogLevels.RETRY]: color.yellowBright, [ListrLogLevels.ROLLBACK]: color.redBright, [ListrLogLevels.PAUSED]: color.yellowBright } }; const LISTR_LOGGER_STDERR_LEVELS = [ ListrLogLevels.RETRY, ListrLogLevels.ROLLBACK, ListrLogLevels.FAILED ]; //#endregion //#region src/utils/logger/logger.ts /** * Creates a new Listr2 logger. * * This logger is used throughout the renderers for consistency. * * @see {@link https://listr2.kilic.dev/renderer/logger.html} */ var ListrLogger = class { process; constructor(options) { this.options = options; this.options = { useIcons: true, toStderr: [], ...options ?? {} }; this.options.fields ??= {}; this.options.fields.prefix ??= []; this.options.fields.suffix ??= []; this.process = this.options.processOutput ?? new ProcessOutput(); } log(level, message, options) { const output = this.format(level, message, options); if (this.options.toStderr.includes(level)) { this.process.toStderr(output); return; } this.process.toStdout(output); } toStdout(message, options, eol = true) { this.process.toStdout(this.format(null, message, options), eol); } toStderr(message, options, eol = true) { this.process.toStderr(this.format(null, message, options), eol); } wrap(message, options) { if (!message) return message; return this.applyFormat(`[${message}]`, options); } splat(...args) { const message = args.shift() ?? ""; return args.length === 0 ? message : splat(message, args); } suffix(message, ...suffixes) { suffixes.filter(Boolean).forEach((suffix) => { message += this.spacing(message); if (typeof suffix === "string") message += this.wrap(suffix); else if (typeof suffix === "object") { suffix.args ??= []; if (typeof suffix.condition === "function" ? !suffix.condition(...suffix.args) : !(suffix.condition ?? true)) return message; message += this.wrap(typeof suffix.field === "function" ? suffix.field(...suffix.args) : suffix.field, { format: suffix?.format(...suffix.args) }); } }); return message; } prefix(message, ...prefixes) { prefixes.filter(Boolean).forEach((prefix) => { message = this.spacing(message) + message; if (typeof prefix === "string") message = this.wrap(prefix) + message; else if (typeof prefix === "object") { prefix.args ??= []; if (typeof prefix.condition === "function" ? !prefix.condition(...prefix.args) : !(prefix.condition ?? true)) return message; message = this.wrap(typeof prefix.field === "function" ? prefix.field(...prefix.args) : prefix.field, { format: prefix?.format() }) + message; } }); return message; } fields(message, options) { if (this.options?.fields?.prefix) message = this.prefix(message, ...this.options.fields.prefix); if (options?.prefix) message = this.prefix(message, ...options.prefix); if (options?.suffix) message = this.suffix(message, ...options.suffix); if (this.options?.fields?.suffix) message = this.suffix(message, ...this.options.fields.suffix); return message; } icon(level, icon) { if (!level) return null; if (!icon) { const i = this.options.icon?.[level]; icon = typeof i === "function" ? i() : i; } const coloring = this.options.color?.[level]; if (icon && coloring) icon = coloring(icon); return icon; } format(level, message, options) { if (!Array.isArray(message)) message = [message]; message = this.splat(message.shift(), ...message).toString().split(EOL).filter((m) => !m || m.trim() !== "").map((m) => { return this.style(level, this.fields(m, { prefix: Array.isArray(options?.prefix) ? options.prefix : [options?.prefix], suffix: Array.isArray(options?.suffix) ? options.suffix : [options?.suffix] })); }).join(EOL); return message; } style(level, message) { if (!level || !message) return message; const icon = this.icon(level, !this.options.useIcons && this.wrap(level)); if (icon) message = icon + " " + message; return message; } applyFormat(message, options) { if (options?.format) return options.format(message); return message; } spacing(message) { return typeof message === "undefined" || message.trim() === "" ? "" : " "; } }; //#endregion //#region src/utils/process-output/process-output-buffer.ts var ProcessOutputBuffer = class { buffer = []; decoder = new StringDecoder(); constructor(options) { this.options = options; } get all() { return this.buffer; } get last() { return this.buffer.at(-1); } get length() { return this.buffer.length; } write(data, ...args) { const callback = args[args.length - 1]; this.buffer.push({ time: Date.now(), stream: this.options?.stream, entry: this.decoder.write(typeof data === "string" ? Buffer.from(data, typeof args[0] === "string" ? args[0] : void 0) : Buffer.from(data)) }); if (this.options?.limit) this.buffer = this.buffer.slice(-this.options.limit); if (typeof callback === "function") callback(); return true; } reset() { this.buffer = []; } }; //#endregion //#region src/utils/process-output/process-output-stream.ts var ProcessOutputStream = class { method; buffer; constructor(stream) { this.stream = stream; this.method = stream.write; this.buffer = new ProcessOutputBuffer({ stream }); } get out() { const self = this; return new Proxy(this.stream, { get(target, prop, receiver) { if (prop === "write") return self.write.bind(self); return Reflect.get(target, prop, receiver); } }); } hijack() { this.stream.write = this.buffer.write.bind(this.buffer); } release() { this.stream.write = this.method; const buffer = [...this.buffer.all]; this.buffer.reset(); return buffer; } write(...args) { return this.method.apply(this.stream, args); } }; //#endregion //#region src/utils/process-output/process-output.ts /** * Creates a new Listr2 process-output controller. * * This is used to control the flow to `process.stdout` and `process.stderr` for all renderers. * * @see {@link https://listr2.kilic.dev/renderer/process-output.html} */ var ProcessOutput = class { stream; active; constructor(stdout, stderr, options) { this.options = options; this.stream = { stdout: new ProcessOutputStream(stdout ?? process.stdout), stderr: new ProcessOutputStream(stderr ?? process.stderr) }; this.options = { dump: ["stdout", "stderr"], leaveEmptyLine: true, ...options }; } get stdout() { return this.stream.stdout.out; } get stderr() { return this.stream.stderr.out; } hijack() { if (this.active) throw new Error("ProcessOutput has been already hijacked!"); this.stream.stdout.write(ANSI_ESCAPE_CODES.CURSOR_HIDE); Object.values(this.stream).forEach((stream) => stream.hijack()); this.active = true; } release() { const output = Object.entries(this.stream).map(([name, stream]) => ({ name, buffer: stream.release() })).filter((output) => this.options.dump.includes(output.name)).flatMap((output) => output.buffer).sort((a, b) => a.time - b.time).map((message) => { return { ...message, entry: cleanseAnsi(message.entry) }; }).filter((message) => message.entry); if (output.length > 0) { if (this.options.leaveEmptyLine) this.stdout.write(EOL); output.forEach((message) => { (message.stream ?? this.stdout).write(message.entry + EOL); }); } this.stream.stdout.write(ANSI_ESCAPE_CODES.CURSOR_SHOW); this.active = false; } toStdout(buffer, eol = true) { if (eol) buffer = buffer + EOL; return this.stream.stdout.write(buffer); } toStderr(buffer, eol = true) { if (eol) buffer = buffer + EOL; return this.stream.stderr.write(buffer); } }; //#endregion //#region src/utils/process-output/writable.ts /* istanbul ignore next */ function createWritable(cb) { const writable = new Writable(); writable.rows = Infinity; writable.columns = Infinity; writable.write = (chunk) => { cb(chunk.toString()); return true; }; return writable; } //#endregion //#region src/utils/prompts/adapter.ts /* istanbul ignore next */ var ListrPromptAdapter = class { state; constructor(task, wrapper) { this.task = task; this.wrapper = wrapper; } reportStarted() { this.state = this.task.state; if (this.task.prompt) throw new PromptError("There is already an active prompt attached to this task which may not be cleaned up properly."); this.task.prompt = this; this.task.state$ = ListrTaskState.PROMPT; } reportFailed() { this.task.state$ = ListrTaskState.PROMPT_FAILED; this.restoreState(); } reportCompleted() { this.task.state$ = ListrTaskState.PROMPT_COMPLETED; this.restoreState(); } restoreState() { this.task.prompt = void 0; if (this.state) this.task.state = this.state; } }; //#endregion //#region src/utils/ui/spinner.ts /* istanbul ignore next */ var Spinner = class { spinner = !isUnicodeSupported() ? [ "-", "\\", "|", "/" ] : [ "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" ]; id; spinnerPosition = 0; spin() { this.spinnerPosition = ++this.spinnerPosition % this.spinner.length; } fetch() { return this.spinner[this.spinnerPosition]; } isRunning() { return !!this.id; } start(cb, interval = 100) { this.id = setInterval(() => { this.spin(); if (cb) cb(); }, interval); } stop() { clearInterval(this.id); this.id = void 0; } }; //#endregion //#region src/renderer/default/renderer.constants.ts let ListrDefaultRendererLogLevels = /* @__PURE__ */ function(ListrDefaultRendererLogLevels) { ListrDefaultRendererLogLevels["SKIPPED_WITH_COLLAPSE"] = "SKIPPED_WITH_COLLAPSE"; ListrDefaultRendererLogLevels["SKIPPED_WITHOUT_COLLAPSE"] = "SKIPPED_WITHOUT_COLLAPSE"; ListrDefaultRendererLogLevels["OUTPUT"] = "OUTPUT"; ListrDefaultRendererLogLevels["OUTPUT_WITH_BOTTOMBAR"] = "OUTPUT_WITH_BOTTOMBAR"; ListrDefaultRendererLogLevels["PENDING"] = "PENDING"; ListrDefaultRendererLogLevels["COMPLETED"] = "COMPLETED"; ListrDefaultRendererLogLevels["COMPLETED_WITH_FAILED_SUBTASKS"] = "COMPLETED_WITH_FAILED_SUBTASKS"; ListrDefaultRendererLogLevels["COMPLETED_WITH_FAILED_SISTER_TASKS"] = "COMPLETED_WITH_SISTER_TASKS_FAILED"; ListrDefaultRendererLogLevels["RETRY"] = "RETRY"; ListrDefaultRendererLogLevels["ROLLING_BACK"] = "ROLLING_BACK"; ListrDefaultRendererLogLevels["ROLLED_BACK"] = "ROLLED_BACK"; ListrDefaultRendererLogLevels["FAILED"] = "FAILED"; ListrDefaultRendererLogLevels["FAILED_WITH_FAILED_SUBTASKS"] = "FAILED_WITH_SUBTASKS"; ListrDefaultRendererLogLevels["WAITING"] = "WAITING"; ListrDefaultRendererLogLevels["PAUSED"] = "PAUSED"; return ListrDefaultRendererLogLevels; }({}); const LISTR_DEFAULT_RENDERER_STYLE = { icon: { [ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE]: figures.arrowDown, [ListrDefaultRendererLogLevels.SKIPPED_WITHOUT_COLLAPSE]: figures.warning, [ListrDefaultRendererLogLevels.OUTPUT]: figures.pointerSmall, [ListrDefaultRendererLogLevels.OUTPUT_WITH_BOTTOMBAR]: figures.pointerSmall, [ListrDefaultRendererLogLevels.PENDING]: figures.pointer, [ListrDefaultRendererLogLevels.COMPLETED]: figures.tick, [ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SUBTASKS]: figures.warning, [ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SISTER_TASKS]: figures.squareSmallFilled, [ListrDefaultRendererLogLevels.RETRY]: figures.warning, [ListrDefaultRendererLogLevels.ROLLING_BACK]: figures.warning, [ListrDefaultRendererLogLevels.ROLLED_BACK]: figures.arrowLeft, [ListrDefaultRendererLogLevels.FAILED]: figures.cross, [ListrDefaultRendererLogLevels.FAILED_WITH_FAILED_SUBTASKS]: figures.pointer, [ListrDefaultRendererLogLevels.WAITING]: figures.squareSmallFilled, [ListrDefaultRendererLogLevels.PAUSED]: figures.squareSmallFilled }, color: { [ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE]: color.yellow, [ListrDefaultRendererLogLevels.SKIPPED_WITHOUT_COLLAPSE]: color.yellow, [ListrDefaultRendererLogLevels.PENDING]: color.yellow, [ListrDefaultRendererLogLevels.COMPLETED]: color.green, [ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SUBTASKS]: color.yellow, [ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SISTER_TASKS]: color.red, [ListrDefaultRendererLogLevels.RETRY]: color.yellowBright, [ListrDefaultRendererLogLevels.ROLLING_BACK]: color.redBright, [ListrDefaultRendererLogLevels.ROLLED_BACK]: color.redBright, [ListrDefaultRendererLogLevels.FAILED]: color.red, [ListrDefaultRendererLogLevels.FAILED_WITH_FAILED_SUBTASKS]: color.red, [ListrDefaultRendererLogLevels.WAITING]: color.dim, [ListrDefaultRendererLogLevels.PAUSED]: color.yellowBright } }; //#endregion //#region src/presets/timer/parser.ts /** * A basic function to parse minutes and tasks passed given a duration. * Useful for renderers to show the task time. */ /* istanbul ignore next */ function parseTimer(duration) { const seconds = Math.floor(duration / 1e3); const minutes = Math.floor(seconds / 60); let parsedTime; if (seconds === 0 && minutes === 0) parsedTime = `0.${Math.floor(duration / 100)}s`; if (seconds > 0) parsedTime = `${seconds % 60}s`; if (minutes > 0) parsedTime = `${minutes}m${parsedTime}`; return parsedTime; } //#endregion //#region src/presets/timer/preset.ts /* istanbul ignore next */ const PRESET_TIMER = { condition: true, field: parseTimer, format: () => color.dim }; //#endregion //#region src/presets/timestamp/parser.ts /* istanbul ignore next */ function parseTimestamp() { const now = /* @__PURE__ */ new Date(); return String(now.getHours()).padStart(2, "0") + ":" + String(now.getMinutes()).padStart(2, "0") + ":" + String(now.getSeconds()).padStart(2, "0"); } //#endregion //#region src/presets/timestamp/preset.ts /* istanbul ignore next */ const PRESET_TIMESTAMP = { condition: true, field: parseTimestamp, format: () => color.dim }; //#endregion //#region src/renderer/default/renderer.ts var DefaultRenderer = class DefaultRenderer { 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 } }; static rendererTaskOptions = { outputBar: true }; prompt; activePrompt; spinner; logger; updater; truncate; wrap; buffer = { output: /* @__PURE__ */ new Map(), bottom: /* @__PURE__ */ new Map() }; cache = { render: /* @__PURE__ */ new Map(), rendererOptions: /* @__PURE__ */ new Map(), rendererTaskOptions: /* @__PURE__ */ new Map() }; constructor(tasks, options, events) { this.tasks = tasks; this.options = options; this.events = events; this.options = { ...DefaultRenderer.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: [] }); this.logger.options.icon = this.options.icon; this.logger.options.color = this.options.color; } 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(); this.updater.clear(); this.updater.done(); 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); } 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.shouldOutputToBottomBar(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) { 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, trim: false }).split(EOL).map((s, i) => this.indent(s, i)); break; default: throw new ListrRendererError("Format option for the renderer is wrong."); } if (this.options.removeEmptyLines) parsed = parsed.filter(Boolean); return parsed.map((str) => indent(str, level * this.options.indentation)); } shouldOutputToOutputBar(task) { const outputBar = this.cache.rendererTaskOptions.get(task.id).outputBar; return typeof outputBar === "number" && outputBar !== 0 || typeof outputBar === "boolean" && outputBar !== false; } shouldOutputToBottomBar(task) { const bottomBar = this.cache.rendererTaskOptions.get(task.id).bottomBar; return typeof bottomBar === "number" && bottomBar !== 0 || typeof bottomBar === "boolean" && bottomBar !== false || !task.hasTitle(); } renderer(tasks, level = 0) { return tasks.flatMap((task) => { if (!task.isEnabled()) return []; if (this.cache.render.has(task.id)) return this.cache.render.get(task.id); this.calculate(task); this.setupBuffer(task); const rendererOptions = this.cache.rendererOptions.get(task.id); const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id); const output = []; 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; } } if (task.hasTitle()) if (!(tasks.some((task) => task.hasFailed()) && !task.hasFailed() && task.options.exitOnError !== false && !(task.isCompleted() || task.isSkipped()))) if (task.hasFailed() && rendererOptions.collapseErrors) 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) 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)) 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 output.push(...this.format(task.title, this.style(task), level)); else output.push(...this.format(task.title, this.logger.icon(ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SISTER_TASKS), level)); if (!task.hasSubtasks() || !rendererOptions.showSubtasks) { if (task.hasFailed() && rendererOptions.collapseErrors === false && (rendererOptions.showErrorMessage || !rendererOptions.showSubtasks)) output.push(...this.dump(task, level, ListrLogLevels.FAILED)); else if (task.isSkipped() && rendererOptions.collapseSkips === false && (rendererOptions.showSkipMessage || !rendererOptions.showSubtasks)) output.push(...this.dump(task, level, ListrLogLevels.SKIPPED)); } if (task.isPending() || rendererTaskOptions.persistentOutput) output.push(...this.renderOutputBar(task, level)); if (rendererOptions.showSubtasks !== false && task.hasSubtasks() && (task.isPending() || task.hasFinalized() && !task.hasTitle() || task.isCompleted() && rendererOptions.collapseSubtasks === false && !task.subtasks.some((subtask) => this.cache.rendererOptions.get(subtask.id)?.collapseSubtasks === true) || task.subtasks.some((subtask) => this.cache.rendererOptions.get(subtask.id)?.collapseSubtasks === false) || task.subtasks.some((subtask) => subtask.hasFailed()) || task.subtasks.some((subtask) => subtask.hasRolledBack()))) { const subtaskLevel = !task.hasTitle() ? level : level + 1; const subtaskRender = this.renderer(task.subtasks, subtaskLevel); output.push(...subtaskRender); } if (task.hasFinalized()) { if (!rendererTaskOptions.persistentOutput) { this.buffer.bottom.delete(task.id); this.buffer.output.delete(task.id); } } if (task.isClosed()) { this.cache.render.set(task.id, output); this.reset(task); } return output; }); } renderOutputBar(task, level) { const output = this.buffer.output.get(task.id); if (!output) return []; return output.all.flatMap((o) => this.dump(task, level, ListrLogLevels.OUTPUT, o.entry)); } renderBottomBar() { if (this.buffer.bottom.size === 0) return []; return Array.from(this.buffer.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, { ...DefaultRenderer.rendererTaskOptions, timer: rendererOptions.timer, ...task.rendererTaskOptions }); } setupBuffer(task) { if (this.buffer.bottom.has(task.id) || this.buffer.output.has(task.id)) return; const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id); if (this.shouldOutputToBottomBar(task) && !this.buffer.bottom.has(task.id)) { this.buffer.bottom.set(task.id, new ProcessOutputBuffer({ limit: typeof rendererTaskOptions.bottomBar === "number" ? rendererTaskOptions.bottomBar : 1 })); task.on(ListrTaskEventType.OUTPUT, (output) => { const data = this.dump(task, -1, ListrLogLevels.OUTPUT, output); this.buffer.bottom.get(task.id).write(data.join(EOL)); }); task.on(ListrTaskEventType.STATE, (state) => { switch (state) { case ListrTaskState.RETRY || ListrTaskState.ROLLING_BACK: this.buffer.bottom.delete(task.id); break; } }); } else if (this.shouldOutputToOutputBar(task) && !this.buffer.output.has(task.id)) { this.buffer.output.set(task.id, new ProcessOutputBuffer({ limit: typeof rendererTaskOptions.outputBar === "number" ? rendererTaskOptions.outputBar : 1 })); task.on(ListrTaskEventType.OUTPUT, (output) => { this.buffer.output.get(task.id).write(output); }); task.on(ListrTaskEventType.STATE, (state) => { switch (state) { case ListrTaskState.RETRY || ListrTaskState.ROLLING_BACK: this.buffer.output.delete(task.id); break; } }); } } reset(task) { this.cache.rendererOptions.delete(task.id); this.cache.rendererTaskOptions.delete(task.id); this.buffer.output.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; } 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.trimEnd(), this.options.indentation) : str.trimEnd(); } }; //#endregion //#region src/renderer/silent/renderer.ts var SilentRenderer = class { static nonTTY = true; static rendererOptions; static rendererTaskOptions; constructor(tasks, options) { this.tasks = tasks; this.options = options; } render() {} end() {} }; //#endregion //#region src/renderer/simple/renderer.ts var SimpleRenderer = class SimpleRenderer { static nonTTY = true; static rendererOptions = { pausedTimer: { ...PRESET_TIMER, field: (time) => `${ListrLogLevels.PAUSED}:${time}`, format: () => color.yellowBright } }; static rendererTaskOptions = {}; logger; cache = { rendererOptions: /* @__PURE__ */ new Map(), rendererTaskOptions: /* @__PURE__ */ new Map() }; constructor(tasks, options) { this.tasks = tasks; this.options = options; this.options = { ...SimpleRenderer.rendererOptions, ...options, icon: { ...LISTR_LOGGER_STYLE.icon, ...options?.icon ?? {} }, color: { ...LISTR_LOGGER_STYLE.color, ...options?.color ?? {} } }; this.logger = this.options.logger ?? new ListrLogger({ useIcons: true, toStderr: LISTR_LOGGER_STDERR_LEVELS }); this.logger.options.icon = this.options.icon; this.logger.options.color = this.options.color; if (this.options.timestamp) this.logger.options.fields.prefix.unshift(this.options.timestamp); } end() {} render() { this.renderer(this.tasks); } renderer(tasks) { tasks.forEach((task) => { this.calculate(task); task.once(ListrTaskEventType.CLOSED, () => { this.reset(task); }); const rendererOptions = this.cache.rendererOptions.get(task.id); const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id); task.on(ListrTaskEventType.SUBTASK, (subtasks) => { this.renderer(subtasks); }); task.on(ListrTaskEventType.STATE, (state) => { if (!task.hasTitle()) return; if (state === ListrTaskState.STARTED) this.logger.log(ListrLogLevels.STARTED, task.title); else if (state === ListrTaskState.COMPLETED) { const timer = rendererTaskOptions?.timer; this.logger.log(ListrLogLevels.COMPLETED, task.title, timer && { suffix: { ...timer, condition: !!task.message?.duration && timer.condition, args: [task.message.duration] } }); } else if (state === ListrTaskState.PROMPT) { this.logger.process.hijack(); task.on(ListrTaskEventType.PROMPT, (prompt) => { this.logger.process.toStderr(prompt, false); }); } else if (state === ListrTaskState.PROMPT_COMPLETED) { task.off(ListrTaskEventType.PROMPT); this.logger.process.release(); } }); task.on(ListrTaskEventType.OUTPUT, (output) => { this.logger.log(ListrLogLevels.OUTPUT, output); }); task.on(ListrTaskEventType.MESSAGE, (message) => { if (message.error) this.logger.log(ListrLogLevels.FAILED, task.title, { suffix: { field: `${ListrLogLevels.FAILED}: ${message.error}`, format: () => color.red } }); else if (message.skip) this.logger.log(ListrLogLevels.SKIPPED, task.title, { suffix: { field: `${ListrLogLevels.SKIPPED}: ${message.skip}`, format: () => color.yellow } }); else if (message.rollback) this.logger.log(ListrLogLevels.ROLLBACK, task.title, { suffix: { field: `${ListrLogLevels.ROLLBACK}: ${message.rollback}`, format: () => color.red } }); else if (message.retry) this.logger.log(ListrLogLevels.RETRY, task.title, { suffix: { field: `${ListrLogLevels.RETRY}:${message.retry.count}`, format: () => color.red } }); else if (message.paused) { const timer = rendererOptions?.pausedTimer; this.logger.log(ListrLogLevels.PAUSED, task.title, timer && { suffix: { ...timer, condition: !!message?.paused && timer.condition, args: [message.paused - Date.now()] } }); } }); }); } 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, { ...SimpleRenderer.rendererTaskOptions, timer: rendererOptions.timer, ...task.rendererTaskOptions }); } reset(task) { this.cache.rendererOptions.delete(task.id); this.cache.rendererTaskOptions.delete(task.id); } }; //#endregion //#region src/renderer/test/serializer.ts var TestRendererSerializer = class { constructor(options) { this.options = options; } serialize(event, data, task) { return JSON.stringify(this.generate(event, data, task)); } generate(event, data, task) { const output = { event, data }; if (typeof this.options?.task !== "boolean") { const t = Object.fromEntries(this.options.task.map((entity) => { const property = task[entity]; if (typeof property === "function") return [entity, property.call(task)]; return [entity, property]; })); if (Object.keys(task).length > 0) output.task = t; } return output; } }; //#endregion //#region src/renderer/test/renderer.ts var TestRenderer = class TestRenderer { static nonTTY = true; static rendererOptions = { subtasks: true, state: Object.values(ListrTaskState), output: true, prompt: true, title: true, messages: [ "skip", "error", "retry", "rollback", "paused" ], messagesToStderr: [ "error", "rollback", "retry" ], task: [ "hasRolledBack", "isRollingBack", "isCompleted", "isSkipped", "hasFinalized", "hasSubtasks", "title", "hasReset", "hasTitle", "isPrompt", "isPaused", "isPending", "isSkipped", "isStarted", "hasFailed", "isEnabled", "isRetrying", "path" ] }; static rendererTaskOptions; logger; serializer; constructor(tasks, options) { this.tasks = tasks; this.options = options; this.options = { ...TestRenderer.rendererOptions, ...this.options }; this.logger = this.options.logger ?? new ListrLogger({ useIcons: false }); this.serializer = new TestRendererSerializer(this.options); } render() { this.renderer(this.tasks); } end() {} renderer(tasks) { tasks.forEach((task) => { if (this.options.subtasks) task.on(ListrTaskEventType.SUBTASK, (subtasks) => { this.renderer(subtasks); }); if (this.options.state) task.on(ListrTaskEventType.STATE, (state) => { this.logger.toStdout(this.serializer.serialize(ListrTaskEventType.STATE, state, task)); }); if (this.options.output) task.on(ListrTaskEventType.OUTPUT, (data) => { this.logger.toStdout(this.serializer.serialize(ListrTaskEventType.OUTPUT, data, task)); }); if (this.options.prompt) task.on(ListrTaskEventType.PROMPT, (prompt) => { this.logger.toStdout(this.serializer.serialize(ListrTaskEventType.PROMPT, prompt, task)); }); if (this.options.title) task.on(ListrTaskEventType.TITLE, (title) => { this.logger.toStdout(this.serializer.serialize(ListrTaskEventType.TITLE, title, task)); }); task.on(ListrTaskEventType.MESSAGE, (message) => { const parsed = Object.fromEntries(Object.entries(message).map(([key, value]) => { if (this.options.messages.includes(key)) return [key, value]; }).filter(Boolean)); if (Object.keys(parsed).length > 0) { const output = this.serializer.serialize(ListrTaskEventType.MESSAGE, parsed, task); if (this.options.messagesToStderr.some((state) => Object.keys(parsed).includes(state))) this.logger.toStderr(output); else this.logger.toStdout(output); } }); }); } }; //#endregion //#region src/renderer/verbose/renderer.ts var VerboseRenderer = class VerboseRenderer { static nonTTY = true; static rendererOptions = { logTitleChange: false, pausedTimer: { ...PRESET_TIMER, format: () => color.yellowBright } }; static rendererTaskOptions; logger; cache = { rendererOptions: /* @__PURE__ */ new Map(), rendererTaskOptions: /* @__PURE__ */ new Map() }; constructor(tasks, options) { this.tasks = tasks; this.options = options; this.options = { ...VerboseRenderer.rendererOptions, ...this.options, icon: { ...LISTR_LOGGER_STYLE.icon, ...options?.icon ?? {} }, color: { ...LISTR_LOGGER_STYLE.color, ...options?.color ?? {} } }; this.logger = this.options.logger ?? new ListrLogger({ useIcons: false, toStderr: LISTR_LOGGER_STDERR_LEVELS }); this.logger.options.icon = this.options.icon; this.logger.options.color = this.options.color; if (this.options.timestamp) this.logger.options.fields.prefix.unshift(this.options.timestamp); } render() { this.renderer(this.tasks); } end() {} renderer(tasks) { tasks.forEach((task) => { this.calculate(task); task.once(ListrTaskEventType.CLOSED, () => { this.reset(task); }); const rendererOptions = this.cache.rendererOptions.get(task.id); const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id); task.on(ListrTaskEventType.SUBTASK, (subtasks) => { this.renderer(subtasks); }); task.on(ListrTaskEventType.STATE, (state) => { if (!task.hasTitle()) return; if (state === ListrTaskState.STARTED) this.logger.log(ListrLogLevels.STARTED, task.title); else if (state === ListrTaskState.COMPLETED) { const timer = rendererTaskOptions.timer; this.logger.log(ListrLogLevels.COMPLETED, task.title, timer && { suffix: { ...timer, condition: !!task.message?.duration && timer.condition, args: [task.message.duration] } }); } }); task.on(ListrTaskEventType.OUTPUT, (data) => { this.logger.log(ListrLogLevels.OUTPUT, data); }); task.on(ListrTaskEventType.PROMPT, (prompt) => { const cleansed = cleanseAnsi(prompt); if (cleansed) this.logger.log(ListrLogLevels.PROMPT, cleansed); }); if (this.options?.logTitleChange !== false) task.on(ListrTaskEventType.TITLE, (title) => { this.logger.log(ListrLogLevels.TITLE, title); }); task.on(ListrTaskEventType.MESSAGE, (message) => { if (message?.error) this.logger.log(ListrLogLevels.FAILED, message.error); else if (message?.skip) this.logger.log(ListrLogLevels.SKIPPED, message.skip); else if (message?.rollback) this.logger.log(ListrLogLevels.ROLLBACK, message.rollback); else if (message?.retry) this.logger.log(ListrLogLevels.RETRY, task.title, { suffix: message.retry.count.toString() }); else if (message?.paused) { const timer = rendererOptions?.pausedTimer; this.logger.log(ListrLogLevels.PAUSED, task.title, timer && { suffix: { ...timer, condition: !!message?.paused && timer.condition, args: [message.paused - Date.now()] } }); } }); }); } 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, { ...VerboseRenderer.rendererTaskOptions, timer: rendererOptions.timer, ...task.rendererTaskOptions }); } reset(task) { this.cache.rendererOptions.delete(task.id); this.cache.rendererTaskOptions.delete(task.id); } }; //#endregion //#region src/utils/ui/renderer.ts const RENDERERS = { default: DefaultRenderer, simple: SimpleRenderer, verbose: VerboseRenderer, test: TestRenderer, silent: SilentRenderer }; function isRendererSupported(renderer) { return process.stdout.isTTY === true || renderer.nonTTY === true; } function getRendererClass(renderer) { if (typeof renderer === "string") return RENDERERS[renderer] ?? RENDERERS.default; return typeof renderer === "function" ? renderer : RENDERERS.default; } function getRenderer(options) { if (assertFunctionOrSelf(options?.silentRendererCondition)) return { renderer: getRendererClass("silent"), selection: ListrRendererSelection.SILENT }; const r = { renderer: getRendererClass(options.renderer), options: options.rendererOptions, selection: ListrRendererSelection.PRIMARY }; if (!isRendererSupported(r.renderer) || assertFunctionOrSelf(options?.fallbackRendererCondition)) return { renderer: getRendererClass(options.fallbackRenderer), options: options.fallbackRendererOptions, selection: ListrRendererSelection.SECONDARY }; return r; } //#endregion //#region src/utils/assert.ts /** * This function asserts the given value as a function or itself. * If the value itself is a function it will evaluate it with the passed in arguments, * elsewise it will directly return itself. */ function assertFunctionOrSelf(functionOrSelf, ...args) { if (typeof functionOrSelf === "function") return functionOrSelf(...args); else return functionOrSelf; } //#endregion //#region src/utils/clone.ts const clone = rfdc({ circles: true }); /*