Angular Schematics

Santosh Yadav

GDE for Angular, GitHub Star, Auth0 Ambassador

Writer AngularInDepth, Software Consultant

 

twitter.com/SantoshYadavDev

github.com/SantoshYadavDev

https://www.linkedin.com/in/yadavsantosh/

santoshyadav.dev

 

Schematics

Automate file creation/modification

Automate creating application/libraries

Automate Installation

Writing Custom Schematics

Rule

Tree

SchematicContext

Action

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

schematics blank --name=ng-add
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';


// You don't have to export the function as default. You can also have more than one rule factory
// per file.
export function ngAdd(_options: any): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    _context.addTask(new NodePackageInstallTask());

    //add a new file
    tree.create('index.ts',`console.log('Schematics is amazing')`);

    //read a file
    const fileData= tree.read('index.ts') || '';
    _context.logger.info(fileData?.toString());

    //overwrite a file
    tree.overwrite('index.ts',`console.log('schematics is great')`)
    _context.logger.info(fileData?.toString());

    //delete a file 
    tree.delete('index.ts');
    
    return tree;
  };
}

generate

import { Tree } from "@angular-devkit/schematics/src/tree/interface";
import { Rule, apply, url, applyTemplates, chain, branchAndMerge, mergeWith, SchematicContext } from "@angular-devkit/schematics";
import { strings } from "@angular-devkit/core";


export function generate(_options: any): Rule {
    return (tree: Tree, context: SchematicContext) => {
        const template = apply(url('./files'), [
            applyTemplates({
                ...strings, 
                ..._options 
            })
        ])

        return chain([
            branchAndMerge(mergeWith(template))
        ])(tree, context);
    }
}

generate/index.ts

{
  "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
  "schematics": {
    "ng-add": {
      "description": "A blank schematic.",
      "factory": "./ng-add/index#ngAdd"
    },
    "generate": {
      "description": "Generate schematic.",
      "factory": "./generate/index#generate"
    }
  }
}

collection.json

{
    "properties": {
        "name": {
            "type": "string",
            "minLength": 1,
            "default": "world",
            "x-prompt": "What is your name?"
        },
        "useColor": {
            "type": "boolean",
            "x-prompt": "Would you like the response in color?"
        }
    }
}

schema.json

 "schematics": "./src/collection.json",

package.json

// will not make any changes as debug is always false
schematcs .:ng-add

// will make actual changes
schematics .:ng-add --debug false


// test generate schematics
schematics .:generate --name test --debug false

Test Schematics

References

https://angular.io/guide/schematics

https://medium.com/@tomastrajan/total-guide-to-custom-angular-schematics-5c50cf90cdb4

https://indepth.dev/posts/1323/angular-schematics-from-0-to-publishing-your-own-library-i

Angular Builders

History

 "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json"
    },
    {
      "project": "src/tsconfig.spec.json"
    },
    {
      "project": "e2e/tsconfig.e2e.json"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  }
 "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json"
    },
    {
      "project": "src/tsconfig.spec.json"
    },
    {
      "project": "e2e/tsconfig.e2e.json"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  }

Issues

Overriding Webpack Configuration.

Building and Publishing Angular Libraries.

Using Other Tools for Test like Jest or cypress.io.

Old Solution

ng eject

Solution

Builders API

"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "browserTarget": "bulma-app:build"
  },
  "configurations": {
    "production": {
      "browserTarget": "bulma-app:build:production"
    }
  }
}
"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "browserTarget": "bulma-app:build"
  },
  "configurations": {
    "production": {
      "browserTarget": "bulma-app:build:production"
    }
  }
}
"build": {
  "builder": "@angular-devkit/build-angular:browser",
  "options": {
    "outputPath": "dist/bulma-app",
    "index": "projects/bulma-app/src/index.html",
    "main": "projects/bulma-app/src/main.ts",
    "polyfills": "projects/bulma-app/src/polyfills.ts",
    "tsConfig": "projects/bulma-app/tsconfig.app.json",
    "aot": false,
    "assets": [
    ],
    "styles": [
    ],
    "scripts": [      
    ]
  },
  "configurations": {
    "production": {
    }
  }
}
"build": {
  "builder": "@angular-devkit/build-angular:browser",
  "options": {
    "outputPath": "dist/bulma-app",
    "index": "projects/bulma-app/src/index.html",
    "main": "projects/bulma-app/src/main.ts",
    "polyfills": "projects/bulma-app/src/polyfills.ts",
    "tsConfig": "projects/bulma-app/tsconfig.app.json",
    "aot": false,
    "assets": [
    ],
    "styles": [
    ],
    "scripts": [      
    ]
  },
  "configurations": {
    "production": {
    }
  }
}
"build": {
  "builder": "@angular-devkit/build-ng-packagr:build",
  "options": {
    "tsConfig": "projects/ngx-bulma/tsconfig.lib.json",
    "project": "projects/ngx-bulma/ng-package.json"
  }
}
"build": {
  "builder": "@angular-devkit/build-ng-packagr:build",
  "options": {
    "tsConfig": "projects/ngx-bulma/tsconfig.lib.json",
    "project": "projects/ngx-bulma/ng-package.json"
  }
}
"test": {
  "builder": "@angular-devkit/build-angular:karma",
  "options": {
    "main": "projects/bulma-app/src/test.ts",
    "polyfills": "projects/bulma-app/src/polyfills.ts",
    "tsConfig": "projects/bulma-app/tsconfig.spec.json",
    "karmaConfig": "projects/bulma-app/karma.conf.js",
    "assets": [
      "projects/bulma-app/src/favicon.ico",
      "projects/bulma-app/src/assets"
    ],
    "styles": [
      "projects/bulma-app/src/styles.css"
    ],
    "scripts": [
    ]
  }
}
"test": {
  "builder": "@angular-devkit/build-angular:karma",
  "options": {
    "main": "projects/bulma-app/src/test.ts",
    "polyfills": "projects/bulma-app/src/polyfills.ts",
    "tsConfig": "projects/bulma-app/tsconfig.spec.json",
    "karmaConfig": "projects/bulma-app/karma.conf.js",
    "assets": [
      "projects/bulma-app/src/favicon.ico",
      "projects/bulma-app/src/assets"
    ],
    "styles": [
      "projects/bulma-app/src/styles.css"
    ],
    "scripts": [
    ]
  }
}
"e2e": {
  "builder": "@angular-devkit/build-angular:protractor",
  "options": {
    "protractorConfig": "projects/bulma-app/e2e/protractor.conf.js",
    "devServerTarget": "bulma-app:serve"
  },
  "configurations": {
    "production": {
      "devServerTarget": "bulma-app:serve:production"
    }
  }
}
"e2e": {
  "builder": "@angular-devkit/build-angular:protractor",
  "options": {
    "protractorConfig": "projects/bulma-app/e2e/protractor.conf.js",
    "devServerTarget": "bulma-app:serve"
  },
  "configurations": {
    "production": {
      "devServerTarget": "bulma-app:serve:production"
    }
  }
}

Custom Builders

import { BuilderContext, BuilderOutput, createBuilder } from
    '@angular-devkit/architect';
import { json } from '@angular-devkit/core';
import { schema as BuilderOptions } from './schema';

export default createBuilder<json.JsonObject
    & BuilderOptions>(execute);

async function execute(options: BuilderOptions,
    context: BuilderContext)
    : Promise<BuilderOutput> {
    // custom logic to process
}

index.ts

import { BuilderContext, BuilderOutput, createBuilder } from
    '@angular-devkit/architect';
import { json } from '@angular-devkit/core';
import { schema as BuilderOptions } from './schema';

export default createBuilder<json.JsonObject
    & BuilderOptions>(execute);

async function execute(options: BuilderOptions,
    context: BuilderContext)
    : Promise<BuilderOutput> {
    // custom logic to process
}

index.ts

    const build = await context.scheduleTarget({
      target: 'build',
      project: context.target?.project || '',
      configuration: 'production'
    });

    const schduleBuild = await context.scheduleBuilder('@angular-devkit/build-angular:browser', {
      main: 'src/main.ts',
      polyfills: "src/polyfills.ts",
    });

index.ts

context.reportStatus('running');
      // "error",
      // "running",
      // "stopped",
      // "waiting"

context.reportRunning();

index.ts

{
  "$schema": "@angular-devkit/architect/src/builders-schema.json",
  "builders": {
    "custom_builder_name": {
      "implementation": "./deploy",
      "schema": "./deploy/schema.json",
      "description": "Runs any command line in the operating system."
    }
  }
}

builders.json

{
  "$schema": "@angular-devkit/architect/src/builders-schema.json",
  "builders": {
    "custom_builder_name": {
      "implementation": "./deploy",
      "schema": "./deploy/schema.json",
      "description": "Runs any command line in the operating system."
    }
  }
}

builders.json

{
  "name": "@netlify-builder/deploy",
  "version": "2.0.3",
  "description": "A Netlify builder schematics for deployment",
  "main": "index.js",
  "builders": "./builders.json"
}

package.json

{
  "name": "@netlify-builder/deploy",
  "version": "2.0.3",
  "description": "A Netlify builder schematics for deployment",
  "main": "index.js",
  "builders": "./builders.json"
}

package.json

Using Builder​

"custom_builder_name": {
  "builder": "package:custom_builder_name",
  "options": {
    // provide options
  }
}

ng run app_name:builder_name

ngx-build-plus

@angular/fire:deploy

@angular-builders/jest

@azure/ng-deploy

Builders By Community

ng deploy

References

Thanks for writing about schematics and Builders

 

twitter.com/SantoshYadavDev

github.com/santoshyadav198613

https://www.linkedin.com/in/yadavsantosh/

santoshyadav.dev

 

Thank you

Made with Slides.com