Générateur de Générateur Nx
Ajoute un Nx Generator à un projet TypeScript, pour vous aider à automatiser des tâches répétitives comme la création de composants ou l’application de structures de projet spécifiques.
Utilisation
Section intitulée « Utilisation »Générer un générateur
Section intitulée « Générer un générateur »Vous pouvez générer un générateur de deux manières :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)dans la section "Common Nx Commands" - Recherchez
@aws/nx-plugin - ts#nx-generator - Remplissez les paramètres requis
- Cliquez sur
Generate
pnpm nx g @aws/nx-plugin:ts#nx-generatoryarn nx g @aws/nx-plugin:ts#nx-generatornpx nx g @aws/nx-plugin:ts#nx-generatorbunx nx g @aws/nx-plugin:ts#nx-generatorVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#nx-generator --dry-runyarn nx g @aws/nx-plugin:ts#nx-generator --dry-runnpx nx g @aws/nx-plugin:ts#nx-generator --dry-runbunx nx g @aws/nx-plugin:ts#nx-generator --dry-run| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
| project Requis | string | - | Projet TypeScript auquel ajouter le générateur. Nous recommandons d'utiliser le générateur ts#nx-plugin pour le créer. |
| name Requis | string | - | Nom du générateur |
| description | string | - | Une description de votre générateur |
| directory | string | - | Le répertoire dans le dossier source du projet de plugin où ajouter le générateur (par défaut : <name>) |
Résultat du générateur
Section intitulée « Résultat du générateur »Le générateur créera les fichiers suivants dans le project spécifié :
Répertoiresrc/<name>/
- schema.json Schéma des entrées de votre générateur
- schema.d.ts Types TypeScript pour votre schéma
- generator.ts Implémentation de base du générateur
- generator.spec.ts Tests pour votre générateur
- README.md Documentation pour votre générateur
- generators.json Configuration Nx pour définir vos générateurs
- package.json Créé ou mis à jour pour ajouter une entrée “generators”
- tsconfig.json Mis à jour pour utiliser CommonJS
Modification du projet
Ce générateur mettra à jour le project sélectionné pour utiliser CommonJS, car les Nx Generators ne prennent actuellement en charge que CommonJS (voir cette issue GitHub pour le support ESM).
Générateurs locaux
Section intitulée « Générateurs locaux »Sélectionnez votre projet local nx-plugin lors de l’exécution du générateur ts#nx-generator, et spécifiez un nom ainsi qu’un répertoire et une description optionnels.
Définition du schéma
Section intitulée « Définition du schéma »Le fichier schema.json définit les options acceptées par votre générateur. Il suit le format JSON Schema avec des extensions spécifiques à Nx.
Structure de base
Section intitulée « Structure de base »Un fichier schema.json a la structure de base suivante :
{ "$schema": "https://json-schema.org/schema", "$id": "YourGeneratorName", "title": "Your Generator Title", "description": "Description of what your generator does", "type": "object", "properties": { // Vos options de générateur ici }, "required": ["requiredOption1", "requiredOption2"]}Exemple simple
Section intitulée « Exemple simple »Voici un exemple simple avec quelques options basiques :
{ "$schema": "https://json-schema.org/schema", "$id": "ComponentGenerator", "title": "Create a Component", "description": "Creates a new React component", "type": "object", "properties": { "name": { "type": "string", "description": "Component name", "x-priority": "important" }, "directory": { "type": "string", "description": "Directory where the component will be created", "default": "src/components" }, "withTests": { "type": "boolean", "description": "Whether to generate test files", "default": true } }, "required": ["name"]}Invites interactives (CLI)
Section intitulée « Invites interactives (CLI) »Vous pouvez personnaliser les invites affichées lors de l’exécution de votre générateur via la CLI en ajoutant la propriété x-prompt :
"name": { "type": "string", "description": "Component name", "x-prompt": "What is the name of your component?"}Pour les options booléennes, vous pouvez utiliser une invite oui/non :
"withTests": { "type": "boolean", "description": "Whether to generate test files", "x-prompt": "Would you like to generate test files?"}Sélections déroulantes
Section intitulée « Sélections déroulantes »Pour les options avec un ensemble fixe de choix, utilisez enum afin que les utilisateurs puissent sélectionner l’une des options.
"style": { "type": "string", "description": "The styling approach to use", "enum": ["css", "scss", "styled-components", "none"], "default": "css"}Menu déroulant de sélection de projet
Section intitulée « Menu déroulant de sélection de projet »Un modèle courant consiste à permettre aux utilisateurs de sélectionner parmi les projets existants dans l’espace de travail :
"project": { "type": "string", "description": "The project to add the component to", "x-prompt": "Which project would you like to add the component to?", "x-dropdown": "projects"}La propriété x-dropdown: "projects" indique à Nx de peupler le menu déroulant avec tous les projets de l’espace de travail.
Arguments positionnels
Section intitulée « Arguments positionnels »Vous pouvez configurer des options pour qu’elles soient passées comme arguments positionnels lors de l’exécution du générateur depuis la ligne de commande :
"name": { "type": "string", "description": "Component name", "x-priority": "important", "$default": { "$source": "argv", "index": 0 }}Cela permet aux utilisateurs d’exécuter votre générateur comme nx g your-generator my-component au lieu de nx g your-generator --name=my-component.
Définition des priorités
Section intitulée « Définition des priorités »Utilisez la propriété x-priority pour indiquer quelles options sont les plus importantes :
"name": { "type": "string", "description": "Component name", "x-priority": "important"}Les options peuvent avoir des priorités de "important" ou "internal". Cela aide Nx à ordonner les propriétés dans l’extension VSCode Nx et la CLI Nx.
Valeurs par défaut
Section intitulée « Valeurs par défaut »Vous pouvez fournir des valeurs par défaut pour les options :
"directory": { "type": "string", "description": "Directory where the component will be created", "default": "src/components"}Plus d’informations
Section intitulée « Plus d’informations »Pour plus de détails sur les schémas, consultez la documentation Nx sur les options de générateur.
Types TypeScript avec schema.d.ts
Section intitulée « Types TypeScript avec schema.d.ts »Avec schema.json, le générateur crée un fichier schema.d.ts qui fournit des types TypeScript pour les options de votre générateur :
export interface YourGeneratorSchema { name: string; directory?: string; withTests?: boolean;}Cette interface est utilisée dans l’implémentation de votre générateur pour fournir la sécurité des types et l’autocomplétion du code :
import { YourGeneratorSchema } from './schema';
export default async function (tree: Tree, options: YourGeneratorSchema) { // TypeScript connaît les types de toutes vos options const { name, directory = 'src/components', withTests = true } = options; // ...}Implémentation d’un générateur
Section intitulée « Implémentation d’un générateur »Après avoir créé le nouveau générateur comme ci-dessus, vous pouvez écrire votre implémentation dans generator.ts.
Un générateur est une fonction qui modifie un système de fichiers virtuel (le Tree), en lisant et en écrivant des fichiers pour effectuer les changements souhaités. Les changements du Tree ne sont écrits sur le disque qu’une fois que le générateur a terminé son exécution, sauf s’il est exécuté en mode “dry-run”. Un générateur vide ressemble à ceci :
export const myGenerator = async (tree: Tree, options: MyGeneratorSchema) => { // Utilisez le tree pour appliquer des modifications};
export default myGenerator;Voici quelques opérations courantes que vous pourriez vouloir effectuer dans votre générateur :
Lecture et écriture de fichiers
Section intitulée « Lecture et écriture de fichiers »// Lire un fichierconst content = tree.read('path/to/file.ts', 'utf-8');
// Écrire un fichiertree.write('path/to/new-file.ts', 'export const hello = "world";');
// Vérifier si un fichier existeif (tree.exists('path/to/file.ts')) { // Faire quelque chose}Génération de fichiers depuis des templates
Section intitulée « Génération de fichiers depuis des templates »Vous pouvez générer des fichiers avec l’utilitaire generateFiles de @nx/devkit. Cela vous permet de définir des templates en syntaxe EJS et de substituer des variables.
import { generateFiles, joinPathFragments } from '@nx/devkit';
// Générer des fichiers depuis des templatesgenerateFiles( tree, joinPathFragments(__dirname, 'files'), // Répertoire de templates 'path/to/output', // Répertoire de sortie { // Variables à remplacer dans les templates name: options.name, nameCamelCase: camelCase(options.name), nameKebabCase: kebabCase(options.name), // Ajoutez plus de variables selon les besoins },);Transformations de code avec GritQL
Section intitulée « Transformations de code avec GritQL »Vous pouvez utiliser GritQL pour rechercher et transformer de manière déclarative le code source dans vos générateurs. GritQL prend en charge plusieurs langages, notamment TypeScript, JavaScript, Python, HCL (Terraform) et plus encore — vous pouvez donc utiliser la même syntaxe de motif dans toute votre pile technologique.
Le Nx Plugin for AWS expose deux assistants :
applyGritQL(tree, filePath, pattern)— applique un motif de réécriture GritQL à un fichier et renvoiePromise<boolean>indiquant si des modifications ont été effectuéesmatchGritQL(tree, filePath, pattern)— vérifie si un motif GritQL correspond quelque part dans un fichier et renvoiePromise<boolean>
import { applyGritQL, matchGritQL } from '@aws/nx-plugin/sdk/utils/ast';
// Remplacer un appel de fonctionawait applyGritQL( tree, 'src/app.ts', '`console.log($msg)` => `logger.info($msg)`',);
// Ajouter un élément à un tableau uniquement s'il n'est pas déjà présentawait applyGritQL( tree, 'src/plugins.ts', '`plugins: [$items]` => `plugins: [$items, myPlugin()]` where { $items <: not contains `myPlugin` }',);
// Vérifier si un motif existe avant d'effectuer des modificationsif (!(await matchGritQL(tree, filePath, '`import { Auth } from "./auth"`'))) { // Ajouter l'import}Les motifs GritQL fonctionnent également sur des fichiers non-TypeScript. Préfixez votre motif avec language <name> pour cibler d’autres langages :
// Python : remplacer les instructions print par des appels de loggingawait applyGritQL( tree, 'src/handler.py', 'language python\n`print($msg)` => `logger.info($msg)`',);Les motifs GritQL utilisent des extraits de code délimités par des backticks avec des $metavariables comme caractères génériques. Utilisez => pour les réécritures et les clauses where pour les conditions.
Ajout de dépendances
Section intitulée « Ajout de dépendances »import { addDependenciesToPackageJson } from '@nx/devkit';
// Ajouter des dépendances au package.jsonaddDependenciesToPackageJson( tree, { 'new-dependency': '^1.0.0', }, { 'new-dev-dependency': '^2.0.0', },);Formatage des fichiers générés
Section intitulée « Formatage des fichiers générés »import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
// Formater tous les fichiers qui ont été modifiésawait formatFilesInSubtree(tree, 'optional/path/to/format');Lecture et mise à jour de fichiers JSON
Section intitulée « Lecture et mise à jour de fichiers JSON »import { readJson, updateJson } from '@nx/devkit';
// Lire un fichier JSONconst packageJson = readJson(tree, 'package.json');
// Mettre à jour un fichier JSONupdateJson(tree, 'tsconfig.json', (json) => { json.compilerOptions = { ...json.compilerOptions, strict: true, }; return json;});Extension d’un générateur du Nx Plugin for AWS
Section intitulée « Extension d’un générateur du Nx Plugin for AWS »Vous pouvez importer des générateurs du Nx Plugin for AWS et les étendre ou les composer comme vous le souhaitez. Par exemple, vous pourriez vouloir créer un générateur qui s’appuie sur un projet TypeScript :
import { tsProjectGenerator } from '@aws/nx-plugin/sdk/ts';
export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => { const callback = await tsProjectGenerator(tree, { ... });
// Étendez le générateur de projet TypeScript ici
// Retournez le callback pour vous assurer que les dépendances sont installées. // Vous pouvez encapsuler le callback si vous souhaitez effectuer des opérations supplémentaires dans le callback du générateur. return callback;};Générateurs OpenAPI
Section intitulée « Générateurs OpenAPI »Vous pouvez utiliser et étendre les générateurs que nous utilisons pour les clients et hooks TypeScript de manière similaire à ce qui précède :
import { openApiTsClientGenerator } from '@aws/nx-plugin/sdk/open-api';
export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => { await openApiTsClientGenerator(tree, { ... });
// Ajoutez des fichiers supplémentaires ici};Nous exposons également une méthode qui vous permet de construire une structure de données pouvant être utilisée pour itérer sur les opérations dans une spécification OpenAPI et donc instrumenter votre propre génération de code, par exemple :
import { buildOpenApiCodeGenerationData } from '@aws/nx-plugin/sdk/open-api.js';
export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => { const data = await buildOpenApiCodeGenerationData(tree, 'path/to/spec.json');
generateFiles( tree, joinPathFragments(__dirname, 'files'), // Répertoire de templates 'path/to/output', // Répertoire de sortie data, );};Ce qui vous permet ensuite d’écrire des templates tels que :
export const myOperationNames = [<%_ allOperations.forEach((op) => { _%> '<%- op.name %>',<%_ }); _%>];Consultez le code source sur GitHub pour des exemples de templates plus complexes.
Exécution de votre générateur
Section intitulée « Exécution de votre générateur »Vous pouvez exécuter votre générateur de deux manières :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)dans la section "Common Nx Commands" - Recherchez
@my-project/nx-plugin - my-generator - Remplissez les paramètres requis
- Cliquez sur
Generate
pnpm nx g @my-project/nx-plugin:my-generatoryarn nx g @my-project/nx-plugin:my-generatornpx nx g @my-project/nx-plugin:my-generatorbunx nx g @my-project/nx-plugin:my-generatorVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @my-project/nx-plugin:my-generator --dry-runyarn nx g @my-project/nx-plugin:my-generator --dry-runnpx nx g @my-project/nx-plugin:my-generator --dry-runbunx nx g @my-project/nx-plugin:my-generator --dry-runTests de votre générateur
Section intitulée « Tests de votre générateur »Les tests unitaires pour les générateurs sont simples à implémenter. Voici un modèle typique :
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';import { yourGenerator } from './generator';
describe('your generator', () => { let tree;
beforeEach(() => { // Créer un arbre d'espace de travail vide tree = createTreeWithEmptyWorkspace();
// Ajouter tous les fichiers qui devraient déjà exister dans l'arbre tree.write( 'project.json', JSON.stringify({ name: 'test-project', sourceRoot: 'src', }), );
tree.write('src/existing-file.ts', 'export const existing = true;'); });
it('should generate expected files', async () => { // Exécuter le générateur await yourGenerator(tree, { name: 'test', // Ajouter d'autres options requises });
// Vérifier que les fichiers ont été créés expect(tree.exists('src/test/file.ts')).toBeTruthy();
// Vérifier le contenu du fichier const content = tree.read('src/test/file.ts', 'utf-8'); expect(content).toContain('export const test');
// Vous pouvez également utiliser des snapshots expect(tree.read('src/test/file.ts', 'utf-8')).toMatchSnapshot(); });
it('should update existing files', async () => { // Exécuter le générateur await yourGenerator(tree, { name: 'test', // Ajouter d'autres options requises });
// Vérifier que les fichiers existants ont été mis à jour const content = tree.read('src/existing-file.ts', 'utf-8'); expect(content).toContain('import { test } from'); });
it('should handle errors', async () => { // S'attendre à ce que le générateur lève une erreur dans certaines conditions await expect( yourGenerator(tree, { name: 'invalid', // Ajouter des options qui devraient provoquer une erreur }), ).rejects.toThrow('Expected error message'); });});Points clés pour tester les générateurs :
- Utilisez
createTreeWithEmptyWorkspace()pour créer un système de fichiers virtuel - Configurez tous les fichiers prérequis avant d’exécuter le générateur
- Testez à la fois la création de nouveaux fichiers et les mises à jour de fichiers existants
- Utilisez des snapshots pour du contenu de fichier complexe
- Testez les conditions d’erreur pour vous assurer que votre générateur échoue gracieusement
Contribution de générateurs à @aws/nx-plugin
Section intitulée « Contribution de générateurs à @aws/nx-plugin »Vous pouvez également utiliser ts#nx-generator pour créer un générateur dans @aws/nx-plugin.
Lorsque ce générateur est exécuté dans notre dépôt, il générera les fichiers suivants pour vous :
Répertoirepackages/nx-plugin/src/<name>/
- schema.json Schéma des entrées de votre générateur
- schema.d.ts Types TypeScript pour votre schéma
- generator.ts Implémentation du générateur
- generator.spec.ts Tests pour votre générateur
Répertoiredocs/src/content/docs/guides/
- <name>.mdx Page de documentation pour votre générateur
- packages/nx-plugin/generators.json Mis à jour pour inclure votre générateur
Vous pouvez ensuite commencer à implémenter votre générateur.