Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

listr2

Package Overview
Dependencies
Maintainers
1
Versions
235
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

listr2 - npm Package Compare versions

Comparing version 2.1.0-beta.1 to 2.1.0-beta.2

8

CHANGELOG.md

@@ -0,1 +1,9 @@

# [2.1.0-beta.2](https://github.com/cenk1cenk2/listr2/compare/v2.1.0-beta.1...v2.1.0-beta.2) (2020-06-02)
### Features
* **prompt:** make prompt module optional, be more compatible to underlying enqurier ([64cecc1](https://github.com/cenk1cenk2/listr2/commit/64cecc10049f5802a6e7a71071ec698e1226bdc2)), closes [#34](https://github.com/cenk1cenk2/listr2/issues/34)
* **prompt:** use enquirer directly ([b34e9d0](https://github.com/cenk1cenk2/listr2/commit/b34e9d0b2ef9b0cbf723759c5a236eca8ac86af0)), closes [#34](https://github.com/cenk1cenk2/listr2/issues/34)
# [2.1.0-beta.1](https://github.com/cenk1cenk2/listr2/compare/v2.0.4...v2.1.0-beta.1) (2020-05-25)

@@ -2,0 +10,0 @@

4

dist/interfaces/listr.interface.d.ts

@@ -10,3 +10,3 @@ /// <reference types="node" />

import { Listr } from '../index';
import { PromptOptionsType, PromptTypes } from '../utils/prompt.interface';
import { PromptOptions } from '../utils/prompt.interface';
export declare type ListrContext = any;

@@ -60,3 +60,3 @@ export declare type ListrDefaultRendererValue = 'default';

run(ctx?: Ctx, task?: ListrTaskWrapper<Ctx, Renderer>): Promise<void>;
prompt<T = any, P extends PromptTypes = PromptTypes>(type: P, options: PromptOptionsType<P>): Promise<T>;
prompt<T = any>(options: PromptOptions | PromptOptions[]): Promise<T>;
stdout(): NodeJS.WritableStream;

@@ -63,0 +63,0 @@ }

@@ -5,3 +5,3 @@ /// <reference types="node" />

import { Listr } from '../index';
import { PromptOptionsType, PromptTypes } from '../utils/prompt.interface';
import { PromptOptions } from '../utils/prompt.interface';
export declare class TaskWrapper<Ctx, Renderer extends ListrRendererFactory> implements ListrTaskWrapper<Ctx, Renderer> {

@@ -19,5 +19,5 @@ task: Task<Ctx, ListrRendererFactory>;

skip(message: string): void;
prompt<T = any, P extends PromptTypes = PromptTypes>(type: P, options: PromptOptionsType<P>): Promise<T>;
prompt<T = any>(options: PromptOptions | PromptOptions[]): Promise<T>;
stdout(): NodeJS.WriteStream & NodeJS.WritableStream;
run(ctx: Ctx): Promise<void>;
}

@@ -67,6 +67,11 @@ "use strict";

}
async prompt(type, options) {
async prompt(options) {
this.task.prompt = true;
Object.assign(options, { stdout: this.stdout() });
return prompt_1.createPrompt.bind(this)(type, options);
const response = await prompt_1.createPrompt.bind(this)(options);
if (Object.keys(response).length === 1) {
return response.default;
}
else {
return response;
}
}

@@ -73,0 +78,0 @@ stdout() {

@@ -147,3 +147,3 @@ "use strict";

if (this.prompt instanceof listr_interface_1.PromptError) {
error = new Error('Cancelled the prompt.');
error = new Error(this.prompt.message);
}

@@ -150,0 +150,0 @@ if (error instanceof listr_interface_1.ListrError) {

@@ -1,6 +0,2 @@

import { Prompt } from 'enquirer';
import { PromptOptionsType, PromptSettings, PromptTypes } from './prompt.interface';
export declare function newPrompt<T extends PromptTypes>(type: T, options: PromptOptionsType<T>): Prompt;
declare type PromptClass = new (options: any) => Prompt;
export declare function createPrompt<T extends PromptTypes>(type: T | PromptClass, options: PromptOptionsType<T>, settings?: PromptSettings): Promise<any>;
export {};
import { PromptOptions, PromptSettings } from './prompt.interface';
export declare function createPrompt(options: PromptOptions | PromptOptions[], settings?: PromptSettings): Promise<any>;
/// <reference types="node" />
import Enquirer from 'enquirer';
import { PromptError } from '../interfaces/listr.interface';
export declare type PromptOptions = ArrayPromptOptions | BooleanPromptOptions | StringPromptOptions | NumberPromptOptions | SnippetPromptOptions | SortPromptOptions | BasePromptOptions;
export declare type PromptOptions = Unionize<{
[K in PromptTypes]-?: {
type: K;
} & PromptOptionsType<K>;
}>;
export declare type Unionize<T extends object> = {
[P in keyof T]: T[P];
}[keyof T];
interface BasePromptOptions {

@@ -63,2 +70,6 @@ name?: string | (() => string);

}
interface SurveyPromptOptions extends ArrayPromptOptions {
scale: BasePromptOptions[];
margin: [number, number, number, number];
}
interface QuizPromptOptions extends ArrayPromptOptions {

@@ -72,3 +83,3 @@ correctChoice: number;

export declare type PromptTypes = 'AutoComplete' | 'BasicAuth' | 'Confirm' | 'Editable' | 'Form' | 'Input' | 'Invisible' | 'List' | 'MultiSelect' | 'Numeral' | 'Password' | 'Quiz' | 'Scale' | 'Select' | 'Snippet' | 'Sort' | 'Survey' | 'Text' | 'Toggle';
export declare type PromptOptionsType<T> = T extends 'AutoComplete' ? ArrayPromptOptions : T extends 'BasicAuth' ? StringPromptOptions : T extends 'Confirm' ? BooleanPromptOptions : T extends 'Editable' ? ArrayPromptOptions : T extends 'Form' ? ArrayPromptOptions : T extends 'Input' ? StringPromptOptions : T extends 'Invisible' ? StringPromptOptions : T extends 'List' ? ArrayPromptOptions : T extends 'MultiSelect' ? ArrayPromptOptions : T extends 'Numeral' ? NumberPromptOptions : T extends 'Password' ? StringPromptOptions : T extends 'Quiz' ? QuizPromptOptions : T extends 'Scale' ? ScalePromptOptions : T extends 'Select' ? ArrayPromptOptions : T extends 'Snippet' ? SnippetPromptOptions : T extends 'Sort' ? SortPromptOptions : T extends 'Survey' ? ArrayPromptOptions : T extends 'Text' ? StringPromptOptions : T extends 'Toggle' ? TogglePromptOptions : any;
export declare type PromptOptionsType<T> = T extends 'AutoComplete' ? ArrayPromptOptions : T extends 'BasicAuth' ? StringPromptOptions : T extends 'Confirm' ? BooleanPromptOptions : T extends 'Editable' ? ArrayPromptOptions : T extends 'Form' ? ArrayPromptOptions : T extends 'Input' ? StringPromptOptions : T extends 'Invisible' ? StringPromptOptions : T extends 'List' ? ArrayPromptOptions : T extends 'MultiSelect' ? ArrayPromptOptions : T extends 'Numeral' ? NumberPromptOptions : T extends 'Password' ? StringPromptOptions : T extends 'Quiz' ? QuizPromptOptions : T extends 'Scale' ? ScalePromptOptions : T extends 'Select' ? ArrayPromptOptions : T extends 'Snippet' ? SnippetPromptOptions : T extends 'Sort' ? SortPromptOptions : T extends 'Survey' ? SurveyPromptOptions : T extends 'Text' ? StringPromptOptions : T extends 'Toggle' ? TogglePromptOptions : T extends Enquirer.Prompt ? any : any;
export interface PromptSettings {

@@ -75,0 +86,0 @@ error?: boolean;

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPrompt = exports.newPrompt = void 0;
const prompts_1 = require("enquirer/lib/prompts");
exports.createPrompt = void 0;
const listr_interface_1 = require("../interfaces/listr.interface");
const task_wrapper_1 = require("../lib/task-wrapper");
function newPrompt(type, options) {
let prompt;
switch (type.toString().toLocaleLowerCase()) {
case 'autocomplete':
prompt = new prompts_1.AutoComplete(options);
break;
case 'basicauth':
prompt = new prompts_1.BasicAuth(options);
break;
case 'confirm':
prompt = new prompts_1.Confirm(options);
break;
case 'editable':
prompt = new prompts_1.Editable(options);
break;
case 'form':
prompt = new prompts_1.Form(options);
break;
case 'input':
prompt = new prompts_1.Input(options);
break;
case 'invisible':
prompt = new prompts_1.Invisible(options);
break;
case 'list':
prompt = new prompts_1.List(options);
break;
case 'multiselect':
prompt = new prompts_1.MultiSelect(options);
break;
case 'numeral':
prompt = new prompts_1.Numeral(options);
break;
case 'password':
prompt = new prompts_1.Password(options);
break;
case 'quiz':
prompt = new prompts_1.Quiz(options);
break;
case 'scale':
prompt = new prompts_1.Scale(options);
break;
case 'select':
prompt = new prompts_1.Select(options);
break;
case 'snippet':
prompt = new prompts_1.Snippet(options);
break;
case 'sort':
prompt = new prompts_1.Sort(options);
break;
case 'survey':
prompt = new prompts_1.Survey(options);
break;
case 'text':
prompt = new prompts_1.Text(options);
break;
case 'toggle':
prompt = new prompts_1.Toggle(options);
break;
default:
throw new listr_interface_1.ListrError('No prompt type this was not supposed to happen.');
}
return prompt;
}
exports.newPrompt = newPrompt;
function isPromptClass(SomeClass) {
try {
new SomeClass({});
return true;
}
catch {
return false;
}
}
function createPrompt(type, options, settings) {
async function createPrompt(options, settings) {
let cancelCallback;

@@ -90,8 +33,20 @@ if (settings === null || settings === void 0 ? void 0 : settings.cancelCallback) {

}
if (isPromptClass(type)) {
return new type(options).on('cancel', cancelCallback.bind(this)).run();
if (!Array.isArray(options)) {
options = options = [{ ...options, name: 'default' }];
}
else {
return newPrompt(type, options).on('cancel', cancelCallback.bind(this)).run();
else if (options.length === 1) {
options = options.reduce((o, option) => {
return [...o, Object.assign(option, { name: 'default' })];
}, []);
}
options = options.reduce((o, option) => {
return [...o, Object.assign(option, { stdout: this.stdout(), onCancel: cancelCallback.bind(this, settings) })];
}, []);
try {
const { prompt } = (await Promise.resolve().then(() => __importStar(require('enquirer')))).default;
return prompt(options);
}
catch (e) {
this.task.prompt = new listr_interface_1.PromptError('Enquirer is a peer dependency that must be installed seperately.');
}
}

@@ -102,3 +57,3 @@ exports.createPrompt = createPrompt;

if ((settings === null || settings === void 0 ? void 0 : settings.error) === true) {
throw new listr_interface_1.PromptError(errorMsg);
throw new Error(errorMsg);
}

@@ -105,0 +60,0 @@ else if (this instanceof task_wrapper_1.TaskWrapper) {

{
"name": "listr2",
"version": "2.1.0-beta.1",
"version": "2.1.0-beta.2",
"description": "Terminal task list reborn! Create beautiful CLI interfaces via easy and logical to implement task lists that feel alive and interactive.",

@@ -60,3 +60,2 @@ "license": "MIT",

"cli-truncate": "^2.1.0",
"enquirer": "^2.3.5",
"figures": "^3.2.0",

@@ -91,2 +90,5 @@ "indent-string": "^4.0.0",

},
"optionalDependencies": {
"enquirer": ">= 2.3.0 < 3"
},
"config": {

@@ -93,0 +95,0 @@ "commitizen": {

@@ -1,8 +0,4 @@

Listr2
====
# Listr2
[![Build Status](https://cd.ev.kilic.dev/api/badges/cenk1cenk2/listr2/status.svg)](https://cd.ev.kilic.dev/cenk1cenk2/listr2)
[![Version](https://img.shields.io/npm/v/listr2.svg)](https://npmjs.org/package/listr2)
[![Downloads/week](https://img.shields.io/npm/dw/listr2.svg)](https://npmjs.org/package/listr2)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
[![Build Status](https://cd.ev.kilic.dev/api/badges/cenk1cenk2/listr2/status.svg)](https://cd.ev.kilic.dev/cenk1cenk2/listr2) [![Version](https://img.shields.io/npm/v/listr2.svg)](https://npmjs.org/package/listr2) [![Downloads/week](https://img.shields.io/npm/dw/listr2.svg)](https://npmjs.org/package/listr2) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)

@@ -14,5 +10,6 @@ **Create beautiful CLI interfaces via easy and logical to implement task lists that feel alive and interactive.**

![Demo](./demo/demo.gif)
> **It breaks backward compatibility with [Listr](https://github.com/SamVerschueren/listr) after v1.3.12, albeit refactoring requires only moving renderer options to their own key, concerning the [conversation on the original repository](https://github.com/SamVerschueren/listr/issues/143#issuecomment-623094930).** You can find the README of compatible version [here](https://github.com/cenk1cenk2/listr2/tree/84ff9c70ba4aab16106d1e7114453ac5e0351ec0). Keep in mind that it will not get further bug fixes.
* [Changelog](./CHANGELOG.md)
- [Changelog](./CHANGELOG.md)

@@ -29,5 +26,7 @@ <!-- toc -->

- [Get User Input](#get-user-input)
- [Attention: Enquirer is a optional dependency. Please install it first.](#attention-enquirer-is-a-optional-dependency-please-install-it-first)
- [Create A Prompt](#create-a-prompt)
- [Single Prompt](#single-prompt)
- [Multiple Prompts](#multiple-prompts)
- [Use an Custom Prompt](#use-an-custom-prompt)
- [Use Enquirer in Your Project Without Explicitly Installing It](#use-enquirer-in-your-project-without-explicitly-installing-it)
- [Enable a Task](#enable-a-task)

@@ -62,2 +61,3 @@ - [Skip a Task](#skip-a-task)

## Install
```bash

@@ -76,2 +76,3 @@ # Install the latest supported version

## Create A New Listr
Create a new task list. It will returns a Listr class.

@@ -86,8 +87,14 @@

const tasks = new Listr<Ctx>([
const tasks = new Listr<Ctx>(
[
/* tasks */
], { /* options */ })
],
{
/* options */
}
)
```
Then you can run this task lists as a async function and it will return the context that is used.
```typescript

@@ -104,2 +111,3 @@ try {

### Tasks
```typescript

@@ -126,2 +134,3 @@ export interface ListrTask<Ctx, Renderer extends ListrRendererFactory> {

### Options
```typescript

@@ -156,2 +165,3 @@ export interface ListrOptions<Ctx = ListrContext> {

## The Concept of Context
Context is the variables that are 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.

@@ -166,14 +176,21 @@

If an outside variable wants to be injected inside the Listr itself it can be done in two ways.
- Injecting it as an option.
```typescript
const ctx: Ctx = {}
const tasks = new Listr<Ctx>([
const tasks = new Listr<Ctx>(
[
/* tasks */
], { ctx })
],
{ ctx }
)
```
- Injecting it at runtime.
```typescript
try {
await tasks.run({ctx})
await tasks.run({ ctx })
} catch (e) {

@@ -184,24 +201,28 @@ console.error(e)

## General Usage
### Subtasks
Any task can return a new Listr. But rather than calling it as `new Listr` to get the full autocompeletion features depending on the parent task's selected renderer, it is a better idea to call it through the `Task` itself by `task.newListr()`.
*Please refer to [examples section](examples/subtasks.example.ts) for more detailed and further examples.*
_Please refer to [examples section](examples/subtasks.example.ts) for more detailed and further examples._
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{
title: 'This task will execute.',
task: (ctx, task): Listr => task.newListr([
{
title: 'This is a subtask.',
task: async (): Promise<void> => {
await delay(3000)
task: (ctx, task): Listr =>
task.newListr([
{
title: 'This is a subtask.',
task: async (): Promise<void> => {
await delay(3000)
}
}
}
])
])
}
], { concurrent: false })
],
{ concurrent: false }
)
```

@@ -212,20 +233,26 @@

This includes renderer options as well as Listr options like `exitOnError`, `concurrent` to be set on a per subtask basis independent of the parent task, while it will always use the most adjacent setting.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{
title: 'This task will execute.',
task: (ctx, task): Listr => task.newListr([
{
title: 'This is a subtask.',
task: async (): Promise<void> => {
await delay(3000)
}
},
{
title: 'This is an another subtask.',
task: async (): Promise<void> => {
await delay(2000)
}
}
], { concurrent: true, rendererOptions: { collapse: true } })
task: (ctx, task): Listr =>
task.newListr(
[
{
title: 'This is a subtask.',
task: async (): Promise<void> => {
await delay(3000)
}
},
{
title: 'This is an another subtask.',
task: async (): Promise<void> => {
await delay(2000)
}
}
],
{ concurrent: true, rendererOptions: { collapse: true } }
)
},

@@ -235,25 +262,34 @@

title: 'This task will execute.',
task: (ctx, task): Listr => task.newListr([
{
title: 'This is a subtask.',
task: async (): Promise<void> => {
await delay(3000)
}
},
{
title: 'This is an another subtask.',
task: async (): Promise<void> => {
await delay(2000)
}
}
], { concurrent: true, rendererOptions: { collapse: false } })
task: (ctx, task): Listr =>
task.newListr(
[
{
title: 'This is a subtask.',
task: async (): Promise<void> => {
await delay(3000)
}
},
{
title: 'This is an another subtask.',
task: async (): Promise<void> => {
await delay(2000)
}
}
],
{ concurrent: true, rendererOptions: { collapse: false } }
)
}
], { concurrent: false })
],
{ concurrent: false }
)
```
*Please refer to [Throw Errors Section](#Throw-Errors) for more detailed and further examples on how to handle silently failing errors.*
_Please refer to [Throw Errors Section](#Throw-Errors) for more detailed and further examples on how to handle silently failing errors._
### Get User Input
The input module uses the beautiful [enquirer](https://www.npmjs.com/package/enquirer).
> ## Attention: Enquirer is a optional dependency. Please install it first.
So with running a `task.prompt` function, you can get access to any [enquirer](https://www.npmjs.com/package/enquirer) default prompts as well as using a custom enquirer prompt.

@@ -269,5 +305,6 @@

*Please refer to [examples section](examples/get-user-input.example.ts) for more detailed and further examples.*
_Please refer to [examples section](examples/get-user-input.example.ts) for more detailed and further examples._
#### Create A Prompt
To access the prompts just utilize the `task.prompt` jumper function. The first argument takes in one of the default [enquirer](https://www.npmjs.com/package/enquirer) prompts as a string or you can also pass in a custom [enquirer](https://www.npmjs.com/package/enquirer) prompt class as well, while the second argument is the options for the given prompt.

@@ -277,63 +314,94 @@

*Please note that I rewrote the types for enquirer, since some of them was failing for me. So it may have a chance of having some mistakes in it since I usually do not use all of them.*
_Please note that I rewrote the types for enquirer, since some of them was failing for me. So it may have a chance of having some mistakes in it since I usually do not use all of them._
**>v2.1.0, defining the prompt style has been changed a little..**
##### Single Prompt
```typescript
new Listr<Ctx>([
{
task: async (ctx, task): Promise<boolean> => ctx.input = await task.prompt<boolean>('Toggle', { message: 'Do you love me?' })
},
{
title: 'This task will get your input.',
task: async (ctx, task): Promise<void> => {
ctx.input = await task.prompt<boolean>('Toggle', { message: 'Do you love me?' })
// do something
if (ctx.input === false) {
throw new Error(':/')
new Listr<Ctx>(
[
{
task: async (ctx, task): Promise<boolean> => (ctx.input = await task.prompt<boolean>({ type: 'Toggle', message: 'Do you love me?' }))
},
{
title: 'This task will get your input.',
task: async (ctx, task): Promise<void> => {
ctx.input = await task.prompt<boolean>({ type: 'Toggle', message: 'Do you love me?' })
// do something
if (ctx.input === false) {
throw new Error(':/')
}
}
}
}
], { concurrent: false })
],
{ concurrent: false }
)
```
#### Use an Custom Prompt
You can either use a custom prompt out of the npm registry or custom-created one as long as it works with [enquirer](https://www.npmjs.com/package/enquirer), it will work expectedly. Instead of passing in the prompt name use the not-generated class.
##### Multiple Prompts
**Important: If you want to pass in an array of prompts, becareful that you should name them. This gets handled to return the single value directly in non-array variation of prompts by internal trickery.**
```typescript
new Listr<Ctx>([
{
title: 'Custom prompt',
task: async (ctx, task): Promise<void> => {
ctx.testInput = await task.prompt(EditorPrompt, {
message: 'Write something in this enquirer custom prompt.',
initial: 'Start writing!',
validate: (response): boolean | string => {
// i do declare you valid!
return true
}
})
}
}
], { concurrent: false })
new Listr<Ctx>(
[
{
title: 'This task will get your input.',
task: async (ctx, task): Promise<void> => {
ctx.input = await task.prompt<{ first: boolean; second: boolean }>([
{ type: 'Toggle', name: 'first', message: 'Do you love me?' },
{ type: 'Toggle', name: 'second', message: 'Do you love me?' }
])
// do something
if (ctx.input.first === false) {
logger.log('oh okay')
}
if (ctx.input.second === false) {
throw new Error('You did not had to tell me for the second time')
}
}
}
],
{ concurrent: false }
)
```
#### Use Enquirer in Your Project Without Explicitly Installing It
**I am planning to move enquirer to peer dependencies as an optional install, so this will likely go away in the near future.**
#### Use an Custom Prompt
If you want to directly run it, and do not want to create a jumper function you can do as follows.
You can either use a custom prompt out of the npm registry or custom-created one as long as it works with [enquirer](https://www.npmjs.com/package/enquirer), it will work expectedly. Instead of passing in the prompt name use the not-generated class.
```typescript
import { createPrompt } from 'listr2'
await createPrompt('Input', { message: 'Hey what is that?' }, { cancelCallback: () => { throw new Error('You made me mad now. Just should have answered me!') }})
new Listr<Ctx>(
[
{
title: 'Custom prompt',
task: async (ctx, task): Promise<void> => {
ctx.testInput = await task.prompt({
type: EditorPrompt,
message: 'Write something in this enquirer custom prompt.',
initial: 'Start writing!',
validate: (response): boolean | string => {
// i do declare you valid!
return true
}
})
}
}
],
{ concurrent: false }
)
```
### Enable a Task
Tasks can be enabled depending on the variables programmatically. This enables to skip them depending on the context. Not enabled tasks will never show up in the default renderer, but when or if they get enabled they will magically appear.
*Please pay attention to asynchronous operation while designing a context enabled task list since it does not await for any variable in the context.*
_Please pay attention to asynchronous operation while designing a context enabled task list since it does not await for any variable in the context._
*Please refer to [examples section](examples/task-enable.example.ts) for more detailed and further examples.*
_Please refer to [examples section](examples/task-enable.example.ts) for more detailed and further examples._
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -351,6 +419,9 @@ title: 'This task will execute.',

}
], { concurrent: false })
],
{ concurrent: false }
)
```
### Skip a Task
Skip is more or less the same with enable when used at `Task` level. But the main difference is it will always render the given task. If it is skipped it renders it as skipped.

@@ -360,9 +431,11 @@

*Please pay attention to asynchronous operation while designing a context skipped task list since it does not await for any variable in the context.*
_Please pay attention to asynchronous operation while designing a context skipped task list since it does not await for any variable in the context._
*Please refer to [examples section](examples/task-skip.example.ts) for more detailed and further examples.*
_Please refer to [examples section](examples/task-skip.example.ts) for more detailed and further examples._
Inside the task itself after some logic is done.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -374,8 +447,12 @@ title: 'This task will execute.',

}
], { concurrent: false })
],
{ concurrent: false }
)
```
Through the task wrapper.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -393,3 +470,5 @@ title: 'This task will execute.',

}
], { concurrent: false })
],
{ concurrent: false }
)
```

@@ -400,2 +479,3 @@

### Show Output
Showing output from a task can be done in various ways.

@@ -406,3 +486,4 @@

```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -415,11 +496,16 @@ title: 'This task will execute.',

}
], { concurrent: false })
],
{ concurrent: false }
)
```
*Please refer to [examples section](examples/show-output.example.ts) for more detailed and further examples.*
_Please refer to [examples section](examples/show-output.example.ts) for more detailed and further examples._
#### Utilizing the Task Itself
This will show the output in a small bar that can only show the last output from the task.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -438,9 +524,13 @@ title: 'This task will execute.',

}
], { concurrent: false })
],
{ concurrent: false }
)
```
#### Utilizing the Bottom Bar
If task output to the bottom bar is selected, it will create a bar at the end of the tasks leaving one line return space in between. The bottom bar can only be used in the default renderer.
Items count that is desired to be showed in the bottom bar can be set through `Task` option `bottomBar`.
- If set to `true` it will only show the last output from the task.

@@ -451,3 +541,4 @@ - If it is set to a number it will limit the output to that number.

```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -469,9 +560,14 @@ title: 'This task will execute.',

}
], { concurrent: false })
],
{ concurrent: false }
)
```
#### Utilizing an Observable or Stream
Since observables and streams are supported they can also be used to generate output.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -494,6 +590,9 @@ // Task can also handle and observable

}
], { concurrent: false })
],
{ concurrent: false }
)
```
#### Passing the Output Through as a Stream
Since `process.stdout` method is controlled by `log-update` to create a refreshing interface, for anything else that might need to output data and can use `Writeable` streams, `task.stdout()` will create a new punch-hole to redirect all the write requests to `task.output`. This is esspecially benefical for external libraries like `enquirer`, which is already integrated or something like `ink`.

@@ -503,3 +602,3 @@

*This unfortunetly relies on cleaning all ANSI escape charachters, since currently I do not find a good way to sandbox them inside `log-update` which utilizes the cursor position by itself. So use this with caution, because it will only render the last chunk in a stream as well as cleaning up all the ANSI escape charachters except for styles.*
_This unfortunetly relies on cleaning all ANSI escape charachters, since currently I do not find a good way to sandbox them inside `log-update` which utilizes the cursor position by itself. So use this with caution, because it will only render the last chunk in a stream as well as cleaning up all the ANSI escape charachters except for styles._

@@ -517,38 +616,41 @@ ```typescript

async function main (): Promise<void> {
async function main(): Promise<void> {
let task: Listr<Ctx, 'default'>
task = new Listr<Ctx, 'default'>([
{
title: 'This task will show INK as output.',
task: async (ctx, task): Promise<any> => {
const Counter = () => {
const [ counter, setCounter ] = useState(0)
task = new Listr<Ctx, 'default'>(
[
{
title: 'This task will show INK as output.',
task: async (ctx, task): Promise<any> => {
const Counter = () => {
const [counter, setCounter] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
setCounter((previousCounter) => previousCounter + 1)
}, 100)
useEffect(() => {
const timer = setInterval(() => {
setCounter((previousCounter) => previousCounter + 1)
}, 100)
return (): void => {
clearInterval(timer)
}
}, [])
return (): void => {
clearInterval(timer)
}
}, [])
return <Color green>{counter} tests passed</Color>
}
return <Color green>{counter} tests passed</Color>
}
const { unmount, waitUntilExit } = render(<Counter />, task.stdout())
const { unmount, waitUntilExit } = render(<Counter />, task.stdout())
setTimeout(unmount, 2000)
setTimeout(unmount, 2000)
return waitUntilExit()
},
}
], { concurrent: false })
return waitUntilExit()
}
}
],
{ concurrent: false }
)
try {
try {
const context = await task.run()
console.log(`Context: ${JSON.stringify(context)}`)
} catch(e) {
} catch (e) {
console.error(e)

@@ -562,7 +664,10 @@ }

### Throw Errors
You can throw errors out of the tasks to show they are insuccessful. While this gives a visual output on the terminal, it also handles how to handle tasks that are failed. The default behaviour is any of the tasks have failed, it will deem itself as unsuccessful and exit. This behaviour can be changed with `exitOnError` option.
- Throw out an error in serial execution mode will cause all of the upcoming tasks to be never executed.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -581,8 +686,12 @@ title: 'This task will fail.',

}
], { concurrent: false })
],
{ concurrent: false }
)
```
- Throwing out an error while execution in parallel mode will immediately stop all the actions.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -601,8 +710,12 @@ title: 'This task will fail.',

}
], { concurrent: true })
],
{ concurrent: true }
)
```
- Default behavior can be changed with `exitOnError` option.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{

@@ -621,24 +734,32 @@ title: 'This task will fail.',

}
], { concurrent: false, exitOnError: false })
],
{ concurrent: false, exitOnError: false }
)
```
- `exitOnError` is subtask based so you can change it on the fly for given set of subtasks.
```typescript
new Listr<Ctx>([
new Listr<Ctx>(
[
{
title: 'This task will execute and not quit on errors.',
task: (ctx, task): Listr => task.newListr([
{
title: 'This is a subtask.',
task: async (): Promise<void> => {
throw new Error('I have failed [0]')
}
},
{
title: 'This is yet an another subtask and it will run.',
task: async (ctx, task): Promise<void> => {
task.title = 'I have succeeded.'
}
}
], { exitOnError: false })
task: (ctx, task): Listr =>
task.newListr(
[
{
title: 'This is a subtask.',
task: async (): Promise<void> => {
throw new Error('I have failed [0]')
}
},
{
title: 'This is yet an another subtask and it will run.',
task: async (ctx, task): Promise<void> => {
task.title = 'I have succeeded.'
}
}
],
{ exitOnError: false }
)
},

@@ -651,10 +772,13 @@ {

}
], { concurrent: false, exitOnError: true })
],
{ concurrent: false, exitOnError: true }
)
```
- The error that makes the application to quit will be thrown out from the async function.
```typescript
try {
const context = await task.run()
} catch(e) {
} catch (e) {
logger.fail(e)

@@ -666,2 +790,3 @@ // which will show the last error

- Access all of the errors that makes the application quit or not through `task.err` which is an array of all the errors encountered.
```typescript

@@ -674,2 +799,3 @@ const task = new Listr(...)

- ListrError which is thrown out of `task.err´ in prior example is in the structure of
```typescript

@@ -682,10 +808,13 @@ public message: string

## Task Manager
Task manager is a great way to create a custom-tailored Listr class once and then utilize it more than once.
*Please refer to [examples section](examples/manager.example.ts) for more detailed and further examples.*
_Please refer to [examples section](examples/manager.example.ts) for more detailed and further examples._
### Basic Use-Case Scenerio
- Create something like a manager factory with your own default settings
```typescript
export function TaskManagerFactory<T = any> (override?: ListrBaseClassOptions): Manager<T> {
export function TaskManagerFactory<T = any>(override?: ListrBaseClassOptions): Manager<T> {
const myDefaultOptions: ListrBaseClassOptions = {

@@ -704,2 +833,3 @@ concurrent: false,

- Create your class that benefits from manager
```typescript

@@ -709,7 +839,7 @@ export class MyMainClass {

constructor () {
constructor() {
this.run()
}
private async run (): Promise<void> {
private async run(): Promise<void> {
// CODE WILL GO HERE IN THIS EXAMPLE

@@ -721,20 +851,25 @@ }

- Add multiple set of subtasks with their own options
```typescript
this.tasks.add([
{
title: 'A task running manager [0]',
task: async (): Promise<void> => {
throw new Error('Do not dare to run the second task.')
}
},
{
title: 'This will never run first one failed.',
task: async (): Promise<void> => {
await delay(2000)
}
this.tasks.add(
[
{
title: 'A task running manager [0]',
task: async (): Promise<void> => {
throw new Error('Do not dare to run the second task.')
}
], { exitOnError: true, concurrent: false })
},
{
title: 'This will never run first one failed.',
task: async (): Promise<void> => {
await delay(2000)
}
}
],
{ exitOnError: true, concurrent: false }
)
```
- Run the tasks. Running the tasks will clear the pending queue so you can go ahead and add more new tasks!
```typescript

@@ -749,7 +884,23 @@ try {

### More Functionality
- Indenting tasks, to change options like `concurrency`, `exitOnError` and so on.
```typescript
this.tasks.add([
this.tasks.add(
[
{
title: 'Some task that will run in sequential execution mode. [0]',
task: async (): Promise<void> => {
await delay(2000)
}
},
{
title: 'Some task that will run in sequential execution mode. [1]',
task: async (): Promise<void> => {
await delay(2000)
}
},
this.tasks.indent([
{
title: 'Some task that will run in sequential execution mode. [0]',
title: 'This will run in parallel. [0]',
task: async (): Promise<void> => {

@@ -760,25 +911,15 @@ await delay(2000)

{
title: 'Some task that will run in sequential execution mode. [1]',
title: 'This will run in parallel. [1]',
task: async (): Promise<void> => {
await delay(2000)
}
},
this.tasks.indent([
{
title: 'This will run in parallel. [0]',
task: async (): Promise<void> => {
await delay(2000)
}
},
{
title: 'This will run in parallel. [1]',
task: async (): Promise<void> => {
await delay(2000)
}
}
])
], { concurrent: true })
}
])
],
{ concurrent: true }
)
```
- Run a Task Directly, which will use the defaults settings you set in the manager.
```typescript

@@ -796,2 +937,3 @@ await this.tasks.run([

- Access the errors of the last task as in the Listr.
```typescript

@@ -811,12 +953,15 @@ await this.tasks.run([

- Access base Listr class directly, this will use the default Listr settings and just a mere jumper function for omiting the need the import the Listr class when using manager.
```typescript
try {
await this.tasks.newListr([
{
title: 'I will die now, goodbye my freinds.',
task: (): void => {
throw new Error('This will not crash since exitOnError is set to false eventhough default setting in Listr is false.')
await this.tasks
.newListr([
{
title: 'I will die now, goodbye my freinds.',
task: (): void => {
throw new Error('This will not crash since exitOnError is set to false eventhough default setting in Listr is false.')
}
}
}
]).run()
])
.run()
} catch (e) {

@@ -828,21 +973,25 @@ this.logger.fail(e)

- Get Task Runtime, and tailor it as your own
```typescript
await this.tasks.run([
{
task: async (ctx): Promise<void> => {
// start the clock
ctx.runTime = Date.now()
}
},
{
title: 'Running',
task: async (): Promise<void> => {
await delay(1000)
}
},
{
task: async (ctx, task): Promise<string> => task.title = this.tasks.getRuntime(ctx.runTime)
await this.tasks.run(
[
{
task: async (ctx): Promise<void> => {
// start the clock
ctx.runTime = Date.now()
}
], { concurrent: false })
// outputs: "1.001s" in seconds
},
{
title: 'Running',
task: async (): Promise<void> => {
await delay(1000)
}
},
{
task: async (ctx, task): Promise<string> => (task.title = this.tasks.getRuntime(ctx.runTime))
}
],
{ concurrent: false }
)
// outputs: "1.001s" in seconds
```

@@ -853,2 +1002,3 @@

### Tasks Without Titles
For default renderer, all tasks that do not have titles will be hidden from the visual task list and executed behind. You can still set `task.title` inside the task wrapper programmatically afterward, if you so desire.

@@ -861,2 +1011,3 @@

### Signal Interrupt
When the interrupt signal is caught Listr will render for one last time therefore you will always have clean exits. This registers event listener `process.on('exit')`, therefore it will use a bit more of CPU cycles depending on the Listr task itself.

@@ -867,2 +1018,3 @@

## Testing
For testing purposes you can use the verbose renderer by passing in the option of `{ renderer: 'verbose' }`. This will generate text-based and linear output which is required for testing.

@@ -874,12 +1026,13 @@

On | Output
---------|----------
Task Started | \[STARTED\] ${TASK TITLE ?? 'Task without title.'}
Task Failure | \[FAILED\] ${TASK TITLE ?? 'Task without title.'}
Task Skipped | \[SKIPPED\] ${SKIP MESSAGE ?? TASK TITLE ?? 'Task without title.'}
Task Successful | \[SUCCESS\] ${TASK TITLE ?? 'Task without title.'}
Spit Output | \[DATA\] ${TASK OUTPUT}
Title Change | \[TITLE\] ${NEW TITLE}
| On | Output |
| --------------- | ------------------------------------------------------------------- |
| Task Started | \[STARTED\] \${TASK TITLE ?? 'Task without title.'} |
| Task Failure | \[FAILED\] \${TASK TITLE ?? 'Task without title.'} |
| Task Skipped | \[SKIPPED\] \${SKIP MESSAGE ?? TASK TITLE ?? 'Task without title.'} |
| Task Successful | \[SUCCESS\] \${TASK TITLE ?? 'Task without title.'} |
| Spit Output | \[DATA\] \${TASK OUTPUT} |
| Title Change | \[TITLE\] \${NEW TITLE} |
## Default Renderers
There are three main renderers which are 'default', 'verbose' and 'silent'. Default renderer is the one that can be seen in the demo, which is an updating renderer. But if the environment advirteses itself as non-tty it will fallback to the verbose renderer automatically. Verbose renderer is a text based renderer. It uses the silent renderer for the subtasks since the parent task already started a renderer. But silent renderer can also be used for processes that wants to have no output but just a task list.

@@ -922,5 +1075,7 @@

## Custom Renderers
Creating a custom renderer with a beautiful interface can be done in one of two ways.
- First create a Listr renderer class.
```typescript

@@ -939,9 +1094,9 @@ /* eslint-disable @typescript-eslint/no-empty-function */

// get tasks to be renderered and options of the renderer from the parent
constructor (public tasks: ListrTaskObject<any, typeof MyAmazingRenderer>[], public options: typeof MyAmazingRenderer['rendererOptions']) {}
constructor(public tasks: ListrTaskObject<any, typeof MyAmazingRenderer>[], public options: typeof MyAmazingRenderer['rendererOptions']) {}
// implement custom logic for render functionality
render (): void {}
render(): void {}
// implement custom logic for end functionality
end (err): void {}
end(err): void {}
}

@@ -972,5 +1127,7 @@ ```

## Render Hooks
Additional to rendering through `task.subscribe` or with a given interval, a renderer can also render on demand via a observable passed through the renderer.
Render hook can be the third optional variable of a given renderer, while using it is always optional.
```typescript

@@ -995,2 +1152,3 @@ constructor (

## Log To A File
Logging to a file can be done utilizing a module like [winston](https://www.npmjs.com/package/winston). This can be obtained through using the verbose renderer and creating a custom logger class that implements `Logger` which is exported from the index.

@@ -1004,5 +1162,4 @@

export class MyLoggerClass implements Logger {
constructor(private options?: LoggerOptions) {}
constructor (private options?: LoggerOptions) {}
/* CUSTOM LOGIC */

@@ -1012,3 +1169,3 @@ /* CUSTOM LOGIC */

public fail (message: string): void {
public fail(message: string): void {
message = this.parseMessage(logLevels.fail, message)

@@ -1018,3 +1175,3 @@ console.error(message)

public skip (message: string): void {
public skip(message: string): void {
message = this.parseMessage(logLevels.skip, message)

@@ -1024,3 +1181,3 @@ console.warn(message)

public success (message: string): void {
public success(message: string): void {
message = this.parseMessage(logLevels.success, message)

@@ -1030,3 +1187,3 @@ console.log(message)

public data (message: string): void {
public data(message: string): void {
message = this.parseMessage(logLevels.data, message)

@@ -1036,3 +1193,3 @@ console.info(message)

public start (message: string): void {
public start(message: string): void {
message = this.parseMessage(logLevels.start, message)

@@ -1042,3 +1199,3 @@ console.log(message)

public title (message: string): void {
public title(message: string): void {
message = this.parseMessage(logLevels.title, message)

@@ -1051,3 +1208,5 @@ console.info(message)

## Migration from Version v1
To migrate from prior versions that are older than v1.3.12, which is advisable due to upcoming potential bug fixes:
- rendererOptions has to be moved to their own key

@@ -1060,2 +1219,3 @@ - some of the types if initiated before assigning a Listr has to be fixed accordingly

## Types
Useful types are exported from the root. It is written with Typescript, so it will work great with any modern IDE/Editor.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc