Complete Nx plugin development toolkit: create custom generators, executors, and extend Nx workspaces with reusable automation
93
94%
Does it follow best practices?
Impact
92%
1.00xAverage score across 5 eval scenarios
Passed
No known issues
Navigation hub for creating custom Nx plugins, generators, and executors.
Start new workspace with plugin:
npx create-nx-plugin my-pluginAdd plugin to existing workspace:
npx nx add @nx/plugin
npx nx g plugin tools/my-pluginCreate a generator:
npx nx g generator my-plugin/src/generators/library-with-readmeImplement with Tree API:
tree.write, generateFiles, updateJson for filesystem edits--dry-run before executionValidate: Run npx nx g my-plugin:library-with-readme mylib --dry-run
Stop if: Unexpected files appear or required options fail validation
Create executor structure:
mkdir -p tools/executors/my-executorImplement with ExecutorContext:
{ success: boolean } from executorValidate: Run npx nx run my-project:my-target --help
Stop if: Executor fails to resolve or schema is invalid
Run tests:
npx nx test my-pluginValidate in real workspace:
npx nx g my-plugin:my-generator sample --dry-run
npx nx run my-project:my-targetnpx create-nx-plugin my-pluginnpx nx add @nx/pluginnpx nx g generator my-plugin/src/generators/my-gennpx nx g my-plugin:my-gen sample --dry-runnpx nx run my-project:my-target --skip-nx-cachenpx nx resetrg -n "generateFiles|updateJson|readProjectConfiguration" plugins toolswriteFileSync("libs/my-lib/src/index.ts", content).tree.write("libs/my-lib/src/index.ts", content).libs/my-lib/... writes.readProjectConfiguration(tree, name).root.schema: any and no guardrails.project.json.const config = readProjectConfiguration(tree, name); config.targets.build = {...}; updateProjectConfiguration(tree, name, config);.executor.ts."executor": "../../tools/executors:task"."executor": "@scope/tools:task"."outputs": ["{options.outputPath}"].fs.readFileSync in main execution flow.await fs.promises.readFile with structured error handling.generateFiles() without calling formatFiles(tree).await generateFiles(...); await formatFiles(tree);.tree.write(path, content) without checking tree.exists(path).if (tree.exists(path)) { throw new Error('File already exists'); }.src/index.ts and generators.json.src/index.ts and register in generators.json with factory path.Generators:
--dry-run before broad rolloutsExecutors:
{ success: boolean } from executor function| Missing Input | Fallback |
|---|---|
directory omitted in generator | Use workspace default location |
| optional flags omitted | Apply safe schema defaults |
| custom template not provided | Generate minimal boilerplate |
| executor outputs not declared | No cache, always re-run |
| Topic | Reference |
|---|---|
| Plugin scaffolding and structure | references/plugin-scaffolding.md |
| Core concepts | knowledge-base/concepts.md |
| Topic | Reference |
|---|---|
| Generator implementation guide | references/generators-guide.md |
| Tree API patterns | references/tree-api-reference.md |
| Template engine guidance | references/template-engine-guide.md |
| Template system details | knowledge-base/template-system.md |
| Topic | Reference |
|---|---|
| Executor implementation guide | references/executors-guide.md |
| ExecutorContext API usage | references/executor-context-api.md |
| Executor schema design | references/executor-schema-design.md |
| Topic | Reference |
|---|---|
| Schema design patterns | references/schema-design-patterns.md |
| Utility helpers | knowledge-base/utilities.md |
| Testing and troubleshooting | references/testing-and-troubleshooting.md |