A project has an architect that knows how to do stuff for this project.

An architect has targets for different types of tasks.

A target contains options and configurations for the builder to do the task.

Configuration

By default, the CLI will recognize multiple Architect Targets. Meaning, these commands are native to the CLI and do not request any more configuration from you, in order to run them. Here the list:

build : ng build … ;

: ; serve : ng serve … ;

: ; e2e : ng e2e … ;

: ; test : ng test … ;

: ; lint : ng lint … ;

: ; extract-i18n : ng xi18n … .

The complete list can be seen here: https://angular.io/cli#command-overview.

Additional targets like server and app-shell , as well as your custom ones, can be run via the command: ng run <targetName> … .

How to know which options are available? We can answer this question with two (2) solutions. The first one would be to go check directly the CLI’s commands.json file here. The second one is to use our IDE to have the intellisense directly in our angular.json file.

Understanding the command mapping

Angular CLI command analysis

As we said earlier, the Angular CLI is understanding some default commands. We simplified a little bit. In reality, these command are mapped to one generic command: ng run <projectName:architectTarget> .

Example: build : ng build … → ng run <projectName>:build .

Knowing this, we can now infer the internal use of these commands for the my-app project as follow:

build : ng build … → ng run my-app:build ;

: → ; serve : ng serve … → ng run my-app:serve ;

: → ; e2e : ng e2e … → ng run my-app:e2e ;

: → ; test : ng test … → ng run my-app:test ;

: → ; lint : ng lint … → ng run my-app:lint ;

: → ; extract-i18n : ng xi18n … → ng run my-app:xi18n .

You could then add your own Architect Target for a specific project to the angular.json file and run it with the ng run command.

Need some help? No worries, you always have the possibility to know what options are available with --help at the end of the command.

ng build my-app --help

ng run my-app:build --help

ng run my-app:myTarget --help

As an example, let’s pretend we have these two (2) Architect Targets in our angular.json , serve (which comes directly from the basic scaffolded code) and myCustomTarget (which you added afterwards for a specific task) for the same project named myProject .

Both are doing the same action, serving the application.

Custom architect target

Here how to call them respectively:

# Serving `myProject` with the `serve` architect target

ng serve myProject

ng run myProject:serve # Serving `myProject` with the `myCustomTarget` architect target

ng run myProject:myCustomTarget

Accessing and changing builders options

A builder is composed of two (2) parts, its implementation ( index.ts ) and its schema ( schema.json ) expressing its available options and configurations.

Be careful to always check the builder’s schema.json file before poking around, or use the intellisense of your preferred IDE to do so.

As an example, we want to generate a stats.json file when bundling our application to be able to analyze it with webpack-bundle-analyzer . To be able to do this, we need more information about this property like its name, which values it takes and where to set it.

First, we need to know which builder it is. Since this is when building our application, we can start by looking at the build command in the architect; We see that the build command refers to an Angular CLI package @angular-devkit/build-angular:browser ; We can go to the builder’ schema declaration of this package or go to the Angular CLI documentation for the build, and check all its property for statsJson ; We can set the property in our angular.json file.

Exploring available options and builders

One really useful and fun way to discovers what properties are available for which builder is to explore the Angular CLI repository in search for the builder schemas. You will find the list of the builders here. All the properties are present in the schema.json file, the implementation of the builder in the index.ts file.

Specifying different configuration based on environment

You can also tweak and refine the options of the builder for each environment. By default, you have two (2) environments available, “production” and un “unnamed” environment which usually corresponds to your development sandbox.

As an example, let’s look at the build command of an angular.json file:

Builder’s configuration for production

In this example, the configuration for the production environment activates a set of properties that we do not need during development, like optimization or extractLicenses . You can add as many environments you need.

Schematics

Schematics are very different from the builders, they serve another purpose. The goal of a schematics is to automate code generation tasks. In this extent, schematics are generators that modify a filesystem.

Schematics are written in typescript and can generate Angular code, but they do not contain any Angular specific code like Service or Component decorators. The true power of Schematics is coming from this generic and agnostic way of generating code. You could write a schematic to generate ReactJS/VueJS/Java code or bash script if you wanted to.

Schematics are not Angular based, they are agnostic, written only in Typescript.

When starting with the Angular CLI and creating a new workspace, a schematic is used to generate all the file tree and setup files and their configuration together.

More generally, each time a command like ng generate [...] or its alias ng g [...] is used, the Angular CLI will make use of a schematic to generate the component/service, add or update a library, or anything you want it to generate. Here are the different commands harnessing the power of schematics:

ng generate / ng g ;

/ ; ng add ;

; ng update .

The concept of Schematics in the workspace has multiple advantages like

enforcing you to follow the DRY (Don’t Repeat Yourself) moto and by extension will enable you to spend less time creating new components/services…

enforcing architecture and consistency between your projects and your team;

adding libraries or updating them automatically.

Like Builders, Schematics also accepts options allowing you to control what/how the code will be generated. They too, come with a schema.json file that lists every option a schematic cansupport. As an example you can navigate to the Angular repository here and see the different options available for a specific schematic.

You can always use the help command, ng generate <schematic_name> —-help to know how to use a schematic. This command will show you all the available options and their relative description.

Schematic vs filesystem

Each schematic has an option for allowing to show a preview of what will change after it has been run. This preview option is call dry-run . Very handy, these options enables you to know and check the validity of what will change and the impacts of the running schematic. But this faculty of previewing the changes is at the center of the way schematics are designed and interact with your filesystem.

Schematics do not run directly against you filesystem, they are describing what changes, what type of transformation to perform.

Those changes and transformations are called Rule and are done on a representation of your filesystem called a Tree .

A Tree is a data structure that contains a base (a set of files that already exists) and a staging area (a list of changes to be applied to the base). When making modifications, you only add those modifications to the staging area, leaving the base always untouched.

Once all the changes performed without errors on the staging area, the last task by the schematic is to apply this set of changes to the filesystem for real.