Ferramentas de Desenvolvimento
Angular CLI

Schematics para bibliotecas

Quando você cria uma biblioteca Angular, pode fornecê-la e empacotá-la com schematics que a integram com o Angular CLI. Com seus schematics, seus usuários podem usar ng add para instalar uma versão inicial de sua biblioteca, ng generate para criar artefatos definidos em sua biblioteca e ng update para ajustar seu projeto para uma nova versão de sua biblioteca que introduz mudanças incompatíveis.

Todos os três tipos de schematics podem fazer parte de uma coleção que você empacota com sua biblioteca.

Criando uma coleção de schematics

Para iniciar uma coleção, você precisa criar os arquivos de schematic. Os passos a seguir mostram como adicionar suporte inicial sem modificar nenhum arquivo do projeto.

  1. Na pasta raiz de sua biblioteca, crie uma pasta schematics.

  2. Na pasta schematics/, crie uma pasta ng-add para seu primeiro schematic.

  3. No nível raiz da pasta schematics, crie um arquivo collection.json.

  4. Edite o arquivo collection.json para definir o schema inicial para sua coleção.

    projects/my-lib/schematics/collection.json (Schematics Collection)

    {  "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",  "schematics": {    "ng-add": {      "description": "Add my library to the project.",      "factory": "./ng-add/index#ngAdd",      "schema": "./ng-add/schema.json"    }  }}
    • O caminho $schema é relativo ao schema de coleção do Angular Devkit.
    • O objeto schematics descreve os schematics nomeados que fazem parte desta coleção.
    • A primeira entrada é para um schematic chamado ng-add. Ela contém a descrição e aponta para a função factory que é chamada quando seu schematic é executado.
  5. No arquivo package.json do projeto de sua biblioteca, adicione uma entrada "schematics" com o caminho para seu arquivo de schema. O Angular CLI usa esta entrada para encontrar schematics nomeados em sua coleção quando executa comandos.

projects/my-lib/package.json (Schematics Collection Reference)

{  "name": "my-lib",  "version": "0.0.1",  "scripts": {    "build": "tsc -p tsconfig.schematics.json",    "postbuild": "copyfiles schematics/*/schema.json schematics/*/files/** schematics/collection.json ../../dist/my-lib/"  },  "peerDependencies": {    "@angular/common": "^20.0.0",    "@angular/core": "^20.0.0"  },  "schematics": "./schematics/collection.json",  "ng-add": {    "save": "devDependencies"  },  "devDependencies": {    "copyfiles": "file:../../node_modules/copyfiles",    "typescript": "file:../../node_modules/typescript"  }}

O schema inicial que você criou diz ao CLI onde encontrar o schematic que suporta o comando ng add. Agora você está pronto para criar esse schematic.

Fornecendo suporte de instalação

Um schematic para o comando ng add pode aprimorar o processo de instalação inicial para seus usuários. Os passos a seguir definem este tipo de schematic.

  1. Vá para a pasta <lib-root>/schematics/ng-add.
  2. Crie o arquivo principal, index.ts.
  3. Abra index.ts e adicione o código-fonte para sua função factory de schematic.

projects/my-lib/schematics/ng-add/index.ts (ng-add Rule Factory)

import {Rule} from '@angular-devkit/schematics';import {addRootImport} from '@schematics/angular/utility';import {Schema} from './schema';export function ngAdd(options: Schema): Rule {  // Add an import `MyLibModule` from `my-lib` to the root of the user's project.  return addRootImport(    options.project,    ({code, external}) => code`${external('MyLibModule', 'my-lib')}`,  );}

O Angular CLI instalará a versão mais recente da biblioteca automaticamente, e este exemplo está indo um passo além adicionando o MyLibModule à raiz da aplicação. A função addRootImport aceita um callback que precisa retornar um bloco de código. Você pode escrever qualquer código dentro da string marcada com a função code e qualquer símbolo externo deve ser envolvido com a função external para garantir que as instruções de import apropriadas sejam geradas.

Definir tipo de dependência

Use a opção save de ng-add para configurar se a biblioteca deve ser adicionada às dependencies, às devDependencies ou não salva no arquivo de configuração package.json do projeto.

projects/my-lib/package.json (ng-add Reference)

{  "name": "my-lib",  "version": "0.0.1",  "scripts": {    "build": "tsc -p tsconfig.schematics.json",    "postbuild": "copyfiles schematics/*/schema.json schematics/*/files/** schematics/collection.json ../../dist/my-lib/"  },  "peerDependencies": {    "@angular/common": "^20.0.0",    "@angular/core": "^20.0.0"  },  "schematics": "./schematics/collection.json",  "ng-add": {    "save": "devDependencies"  },  "devDependencies": {    "copyfiles": "file:../../node_modules/copyfiles",    "typescript": "file:../../node_modules/typescript"  }}

Valores possíveis são:

Valores Detalhes
false Não adicione o pacote ao package.json
true Adicione o pacote às dependencies
"dependencies" Adicione o pacote às dependencies
"devDependencies" Adicione o pacote às devDependencies

Construindo seus schematics

Para empacotar seus schematics junto com sua biblioteca, você deve configurar a biblioteca para construir os schematics separadamente e depois adicioná-los ao pacote. Você deve construir seus schematics depois de construir sua biblioteca, para que sejam colocados no diretório correto.

  • Sua biblioteca precisa de um arquivo de configuração TypeScript personalizado com instruções sobre como compilar seus schematics em sua biblioteca distribuída
  • Para adicionar os schematics ao pacote da biblioteca, adicione scripts ao arquivo package.json da biblioteca

Suponha que você tenha um projeto de biblioteca my-lib em seu workspace Angular. Para dizer à biblioteca como construir os schematics, adicione um arquivo tsconfig.schematics.json ao lado do arquivo tsconfig.lib.json gerado que configura a construção da biblioteca.

  1. Edite o arquivo tsconfig.schematics.json para adicionar o seguinte conteúdo.

    projects/my-lib/tsconfig.schematics.json (TypeScript Config)

    {  "compilerOptions": {    "baseUrl": ".",    "lib": [      "es2018",      "dom"    ],    "declaration": true,    "module": "commonjs",    "moduleResolution": "node",    "noEmitOnError": true,    "noFallthroughCasesInSwitch": true,    "noImplicitAny": true,    "noImplicitThis": true,    "noUnusedParameters": true,    "noUnusedLocals": true,    "rootDir": "schematics",    "outDir": "../../dist/my-lib/schematics",    "skipDefaultLibCheck": true,    "skipLibCheck": true,    "sourceMap": true,    "strictNullChecks": true,    "target": "es6",    "types": [      "jasmine",      "node"    ]  },  "include": [    "schematics/**/*"  ],  "exclude": [    "schematics/*/files/**/*"  ]}
    Opções Detalhes
    rootDir Especifica que sua pasta schematics contém os arquivos de entrada a serem compilados.
    outDir Mapeia para a pasta de saída da biblioteca. Por padrão, esta é a pasta dist/my-lib na raiz de seu workspace.
  2. Para garantir que seus arquivos-fonte de schematics sejam compilados no pacote da biblioteca, adicione os seguintes scripts ao arquivo package.json na pasta raiz do projeto de sua biblioteca (projects/my-lib).

    projects/my-lib/package.json (Build Scripts)

    {  "name": "my-lib",  "version": "0.0.1",  "scripts": {    "build": "tsc -p tsconfig.schematics.json",    "postbuild": "copyfiles schematics/*/schema.json schematics/*/files/** schematics/collection.json ../../dist/my-lib/"  },  "peerDependencies": {    "@angular/common": "^20.0.0",    "@angular/core": "^20.0.0"  },  "schematics": "./schematics/collection.json",  "ng-add": {    "save": "devDependencies"  },  "devDependencies": {    "copyfiles": "file:../../node_modules/copyfiles",    "typescript": "file:../../node_modules/typescript"  }}
    • O script build compila seu schematic usando o arquivo personalizado tsconfig.schematics.json
    • O script postbuild copia os arquivos de schematic após a conclusão do script build
    • Tanto o script build quanto o postbuild requerem as dependências copyfiles e typescript. Para instalar as dependências, navegue até o caminho definido em devDependencies e execute npm install antes de executar os scripts.

Fornecendo suporte de geração

Você pode adicionar um schematic nomeado à sua coleção que permite que seus usuários usem o comando ng generate para criar um artefato definido em sua biblioteca.

Vamos supor que sua biblioteca defina um service, my-service, que requer alguma configuração. Você quer que seus usuários possam gerá-lo usando o seguinte comando CLI.

ng generate my-lib:my-service

Para começar, crie uma nova subpasta, my-service, na pasta schematics.

Configurar o novo schematic

Quando você adiciona um schematic à coleção, você precisa apontá-lo no schema da coleção e fornecer arquivos de configuração para definir opções que um usuário pode passar ao comando.

  1. Edite o arquivo schematics/collection.json para apontar para a nova subpasta de schematic e incluir um ponteiro para um arquivo de schema que especifica entradas para o novo schematic.

projects/my-lib/schematics/collection.json (Schematics Collection)

{  "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",  "schematics": {    "ng-add": {      "description": "Add my library to the project.",      "factory": "./ng-add/index#ngAdd",      "schema": "./ng-add/schema.json"    },    "my-service": {      "description": "Generate a service in the project.",      "factory": "./my-service/index#myService",      "schema": "./my-service/schema.json"    }  }}
  1. Vá para a pasta <lib-root>/schematics/my-service.

  2. Crie um arquivo schema.json e defina as opções disponíveis para o schematic.

    projects/my-lib/schematics/my-service/schema.json (Schematic JSON Schema)

    {  "$schema": "http://json-schema.org/schema",  "$id": "SchematicsMyService",  "title": "My Service Schema",  "type": "object",  "properties": {    "name": {      "description": "The name of the service.",      "type": "string"    },    "path": {      "type": "string",      "format": "path",      "description": "The path to create the service.",      "visible": false,      "$default": {        "$source": "workingDirectory"      }    },    "project": {      "type": "string",      "description": "The name of the project.",      "$default": {        "$source": "projectName"      }    }   },  "required": [    "name"  ]}
    • id: Um ID único para o schema na coleção.
    • title: Uma descrição legível do schema.
    • type: Um descritor para o tipo fornecido pelas propriedades.
    • properties: Um objeto que define as opções disponíveis para o schematic.

    Cada opção associa uma chave com um tipo, descrição e alias opcional. O tipo define a forma do valor que você espera, e a descrição é exibida quando o usuário solicita ajuda de uso para seu schematic.

    Veja o schema do workspace para personalizações adicionais para opções de schematic.

  3. Crie um arquivo schema.ts e defina uma interface que armazena os valores das opções definidas no arquivo schema.json.

    projects/my-lib/schematics/my-service/schema.ts (Schematic Interface)

    export interface Schema {  // The name of the service.  name: string;  // The path to create the service.  path?: string;  // The name of the project.  project?: string;}
    Opções Detalhes
    name O nome que você deseja fornecer para o service criado.
    path Substitui o caminho fornecido ao schematic. O valor padrão do caminho é baseado no diretório de trabalho atual.
    project Fornece um projeto específico para executar o schematic. No schematic, você pode fornecer um padrão se a opção não for fornecida pelo usuário.

Adicionar arquivos de template

Para adicionar artefatos a um projeto, seu schematic precisa de seus próprios arquivos de template. Templates de schematic suportam sintaxe especial para executar código e substituição de variáveis.

  1. Crie uma pasta files/ dentro da pasta schematics/my-service/.
  2. Crie um arquivo chamado __name@dasherize__.service.ts.template que define um template a ser usado para gerar arquivos. Este template gerará um service que já tem o HttpClient do Angular injetado em uma propriedade http.

    projects/my-lib/schematics/my-service/files/__name@dasherize__.service.ts.template (Schematic Template)

    import { Injectable } from '@angular/core';import { HttpClient } from '@angular/common/http';@Injectable({providedIn: 'root'})export class <%= classify(name) %>Service {private http = inject(HttpClient);}
    • Os métodos classify e dasherize são funções utilitárias que seu schematic usa para transformar seu template de origem e nome de arquivo.
    • O name é fornecido como uma propriedade de sua função factory. É o mesmo name que você definiu no schema.

Adicionar a função factory

Agora que você tem a infraestrutura em vigor, pode definir a função principal que executa as modificações necessárias no projeto do usuário.

O framework Schematics fornece um sistema de template de arquivo, que suporta templates de caminho e conteúdo. O sistema opera em placeholders definidos dentro de arquivos ou caminhos que são carregados na Tree de entrada. Ele os preenche usando valores passados para a Rule.

Para detalhes dessas estruturas de dados e sintaxe, veja o Schematics README.

  1. Crie o arquivo principal index.ts e adicione o código-fonte para sua função factory de schematic.
  2. Primeiro, importe as definições de schematics que você precisará. O framework Schematics oferece muitas funções utilitárias para criar e usar regras ao executar um schematic.

projects/my-lib/schematics/my-service/index.ts (Imports)

import {  Rule,  Tree,  SchematicsException,  apply,  url,  applyTemplates,  move,  chain,  mergeWith,} from '@angular-devkit/schematics';import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';import {Schema as MyServiceSchema} from './schema';function createHost(tree: Tree): workspaces.WorkspaceHost {  return {    async readFile(path: string): Promise<string> {      const data = tree.read(path);      if (!data) {        throw new SchematicsException('File not found.');      }      return virtualFs.fileBufferToString(data);    },    async writeFile(path: string, data: string): Promise<void> {      return tree.overwrite(path, data);    },    async isDirectory(path: string): Promise<boolean> {      return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;    },    async isFile(path: string): Promise<boolean> {      return tree.exists(path);    },  };}export function myService(options: MyServiceSchema): Rule {  return async (tree: Tree) => {    const host = createHost(tree);    const {workspace} = await workspaces.readWorkspace('/', host);    const project = options.project != null ? workspace.projects.get(options.project) : null;    if (!project) {      throw new SchematicsException(`Invalid project name: ${options.project}`);    }    const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';    if (options.path === undefined) {      options.path = `${project.sourceRoot}/${projectType}`;    }    const templateSource = apply(url('./files'), [      applyTemplates({        classify: strings.classify,        dasherize: strings.dasherize,        name: options.name,      }),      move(normalize(options.path as string)),    ]);    return chain([mergeWith(templateSource)]);  };}
  1. Importe a interface de schema definida que fornece as informações de tipo para as opções do seu schematic.

projects/my-lib/schematics/my-service/index.ts (Schema Import)

import {  Rule,  Tree,  SchematicsException,  apply,  url,  applyTemplates,  move,  chain,  mergeWith,} from '@angular-devkit/schematics';import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';import {Schema as MyServiceSchema} from './schema';function createHost(tree: Tree): workspaces.WorkspaceHost {  return {    async readFile(path: string): Promise<string> {      const data = tree.read(path);      if (!data) {        throw new SchematicsException('File not found.');      }      return virtualFs.fileBufferToString(data);    },    async writeFile(path: string, data: string): Promise<void> {      return tree.overwrite(path, data);    },    async isDirectory(path: string): Promise<boolean> {      return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;    },    async isFile(path: string): Promise<boolean> {      return tree.exists(path);    },  };}export function myService(options: MyServiceSchema): Rule {  return async (tree: Tree) => {    const host = createHost(tree);    const {workspace} = await workspaces.readWorkspace('/', host);    const project = options.project != null ? workspace.projects.get(options.project) : null;    if (!project) {      throw new SchematicsException(`Invalid project name: ${options.project}`);    }    const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';    if (options.path === undefined) {      options.path = `${project.sourceRoot}/${projectType}`;    }    const templateSource = apply(url('./files'), [      applyTemplates({        classify: strings.classify,        dasherize: strings.dasherize,        name: options.name,      }),      move(normalize(options.path as string)),    ]);    return chain([mergeWith(templateSource)]);  };}
  1. Para construir o schematic de geração, comece com uma rule factory vazia.

projects/my-lib/schematics/my-service/index.ts (Initial Rule)

import {Rule, Tree} from '@angular-devkit/schematics';import {Schema as MyServiceSchema} from './schema';export function myService(options: MyServiceSchema): Rule {  return (tree: Tree) => tree;}

Esta rule factory retorna a tree sem modificação. As opções são os valores de opção passados através do comando ng generate.

Definir uma regra de geração

Agora você tem a estrutura em vigor para criar o código que realmente modifica a aplicação do usuário para configurá-la para o service definido em sua biblioteca.

O workspace Angular onde o usuário instalou sua biblioteca contém múltiplos projetos (aplicações e bibliotecas). O usuário pode especificar o projeto na linha de comando ou deixá-lo como padrão. Em qualquer caso, seu código precisa identificar o projeto específico ao qual este schematic está sendo aplicado, para que você possa recuperar informações da configuração do projeto.

Faça isso usando o objeto Tree que é passado para a função factory. Os métodos de Tree dão acesso à árvore de arquivos completa em seu workspace, permitindo que você leia e escreva arquivos durante a execução do schematic.

Obter a configuração do projeto

  1. Para determinar o projeto de destino, use o método workspaces.readWorkspace para ler o conteúdo do arquivo de configuração do workspace, angular.json. Para usar workspaces.readWorkspace você precisa criar um workspaces.WorkspaceHost a partir da Tree. Adicione o seguinte código à sua função factory.

    projects/my-lib/schematics/my-service/index.ts (Schema Import)

    import {  Rule,  Tree,  SchematicsException,  apply,  url,  applyTemplates,  move,  chain,  mergeWith,} from '@angular-devkit/schematics';import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';import {Schema as MyServiceSchema} from './schema';function createHost(tree: Tree): workspaces.WorkspaceHost {  return {    async readFile(path: string): Promise<string> {      const data = tree.read(path);      if (!data) {        throw new SchematicsException('File not found.');      }      return virtualFs.fileBufferToString(data);    },    async writeFile(path: string, data: string): Promise<void> {      return tree.overwrite(path, data);    },    async isDirectory(path: string): Promise<boolean> {      return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;    },    async isFile(path: string): Promise<boolean> {      return tree.exists(path);    },  };}export function myService(options: MyServiceSchema): Rule {  return async (tree: Tree) => {    const host = createHost(tree);    const {workspace} = await workspaces.readWorkspace('/', host);    const project = options.project != null ? workspace.projects.get(options.project) : null;    if (!project) {      throw new SchematicsException(`Invalid project name: ${options.project}`);    }    const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';    if (options.path === undefined) {      options.path = `${project.sourceRoot}/${projectType}`;    }    const templateSource = apply(url('./files'), [      applyTemplates({        classify: strings.classify,        dasherize: strings.dasherize,        name: options.name,      }),      move(normalize(options.path as string)),    ]);    return chain([mergeWith(templateSource)]);  };}

    Certifique-se de verificar que o contexto existe e lançar o erro apropriado.

  2. Agora que você tem o nome do projeto, use-o para recuperar as informações de configuração específicas do projeto.

    projects/my-lib/schematics/my-service/index.ts (Project)

    import {  Rule,  Tree,  SchematicsException,  apply,  url,  applyTemplates,  move,  chain,  mergeWith,} from '@angular-devkit/schematics';import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';import {Schema as MyServiceSchema} from './schema';function createHost(tree: Tree): workspaces.WorkspaceHost {  return {    async readFile(path: string): Promise<string> {      const data = tree.read(path);      if (!data) {        throw new SchematicsException('File not found.');      }      return virtualFs.fileBufferToString(data);    },    async writeFile(path: string, data: string): Promise<void> {      return tree.overwrite(path, data);    },    async isDirectory(path: string): Promise<boolean> {      return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;    },    async isFile(path: string): Promise<boolean> {      return tree.exists(path);    },  };}export function myService(options: MyServiceSchema): Rule {  return async (tree: Tree) => {    const host = createHost(tree);    const {workspace} = await workspaces.readWorkspace('/', host);    const project = options.project != null ? workspace.projects.get(options.project) : null;    if (!project) {      throw new SchematicsException(`Invalid project name: ${options.project}`);    }    const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';    if (options.path === undefined) {      options.path = `${project.sourceRoot}/${projectType}`;    }    const templateSource = apply(url('./files'), [      applyTemplates({        classify: strings.classify,        dasherize: strings.dasherize,        name: options.name,      }),      move(normalize(options.path as string)),    ]);    return chain([mergeWith(templateSource)]);  };}

    O objeto workspace.projects contém todas as informações de configuração específicas do projeto.

  3. O options.path determina para onde os arquivos de template do schematic são movidos uma vez que o schematic é aplicado.

    A opção path no schema do schematic é substituída por padrão pelo diretório de trabalho atual. Se o path não estiver definido, use o sourceRoot da configuração do projeto junto com o projectType.

    projects/my-lib/schematics/my-service/index.ts (Project Info)

    import {  Rule,  Tree,  SchematicsException,  apply,  url,  applyTemplates,  move,  chain,  mergeWith,} from '@angular-devkit/schematics';import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';import {Schema as MyServiceSchema} from './schema';function createHost(tree: Tree): workspaces.WorkspaceHost {  return {    async readFile(path: string): Promise<string> {      const data = tree.read(path);      if (!data) {        throw new SchematicsException('File not found.');      }      return virtualFs.fileBufferToString(data);    },    async writeFile(path: string, data: string): Promise<void> {      return tree.overwrite(path, data);    },    async isDirectory(path: string): Promise<boolean> {      return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;    },    async isFile(path: string): Promise<boolean> {      return tree.exists(path);    },  };}export function myService(options: MyServiceSchema): Rule {  return async (tree: Tree) => {    const host = createHost(tree);    const {workspace} = await workspaces.readWorkspace('/', host);    const project = options.project != null ? workspace.projects.get(options.project) : null;    if (!project) {      throw new SchematicsException(`Invalid project name: ${options.project}`);    }    const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';    if (options.path === undefined) {      options.path = `${project.sourceRoot}/${projectType}`;    }    const templateSource = apply(url('./files'), [      applyTemplates({        classify: strings.classify,        dasherize: strings.dasherize,        name: options.name,      }),      move(normalize(options.path as string)),    ]);    return chain([mergeWith(templateSource)]);  };}

Definir a regra

Uma Rule pode usar arquivos de template externos, transformá-los e retornar outro objeto Rule com o template transformado. Use o templating para gerar quaisquer arquivos personalizados necessários para seu schematic.

  1. Adicione o seguinte código à sua função factory.

    projects/my-lib/schematics/my-service/index.ts (Template transform)

    import {  Rule,  Tree,  SchematicsException,  apply,  url,  applyTemplates,  move,  chain,  mergeWith,} from '@angular-devkit/schematics';import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';import {Schema as MyServiceSchema} from './schema';function createHost(tree: Tree): workspaces.WorkspaceHost {  return {    async readFile(path: string): Promise<string> {      const data = tree.read(path);      if (!data) {        throw new SchematicsException('File not found.');      }      return virtualFs.fileBufferToString(data);    },    async writeFile(path: string, data: string): Promise<void> {      return tree.overwrite(path, data);    },    async isDirectory(path: string): Promise<boolean> {      return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;    },    async isFile(path: string): Promise<boolean> {      return tree.exists(path);    },  };}export function myService(options: MyServiceSchema): Rule {  return async (tree: Tree) => {    const host = createHost(tree);    const {workspace} = await workspaces.readWorkspace('/', host);    const project = options.project != null ? workspace.projects.get(options.project) : null;    if (!project) {      throw new SchematicsException(`Invalid project name: ${options.project}`);    }    const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';    if (options.path === undefined) {      options.path = `${project.sourceRoot}/${projectType}`;    }    const templateSource = apply(url('./files'), [      applyTemplates({        classify: strings.classify,        dasherize: strings.dasherize,        name: options.name,      }),      move(normalize(options.path as string)),    ]);    return chain([mergeWith(templateSource)]);  };}
    Métodos Detalhes
    apply() Aplica múltiplas regras a uma fonte e retorna a fonte transformada. Recebe 2 argumentos, uma fonte e um array de regras.
    url() Lê arquivos-fonte do seu sistema de arquivos, relativo ao schematic.
    applyTemplates() Recebe um argumento de métodos e propriedades que você deseja disponibilizar ao template do schematic e aos nomes de arquivo do schematic. Retorna uma Rule. É aqui que você define os métodos classify() e dasherize(), e a propriedade name.
    classify() Recebe um valor e retorna o valor em title case. Por exemplo, se o nome fornecido é my service, é retornado como MyService.
    dasherize() Recebe um valor e retorna o valor em dashed e minúsculas. Por exemplo, se o nome fornecido é MyService, é retornado como my-service.
    move() Move os arquivos-fonte fornecidos para seu destino quando o schematic é aplicado.
  2. Finalmente, a rule factory deve retornar uma regra.

    projects/my-lib/schematics/my-service/index.ts (Chain Rule)

    import {  Rule,  Tree,  SchematicsException,  apply,  url,  applyTemplates,  move,  chain,  mergeWith,} from '@angular-devkit/schematics';import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';import {Schema as MyServiceSchema} from './schema';function createHost(tree: Tree): workspaces.WorkspaceHost {  return {    async readFile(path: string): Promise<string> {      const data = tree.read(path);      if (!data) {        throw new SchematicsException('File not found.');      }      return virtualFs.fileBufferToString(data);    },    async writeFile(path: string, data: string): Promise<void> {      return tree.overwrite(path, data);    },    async isDirectory(path: string): Promise<boolean> {      return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;    },    async isFile(path: string): Promise<boolean> {      return tree.exists(path);    },  };}export function myService(options: MyServiceSchema): Rule {  return async (tree: Tree) => {    const host = createHost(tree);    const {workspace} = await workspaces.readWorkspace('/', host);    const project = options.project != null ? workspace.projects.get(options.project) : null;    if (!project) {      throw new SchematicsException(`Invalid project name: ${options.project}`);    }    const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';    if (options.path === undefined) {      options.path = `${project.sourceRoot}/${projectType}`;    }    const templateSource = apply(url('./files'), [      applyTemplates({        classify: strings.classify,        dasherize: strings.dasherize,        name: options.name,      }),      move(normalize(options.path as string)),    ]);    return chain([mergeWith(templateSource)]);  };}

    O método chain() permite combinar múltiplas regras em uma única regra, para que você possa executar múltiplas operações em um único schematic. Aqui você está apenas mesclando as regras de template com qualquer código executado pelo schematic.

Veja um exemplo completo da seguinte função de regra de schematic.

projects/my-lib/schematics/my-service/index.ts

import {  Rule,  Tree,  SchematicsException,  apply,  url,  applyTemplates,  move,  chain,  mergeWith,} from '@angular-devkit/schematics';import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';import {Schema as MyServiceSchema} from './schema';function createHost(tree: Tree): workspaces.WorkspaceHost {  return {    async readFile(path: string): Promise<string> {      const data = tree.read(path);      if (!data) {        throw new SchematicsException('File not found.');      }      return virtualFs.fileBufferToString(data);    },    async writeFile(path: string, data: string): Promise<void> {      return tree.overwrite(path, data);    },    async isDirectory(path: string): Promise<boolean> {      return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;    },    async isFile(path: string): Promise<boolean> {      return tree.exists(path);    },  };}export function myService(options: MyServiceSchema): Rule {  return async (tree: Tree) => {    const host = createHost(tree);    const {workspace} = await workspaces.readWorkspace('/', host);    const project = options.project != null ? workspace.projects.get(options.project) : null;    if (!project) {      throw new SchematicsException(`Invalid project name: ${options.project}`);    }    const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';    if (options.path === undefined) {      options.path = `${project.sourceRoot}/${projectType}`;    }    const templateSource = apply(url('./files'), [      applyTemplates({        classify: strings.classify,        dasherize: strings.dasherize,        name: options.name,      }),      move(normalize(options.path as string)),    ]);    return chain([mergeWith(templateSource)]);  };}

Para mais informações sobre regras e métodos utilitários, veja Provided Rules.

Executando seu schematic de biblioteca

Depois de construir sua biblioteca e schematics, você pode instalar a coleção de schematics para executar contra seu projeto. Os passos a seguir mostram como gerar um service usando o schematic que você criou anteriormente.

Construir sua biblioteca e schematics

Da raiz de seu workspace, execute o comando ng build para sua biblioteca.

ng build my-lib

Então, você muda para o diretório de sua biblioteca para construir o schematic

cd projects/my-libnpm run build

Sua biblioteca e schematics são empacotados e colocados na pasta dist/my-lib na raiz de seu workspace. Para executar o schematic, você precisa fazer link da biblioteca em sua pasta node_modules. Da raiz de seu workspace, execute o comando npm link com o caminho para sua biblioteca distribuível.

npm link dist/my-lib

Executar o schematic

Agora que sua biblioteca está instalada, execute o schematic usando o comando ng generate.

ng generate my-lib:my-service --name my-data

No console, você vê que o schematic foi executado e o arquivo my-data.service.ts foi criado na pasta de sua aplicação.

CREATE src/app/my-data.service.ts (208 bytes)