Angular schematics is a great tool to auto-generate code and to automate repetitive developer tasks like updating configuration, importing a module or scaffolding your redux files. The tool is already used by the angular CLI to generate components, services, modules… and everything that is done with the ng g command. It is also used by popular libraries in the community like @ngrx.
The big issue with schematics is the lack of documentation. At the time of writing this post, there was no official documentation. There is an open issue about it, so hopefully it will change in the future. There are some videos from the angular team and some articles here and there (I recommend to check Manfred Steyer's articles about the same subject), but that’s it.
I decided to share my findings to spare everyone the time it took me to dig into the @angular/devkit source code and try to figure out how to do things.
In this article, I will cover three use cases on how to add auto-generated code to 3 different file types (TypeScript, HTML, JSON) with schematics.
!Note: I will go straight to actually do stuff with schematics rather than explaining schematics and its fundamentals.
If you are interested in what is under the hood, I recommend you watch this video from Hans Larsen from the angular core team.
The first part in this series will be a general schematics introduction + auto-generating code in a TypeScript file. It may already be covered by other articles but, I still wanted to share my point of view about this as other articles might get too deep and complex. The goal here is to simplify the concept as much as I can.
I decided to write the second part because I was unable to find an article that shows how to modify HTML and JSON files in a clear way.
Introduction and how to add auto-generated code into an existing Typescript file.
The best and safest way to modify TypeScript files with schematics is to use the TypeScript compiler API, which allows us to access the Abstract Syntax Tree (AST). Manfred Steyer explained in an article how this works in a very nice way by doing a small experiment which I will go through again briefly.
- Create a new project and run the following commands to install typescript, and node types:
npm init
npm i typescript --save
npm i @types/node --save-dev
- Now start by creating a simple typescript file that just defines a const object.
<p> CODE: https://gist.github.com/aziz-haddad/00edc07d5215a7de52ad91d6d9d1f33c.js </p>
- The next step is to create a typescript file that has a function to show the AST of the demo.ts file that we have just created.
<p> CODE: https://gist.github.com/aziz-haddad/ca178430aaad27c88bd87f37daf6ad9a.js </p>
- Now to execute this file we have to compile it by running tsc index.ts in your terminal
- and finally, run the script by running node index.js
The result of the execution is the syntax tree and it will look like this.
As you can see in the picture, this is a tree representation of our code, you can see that everything we have written in our code has its own node that has different syntax type, text and sometimes children.
Now you may say “Ok, that’s cool but how can we use this in schematics or how is this even helpful?” Fair question and the quick answer to this is that this AST will help us find out where exactly to insert our auto-generated code in our existing TypeScript files.
Talk is cheap — let’s write some code and see how we can use this.
Let's say we have that we have a typescript file in our angular application that contains an array of objects which would look like this.
<p> CODE: https://gist.github.com/aziz-haddad/b561e62e0b17f77c50352a3473e2aa31.js </p>
Our goal is to auto-generate another object to the people array with schematics. In order to do this, we have to go through multiple steps.
1- Create our schematics which is our actual code generator, to do this we have to install @angular-devkit/schematics-cli globally by running
npm i -g @angular-devkit/schematics-cli
and then to scaffold an empty schematic run this command
schematics blank --name=tutorial-schematic
The scaffolded schematic will look like this, I added the utils folder this is for another purpose that we will talk about in a minute.
2- The second step is to create our own Rule. A rule in schematics world is a function that takes a tree (which is the file tree where we run the schematics) with some options (the options that we provide when we are running the schematics). It will run some modifications on the tree and returns a new tree.
The best practice when creating the rule function is to separate the concerns by creating two other functions that will be called in the rule function. We will save them all in the utils folder that we created before.
- The first function will create the context. It will be a function that will get the options and provide us with some useful variables that we can use in our rule. In our case, in the context, we will define the path to the file to modify and the name and the sex value of the object.
<p> CODE: https://gist.github.com/aziz-haddad/090660b6dbc32b1415ee69c49bf0df4b.js </p>
- The second function is the most important part. It will define the changes by getting the context and the tree as arguments and return the change as Change (a class that comes with schematics). Remember the experiment that we did before with the TypeScript compiler API, we will use it intensively here to find out where to add the changes.
<p> CODE: https://gist.github.com/aziz-haddad/29532c62702d2d8e2609cf4c0cfa256f.js </p>
This function looks very long but that's mostly because we are validating every time if a node exists, if not, we throw an error. Apart from that, the function is a straight-forward implementation of the TypeScript compiler API.
To find the place to insert our new person object, you can follow the comments in the code to understand how we get to this point exactly.
In the end, we return a new instance of InsertChange class.
Now Finally let's go back to create our rule which will call createAddObjectToPeopleArrayContext(), addObjectToArrayChange() that we just created and will apply the changes on the tree and return the updated tree. The function will look like this.
<p> CODE: https://gist.github.com/aziz-haddad/4de0382d5c475790607af2e7ede52ae7.js </p>
3- Now we need to add this rule to our schematic in the index.ts file under src/tutorial-schematic like this
<p> CODE: https://gist.github.com/aziz-haddad/fd2d8267c9624367473756fc8842ea43.js </p>
Now, we have everything in place to run our schematic and auto-generate the new object in the people array. You now have to build your schematic, publish it, npm install it in your project, then run schematics .:tutorial-schematic --name=derpinho sex=male
!Note the "." in the command is the path to your schematic.
Now just enjoy the magic of schematics!!
The next part will cover how to modify existing HTML and JSON files with schematics.