Upgrade to Pro — share decks privately, control downloads, hide ads and more …

schematics-wdc-19

 schematics-wdc-19

Gregor Woiwode

October 14, 2019
Tweet

More Decks by Gregor Woiwode

Other Decks in Programming

Transcript

  1. Agenda ▪ What is a schematic? ▪ Dive into schematic

    projects ▪ Rules ▪ Templates ▪ ng-add ▪ ng-update
  2. When do we need Schematics? ▪ Scaffold files to match

    Coding Guidelines ▪ Automatically migrate breaking changes introduced by our own libraries ▪ Simplify setup of 3-party-libraries
  3. What benefits bring Schematics? ▪ ⏱ Saves time when ➝

    solving recurring developer tasks ➝ setting up 3-party-libraries ➝ migrating to newer library versions ➝ sharing project wide coding guidelines
  4. Define a collection collection.json ... "schematic-a": { "aliases": ["a"], "description":

    "Short description", "factory": "./schematic-a/index#methodName", "schema": "./schematic-a/schema.json", "private": false|true }
  5. Define a schema schematic-a/schema.json { "$schema": "http://json-schema.org/schema", "id": "schmatic-a", "title":

    "schematic title", "type": "object", "description": "schematic description", "properties": { ... }, "required": ["name"] }
  6. Schematic parameters schematic-a/schema.json { "properties": { "yourProperty": { "type": "string",

    "description": "The name of the Component.", "default": "some default", "x-prompt": "What name would you like to use for the Component?" } } }
  7. Schematic parameters schematic-a/schema.json { "properties": { "yourProperty": { "type": "string",

    "description": "Short description", "$default": { "$source": "argv", "index": 0 }, "x-prompt": "What name would you like to use for the Component?" } } }
  8. Define a factory schematic-a/index.ts import { HostTree } from '@angular-devkit/schematics';

    export function factory(): Rule { return (tree: HostTree) => { return tree; }; }
  9. Define a factory taking parameters schematic-a/index.ts import { HostTree }

    from '@angular-devkit/schematics'; export function factory(options: any): Rule { return (tree: HostTree) => { return tree; }; } Properties defined in schema.json
  10. Relation between collection & schematic index.ts import { Observable }

    from 'rxjs'; const helloWorld$ = new Observable( function subscribe(observer) { observer.next('Hello'); observer.next('World'); observer.complete(); } ); collection.json "schematic-a": { "aliases": ["f"], "description": "...", "factory": ".../index#factory", "schema": "..../schema.json", } export function factory(): Rule { return (tree: HostTree) => { return tree; }; }
  11. HostTree, Tree ▪ Tree → Staging area for changes ▪

    Contains ➝ the original file system ➝ a list of changes to apply to tree ➝ Virtual representation of every file in the workspace ▪ Tree is an interface ▪ HostTree implements Tree interface ▪ Provides API for work with file system and changes
  12. create() HostTree API Example import { Tree } from '@angular-devkit/schematics';

    tree.create(path: string, content: Buffer | string) // Example tree.create('./welcome.txt', 'Welcome to WDC 2018!'); Filename or/with path to file Content to be inserted in the file
  13. exists() HostTree API Example import { Tree } from '@angular-devkit/schematics';

    tree.exists(path: string): boolean // Example if (tree.exists('./welcome.txt')) { // do something if file existing } Filename or/with path to file
  14. overwrite() HostTree API Example import { Tree } from '@angular-devkit/schematics';

    tree.overwrite(path: string, content: Buffer | string) // Example tree.overwrite('./welcome.txt', 'Welcome to WDC 2019!'); Path to the file to be overwritten Overwrite with new content
  15. beginUpdate() and commitUpdate() HostTree API Example import { Tree }

    from '@angular-devkit/schematics'; tree.overwrite(path: string, content: Buffer | string) // Example tree.overwrite('./welcome.txt', 'Welcome to WDC 2019!'); Path to the file to be overwritten Overwrite with new content
  16. Context ▪ Each schematic runs in a context ▪ Provides

    access to utility functions and metadata ▪ Including a logging API → Helps with debugging ▪ Defines merge strategy
  17. Logging API SchematicContext API Example import { SchematicContext } from

    '@angular-devkit/schematics'; return (tree: Tree, context: SchematicContext) => { context.logger.info('Hello WDC 2019!'); context.logger.error('Error!'); context.logger.warn('Warning!'); ... return tree; } Define schematic context Write warning message to console
  18. Tasks ▪ SchematicContext object allow to run tasks ▪ Usage:

    context.addTask() ▪ e.g.: NodePackageInstallTask() ▪ e.g.: RunSchematicTask()
  19. Template Templates @Component({ selector: <%= selector %>, templateUrl: './<%= kebabCase(name)

    %>.page.html', }) export class <%= upperFirst(camelCase(name)) %>Page implements OnInit { <% if(routePath) { %>private route: ActivatedRoute <% } %> }
  20. File operators ▪ Allow loading and transforming file templates. ▪

    Create own virtual file streams. ▪ Turn virtual file streams into reality by merging them into host tree.
  21. API Demo Load, transform, merge const templates = apply( url('./files'),

    [ applyTemplates({ ...options, ...strings }) ]); Create a virtual file tree
  22. API Demo Load, transform, merge const templates = apply( url('./files'),

    [ applyTemplates({ ...options, ...strings }) ]); Load templates from path.
  23. Factory Load & transform const templates = apply( url('./files'), [

    applyTemplates({ ...options, ...strings }) ]); Merge options and pipes into templates
  24. Merge into tree Factory export function component(options: ComponentOptions): Rule {

    return (_host: HostTree) => { const templates = apply(url('./files'), [ applyTemplates({ ...options, ...strings }) ]); return chain([mergeWith(templates)]); }; }
  25. ng-add ▪ Automatically add library to project ▪ Download and

    install dependencies ▪ Invokes installation script
  26. collection.json Define ng-add collection ... "ng-add": { "aliases": ["install"], "description":

    "Custom ng-add schematic", "factory": "./ng-add/index", "schema": "./ng-add/schema.json" } ...
  27. Relation between collection & schematic index.ts import { Observable }

    from 'rxjs'; const helloWorld$ = new Observable( function subscribe(observer) { observer.next('Hello'); observer.next('World'); observer.complete(); } ); collection.json ... "ng-add": { "aliases": ["install"], "description": "...", "factory": "./ng-add/index", "schema": "./ng-add/schema.json" } export default function(options: any): Rule { return (tree: Tree,) => { // ... return tree; } }
  28. Helper tools ▪ Add node dependencies to package.json ▪ Interface

    for node dependency ▪ Enum for node dependency types
  29. NodeDependency and NodeDependencyType dependencies.d.ts export interface NodeDependency { type: NodeDependencyType;

    name: string; version: string; overwrite?: boolean; } dependencies.d.ts export enum NodeDependencyType { Default = "dependencies", Dev = "devDependencies", Peer = "peerDependencies", Optional = "optionalDependencies" }
  30. index.ts addPackageJsonDependency() import { addPackageJsonDependency, NodeDependency } from '@schematics/angular/utility/dependencies'; ...

    function addPackageJsonDependencies(): Rule { return (tree: Tree, context: SchematicContext) => { const dependency: NodeDependency = { ... }; addPackageJsonDependency(tree, dependency); return tree; } }
  31. migrations.json Define migration { "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "migration-v2": {

    "version": "2.0.0", "description": "Add test file to component", "factory": "./ng-update/index#updateToV2" } } }
  32. Relation between migrations & schematic index.ts import { Observable }

    from 'rxjs'; const helloWorld$ = new Observable( function subscribe(observer) { observer.next('Hello'); observer.next('World'); observer.complete(); } ); migrations.json ... "migration-v2": { "version": "2.0.0", "description": "...", "factory": ".../index#migration", } export function migration(): Rule { return (tree: HostTree) => { // ... return tree; }; }
  33. Assets | Code hello-world.ts hello-world.ts import { Observable } from

    'rxjs'; const helloWorld$ = new Observable( function subscribe(observer) { observer.next('Hello'); observer.next('World'); observer.complete(); } ); // ... const observer = { next: (value) => console.log(value), error: (err) => console.error(err), complete: () => console.log('done') }; const subscription = helloWorld$.subscribe(observer);