0
# Task System
1
2
Task execution, caching, and orchestration APIs for building advanced build tools and custom task runners.
3
4
## Capabilities
5
6
### Task Execution
7
8
Functions for running executors and tasks programmatically.
9
10
```typescript { .api }
11
/**
12
* Runs an executor programmatically
13
* @param targetDescription - Target to execute (project:target:configuration)
14
* @param overrides - Options to override target configuration
15
* @param context - Execution context
16
* @returns Async iterator yielding execution results
17
*/
18
function runExecutor<T = any>(
19
targetDescription: Target,
20
overrides: Partial<T>,
21
context: ExecutorContext
22
): Promise<AsyncIterableIterator<{ success: boolean; [key: string]: any }>>;
23
24
/**
25
* Parses a target string into its components
26
* @param targetString - Target in format "project:target" or "project:target:configuration"
27
* @returns Parsed target object
28
*/
29
function parseTargetString(targetString: string): Target;
30
31
/**
32
* Converts target object to string representation
33
* @param target - Target object
34
* @returns Target string in format "project:target:configuration"
35
*/
36
function targetToTargetString(target: Target): string;
37
38
interface Target {
39
project: string;
40
target: string;
41
configuration?: string;
42
}
43
44
interface ExecutorContext {
45
/** Workspace root directory */
46
root: string;
47
/** Current working directory */
48
cwd: string;
49
/** Workspace configuration */
50
workspace: WorkspaceJsonConfiguration;
51
/** Whether verbose logging is enabled */
52
isVerbose: boolean;
53
/** Name of the project being executed */
54
projectName?: string;
55
/** Name of the target being executed */
56
targetName?: string;
57
/** Name of the configuration being used */
58
configurationName?: string;
59
/** Graph of all projects */
60
projectGraph?: ProjectGraph;
61
/** Task graph for current execution */
62
taskGraph?: TaskGraph;
63
/** Hash of the current task */
64
hash?: string;
65
}
66
```
67
68
### Task Graph Operations
69
70
Functions for working with task dependency graphs and task orchestration.
71
72
```typescript { .api }
73
/**
74
* Creates a task graph from target dependencies
75
* @param projectGraph - Project dependency graph
76
* @param targets - Array of targets to execute
77
* @param nxJson - Nx configuration
78
* @param overrides - Task overrides
79
* @returns Task graph with dependencies
80
*/
81
function createTaskGraph(
82
projectGraph: ProjectGraph,
83
targets: Target[],
84
nxJson: NxJsonConfiguration,
85
overrides: { [target: string]: any }
86
): TaskGraph;
87
88
/**
89
* Maps targets to projects for task graph creation
90
* @param targets - Array of targets
91
* @param projectGraph - Project dependency graph
92
* @returns Map of project names to target arrays
93
*/
94
function groupTargetsByProject(
95
targets: Target[],
96
projectGraph: ProjectGraph
97
): Map<string, Target[]>;
98
99
interface TaskGraph {
100
/** Map of task IDs to task definitions */
101
tasks: Record<string, Task>;
102
/** Map of task IDs to their dependency task IDs */
103
dependencies: Record<string, string[]>;
104
/** Array of root task IDs (tasks with no dependencies) */
105
roots: string[];
106
}
107
108
interface Task {
109
/** Unique task identifier */
110
id: string;
111
/** Target being executed */
112
target: Target;
113
/** Project root path */
114
projectRoot: string;
115
/** Override options for this task */
116
overrides: any;
117
/** Hash representing task inputs */
118
hash?: string;
119
/** Array of output paths for this task */
120
outputs?: string[];
121
}
122
```
123
124
### Task Hashing and Caching
125
126
APIs for computing task hashes and managing task caching.
127
128
```typescript { .api }
129
/**
130
* Task hasher for computing deterministic task hashes
131
*/
132
interface TaskHasher {
133
/** Hashes a single task */
134
hashTask(task: Task): Promise<string>;
135
/** Hashes multiple tasks in batch */
136
hashTasks(tasks: Task[]): Promise<Record<string, string>>;
137
}
138
139
/**
140
* Creates a task hasher instance
141
* @param projectGraph - Project dependency graph
142
* @param nxJson - Nx configuration
143
* @returns Task hasher instance
144
*/
145
function createTaskHasher(
146
projectGraph: ProjectGraph,
147
nxJson: NxJsonConfiguration
148
): TaskHasher;
149
150
/**
151
* Hash computation utilities
152
*/
153
function hashArray(values: string[]): string;
154
function hashObject(obj: any): string;
155
156
interface FileData {
157
file: string;
158
hash: string;
159
}
160
161
/**
162
* Computes hash for workspace files
163
* @param workspaceRoot - Workspace root directory
164
* @param globs - Array of glob patterns to include
165
* @returns Promise resolving to file data with hashes
166
*/
167
function hashFiles(workspaceRoot: string, globs: string[]): Promise<FileData[]>;
168
```
169
170
### Task Runner Integration
171
172
Types and utilities for integrating with Nx task runners.
173
174
```typescript { .api }
175
/**
176
* Task runner interface for custom task execution
177
*/
178
interface TasksRunner {
179
invoke(
180
tasks: Task[],
181
options: any,
182
context?: TasksRunnerContext
183
): Promise<{ [taskId: string]: TaskResult }>;
184
}
185
186
interface TasksRunnerContext {
187
target?: string;
188
initiatingProject?: string;
189
projectGraph: ProjectGraph;
190
nxJson: NxJsonConfiguration;
191
hasher: TaskHasher;
192
}
193
194
interface TaskResult {
195
success: boolean;
196
code?: number;
197
terminalOutput?: string;
198
startTime?: number;
199
endTime?: number;
200
}
201
202
/**
203
* Default task runner options
204
*/
205
interface DefaultTasksRunnerOptions {
206
parallel?: number;
207
maxParallel?: number;
208
cacheableOperations?: string[];
209
runtimeCacheInputs?: string[];
210
cacheDirectory?: string;
211
remoteCache?: RemoteCacheOptions;
212
}
213
214
interface RemoteCacheOptions {
215
enabled?: boolean;
216
url?: string;
217
timeout?: number;
218
}
219
```
220
221
### Executor Development
222
223
Types and utilities for creating custom executors.
224
225
```typescript { .api }
226
/**
227
* Executor function signature
228
*/
229
type Executor<T = any> = (
230
options: T,
231
context: ExecutorContext
232
) => Promise<{ success: boolean; [key: string]: any }> |
233
AsyncIterableIterator<{ success: boolean; [key: string]: any }>;
234
235
/**
236
* Promise-based executor signature
237
*/
238
type PromiseExecutor<T = any> = (
239
options: T,
240
context: ExecutorContext
241
) => Promise<{ success: boolean; [key: string]: any }>;
242
243
/**
244
* Creates an executor that converts async iterators to promises
245
* @param executor - Async iterator executor
246
* @returns Promise-based executor
247
*/
248
function convertToPromiseExecutor<T>(
249
executor: Executor<T>
250
): PromiseExecutor<T>;
251
252
/**
253
* Schema validation for executor options
254
*/
255
interface ExecutorSchema {
256
version: number;
257
title: string;
258
description?: string;
259
type: 'object';
260
properties: Record<string, any>;
261
additionalProperties?: boolean;
262
required?: string[];
263
}
264
```
265
266
## Usage Examples
267
268
### Running Tasks Programmatically
269
270
```typescript
271
import {
272
runExecutor,
273
parseTargetString,
274
ExecutorContext,
275
logger
276
} from "nx/src/devkit-exports";
277
278
async function buildProject(projectName: string) {
279
const target = parseTargetString(`${projectName}:build`);
280
281
const context: ExecutorContext = {
282
root: process.cwd(),
283
cwd: process.cwd(),
284
workspace: readWorkspaceConfiguration(),
285
isVerbose: false,
286
projectName: target.project,
287
targetName: target.target
288
};
289
290
try {
291
const results = await runExecutor(target, {}, context);
292
293
for await (const result of results) {
294
if (result.success) {
295
logger.info(`✅ ${projectName}:build completed successfully`);
296
} else {
297
logger.error(`❌ ${projectName}:build failed`);
298
return false;
299
}
300
}
301
302
return true;
303
} catch (error) {
304
logger.error(`Failed to run ${projectName}:build: ${error.message}`);
305
return false;
306
}
307
}
308
```
309
310
### Custom Task Runner
311
312
```typescript
313
import {
314
Task,
315
TasksRunner,
316
TasksRunnerContext,
317
TaskResult,
318
logger
319
} from "nx/src/devkit-exports";
320
321
class CustomTasksRunner implements TasksRunner {
322
async invoke(
323
tasks: Task[],
324
options: any,
325
context?: TasksRunnerContext
326
): Promise<{ [taskId: string]: TaskResult }> {
327
const results: { [taskId: string]: TaskResult } = {};
328
329
logger.info(`Executing ${tasks.length} tasks`);
330
331
for (const task of tasks) {
332
const startTime = Date.now();
333
334
try {
335
// Execute task using runExecutor
336
const target = task.target;
337
const executorContext: ExecutorContext = {
338
root: context.projectGraph.nodes[target.project].data.root,
339
cwd: process.cwd(),
340
workspace: {} as any, // Would normally load workspace config
341
isVerbose: false,
342
projectName: target.project,
343
targetName: target.target,
344
configurationName: target.configuration,
345
projectGraph: context.projectGraph,
346
hash: task.hash
347
};
348
349
const executorResults = await runExecutor(target, task.overrides, executorContext);
350
let success = true;
351
352
for await (const result of executorResults) {
353
success = success && result.success;
354
}
355
356
results[task.id] = {
357
success,
358
startTime,
359
endTime: Date.now()
360
};
361
362
} catch (error) {
363
results[task.id] = {
364
success: false,
365
startTime,
366
endTime: Date.now(),
367
terminalOutput: error.message
368
};
369
}
370
}
371
372
return results;
373
}
374
}
375
376
export default CustomTasksRunner;
377
```
378
379
### Task Graph Analysis
380
381
```typescript
382
import {
383
createTaskGraph,
384
parseTargetString,
385
createProjectGraphAsync,
386
readNxJson,
387
logger
388
} from "nx/src/devkit-exports";
389
390
async function analyzeTaskDependencies(targetStrings: string[]) {
391
const projectGraph = await createProjectGraphAsync();
392
const nxJson = readNxJson();
393
394
// Parse target strings
395
const targets = targetStrings.map(parseTargetString);
396
397
// Create task graph
398
const taskGraph = createTaskGraph(projectGraph, targets, nxJson, {});
399
400
logger.info(`Task Graph Analysis:`);
401
logger.info(` Total tasks: ${Object.keys(taskGraph.tasks).length}`);
402
logger.info(` Root tasks: ${taskGraph.roots.length}`);
403
404
// Find task dependencies
405
for (const [taskId, task] of Object.entries(taskGraph.tasks)) {
406
const dependencies = taskGraph.dependencies[taskId] || [];
407
logger.info(` ${task.target.project}:${task.target.target} depends on: ${dependencies.length} tasks`);
408
}
409
410
return taskGraph;
411
}
412
```
413
414
### Custom Executor Implementation
415
416
```typescript
417
import { ExecutorContext, logger } from "nx/src/devkit-exports";
418
419
interface CustomBuildOptions {
420
command: string;
421
args?: string[];
422
cwd?: string;
423
env?: Record<string, string>;
424
}
425
426
export default async function customBuildExecutor(
427
options: CustomBuildOptions,
428
context: ExecutorContext
429
): Promise<{ success: boolean }> {
430
logger.info(`Running custom build for ${context.projectName}`);
431
432
const { spawn } = await import('child_process');
433
434
return new Promise((resolve) => {
435
const child = spawn(options.command, options.args || [], {
436
cwd: options.cwd || context.root,
437
env: { ...process.env, ...options.env },
438
stdio: 'inherit'
439
});
440
441
child.on('close', (code) => {
442
const success = code === 0;
443
444
if (success) {
445
logger.info(`✅ Custom build completed successfully`);
446
} else {
447
logger.error(`❌ Custom build failed with code ${code}`);
448
}
449
450
resolve({ success });
451
});
452
453
child.on('error', (error) => {
454
logger.error(`Failed to start custom build: ${error.message}`);
455
resolve({ success: false });
456
});
457
});
458
}
459
460
// Executor schema (schema.json)
461
export const schema: ExecutorSchema = {
462
version: 1,
463
title: 'Custom Build Executor',
464
description: 'Runs a custom build command',
465
type: 'object',
466
properties: {
467
command: {
468
type: 'string',
469
description: 'Command to execute'
470
},
471
args: {
472
type: 'array',
473
description: 'Command arguments',
474
items: { type: 'string' }
475
},
476
cwd: {
477
type: 'string',
478
description: 'Working directory'
479
},
480
env: {
481
type: 'object',
482
description: 'Environment variables'
483
}
484
},
485
required: ['command']
486
};
487
```
488
489
### Task Caching
490
491
```typescript
492
import {
493
createTaskHasher,
494
createProjectGraphAsync,
495
readNxJson,
496
Task,
497
logger
498
} from "nx/src/devkit-exports";
499
500
async function computeTaskHashes(tasks: Task[]) {
501
const projectGraph = await createProjectGraphAsync();
502
const nxJson = readNxJson();
503
504
const hasher = createTaskHasher(projectGraph, nxJson);
505
506
logger.info('Computing task hashes...');
507
508
const hashes = await hasher.hashTasks(tasks);
509
510
for (const [taskId, hash] of Object.entries(hashes)) {
511
const task = tasks.find(t => t.id === taskId);
512
logger.info(`${task.target.project}:${task.target.target} -> ${hash}`);
513
}
514
515
return hashes;
516
}
517
```