nx/docs/shared/recipes/plugins/compose-executors.md
Jack Hsu 66eaf2fc74
docs(misc): remove /nx-api pages (#31453)
This PR removes the `/nx-api` pages from `nx-dev`. They are already
redirected from `/nx-api` to either `/technologies` or
`/reference/core-api` URLs.

e.g. `/nx-api/nx` goes to `/reference/core-api/nx` and `/nx-api/react`
goes to `/technologies/react/api`

**Changes**:
- Remove old `nx-api.json` from being generated in
`scripts/documentation/generators/generate-manifests.ts` -- this was
used to generate the sitemap
- Remove `pages/nx-api` from Next.js app since we don't need them
- Remove workaround from link checker
`scripts/documentation/internal-link-checker.ts` -- the angular
rspack/rsbuild and other workarounds are gone now that they are proper
docs in `map.json`
- Update Powerpack/Remote Cache reference docs to exclude API documents
(since they are duplicated in the Intro page) --
`nx-dev/models-document/src/lib/mappings.ts`
- All content in `docs` have been updated with new URL structure

**Note:** Redirects are already handled, and Claude Code was used to
verify the updated `docs/` URLs (see report below). The twelve 404s
links were updated by hand.

## Verification Report

https://gist.github.com/jaysoo/c7863fe7e091cb77929d1976165c357a
2025-06-04 16:57:01 -04:00

103 lines
3.4 KiB
Markdown

---
title: Compose Executors
description: Learn how to compose and chain Nx executors together, including how to invoke other targets and executors from within your custom executors.
---
# Compose executors
An executor is just a function, so you can import and invoke it directly, as follows:
```typescript
import printAllCaps from 'print-all-caps';
export default async function (
options: Schema,
context: ExecutorContext
): Promise<{ success: true }> {
// do something before
await printAllCaps({ message: 'All caps' });
// do something after
}
```
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:
```json {% fileName="project.json" %}
{
"e2e": {
"builder": "@nx/cypress:cypress",
"options": {
"cypressConfig": "apps/myapp-e2e/cypress.json",
"tsConfig": "apps/myapp-e2e/tsconfig.e2e.json",
"devServerTarget": "myapp:serve"
}
}
}
```
In this case we need to invoke the target configured in devSeverTarget. We can do it as follows:
```typescript
async function* startDevServer(
opts: CypressExecutorOptions,
context: ExecutorContext
) {
const { project, target, configuration } = parseTargetString(
opts.devServerTarget
);
for await (const output of await runExecutor<{
success: boolean;
baseUrl?: string;
}>(
{ project, target, configuration },
{
watch: opts.watch,
},
context
)) {
if (!output.success && !opts.watch)
throw new Error('Could not compile application files');
yield opts.baseUrl || (output.baseUrl as string);
}
}
```
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](/reference/core-api/devkit/documents/nx_devkit#functions)
## 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`.
```typescript
import { of } from 'rxjs';
export default async function (opts) {
return of({ success: true }).toPromise();
}
```
You can use the [`rxjs-for-await`](https://www.npmjs.com/package/rxjs-for-await) library to convert an `Observable` into an async iterable.
```typescript
import { of } from 'rxjs';
import { eachValueFrom } from 'rxjs-for-await';
export default async function (opts) {
return eachValueFrom(of({ success: true }));
}
```