nx/docs/shared/recipes/generators/generator-options.md

21 KiB

title description
Customizing Generator Options Learn how to define and customize options for Nx generators, including TypeScript schemas, static options, and dynamic prompts for user input.

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.

export interface GeneratorOptions {
  name: string;
  type?: string;
}

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

import { Tree, formatFiles, installPackagesTask } from '@nx/devkit';
import { libraryGenerator } from '@nx/js';

export default async function (tree: Tree, schema: GeneratorOptions) {
  await libraryGenerator(tree, { name: `${schema.name}-${schema.type || ''}` });
  await formatFiles(tree);
  return () => {
    installPackagesTask(tree);
  };
}

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.

{
  "$schema": "https://json-schema.org/schema",
  "id": "my-generator",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Library name",
      "$default": {
        "$source": "argv",
        "index": 0
      }
    },
    "type": {
      "type": "string",
      "description": "Provide the library type, such as 'data-access' or 'state'"
    }
  },
  "required": ["name"]
}

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.

{
  "$schema": "https://json-schema.org/schema",
  "id": "my-generator",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Library name",
      "$default": {
        "$source": "argv",
        "index": 0
      }
    },
    "type": {
      "type": "string",
      "description": "Provide the library type",
      "x-prompt": {
        "message": "Which type of library would you like to generate?",
        "type": "list",
        "items": [
          {
            "value": "data-access",
            "label": "Data Access"
          },
          {
            "value": "feature",
            "label": "Feature"
          },
          {
            "value": "state",
            "label": "State Management"
          }
        ]
      }
    }
  },
  "required": ["name"]
}

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

Selecting a project

There's a special dynamic option property that populates a selection list with your workspace's projects. Add "x-dropdown": "projects" to your object to provide the prompt.

{
  "$schema": "https://json-schema.org/schema",
  "id": "my-generator",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Component name",
      "$default": {
        "$source": "argv",
        "index": 0
      }
    },
    "project": {
      "type": "string",
      "description": "The project where the component will be located.",
      "x-prompt": "Which project will this component be located in?",
      "x-dropdown": "projects"
    }
  },
  "required": ["name", "project"]
}

All configurable schema options

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

Schema

{
  "properties": {
    "name": {} // see Properties
  },
  "required": [],
  "description": "",
  "definitions": {}, // same as "properties"
  "additionalProperties": false
}

⚠️ properties

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

{
  "properties_name": {
    // properties configuration
  }
}

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

required

The property keys that are required. Example:

{
  "properties": {
    "name": {
      "type": "string"
    },
    "type": {
      "type": "string"
    }
  },
  "required": ["name"]
}

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

interface Schema {
  name: string; // required
  type?: string; // optional
}

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:

{
  "$id": "https://example.com/schemas/customer",
  "$schema": "https://json-schema.org/draft/2020-12/schema",

  "type": "object",
  "properties": {
    "first_name": { "type": "string" },
    "last_name": { "type": "string" },
    "shipping_address": { "$ref": "/schemas/address" },
    "billing_address": { "$ref": "/schemas/address" }
  },
  "required": [
    "first_name",
    "last_name",
    "shipping_address",
    "billing_address"
  ],

  "$defs": {
    "address": {
      "$id": "/schemas/address",
      "$schema": "http://json-schema.org/draft-07/schema#",

      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "$ref": "#/definitions/state" }
      },
      "required": ["street_address", "city", "state"],

      "definitions": {
        "state": { "enum": ["CA", "NY", "... etc ..."] }
      }
    }
  }
}

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:

{
  "type": "object",
  "properties": {
    "number": { "type": "number" },
    "street_name": { "type": "string" },
    "street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
  },
  "additionalProperties": false
}

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.

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

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

Properties

{
  "type": "",
  "required": [],
  "enum": [],
  "properties": {},
  "oneOf": [],
  "anyOf": [],
  "allOf": [],
  "items": [],
  "alias": "",
  "aliases": [],
  "description": "",
  "format": "",
  "visible": false,
  "default": "",
  "$ref": "",
  "$default": {
    "$source": "argv",
    "index": 0
  },
  "additionalProperties": false,
  "x-prompt": {
    "message": "",
    "type": "",
    "items": [],
    "multiselect": false
  },
  "x-deprecated": false,
  "x-priority": "important",
  "x-dropdown": "projects"
}

Options available in number type:

{
  "multipleOf": 5,
  "minimum": 5,
  "exclusiveMinimum": 4,
  "maximum": 200,
  "exclusiveMaximum": 201
}

Options available in string type:

{
  "pattern": "\\d+",
  "minLength": 10,
  "maxLength": 100
}

type

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

Example:

{
  "type": "string",
  "minLength": "10"
}

required

The property keys that are required. Example:

{
  "properties": {
    "a": {
      "type": "boolean"
    },
    "b": {
      "type": "boolean"
    }
  },
  "required": ["a"]
}

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:

{
  "type": "string",
  "enum": ["foo", "bar"]

  // valid case: `foo`, `bar`
  // invalid case: any other string like `hello`
}

properties

The sub-properties of a property. Example:

{
  "index": {
    "description": "Configures the generation of the application's HTML index.",
    "type": "object",
    "properties": {
      "input": {
        "type": "string",
        "minLength": 1,
        "description": "The path of a file to use for the application's generated HTML index."
      },
      "output": {
        "type": "string",
        "minLength": 1,
        "default": "index.html",
        "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."
      }
    },
    "required": ["input"]
  }
}

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:

{
  "sourceMap": {
    "description": "Output sourcemaps. Use 'hidden' for use with error reporting tools without generating sourcemap comment.",
    "default": true,
    "oneOf": [
      {
        "type": "boolean"
      },
      {
        "type": "string"
      }
    ]
  }
}

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

{
  "optimization": {
    "description": "Enables optimization of the build output.",
    "oneOf": [
      {
        "type": "object",
        "properties": {
          "scripts": {
            "type": "boolean",
            "description": "Enables optimization of the scripts output.",
            "default": true
          },
          "styles": {
            "type": "boolean",
            "description": "Enables optimization of the styles output.",
            "default": true
          }
        },
        "additionalProperties": false
      },
      {
        "type": "boolean"
      }
    ]
  }
}

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:

{
  "format": {
    "type": "string",
    "description": "ESLint Output formatter (https://eslint.org/docs/user-guide/formatters).",
    "default": "stylish",
    "anyOf": [
      {
        "enum": [
          "stylish",
          "compact",
          "codeframe",
          "unix",
          "visualstudio",
          "table",
          "checkstyle",
          "html",
          "jslint-xml",
          "json",
          "json-with-metadata",
          "junit",
          "tap"
        ]
      },
      { "minLength": 1 }
    ]
  }
}

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:

{
  "a": {
    "type": "number",
    "allOf": [{ "multipleOf": 5 }, { "multipleOf": 3 }]
  }
}

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

alias

The alias of this property. Example:

{
  "tags": {
    "type": "string",
    "description": "Add tags to the project (used for linting)",
    "alias": "t"
  },
  "directory": {
    "type": "string",
    "description": "A directory where the project is placed",
    "alias": "d"
  }
}

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:

{
  "directory": {
    "description": "Directory where the generated files are placed.",
    "type": "string",
    "aliases": ["dir", "path"]
  }
}

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:

{
  "flat": {
    "description": "Flag to indicate if a directory is created.",
    "type": "boolean",
    "default": false
  }
}

format

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

{
  "prefix": {
    "type": "string",
    "format": "html-selector",
    "description": "The prefix to apply to generated selectors.",
    "alias": "p"
  }
}

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:

{
  "path": {
    "format": "path",
    "visible": false
  }
}

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:

{
  "linter": {
    "description": "The tool to use for running lint checks.",
    "type": "string",
    "enum": ["eslint"],
    "default": "eslint"
  }
}

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:

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

Example of $source: argv:

{
  "name": {
    "type": "string",
    "description": "Library name",
    "$default": {
      "$source": "argv",
      "index": 0
    },
    "x-prompt": "What name would you like to use for the library?",
    "pattern": "^[a-zA-Z].*$"
  }
}

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

Example of $source: projectName:

{
  "project": {
    "type": "string",
    "description": "The name of the project.",
    "alias": "p",
    "$default": {
      "$source": "projectName"
    },
    "x-prompt": "What is the name of the project for the migration?"
  }
}

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:

// with ? - optional
// without ? - required
// | - or
'x-prompt'?:
  | string
  | { message: string; type: string; items: any[]; multiselect?: boolean };

The string x-prompt example:

{
  "name": {
    "type": "string",
    "description": "Library name",
    "$default": {
      "$source": "argv",
      "index": 0
    },
    "x-prompt": "What is your desired library name?"
  }
}

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:

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

Example that contains value and label:

{
  "style": {
    "description": "The file extension to be used for style files.",
    "type": "string",
    "default": "css",
    "enum": ["css", "scss", "sass", "less"],
    "x-prompt": {
      "message": "Which stylesheet format would you like to use?",
      "type": "list",
      "items": [
        {
          "value": "css",
          "label": "CSS"
        },
        {
          "value": "scss",
          "label": "SASS(.scss)  [ https://sass-lang.com   ]"
        },
        {
          "value": "sass",
          "label": "SASS(.sass)  [ https://sass-lang.com   ]"
        },
        {
          "value": "less",
          "label": "LESS         [ https://lesscss.org     ]"
        }
      ]
    }
  }
}
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:

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

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

{
  "tsSpecConfig": {
    "type": "string",
    "description": "The tsconfig file for specs.",
    "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."
  }
}

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

x-priority

Indicates the priority of a property. Can either be important or internal. This will be used to sort the properties on nx.dev, in Nx Console and when calling a generator with --help. important properties are displayed right after required ones while internal properties are shown at the end or hidden.

{
  "directory": {
    "description": "The directory of the new application.",
    "type": "string",
    "x-priority": "important"
  }
}

x-dropdown

Populates the list of projects in your workspace to a selection prompt.

{
  "project": {
    "description": "The project where the component will be located.",
    "type": "string",
    "x-prompt": "Which project will this component be located in?",
    "x-dropdown": "projects"
  }
}

number specific: multipleOf

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

{
  "a": {
    "type": "number",
    "multipleOf": 5
  }
}

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.

{
  "value": {
    "type": "number",
    "minimum": 5
  }
}

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.

{
  "value": {
    "type": "number",
    "exclusiveMinimum": 4
  }
}

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.

{
  "value": {
    "type": "number",
    "maximum": 200
  }
}

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.

{
  "value": {
    "type": "number",
    "exclusiveMaximum": 201
  }
}

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.

{
  "value": {
    "type": "string",
    "pattern": "^\\d+$"
  }
}

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.

{
  "value": {
    "type": "string",
    "minLength": 10
  }
}

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.

{
  "value": {
    "type": "string",
    "maxLength": 10
  }
}

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!