Guide to custom Angular Schematics

Anjali Verma
5 min readMar 24, 2021

When creating a new Angular project, how many times have you thought of copying files and folders for an initial setup of your Angular project? I’ve done it a million times (okay, not million, but you get the point, right?).

Rationale behind using Schematics

Be it a personal project or an enterprise project, I always wish I could use my own template project to get a great kick off. I wanted to have my core module, graphQL module, all the Apollo dependencies automatically installed, ESLint, prettier and husky hooks configured and etc etc.. every time I started my project. BUTT, I always had to copy paste the exact folders and files every time. And I recently stumbled upon the concept of Schematics and it has made the start of the new project 2x faster.

What the heck is Schematics, after all?

A schematic is a template-based code generator that supports complex logic. It is a set of instructions for transforming a software project by generating or modifying code. Schematics are packaged into collections and installed with npm.

Basically, we all know about ng new project-name command, right? This is a schematics which helps us in creating our Angular project, which adds all the files required to build an Angular project. Now, suppose you want to create your own command so that helps you add all the folders and dependencies that are required for you in every new project you create.

The schematic collection can be a powerful tool for creating, modifying, and maintaining any software project, but is particularly useful for customizing Angular projects to suit the particular needs of your own organization.

Creating a new Schematic

To create a new schematics project we’re going to use an NPM package called schematics. You’ll need to install it globally, just like the Angular CLI.

npm install -g @angular-devkit/schematics-cli

Now, we can use it to create a new schematic project, just like we use the Angular CLI to create a new app.

schematics blank --name=angular-schematics

The blank parameter is used to create a project with minimal files.

Main files that we need to know about:-

  • collection.json is like an index of the schematics in this project. It links the name of each schematic with the code that runs it.
  • sample-schematics-project/index.ts is the code run by our schematic.

Because everything is in TypeScript, we need to build it before we run it. We can do that by running `npm run build`. Running the build script manually can get quite tedious. Instead, we can get the TypeScript compiler to watch for changes and build automatically, by adding a watch script to package.json

"scripts": {
"build": "tsc -p tsconfig.json",
"watch": "tsc -p tsconfig.json --watch",

}

Getting User Input

We’ve talked about how to create a dummy schematic, which doesn’t really do anything, let’s see how can e get user inputs and how can we create a dummy project.

We’ve to tell schema.json that our schematic is expecting an input, for example in the below code we’ve added name as one of the properties and it’s data type as string.

Allowed Data Types :-

  1. String
  2. Number
  3. Boolean
  4. enum
Asking for user input

The x-prompt value is the prompt that the schematic will use to ask the user for a value, if they don’t supply one. The default object allows you to provide a default value. In this case, the default value will be the first argument the user passes in on the command line.

Calling “ng new” Schematic

Now, let’s talk about how can we create an Angular project using a schematic. We’re aware of ng new command, we’ll see how can we make our schematic call an external schematic.

Calling ng new schematic

The first thing we want to do is call the ng new schematic, passing in our name option, and a few other default options. To do that, we’re finally going to write some TypeScript!

We have a function, angularSchematic, which is going to get called when the schematic runs. It’s going to get passed in _options, which we can use to get the name. It’s going to return a function, which takes a Tree and a SchematicContext, and returns a new Tree. A function like this is referred to as a Rule. A function which returns a Rule is a rule factory.

A Tree is a fundamental concept in Schematics. It refers to the schematic’s internal representation of the file system. For those familiar with React, it’s kind of like a virtual DOM, but for the file system. When we make changes, they’re applied to the Tree, rather than the actual files. Once we’ve finished with all our changes, the Tree is written to the file system (or not, if we’re in debug mode).

Calling a custom Schematic and creating custom rules

Now let’s suppose we want that whenever we use this schematic we add certain files. For that, firstly we need to put all the files and folders in our project and you can also add placeholders inside files, using <%= optionName %>.

Ok, once we’ve got our files, we need a way to add them to the Tree. To do this, we can use another Rule factory, called mergeWith. To use mergeWith, we need to pass in a templateSource and a MergeStrategy.

'./files' is the location of the files we want to include, _options is the options passed into our schematic (which will be used by the placeholders), and strings is a collection of utility functions (like camelize and classify) provided by schematics. You can import string from @angular-devkit/core.

Once we’ve got our templateSource, we can pass it into the mergeWith factory along with a MergeStrategy.

const merged = mergeWith(templateSource, MergeStrategy.Overwrite)

MergeStrategy.Overwrite means that if the Tree contains a file that’s also in our templateSource, then use the one in our templateSource.

Ok, so now we have two rules — one returned by mergeWith, and one from externalSchematic. We need to apply both of them to our tree, but we can only return a single rule. So we need some way to combine the two.

Chain takes in a list of Rule factories, and returns a factory that combines them all.

Update package.json

Now if we want to add a few dependencies to our project upfront we can easily do that with our schematics.

To do this, we’re going to write our own Rule factory, called updatePackageJson. This factory will need to be passed in the name of the project, and will return a Rule. The next thing we need to do is read in the current package.json file. We can use tree.read(path) to fetch the contents of the file as a buffer, then, because we’re dealing with a JSON file, we can use JSON.parse() to parse it.

Read package.json

Now that we’ve parsed our package.json file, we can manipulate it just like any other object.

Updating package.json

Run the Schematic

schematics ./new-project/src/collection.json:angular-schematic new-app

This will create a new Angular application with name new-app.

Publish our Schematic

Now, once we have created our schematic we can publish it using npm publish and everyone can start using it.

--

--

Anjali Verma

Web Developer, loves to write about UI technologies