#angular #cli #schematics #builders #automation

Day 28 — Advanced CLI: Custom Schematics & Builders

📘 Day 28 — Advanced CLI: Custom Schematics & Builders

Zero to Hero — Hands-on Angular Tutorial

Today you will learn:

  • ✔️ What are Schematics? (ng generate ...)
  • ✔️ Installing the Schematics CLI
  • ✔️ Creating a Custom Schematic (e.g., “Company Standard Component”)
  • ✔️ What are Builders? (ng build ...)
  • ✔️ Creating a Builder (e.g., “Deploy to AWS”)

You use ng generate component every day. Today, you will learn how to write your own ng generate my-company-component. 🛠️


🟦 1. What are Schematics?

Schematics are code generators. They take a “Tree” (your file system), apply transformations (create/modify files), and return a new “Tree”.

Why build your own?

  • Enforce naming conventions.
  • Auto-generate Service + Store + Component files in one command.
  • Update 50 files automatically during a migration.

🟩 2. Creating a Schematic

Step 1: Install Tools

npm install -g @angular-devkit/schematics-cli

Step 2: Create Project

schematics blank my-schematic
cd my-schematic

Step 3: Define the Logic Edit src/my-schematic/index.ts:

import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';

export function mySchematic(_options: any): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    
    // 1. Create a file
    tree.create('hello-world.txt', 'Hello from your Custom Schematic!');
    
    // 2. Log message
    _context.logger.info('🎉 Created hello-world.txt');

    return tree;
  };
}

Step 4: Build & Run

npm run build
schematics .:my-schematic

It should generate hello-world.txt in your current folder!


🟧 3. Using Templates (EJS)

Creating files string-by-string is hard. We use Templates.

Create Template File: src/my-schematic/files/__name@dasherize__.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-<%= dasherize(name) %>',
  template: `<h1><%= classify(name) %> Works!</h1>`
})
export class <%= classify(name) %>Component {}
  • <%= name %>: Variable replacement.
  • __name@dasherize__: Dynamic filename (e.g., my-component.component.ts).

Update index.ts to use TemplateSource:

import { url, apply, template, mergeWith } from '@angular-devkit/schematics';
import { strings } from '@angular-devkit/core';

export function myComponent(options: any): Rule {
  return () => {
    const source = apply(url('./files'), [
      template({
        ...options,
        ...strings // helper functions like dasherize, classify
      })
    ]);
    return mergeWith(source);
  };
}

Now schematics .:my-component --name=UserProfile creates a real Angular component!


🟥 4. What are Builders?

Builders control the process (ng build, ng test, ng deploy). They don’t generate code; they run tasks.

Example:

  • ng build uses @angular-devkit/build-angular:browser
  • ng test uses @angular-devkit/build-angular:karma

Why build a Custom Builder?

  • “I want ng deploy to upload to my FTP server.”
  • “I want ng build to also compress images.”

🟫 5. Creating a Custom Builder

Step 1: Create File builder.ts

import { createBuilder, BuilderContext, BuilderOutput } from '@angular-devkit/architect';

export default createBuilder(commandBuilder);

async function commandBuilder(
  options: any,
  context: BuilderContext,
): Promise<BuilderOutput> {
  context.reportStatus(`Executing custom command...`);
  
  context.logger.info(`✨ Running custom build for: ${options.name}`);

  // Simulate work
  await new Promise(resolve => setTimeout(resolve, 1000));

  context.logger.info('✅ Done!');

  return { success: true };
}

Step 2: usage in angular.json

"my-custom-command": {
  "builder": "./my-builder:command",
  "options": {
    "name": "Production"
  }
}

Running ng run my-app:my-custom-command will now trigger your script.


🎉 End of Day 28 — What You Learned

Today you became a Toolmaker:

  • ✔️ Schematics: How to generate code programmatically.
  • ✔️ Templating: Using EJS to create dynamic files.
  • ✔️ Builders: How to hook into the ng command system.

Automation is the key to scaling huge teams. 🚀


🧪 Day 28 Challenge

“The Service Generator”

  1. Create a schematic named fast-service.
  2. It should accept a --name argument.
  3. It should create [name].service.ts.
  4. The service template should automatically include:
    • providedIn: 'root'
    • A dummy getItems() method that returns an Observable.
    • Imported HttpClient automatically.
  5. Test it by generating user.service.ts.

Want the solution? Say: “Show the Day 28 challenge solution”