listr2
Version:
Terminal task list reborn! Create beautiful CLI interfaces via easy and logical to implement task lists that feel alive and interactive.
1,600 lines (1,554 loc) • 67.1 kB
JavaScript
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: "\x1B[?25l",
CURSOR_SHOW: "\x1B[?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["LISTR_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: {
["STARTED"]: figures.pointer,
["FAILED"]: figures.cross,
["SKIPPED"]: figures.arrowDown,
["COMPLETED"]: figures.tick,
["OUTPUT"]: figures.pointerSmall,
["TITLE"]: figures.arrowRight,
["RETRY"]: figures.warning,
["ROLLBACK"]: figures.arrowLeft,
["PAUSED"]: figures.squareSmallFilled
},
color: {
["STARTED"]: color.yellow,
["FAILED"]: color.red,
["SKIPPED"]: color.yellow,
["COMPLETED"]: color.green,
["RETRY"]: color.yellowBright,
["ROLLBACK"]: color.redBright,
["PAUSED"]: color.yellowBright
}
};
const LISTR_LOGGER_STDERR_LEVELS = [
"RETRY",
"ROLLBACK",
"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 {
options;
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 {
options;
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 {
stream;
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 {
options;
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 {
task;
wrapper;
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$ = "PROMPT";
}
reportFailed() {
this.task.state$ = "PROMPT_FAILED";
this.restoreState();
}
reportCompleted() {
this.task.state$ = "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: {
["SKIPPED_WITH_COLLAPSE"]: figures.arrowDown,
["SKIPPED_WITHOUT_COLLAPSE"]: figures.warning,
["OUTPUT"]: figures.pointerSmall,
["OUTPUT_WITH_BOTTOMBAR"]: figures.pointerSmall,
["PENDING"]: figures.pointer,
["COMPLETED"]: figures.tick,
["COMPLETED_WITH_FAILED_SUBTASKS"]: figures.warning,
["COMPLETED_WITH_SISTER_TASKS_FAILED"]: figures.squareSmallFilled,
["RETRY"]: figures.warning,
["ROLLING_BACK"]: figures.warning,
["ROLLED_BACK"]: figures.arrowLeft,
["FAILED"]: figures.cross,
["FAILED_WITH_SUBTASKS"]: figures.pointer,
["WAITING"]: figures.squareSmallFilled,
["PAUSED"]: figures.squareSmallFilled
},
color: {
["SKIPPED_WITH_COLLAPSE"]: color.yellow,
["SKIPPED_WITHOUT_COLLAPSE"]: color.yellow,
["PENDING"]: color.yellow,
["COMPLETED"]: color.green,
["COMPLETED_WITH_FAILED_SUBTASKS"]: color.yellow,
["COMPLETED_WITH_SISTER_TASKS_FAILED"]: color.red,
["RETRY"]: color.yellowBright,
["ROLLING_BACK"]: color.redBright,
["ROLLED_BACK"]: color.redBright,
["FAILED"]: color.red,
["FAILED_WITH_SUBTASKS"]: color.red,
["WAITING"]: color.dim,
["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 {
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
}
};
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("SHOUD_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("SKIPPED_WITH_COLLAPSE");
else if (rendererOptions.collapseSkips === false) return this.logger.icon("SKIPPED_WITHOUT_COLLAPSE");
}
if (output) {
if (this.shouldOutputToBottomBar(task)) return this.logger.icon("OUTPUT_WITH_BOTTOMBAR");
return this.logger.icon("OUTPUT");
}
if (task.hasSubtasks()) {
if (task.isStarted() || task.isPrompt() && rendererOptions.showSubtasks !== false && !task.subtasks.every((subtask) => !subtask.hasTitle())) return this.logger.icon("PENDING");
else if (task.isCompleted() && task.subtasks.some((subtask) => subtask.hasFailed())) return this.logger.icon("COMPLETED_WITH_FAILED_SUBTASKS");
else if (task.hasFailed()) return this.logger.icon("FAILED_WITH_SUBTASKS");
}
if (task.isStarted() || task.isPrompt()) return this.logger.icon("PENDING", !this.options?.lazy && this.spinner.fetch());
else if (task.isCompleted()) return this.logger.icon("COMPLETED");
else if (task.isRetrying()) return this.logger.icon("RETRY", !this.options?.lazy && this.spinner.fetch());
else if (task.isRollingBack()) return this.logger.icon("ROLLING_BACK", !this.options?.lazy && this.spinner.fetch());
else if (task.hasRolledBack()) return this.logger.icon("ROLLED_BACK");
else if (task.hasFailed()) return this.logger.icon("FAILED");
else if (task.isPaused()) return this.logger.icon("PAUSED");
return this.logger.icon("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("PROMPT", (prompt) => {
const cleansed = cleanseAnsi(prompt);
if (cleansed) this.prompt = cleansed;
});
task.on("STATE", (state) => {
if (state === "PROMPT_COMPLETED" || task.hasFinalized() || task.hasReset()) {
this.prompt = null;
this.activePrompt = null;
task.off("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: "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: `${"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("COMPLETED_WITH_SISTER_TASKS_FAILED"), level));
if (!task.hasSubtasks() || !rendererOptions.showSubtasks) {
if (task.hasFailed() && rendererOptions.collapseErrors === false && (rendererOptions.showErrorMessage || !rendererOptions.showSubtasks)) output.push(...this.dump(task, level, "FAILED"));
else if (task.isSkipped() && rendererOptions.collapseSkips === false && (rendererOptions.showSkipMessage || !rendererOptions.showSubtasks)) output.push(...this.dump(task, level, "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, "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("OUTPUT", (output) => {
const data = this.dump(task, -1, "OUTPUT", output);
this.buffer.bottom.get(task.id).write(data.join(EOL));
});
task.on("STATE", (state) => {
switch (state) {
case "RETRY" || "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("OUTPUT", (output) => {
this.buffer.output.get(task.id).write(output);
});
task.on("STATE", (state) => {
switch (state) {
case "RETRY" || "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 = "OUTPUT", data) {
if (!data) switch (source) {
case "OUTPUT":
data = task.output;
break;
case "SKIPPED":
data = task.message.skip;
break;
case "FAILED":
data = task.message.error;
break;
}
if (task.hasTitle() && source === "FAILED" && data === task.title || typeof data !== "string") return [];
if (source === "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 {
tasks;
options;
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 {
tasks;
options;
static nonTTY = true;
static rendererOptions = { pausedTimer: {
...PRESET_TIMER,
field: (time) => `${"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("CLOSED", () => {
this.reset(task);
});
const rendererOptions = this.cache.rendererOptions.get(task.id);
const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id);
task.on("SUBTASK", (subtasks) => {
this.renderer(subtasks);
});
task.on("STATE", (state) => {
if (!task.hasTitle()) return;
if (state === "STARTED") this.logger.log("STARTED", task.title);
else if (state === "COMPLETED") {
const timer = rendererTaskOptions?.timer;
this.logger.log("COMPLETED", task.title, timer && { suffix: {
...timer,
condition: !!task.message?.duration && timer.condition,
args: [task.message.duration]
} });
} else if (state === "PROMPT") {
this.logger.process.hijack();
task.on("PROMPT", (prompt) => {
this.logger.process.toStderr(prompt, false);
});
} else if (state === "PROMPT_COMPLETED") {
task.off("PROMPT");
this.logger.process.release();
}
});
task.on("OUTPUT", (output) => {
this.logger.log("OUTPUT", output);
});
task.on("MESSAGE", (message) => {
if (message.error) this.logger.log("FAILED", task.title, { suffix: {
field: `${"FAILED"}: ${message.error}`,
format: () => color.red
} });
else if (message.skip) this.logger.log("SKIPPED", task.title, { suffix: {
field: `${"SKIPPED"}: ${message.skip}`,
format: () => color.yellow
} });
else if (message.rollback) this.logger.log("ROLLBACK", task.title, { suffix: {
field: `${"ROLLBACK"}: ${message.rollback}`,
format: () => color.red
} });
else if (message.retry) this.logger.log("RETRY", task.title, { suffix: {
field: `${"RETRY"}:${message.retry.count}`,
format: () => color.red
} });
else if (message.paused) {
const timer = rendererOptions?.pausedTimer;
this.logger.log("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 {
options;
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 {
tasks;
options;
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("SUBTASK", (subtasks) => {
this.renderer(subtasks);
});
if (this.options.state) task.on("STATE", (state) => {
this.logger.toStdout(this.serializer.serialize("STATE", state, task));
});
if (this.options.output) task.on("OUTPUT", (data) => {
this.logger.toStdout(this.serializer.serialize("OUTPUT", data, task));
});
if (this.options.prompt) task.on("PROMPT", (prompt) => {
this.logger.toStdout(this.serializer.serialize("PROMPT", prompt, task));
});
if (this.options.title) task.on("TITLE", (title) => {
this.logger.toStdout(this.serializer.serialize("TITLE", title, task));
});
task.on("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("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 {
tasks;
options;
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("CLOSED", () => {
this.reset(task);
});
const rendererOptions = this.cache.rendererOptions.get(task.id);
const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id);
task.on("SUBTASK", (subtasks) => {
this.renderer(subtasks);
});
task.on("STATE", (state) => {
if (!task.hasTitle()) return;
if (state === "STARTED") this.logger.log("STARTED", task.title);
else if (state === "COMPLETED") {
const timer = rendererTaskOptions.timer;
this.logger.log("COMPLETED", task.title, timer && { suffix: {
...timer,
condition: !!task.message?.duration && timer.condition,
args: [task.message.duration]
} });
}
});
task.on("OUTPUT", (data) => {
this.logger.log("OUTPUT", data);
});
task.on("PROMPT", (prompt) => {
const cleansed = cleanseAnsi(prompt);
if (cleansed) this.logger.log("PROMPT", cleansed);
});
if (this.options?.logTitleChange !== false) task.on("TITLE", (title) => {
this.logger.log("TITLE", title);
});
task.on("MESSAGE", (message) => {
if (message?.error) this.logger.log("FAILED", message.error);
else if (message?.skip) this.logger.log("SKIPPED", message.skip);
else if (message?.rollback) this.logger.log("ROLLBACK", message.rollback);
else if (message?.retry) this.logger.log("RETRY", task.title, { suffix: message.retry.count.toString() });
else if (message?.paused) {
const timer = rendererOptions?.pausedTimer;
this.logger.log("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: "SILENT"
};
const r = {
renderer: getRendererClass(options.renderer),
options: options.rendererOptions,
selection: "PRIMARY"
};
if (!isRendererSupported(r.renderer) || assertFunctionOrSelf(options?.fallbackRendererCondition)) return {
renderer: getRendererClass(options.fallbackRenderer),
options: options.fallbackRendererOptions,
selection: "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 });
/**
* Deep clones a object in the easiest manner.
*/
function cloneObject(obj) {
return clone(obj);
}
//#endregion
//#region src/utils/concurrency.ts
var Concurrency = class {
concurrency;
count;
queue;
constructor(options) {
this.concurrency = options.concurrency;
this.count = 0;
this.queue = /* @__PURE__ */ new Set();
}
add(fn) {
if (this.count < this.concurrency) return this.run(fn);
return new Promise((resolve) => {
const callback = () => resolve(this.run(fn));
this.queue.add(callback);
});
}
flush() {
for (const callback of this.queue) {
if (this.count >= this.concurrency) break;
this.queue.delete(callback);
callback();
}
}
run(fn) {
this.count++;
const promise = fn();
const cleanup = () => {
this.count--;
this.flush();
};
promise.then(cleanup, () => {
this.queue.clear();
});
return promise;
}
};
//#endregion
//#region src/utils/delay.ts
function delay(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}
//#endregion
//#region src/interfaces/listr-error.interface.ts
/**
* Internal error handling mechanism for Listr collects the errors and details for a failed task.
*
* @see {@link https://listr2.kilic.dev/task/error-handling.html}
*/
var ListrError = class extends Error {
error;
type;
task;
path;
ctx;
constructor(error, type, task) {
super(error.message);
this.error = error;
this.type = type;
this.task = task;
this.name = "ListrError";
this.path = task.path;
if (task?.options.collectErrors === "full") {
this.task = cloneObject(task);
this.ctx = cloneObject(task.listr.ctx);
}
this.stack = error?.stack;
}
};
//#endregion
//#region src/interfaces/listr-renderer-error.interface.ts
/**
* Internal error coming from renderer.
*/
var ListrRendererError = class extends Error {};
//#endregion
//#region src/interfaces/prompt-error.interface.ts
/**
* Internal error handling mechanism for Listr prompts to identify the failing cause is coming from a prompt.
*
* @see {@link https://listr2.kilic.dev/task/prompts.html}
*/
var PromptError = class extends Error {};
//#endregion
//#region src/lib/task-wrapper.ts
/**
* The original Task that is defined by the user is wrapped with the TaskWrapper to provide additional functionality.
*
* @see {@link https://listr2.kilic.dev/task/task.html}
*/
var TaskWrapper = class {
task;
constructor(task) {
this.task = task;
}
/* istanbul ignore next */
get title() {
return this.task.title;
}
/**
* Title of the current task.
*
* @see {@link https://listr2.kilic.dev/task/title.html}
*/
set title(title) {
title = Array.isAr