0
# Task & Lifecycle Management
1
2
Queue-based task execution system with custom priorities, composition, and lifecycle management that orchestrates generator method execution and composition with other generators.
3
4
## Capabilities
5
6
### Task Queuing
7
8
Core task management system for scheduling and executing generator methods.
9
10
```typescript { .api }
11
/**
12
* Schedule tasks on a run queue
13
* @param task - Task configuration object
14
*/
15
queueTask(task: Task): void;
16
17
/**
18
* Schedule methods on a run queue
19
* @param method - Method to be scheduled or object with function properties
20
* @param methodName - Name of the method (task) to be scheduled
21
* @param queueName - Name of the queue to be scheduled on
22
* @param reject - Reject callback for error handling
23
*/
24
queueMethod(method: Task['method'], methodName: string, queueName: string, reject?: Task['reject']): void;
25
queueMethod(method: Record<string, Task['method']>, methodName?: string | Task['reject'], reject?: Task['reject']): void;
26
27
/**
28
* Schedule tasks from a group on a run queue
29
* @param taskGroup - Object containing tasks
30
* @param taskOptions - Task configuration options
31
*/
32
queueTaskGroup(taskGroup: Record<string, Task['method']>, taskOptions: TaskOptions): void;
33
```
34
35
**Usage Examples:**
36
37
```typescript
38
export default class MyGenerator extends Generator {
39
initializing() {
40
// Queue a single task
41
this.queueTask({
42
method: async () => {
43
this.log('Custom initialization task');
44
await this.checkEnvironment();
45
},
46
taskName: 'check-environment',
47
queueName: 'initializing'
48
});
49
50
// Queue method by name
51
this.queueMethod(
52
this.setupDependencies.bind(this),
53
'setup-dependencies',
54
'configuring'
55
);
56
57
// Queue multiple methods from object
58
this.queueTaskGroup({
59
validateInput: () => this.validateAnswers(),
60
createDirectories: () => this.setupFolders(),
61
copyBaseFiles: () => this.copyTemplates()
62
}, { queueName: 'writing' });
63
}
64
65
async checkEnvironment() {
66
// Custom task implementation
67
}
68
69
setupDependencies() {
70
// Custom method implementation
71
}
72
}
73
```
74
75
### Lifecycle Management
76
77
Methods for managing the generator lifecycle and execution flow.
78
79
```typescript { .api }
80
/**
81
* Schedule every generator's methods on a run queue
82
* Analyzes generator prototype and queues methods matching lifecycle phases
83
* @param taskOptions - Configuration for task execution
84
*/
85
queueOwnTasks(taskOptions: TaskOptions): void;
86
87
/**
88
* Schedule a generator's method on a run queue
89
* @param name - The method name to schedule
90
* @param taskOptions - Task configuration options
91
*/
92
queueOwnTask(name: string, taskOptions: TaskOptions): void;
93
94
/**
95
* Execute a task with error handling and lifecycle events
96
* @param task - Task to be executed
97
* @param args - Task arguments
98
* @param taskStatus - Task status for cancellation support
99
*/
100
async executeTask(task: Task, args?: any[], taskStatus?: TaskStatus): Promise<void>;
101
102
/**
103
* Get available task names from generator prototype
104
* @returns Array of valid method names that can be queued
105
*/
106
getTaskNames(): string[];
107
```
108
109
**Usage Example:**
110
111
```typescript
112
export default class MyGenerator extends Generator {
113
constructor(args, opts) {
114
super(args, opts);
115
116
// Configure task behavior
117
this.setFeatures({
118
tasksMatchingPriority: true, // Only queue methods matching lifecycle phases
119
taskPrefix: 'task_', // Methods must start with 'task_'
120
inheritTasks: true // Include parent class methods
121
});
122
}
123
124
// These methods will be automatically queued
125
task_initializing() {
126
this.log('Custom initializing task');
127
}
128
129
task_prompting() {
130
return this.prompt([...]);
131
}
132
133
task_writing() {
134
this.renderTemplate('template.ejs', 'output.js');
135
}
136
137
// This method won't be queued (doesn't match prefix)
138
helperMethod() {
139
return 'helper';
140
}
141
142
// Get available tasks
143
listTasks() {
144
const tasks = this.getTaskNames();
145
this.log(`Available tasks: ${tasks.join(', ')}`);
146
// Output: Available tasks: initializing, prompting, writing
147
}
148
}
149
```
150
151
### Priority and Queue Management
152
153
Manage custom execution priorities and queue ordering.
154
155
```typescript { .api }
156
/**
157
* Register custom priorities for this generator
158
* @param priorities - Array of priority configurations
159
*/
160
registerPriorities(priorities: Priority[]): void;
161
162
/**
163
* Extract tasks from a priority queue
164
* @param name - Priority/queue name
165
* @param taskOptions - Task configuration options
166
* @returns Array of tasks from the priority
167
*/
168
extractTasksFromPriority(name: string, taskOptions?: TaskOptions): Task[];
169
170
/**
171
* Extract tasks from a task group object
172
* @param group - Object containing task methods
173
* @param taskOptions - Task configuration options
174
* @returns Array of individual tasks
175
*/
176
extractTasksFromGroup(group: Record<string, Task['method']>, taskOptions: TaskOptions): Task[];
177
```
178
179
**Usage Examples:**
180
181
```typescript
182
export default class MyGenerator extends Generator {
183
constructor(args, opts) {
184
super(args, opts);
185
186
// Register custom priorities
187
this.registerPriorities([
188
{
189
priorityName: 'validation',
190
before: 'prompting' // Run before prompting phase
191
},
192
{
193
priorityName: 'cleanup',
194
queueName: 'custom-cleanup-queue',
195
before: 'end' // Run before end phase
196
}
197
]);
198
}
199
200
// Custom priority methods
201
validation() {
202
this.log('Running validation phase');
203
this.validateEnvironment();
204
}
205
206
cleanup() {
207
this.log('Running cleanup phase');
208
this.removeTemporaryFiles();
209
}
210
211
// Extract and manipulate tasks
212
customTaskHandling() {
213
const validationTasks = this.extractTasksFromPriority('validation');
214
const groupTasks = this.extractTasksFromGroup({
215
step1: () => this.log('Step 1'),
216
step2: () => this.log('Step 2'),
217
step3: () => this.log('Step 3')
218
}, { queueName: 'custom' });
219
220
// Queue extracted tasks with custom options
221
groupTasks.forEach(task => this.queueTask({
222
...task,
223
once: true // Only run once per generator instance
224
}));
225
}
226
}
227
```
228
229
### Generator Composition
230
231
Compose with other generators to create complex scaffolding workflows.
232
233
```typescript { .api }
234
/**
235
* Compose this generator with another one
236
* @param generator - Path to generator module or generator object
237
* @param options - Composition options
238
* @returns Promise resolving to composed generator instance(s)
239
*/
240
async composeWith<G extends BaseGenerator = BaseGenerator>(
241
generator: string | { Generator: any; path: string },
242
options?: ComposeOptions<G>
243
): Promise<G>;
244
245
async composeWith<G extends BaseGenerator = BaseGenerator>(
246
generator: string[],
247
options?: ComposeOptions<G>
248
): Promise<G[]>;
249
250
async composeWith<G extends BaseGenerator = BaseGenerator>(
251
generator: string | { Generator: any; path: string },
252
args: string[],
253
options?: Partial<GetGeneratorOptions<G>>,
254
immediately?: boolean
255
): Promise<G>;
256
```
257
258
**Usage Examples:**
259
260
```typescript
261
export default class MyGenerator extends Generator {
262
async initializing() {
263
// Compose with a peerDependency generator
264
await this.composeWith('bootstrap', {
265
sass: true,
266
version: '5.0'
267
});
268
269
// Compose with local generator
270
await this.composeWith(
271
path.resolve(__dirname, '../generator-component/app/main.js'),
272
{ componentType: 'functional' }
273
);
274
275
// Compose with multiple generators
276
await this.composeWith([
277
'typescript',
278
'eslint',
279
'prettier'
280
], {
281
skipInstall: this.options.skipInstall
282
});
283
284
// Compose with arguments
285
await this.composeWith(
286
'sub-generator',
287
['component', 'Button'],
288
{ typescript: true }
289
);
290
}
291
292
async writing() {
293
// Compose with Generator class directly
294
const SubGenerator = await import('./sub-generator.js');
295
await this.composeWith({
296
Generator: SubGenerator.default,
297
path: './sub-generator.js'
298
}, {
299
forwardOptions: true // Forward all options
300
});
301
}
302
}
303
```
304
305
### Task Cancellation and Restart
306
307
Manage task cancellation and generator restart functionality.
308
309
```typescript { .api }
310
/**
311
* Ignore cancellable tasks
312
* Marks current task status as cancelled to skip remaining cancellable tasks
313
*/
314
cancelCancellableTasks(): void;
315
316
/**
317
* Start the generator again with new options
318
* @param options - New options to merge with existing ones
319
*/
320
startOver(options?: BaseOptions): void;
321
322
/**
323
* Queue generator tasks (main entry point for task scheduling)
324
*/
325
async queueTasks(): Promise<void>;
326
```
327
328
**Usage Examples:**
329
330
```typescript
331
export default class MyGenerator extends Generator {
332
async prompting() {
333
this.answers = await this.prompt([
334
{
335
type: 'confirm',
336
name: 'continueAdvanced',
337
message: 'Continue with advanced setup?'
338
}
339
]);
340
341
if (!this.answers.continueAdvanced) {
342
// Cancel remaining cancellable tasks
343
this.cancelCancellableTasks();
344
return;
345
}
346
}
347
348
// This task can be cancelled
349
writing() {
350
this.queueTask({
351
method: () => {
352
this.log('This task can be cancelled');
353
this.renderTemplate('advanced.ejs', 'advanced.js');
354
},
355
taskName: 'advanced-setup',
356
queueName: 'writing',
357
cancellable: true
358
});
359
}
360
361
// Restart scenario
362
async handleError() {
363
try {
364
await this.someFailingOperation();
365
} catch (error) {
366
this.log('Setup failed, restarting with safe defaults...');
367
this.startOver({
368
safeMode: true,
369
skipAdvanced: true
370
});
371
}
372
}
373
}
374
```
375
376
### Pipeline Operations
377
378
File processing pipelines with transform streams.
379
380
```typescript { .api }
381
/**
382
* Process files through transform streams
383
* @param options - Pipeline configuration options
384
* @param transforms - Array of transform streams to apply
385
*/
386
async pipeline(
387
options?: GeneratorPipelineOptions,
388
...transforms: Array<FileTransform<MemFsEditorFile>>
389
): Promise<void>;
390
391
/**
392
* Add a transform stream to the commit stream
393
* @param options - Pipeline options with priority configuration
394
* @param transforms - Transform streams to queue
395
* @returns This generator for chaining
396
*/
397
queueTransformStream(
398
options?: GeneratorPipelineOptions & { priorityToQueue?: string },
399
...transforms: Array<FileTransform<MemFsEditorFile>>
400
): this;
401
```
402
403
**Usage Examples:**
404
405
```typescript
406
import { Transform } from 'stream';
407
408
export default class MyGenerator extends Generator {
409
writing() {
410
// Copy files first
411
this.copyTemplate('src/**/*', 'src/');
412
413
// Queue transform pipeline
414
this.queueTransformStream(
415
{ priorityToQueue: 'transform' },
416
new Transform({
417
objectMode: true,
418
transform(file, encoding, callback) {
419
if (file.path.endsWith('.js')) {
420
// Add strict mode to JS files
421
file.contents = Buffer.from(
422
`'use strict';\n${file.contents.toString()}`
423
);
424
}
425
callback(null, file);
426
}
427
})
428
);
429
}
430
431
async install() {
432
// Run pipeline directly
433
await this.pipeline(
434
{ name: 'Post-install processing' },
435
new Transform({
436
objectMode: true,
437
transform(file, encoding, callback) {
438
if (file.path.includes('node_modules')) {
439
// Skip node_modules
440
return callback();
441
}
442
callback(null, file);
443
}
444
})
445
);
446
}
447
}
448
```
449
450
## Types
451
452
```typescript { .api }
453
interface Task<TaskContext = any> {
454
method: (this: TaskContext, ...args: any[]) => unknown | Promise<unknown>;
455
taskName: string;
456
queueName?: string;
457
once?: boolean;
458
run?: boolean;
459
edit?: boolean;
460
skip?: boolean;
461
args?: any[] | ((generator: Generator) => any[]);
462
reject?: (error: unknown) => void;
463
taskPrefix?: string;
464
auto?: boolean;
465
taskOrigin?: any;
466
cancellable?: boolean;
467
}
468
469
interface TaskOptions {
470
queueName?: string;
471
once?: boolean;
472
run?: boolean;
473
edit?: boolean;
474
skip?: boolean;
475
args?: any[] | ((generator: Generator) => any[]);
476
reject?: (error: unknown) => void;
477
taskPrefix?: string;
478
auto?: boolean;
479
taskOrigin?: any;
480
cancellable?: boolean;
481
}
482
483
interface Priority {
484
priorityName: string;
485
queueName?: string;
486
before?: string;
487
once?: boolean;
488
run?: boolean;
489
edit?: boolean;
490
skip?: boolean;
491
args?: any[] | ((generator: Generator) => any[]);
492
}
493
494
interface ComposeOptions<G extends BaseGenerator = BaseGenerator> {
495
generatorArgs?: string[];
496
generatorOptions?: Partial<GetGeneratorOptions<G>>;
497
destinationRoot?: string;
498
skipEnvRegister?: boolean;
499
forceResolve?: boolean;
500
forwardOptions?: boolean;
501
schedule?: boolean;
502
}
503
504
interface GeneratorPipelineOptions {
505
disabled?: boolean;
506
name?: string;
507
pendingFiles?: boolean;
508
filter?: (file: MemFsEditorFile) => boolean;
509
}
510
511
type TaskStatus = {
512
cancelled: boolean;
513
timestamp: Date;
514
};
515
```