Lorsque vous créez une library Angular, vous pouvez la fournir et la packager avec des schematics qui l'intègrent à l'Angular CLI.
Grâce à vos schematics, vos utilisateurs peuvent utiliser ng add pour installer une version initiale de votre library,
ng generate pour créer des artefacts définis dans votre library, et ng update pour adapter leur projet à une nouvelle version de votre library qui introduit des breaking changes.
Ces trois types de schematics peuvent faire partie d'une collection que vous packagez avec votre library.
Créer une collection de schematics
Pour démarrer une collection, vous devez créer les fichiers du schematic. Les étapes suivantes vous montrent comment ajouter une prise en charge initiale sans modifier aucun fichier du projet.
Dans le dossier racine de votre library, créez un dossier
schematics.Dans le dossier
schematics/, créez un dossierng-addpour votre premier schematic.À la racine du dossier
schematics, créez un fichiercollection.json.Modifiez le fichier
collection.jsonpour définir le schéma initial de votre collection.projects/my-lib/schematics/collection.json (Collection de schematics)
{ "$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" } } }- Le chemin
$schemaest relatif au schéma de collection de l'Angular Devkit. - L'objet
schematicsdécrit les schematics nommés qui font partie de cette collection. - La première entrée concerne un schematic nommé
ng-add. Elle contient la description et pointe vers la fonction de fabrique appelée lors de l'exécution de votre schematic.
- Le chemin
Dans le fichier
package.jsondu projet de votre library, ajoutez une entrée « schematics » avec le chemin vers votre fichier de schéma. L'Angular CLI utilise cette entrée pour trouver les schematics nommés de votre collection lorsqu'elle exécute des commandes.
projects/my-lib/package.json (Référence à la collection de schematics)
{
"name": "my-lib",
"version": "0.0.1",
"schematics": "./schematics/collection.json",
}
Le schéma initial que vous avez créé indique à la CLI où trouver le schematic qui prend en charge la commande ng add.
Vous êtes maintenant prêt à créer ce schematic.
Prendre en charge l'installation
Un schematic pour la commande ng add peut améliorer le processus d'installation initiale pour vos utilisateurs.
Les étapes suivantes définissent ce type de schematic.
Accédez au dossier
<lib-root>/schematics/ng-add.Créez un fichier
schema.jsonpour définir les options acceptées par le schematic.projects/my-lib/schematics/ng-add/schema.json (Schéma ng-add)
{ "$schema": "https://json-schema.org/schema", "$id": "SchematicsMyLibNgAdd", "title": "MyLib ng add Schema", "type": "object", "properties": { "project": { "type": "string", "description": "Name of the project.", "$default": { "$source": "projectName" } } } }Créez un fichier
schema.tspour définir une interface pour les options définies dans le fichierschema.json.projects/my-lib/schematics/ng-add/schema.ts (Interface du schéma ng-add)
export interface Schema { // Name of the project. project: string; }Créez le fichier principal,
index.ts, et ajoutez le code source de la fonction de fabrique de votre schematic.projects/my-lib/schematics/ng-add/index.ts (Fabrique de règle ng-add)
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')}`, ); }
L'Angular CLI installe automatiquement la dernière version de la library, et cet exemple va plus loin en ajoutant MyLibModule à la racine de l'application. La fonction addRootImport accepte un callback qui doit renvoyer un bloc de code. Vous pouvez écrire n'importe quel code dans la chaîne étiquetée avec la fonction code, et tout symbole externe doit être enveloppé dans la fonction external pour garantir que les instructions d'import appropriées sont générées.
Définir le type de dépendance
Utilisez l'option save de ng-add pour indiquer si la library doit être ajoutée aux dependencies, aux devDependencies, ou ne pas être enregistrée du tout dans le fichier de configuration package.json du projet.
projects/my-lib/package.json (Référence ng-add)
"ng-add": {
"save": "devDependencies"
},
Les valeurs possibles sont :
| Valeurs | Détails |
|---|---|
false |
N'ajoute pas le package à package.json |
true |
Ajoute le package aux dependencies |
"dependencies" |
Ajoute le package aux dependencies |
"devDependencies" |
Ajoute le package aux devDependencies |
Compiler vos schematics
Pour intégrer vos schematics au bundle de votre library, vous devez configurer la library pour qu'elle compile les schematics séparément, puis les ajouter au bundle. Vous devez compiler vos schematics après avoir compilé votre library, afin qu'ils soient placés dans le bon répertoire.
- Votre library a besoin d'un fichier de configuration TypeScript personnalisé contenant les instructions pour compiler vos schematics dans votre library distribuée
- Pour ajouter les schematics au bundle de la library, ajoutez des scripts au fichier
package.jsonde la library
Supposons que vous ayez un projet de library my-lib dans votre workspace Angular.
Pour indiquer à la library comment compiler les schematics, ajoutez un fichier tsconfig.schematics.json à côté du fichier tsconfig.lib.json généré, qui configure le build de la library.
Modifiez le fichier
tsconfig.schematics.jsonpour y ajouter le contenu suivant.projects/my-lib/tsconfig.schematics.json (Configuration TypeScript)
{ "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, "target": "es6", "types": ["jasmine", "node"] }, "include": ["schematics/**/*"], "exclude": ["schematics/*/files/**/*"] }Options Détails rootDirIndique que votre dossier schematicscontient les fichiers d'entrée à compiler.outDirCorrespond au dossier de sortie de la library. Par défaut, il s'agit du dossier dist/my-libà la racine de votre workspace.Pour vous assurer que les fichiers source de vos schematics sont compilés dans le bundle de la library, ajoutez les scripts suivants au fichier
package.jsonsitué dans le dossier racine du projet de votre library (projects/my-lib).projects/my-lib/package.json (Scripts de build)
{ "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": "^22.0.0", "@angular/core": "^22.0.0" }, "schematics": "./schematics/collection.json", "ng-add": { "save": "devDependencies" }, "devDependencies": { "copyfiles": "file:../../node_modules/copyfiles", "typescript": "file:../../node_modules/typescript" } }- Le script
buildcompile votre schematic à l'aide du fichier personnalisétsconfig.schematics.json - Le script
postbuildcopie les fichiers du schematic une fois le scriptbuildterminé - Les scripts
buildetpostbuildnécessitent tous deux les dépendancescopyfilesettypescript. Pour installer les dépendances, placez-vous dans le chemin défini dansdevDependencieset exécuteznpm installavant de lancer les scripts.
- Le script
Prendre en charge la génération
Vous pouvez ajouter à votre collection un schematic nommé qui permet à vos utilisateurs d'utiliser la commande ng generate pour créer un artefact défini dans votre library.
Supposons que votre library définisse un service, my-service, qui nécessite une certaine configuration.
Vous voulez que vos utilisateurs puissent le générer avec la commande CLI suivante.
ng generate my-lib:my-service
Pour commencer, créez un nouveau sous-dossier, my-service, dans le dossier schematics.
Configurer le nouveau schematic
Lorsque vous ajoutez un schematic à la collection, vous devez le référencer dans le schéma de la collection et fournir des fichiers de configuration pour définir les options qu'un utilisateur peut passer à la commande.
Modifiez le fichier
schematics/collection.jsonpour pointer vers le sous-dossier du nouveau schematic, et ajoutez une référence vers un fichier de schéma qui spécifie les entrées du nouveau schematic.projects/my-lib/schematics/collection.json (Collection de schematics)
{ "$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" } } }Accédez au dossier
<lib-root>/schematics/my-service.Créez un fichier
schema.jsonet définissez les options disponibles pour le schematic.projects/my-lib/schematics/my-service/schema.json (Schéma JSON du schematic)
{ "$schema": "https://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 identifiant unique pour le schéma dans la collection.
- title : une description du schéma lisible par un humain.
- type : un descripteur du type fourni par les propriétés.
- properties : un objet qui définit les options disponibles pour le schematic.
Chaque option associe une clé à un type, une description et un alias facultatif. Le type définit la forme de la valeur attendue, et la description s'affiche lorsque l'utilisateur demande l'aide d'utilisation de votre schematic.
Consultez le schéma du workspace pour d'autres personnalisations des options de schematic.
Créez un fichier
schema.tset définissez une interface qui stocke les valeurs des options définies dans le fichierschema.json.projects/my-lib/schematics/my-service/schema.ts (Interface du schematic)
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; }Options Détails name Le nom que vous voulez donner au service créé. path Remplace le chemin fourni au schematic. La valeur par défaut du chemin est basée sur le répertoire de travail courant. project Indique un projet précis sur lequel exécuter le schematic. Dans le schematic, vous pouvez fournir une valeur par défaut si l'utilisateur ne fournit pas l'option.
Ajouter les fichiers de template
Pour ajouter des artefacts à un projet, votre schematic a besoin de ses propres fichiers de template. Les templates de schematic prennent en charge une syntaxe spéciale pour exécuter du code et substituer des variables.
Créez un dossier
files/dans le dossierschematics/my-service/.Créez un fichier nommé
__name@dasherize__.service.ts.templatequi définit un template à utiliser pour générer des fichiers. Ce template génère un service dans lequel leHttpClientd'Angular est déjà injecté dans une propriétéhttp.projects/my-lib/schematics/my-service/files/__name@dasherize__.service.ts.template (Schematic Template)
import { Service } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Service() export class <%= classify(name) %>Service { private http = inject(HttpClient); }- Les méthodes
classifyetdasherizesont des fonctions utilitaires que votre schematic utilise pour transformer votre template source et le nom de fichier. - Le
nameest fourni sous forme de propriété par votre fonction de fabrique. Il s'agit du mêmenameque celui défini dans le schéma.
- Les méthodes
Ajouter la fonction de fabrique
Maintenant que l'infrastructure est en place, vous pouvez définir la fonction principale qui effectue les modifications nécessaires dans le projet de l'utilisateur.
Le framework Schematics fournit un système de templating de fichiers, qui prend en charge à la fois les templates de chemin et de contenu.
Le système opère sur des placeholders définis dans les fichiers ou les chemins chargés dans l'arbre (Tree) d'entrée.
Il les remplit à l'aide des valeurs passées à la règle (Rule).
Pour plus de détails sur ces structures de données et cette syntaxe, consultez le README de Schematics.
Créez le fichier principal
index.tset ajoutez le code source de la fonction de fabrique de votre schematic.Commencez par importer les définitions de schematics dont vous aurez besoin. Le framework Schematics propose de nombreuses fonctions utilitaires pour créer et utiliser des règles lors de l'exécution d'un schematic.
Importez l'interface de schéma définie, qui fournit les informations de type pour les options de votre schematic.
projects/my-lib/schematics/my-service/index.ts (Import du schéma)
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';Pour construire le schematic de génération, commencez par une fabrique de règle vide.
projects/my-lib/schematics/my-service/index.ts (Règle initiale)
export function myService(options: MyServiceSchema): Rule { return (tree: Tree) => tree; }
Cette fabrique de règle renvoie l'arbre sans modification.
Les options sont les valeurs d'options transmises par la commande ng generate.
Définir une règle de génération
Vous disposez maintenant du cadre nécessaire pour écrire le code qui modifie réellement l'application de l'utilisateur afin de la configurer pour le service défini dans votre library.
Le workspace Angular dans lequel l'utilisateur a installé votre library contient plusieurs projets (applications et libraries). L'utilisateur peut indiquer le projet en ligne de commande, ou laisser la valeur par défaut. Dans les deux cas, votre code doit identifier le projet précis auquel ce schematic est appliqué, afin de pouvoir récupérer des informations dans la configuration du projet.
Faites-le à l'aide de l'objet Tree passé à la fonction de fabrique.
Les méthodes de Tree vous donnent accès à l'arbre de fichiers complet de votre workspace, ce qui vous permet de lire et d'écrire des fichiers pendant l'exécution du schematic.
Obtenir la configuration du projet
Pour déterminer le projet de destination, utilisez la méthode
workspaces.readWorkspaceafin de lire le contenu du fichier de configuration du workspace,angular.json. Pour utiliserworkspaces.readWorkspace, vous devez créer unworkspaces.WorkspaceHostà partir duTree. Ajoutez le code suivant à votre fonction de fabrique.projects/my-lib/schematics/my-service/index.ts (Import du schéma)
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); }; }Veillez à vérifier que le contexte existe et à lever l'erreur appropriée.
Maintenant que vous avez le nom du projet, utilisez-le pour récupérer les informations de configuration propres au projet.
projects/my-lib/schematics/my-service/index.ts (Projet)
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';L'objet
workspace.projectscontient toutes les informations de configuration propres aux projets.options.pathdétermine l'emplacement où les fichiers de template du schematic sont déplacés une fois le schematic appliqué.Par défaut, l'option
pathdu schéma du schematic est remplacée par le répertoire de travail courant. Sipathn'est pas défini, utilisez lesourceRootde la configuration du projet ainsi que leprojectType.projects/my-lib/schematics/my-service/index.ts (Informations du projet)
if (options.path === undefined) { options.path = `${project.sourceRoot}/${projectType}`; }
Définir la règle
Une Rule peut utiliser des fichiers de template externes, les transformer et renvoyer un autre objet Rule avec le template transformé.
Utilisez le templating pour générer tous les fichiers personnalisés nécessaires à votre schematic.
Ajoutez le code suivant à votre fonction de fabrique.
projects/my-lib/schematics/my-service/index.ts (Transformation du template)
const templateSource = apply(url('./files'), [ applyTemplates({ classify: strings.classify, dasherize: strings.dasherize, name: options.name, }), move(normalize(options.path as string)), ]);Méthodes Détails apply()Applique plusieurs règles à une source et renvoie la source transformée. Elle prend 2 arguments : une source et un tableau de règles. url()Lit les fichiers source depuis votre système de fichiers, relativement au schematic. applyTemplates()Reçoit en argument les méthodes et propriétés que vous voulez rendre disponibles pour le template du schematic et les noms de fichiers du schematic. Elle renvoie une Rule. C'est ici que vous définissez les méthodesclassify()etdasherize(), ainsi que la propriéténame.classify()Prend une valeur et la renvoie avec des majuscules en début de mot. Par exemple, si le nom fourni est my service, il est renvoyé sous la formeMyService.dasherize()Prend une valeur et la renvoie en minuscules, avec des tirets. Par exemple, si le nom fourni est MyService, il est renvoyé sous la forme my-service.move()Déplace les fichiers source fournis vers leur destination lorsque le schematic est appliqué. Enfin, la fabrique de règle doit renvoyer une règle.
projects/my-lib/schematics/my-service/index.ts (Règle chain)
return chain([mergeWith(templateSource)]);La méthode
chain()vous permet de combiner plusieurs règles en une seule, afin d'effectuer plusieurs opérations dans un même schematic. Ici, vous fusionnez simplement les règles de template avec le code exécuté par le schematic.
Voici un exemple complet de la fonction de règle de schematic suivante.
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)]);
};
}
Pour plus d'informations sur les règles et les méthodes utilitaires, consultez Provided Rules.
Exécuter le schematic de votre library
Après avoir compilé votre library et vos schematics, vous pouvez installer la collection de schematics pour l'exécuter sur votre projet. Les étapes suivantes vous montrent comment générer un service à l'aide du schematic créé précédemment.
Compiler votre library et vos schematics
Depuis la racine de votre workspace, exécutez la commande ng build pour votre library.
ng build my-lib
Placez-vous ensuite dans le répertoire de votre library pour compiler le schematic
cd projects/my-lib
npm run build
Lier la library
Votre library et vos schematics sont packagés et placés dans le dossier dist/my-lib à la racine de votre workspace.
Pour exécuter le schematic, vous devez lier la library dans votre dossier node_modules.
Depuis la racine de votre workspace, exécutez la commande npm link avec le chemin vers votre library distribuable.
npm link dist/my-lib
Exécuter le schematic
Maintenant que votre library est installée, exécutez le schematic avec la commande ng generate.
ng generate my-lib:my-service --name my-data
Dans la console, vous voyez que le schematic a été exécuté et que le fichier my-data.service.ts a été créé dans le dossier de votre application.
CREATE src/app/my-data.service.ts (208 bytes)