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.
Na pasta raiz de sua biblioteca, crie uma pasta
schematics.Na pasta
schematics/, crie uma pastang-addpara seu primeiro schematic.No nível raiz da pasta
schematics, crie um arquivocollection.json.Edite o arquivo
collection.jsonpara 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
schematicsdescreve 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.
- O caminho
No arquivo
package.jsondo 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.
- Vá para a pasta
<lib-root>/schematics/ng-add. - Crie o arquivo principal,
index.ts. - Abra
index.tse 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.jsonda 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.
Edite o arquivo
tsconfig.schematics.jsonpara 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 rootDirEspecifica que sua pasta schematicscontém os arquivos de entrada a serem compilados.outDirMapeia para a pasta de saída da biblioteca. Por padrão, esta é a pasta dist/my-libna raiz de seu workspace.Para garantir que seus arquivos-fonte de schematics sejam compilados no pacote da biblioteca, adicione os seguintes scripts ao arquivo
package.jsonna 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
buildcompila seu schematic usando o arquivo personalizadotsconfig.schematics.json - O script
postbuildcopia os arquivos de schematic após a conclusão do scriptbuild - Tanto o script
buildquanto opostbuildrequerem as dependênciascopyfilesetypescript. Para instalar as dependências, navegue até o caminho definido emdevDependenciese executenpm installantes de executar os scripts.
- O script
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.
- Edite o arquivo
schematics/collection.jsonpara 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" } }}
Vá para a pasta
<lib-root>/schematics/my-service.Crie um arquivo
schema.jsone 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.
Crie um arquivo
schema.tse defina uma interface que armazena os valores das opções definidas no arquivoschema.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.
- Crie uma pasta
files/dentro da pastaschematics/my-service/. - Crie um arquivo chamado
__name@dasherize__.service.ts.templateque define um template a ser usado para gerar arquivos. Este template gerará um service que já tem oHttpClientdo Angular injetado em uma propriedadehttp.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
classifyedasherizesã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 mesmonameque você definiu no schema.
- Os métodos
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.
- Crie o arquivo principal
index.tse adicione o código-fonte para sua função factory de schematic. - 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)]); };}
- 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)]); };}
- 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
Para determinar o projeto de destino, use o método
workspaces.readWorkspacepara ler o conteúdo do arquivo de configuração do workspace,angular.json. Para usarworkspaces.readWorkspacevocê precisa criar umworkspaces.WorkspaceHosta partir daTree. 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.
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.projectscontém todas as informações de configuração específicas do projeto.O
options.pathdetermina para onde os arquivos de template do schematic são movidos uma vez que o schematic é aplicado.A opção
pathno schema do schematic é substituída por padrão pelo diretório de trabalho atual. Se opathnão estiver definido, use osourceRootda configuração do projeto junto com oprojectType.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.
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étodosclassify()edasherize(), e a propriedadename.classify()Recebe um valor e retorna o valor em title case. Por exemplo, se o nome fornecido é my service, é retornado comoMyService.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. 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
Fazer link da biblioteca
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)