Generators that create new generators.
Meta-generators enable engineers to extend the generation infrastructure by creating custom generators with consistent structure.
| Type | Purpose | Target Path |
|---|---|---|
domain |
DDD entities, value objects | libs/<scope>/<name> |
service |
Backend services | apps/<name> |
component |
UI components | libs/<scope>/ui/<name> |
adapter |
Infrastructure adapters | libs/<scope>/infrastructure/<name> |
utility |
Shared utilities | libs/shared/<name> |
custom |
Blank template | Configurable |
1
2
3
4
5
6
7
8
# Create a domain generator
just generator-new order --type=domain
# Create a service generator
just generator-new billing --type=service
# Create a custom generator
just generator-new my-thing --type=custom
1
2
3
pnpm exec nx g @nx/plugin:generator my-generator \
--project=my-generators \
--directory=src/generators/my-generator
1
2
3
4
5
6
7
8
9
10
generators/
βββ my-generator/
βββ generator.ts # Main generator logic
βββ schema.json # Configuration schema
βββ schema.d.ts # TypeScript types
βββ files/ # Template files
βββ __name__/
βββ src/
β βββ index.ts__tmpl__
βββ package.json__tmpl__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"$schema": "http://json-schema.org/schema",
"id": "MyGenerator",
"title": "My Generator",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the generated project",
"$default": { "$source": "argv", "index": 0 }
},
"directory": {
"type": "string",
"description": "Directory where the project will be created"
},
"tags": {
"type": "string",
"description": "Comma-separated tags"
}
},
"required": ["name"]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import {
Tree,
formatFiles,
generateFiles,
names,
offsetFromRoot,
joinPathFragments,
} from '@nx/devkit';
import { MyGeneratorSchema } from './schema';
export default async function myGenerator(
tree: Tree,
options: MyGeneratorSchema
) {
const projectRoot = joinPathFragments(
options.directory ?? 'libs',
options.name
);
// Generate files from templates
generateFiles(
tree,
joinPathFragments(__dirname, './files'),
projectRoot,
{
...options,
...names(options.name),
offsetFromRoot: offsetFromRoot(projectRoot),
tmpl: '', // For __tmpl__ files
}
);
// Update project configuration
// Add to workspace...
await formatFiles(tree);
}
1
2
3
4
// __name__.ts__tmpl__
export function <%= name %>() {
return '<%= propertyName %>';
}
| Variable | Example Input | Output |
|---|---|---|
name |
my-service |
my-service |
className |
my-service |
MyService |
propertyName |
my-service |
myService |
constantName |
my-service |
MY_SERVICE |
fileName |
my-service |
my-service |
1
2
3
4
5
6
7
8
9
{
"generators": {
"my-generator": {
"factory": "./src/generators/my-generator/generator",
"schema": "./src/generators/my-generator/schema.json",
"description": "Creates a new my-thing"
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import myGenerator from './generator';
describe('my-generator', () => {
it('should create expected files', async () => {
const tree = createTreeWithEmptyWorkspace();
await myGenerator(tree, { name: 'test' });
expect(tree.exists('libs/test/src/index.ts')).toBeTruthy();
});
});
Every generator must document its SEAβ’ spec source:
Policy "GeneratorTraceability" per Constraint Obligation priority 10 as:
forall g in generators:
exists spec in g.metadata: (spec.id matches /SDS-\d+/)