Cuando creas una librería Angular, puedes proporcionarla y empaquetarla con schematics que la integren con el Angular CLI.
Con tus schematics, tus usuarios pueden usar ng add para instalar una versión inicial de tu librería,
ng generate para crear artefactos definidos en tu librería, y ng update para ajustar su proyecto para una nueva versión de tu librería que introduce cambios disruptivos.
Los tres tipos de schematics pueden ser parte de una colección que empaquetas con tu librería.
Creando una colección de schematics
Para iniciar una colección, necesitas crear los archivos schematic. Los siguientes pasos te muestran cómo agregar soporte inicial sin modificar ningún archivo de proyecto.
En la carpeta raíz de tu librería, crea una carpeta
schematics.En la carpeta
schematics/, crea una carpetang-addpara tu primer schematic.En el nivel raíz de la carpeta
schematics, crea un archivocollection.json.Edita el archivo
collection.jsonpara definir el esquema inicial para tu colección.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" } }}- La ruta
$schemaes relativa al esquema de colección Angular Devkit. - El objeto
schematicsdescribe los schematics con nombre que son parte de esta colección. - La primera entrada es para un schematic llamado
ng-add. Contiene la descripción, y apunta a la función factory que se llama cuando tu schematic se ejecuta.
- La ruta
En el archivo
package.jsondel proyecto de tu librería, agrega una entrada "schematics" con la ruta a tu archivo de esquema. El Angular CLI usa esta entrada para encontrar schematics con nombre en tu colección cuando ejecuta 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" }}
El esquema inicial que has creado le dice al CLI dónde encontrar el schematic que soporta el comando ng add.
Ahora estás listo para crear ese schematic.
Proporcionando soporte de instalación
Un schematic para el comando ng add puede mejorar el proceso de instalación inicial para tus usuarios.
Los siguientes pasos definen este tipo de schematic.
- Ve a la carpeta
<lib-root>/schematics/ng-add. - Crea el archivo principal,
index.ts. - Abre
index.tsy agrega el código fuente para tu función factory 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')}`, );}
El Angular CLI instalará la última versión de la librería automáticamente, y este ejemplo va un paso más allá agregando el MyLibModule a la raíz de la aplicación. La función addRootImport acepta un callback que necesita devolver un bloque de código. Puedes escribir cualquier código dentro de la cadena etiquetada con la función code y cualquier símbolo externo tiene que estar envuelto con la función external para asegurar que se generen las declaraciones de importación apropiadas.
Definir tipo de dependencia
Usa la opción save de ng-add para configurar si la librería debería agregarse a las dependencies, las devDependencies, o no guardarse en absoluto en el archivo de configuración package.json del proyecto.
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" }}
Los valores posibles son:
| Valores | Detalles |
|---|---|
false |
No agregar el paquete a package.json |
true |
Agregar el paquete a las dependencies |
"dependencies" |
Agregar el paquete a las dependencies |
"devDependencies" |
Agregar el paquete a las devDependencies |
Construyendo tus schematics
Para empaquetar tus schematics junto con tu librería, debes configurar la librería para construir los schematics por separado, luego agregarlos al bundle. Debes construir tus schematics después de construir tu librería, para que se coloquen en el directorio correcto.
- Tu librería necesita un archivo de configuración TypeScript personalizado con instrucciones sobre cómo compilar tus schematics en tu librería distribuida
- Para agregar los schematics al bundle de la librería, agrega scripts al archivo
package.jsonde la librería
Supón que tienes un proyecto de librería my-lib en tu workspace Angular.
Para decirle a la librería cómo construir los schematics, agrega un archivo tsconfig.schematics.json junto al archivo generado tsconfig.lib.json que configura la construcción de la librería.
Edita el archivo
tsconfig.schematics.jsonpara agregar el siguiente contenido.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/**/*" ]}Opciones Detalles rootDirEspecifica que tu carpeta schematicscontiene los archivos de entrada a compilar.outDirMapea a la carpeta de salida de la librería. Por defecto, esta es la carpeta dist/my-liben la raíz de tu workspace.Para asegurarte de que los archivos fuente de tus schematics se compilen en el bundle de la librería, agrega los siguientes scripts al archivo
package.jsonen la carpeta raíz del proyecto de tu librería (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" }}- El script
buildcompila tu schematic usando el archivo personalizadotsconfig.schematics.json - El script
postbuildcopia los archivos schematic después de que el scriptbuildcompleta - Tanto el script
buildcomo elpostbuildrequieren las dependenciascopyfilesytypescript. Para instalar las dependencias, navega a la ruta definida endevDependenciesy ejecutanpm installantes de ejecutar los scripts.
- El script
Proporcionando soporte de generación
Puedes agregar un schematic con nombre a tu colección que permita a tus usuarios usar el comando ng generate para crear un artefacto que está definido en tu librería.
Supondremos que tu librería define un servicio, my-service, que requiere alguna configuración.
Quieres que tus usuarios puedan generarlo usando el siguiente comando CLI.
ng generate my-lib:my-service
Para comenzar, crea una nueva subcarpeta, my-service, en la carpeta schematics.
Configurar el nuevo schematic
Cuando agregas un schematic a la colección, tienes que apuntar a él en el esquema de la colección, y proporcionar archivos de configuración para definir opciones que un usuario puede pasar al comando.
Edita el archivo
schematics/collection.jsonpara apuntar a la nueva subcarpeta schematic, e incluir un puntero a un archivo de esquema que especifica las entradas para el nuevo 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" } }}Ve a la carpeta
<lib-root>/schematics/my-service.Crea un archivo
schema.jsony define las opciones disponibles para el 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: Un ID único para el esquema en la colección.
- title: Una descripción legible para humanos del esquema.
- type: Un descriptor para el tipo proporcionado por las propiedades.
- properties: Un objeto que define las opciones disponibles para el schematic.
Cada opción asocia una clave con un tipo, descripción y alias opcional. El tipo define la forma del valor que esperas, y la descripción se muestra cuando el usuario solicita ayuda de uso para tu schematic.
Consulta el esquema del workspace para personalizaciones adicionales para opciones schematic.
Crea un archivo
schema.tsy define una interfaz que almacene los valores de las opciones definidas en el archivoschema.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;}Opciones Detalles name El nombre que quieres proporcionar para el servicio creado. path Sobrescribe la ruta proporcionada al schematic. El valor de ruta predeterminado se basa en el directorio de trabajo actual. project Proporciona un proyecto específico en el que ejecutar el schematic. En el schematic, puedes proporcionar un valor predeterminado si la opción no es proporcionada por el usuario.
Agregar archivos de plantilla
Para agregar artefactos a un proyecto, tu schematic necesita sus propios archivos de plantilla. Las plantillas schematic soportan sintaxis especial para ejecutar código y sustitución de variables.
Crea una carpeta
files/dentro de la carpetaschematics/my-service/.Crea un archivo llamado
__name@dasherize__.service.ts.templateque define una plantilla a usar para generar archivos. Esta plantilla generará un servicio que ya tiene elHttpClientde Angular inyectado en una propiedadhttp.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); }Los métodos
classifyydasherizeson funciones utilitarias que tu schematic usa para transformar tu plantilla fuente y nombre de archivo.El
namese proporciona como una propiedad de tu función factory. Es el mismonameque definiste en el esquema.
Agregar la función factory
Ahora que tienes la infraestructura en su lugar, puedes definir la función principal que realiza las modificaciones que necesitas en el proyecto del usuario.
El framework de Schematics proporciona un sistema de plantillas de archivo, que soporta tanto plantillas de ruta como de contenido.
El sistema opera en marcadores de posición definidos dentro de archivos o rutas que se cargan en el Tree de entrada.
Los llena usando valores pasados a la Rule.
Para detalles de estas estructuras de datos y sintaxis, consulta el Schematics README.
Crea el archivo principal
index.tsy agrega el código fuente para tu función factory schematic.Primero, importa las definiciones de schematics que necesitarás. El framework de Schematics ofrece muchas funciones utilitarias para crear y usar reglas al ejecutar un 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)]); };}Importa la interfaz de esquema definida que proporciona la información de tipo para las opciones de tu 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 el schematic de generación, comienza con una factory de regla vacía.
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 factory de regla devuelve el árbol sin modificación.
Las opciones son los valores de opción pasados desde el comando ng generate.
Definir una regla de generación
Ahora tienes el framework en su lugar para crear el código que realmente modifica la aplicación del usuario para configurarla para el servicio definido en tu librería.
El workspace Angular donde el usuario instaló tu librería contiene múltiples proyectos (aplicaciones y librerías). El usuario puede especificar el proyecto en la línea de comandos, o dejarlo predeterminado. En cualquier caso, tu código necesita identificar el proyecto específico al que se está aplicando este schematic, para que puedas recuperar información de la configuración del proyecto.
Haz esto usando el objeto Tree que se pasa a la función factory.
Los métodos Tree te dan acceso al árbol completo de archivos en tu workspace, permitiéndote leer y escribir archivos durante la ejecución del schematic.
Obtener la configuración del proyecto
Para determinar el proyecto de destino, usa el método
workspaces.readWorkspacepara leer el contenido del archivo de configuración del workspace,angular.json. Para usarworkspaces.readWorkspacenecesitas crear unworkspaces.WorkspaceHostdesde elTree. Agrega el siguiente código a tu función 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)]); };}Asegúrate de verificar que el contexto existe y lanza el error apropiado.
Ahora que tienes el nombre del proyecto, úsalo para recuperar la información de configuración específica del proyecto.
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)]); };}El objeto
workspace.projectscontiene toda la información de configuración específica del proyecto.El
options.pathdetermina dónde se mueven los archivos de plantilla schematic una vez que se aplica el schematic.La opción
pathen el esquema del schematic se sustituye por defecto con el directorio de trabajo actual. Si elpathno está definido, usa elsourceRootde la configuración del proyecto junto con elprojectType.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 la regla
Una Rule puede usar archivos de plantilla externos, transformarlos y devolver otro objeto Rule con la plantilla transformada.
Usa las plantillas para generar cualquier archivo personalizado requerido para tu schematic.
Agrega el siguiente código a tu función 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 Detalles apply()Aplica múltiples reglas a una fuente y devuelve la fuente transformada. Toma 2 argumentos, una fuente y un array de reglas. url()Lee archivos fuente de tu sistema de archivos, relativo al schematic. applyTemplates()Recibe un argumento de métodos y propiedades que quieres hacer disponibles para la plantilla schematic y los nombres de archivo schematic. Devuelve una Rule. Aquí es donde defines los métodosclassify()ydasherize(), y la propiedadname.classify()Toma un valor y devuelve el valor en título. Por ejemplo, si el nombre proporcionado es my service, se devuelve comoMyService.dasherize()Toma un valor y devuelve el valor en guiones y minúsculas. Por ejemplo, si el nombre proporcionado es MyService, se devuelve como my-service.move()Mueve los archivos fuente proporcionados a su destino cuando se aplica el schematic. Finalmente, la factory de regla debe devolver una regla.
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)]); };}El método
chain()te permite combinar múltiples reglas en una sola regla, para que puedas realizar múltiples operaciones en un solo schematic. Aquí solo estás fusionando las reglas de plantilla con cualquier código ejecutado por el schematic.
Consulta un ejemplo completo de la siguiente función de regla 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 más información sobre reglas y métodos utilitarios, consulta Provided Rules.
Ejecutando tu schematic de librería
Después de construir tu librería y schematics, puedes instalar la colección de schematics para ejecutarla contra tu proyecto. Los siguientes pasos te muestran cómo generar un servicio usando el schematic que creaste anteriormente.
Construir tu librería y schematics
Desde la raíz de tu workspace, ejecuta el comando ng build para tu librería.
ng build my-lib
Luego, cambias al directorio de tu librería para construir el schematic
cd projects/my-libnpm run build
Enlazar la librería
Tu librería y schematics se empaquetan y colocan en la carpeta dist/my-lib en la raíz de tu workspace.
Para ejecutar el schematic, necesitas enlazar la librería en tu carpeta node_modules.
Desde la raíz de tu workspace, ejecuta el comando npm link con la ruta a tu librería distribuible.
npm link dist/my-lib
Ejecutar el schematic
Ahora que tu librería está instalada, ejecuta el schematic usando el comando ng generate.
ng generate my-lib:my-service --name my-data
En la consola, ves que el schematic se ejecutó y el archivo my-data.service.ts fue creado en tu carpeta de aplicación.
CREATE src/app/my-data.service.ts (208 bytes)