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
3 comments
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
3 comments
3 comments

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 runProject 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-schematicProject 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-schematicProject 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-schematicProject 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 (3)

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.

authormahmoud-realdolman
2 November 2022

Dear Shhdharmen

I am trying to run schematics from another schematics and i tried to follow your example but i am getting some errors can you please provide me the schema.json file of this example to compare it with mine..

Thanks in advance

authorshhdharmen
2 November 2022

Code link is already given in article, it's at https://github.com/shhdharmen/run-schematics

authormahmoud-realdolman
3 November 2022

Thanks but am getting this error if i implement the code . export function parentschematics(_options: any): Rule { return (_tree: Tree, _context: SchematicContext) => { const rule1 = schematic("realdolmen", _options); /* internal schematics*/

const rule2 = externalSchematic(
  "@schematics/angular",
  "component",
  _options
);

return chain([rule1, rule2]);

}; }

Am getting this error

"A merge conflicted on path "/src/app/latst-updated/latst-updated.component.html".

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.

Featured articles