Compose executors
An executor is just a function, so you can import and invoke it directly, as follows:
1import printAllCaps from 'print-all-caps';
2
3export default async function (
4 options: Schema,
5 context: ExecutorContext
6): Promise<{ success: true }> {
7 // do something before
8 await printAllCaps({ message: 'All caps' });
9 // do something after
10}
11
This only works when you know what executor you want to invoke. Sometimes, however, you need to invoke a target. For instance, the e2e target is often configured like this:
1{
2 "e2e": {
3 "builder": "@nrwl/cypress:cypress",
4 "options": {
5 "cypressConfig": "apps/myapp-e2e/cypress.json",
6 "tsConfig": "apps/myapp-e2e/tsconfig.e2e.json",
7 "devServerTarget": "myapp:serve"
8 }
9 }
10}
11
In this case we need to invoke the target configured in devSeverTarget. We can do it as follows:
1async function* startDevServer(
2 opts: CypressExecutorOptions,
3 context: ExecutorContext
4) {
5 const { project, target, configuration } = parseTargetString(
6 opts.devServerTarget
7 );
8 for await (const output of await runExecutor<{
9 success: boolean;
10 baseUrl?: string;
11 }>(
12 { project, target, configuration },
13 {
14 watch: opts.watch,
15 },
16 context
17 )) {
18 if (!output.success && !opts.watch)
19 throw new Error('Could not compile application files');
20 yield opts.baseUrl || (output.baseUrl as string);
21 }
22}
23
The runExecutor
utility will find the target in the configuration, find the executor, construct the options (as if you invoked it in the terminal) and invoke the executor. Note that runExecutor
always returns an iterable instead of a promise.
Devkit helper functions
Property | Description |
---|---|
logger | Wraps console to add some formatting |
getPackageManagerCommand | Returns commands for the package manager used in the workspace |
parseTargetString | Parses a target string into {project, target, configuration} |
readTargetOptions | Reads and combines options for a given target |
runExecutor | Constructs options and invokes an executor |
See more helper functions in the Devkit API Docs
Using RxJS observables
The Nx devkit only uses language primitives (promises and async iterables). It doesn't use RxJS observables, but you can use them and convert them to a Promise
or an async iterable.
You can convert Observables
to a Promise
with toPromise
.
1import { of } from 'rxjs';
2
3export default async function (opts) {
4 return of({ success: true }).toPromise();
5}
6
You can use the rxjs-for-await
library to convert an Observable
into an async iterable.
1import { of } from 'rxjs';
2import { eachValueFrom } from 'rxjs-for-await';
3
4export default async function (opts) {
5 return eachValueFrom(of({ success: true }));
6}
7
Related Documentation
Concepts
Recipes
- Running Custom Commands
- Use Executor Configurations
- Local Executors
- Profiling Build Performance
- Customizing Webpack Config
- Faster Builds with Module Federation