0
# Task Configuration
1
2
Individual task definition, execution control, and lifecycle management including skip conditions, retry logic, rollback functionality, and dynamic task behavior.
3
4
## Capabilities
5
6
### ListrTask Interface
7
8
Core interface for defining individual tasks with all available configuration options.
9
10
```typescript { .api }
11
/**
12
* Configuration interface for individual tasks
13
* @template Ctx - Context type shared between tasks
14
* @template Renderer - Renderer type for this task
15
* @template FallbackRenderer - Fallback renderer type
16
*/
17
interface ListrTask<Ctx, Renderer, FallbackRenderer> {
18
/** Task display title (can be dynamic) */
19
title?: string | any[];
20
21
/** Main task execution function */
22
task: ListrTaskFn<Ctx, Renderer, FallbackRenderer>;
23
24
/** Condition to determine if task should be enabled */
25
enabled?: boolean | ((ctx: Ctx) => boolean | Promise<boolean>);
26
27
/** Condition or message for skipping the task */
28
skip?: boolean | string | ((ctx: Ctx) => boolean | string | Promise<boolean | string>);
29
30
/** Retry configuration for failed tasks */
31
retry?: number | ListrTaskRetry;
32
33
/** Rollback function to execute if task fails */
34
rollback?: ListrTaskFn<Ctx, Renderer, FallbackRenderer>;
35
36
/** Whether to exit immediately if this task fails */
37
exitOnError?: boolean | ((ctx: Ctx) => boolean | Promise<boolean>);
38
39
/** Renderer-specific options for primary renderer */
40
rendererOptions?: ListrGetRendererTaskOptions<ListrGetRendererClassFromValue<Renderer>>;
41
42
/** Renderer-specific options for fallback renderer */
43
fallbackRendererOptions?: ListrGetRendererTaskOptions<ListrGetRendererClassFromValue<FallbackRenderer>>;
44
}
45
```
46
47
### Task Execution Function
48
49
The main function that defines what a task does during execution.
50
51
```typescript { .api }
52
/**
53
* Task execution function signature
54
* @template Ctx - Context type
55
* @template Renderer - Renderer type
56
* @template FallbackRenderer - Fallback renderer type
57
* @param ctx - Shared context object
58
* @param task - Task wrapper for manipulation and output
59
* @returns Void, promise, string output, observable, or subtask list
60
*/
61
type ListrTaskFn<Ctx, Renderer, FallbackRenderer> = (
62
ctx: Ctx,
63
task: TaskWrapper<Ctx, Renderer, FallbackRenderer>
64
) => void | ListrTaskResult<Ctx>;
65
66
/**
67
* Valid return types from task execution functions
68
*/
69
type ListrTaskResult<Ctx> =
70
| string
71
| Promise<any>
72
| Listr<Ctx, any, any>
73
| ReadableLike
74
| ObservableLike<any>;
75
```
76
77
### Retry Configuration
78
79
Advanced retry configuration for handling task failures with customizable delays and attempt limits.
80
81
```typescript { .api }
82
/**
83
* Retry configuration for failed tasks
84
*/
85
interface ListrTaskRetry {
86
/** Number of retry attempts */
87
tries: number;
88
/** Delay between retry attempts in milliseconds */
89
delay?: number;
90
}
91
```
92
93
**Usage Examples:**
94
95
### Basic Task Configuration
96
97
```typescript
98
import { Listr } from "listr2";
99
100
const tasks = new Listr([
101
{
102
title: "Simple task",
103
task: () => {
104
console.log("Executing task...");
105
}
106
},
107
{
108
title: "Async task with output",
109
task: async (ctx, task) => {
110
task.output = "Starting async operation...";
111
await new Promise(resolve => setTimeout(resolve, 2000));
112
task.output = "Async operation completed";
113
return "Task completed successfully";
114
}
115
}
116
]);
117
```
118
119
### Conditional Task Execution
120
121
```typescript
122
import { Listr } from "listr2";
123
124
interface BuildContext {
125
environment: 'development' | 'production';
126
skipTests: boolean;
127
}
128
129
const tasks = new Listr<BuildContext>([
130
{
131
title: "Install dependencies",
132
task: () => {
133
// Always runs
134
return Promise.resolve();
135
}
136
},
137
{
138
title: "Run tests",
139
enabled: (ctx) => !ctx.skipTests,
140
task: () => {
141
return Promise.resolve();
142
}
143
},
144
{
145
title: "Production build",
146
enabled: (ctx) => ctx.environment === 'production',
147
task: () => {
148
return Promise.resolve();
149
}
150
},
151
{
152
title: "Development build",
153
enabled: (ctx) => ctx.environment === 'development',
154
task: () => {
155
return Promise.resolve();
156
}
157
}
158
]);
159
160
await tasks.run({ environment: 'production', skipTests: false });
161
```
162
163
### Skip Conditions
164
165
```typescript
166
import { Listr } from "listr2";
167
168
const tasks = new Listr([
169
{
170
title: "Check prerequisites",
171
task: (ctx) => {
172
ctx.hasPrereqs = Math.random() > 0.5;
173
}
174
},
175
{
176
title: "Main task",
177
skip: (ctx) => !ctx.hasPrereqs ? "Prerequisites not met" : false,
178
task: () => {
179
return Promise.resolve();
180
}
181
},
182
{
183
title: "Always skipped task",
184
skip: "This task is always skipped",
185
task: () => {
186
return Promise.resolve();
187
}
188
}
189
]);
190
```
191
192
### Retry Logic
193
194
```typescript
195
import { Listr } from "listr2";
196
197
const tasks = new Listr([
198
{
199
title: "Flaky network request",
200
retry: 3, // Simple retry count
201
task: async () => {
202
if (Math.random() < 0.7) {
203
throw new Error("Network request failed");
204
}
205
return "Request successful";
206
}
207
},
208
{
209
title: "API call with delay",
210
retry: {
211
tries: 5,
212
delay: 1000 // 1 second delay between retries
213
},
214
task: async () => {
215
// Simulate API call that might fail
216
const response = await fetch("https://api.example.com/data");
217
if (!response.ok) {
218
throw new Error(`API request failed: ${response.status}`);
219
}
220
return response.json();
221
}
222
}
223
]);
224
```
225
226
### Rollback Functionality
227
228
```typescript
229
import { Listr } from "listr2";
230
231
interface DeployContext {
232
backupCreated: boolean;
233
deploymentId?: string;
234
}
235
236
const tasks = new Listr<DeployContext>([
237
{
238
title: "Create backup",
239
task: (ctx) => {
240
// Create backup logic
241
ctx.backupCreated = true;
242
return Promise.resolve();
243
}
244
},
245
{
246
title: "Deploy application",
247
task: (ctx) => {
248
ctx.deploymentId = "deploy-" + Date.now();
249
// Simulate deployment that might fail
250
if (Math.random() < 0.3) {
251
throw new Error("Deployment failed");
252
}
253
return Promise.resolve();
254
},
255
rollback: (ctx, task) => {
256
if (ctx.backupCreated && ctx.deploymentId) {
257
task.output = `Rolling back deployment ${ctx.deploymentId}...`;
258
// Rollback logic here
259
return Promise.resolve();
260
}
261
}
262
}
263
]);
264
```
265
266
### Dynamic Task Titles
267
268
```typescript
269
import { Listr } from "listr2";
270
271
const files = ["file1.txt", "file2.txt", "file3.txt"];
272
273
const tasks = new Listr(
274
files.map((file, index) => ({
275
title: `Processing ${file}`,
276
task: (ctx, task) => {
277
return new Promise(resolve => {
278
setTimeout(() => {
279
task.title = `✅ Processed ${file}`;
280
resolve(undefined);
281
}, 1000);
282
});
283
}
284
}))
285
);
286
```
287
288
### Observable and Stream Integration
289
290
```typescript
291
import { Listr } from "listr2";
292
import { Observable } from "rxjs";
293
import { spawn } from "child_process";
294
295
const tasks = new Listr([
296
{
297
title: "Observable task",
298
task: () => {
299
return new Observable(observer => {
300
let count = 0;
301
const interval = setInterval(() => {
302
observer.next(`Progress: ${++count}/10`);
303
if (count >= 10) {
304
observer.complete();
305
clearInterval(interval);
306
}
307
}, 100);
308
});
309
}
310
},
311
{
312
title: "Stream from child process",
313
task: (ctx, task) => {
314
const child = spawn('npm', ['install'], { cwd: process.cwd() });
315
316
// Pipe stdout to task output
317
child.stdout.on('data', (data) => {
318
task.output = data.toString();
319
});
320
321
return new Promise((resolve, reject) => {
322
child.on('close', (code) => {
323
if (code === 0) {
324
resolve(undefined);
325
} else {
326
reject(new Error(`Process exited with code ${code}`));
327
}
328
});
329
});
330
}
331
}
332
]);
333
```
334
335
### Error Handling Options
336
337
```typescript
338
import { Listr } from "listr2";
339
340
const tasks = new Listr([
341
{
342
title: "Critical task",
343
exitOnError: true, // Stop entire task list if this fails
344
task: () => {
345
if (Math.random() < 0.5) {
346
throw new Error("Critical failure");
347
}
348
return Promise.resolve();
349
}
350
},
351
{
352
title: "Non-critical task",
353
exitOnError: false, // Continue even if this fails
354
task: () => {
355
throw new Error("Non-critical failure");
356
}
357
},
358
{
359
title: "Context-dependent error handling",
360
exitOnError: (ctx) => ctx.strictMode === true,
361
task: () => {
362
throw new Error("Conditional failure");
363
}
364
}
365
]);
366
367
await tasks.run({ strictMode: false });
368
```
369
370
## Types
371
372
```typescript { .api }
373
interface ListrTaskMessage {
374
/** Error message if task failed */
375
error?: string;
376
/** Skip message if task was skipped */
377
skip?: string;
378
/** Rollback message during rollback operations */
379
rollback?: string;
380
/** Retry message during retry attempts */
381
retry?: { count: number; message?: string };
382
}
383
384
interface ListrTaskPrompt {
385
/** Current prompt instance */
386
prompt?: any;
387
/** Prompt error if prompt failed */
388
error?: Error;
389
}
390
391
/**
392
* Stream-like interface for task output
393
*/
394
interface ReadableLike {
395
readable: boolean;
396
read: (size?: number) => string | Buffer;
397
on: (eventName: 'data' | 'error' | 'end', listener: (data: Buffer | string) => void) => unknown;
398
}
399
400
/**
401
* Observable-like interface for task progress
402
*/
403
interface ObservableLike<T> {
404
subscribe: (observer: ObserverLike<T>) => unknown;
405
}
406
407
interface ObserverLike<T> {
408
next?: (value: T) => void;
409
error?: (error: any) => void;
410
complete?: () => void;
411
}
412
```