Générateur de générateur Nx
Ajoute un générateur Nx à un projet TypeScript, pour vous aider à automatiser les tâches répétitives telles que la génération de composants ou l’application de structures de projet particulières.
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 :
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- 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
| 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>) |
| preferInstallDependencies | boolean | true | Indique s'il faut privilégier l'installation des dépendances après l'exécution du générateur. Définir sur false pour différer l'installation lors de l'exécution de plusieurs générateurs en lot (une installation s'exécute quand même si nécessaire pour que les générateurs suivants puissent calculer le graphe de projet Nx) ; installer une seule fois à la fin. |
Sortie du générateur
Section intitulée « Sortie du générateur »Le générateur créera les fichiers de projet suivants dans le project donné :
Répertoiresrc/<name>/
- schema.json Schéma pour l’entrée de votre générateur
- schema.d.ts Types TypeScript pour votre schéma
- generator.ts Implémentation de générateur stub
- 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 générateurs Nx ne prennent en charge que CommonJS pour le moment (consultez ce problème GitHub pour la prise en charge ESM).
Générateurs locaux
Section intitulée « Générateurs locaux »Sélectionnez votre projet nx-plugin local lors de l’exécution du générateur ts#nx-generator, et spécifiez un nom et éventuellement un répertoire et une description.
Définir le schéma
Section intitulée « Définir le schéma »Le fichier schema.json définit les options que votre générateur accepte. 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": { // Your generator options go here }, "required": ["requiredOption1", "requiredOption2"]}Exemple simple
Section intitulée « Exemple simple »Voici un exemple simple avec quelques options de base :
{ "$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 remplir 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éfinir les priorités
Section intitulée « Définir les 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 Nx VSCode 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 des options de générateur Nx.
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 la complétion de code :
import { YourGeneratorSchema } from './schema';
export default async function (tree: Tree, options: YourGeneratorSchema) { // TypeScript knows the types of all your options const { name, directory = 'src/components', withTests = true } = options; // ...}Implémenter un générateur
Section intitulée « Implémenter 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 mute un système de fichiers virtuel (le Tree), en lisant et en écrivant des fichiers pour effectuer les modifications souhaitées. Les modifications du Tree ne sont écrites sur le disque qu’une fois que le générateur a fini de s’exécuter, 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) => { // Use the tree to apply changes};
export default myGenerator;Voici quelques opérations courantes que vous pourriez vouloir effectuer dans votre générateur :
Lire et écrire des fichiers
Section intitulée « Lire et écrire des fichiers »// Read a fileconst content = tree.read('path/to/file.ts', 'utf-8');
// Write a filetree.write('path/to/new-file.ts', 'export const hello = "world";');
// Check if a file existsif (tree.exists('path/to/file.ts')) { // Do something}Générer des fichiers à partir de modèles
Section intitulée « Générer des fichiers à partir de modèles »Vous pouvez générer des fichiers avec l’utilitaire generateFiles de @nx/devkit. Cela vous permet de définir des modèles avec la syntaxe EJS, et de substituer des variables.
import { generateFiles, joinPathFragments } from '@nx/devkit';
// Generate files from templatesgenerateFiles( tree, joinPathFragments(import.meta.dirname, 'files'), // Template directory 'path/to/output', // Output directory { // Variables to replace in templates name: options.name, nameCamelCase: camelCase(options.name), nameKebabCase: kebabCase(options.name), // Add more variables as needed },);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.
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é apporté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';
// Replace a function callawait applyGritQL( tree, 'src/app.ts', '`console.log($msg)` => `logger.info($msg)`',);
// Add an element to an array only if not already presentawait applyGritQL( tree, 'src/plugins.ts', '`plugins: [$items]` => `plugins: [$items, myPlugin()]` where { $items <: not contains `myPlugin` }',);
// Check if a pattern exists before making changesif (!(await matchGritQL(tree, filePath, '`import { Auth } from "./auth"`'))) { // Add the import}Les motifs GritQL fonctionnent également sur des fichiers non-TypeScript. Préfixez votre motif avec language <name> pour cibler d’autres langages :
// Python: replace print statements with logging callsawait 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 jokers. Utilisez => pour les réécritures et les clauses where pour les conditions.
Ajouter des dépendances
Section intitulée « Ajouter des dépendances »import { addDependenciesToPackageJson } from '@nx/devkit';
// Add dependencies to package.jsonaddDependenciesToPackageJson( tree, { 'new-dependency': '^1.0.0', }, { 'new-dev-dependency': '^2.0.0', },);Formater les fichiers générés
Section intitulée « Formater les fichiers générés »import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
// Format all files that were modifiedawait formatFilesInSubtree(tree, 'optional/path/to/format');Lire et mettre à jour des fichiers JSON
Section intitulée « Lire et mettre à jour des fichiers JSON »import { readJson, updateJson } from '@nx/devkit';
// Read a JSON fileconst packageJson = readJson(tree, 'package.json');
// Update a JSON fileupdateJson(tree, 'tsconfig.json', (json) => { json.compilerOptions = { ...json.compilerOptions, strict: true, }; return json;});Étendre un générateur du Nx Plugin for AWS
Section intitulée « Étendre 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 souhaiter 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, { ... });
// Extend the TypeScript project generator here
// Return the callback to ensure dependencies are installed. // You can wrap the callback if you wish to perform additional operations in the generator callback. 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, { ... });
// Add additional files here};Nous exposons également une méthode qui vous permet de construire une structure de données qui peut ê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(import.meta.dirname, 'files'), // Template directory 'path/to/output', // Output directory data, );};Ce qui vous permet ensuite d’écrire des modèles tels que :
export const myOperationNames = [<%_ allOperations.forEach((op) => { _%> '<%- op.name %>',<%_ }); _%>];Consultez le code source sur GitHub pour des exemples de modèles plus complexes.
Exécuter votre générateur
Section intitulée « Exécuter votre générateur »Vous pouvez exécuter votre générateur de deux manières :
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-run- 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
Tester votre générateur
Section intitulée « Tester 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.js';
describe('your generator', () => { let tree;
beforeEach(() => { // Create an empty workspace tree tree = createTreeWithEmptyWorkspace();
// Add any files that should already exist in the tree 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 () => { // Run the generator await yourGenerator(tree, { name: 'test', // Add other required options });
// Check that files were created expect(tree.exists('src/test/file.ts')).toBeTruthy();
// Check file content const content = tree.read('src/test/file.ts', 'utf-8'); expect(content).toContain('export const test');
// You can also use snapshots expect(tree.read('src/test/file.ts', 'utf-8')).toMatchSnapshot(); });
it('should update existing files', async () => { // Run the generator await yourGenerator(tree, { name: 'test', // Add other required options });
// Check that existing files were updated const content = tree.read('src/existing-file.ts', 'utf-8'); expect(content).toContain('import { test } from'); });
it('should handle errors', async () => { // Expect the generator to throw an error in certain conditions await expect( yourGenerator(tree, { name: 'invalid', // Add options that should cause an error }), ).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 le contenu de fichiers complexes
- Testez les conditions d’erreur pour vous assurer que votre générateur échoue gracieusement
Contribuer des générateurs à @aws/nx-plugin
Section intitulée « Contribuer des générateurs à @aws/nx-plugin »Vous pouvez également utiliser ts#nx-generator pour échafauder 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 pour l’entrée 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
- packages/nx-plugin/sdk/<prefix>.ts Mis à jour pour exposer votre générateur depuis le SDK (pour les générateurs
ts#etpy#)
Vous pouvez ensuite commencer à implémenter votre générateur.