Different ways to run schematics from another schematics

Post Editor

When we create schematics, we sometimes face a situation where we want to run other existing schematics from the same or external collection. In this article we will learn how to do it.

6 min read
1 comment
post

Different ways to run schematics from another schematics

When we create schematics, we sometimes face a situation where we want to run other existing schematics from the same or external collection. In this article we will learn how to do it.

post
post
6 min read
1 comment
1 comment

A schematic is a code generator, which follows a set of instructions and transforms your project by adding or modifying the code.

The schematic collection can be a powerful tool for creating, modifying, and maintaining any angular project. You might use schematics, for example, to generate commonly-used UI patterns or specific components, using predefined templates or layouts. You can use schematics to enforce architectural rules and conventions, making your projects consistent and inter-operative.

Sometimes we need to run other existing schematics from the same or external collection. Let's see when you might need this.

Use Cases
Link to this section

Following are some of use cases when running other schematics are required:

  1. Repeated pattern across files based on existing boiler-plate
    This can be specific to your team’s project, where you want everyone to follow the same boilerplate. And you want to create boiler-plate on top of existing ones. @maciej_wwojcik has written great article about it: Extend Angular Schematics to customize your development process - Angular inDepth
  2. Need to execute the same instructions available in other schematics
    Let’s say with your schematic, you also want to pack some other schematic’s features. For example, you want to create a generic library generator, which works with both Angular and NX, for that, utilizing Angular’s and Nx’s library generator schematics becomes handy.
  3. Decouple set of instructions for better usability
    This can be helpful when you have many tasks going on with a single schematic, but you also want users to run only specific ones. For example, you have a main-schematic, which runs task-1 and task-2. Now to give users the option to run only task-2, you can create one more schematic just to run task-2 and refactor your main-schematic.

For above use cases, we just have to figure out how to give our schematics an instruction to run another schematic. Let’s see.

Provide instructions to the schematic
Link to this section

There are mainly 2 ways we can execute other schematic code from inside the current schematic :

  1. Create a rule - A Rule object defines a function that takes a Tree, applies transformations, and returns a new Tree. The main file for a schematic, index.ts, defines a set of rules that implement the schematic's logic.
  2. Add task in context - Each schematic runs in a context, represented by a SchematicContext object. Adding tasks in context is useful when you want to perform operations on tree generated through current execution, like installing packages, performing linting/formatting, etc.

And there can be 2 types of schematics which can be run:

  1. Schematic from our collection
  2. Schematic from external collection

So basically there are 4 combinations we’ll explore in this article:

  1. Create a rule to run Schematic from our collection
  2. Create a rule to run Schematic from external collection
  3. Add task in context to run Schematic from our collection
  4. Add task in context to run Schematic from external collection

Create schematics
Link to this section

Let’s first create a schematics project named run-schematics:

<>Copy
npm install -g @angular-devkit/schematics-cli schematics blank --name=run-schematics cd ./run-schematics

The command above created a schematics collection named run-schematics. It also created a blank schematic named run-schematics. You will see below project structure created for you:

Project structure after initial run
Project structure after initial run

Let’s add one more schematic in the same collection:

<>Copy
schematics blank --name=child-schematic

Above command will add a schematic named child-schematic in the collection.

Let’s modify child-schematic:

<>Copy
// src/child-schematic/index.ts import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; export function childSchematic(_options: any): Rule { return (tree: Tree, _context: SchematicContext) => { _context.logger.info('Hi from child-schematic'); return tree; }; }

Next, let’s build run-schematics collection:

<>Copy
npm run build

You will need to run build each time when you make changes in the schematics.

To use run-schematics in any project, we will also link it:

<>Copy
npm link

At this point, our project folder looks like below:

Project structure after adding child-schematic
Project structure after adding child-schematic

Now, go to your angular project, link the schematics and run it:

<>Copy
cd ./path/to/angular/project npm link run-schematics ng g run-schematics:run-schematics # Nothing to be done ng g run-schematics:child-schematic # Hi from child-schematic # Nothing to be done

Create rules
Link to this section

We will use schematic and externalSchematic functions from @angular-devkit/schematics to create rules.

Create a rule to run schematics from same collection

Let’s modify run-schematics:

<>Copy
// src/run-schematics/index.ts export function runSchematics(_options: any): Rule { return (_tree: Tree, _context: SchematicContext) => { const rule = schematic("child-schematic", _options); return rule; }; }

We are using schematic function, which returns a Rule and it takes 2 arguments:

  • schematicName - The name of the schematic to run
  • options - The options to pass as input to the RuleFactory

Let’s now run the schematics:

<>Copy
ng g run-schematics:run-schematics # Hi from child-schematic # Nothing to be done

Create a rule to run schematics from external collection

<>Copy
// src/run-schematics/index.ts export function runSchematics(_options: any): Rule { return (_tree: Tree, _context: SchematicContext) => { const rule1 = schematic("child-schematic", _options); const rule2 = externalSchematic( "@schematics/angular", "component", _options ); return chain([rule1, rule2]); }; }

We are using the externalSchematic function, it returns a Rule. It take 3 arguments:

  • collectionName - The name of the collection that contains the schematic to run
  • Rest 2 are same as schematic function

Let’s try to run the schematics:

<>Copy
ng g run-schematics:run-schematics Hi from child-schematic ? What name would you like to use for the component? hero CREATE src/app/hero/hero.component.ts (259 bytes) UPDATE src/app/app.module.ts (738 bytes)

Add tasks in context
Link to this section

To add tasks in SchematicContext, we will use its addTask method. addTask method takes an argument of type TaskConfigurationGenerator.

There are total 4 task classes which implements TaskConfigurationGenerator:

  1. NodePackageInstallTask
  2. NodePackageLinkTask
  3. RepositoryInitializerTask
  4. RunSchematicTask

For our use-case, we will use RunSchematicTask. It has 2 constructor overrides:

  1. constructor(schemaName: string, options: T) - Runs schematic from the same collection
  2. constructor(collectionName: string, schemaName: string, options: T) - Runs schematic from the external collection

Our goal is to create a sub-application and perform linting on the newly created application.

For above goal, we will do as below:

  1. Create lint-schematic - This will perform linting on the newly created sub-app
  2. Create lint-caller-schematic - This will create a sub-app and call lint-schematic through context’s task

Let’s create lint-schematic first:

<>Copy
schematics blank --name=lint-schematic
Project structure after adding lint-schematic
Project structure after adding lint-schematic

Next, replace it’s content with below:

<>Copy
// src/lint-schematic/index.ts import { Rule, SchematicContext, Tree } from "@angular-devkit/schematics"; import { execSync } from "child_process"; export function lintSchematic(_options: { name: string }): Rule { return (_tree: Tree, _context: SchematicContext) => { _context.logger.info(`Executing: npm run lint -- --fix ${_options.name}`); execSync("npm run lint -- --fix " + _options.name); }; }

Let’s create lint-caller-schematic:

<>Copy
schematics blank --name=lint-caller-schematic
Project structure after adding lint-caller-schematic
Project structure after adding lint-caller-schematic

Modify it’s content with below:

<>Copy
// src/lint-caller-schematic/index.ts import { externalSchematic, Rule, SchematicContext, Tree, } from "@angular-devkit/schematics"; import { RunSchematicTask } from "@angular-devkit/schematics/tasks"; export function lintCallerSchematic(_options: any): Rule { return (_tree: Tree, _context: SchematicContext) => { const rule = externalSchematic( "@schematics/angular", "application", _options ); _context.addTask(new RunSchematicTask("lint-schematic", _options)); return rule; }; }

Let’s now run the schematic:

<>Copy
ng g run-schematics:lint-caller-schematic --name=sub-app --defaults

Above schematic will create a sub-app application in your angular workspace and it will also run npm run lint --fix sub-app once the project is created.

To add schematic from external collection in context, you will have to use same class RunSchematicTask, but provide one more argument for collection name:

<>Copy
_context.addTask(new RunSchematicTask("@schematics/angular", "service", _options));

Summary
Link to this section

We learned various use-cases when running a schematic from our schematic can be helpful. I am sure this will help you create much more advanced library schematics.

Let’s quickly revise all the ways.

To run schematic from the same collection

  1. Create a rule
<>Copy
rule = schematic(schemaName, options)

2. Add task in context

<>Copy
context.addTask(new RunSchematicTask(schemaName, options))

To run schematic from external collection

  1. Create a rule
<>Copy
rule = externalSchematic(collectionName, schemaName, options)

2. Add task in context

<>Copy
context.addTask(new RunSchematicTask(collectionName, schemaName, options))

Above schematics are available on GitHub.

Thanks for reading!


Credits
Link to this section

I would like to thanks @kasparovairina for creating great banner for this article.

Comments (1)

authorpatelvimal
13 April 2021

You mentioned we need to run below 2 commands whenever we made make changes in the schematics. npm run build npm link

I dont think so. You only need to run "npm run build" command, because "npm link" is used to provided the package linking which is required only for the first time or if you linking gets reset(if you run npm install).

authorshhdharmen
13 April 2021

Right, thanks for your inputs. I have updated the article.

Share

About the author

author_image

I am a Front-end Developer. I like to work on Angular, React, Bootstrap, CSS, SCSS & Electron. I also love to contribute to Open-Source Projects and sometime write articles.

author_image

About the author

Dharmen Shah

I am a Front-end Developer. I like to work on Angular, React, Bootstrap, CSS, SCSS & Electron. I also love to contribute to Open-Source Projects and sometime write articles.

About the author

author_image

I am a Front-end Developer. I like to work on Angular, React, Bootstrap, CSS, SCSS & Electron. I also love to contribute to Open-Source Projects and sometime write articles.

Looking for a JS job?
Job logo
Senior Full-Stack Developer (Node+Angular)

A-Listware

Ukraine
Remote
$48k - $78k
Job logo
Senior Full stack (Angular+Node)

Monolith

Ukraine
Remote
$60k - $84k
Job logo
AngularJS Developer/.net Core - Remote Contract

InfoMagnus

United States
Remote
$115k - $134k
Job logo
Angular Web Developer

NTT Data Services, Inc.

United States
Remote
$115k - $134k
More jobs
NxAngularCli
NxAngularCli
NxAngularCli

Featured articles

Angularpost
13 September 20218 min read
Tracking user interaction area

Explore one of the most complex pieces of Taiga UI — ActiveZone directive that keeps an eye on what region user is working with. It touches on low-level native DOM events API, advanced RxJS and Dependency Injection, ShadowDOM and more!

Angularpost
13 September 20218 min read
Tracking user interaction area

Explore one of the most complex pieces of Taiga UI — ActiveZone directive that keeps an eye on what region user is working with. It touches on low-level native DOM events API, advanced RxJS and Dependency Injection, ShadowDOM and more!

Read more
AngularpostTracking user interaction area

13 September 2021

8 min read

Explore one of the most complex pieces of Taiga UI — ActiveZone directive that keeps an eye on what region user is working with. It touches on low-level native DOM events API, advanced RxJS and Dependency Injection, ShadowDOM and more!

Read more
Angularpost
7 September 202122 min read
Designing Angular architecture - Container-Presentation pattern

Designing architecture could be tricky, especially in the agile world, where requirement changes are frequent. So your design has to support that and provides extendibility without the need for serious modification. In such cases, you will find the Container-Presentation pattern instrumental.

micro frontendspost
6 September 202125 min read
Taking micro-frontends to the next level

The micro-frontends concept has been out there for quite a while. We’ve been using this architecture in Wix since around 2013, long before it was even given this name. In this article I’d like to share some of the things we did in order to evolve the concept of developing big scale micro-frontends.

micro frontendspost
6 September 202125 min read
Taking micro-frontends to the next level

The micro-frontends concept has been out there for quite a while. We’ve been using this architecture in Wix since around 2013, long before it was even given this name. In this article I’d like to share some of the things we did in order to evolve the concept of developing big scale micro-frontends.

Read more
micro frontendspostTaking micro-frontends to the next level

6 September 2021

25 min read

The micro-frontends concept has been out there for quite a while. We’ve been using this architecture in Wix since around 2013, long before it was even given this name. In this article I’d like to share some of the things we did in order to evolve the concept of developing big scale micro-frontends.

Read more