Different ways to run schematics from another schematics
Tags: angular, schematic, typescript
Published: 2021-04-16 05:29:48

---
title: Different ways to run schematics from another schematics
published: true
description: 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.
tags: angular,schematic,typescript
cover_image: https://images.indepth.dev/images/2021/04/Different-ways-to-run-schematics-from-the-schematic-1.jpg
canonical_url: https://indepth.dev/posts/1453/different-ways-to-run-schematics-from-the-schematic
---

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

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

- **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](https://indepth.dev/posts/1438/extend-angular-schematics-to-customize-your-development-process) 

- **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.

- **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

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

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

```bash
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](https://cdn.hashnode.com/res/hashnode/image/upload/v1618478408316/f5VCLVYYY.png)

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

```bash
schematics blank --name=child-schematic
```

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

Let’s modify `child-schematic`:

```typescript
// 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:

```bash
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:

```bash
npm link
```

At this point, our project folder looks like below:

![Project structure after adding child-schematic](https://cdn.hashnode.com/res/hashnode/image/upload/v1618478550627/sLbZsl0b0.png)

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

```bash
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

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`:

```typescript
// 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:

```bash
ng g run-schematics:run-schematics
# Hi from child-schematic
# Nothing to be done
```

#### Create a rule to run schematics from external collection

```typescript
// 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 takes 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:

```bash
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

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:

```bash
schematics blank --name=lint-schematic
```

![Project structure after adding lint-schematic](https://cdn.hashnode.com/res/hashnode/image/upload/v1618480939132/3YutA6g1h.png)

Next, replace it’s content with below:

```typescript
// 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`:

```bash
schematics blank --name=lint-caller-schematic
```

![Project structure after adding lint-caller-schematic](https://cdn.hashnode.com/res/hashnode/image/upload/v1618481005452/C7LSh-LSbV.png)

Modify it’s content with below:

```typescript
// 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:

```bash
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:

```typescript
_context.addTask(new RunSchematicTask("@schematics/angular", "service", _options));
```

## Summary

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 same collection

#### Create a rule

```typescript
rule = schematic(schemaName, options)
```

#### Add task in context

```typescript
context.addTask(new RunSchematicTask(schemaName, options))
```

### To run schematic from external collection

#### Create a rule

```typescript
rule = externalSchematic(collectionName, schemaName, options)
```

#### Add task in context

```typescript
context.addTask(new RunSchematicTask(collectionName, schemaName, options))
```

Above schematics are available on  [GitHub](https://github.com/shhdharmen/run-schematics).

Thanks for reading!

---

## Credits

I would like to thanks  [@kasparovairina](https://twitter.com/kasparovairina) for creating great banner for this article.