--- url: https://listr2.kilic.dev/repository/changelog.md description: The changelog of the library. --- # {{ $frontmatter.title }} The changelog for each published package is maintained on GitHub — jump to the one you need: * [`listr2`](https://github.com/listr2/listr2/blob/master/packages/listr2/CHANGELOG.md) * [`@listr2/manager`](https://github.com/listr2/listr2/blob/master/packages/manager/CHANGELOG.md) * [`@listr2/prompt-adapter-enquirer`](https://github.com/listr2/listr2/blob/master/packages/prompt-adapter-enquirer/CHANGELOG.md) * [`@listr2/prompt-adapter-inquirer`](https://github.com/listr2/listr2/blob/master/packages/prompt-adapter-inquirer/CHANGELOG.md) --- --- url: https://listr2.kilic.dev/repository/foreword.md --- # {{ $frontmatter.title }} Contributions are very welcome in this repository, there is no need to be toxic about anything, and everything is open to discussion. This includes but is not limited to **features, requests, bugs, and even help requests**! Nothing will mostly go stale unless there are some technical limitations/blockers, but it might take time due to **life**. ## State of Open-Source Open source takes time and nobody should cry about something that is a personal choice, but we all know that if anybody ever complains about it is because of **toxicity**. Please be kind to anybody that provides you with a library, application, and lots of documentation free of charge, this is how the community progresses forward. ## Contributions are Welcome! Contributions from all skill levels of fellow programmers are **welcome**. You can always propose a new feature or a bug request and improve documentation, examples, or tests. There are no strict guidelines for contributing but following the [general ones](/repository/contributions.html) would force consistency. ## Donations are Welcome! Even though I know nobody even cares, and this library is mostly hidden in tools that you use on a day-to-day basis, **this is mostly a one man show, and donations are always welcome through the provided sponsor links**. [![github sponsors](https://img.shields.io/github/sponsors/cenk1cenk2?label=github%20sponsors\&style=flat-square\&logo=github)](https://github.com/sponsors/cenk1cenk2) [![opencollective](https://img.shields.io/opencollective/sponsors/listr2?label=open%20collective\&logo=opencollective)](https://opencollective.com/listr2) --- --- url: https://listr2.kilic.dev/repository/llms.md --- # {{ $frontmatter.title }} The documentation site is published in an LLM-friendly form so AI assistants and agents can consume it directly. It is generated at build time by [`vitepress-plugin-llms`](https://github.com/okineadev/vitepress-plugin-llms), following the [llms.txt](https://llmstxt.org) convention. ## Endpoints * **[`/llms.txt`](https://listr2.kilic.dev/llms.txt)** — an index of the documentation: the site description plus a linked table of contents. * **[`/llms-full.txt`](https://listr2.kilic.dev/llms-full.txt)** — the entire documentation concatenated into a single Markdown file, to drop into a context window in one shot. The generated API reference under `/api` is excluded to keep it focused. * **Per-page Markdown** — every page is also emitted as raw Markdown at its path with a `.md` suffix (e.g. `/task/task.md`), so an agent can fetch just the page it needs. ## Using it with an agent Point your agent or LLM at whichever endpoint fits the task: * For a broad overview, or to let the model discover the right page, give it `https://listr2.kilic.dev/llms.txt`. * To load the whole documentation at once, use `https://listr2.kilic.dev/llms-full.txt`. * To answer a question about a single topic, fetch that page's Markdown — e.g. `https://listr2.kilic.dev/listr/listr.md`. These are plain Markdown, so any tool that can fetch a URL — a coding agent, a retrieval pipeline, or a chat with browsing — can read them without scraping the rendered HTML. --- --- url: https://listr2.kilic.dev/repository/contributions.md --- # {{ $frontmatter.title }} Contributions are very welcome in this repository. The repository is somewhat stable in the sense of how it works but it can always use some improvement in a highly evolving environment. ## Contributions Accepted Contributions will be appreciated in bugs, feature requests, and supporting parts of the repository like tests, examples, documentation, `JSDocs`. You can always open up a new issue for reporting the bugs and requesting features. Even you can even open up an issue for usage, where you are failing to do something due to the missing documentation. **Please consider and give a try to implement your feature/bug-fix, most of them are immediately accepted and published as soon as possible.** ## Looking for Contributions This section implies the following features or bug fixes are blocked for some reason or other and would be more than happy for any contributors. ### Progress Bar A progress bar can be used like the `task.progress()` inside a *Task* and sets the progress to `100%` whenever the task finishes, can be a very good addition to the current architecture. This can be rendered just after the task output, or the task title. I am not sure, which would be the best approach. So we can kind of give users an `ETA` of something whenever there are long-running tasks. ## Preflight Checks ### Checklist for Contributions for Features and Feature Requests * \[x] Create an issue for a preliminary discussion or link the existing issue. * \[x] Read/Search existing issues and the provided documentation. * \[x] Provide the implementation, and a pull request whenever finalized. * \[x] Follow guidelines enforced by `git-hooks` of linting, tests, and commit convention, which is [Angular conventional commits](https://www.conventionalcommits.org/). * \[ ] Add tests whenever or if possible. * \[ ] Update the documentation. **Much appreciated!** ### Checklist for Contributions for Bug Fixes * \[x] Link the existing issue. * \[x] Provide the implementation, and a pull request whenever finalized. * \[x] Follow guidelines enforced by `git-hooks` of linting, tests, and commit convention, which is [Angular conventional commits](https://www.conventionalcommits.org/). * \[ ] Add tests whenever or if possible. * \[ ] Update the documentation. **Much appreciated!** ### Checklist for Opening an Issue for Feature Requests * \[x] Read/Search existing issues and the provided [documentation](https://listr2.kilic.dev). * \[x] Be sure to not open an issue in the [looking for contributions section](/repository/contributions.html#looking-for-contributions). * \[x] Describe what is missing from the current implementation, and how is it not achievable with whatever is provided. * \[x] Checking the latest version of the software to confirm it has not been added already. ### Checklist for Opening an Issue for Bug Fixes * \[x] Read/Search existing issues and the provided [documentation](https://listr2.kilic.dev). * \[x] Be sure to not open an issue in the [looking for contributions section](/repository/contributions.html#looking-for-contributions). * \[x] Describe your problem clearly. * \[x] Provide replication through snippets, repository or [replit](https://replit.com/) if possible. * \[x] Checking the latest version of the software to confirm it has not been fixed already. --- --- url: https://listr2.kilic.dev/repository/release.md --- # {{ $frontmatter.title }} This library uses [semantic-release](https://github.com/semantic-release/semantic-release) for automated versioning and [renovate](https://github.com/renovatebot/renovate) for dependency updates. ## Semantic Versioning `listr2` follows the strict publishing guidelines with [semantic-versioning](https://semver.org/). This is enforced through [commitizen](https://github.com/commitizen/cz-cli) and [Angular conventional commits](https://www.conventionalcommits.org/). We are utilizing the default preset to publish a new version. Whenever you want to do a contribution, you should also follow it to ensure a proper version has been published, but no worries I will usually double-check this. **You can be sure that no breaking change will be published in a non-major version, but if something is not already working as intended it can have a patch with minor breaking changes.** Commits are usually scoped to the changes being done, so even though it is a huge change it can be committed as smaller chunks to show the flow. ## Updates `listr2` employs automated updates to all dependencies utilizing [renovate](https://github.com/renovatebot/renovate). After migrating `ts-node`, `jest`, `ts-jest` to `esm` as well in the repository itself, there is nothing holding us back to use the latest versions of the dependencies. * A new version will be published whenever a production dependency is updated. * Major dependencies require accepting the pull requests manually. * Every CI rule that this repository employs will run before updating any dependency. So if something fails the tests, it will not be merged in. Till now it has never been an instance where garbage/unworking code has been published with this methodology, so it is not much of a worry. * This is running on my Gitlab instance, so you can not directly see the changes/requests in Github. It can only be inspected further through the public pipelines or changelog after the fact. ## Hooks A set of `git-hooks` are inplace to ensure consistency. * `lint-staged` runs with the linting configuration for each commit, to ensure the styling of the code is consistent. * `commitizen` runs with `@cenk1cenk2/cz-cc` preset for each commit, to ensure the commits are in conventional-commits format. * `pnpm run test` run before pushing back to the repository. --- --- url: https://listr2.kilic.dev/listr/installation.md --- # {{ $frontmatter.title }} Add `listr2` dependency to your project through your preferred package manager. ::: code-group ```bash [npm] npm i listr2 ``` ```bash [yarn] yarn add listr2 ``` ```bash [pnpm] pnpm i listr2 ``` ::: --- --- url: https://listr2.kilic.dev/listr/environment.md --- # {{ $frontmatter.title }} `listr2` supports only modern `node.js` environments, since it is mostly intended for CLI applications, but can be used as a task list in any application. * `listr2` supports both `esm` and `cjs` modules **for now**. But is only bundled for `esm` modules going forward. * You need a supported `node.js` version, end-of-life versions are deprecated and not supported. ## Supporting Multiple Node Module Structures `listr2` supports both `esm` and `cjs` module structures however with the recent changes on [node](https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require), we only bundle `esm` modules going forward. This will still support `cjs` module structure. Thanks to [@hyperz111](https://github.com/hyperz111) for making me aware of this change, which cut our bundle distribution size in half. Recent changes to `ts-node`, `jest`, and `ts-jest` let us move the entire repository to `esm`. This enabled the repository starting from version to use dynamic imports for anything that is using an `esm` module. So from that version and upward, everything should be up to date with the upstream of the dependencies. So keeping the `cjs` version does not hinder us from updating the given packages. > The community is moving to pure `esm` modules. [sindresorhus](https://github.com/sindresorhus) who is the maintainer of many-core `npm` packages ([cheers!](https://github.com/sponsors/sindresorhus) ) has to lead the movement with the deprecated `node.js` `10` support. You can read more about it [here](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) in his post. --- --- url: https://listr2.kilic.dev/listr/examples.md --- # {{ $frontmatter.title }} `listr2` boasts many examples in the repository where you can dive deeper into the usage of the application, whenever the documentation is insufficient for advanced cases. ## Examples Examples live in the folder [`./examples/`](https://github.com/listr2/listr2/tree/master/examples), and they can be run on your environment since they are all working examples. The documentation also includes many examples, these examples are also working examples that can be run/inspected in the folder [`./examples/docs/`](https://github.com/listr2/listr2/tree/master/examples/docs). If you ever choose to clone the repository, you can run all the examples locally with the `pnpm run --filter @listr2/examples start` script, to learn about or test new things out. ```bash # clone the repository git clone git@github.com:listr2/listr2.git # install the dependencies pnpm install # build packages pnpm build # run any example, by giving the script a relative path in the repository pnpm run --filter @listr2/examples start examples/renderer-default.example.ts ``` ## `jsdoc` For every exposed option mostly includes a brief explanation, if it is missing in the examples. ## Tests In the cases where the examples might not be enough, you can always dive deeper into the tests folder that lives in the folder [`./tests/`](https://github.com/listr2/listr2/tree/master/tests). This application is mostly well-tested for edge conditions, however, it is limited to being a primarily terminal application. Unfortunately, since the terminal is mocked while testing you cannot directly see your changes on the tests, but you can always dive deeper into the examples given there. --- --- url: https://listr2.kilic.dev/listr/listr.md --- # {{ $frontmatter.title }} `listr2` is a stateful task list, therefore it is based on classes. To create a new task list, you must create an instance of [Listr](/api/listr2/classes/Listr.html) first. ## Generate New Class Import and create a new task list from the prototype. It will return the created [Listr](/api/listr2/classes/Listr.html) class. <<< @../../examples/docs/listr/new-listr/creating-a-new-instance.ts#create{1,7} ## Run the Generated Task List Then you can run this task list as an `async` function and as the result, it will return the context through the tasks. <<< @../../examples/docs/listr/new-listr/creating-a-new-instance.ts#run{2} --- --- url: https://listr2.kilic.dev/listr/context.md --- # {{ $frontmatter.title }} While running a `listr2` task list, a self-contained variable is shared across the running tasks called the `ctx`. This has the same basic idea as shared contexts in other programming languages. ## Context Variable A context is an object that is shared across the task list. Even though external variables can be used to do the same operation, context gives a self-contained way to process internal tasks. Context can be anything that satisfies the type [ListrContext](/api/listr2/type-aliases/ListrContext.html), which is by default of type `any`, but it is advised to be an object, due to the intention of using it as a mutable structure that goes through the task. Context type can be injected as a type parameter to Listr class to limit what is being used as the context throughout the task and ensure type safety. * A successful task will return the context for further operation. * Context can be injected from outside while creating the task list or running the task list. * If an error is encountered, the failed `task` is available on the collected `ListrError` for further debugging; the context itself is no longer attached. You can also manually inject a context variable default in multiple ways. ::: info Hint If all tasks are in the same task list, the context will be automatically injected into all the subtasks. ::: ## Injecting Context ### Injecting Context as an Option Context can be injected as an option to the *Listr*. <<< @../../examples/docs/listr/context/as-option.ts #### Multiple Contexts This can also be used to inject a different context into subtasks. Imagine that you want to have some set of variables that you want to use only the subtask context, then you can pass it through the option. This variable will be garbage-collected whenever the subtasks finish. So if you want to return some values before it gets lost forever, you can just assign them to the parent context since it is accessible. ::: details Code Example <<< @../../examples/docs/listr/context/multiple-contexts.ts ::: ### Injecting Context at Runtime <<< @../../examples/docs/listr/context/at-runtime.ts ## Retrieving Context ### As the ReturnValue of the Task A successful task will always return the context at the end of the task. <<< @../../examples/docs/listr/context/retrieve-return.ts{11,13} ### As Property of Task List The root *Listr* class itself holds the context value as a public property. <<< @../../examples/docs/listr/context/retrieve-property.ts{6,13} --- --- url: https://listr2.kilic.dev/listr/manager.md --- # {{ $frontmatter.title }} [Manager](/api/@listr2/manager/classes/Manager.html) is a great way to create a custom-tailored *Listr* class once and then utilize it more than once. ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/manager.example.ts). You can also find a real-world use case [here](https://github.com/tailoredmedia/backend-nx-skeleton/blob/master/packages/nx-tools/src/utils/manager.ts). ::: ::: danger `@listr2/manager` is an optional peer dependency. Please install it first. ::: code-group ```bash [npm] npm i @listr2/manager ``` ```bash [yarn] yarn add @listr2/manager ``` ```bash [pnpm] pnpm i @listr2/manager ``` ::: ## Idea The idea of having an additional task manager is to create a higher-order factory to create *Listr* on demand with always your options. This allows you to not store your options as variables that you inject every time you create a new *Listr* but store it in a stateful Manager where you can initiate as many *Listr* task lists as you want. It can alternatively be used as a running task list for ongoing actions, where you add more tasks over time through `manager.add()`. This is also possible with a plain *Listr*, but less convenient. ## Usage ### Creating a Manager Create the *Manager* once with the base options every list should share, then reuse it. Wrapping it in a factory keeps the preset in a single place. ```typescript import { Manager } from '@listr2/manager' import type { ListrBaseClassOptions } from 'listr2' function TaskManagerFactory(override?: ListrBaseClassOptions): Manager { return new Manager({ concurrent: false, exitOnError: false, ...override }) } ``` ### Creating a List on Demand `manager.newListr(tasks, options?)` returns a normal [*Listr*](/listr/listr.html) instance built with the manager's preset options, which you then run yourself. Per-call `options` are merged over the preset. ```typescript const manager = TaskManagerFactory() const list = manager.newListr([{ title: 'A task', task: async (): Promise => {} }]) await list.run() ``` ### Queueing and Running `manager.add(tasks, options?)` appends to an internal queue instead of running immediately — each `add()` becomes its own indented group. `manager.runAll(options?)` then runs everything queued and clears the queue. `add()` also accepts a function that receives the context, so a group can be built lazily from the state produced by earlier groups. ```typescript manager.add([{ title: 'First group', task: async (): Promise => {} }]) manager.add((ctx) => [{ title: 'Built from context', task: async (): Promise => {} }]) const ctx = await manager.runAll() ``` ### Running Once `manager.run(tasks, options?)` builds and runs a one-off list with the preset options in a single call — the immediate counterpart to `newListr` followed by `run`. ### Shared Context and Errors Seed the context passed to every list the manager creates through `manager.ctx`. Errors collected from each run — when `collectErrors` is enabled — accumulate on `manager.errors` across the manager's lifetime. --- --- url: https://listr2.kilic.dev/listr/interruption.md --- # {{ $frontmatter.title }} While a run is in progress, *Listr* registers a `SIGINT` handler so it can be interrupted gracefully with `Ctrl+C` instead of the process being killed outright. ::: info Example You can find the related example [here](https://github.com/listr2/listr2/tree/master/examples/interruption.example.ts). ::: ## Behavior When a run is interrupted with `Ctrl+C` (`SIGINT`): * Every in-flight task that defines a [`rollback`](/task/rollback.html) is rolled back before the process exits, reusing the same rollback mechanism as a normal failure. * Nested rollbacks run from the innermost task outwards, so a subtask always rolls back before its parent. * Tasks without a `rollback`, as well as any tasks that had not started yet, are marked with the `cancelled` state, shown as a `⊘` in the default renderer. This is distinct from `failed`, so an interrupted run is not mistaken for one that errored. * The process exits with code `127`, but only after every in-flight rollback has settled. ## The signal mechanism Interruption can not cancel an already-running *Promise*, so the original task function keeps executing in the background until it settles on its own while its `rollback` runs. To cancel your own asynchronous work cooperatively, use the `AbortSignal` exposed as `task.signal`, which aborts as soon as the run is interrupted. ```typescript { title: 'Downloading a large file.', task: async (ctx, task): Promise => { await fetch('https://listr2.kilic.dev', { signal: task.signal }) }, rollback: async (ctx, task): Promise => { // remove the partially downloaded file } } ``` `task.signal` is a standard [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) and can be handed to anything that accepts one, like `fetch`, `node:child_process` or timers, or subscribed to directly through `task.signal.addEventListener('abort', ...)`. ## Triggering it programmatically The run can be interrupted from your own code, which is useful when a task needs to abort the whole run — and cancel or roll back its siblings — on some condition. Call `task.cancel()` from inside a task, or `listr.cancel()` on the instance you are running. ```typescript { title: 'Guard the deployment.', task: async (ctx, task): Promise => { if (await detectedFatalCondition()) { // cancel every other in-flight task, run their rollbacks, then exit with 127 task.cancel() } } } ``` `cancel()` follows the exact same path as pressing `Ctrl+C` — the other in-flight tasks roll back or are marked as `cancelled`, and the process exits with `127` once every rollback has settled — but without going through an operating-system signal, so it works regardless of the `registerSignalListeners` option. Sending `SIGINT` yourself with `process.kill(process.pid, 'SIGINT')` has the same effect. ::: warning An interruption always terminates the process with `127`, whether it comes from `Ctrl+C` or `cancel()`. ::: ## Disabling the signal handlers The `SIGINT` handling, and therefore the rollback-on-interruption behavior, can be turned off through the `registerSignalListeners` *Listr* option. ::: warning When `registerSignalListeners` is set to `false`, `Ctrl+C` terminates the process immediately without running any rollback or cleanup. ::: --- --- url: https://listr2.kilic.dev/task/task.md --- # {{ $frontmatter.title }} `listr2` is a collection of tasks that are housed in a single instance as we have just created. Therefore the [Task](/api/listr2/interfaces/ListrTask.html) is the smallest building block of your task list. ## Task A single task is an object with the [given properties](/api/listr2/interfaces/ListrTask.html#properties), where the `task` is the main attraction that the desired function gets executed. A task can be in the form of, which is ensured by the typings: * `Function`/`Promise` * *Listr* \[^subtasks] * `Stream` * `Observable` \[^subtasks]: A subtask must be created through the helper function of `task.newListr` since there are injections of singleton instances of parent task performed while creating a subtask. Please check out the [related section](/task/subtasks.html). ## Creating Your First Task <<< @../../examples/docs/task/task/basic.ts{9-14} ## Append To Existing *Listr* <<< @../../examples/docs/task/task/append.ts{7-9,11-18} --- --- url: https://listr2.kilic.dev/task/task-options.md --- # {{ $frontmatter.title }} `listr2` can have global or per-task options to change the behavior of how a task or the whole set of tasks in subtask behaves. ## Per *Listr* *Listr* task list can be configured how to behave globally by using the second argument of the prototype with [given properties](/api/listr2/interfaces/ListrOptions.html#properties). ## Per *Subtask* This behavior can be further expanded, if the subtask requires a different approach, in this case, these options are generated depending on the current renderer with the [given properties](/api/listr2/interfaces/ListrSubClassOptions.html#properties). Naturally, subtasks options are a subset of the general options, since some options are needed to be set only one time, and do not make sense to change per task. ## Per *Task* Some of the task options propagate down to the per-task level. These per-task options are limited in scope, but usually enough to change a single task's behavior without wrapping it in a subtask. ## Adding Task Options Task options can be added as follows. <<< @../../examples/docs/task/task-options/task-options.ts{26-28,32-34,40-41} --- --- url: https://listr2.kilic.dev/task/error-handling.md --- # {{ $frontmatter.title }} Exceptions that occur while running the *Task* will be handled internally through *Listr*. You can throw errors out of the tasks to show they are unsuccessful or stop execution. This can further be customized at *Listr*, *Subtask* or *Task* level. Errors will yield a visual output on the terminal depending on the current renderer, and will also handle the *Task* that has failed depending on the configuration. If an application needs to quit prematurely and fail a specific task just throw out an instance of `Error`. The default behavior is if any of the tasks have failed, it will deem itself as unsuccessful and exit. This behavior can be changed with the `exitOnError` option. If the `exitOnError` is `true`, the first error encountered will be thrown out, and it will propagate outwards starting from the *Task*. ::: warning An `Error` should be always a real `Error` type extended from the JavaScript/Typescript `Error` class. ::: ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/error-handling.example.ts). ::: ## Throwing a Error Throwing an error will stop any further action from the current *Task* and will propagate outwards of the *Task* to *Listr* and depending on the `exitOnError` configuration, execution will be slowly halted for upcoming or concurrent tasks. ::: info You don't have to catch errors explicitly since they will always be handled and surfaced by *Listr*. Collecting them into `Listr.errors` for later inspection is a separate, opt-in behavior covered below. ::: ::: warning Be aware that the execution will only stop after the error is thrown out. This can kill any asynchronous action prematurely. ::: <<< @../../examples/docs/task/error-handling/basic-error.ts{9} ## Changing the Behavior ### Per *Listr* <<< @../../examples/docs/task/error-handling/change-behavior-exitonerror-listr.ts{9,19} ### Per *Subtask* <<< @../../examples/docs/task/error-handling/change-behavior-exitonerror-subtask.ts{13,23,29,33} ### Per *Task* <<< @../../examples/docs/task/error-handling/change-behavior-exitonerror-task.ts{9,11,22} ## Renderer ### *DefaultRenderer* Default renderer has options where you can change how the errors are displayed. ### *SimpleRenderer* & *VerboseRenderer* The non-TTY renderers log a `FAILED` entry carrying the error message. ## Collected Errors Errors from the *Task* are collected inside an array in the main *Listr* task list as `tasks.errors` where `tasks` is the *Listr* class. **This option is opt-in since .** Since there are options to ignore some errors on cases like `exitOnError`, or the ability to retry the given task through `task.retry`, encountered errors can be swallowed while the execution. To deal with those swallowed errors, all the errors that are encountered even though it does not stops the execution gets collected through this property. ### Enabling Error collection is toggled per *Task* through the *Listr* options with the key `collectErrors`, which is a `boolean`. The default is `false` since this is the most-underused functionality, and it should be at least opt-in for saving some memory. Setting it to `true` will collect where the error has occurred, when it has been encountered and what the `error.message` is. The context is no longer cloned in to the `ListrError` to avoid potential memory leaks and issues with cloning non-serializable values. While collection is disabled, `Listr.errors` is `null` instead of an empty array. This way an empty array always means that collection is enabled and no errors have been encountered yet, while `null` means the errors were never collected. Guard reads with `listr.errors?.length` or an explicit `null` check. ### ListrError [`ListrError`](/api/listr2/classes/ListrError.html) class extends the default `Error` and has some additional information like the cause of the error and where it is coming from to further debug the issue while execution. ### ListrErrorTypes A listr error can be caused by multiple reasons, for a better explanation of why that particular error occurred, a type property on the `ListrError` exists in the form of enum [`ListrErrorTypes`](/api/listr2/enumerations/ListrErrorTypes.html). ### Reporting an Error Manually A *Task* can attach an error to the collection without throwing or interrupting its own execution through `task.report(error, type)`, where `type` is one of [`ListrErrorTypes`](/api/listr2/enumerations/ListrErrorTypes.html). This is handy when a task recovers from a failure but you still want it recorded. The error is only stored while `collectErrors` is enabled. ```typescript task.report(new Error('recovered, but worth noting'), ListrErrorTypes.HAS_FAILED) ``` ### Methodology The order of the array `tasks.errors` where `tasks` is the *Listr* class, represents the order of errors that are encountered. To keep the error collection mechanism simple and predictable, it might also process the errors coming from the subtasks as well. For example, the following example will clear some things up about the given mindset. ::: details Code <<< @../../examples/docs/task/error-handling/collection.ts ::: ::: details Output <<< @../../examples/docs/task/error-handling/collection.output.txt{bash} ::: ::: details Flow * First error will be thrown from the first task. Since exitOnError is `false` on that context, `ListrError` will get collected by `tasks.errors`, and the value will be `{ message: '1', type: ListrErrorTypes.HAS_FAILED_WITHOUT_ERROR }`. * Then it will recurse into the second task, which has two subtasks. * The first task from the subtasks will fail and since the `exitOnError` is set to `true` in that context, that subtasks will fail and throw. The `ListrError` appended to the `tasks.errors` will be `{ message: '3', type: ListrErrorTypes.HAS_FAILED }` * Since the subtask has crashed, it will not execute the upcoming tasks in the subtasks. * It will return to the main task list and execute the 3rd task from that list. It will again show the same behavior with the first task, and the `ListrError` will be `{ message: '2', type: ListrErrorTypes.HAS_FAILED_WITHOUT_ERROR }`. ::: --- --- url: https://listr2.kilic.dev/task/subtasks.md --- # {{ $frontmatter.title }} `listr2` can be infinitely nested by utilizing subtasks, which is the core part of the design. A *Task* can return a new *Listr*. But rather than calling it by invoking a `new Listr` to get the full auto-completion features depending on the parent task's selected renderer, it is mandatory to call it through the *Task* itself by `task.newListr()` since they are sharing components internally that makes the application tick. Subtasks can be nested indefinitely as long as the terminal width is enough to support them. Subtasks give the advantage of grouping similar tasks, changing the behavior of *Listr* for a certain set of tasks, or cleaning up the rendering area when certain tasks have finished. ## Usage <<< @../../examples/docs/task/subtasks/usage.ts{8} ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/subtasks.example.ts). ::: ## Overwriting the Default Behavior Through Subtask Options You can change the behavior of *Listr*, and the selected renderer of the parent through the options of a subtask. This includes selected renderer options as well as **Listr** options like `exitOnError`, `concurrent` to be set per-subtaskly independent of the parent task, while it will always **inherit the defaults from the parent task**. Due to design limitations of making renderers a bit simpler, some of the options that cannot be changed from the renderer are marked as `@global` in the hover documentation. This is not disabled through typings due to keeping the renderer instances fully self-contained. ::: details Code Example <<< @../../examples/docs/task/subtasks/overwriting-options.ts{8,23,30,45} ::: ## Access Parent Task from Subtasks You can access the parent task class from subtasks by passing the function signature `(parent) => Listr` to `task.newListr`. This way you can change the title of the parent task or access its functionality. ::: details Code Example <<< @../../examples/docs/task/subtasks/access-parent-task.ts{9,15} ::: ## Renderer ### *DefaultRenderer* Subtasks are rendered as an indented block under the parent task and can be collapsed once finished through the `collapseSubtasks` renderer option. ### *SimpleRenderer* & *VerboseRenderer* The non-TTY renderers have no nesting area to collapse; each subtask's state changes are logged inline as they happen, in the order they run. --- --- url: https://listr2.kilic.dev/task/title.md --- # {{ $frontmatter.title }} *Task* can have a title to stand out from the crowd and give the user visual queues of what is actually running. ## Usage The title of the *Task* can be initiated while creating the task itself and, can be directly manipulated through the injected `task` object during runtime. This allows the user to change the title depending on the progress made throughout the task, or simply to inform the user that the task is finished. <<< @../../examples/docs/task/title/task-title.ts{5,9,13} ## Tasks without a Title The title of a *Task* is an optional property. For most of the renderers, except for *TestRenderer*, the tasks that do not have a title would be hidden. For something like a default renderer tasks that have subtasks with no title will be flattened visually. You can always use `task.title` programmatically to add titles, and visually pop tasks to existence. ::: details Code Example <<< @../../examples/docs/task/title/task-title-pop.ts{8,12,19,23} ::: --- --- url: https://listr2.kilic.dev/task/output.md --- # {{ $frontmatter.title }} *Task* can push output while running for informing the user of what is going on or programmatically for more information about an underlying task. ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/task-output.example.ts). ::: ## Usage Depending on the renderer selected, the format of the output will change. For *DefaultRenderer* everything will be rendered in a small bar just after the task, while for *SimpleRenderer*, *VerboseRenderer*, or *TestRenderer* it will be more like a logger. You can find the individual properties for *Task* `output` behavior in the next section. ### Show Output Through the Task Itself This will show the output in a small bar that can only show the last output from the task. <<< @../../examples/docs/task/output/with-task.ts{8,11,14} ### Passing Data Through an Observable or a Stream Since observables and streams are supported they can also be used to generate output. ::: details Code Example — Stream <<< @../../examples/docs/task/output/stream.ts ::: ::: details Code Example — Observable <<< @../../examples/docs/task/output/observable.ts ::: ### Reset the Output Setting the output to `null` clears whatever the *Task* has already streamed instead of rendering anything new. ```typescript task.output = 'I will push an output.' task.output = null ``` For *DefaultRenderer* this empties the output bar and the bottom bar for that *Task*, so a following `{ persistentOutput: true }` finalize keeps nothing. The append-only loggers, *SimpleRenderer*, *VerboseRenderer*, and *TestRenderer*, ignore it since their history can not be cleared. ::: warning Prior to this, `task.output = null` used to render the literal string `"null"`. ::: ## Render a WritableStream Directly `process.stdout` and `process.stderr` might get hooked depending on the usage of *ProcessOutput* on the selected renderer. So anything that requires a `WritableStream` while the task is running to dump the output should go through `task.stdout()`, which creates a temporary `WritableStream` for the task, instead of writing to the stream directly. ## Render Output of a Command Task output can be piped to the `task.stdout()` directly since it is a `WritableStream`, whenever you are running something that writes to the `process.output`. This usually can be utilized to show the outputs of the commands. ::: details Code Example <<< @../../examples/docs/task/output/pass-stdout.ts ::: Whenever more control over the stream is required, temporary `WritableStream` can be created through helper function `createWritable` via passing it a callback to dictate the behavior of the `write` call. ::: details Code Example <<< @../../examples/docs/task/output/pass-stdout-with-control.ts ::: ## Renderer ### *DefaultRenderer* #### Use the Output Bar For *DefaultRenderer*, if the task has a title, last line of output will be rendered under the task title by default. Item count that is desired to be showed in the output bar can be set through the renderer option `outputBar` and is per-task. * `true` only keep the last line. * `Infinity` will keep all the lines. * `number` will keep the defined amount of lines. * `false` will not render output with this method. ::: details Code Example <<< @../../examples/docs/task/output/renderer-default-outputbar.ts ::: #### Use the Bottom Bar For *DefaultRenderer*, data can be outputted to a bar below all the render area, this is useful for fast moving logs. If the task has no title, last line of output will be rendered in the bottom bar by default. Bottom bar can be selected through *Task* renderer options, where it will create a bar at the end of the tasks leaving one line return space in between. Item count that is desired to be showed in the bottom bar can be set through the renderer option `bottomBar` and is per-task. * `true` only keep the last line. * `Infinity` will keep all the lines. * `number` will keep the defined amount of lines. * `false` will not render output with this method. ::: details Code Example <<< @../../examples/docs/task/output/renderer-default-bottombar.ts ::: #### Persistent Output To keep the output after the task has been completed while using the default renderer, you can set `{ persistentOutput: true }` in the *Task* or *Listr* renderer options. ::: details Code Example <<< @../../examples/docs/task/output/renderer-default-persistent.ts ::: ### *SimpleRenderer* & *VerboseRenderer* The non-TTY renderers have no bars. Every line written to `task.output` is logged as an `OUTPUT` entry as it arrives, interleaved with the rest of the log, so the output is inherently persistent — the `outputBar`, `bottomBar` and `persistentOutput` options do not apply. --- --- url: https://listr2.kilic.dev/task/enable.md --- # {{ $frontmatter.title }} *Task* can be enabled depending on the variables programmatically. This enables the creation of tasks that are dependent on the context or outside conditions. ::: warning *Task* conditional enable is determined upon the initial run of the Listr when you create the class for a given *Task* or *Subtask*, so be careful with using it while using internal context variables. After the initial evaluation when the execution time comes for that particular *Task*, it will get re-evaluated. ::: ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/task-enable.example.ts). ::: ## Usage <<< @../../examples/docs/task/enable/basic.ts{18} ## Renderer Disabled tasks will not be rendered. --- --- url: https://listr2.kilic.dev/task/skip.md --- # {{ $frontmatter.title }} Conditional skip is a way of skipping a *Task* depending on the given context. But the main difference between `enable` and `skip` is `skip` will always render the given task. When the execution time comes, and it turns out that it should be skipped, it will render or mark it as skipped. ::: warning Please pay attention to asynchronous operation while designing a context-enabled task list since it does not wait for any variable in the context. ::: ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/task-skip.example.ts). ::: ## Usage Skip call can either have or not have a message, therefore it is optional. Having a message combined with the selected renderer and its settings will yield a different output, where skip message could be directly shown. ## Skip inside a *Task* <<< @../../examples/docs/task/skip/inside.ts{8} ## Skip conditionally defining the *Task* <<< @../../examples/docs/task/skip/task.ts{8,14} ## Renderer ### *DefaultRenderer* The default renderer has options where you can change how the skip messages are displayed. ### *SimpleRenderer* & *VerboseRenderer* The non-TTY renderers log a `SKIPPED` entry with the skip reason when a task is skipped; the display options above are specific to the *DefaultRenderer*. --- --- url: https://listr2.kilic.dev/task/prompts.md --- # {{ $frontmatter.title }} Prompts use adapters and optional peer dependencies to provide interactivity with the user. The problem that we have with this application is that we are utilizing a single console updater, therefore we cannot directly write to `process.stdout`. This behavior requires a adapter in between to instead write to `task.stdout` and control the ANSI escape sequences for clearing lines since we do not have a `vt100` compatible interface through the console updater. Since , for the ability to support multiple prompt providers, signature of the function `task.prompt` has changed requiring a adapter first. ## Adapters ### `inquirer` The recommended input adapter uses [`@inquirer/prompts`](https://github.com/SBoudrias/Inquirer.js), the actively maintained modern *Inquirer* implementation. Prefer this adapter for new code. ::: danger `inquirer` is an optional peer dependency. Please install it first. This library utilizes `@inquirer/prompts` instead of the legacy implementation `inquirer`. Please also add the necessary prompt package for using a prompt from `inquirer`, you can read more about it in their [documentation](https://github.com/SBoudrias/Inquirer.js/blob/master/packages/prompts/README.md). ::: code-group ```bash [npm] npm i @listr2/prompt-adapter-inquirer @inquirer/prompts ``` ```bash [yarn] yarn add @listr2/prompt-adapter-inquirer @inquirer/prompts ``` ```bash [pnpm] pnpm i @listr2/prompt-adapter-inquirer @inquirer/prompts ``` ::: ##### Single Prompt <<< @../../examples/docs/task/prompts/inquirer-single.ts{12} #### Cancel a Prompt Since *Task* keeps track of the active prompt and this adapter exposes a `cancel` method, you can cancel a prompt while it is still active. ::: warning `inquirer` acts a little bit different while canceling the prompt, since it is implemented as a `CancellablePromise` that does not expose `submit` externally; cancelling the promise throws an error out of it. ::: <<< @../../examples/docs/task/prompts/inquirer-cancel.ts{17} ### `enquirer` ::: warning Deprecated — lifeline support only [`enquirer`](https://www.npmjs.com/package/enquirer) is effectively unmaintained, with its last release in 2021, and is kept on **lifeline support only**. Prefer the [`inquirer`](#inquirer) adapter above for new code. A `readline` change introduced in Node.js 24+ (`ERR_USE_AFTER_CLOSE`) breaks `enquirer`'s internal teardown in non-interactive / piped-`stdin` scenarios. Since , where Node.js 26 was added to the test and build matrix and this issue first surfaced, **the `enquirer` adapter is no longer guaranteed on Node.js 26 and above**. It is expected to keep working on Node.js 22 and 24. ::: The input adapter uses [`enquirer`](https://www.npmjs.com/package/enquirer). ::: danger `enquirer` is an optional peer dependency. Please install it first. ::: code-group ```bash [npm] npm i @listr2/prompt-adapter-enquirer enquirer ``` ```bash [yarn] yarn add @listr2/prompt-adapter-enquirer enquirer ``` ```bash [pnpm] pnpm i @listr2/prompt-adapter-enquirer enquirer ``` ::: Inside a *Task*, the `task.prompt` function gives you access to any [`enquirer`](https://www.npmjs.com/package/enquirer) default prompt as well as ability to modify the underlying instance for using a custom `enquirer` prompt. To get input from the user you can assign the task a new prompt in an async function and write the response to the context. ::: warning It is not advised to run prompts in concurrent tasks because multiple prompts will clash and overwrite each other's console output and when you do keyboard movements it will apply to them both. This has been disabled to do in some renderers, but you are still able to do it with some renderers. ::: ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/task-prompt-enquirer.example.ts). ::: #### Usage To access the prompts just utilize the `task.prompt` jumper function by passing in your [`enquirer`](https://www.npmjs.com/package/enquirer) prompts as an argument. ::: info The types for `enquirer` are re-written and bundled with this adapter, so they may have some inaccuracies. They will be reconciled with the upstream types once `enquirer` merges its pending typings work (). ::: ##### Single Prompt ::: danger As a convenience, whenever you have just one prompt, you do not have to name your prompt as in `enquirer`; it will be automatically named and then returned. ::: <<< @../../examples/docs/task/prompts/enquirer-single.ts{11,16} ##### Multiple Prompts ::: warning If you want to pass in an array of prompts, be careful that you should name them, this is also enforced by Typescript as well. This is not true for single prompts, since they only return a single value that is passed directly to the assigned variable. ::: <<< @../../examples/docs/task/prompts/enquirer-multiple.ts{16-27} ##### Use a Custom Prompt You can either use a custom prompt out of the npm registry, or a custom-created one as long as it works with the [`enquirer`](https://www.npmjs.com/package/enquirer), it will work as expected. Instead of passing in the prompt name use the not-new-invoked class. ```typescript import Enquirer from 'enquirer' import EditorPrompt from 'enquirer-editor' import { Listr, ListrEnquirerPromptAdapter } from 'listr2' const enquirer = new Enquirer() enquirer.register('editor', EditorPrompt) const tasks = new Listr( [ { title: 'Custom prompt', task: async (ctx, task): Promise => { ctx.testInput = await task.prompt(ListrEnquirerPromptAdapter).run( { type: 'editor', message: 'Write something in this enquirer custom prompt.', initial: 'Start writing!', validate: (response): boolean | string => { return true } }, { enquirer } ) } } ], { concurrent: false } ) const ctx = await tasks.run() console.log(ctx) ``` #### Cancel a Prompt Since *Task* keeps track of the active prompt and this adapter exposes a `cancel` method, you can cancel a prompt while it is still active. <<< @../../examples/docs/task/prompts/enquirer-cancel.ts{16} ## Renderer Prompts, since their output passes through an internal `WritableStream` as a `process.stdout` will render multiple times in non-TTY renderers. It will work anyhow albeit it might not look great. Since prompts are not even intended for non-TTY terminals, this is a novelty. ### *DefaultRenderer* Prompts can either have a title or not, but they will always be rendered at the end of the current console output. --- --- url: https://listr2.kilic.dev/task/retry.md --- # {{ $frontmatter.title }} If you want to retry a task that had failed a couple of times more, you can use the `retry` property in the `Task`. ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/task-retry.example.ts). ::: ## Usage <<< @../../examples/docs/task/retry/basic.ts{18} ## Retry Delay Retry action can have a delay between the tries. For enabling this behavior, you can pass the retry to the given task as an object. <<< @../../examples/docs/task/retry/retry-delay.ts{18-21} While waiting between attempts the task enters a paused state, and the *DefaultRenderer* shows a live countdown until the next try through its `pausedTimer` option. You can check for this state inside a task with `task.isPaused()`. ## Retry Event Retrying is self-aware, and you can access the task if it is retrying via `task.isRetrying()`. It will either return an object [with the given interface](/api/listr2/interfaces/ListrTaskRetry.html) where the `count` will be `0` for not repeating tasks, and `error` is the last encountered error if retrying. ### Retry Count <<< @../../examples/docs/task/retry/retry-count.ts{8,10-13} ### Last Error <<< @../../examples/docs/task/retry/last-error.ts{8,10-13} ## Renderer * When retrying, the task title will be reset to the original task title. ### *DefaultRenderer* ### *SimpleRenderer* & *VerboseRenderer* The non-TTY renderers log a `RETRY` entry with the current attempt count on each retry, rather than resetting an in-place title. --- --- url: https://listr2.kilic.dev/task/rollback.md --- # {{ $frontmatter.title }} Whenever the *Task* itself failed or its subtasks have failed, `rollback` will revert anything that needs to be reverted by that uncompleted action. Rollback will only execute if the task itself has been marked as failed and can be defined as the `rollback` property of a task. > Since when you return a new *Listr* as a subtask list, it is not the easiest, and most convenient to do something on failure, and each subtask should be handled separately. But this can still be used for singular tasks where some action needs to be reverted if the task does not complete. ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/task-rollback.example.ts). ::: ## Usage ### For *Subtask* <<< @../../examples/docs/task/rollback/for-subtasks.ts{26-36} ## Options Rollback, when it fails by default, throws an exception and stops the execution of the upcoming tasks. But this can be overwritten by `{ exitAfterRollback: false }` option. This is the main Listr option that acts independently of `exitOnError` since failing the rollback might have worse consequences. ## Interruption Besides task failures, `rollback` also runs when a run is interrupted with `Ctrl+C`. See [Interruption](/listr/interruption.html) for how interruption behaves and how to cancel your own work cooperatively through `task.signal`. ## Renderer ### *DefaultRenderer* When rollback is activated the default renderer will change the spinner color to bright red, if the rollback successfully concludes then it will be a redback arrow, else it would be like a normal error where it will show the error from the rollback action itself. ### *SimpleRenderer* & *VerboseRenderer* The non-TTY renderers cannot recolor a spinner, so they log a `ROLLBACK` entry carrying the rollback message instead. --- --- url: https://listr2.kilic.dev/renderer/renderer.md --- # {{ $frontmatter.title }} Renderers are the communication interface with your *Listr*. There are five renderers: `default`, `simple`, `verbose`, `silent`, and the testing-only `test`. *DefaultRenderer* is the default choice. If the environment advertises itself as non-TTY it will fall back to the fallback renderer automatically. *SimpleRenderer* is an alternative to *DefaultRenderer*, which has all its capabilities but does not try to update your `vt100` compatible terminal if you are not using prompts. From it is designated as the default choice for the fallback renderer. *VerboseRenderer* was the default choice for the fallback renderer prior to . It is a fully text-based renderer. *SilentRenderer* is used for the *Subtask* of *Task* since the parent already started a renderer. This renderer can also be used for using `listr2` as only a task list without any output to the terminal directly, so you could use your method of communication, or a logger however you like it. *TestRenderer* can be used for tests, where it will only output JSON output per-line for specific events that happen throughout the render. ## *Listr* and *Subtask* Renderer Options and Per *Task* Renderer Options Renderers can have global options, which it is set through `rendererOptions`, can be modified for a certain *Subtask*, and per *Task* options, which are injected inside the *Task* as `options`. Depending on the selected `renderer`, `rendererOptions` as well as the `options` in the `Task` will change accordingly. For also configuring the `fallbackRenderer`, you can pass `fallbackRendererOptions` to *Listr*. --- --- url: https://listr2.kilic.dev/renderer/fallback-condition.md --- # {{ $frontmatter.title }} There are times other than `non-TTY` environments when you want to fallback to a fallback/silent renderer than the selected renderer. A function that returns a boolean , or directly a boolean can be passed to *Listr* for automatically stepping down to the `fallbackRenderer` or directly to *SilentRenderer* when the condition is met. ## Behavior ### TTY or non-TTY Environment `fallbackRenderer` will be automatically used whenever you are in a `non-TTY` environment. * You can force to use TTY environment via *Listr* option `forceTTY` or set the environment variable `LISTR_FORCE_TTY=1`. ### Coloring Node's built-in [`styleText`](https://nodejs.org/api/util.html#utilstyletextformat-text-options) from `node:util` is used as the underlying coloring mechanism. Colors are disabled automatically whenever it is detected as not supported. * You can set the environment variable `FORCE_COLOR=1` to force colors. * You can set the environment variable `NO_COLOR=1` to disable colors completely even though your environment supports it. This is very useful for tests. ### Unicode Unicode characters like icons are not used whenever it is detected that your output does not support them. * You can use the `forceUnicode` option on *Listr* or set the environment variable `LISTR_FORCE_UNICODE=1` to force the usage of the Unicode characters. ::: warning These checks are primal at best but do not forget that in many cases, your terminal might support any of these UI properties, but the application in between might abstract access to them therefore it can be detected otherwise. ::: ## Usage ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/renderer-fallback-condition.example.ts). ::: ### Renderer Fallback You can use the `fallbackRendererCondition` condition on *Listr* to determine changing your renderer to the fallback renderer. <<< @../../examples/docs/renderer/fallback-condition/renderer-fallback.ts{15} ### Silent Renderer Fallback You can use the `silentRendererCondition` condition on *Listr* to determine changing your renderer to *SilentRenderer*. <<< @../../examples/docs/renderer/fallback-condition/renderer-silent.ts{15} --- --- url: https://listr2.kilic.dev/renderer/default.md --- # {{ $frontmatter.title }} *DefaultRenderer* is the main renderer of `listr2`. *DefaultRenderer* is intended for `TTY` environments with `vt100` terminal compatibility, where it continuously redraws the terminal output as the state of a *Task* changes. This renderer has many options for customization, these options can be changed at *Listr*, *Subtask* or *Task* level. This renderer uses *ProcessOutput* to take control of the terminal. ![demo](../../examples/renderer-default.gif) ## How Updates Are Rendered The task list is drawn with partial, differential updates through [`log-update`](https://www.npmjs.com/package/log-update) `v8`. Instead of clearing and rewriting the whole screen on every change, only the lines that actually changed are rewritten, and each redraw is wrapped in synchronized-output markers (`ESC[?2026h`/`ESC[?2026l`) on supporting terminals to reduce flicker. The visible result is identical to previous versions, only smoother. ## Renderer Options ## Renderer Task Options --- --- url: https://listr2.kilic.dev/renderer/simple.md --- # {{ $frontmatter.title }} *SimpleRenderer* is an alternative to *DefaultRenderer*, which does not directly update the terminal but works more in a logger-like manner. *SimpleRenderer* still requires `vt100` terminal compatibility if you are using prompts, but can work in `non-TTY` environments other than that. ![demo](../../examples/renderer-simple.gif) ## Renderer Options ## Renderer Task Options --- --- url: https://listr2.kilic.dev/renderer/verbose.md --- # {{ $frontmatter.title }} *VerboseRenderer* was the default `non-TTY` renderer prior to and works mostly like a logger. ![demo](../../examples/renderer-verbose.gif) ## Renderer Options ## Renderer Task Options --- --- url: https://listr2.kilic.dev/renderer/silent.md --- # {{ $frontmatter.title }} *SilentRenderer* outputs nothing. Bring your logger for logging through the tasks and just use *Listr* as a logical task list for your actions. --- --- url: https://listr2.kilic.dev/renderer/test.md --- # {{ $frontmatter.title }} *TestRenderer* is intended to use in tests and provides a per-line JSON format output, that is configurable through the renderer options. This JSON format specific can be seen [here](/api/listr2/interfaces/TestRendererSerializerOutput.html), but needs a better understanding of internal workings of this library. ## Renderer Options --- --- url: https://listr2.kilic.dev/renderer/custom.md --- # {{ $frontmatter.title }} You can create a custom renderer that is fit for your application. ## Implement or Extend the Default *Listr* Renderer <<< @../../examples/docs/renderer/custom/create-renderer.ts#create ::: info For Javascript, since you cannot implement `ListrRenderer`, you can extend either the *SilentRenderer* or `ListrBaseRenderer`. ::: ## Utilizing the *Task* Take a look at *DefaultRenderer* since it is implemented this way. ::: details Code Example <<< @../../packages/listr2/src/renderer/default/renderer.ts ::: ## Utilizing the Events *Listr* and its *Task* fires many events to indicate the task status. *Task* depending on what is currently done will fire [ListrTaskState](/api/listr2/enumerations/ListrTaskState.html) and [ListrTaskEventType](/api/listr2/enumerations/ListrTaskEventType.html) through [ListrTaskEventManager](/api/listr2/classes/ListrTaskEventManager.html) which you can subscribe to. Take a look at *SimpleRenderer* or *VerboseRenderer* since it is implemented this way. A renderer that buffers per-task output — as the *DefaultRenderer* does for its output bar and bottom bar — subscribes to the individual *Task* through `task.on(...)`, reacting to [`ListrTaskEventType`](/api/listr2/enumerations/ListrTaskEventType.html) events such as `OUTPUT`, `OUTPUT_RESET`, `STATE` and `PROMPT`. ::: details Code Example <<< @../../packages/listr2/src/renderer/simple/renderer.ts ::: ::: details Code Example <<< @../../packages/listr2/src/renderer/verbose/renderer.ts ::: ## Using Render Hooks Additional to listening to the events, another singleton hook that comes from the root *Listr* is `events`. This provides some generic events like [`ListrEventType.SHOULD_REFRESH_RENDER`](/api/listr2/enumerations/ListrEventType.html#should-refresh-render) which can be used to trigger an update on an updating renderer. These `events` can be the third optional variable of a given renderer while using it is always optional. ```typescript export class MyAmazingRenderer implements ListrRenderer { constructor( private readonly tasks: ListrDefaultRendererTasks, private readonly options: ListrDefaultRendererOptions, private readonly events: ListrEventManager ) {} } ``` These events can be later listened to trigger an update. ```typescript this.events.on(ListrEventType.SHOULD_REFRESH_RENDER, () => { this.update() }) ``` ## Using a Custom Renderer You can tell Listr to use your custom renderer by setting the `renderer` option in *Listr* to your custom renderer. <<< @../../examples/docs/renderer/custom/create-renderer.ts#run --- --- url: https://listr2.kilic.dev/renderer/logger.md --- # {{ $frontmatter.title }} [ListrLogger](/api/listr2/classes/ListrLogger.html) is a common interface that enables the renderers to have a certain output format. *ListrLogger* is used for every renderer to a certain degree. *ListrLogger* is a middleware between the console outputs of the renderers and handles the output streams `stdout` and `stderr` through *ProcessOutput*, and it might take full control of those streams while the renderer is rendering to avoid something else trying to write to the terminal. *ListrLogger* also handles the styling and formatting of to be dumped data to the terminal depending on their level, which includes the colors and styling. ## Log Levels Log levels for the *ListrLogger* are dynamically injected while creating an instance of *ListrLogger* and will affect the styling section of the instance. By default, [ListrLogLevels](/api/listr2/enumerations/ListrLogLevels.html) is used for text-based renderers. For renderers like *DefaultRenderer* styling require more cases than usual compared to the text-based renderers, therefore custom log levels [ListrDefaultRendererListrLogLevels](/api/listr2/enumerations/ListrDefaultRendererListrLogLevels.html) are injected. ## Style The *style* of *ListrLogger* can be customized through [ListrLoggerOptions](/api/listr2/interfaces/ListrLoggerOptions.html). Since every renderer in some form is using *ListrLogger* this functionality can be used to customize the renderers directly without implementing your renderer. ### Icons and Colors *ListrLogger* can be customized for any renderer through the exposed fields on your renderer inside the respective renderer options that use the *ListrLogger*. The `icon` and `color` section of the supported renderer is in the form of [ListrLoggerStyleMap](/api/listr2/interfaces/ListrLoggerStyleMap.html). By injecting new style options into the *ListrLogger* you can change the icons and colors of every possible task. ::: details Code Example <<< @../../examples/docs/renderer/style/icon-and-color.ts ::: ## Fields *ListrLogger* can wrap each entry with fields in the form of prefixes and suffixes through `logger.prefix(message, ...fields)` and `logger.suffix(message, ...fields)`. A field is either a plain `string` or a [`LoggerField`](/api/listr2/type-aliases/LoggerField.html) object, which can compute its value from arguments, render conditionally, and carry its own formatting. ```typescript logger.suffix('a message', { field: () => new Date().toISOString(), condition: showTimestamp }) ``` The [presets](#presets) below are prebuilt fields — timers and timestamps — that plug into this same prefix/suffix mechanism. ## Presets The Preset mechanism is used for displaying additional data like [timestamps](/api/listr2/variables/PRESET_TIMESTAMP.html) or [timers](/api/listr2/variables/PRESET_TIMER.html). This also gives flexibility to making things dynamic with a conditional display of fields or conditional styling. Different renderers support different presets, depending on whether it fits in the style of the selected renderer. Presets are not directly tied with the *ListrLogger* itself but it is mostly leveraging the mechanism to have fields in the sense of prefixes and suffixes in the logging entry. You can either pass the predefined preset to a given field or override a preset's behavior by overwriting the fields of the preset since it is designed as an object. ### Preset Timer This preset can be used to show how much time has elapsed for a given thing to happen. This preset is available for the *DefaultRenderer*, *VerboseRenderer*, and *SimpleRenderer* on both *Listr* and *Task* level options. ::: details Code Example <<< @../../examples/docs/renderer/logger/preset-timer.ts ::: ### Preset Timestamp This preset can be used to stamp log entries with the current timestamp. This preset is available for the *VerboseRenderer*, *SimpleRenderer* on *Listr* level options. ::: details Code Example <<< @../../examples/docs/renderer/logger/preset-timestamp.ts ::: ## Custom Logger You can use your custom *ListrLogger* whenever the underlying renderer is using it directly. This argument is always true for the provided renderers that output to the console. To do this you must expand the original *ListrLogger* implementation since it is again a stateful class. ::: details Code Example <<< @../../examples/docs/renderer/logger/custom-implementation.ts ::: > After a couple of attempts with , this turned out to be the most flexible solution for modifying the logger, where it is shared with multiple different implementations of renderers, and they do not use it the same way. ::: warning Some options for the *ListrLogger* are forcefully injected through the selected renderer. These are the options like global field options like `timestamp` or `icon` and `style` options, to expose them without requiring a custom logger since this falls into the more advanced use cases. ::: --- --- url: https://listr2.kilic.dev/renderer/process-output.md --- # {{ $frontmatter.title }} [ProcessOutput](/api/listr2/classes/ProcessOutput.html), [ProcessOutputStream](/api/listr2/classes/ProcessOutputStream.html), [ProcessOutputBuffer](/api/listr2/classes/ProcessOutputBuffer.html) is used to take control of the current `stdout` and `stderr` for *ListrLogger* to ensure that nothing else is written to the console and creates an abstraction for accessing `process.stdout` and `process.stderr` when needed. ## Hijack [ProcessOutput](/api/listr2/classes/ProcessOutput.html) for renderers like *DefaultRenderer* that need updating your `vt100` compatible terminal gives the ability to hijack the current `process.stdout` and `process.stderr`, create a temporary buffer for storing anything that is trying to write to the terminal since it will corrupt the output of *Listr*. If the renderer does not request to hijack the terminal output, `process.stdout` and `process.stderr` will be used directly without any trickery. ## Release After the renderer releases the *ProcessOutput* and marks it ready to use, everything that has been outputted to the hooked streams will be dumped. **This intends to solve the most common cause of opening an issue in the repository which is saying that the output is corrupted and not realizing something else is writing to output the terminal.** ## Writing Through Process Output When you write your own renderer or logger, emit through the *ProcessOutput* instead of touching `process.stdout` directly, so your output stays coordinated with the hijack/release cycle. `output.toStdout(buffer)` and `output.toStderr(buffer)` write to the correct stream, and the underlying streams are also reachable through `output.stream`. ## Extending Process Output You can override the default *ProcessOutput* by extending the class with your expected behavior (e.g. writing to a log file) on the *ListrLogger* since all the renderers, that either use or do not use the hijacking function, use *ProcessOutput* through *ListrLogger* itself. For most cases, just creating a `new ProcessOutput()` by passing your own `WriteStream` for `process.stdout` and `process.stderr` through the constructor should be good enough. ### Changing the Behavior You can change the behavior of the *ProcessOutput* through injecting it to the logger. Since every renderer at some level uses the underlying logger, this can effectively be used to change the behavior of the ProcessOutput as well. If you do not like the behavior of the *ProcessOutput*, you can always implement and bring your own through this interface as well. ::: details Code Example <<< @../../examples/docs/renderer/process-output/change-behavior.ts ::: ## Routing All Output to `stderr` Since every renderer writes through the *ProcessOutput* on the *ListrLogger*, you can keep `stdout` clean and pipeable (e.g. emitting `JSON` to pipe into `jq`) while the live UI and the final output still show up in the terminal by routing everything to `stderr`. Pass `process.stderr` for both the `stdout` and `stderr` slots of the *ProcessOutput*, then inject the logger into the renderer. There is one caveat: piping `stdout` makes it non-`TTY`, which normally steps *Listr* down to the fallback renderer with a default logger. To keep the *DefaultRenderer* and route the fallback through the same logger, set `forceTTY` and provide the logger on both `rendererOptions` and `fallbackRendererOptions`. ::: details Code Example <<< @../../examples/docs/renderer/process-output/route-to-stderr.ts ::: --- --- url: https://listr2.kilic.dev/migration/v6.md --- has many new features, speed improvements and dependency updates. ## Foreword **I am aware that breaking changes are inconvenient. Breaking changes that require you to refactor your code in this repository is rare and far between.** But this is a big update where a core component like [`rxjs`](https://npmjs.com/rxjs) has been replaced with an event-emitter-based approach, which is required to refactor anything that is renderer related. So in this update, custom renderers might take the biggest hit since rendering has been completely refactored. While doing so I have decided to address one of the biggest problems with the repository. While using an updating renderer like *DefaultRenderer*, you might accidentally or unwillingly log into the console, where this completely breaks the underlying [`log-update`](https://npmjs.com/log-update) library. Now there is a *ProcessOutput* abstraction is in place to avoid this as much as we can. The naming scheme of some options was inconsistent and was bugging me for a long-time. Some of the fields are just kept to be somewhat compatible with the original [Listr](https://github.com/SamVerschueren/listr) library. Since this is no longer a concern of mine and this library is extended beyond it is compatibility with the original, I have taken the initiative to update some options with a more consistent naming scheme. ## Migration ::: warning If you encounter any more of the breaking changes, please feel free to contribute by editing this page. ::: * `node.js` version 16 is required as a minimum since support for version 14 has been terminated. * The fallback renderer default has been changed from *VerboseRenderer* to *SimpleRenderer* since it is mostly non-TTY compatible if there are no prompts. * [`rxjs`](https://npmjs.com/rxjs) has been dropped in favor of an event-emitter-based approach with [`eventemitter3`](https://npmjs.com/eventemitter3). This will possibly break every custom renderer since custom-renderers might rely on `subscribe` on *Task*. The refactoring is nothing very marginal where you can compare the current and old *VerboseRenderer* for guidance. * The [Listr](/api/listr2/classes/class..Listr.html) and [Manager](/api/listr2/classes/class._manager.Manager.html) `err` fields have been renamed to `errors` for consistency. * *DefaultRenderer* default output format, which handles fitting the rendered data inside the terminal columns, has been changed from `truncate` to `wrap`, since it is the more logical choice to display all the output data. * The [Listr](/api/listr2/classes/class..Listr.html) options `nonTTYRenderer` and `nonTTYRendererOptions` have been renamed to `fallbackRenderer`, `fallbackRendererOptions` respectively for consistency. * The [Listr](/api/listr2/classes/class..Listr.html) options `fallbackCondition` and `silentCondition` have been renamed to `fallbackRendererCondition`, `silentRendererCondition` respectively for consistency. * The [Listr](/api/listr2/classes/class..Listr.html) option `collectErrors` default behavior has been changed from `'minimal'` to `false` since this is the most underused functionality and decided that it should be opt-in for saving some memory. * The [Listr](/api/listr2/classes/class..Listr.html) property `path` which is mostly used for collecting the errors to find out the task hierarchy is now an array of strings instead of a joined string. * The [ListrLogger](/api/listr2/classes/class..ListrLogger.html) implementation has been completely reworked and renamed from `Logger` to `ListrLogger`, anything that relies on it has to be reworked. * Most of the internal API of the library is now exported, so you can directly use functionality like `color` through `colorrette` etc. without bringing your own library. * *DefaultRenderer*, *VerboseRenderer* and *SimpleRenderer* now use [pluggable components](/renderer/logger.html#presets) for timestamps and task time parsing. * *DefaultRenderer* options `collapse` which corresponded whether the subtasks would be collapsed or not is renamed to `collapseSubtasks` for consistency. * *DefaultRenderer* option `suffixSkips` now has the default value of `false` where it would not have `[SKIPPED]` at the end of the task. * *VerboseRenderer* no longer has the field `logEmptyTitle` and skips tasks without titles instead of logging them as `'Task without title.'`. * *TestRenderer* is encouraged to be used for testing purposes instead of *VerboseRenderer*. * [ListrTaskState](/api/listr2/enumerations/enumeration.ListrTaskState.html) `PENDING` has been renamed to `STARTED` and [ListrEventType](/api/listr2/enumerations/enumeration.ListrTaskEventType.html) `DATA` has been renamed to `OUTPUT` for consistency. * [ListrTaskRetry](/api/listr2/interfaces/interface.ListrTaskRetry.html) which is the output of the `task.isRetrying()` now holds the error value in `error` instead of prior `withError`. * The [Listr](/api/listr2/classes/class..Listr.html) now has additional options to `forceColor` and `forceTTY` since this was a complaint. * To use up-to-date dependencies, everything development related in the repository has been converted to `esm` and *DefaultRenderer* now imports anything `esm` through dynamical imports. * Prompts in the tasks now have their channel, which might break your custom renderer implementation. --- --- url: https://listr2.kilic.dev/migration/v7.md --- ## Migration ::: warning If you encounter any more of the breaking changes, please feel free to contribute by editing this page. ::: * The repository has been converted to a monorepo and some parts of it has been broken down in to smaller packages. * Since this is still a hybrid module transpiled for `cjs`/`esm` imports, with the new changes for Typescript `v5.0.0`+ should be fixed. * *Listr* options `disableColor` and `forceColor` have been removed since they were not working as intended. Users are encouraged to use underlying environment variables for [colorette](https://www.npmjs.com/package/colorette), which are `FORCE_COLOR` and `NO_COLOR`. * Environment variable `LISTR_DISABLE_COLOR` has been removed in favor of using the underlying library's `NO_COLOR` option instead to have consistency with other libraries. * *Task* `options` property, which is used for per-renderer task options has been deprecated in favor of `rendererOptions` and `fallbackRendererOptions` to define the per-renderer task options. This change has been done to properly inject circular types, as well as, pass task options to fallback renderer. * *Manager* is now its own seperate package that can be installed on demand through `@listr2/manager`. * `task.prompt` has been refactored to support multiple adapters, this will break the existing behavior but will bring the ability to use different prompt libraries. Now `inquirer` is also supported addition to initially supported `enquirer`. Prompts adapters are in their respective packages of `@listr2/prompt-adapter-enquirer`, `@listr2/prompt-adapter-inquirer`. Old form: ```typescript import { Listr } from 'listr2' await new Listr([ { task: async (ctx, task): Promise => { ctx.input = await task.prompt({ type: 'Toggle', message: 'Do you love me?' }) } } ]).run() ``` Can be migrated with ease: ```typescript import { Listr } from 'listr2' import { ListrEnquirerPromptAdapter } from '@listr2/prompt-adapter-enquirer' await new Listr([ { task: async (ctx, task): Promise => { ctx.input = await task.prompt(ListrEnquirerPromptAdapter).run({ type: 'Toggle', message: 'Do you love me?' }) } } ]).run() ``` If you are using it in multiple places like canceling a prompt or asking multiple questions in a prompt, you can assign the adapter to a variable instead. ```typescript import { Listr } from 'listr2' import { ListrEnquirerPromptAdapter } from '@listr2/prompt-adapter-enquirer' await new Listr([ { task: async (ctx, task): Promise => { // create the adapter first const prompt = task.prompt(ListrEnquirerPromptAdapter) ctx.input = await prompt.run({ type: 'Toggle', message: 'Do you love me?' }) ctx.input = await prompt.run({ type: 'Toggle', message: 'And another one?' }) } } ]).run() ``` * Any `enquirer` related exported types have been prefixed with `Enquirer`, e.g. `PromptTypes` became `EnquirerPromptTypes`. * *Listr* option `injectWrapper` has been removed. This was only being used by the prompt system. Now you can inject an `enquirer` instance through the adapter directly. * There is a new `ListrTaskState` which is `PROMPT_FAILED`, that will be emitted whenever a prompt has failed. --- --- url: https://listr2.kilic.dev/migration/v8.md --- ## Migration * The minimum supported `node` version is now `v18.0.0` since older versions are marked as end-of-life. --- --- url: https://listr2.kilic.dev/migration/v9.md --- ## Breaking Changes * Minimum supported `node.js` version is now `v20.0.0` since `v18.0.0` is now EOL. ## Features * \[x] task output trimming * \[x] return writable from `createWritable` * \[x] migrate repository to `eslint` `v9.0.0+` ## Internal * Migrates build system to `(tsdown)[https://tsdown.dev/]`. ## Migration * `createWritable` function in `packages/listr2/src/utils/process-output/writable.ts` now returns the actual `Writable` stream. Prior to this it has been hacked out to simulate `NodeJS.WritableStream`. --- --- url: https://listr2.kilic.dev/migration/v10.md --- ## Breaking Changes * Minimum supported `node.js` version is now `v22.0.0` since `20.0.0` is now EOL. * `enquirer` is now mostly failing on v24 and will only git worse, use the `@listr2/prompt-adapter-enquirer` with caution. ## Features * Uses `[log-update](https://github.com/sindresorhus/log-update)` `v7+` which changes how it handles the terminal rendering, therefore snapshot tests might look different. * Now supports `esm` for module distribution only, `cjs` is no longer bundled but still supported thanks to . --- --- url: https://listr2.kilic.dev/migration/v11.md --- ## Breaking Changes These require action to migrate. Each entry shows what changed and how to update your code. ### `collectErrors` is now a boolean The *Listr* option `collectErrors` no longer accepts `'minimal'` or `'full'` — it is a `boolean`. The `'full'` mode, which deep-cloned the context into every collected error, has been removed. ```typescript // before new Listr(tasks, { collectErrors: 'minimal' }) // after — 'minimal' becomes true; 'full' has no replacement new Listr(tasks, { collectErrors: true }) ``` ### `Listr.errors` is `null` when collection is disabled With the default `collectErrors: false`, `Listr.errors` is now `null` instead of an empty array. An empty array now unambiguously means "collection is enabled and nothing failed". Because the collection is shared from the root of the run, `collectErrors` is effectively root-scoped: enabling it only on a subtask while the root leaves it disabled no longer collects those errors. ```typescript // before — always an array if (listr.errors.length) { /* ... */ } // after — null unless collectErrors is enabled if (listr.errors?.length) { /* ... */ } ``` ### `ListrError.ctx` has been removed `ListrError` no longer clones the context into the error instance. Only the `error`, its `type`, and the `path` of the failed task are collected. Read the live `task` reference if you need more. ```typescript // before const context = listr.errors[0].ctx // after — ctx is gone; the failed task is still available const task = listr.errors[0].task ``` ### `cloneObject` export and `rfdc` dependency removed Both existed only for the removed `'full'` error collection mode. If you imported `cloneObject` from `listr2`, use `structuredClone` (or your own clone) instead. ### `eventemitter3` dropped for Node's built-in `EventEmitter` The internal event handling now uses Node.js's built-in `EventEmitter` from `node:events`. The public API is unchanged; only act if you relied on `listr2` pulling in `eventemitter3` transitively — install it directly in that case. ### `Ctrl+C` now rolls back before exiting Interrupting a run with `Ctrl+C` (`SIGINT`) now runs the `rollback` of any in-flight task that defines one and defers the `exit(127)` until every in-flight rollback has settled, instead of marking the pending tasks as failed and exiting synchronously. If you relied on the previous synchronous fail-and-exit, be aware that rollbacks now run first. ### New `ListrTaskState.CANCELLED` state Tasks stopped by an interruption that do not roll back — and any tasks that had not started — are now marked `CANCELLED` (a `⊘` icon in the built-in renderers) instead of `FAILED`. A `task.isCancelled()` helper is available. If you maintain a **custom renderer** that switches on the task state, handle the new state: ```typescript switch (task.state) { // ...your existing cases case ListrTaskState.CANCELLED: // render the cancelled task break } ``` If you assert against the **test renderer** in your own test suite, the serialized task now carries an `isCancelled` field and interrupted tasks report the `CANCELLED` state — regenerate your snapshots (for example `jest -u`). ## Features * Expose the run's `AbortSignal` on the task wrapper as `task.signal`, so task functions can cooperatively cancel their own asynchronous work like `fetch`, child processes or timers when the run is interrupted. * Add `listr.cancel()` and `task.cancel()` to interrupt a run programmatically without an operating-system signal, rolling back or cancelling the in-flight tasks the same way `Ctrl+C` does. * Reset a task's streamed output by setting `task.output = null`, which clears what the task already streamed to the output bar and bottom bar in the default renderer. * Upgrade `log-update` to `v8` and `cli-truncate` to `v6`, enabling partial/differential rendering in the default renderer — the task list is redrawn with smaller, targeted terminal writes wrapped in synchronized-output markers (`ESC[?2026h`/`ESC[?2026l`) on supporting terminals, reducing flicker. The visible output is unchanged. * Document routing all renderer output to `stderr` to keep `stdout` clean and pipeable. See [Process Output](../renderer/process-output.md). ## Bug Fixes * Fix a memory leak in the default renderer where a finalized task had its output buffer re-created and its listeners re-attached on every render, which exhausted the heap when another task streamed to `task.output` at a high rate over a long run. * Preserve OSC-8 hyperlinks, bells and other formatting in the task output, only stripping the cursor and erase codes that would disrupt the rendering. * Derive the default renderer's wrap width from the configured output stream instead of `process.stdout`, so routing the renderer to `stderr` wraps to the correct width. * `task.output = null` no longer renders the literal string `"null"`. ## Node.js Support The test and build matrix now covers Node.js 22, 24 and 26. The minimum supported version is unchanged at `>=22.13.0`. ::: warning The optional `enquirer` peer dependency is effectively unmaintained, and a `readline` change in Node.js 24+ (`ERR_USE_AFTER_CLOSE`) breaks its teardown in some non-interactive scenarios. Adding Node.js 26 to the test matrix surfaced this. The `enquirer` prompt adapter is expected to keep working on Node.js 22 and 24, but **is no longer guaranteed on Node.js 26 and above** — prefer the [`inquirer`](../task/prompts.md#inquirer) adapter on newer Node.js versions. ::: ## Documentation The documentation site now publishes an LLM-friendly form of the docs — `llms.txt`, `llms-full.txt`, and per-page Markdown — so AI assistants and agents can consume it directly. See [Agents](../repository/llms.md). --- --- url: https://listr2.kilic.dev/format.md --- <<< @../../examples/docs/task/subtasks/overwriting-options.ts{8,23} ::: details Code Example <<< @../../examples/docs/task/subtasks/overwriting-options.ts{8,23,30,45,49} ::: ::: info Example You can find the related examples [here](https://github.com/listr2/listr2/tree/master/examples/subtasks.example.ts). ::: --- --- url: https://listr2.kilic.dev/migration.md --- # {{ $frontmatter.title }} * [v6](./v6.html) * [v7](./v7.html) * [v8](./v8.html) * [v9](./v9.html) * [v10](./v10.html) * [v11](./v11.html)