• Stars
    star
    3,194
  • Rank 14,052 (Top 0.3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 8 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Terminal task list

listr

GitHub Actions Coverage Status

Terminal task list

Install

$ npm install --save listr

Usage

import execa from 'execa';
import Listr from 'listr';

const tasks = new Listr([
	{
		title: 'Git',
		task: () => {
			return new Listr([
				{
					title: 'Checking git status',
					task: () => execa.stdout('git', ['status', '--porcelain']).then(result => {
						if (result !== '') {
							throw new Error('Unclean working tree. Commit or stash changes first.');
						}
					})
				},
				{
					title: 'Checking remote history',
					task: () => execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => {
						if (result !== '0') {
							throw new Error('Remote history differ. Please pull changes.');
						}
					})
				}
			], {concurrent: true});
		}
	},
	{
		title: 'Install package dependencies with Yarn',
		task: (ctx, task) => execa('yarn')
			.catch(() => {
				ctx.yarn = false;

				task.skip('Yarn not available, install it via `npm install -g yarn`');
			})
	},
	{
		title: 'Install package dependencies with npm',
		enabled: ctx => ctx.yarn === false,
		task: () => execa('npm', ['install'])
	},
	{
		title: 'Run tests',
		task: () => execa('npm', ['test'])
	},
	{
		title: 'Publish package',
		task: () => execa('npm', ['publish'])
	}
]);

tasks.run().catch(err => {
	console.error(err);
});

Task

A task can return different values. If a task returns, it means the task was completed successfully. If a task throws an error, the task failed.

const tasks = new Listr([
	{
		title: 'Success',
		task: () => 'Foo'
	},
	{
		title: 'Failure',
		task: () => {
			throw new Error('Bar')
		}
	}
]);

Promises

A task can also be async by returning a Promise. If the promise resolves, the task completed successfully, if it rejects, the task failed.

const tasks = new Listr([
	{
		title: 'Success',
		task: () => Promise.resolve('Foo')
	},
	{
		title: 'Failure',
		task: () => Promise.reject(new Error('Bar'))
	}
]);

Tip: Always reject a promise with some kind of Error object.

Observable

A task can also return an Observable. The thing about observables is that it can emit multiple values and can be used to show the output of the task. Please note that only the last line of the output is rendered.

import {Observable} from 'rxjs';

const tasks = new Listr([
	{
		title: 'Success',
		task: () => {
			return new Observable(observer => {
				observer.next('Foo');

				setTimeout(() => {
					observer.next('Bar');
				}, 2000);

				setTimeout(() => {
					observer.complete();
				}, 4000);
			});
		}
	},
	{
		title: 'Failure',
		task: () => Promise.reject(new Error('Bar'))
	}
]);

You can use the Observable package you feel most comfortable with, like RxJS or zen-observable.

Streams

It's also possible to return a ReadableStream. The stream will be converted to an Observable and handled as such.

import fs from 'fs';
import split from 'split';

const list = new Listr([
	{
		title: 'File',
		task: () => fs.createReadStream('data.txt', 'utf8')
			.pipe(split(/\r?\n/, null, {trailing: false}))
	}
]);

Skipping tasks

Optionally specify a skip function to determine whether a task can be skipped.

  • If the skip function returns a truthy value or a Promise that resolves to a truthy value then the task will be skipped.
  • If the returned value is a string it will be displayed as the reason for skipping the task.
  • If the skip function returns a falsey value or a Promise that resolves to a falsey value then the task will be executed as normal.
  • If the skip function throws or returns a Promise that rejects, the task (and the whole build) will fail.
const tasks = new Listr([
	{
		title: 'Task 1',
		task: () => Promise.resolve('Foo')
	},
	{
		title: 'Can be skipped',
		skip: () => {
			if (Math.random() > 0.5) {
				return 'Reason for skipping';
			}
		},
		task: () => 'Bar'
	},
	{
		title: 'Task 3',
		task: () => Promise.resolve('Bar')
	}
]);

Tip: You can still skip a task while already executing the task function with the task object.

Enabling tasks

By default, every task is enabled which means that every task will be executed. However, it's also possible to provide an enabled function that returns whether the task should be executed or not.

const tasks = new Listr([
	{
		title: 'Install package dependencies with Yarn',
		task: (ctx, task) => execa('yarn')
			.catch(() => {
				ctx.yarn = false;

				task.skip('Yarn not available, install it via `npm install -g yarn`');
			})
	},
	{
		title: 'Install package dependencies with npm',
		enabled: ctx => ctx.yarn === false,
		task: () => execa('npm', ['install'])
	}
]);

In the above example, we try to run yarn first, if that fails we will fall back to npm. However, at first only the Yarn task will be visible. Because we set the yarn flag of the context object to false, the second task will automatically be enabled and will be executed.

Note: This does not work in combination with concurrent tasks.

Context

A context object is passed as argument to every skip and task function. This allows you to create composable tasks and change the behaviour of your task depending on previous results.

const tasks = new Listr([
	{
		title: 'Task 1',
		skip: ctx => ctx.foo === 'bar',
		task: () => Promise.resolve('Foo')
	},
	{
		title: 'Can be skipped',
		skip: () => {
			if (Math.random() > 0.5) {
				return 'Reason for skipping';
			}
		},
		task: ctx => {
			ctx.unicorn = 'rainbow';
		}
	},
	{
		title: 'Task 3',
		task: ctx => Promise.resolve(`${ctx.foo} ${ctx.bar}`)
	}
]);

tasks.run({
	foo: 'bar'
}).then(ctx => {
	console.log(ctx);
	//=> {foo: 'bar', unicorn: 'rainbow'}
});

Task object

A special task object is passed as second argument to the task function. This task object lets you change the title while running your task, you can skip it depending on some results or you can update the task's output.

const tasks = new Listr([
	{
		title: 'Install package dependencies with Yarn',
		task: (ctx, task) => execa('yarn')
			.catch(() => {
				ctx.yarn = false;

				task.title = `${task.title} (or not)`;
				task.skip('Yarn not available');
			})
	},
	{
		title: 'Install package dependencies with npm',
		skip: ctx => ctx.yarn !== false && 'Dependencies already installed with Yarn',
		task: (ctx, task) => {
			task.output = 'Installing dependencies...';

			return execa('npm', ['install'])
		}
	}
]);

tasks.run();

Custom renderers

It's possible to write custom renderers for Listr. A renderer is an ES6 class that accepts the tasks that it should render, and the Listr options object. It has two methods, the render method which is called when it should start rendering, and the end method. The end method is called when all the tasks are completed or if a task failed. If a task failed, the error object is passed in via an argument.

class CustomRenderer {

	constructor(tasks, options) { }

	static get nonTTY() {
		return false;
	}

	render() { }

	end(err) { }
}

module.exports = CustomRenderer;

Note: A renderer is not passed through to the subtasks, only to the main task. It is up to you to handle that case.

The nonTTY property returns a boolean indicating if the renderer supports non-TTY environments. The default for this property is false if you do not implement it.

Observables

Every task is an observable. The task emits three different events and every event is an object with a type property.

  1. The state of the task has changed (STATE).
  2. The task outputted data (DATA).
  3. The task returns a subtask list (SUBTASKS).
  4. The task's title changed (TITLE).
  5. The task became enabled or disabled (ENABLED).

This allows you to flexibly build your UI. Let's render every task that starts executing.

class CustomRenderer {

	constructor(tasks, options) {
		this._tasks = tasks;
		this._options = Object.assign({}, options);
	}

	static get nonTTY() {
		return true;
	}

	render() {
		for (const task of this._tasks) {
			task.subscribe(event => {
				if (event.type === 'STATE' && task.isPending()) {
					console.log(`${task.title} [started]`);
				}
			});
		}
	}

	end(err) { }
}

module.exports = CustomRenderer;

If you want more complex examples, take a look at the update and verbose renderers.

API

Listr([tasks], [options])

tasks

Type: object[]

List of tasks.

title

Type: string

Title of the task.

task

Type: Function

Task function.

skip

Type: Function

Skip function. Read more about skipping tasks.

options

Any renderer specific options. For instance, when using the update-renderer, you can pass in all of its options.

concurrent

Type: boolean number
Default: false

Set to true if you want to run tasks in parallel, set to a number to control the concurrency. By default it runs tasks sequentially.

exitOnError

Type: boolean
Default: true

Set to false if you don't want to stop the execution of other tasks when one or more tasks fail.

renderer

Type: string object
Default: default
Options: default verbose silent

Renderer that should be used. You can either pass in the name of the known renderer, or a class of a custom renderer.

nonTTYRenderer

Type: string object
Default: verbose

The renderer that should be used if the main renderer does not support TTY environments. You can either pass in the name of the renderer, or a class of a custom renderer.

Instance

add(task)

Returns the instance.

task

Type: object object[]

Task object or multiple task objects.

run([context])

Start executing the tasks. Returns a Promise for the context object.

context

Type: object
Default: Object.create(null)

Initial context object.

Related

  • ora - Elegant terminal spinner
  • cli-spinners - Spinners for use in the terminal

License

MIT © Sam Verschueren


Get professional support for this package with a Tidelift subscription
Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies.

More Repositories

1

tsd

Check TypeScript type definitions
TypeScript
2,116
star
2

alfred-fkill

Alfred 3 workflow to fabulously search and kill processes
JavaScript
473
star
3

dev-time-cli

Get the current local time of a GitHub user.
JavaScript
181
star
4

generator-alfred

Scaffold out an Alfred workflow
JavaScript
169
star
5

babel-engine-plugin

Webpack plugin that transpiles dependencies targeting Node.js versions newer than Node.js 0.10
JavaScript
166
star
6

decode-uri-component

A better decodeURIComponent
JavaScript
144
star
7

clinton

Project style linter
JavaScript
124
star
8

alfred-updater

Alfred workflow updater
JavaScript
107
star
9

mobicon-cli

Mobile icon generator
JavaScript
102
star
10

expiry-map

A Map implementation with expirable items
TypeScript
92
star
11

vscode-yo

Yeoman plugin for VS Code
TypeScript
88
star
12

uppercamelcase

Convert a dash/dot/underscore/space separated string to UpperCamelCase: foo-bar → FooBar
JavaScript
81
star
13

vscode-ava

Snippets for AVA
72
star
14

alfred-notifier

Update notifications for your Alfred workflow
JavaScript
71
star
15

alfred-link

Make your Alfred workflows installable from npm
JavaScript
68
star
16

mobisplash-cli

Mobile app splash screen generator
JavaScript
65
star
17

clean-regexp

Clean up regular expressions
JavaScript
61
star
18

dynongo

MongoDB like syntax for DynamoDB
TypeScript
58
star
19

dev-time

Get the current local time of a GitHub user.
JavaScript
57
star
20

aws-lambda-mock-context

AWS Lambda mock context object
JavaScript
52
star
21

mongoose-seeder

Seed your MongoDB database easily
JavaScript
48
star
22

bragg

AWS λ web framework
JavaScript
47
star
23

ngx-ow

Angular form validation on steroids
TypeScript
45
star
24

mobisplash

Mobile app splash screen generator
JavaScript
45
star
25

alfred-ng

Search through the Angular documentation on angular.io
JavaScript
44
star
26

obsify

Observableify a callback-style function
JavaScript
41
star
27

alfred-config

Allow easy user configurations for your Alfred workflows
JavaScript
41
star
28

gulp-cordova

Documentation of gulp-cordova
40
star
29

ngx-monaco

Monaco Editor for Angular
TypeScript
39
star
30

alfred-font-awesome

Alfred workflow to search for font-awesome icons
JavaScript
37
star
31

listr-update-renderer

Listr update renderer
JavaScript
37
star
32

listr-input

Input module for Listr
JavaScript
33
star
33

aws-lambda-stop-server

AWS Lambda function that will stop servers.
JavaScript
32
star
34

alfred-rxjs

Search through the RxJS documentation
JavaScript
29
star
35

angular2-polyfill

Angular2 polyfill for Angular1
TypeScript
29
star
36

got4aws

Convenience wrapper for Got to interact with AWS v4 signed APIs
TypeScript
27
star
37

observable-conf

Listen for changes in your conf config
JavaScript
27
star
38

aws-lambda-start-server

AWS Lambda function that will start servers.
TypeScript
25
star
39

mobicon

Mobile icon generator
JavaScript
25
star
40

kap-s3

Kap plugin - Share on Amazon S3
JavaScript
25
star
41

tsd-typescript

TypeScript with some extras for type-checking.
JavaScript
22
star
42

vscode-final-newline

Inserts a final newline when saving the document.
TypeScript
22
star
43

aws-lambda-invoke

Invoke AWS Lambda functions with ease
JavaScript
22
star
44

cache-conf

Simple cache config handling for your app or module
JavaScript
21
star
45

alfy-test

Test your Alfy workflows
JavaScript
21
star
46

alfred-aws

Search through the AWS JavaScript SDK
JavaScript
21
star
47

alfred-node

Alfred 3 workflow to search for Node.js documentation
JavaScript
19
star
48

map-age-cleaner

Cleanup expired items in a Map
TypeScript
18
star
49

expiry-set

A Set implementation with expirable keys
TypeScript
16
star
50

stats-map

Map that keeps track of the hits and misses
JavaScript
16
star
51

generator-aws-lambda

Scaffold out an AWS Lambda module.
JavaScript
16
star
52

gulp-cordova-build-android

Gulp plugin for building an android Cordova project
JavaScript
14
star
53

vinyl-read

Create vinyl files from glob patterns
JavaScript
13
star
54

cordova-config

Parse and edit the config.xml file of a cordova project
JavaScript
13
star
55

listr-verbose-renderer

Listr verbose renderer
JavaScript
13
star
56

obj-clean

Remove empty objects, empty strings, null and undefined values from objects.
JavaScript
12
star
57

aws-sns-publish

Publish messages to AWS SNS
JavaScript
12
star
58

vali-date

Validate a date
JavaScript
12
star
59

resolve-alfred-prefs

Resolve the path of Alfred.alfredpreferences
JavaScript
11
star
60

dynamo-seeder

Seed your DynamoDB database easily
JavaScript
11
star
61

angular-ga

Google Analytics for your Angular application
TypeScript
11
star
62

tz-format

Format a date with timezone
JavaScript
10
star
63

ng2-hello-world

Angular2 Hello World starter application
JavaScript
10
star
64

rfpify

[DEPRECATED] Promisify a result-first callback function.
JavaScript
10
star
65

aws-lambda-pify

Promisify an AWS lambda function.
JavaScript
9
star
66

get-gravatar-cli

Get a Gravatar image
JavaScript
9
star
67

kap-plugin-test

Test your Kap plugins
JavaScript
9
star
68

gulp-api-doc

RESTful web API Documentation Generator for Gulp
JavaScript
9
star
69

alfred-css-triggers

Alfred workflow to search through csstriggers.com
JavaScript
8
star
70

listr-silent-renderer

Supress Listr rendering output
JavaScript
8
star
71

ngx-api-gateway-client

AWS API Gateway Client for Angular
TypeScript
8
star
72

travis-got

Convenience wrapper for `got` to interact with the Travis API
JavaScript
7
star
73

github-parse-link

Parse GitHub `link` header response
JavaScript
7
star
74

npm-publish-index-test

JavaScript
6
star
75

kap-plugin-mock-context

Kap plugin mock context
JavaScript
6
star
76

parse-aws-lambda-name

Parse an AWS Lambda function name into a name and a qualifier
JavaScript
6
star
77

beRail

beRail is a BlackBerry 10 application for the belgian railways.
C++
5
star
78

promise-concurrent

Execute a list of promises with a limited concurrency
JavaScript
5
star
79

alfred-ionic

Search through the Ionic documentation
JavaScript
5
star
80

capture-pdf

Capture html in a pdf buffer
JavaScript
5
star
81

BB10-OAuth

OAuth framework for BlackBerry10
C++
5
star
82

latest-push

Get the latest push of a GitHub user.
JavaScript
5
star
83

node-mongoose-validator

Easily validate mongoose schema paths.
JavaScript
5
star
84

gulp-cordova-plugin

This library adds a cordova plugin to the cordova project
JavaScript
4
star
85

string-occurrence

Get the number of occurrences of a string in a string
JavaScript
4
star
86

sam-playground

JavaScript
4
star
87

dynongo-pager

Easy paging for DynamoDB with dynongo
TypeScript
4
star
88

ng2-hello-world-lazy-routing

Angular2 Hello World lazy routing application
JavaScript
4
star
89

gh-lint-brainstorm

Brainstorming repository for a repository linter
4
star
90

regex-occurrence

Get the number of occurrences of a RegExp in a string
JavaScript
4
star
91

is-d

Check if a file is a directory
JavaScript
3
star
92

Take-A-Look-Inside-Web

Web version for take a look inside
JavaScript
3
star
93

gulp-cordova-build-ios

Gulp plugin for building an iOS Cordova project
JavaScript
3
star
94

dynamo-discovery-service

Discovery service like etcd but implemented with DynamoDB
JavaScript
3
star
95

lambda-update-alias

Update or create a AWS lambda alias
JavaScript
2
star
96

dynamo-discovery-service-cli

CLI for the discovery service
JavaScript
2
star
97

amplify-starter

JavaScript
2
star
98

get-exec-file

Promisify execFile
JavaScript
2
star
99

angular2-polyfill-heroes

Tour of Heroes implementation with angular2-polyfill
TypeScript
2
star
100

bragg-sns

SNS middleware for bragg
JavaScript
2
star