{ "id": "guide/schematics-authoring", "title": "Authoring schematics", "contents": "\n\n\n
\n mode_edit\n
\n\n\n
\n

Authoring schematicslink

\n

You can create your own schematics to operate on Angular projects.\nLibrary developers typically package schematics with their libraries in order to integrate them with the Angular CLI.\nYou can also create stand-alone schematics to manipulate the files and constructs in Angular applications as a way of customizing them for your development environment and making them conform to your standards and constraints.\nSchematics can be chained, running other schematics to perform complex operations.

\n

Manipulating the code in an application has the potential to be both very powerful and correspondingly dangerous.\nFor example, creating a file that already exists would be an error, and if it was applied immediately, it would discard all the other changes applied so far.\nThe Angular Schematics tooling guards against side effects and errors by creating a virtual file system.\nA schematic describes a pipeline of transformations that can be applied to the virtual file system.\nWhen a schematic runs, the transformations are recorded in memory, and only applied in the real file system once they're confirmed to be valid.

\n

Schematics conceptslink

\n

The public API for schematics defines classes that represent the basic concepts.

\n\n

The context object passed into a rule provides access to utility functions and metadata that the schematic may need to work with, including a logging API to help with debugging.\nThe context also defines a merge strategy that determines how changes are merged from the staged tree into the base tree. A change can be accepted or ignored, or throw an exception.

\n

Defining rules and actionslink

\n

When you create a new blank schematic with the Schematics CLI, the generated entry function is a rule factory.\nA RuleFactory object defines a higher-order function that creates a Rule.

\n\nimport { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';\n\n// You don't have to export the function as default.\n// You can also have more than one rule factory per file.\nexport function helloWorld(_options: any): Rule {\n return (tree: Tree, _context: SchematicContext) => {\n return tree;\n };\n}\n\n

Your rules can make changes to your projects by calling external tools and implementing logic.\nYou need a rule, for example, to define how a template in the schematic is to be merged into the hosting project.

\n

Rules can make use of utilities provided with the @schematics/angular package. Look for helper functions for working with modules, dependencies, TypeScript, AST, JSON, Angular CLI workspaces and projects, and more.

\n\n\nimport {\n JsonAstObject,\n JsonObject,\n JsonValue,\n Path,\n normalize,\n parseJsonAst,\n strings,\n} from '@angular-devkit/core';\n\n\n

Defining input options with a schema and interfaceslink

\n

Rules can collect option values from the caller and inject them into templates.\nThe options available to your rules, with their allowed values and defaults, are defined in the schematic's JSON schema file, <schematic>/schema.json.\nYou can define variable or enumerated data types for the schema using TypeScript interfaces.

\n

The schema defines the types and default values of variables used in the schematic.\nFor example, the hypothetical \"Hello World\" schematic might have the following schema.

\n\n\n{\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"default\": \"world\"\n },\n \"useColor\": {\n \"type\": \"boolean\"\n }\n }\n}\n\n

You can see examples of schema files for the Angular CLI command schematics in @schematics/angular.

\n

Schematic promptslink

\n

Schematic prompts introduce user interaction into schematic execution.\nYou can configure schematic options to display a customizable question to the user.\nThe prompts are displayed before the execution of the schematic, which then uses the response as the value for the option.\nThis allows users to direct the operation of the schematic without requiring in-depth knowledge of the full spectrum of available options.

\n

The \"Hello World\" schematic might, for example, ask the user for their name, and display that name in place of the default name \"world\". To define such a prompt, add an x-prompt property to the schema for the name variable.

\n

Similarly, you can add a prompt to allow the user to decide whether the schematic will use color when executing its hello action. The schema with both prompts would be as follows.

\n\n\n{\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"minLength\": 1,\n \"default\": \"world\",\n \"x-prompt\": \"What is your name?\"\n },\n \"useColor\": {\n \"type\": \"boolean\",\n \"x-prompt\": \"Would you like the response in color?\"\n }\n }\n}\n\n

Prompt short-form syntaxlink

\n

These examples use a shorthand form of the prompt syntax, supplying only the text of the question.\nIn most cases, this is all that is required.\nNotice however, that the two prompts expect different types of input.\nWhen using the shorthand form, the most appropriate type is automatically selected based on the property's schema.\nIn the example, the name prompt uses the input type because it it is a string property.\nThe useColor prompt uses a confirmation type because it is a Boolean property.\nIn this case, \"yes\" corresponds to true and \"no\" corresponds to false.

\n

There are three supported input types.

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Input typeDescription
confirmationA yes or no question; ideal for Boolean options.
inputTextual input; ideal for string or number options.
listA predefined set of allowed values.
\n

In the short form, the type is inferred from the property's type and constraints.

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Property SchemaPrompt Type
\"type\": \"boolean\"confirmation (\"yes\"=true, \"no\"=false)
\"type\": \"string\"input
\"type\": \"number\"input (only valid numbers accepted)
\"type\": \"integer\"input (only valid numbers accepted)
\"enum\": [...]list \t(enum members become list selections)
\n

In the following example, the property takes an enumerated value, so the schematic automatically chooses the list type, and creates a menu from the possible values.

\n\n\n \"style\": {\n \"description\": \"The file extension or preprocessor to use for style files.\",\n \"type\": \"string\",\n \"default\": \"css\",\n \"enum\": [\n \"css\",\n \"scss\",\n \"sass\",\n \"less\",\n \"styl\"\n ],\n \"x-prompt\": \"Which stylesheet format would you like to use?\"\n }\n\n\n

The prompt runtime automatically validates the provided response against the constraints provided in the JSON schema.\nIf the value is not acceptable, the user is prompted for a new value.\nThis ensures that any values passed to the schematic meet the expectations of the schematic's implementation, so that you do not need to add additional checks within the schematic's code.

\n

Prompt long-form syntaxlink

\n

The x-prompt field syntax supports a long form for cases where you require additional customization and control over the prompt.\nIn this form, the x-prompt field value is a JSON object with subfields that customize the behavior of the prompt.

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
FieldData Value
typeconfirmation, input, or list (selected automatically in short form)
messagestring (required)
itemsstring and/or label/value object pair (only valid with type list)
\n

The following example of the long form is from the JSON schema for the schematic that the CLI uses to generate applications.\nIt defines the prompt that allows users to choose which style preprocessor they want to use for the application being created.\nBy using the long form, the schematic can provide more explicit formatting of the menu choices.

\n\n\n \"style\": {\n \"description\": \"The file extension or preprocessor to use for style files.\",\n \"type\": \"string\",\n \"default\": \"css\",\n \"enum\": [\n \"css\",\n \"scss\",\n \"sass\",\n \"less\",\n \"styl\"\n ],\n \"x-prompt\": {\n \"message\": \"Which stylesheet format would you like to use?\",\n \"type\": \"list\",\n \"items\": [\n { \"value\": \"css\", \"label\": \"CSS\" },\n { \"value\": \"scss\", \"label\": \"SCSS [ https://sass-lang.com/documentation/syntax#scss ]\" },\n { \"value\": \"sass\", \"label\": \"Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]\" },\n { \"value\": \"less\", \"label\": \"Less [ http://lesscss.org/ ]\" },\n { \"value\": \"styl\", \"label\": \"Stylus [ https://stylus-lang.com/ ]\" }\n ]\n },\n },\n\n

x-prompt schemalink

\n

The JSON schema that defines a schematic's options supports extensions to allow the declarative definition of prompts and their respective behavior.\nNo additional logic or changes are required to the code of a schematic to support the prompts.\nThe following JSON schema is a complete description of the long-form syntax for the x-prompt field.

\n\n\n{\n \"oneOf\": [\n { \"type\": \"string\" },\n {\n \"type\": \"object\",\n \"properties\": {\n \"type\": { \"type\": \"string\" },\n \"message\": { \"type\": \"string\" },\n \"items\": {\n \"type\": \"array\",\n \"items\": {\n \"oneOf\": [\n { \"type\": \"string\" },\n {\n \"type\": \"object\",\n \"properties\": {\n \"label\": { \"type\": \"string\" },\n \"value\": { }\n },\n \"required\": [ \"value\" ]\n }\n ]\n }\n }\n },\n \"required\": [ \"message\" ]\n }\n ]\n}\n\n\n\n

Schematics CLIlink

\n

Schematics come with their own command-line tool.\nUsing Node 6.9 or above, install the Schematics command line tool globally:

\n\nnpm install -g @angular-devkit/schematics-cli\n\n

This installs the schematics executable, which you can use to create a new schematics collection in its own project folder, add a new schematic to an existing collection, or extend an existing schematic.

\n

In the following sections, we will create a new schematics collection using the CLI in order to introduce the files and file structure, and some of the basic concepts.

\n

The most common use of schematics, however, is to integrate an Angular library with the Angular CLI.\nYou can do this by creating the schematic files directly within the library project in an Angular workspace, without using the Schematics CLI.\nSee Schematics for Libraries.

\n

Creating a schematics collectionlink

\n

The following command creates a new schematic named hello-world in a new project folder of the same name.

\n\nschematics blank --name=hello-world\n\n

The blank schematic is provided by the Schematics CLI. The command creates a new project folder (the root folder for the collection) and an initial named schematic in the collection.

\n

Go to the collection folder, install your npm dependencies, and open your new collection in your favorite editor to see the generated files. For example, if you are using VSCode:

\n\ncd hello-world\nnpm install\nnpm run build\ncode .\n\n

The initial schematic gets the same name as the project folder, and is generated in src/hello-world.\nYou can add related schematics to this collection, and modify the generated skeleton code to define your schematic's functionality.\nEach schematic name must be unique within the collection.

\n

Running a schematiclink

\n

Use the schematics command to run a named schematic.\nProvide the path to the project folder, the schematic name, and any mandatory options, in the following format.

\n\nschematics <path-to-schematics-project>:<schematics-name> --<required-option>=<value>\n\n

The path can be absolute or relative to the current working directory where the command is executed.\nFor example, to run the schematic we just generated (which has no required options), use the following command.

\n\nschematics .:hello-world\n\n

Adding a schematic to a collectionlink

\n

To add a schematic to an existing collection, use the same command you use to start a new schematics project, but run the command inside the project folder.

\n\ncd hello-world\nschematics blank --name=goodbye-world\n\n

The command generates the new named schematic inside your collection, with a main index.ts file and its associated test spec.\nIt also adds the name, description, and factory function for the new schematic to the collection's schema in the collection.json file.

\n

Collection contentslink

\n

The top level of the root project folder for a collection contains configuration files, a node_modules folder, and a src/ folder.\nThe src/ folder contains subfolders for named schematics in the collection, and a schema, collection.json, which describes the collected schematics.\nEach schematic is created with a name, description, and factory function.

\n\n{\n \"$schema\":\n \"../node_modules/@angular-devkit/schematics/collection-schema.json\",\n \"schematics\": {\n \"hello-world\": {\n \"description\": \"A blank schematic.\",\n \"factory\": \"./hello-world/index#helloWorld\"\n }\n }\n}\n\n\n

Named schematicslink

\n

When you use the Schematics CLI to create a blank schematics project, the new blank schematic is the first member of the collection, and has the same name as the collection.\nWhen you add a new named schematic to this collection, it is automatically added to the collection.json schema.

\n

In addition to the name and description, each schematic has a factory property that identifies the schematic’s entry point.\nIn the example, you invoke the schematic's defined functionality by calling the helloWorld() function in the main file, hello-world/index.ts.

\n
\n \"overview\"\n
\n

Each named schematic in the collection has the following main parts.

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
index.tsCode that defines the transformation logic for a named schematic.
schema.jsonSchematic variable definition.
schema.d.tsSchematic variables.
files/Optional component/template files to replicate.
\n

It is possible for a schematic to provide all of its logic in the index.ts file, without additional templates.\nYou can create dynamic schematics for Angular, however, by providing components and templates in the files/ folder, like those in standalone Angular projects.\nThe logic in the index file configures these templates by defining rules that inject data and modify variables.

\n\n \n
\n\n\n" }