Picture of Brian Love wearing black against a dark wall in Portland, OR.

Brian Love

Angular Schematics Tutorial

Learn how to create Angular schematics.

What are Schematics?

Schematics are generators that transform an existing filesystem.

With schematics we can:

What can Schematics do?

In general, schematics:

The possibilities for using schematics in your own projects or at your organization is endless. A few examples of how you or your organization can benefit from creating a schematics collection include:

Angular Specific?

Currently, yes, schematics are part of the Angular ecosystem.

CLI Integration?

Yes, schematics are tightly integrated with the Angular CLI. You use schematics with the following CLI commands (to name a few):

What is a Collection?

A collection is a list of schematics. We’ll define the metadata for each schematic in our project’s collection.json file.

Installation

Ok, let’s dive in. 🏄‍ 🏄‍ First, install the schematics CLI via npm or yarn:

$ npm install -g @angular-devkit/schematics-cli
$ yarn add -g @angular-devkit/schematics-cli

Getting Started

To get started, I would recommend that you check out the sample schematics and read through the code and associated comments. You can generate the sample schematics via:

$ schematics schematic --name demo

Hello World Demo

Check out the repository to follow along as we build a simple Hello World schematic:

Hello World

A good place to start is with a simple “Hello World” schematic. Create a new schematics blank project using the schematics CLI:

$ schematics blank --name=hello-world
$ cd hello-world

Open the src/collection.json file:

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

Let’s review the collection.json configuration:

We can also specify some additional properties for a schematic:

It’s also important to note that our project’s package.json file contains a new schematics property that points to the src/collections.json file:

{
  "name": "hello-world",
  "version": "0.0.0",
  "description": "A blank schematics",
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "test": "npm run build && jasmine src/**/*_spec.js"
  },
  "keywords": [
    "schematics"
  ],
  "author": "",
  "license": "MIT",
  "schematics": "./src/collection.json",
  "dependencies": {
    "@angular-devkit/core": "^7.0.2",
    "@angular-devkit/schematics": "^7.0.2",
    "@types/jasmine": "^2.6.0",
    "@types/node": "^8.0.31",
    "jasmine": "^2.8.0",
    "typescript": "^2.5.2"
  }
}

Entry Function

Let’s open the src/hello-world/index.ts file and examine the contents:

import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';

export function helloWorld(options: any): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    return tree;
  };
}

What is a Tree?

According to the documenation, a Tree is:

A staging area for changes, containing the original file system, and a list of changes to apply to it.

A few of things we can use the tree to accomplish include:

What is a Rule?

A Rule is a function that applies actions to a Tree given the SchematicContext:

declare type Rule =
 (tree: Tree, context: SchematicContext) =>
 Tree | Observable<Tree> | Rule | void;

In the hello world example code above, note that the helloWorld() “entry” function returns a Rule.

Build and Execute

Let’s go ahead and build and execute our schematics:

$ yarn build
$ schematics .:hello-world --foo=bar

A few things to note:

Let’s start by adding a log statement to the function that we return from the helloWorld function:

export function helloWorld(options: any): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    console.log(options);
    return tree;
  };
}

Build and execute the schematic and you should now see the logging of the options that you specify to the schematic:

$ yarn build
$ schematics .:hello-world --foo=bar
{ foo: 'bar' }
Nothing to be done.

Generate a Hello World Template

Our schematic doesn’t no anything right now other than log out the user specified options. Let’s update our Rule function to use the tree.create() method to create a new file:

export function helloWorld(options: any): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    tree.create("hello-world.html", `<h1>Hello ${options.name} 👋</h1>`);
    return tree;
  };
}

As we learned previously, the create() method accepts the path to the file we are creating along with the content for the file. In this example, we’re simply using the name value that the user specified to populate a hello-world.html template with the string “Hello” followed by the name.

When we build and execute our updated schematic we should see:

$ yarn build
$ schematics .:hello-world --name="Angular Community" --dry-run
CREATE /hello-world.html (37 bytes)

Note:

Removing the dry-run option should create the hello-world.html template in your current working directory.

Unit Tests

Angular schematics include a SchematicTestRunner for building a suite of unit tests to ensure the quality of your schematics collection.

Let’s test our newly created hello-world schematic:

const collectionPath = path.join(__dirname, '../collection.json');

describe('hello-world', () => {
  it('works', () => {
    const runner = new SchematicTestRunner('schematics', collectionPath);
    const tree = runner.runSchematic('hello-world', {}, Tree.empty());

    expect(tree.files.length).toEqual(1);
  });
});

Conclusion

I hope this is a start to your learning journey with Angular Schematics.

For some further resources, check out: