Customizing generator options

Adding a TypeScript schema

To create a TypeScript schema to use in your generator function, define a TypeScript file next to your schema.json named schema.ts. Inside the schema.ts, define an interface to match the properties in your schema.json file, and whether they are required.

1export interface GeneratorOptions {
2  name: string;
3  type?: string;
4}
5

Import the TypeScript schema into your generator file and replace the any in your generator function with the interface.

1import { Tree, formatFiles, installPackagesTask } from '@nrwl/devkit';
2import { libraryGenerator } from '@nrwl/workspace/generators';
3
4export default async function (tree: Tree, schema: GeneratorOptions) {
5  await libraryGenerator(tree, { name: `${schema.name}-${schema.type || ''}` });
6  await formatFiles(tree);
7  return () => {
8    installPackagesTask(tree);
9  };
10}
11

Adding static options

Static options for a generator don't prompt the user for input. To add a static option, define a key in the schema.json file with the option name, and define an object with its type, description, and optional default value.

1{
2  "$schema": "http://json-schema.org/schema",
3  "id": "my-generator",
4  "type": "object",
5  "properties": {
6    "name": {
7      "type": "string",
8      "description": "Library name",
9      "$default": {
10        "$source": "argv",
11        "index": 0
12      }
13    },
14    "type": {
15      "type": "string",
16      "description": "Provide the library type, such as 'data-access' or 'state'"
17    }
18  },
19  "required": ["name"]
20}
21

If you run the generator without providing a value for the type, it is not included in the generated name of the library.

Adding dynamic prompts

Dynamic options can prompt the user to select from a list of options. To define a prompt, add a x-prompt property to the option object, set the type to list, and define an items array for the choices.

1{
2  "$schema": "http://json-schema.org/schema",
3  "id": "my-generator",
4  "type": "object",
5  "properties": {
6    "name": {
7      "type": "string",
8      "description": "Library name",
9      "$default": {
10        "$source": "argv",
11        "index": 0
12      }
13    },
14    "type": {
15      "type": "string",
16      "description": "Provide the library type",
17      "x-prompt": {
18        "message": "Which type of library would you like to generate?",
19        "type": "list",
20        "items": [
21          {
22            "value": "data-access",
23            "label": "Data Access"
24          },
25          {
26            "value": "feature",
27            "label": "Feature"
28          },
29          {
30            "value": "state",
31            "label": "State Management"
32          }
33        ]
34      }
35    }
36  },
37  "required": ["name"]
38}
39

Running the generator without providing a value for the type will prompt the user to make a selection.

All configurable schema options

Properties tagged with ⚠️ are required. Others are optional.

Schema

1{
2  "properties": {
3    "name": {} // see Properties
4  },
5  "required": [],
6  "description": "",
7  "definitions": {}, // same as "properties"
8  "additionalProperties": false
9}
10

⚠️ properties

The properties of a generator. Properties are listed by name:

1{
2  "properties_name": {
3    // properties configuration
4  }
5}
6

The available options of the properties' configuration can be seen in the Properties section.

required

The property keys that are required. Example:

1{
2  "properties": {
3    "name": {
4      "type": "string"
5    },
6    "type": {
7      "type": "string"
8    }
9  },
10  "required": ["name"]
11}
12

In this example, the property name is required, while the property type is optional. You can define your TypeScript schema like this:

1interface Schema {
2  name: string; // required
3  type?: string; // optional
4}
5

description

The description of your schema for users to understand what they can do with the generator.

Example: A exception class generator.

definitions

Define an auxiliary schema in order to be reused and combined later on. Examples:

1{
2  "$id": "https://example.com/schemas/customer",
3  "$schema": "https://json-schema.org/draft/2020-12/schema",
4
5  "type": "object",
6  "properties": {
7    "first_name": { "type": "string" },
8    "last_name": { "type": "string" },
9    "shipping_address": { "$ref": "/schemas/address" },
10    "billing_address": { "$ref": "/schemas/address" }
11  },
12  "required": [
13    "first_name",
14    "last_name",
15    "shipping_address",
16    "billing_address"
17  ],
18
19  "$defs": {
20    "address": {
21      "$id": "/schemas/address",
22      "$schema": "http://json-schema.org/draft-07/schema#",
23
24      "type": "object",
25      "properties": {
26        "street_address": { "type": "string" },
27        "city": { "type": "string" },
28        "state": { "$ref": "#/definitions/state" }
29      },
30      "required": ["street_address", "city", "state"],
31
32      "definitions": {
33        "state": { "enum": ["CA", "NY", "... etc ..."] }
34      }
35    }
36  }
37}
38

In this example, we defined the state in the definitions and reference it later by $ref.

Reference 1: JSON Schema > Definitions & References

Reference 2: Understanding JSON Schema > Extending Recursive Schemas

additionalProperties

Specify whether the additional properties in the input are allowed. Example:

1{
2  "type": "object",
3  "properties": {
4    "number": { "type": "number" },
5    "street_name": { "type": "string" },
6    "street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
7  },
8  "additionalProperties": false
9}
10

In this example, this schema only accepts the properties that are explicitly defined in the properties object such like:

{ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" }

Any additional properties will be considered invalid.

1{
2  "number": 1600,
3  "street_name": "Pennsylvania",
4  "street_type": "Avenue",
5  "direction": "NW"
6}
7

The above examples are from Understanding JSON schema > Additional Properties. There are more details in that tutorial.

Properties

1{
2  "type": "",
3  "required": [],
4  "enum": [],
5  "properties": {},
6  "oneOf": [],
7  "anyOf": [],
8  "allOf": [],
9  "items": [],
10  "alias": "",
11  "aliases": [],
12  "description": "",
13  "format": "",
14  "visible": false,
15  "default": "",
16  "$ref": "",
17  "$default": {
18    "$source": "argv",
19    "index": 0
20  },
21  "additionalProperties": false,
22  "x-prompt": {
23    "message": "",
24    "type": "",
25    "items": [],
26    "multiselect": false
27  },
28  "x-deprecated": false
29}
30

Options available in number type:

1{
2  "multipleOf": 5,
3  "minimum": 5,
4  "exclusiveMinimum": 4,
5  "maximum": 200,
6  "exclusiveMaximum": 201
7}
8

Options available in string type:

1{
2  "pattern": "\\d+",
3  "minLength": 10,
4  "maxLength": 100
5}
6

type

The type of the input. Can be one of string, number, bigint, boolean, object or array.

Example:

1{
2  "type": "string",
3  "minLength": "10"
4}
5

required

The property keys that are required. Example:

1{
2  "properties": {
3    "a": {
4      "type": "boolean"
5    },
6    "b": {
7      "type": "boolean"
8    }
9  },
10  "required": ["a"]
11}
12

In this example, the property a is required, while the property b is optional.

enum

Make sure that the value is in the enumeration. Example:

1{
2  "type": "string",
3  "enum": ["foo", "bar"]
4
5  // valid case: `foo`, `bar`
6  // invalid case: any other string like `hello`
7}
8

properties

The sub-properties of a property. Example:

1{
2  "index": {
3    "description": "Configures the generation of the application's HTML index.",
4    "type": "object",
5    "description": "",
6    "properties": {
7      "input": {
8        "type": "string",
9        "minLength": 1,
10        "description": "The path of a file to use for the application's generated HTML index."
11      },
12      "output": {
13        "type": "string",
14        "minLength": 1,
15        "default": "index.html",
16        "description": "The output path of the application's generated HTML index file. The full provided path will be used and will be considered relative to the application's configured output path."
17      }
18    },
19    "required": ["input"]
20  }
21}
22

In this example, the property index is a object, which accepts two properties: input and output.

oneOf

Only accepts a value that matches one of the condition properties. Example:

1{
2  "sourceMap": {
3    "description": "Output sourcemaps. Use 'hidden' for use with error reporting tools without generating sourcemap comment.",
4    "default": true,
5    "oneOf": [
6      {
7        "type": "boolean"
8      },
9      {
10        "type": "string"
11      }
12    ]
13  }
14}
15

In this example, sourceMap accepts a value whose type is either boolean or string. Another example:

1{
2  "optimization": {
3    "description": "Enables optimization of the build output.",
4    "oneOf": [
5      {
6        "type": "object",
7        "properties": {
8          "scripts": {
9            "type": "boolean",
10            "description": "Enables optimization of the scripts output.",
11            "default": true
12          },
13          "styles": {
14            "type": "boolean",
15            "description": "Enables optimization of the styles output.",
16            "default": true
17          }
18        },
19        "additionalProperties": false
20      },
21      {
22        "type": "boolean"
23      }
24    ]
25  }
26}
27

optimization accepts either an object that includes scripts and styles properties, or a boolean that switches the optimization on or off.

anyOf

Only accepts a value that matches one of the condition properties. Example:

1{
2  "format": {
3    "type": "string",
4    "description": "ESLint Output formatter (https://eslint.org/docs/user-guide/formatters).",
5    "default": "stylish",
6    "anyOf": [
7      {
8        "enum": [
9          "stylish",
10          "compact",
11          "codeframe",
12          "unix",
13          "visualstudio",
14          "table",
15          "checkstyle",
16          "html",
17          "jslint-xml",
18          "json",
19          "json-with-metadata",
20          "junit",
21          "tap"
22        ]
23      },
24      { "minLength": 1 }
25    ]
26  }
27}
28

In this example, format accepts a string listed in the enum property, and/or a string whose minimum length is larger than 1.

allOf

Only accepts a value that matches all the condition properties. Example:

1{
2  "a": {
3    "type": "number",
4    "allOf": [{ "multipleOf": 5 }, { "multipleOf": 3 }]
5  }
6}
7

In this example, a only accepts a value that can be divided by 5 and 3.

alias

The alias of this property. Example:

1{
2  "tags": {
3    "type": "string",
4    "description": "Add tags to the project (used for linting)",
5    "alias": "t"
6  },
7  "directory": {
8    "type": "string",
9    "description": "A directory where the project is placed",
10    "alias": "d"
11  }
12}
13

You can pass either --tags or -t to provide the value of the property tag; either --directory or -d to provide the value of the property directory.

aliases

Mostly same as alias, but it can accept multiple aliases. Example:

1{
2  "directory": {
3    "description": "Directory where the generated files are placed.",
4    "type": "string",
5    "aliases": ["dir", "path"]
6  }
7}
8

You can pass either --dir, --path or even --directory to provide the value of the property directory.

description

The description for users of your property. Example:

1{
2  "flat": {
3    "description": "Flag to indicate if a directory is created.",
4    "type": "boolean",
5    "default": false
6  }
7}
8

format

The format of this property. Available options are: path, html-selector, etc. Example:

1{
2  "prefix": {
3    "type": "string",
4    "format": "html-selector",
5    "description": "The prefix to apply to generated selectors.",
6    "alias": "p"
7  }
8}
9

In this example, the value provided for prefix should be formatted using the html-selector schema.

visible

Indicate whether the property should be visible in the configuration UI. Example:

1{
2  "path": {
3    "format": "path",
4    "visible": false
5  }
6}
7

In this example, the path won't be visible in the configuration UI, and will apply a default value.

default

The default value of this property. Example:

1{
2  "linter": {
3    "description": "The tool to use for running lint checks.",
4    "type": "string",
5    "enum": ["eslint", "tslint"],
6    "default": "eslint"
7  }
8}
9

In this example, linter will pick eslint when users do not provide the value explicitly.

$ref

Reference to a schema. Examples can be seen in the definitions section.

$default

The default source of this property. The full declaration of $default is:

1// with ? - optional
2// without ? - required
3// | - or
4$default?: { $source: 'argv'; index: number } | { $source: 'projectName' };
5

Example of $source: argv:

1{
2  "name": {
3    "type": "string",
4    "description": "Library name",
5    "$default": {
6      "$source": "argv",
7      "index": 0
8    },
9    "x-prompt": "What name would you like to use for the library?",
10    "pattern": "^[a-zA-Z].*$"
11  }
12}
13

name will pick the first argument of the command line as the default value.

Example of $source: projectName:

1{
2  "project": {
3    "type": "string",
4    "description": "The name of the project.",
5    "alias": "p",
6    "$default": {
7      "$source": "projectName"
8    },
9    "x-prompt": "What is the name of the project for the migration?"
10  }
11}
12

project will pick the default project name as the default value.

additionalProperties

See the above additionalProperties section.

x-prompt

Prompt and help user to input the value of the property. It can be a string or a object. The full declaration is:

1// with ? - optional
2// without ? - required
3// | - or
4'x-prompt'?:
5  | string
6  | { message: string; type: string; items: any[]; multiselect?: boolean };
7

The string x-prompt example:

1{
2  "name": {
3    "type": "string",
4    "description": "Library name",
5    "$default": {
6      "$source": "argv",
7      "index": 0
8    },
9    "x-prompt": "What is your desired library name?"
10  }
11}
12

The object example can be seen at Adding dynamic prompts.

⚠️ x-prompt > message

The prompt message.

Example: Which type of library would you like to generate?

⚠️ x-prompt > type

The type of the prompt.

⚠️ x-prompt > items

The choice of the prompt. The x-prompt.type must be list. The declaration of items is:

1// with ? - optional
2// without ? - required
3// | - or
4items?: (string | { name: string; message: string })[];
5

Example that contains value and label:

1{
2  "style": {
3    "description": "The file extension to be used for style files.",
4    "type": "string",
5    "default": "css",
6    "enum": ["css", "scss", "sass", "less"],
7    "x-prompt": {
8      "message": "Which stylesheet format would you like to use?",
9      "type": "list",
10      "items": [
11        {
12          "value": "css",
13          "label": "CSS"
14        },
15        {
16          "value": "scss",
17          "label": "SASS(.scss)  [ http://sass-lang.com   ]"
18        },
19        {
20          "value": "sass",
21          "label": "SASS(.sass)  [ http://sass-lang.com   ]"
22        },
23        {
24          "value": "less",
25          "label": "LESS         [ http://lesscss.org     ]"
26        }
27      ]
28    }
29  }
30}
31
x-prompt > multiselect

Allow to multi-select in the prompt.

x-deprecated

Indicate whether the property is deprecated. Can be a boolean or a string. The boolean example:

1{
2  "setupFile": {
3    "description": "The name of a setup file used by Jest. (use Jest config file https://jestjs.io/docs/en/configuration#setupfilesafterenv-array)",
4    "type": "string",
5    "x-deprecated": true
6  }
7}
8

This indicates that the property setupFile is deprecated without a reason. The string example:

1{
2  "tsSpecConfig": {
3    "type": "string",
4    "description": "The tsconfig file for specs.",
5    "x-deprecated": "Use the `tsconfig` property for `ts-jest` in the e2e project `jest.config.js` file. It will be removed in the next major release."
6  }
7}
8

This indicates that users should use the tsconfig property rather than specify this property.

number specific: multipleOf

Make sure that the number can be divided by the specified number. Example:

1{
2  "a": {
3    "type": "number",
4    "multipleOf": 5
5  }
6}
7

In this example, a only accepts the value that can be divided by 5.

number specific: minimum

Make sure that the number is greater than or equal to the specified number.

1{
2  "value": {
3    "type": "number",
4    "minimum": 5
5  }
6}
7

In this example, value only accepts a value that is greater than or equal to 5 (value >= 5).

You can read more at Understanding JSON schema.

number specific: exclusiveMinimum

Make sure that the number is greater than the specified number.

1{
2  "value": {
3    "type": "number",
4    "exclusiveMinimum": 4
5  }
6}
7

In this example, value only accepts a value that is greater than 4 (value > 4).

You can read more at Understanding JSON schema.

number specific: maximum

Make sure that the number is less than or equal to the specified number.

1{
2  "value": {
3    "type": "number",
4    "maximum": 200
5  }
6}
7

In this example, value only accepts a value that is less than or equal to 200 (value <= 200).

You can read more at Understanding JSON schema.

number specific: exclusiveMaximum

Make sure that the number is less than the specified number.

1{
2  "value": {
3    "type": "number",
4    "maximum": 201
5  }
6}
7

In this example, value only accepts a value that is less than 201 (value < 201).

You can read more at Understanding JSON schema.

string specific: pattern

Make sure that the string matches the Regexp pattern.

1{
2  "value": {
3    "type": "string",
4    "pattern": "^\\d+$"
5  }
6}
7

In this example, value requires the value to match the ^\\d+$ pattern, which is a regular expression that matches a string that contains only digits.

string specific: minLength

Make sure that the string length is greater than or equal to the specified value.

1{
2  "value": {
3    "type": "string",
4    "minLength": 10
5  }
6}
7

In this example, value requires the value to be at least 10 characters long.

string specific: maxLength

Make sure that the string length is less than or equal to the specified value.

1{
2  "value": {
3    "type": "string",
4    "maxLength": 10
5  }
6}
7

In this example, value requires the value to be at most 10 characters long.

More information

The current configurable options (and its parse method) can be found here. You would need a basic knowledge of TypeScript to read this.

Most examples are referenced from the codebase of Nx. Thanks to everyone who have ever contributed to Nx!