Aller au contenu

Générateur de Générateur Nx

Ajoute un Générateur Nx à un projet TypeScript pour automatiser des tâches répétitives comme la création de composants ou l’application de structures de projet spécifiques.

Utilisation

Générer un générateur

Vous pouvez générer un générateur de deux manières :

  1. Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
  2. Ouvrez la console Nx dans VSCode
  3. Cliquez sur Generate (UI) dans la section "Common Nx Commands"
  4. Recherchez @aws/nx-plugin - ts#nx-generator
  5. Remplissez les paramètres requis
    • Cliquez sur Generate

    Options

    Paramètre Type Par défaut Description
    pluginProject Requis string - TypeScript project to add the generator to. We recommend creating a ts#project in a top-level 'tools' directory.
    name Requis string - Generator name
    description string - A description of your generator
    directory string - The directory within the plugin project's source folder to add the generator to (default: <name>)

    Résultat du générateur

    Le générateur créera les fichiers suivants dans le pluginProject spécifié :

    • Répertoiresrc/<name>/
      • schema.json Schéma d’entrée pour 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
    • 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

    Ce générateur mettra à jour le pluginProject sélectionné pour utiliser CommonJS, car les générateurs Nx ne prennent actuellement en charge que CommonJS (voir cette issue GitHub pour le support ESM).

    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

    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

    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

    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)

    Personnalisez les invites affichées dans le CLI avec la propriété x-prompt :

    "name": {
    "type": "string",
    "description": "Component name",
    "x-prompt": "Quel est le nom de votre composant ?"
    }

    Pour les options booléennes :

    "withTests": {
    "type": "boolean",
    "description": "Whether to generate test files",
    "x-prompt": "Souhaitez-vous générer des fichiers de tests ?"
    }

    Sélections par menu déroulant

    Pour des choix prédéfinis, utilisez enum :

    "style": {
    "type": "string",
    "description": "The styling approach to use",
    "enum": ["css", "scss", "styled-components", "none"],
    "default": "css"
    }

    Sélection de projet

    Permettez aux utilisateurs de sélectionner un projet existant :

    "project": {
    "type": "string",
    "description": "The project to add the component to",
    "x-prompt": "Dans quel projet souhaitez-vous ajouter le composant ?",
    "x-dropdown": "projects"
    }

    La propriété x-dropdown: "projects" indique à Nx de peupler le menu avec tous les projets du workspace.

    Arguments positionnels

    Configurez des options comme arguments positionnels :

    "name": {
    "type": "string",
    "description": "Component name",
    "x-priority": "important",
    "$default": {
    "$source": "argv",
    "index": 0
    }
    }

    Cela permet d’exécuter le générateur via nx g your-generator mon-composant.

    Définir les priorités

    Utilisez x-priority pour indiquer l’importance des options :

    "name": {
    "type": "string",
    "description": "Component name",
    "x-priority": "important"
    }

    Valeurs par défaut

    Définissez des valeurs par défaut :

    "directory": {
    "type": "string",
    "description": "Répertoire de création du composant",
    "default": "src/components"
    }

    Plus d’informations

    Consultez la documentation Nx sur les options de générateur.

    Types TypeScript avec schema.d.ts

    Le générateur crée un fichier schema.d.ts avec des types TypeScript :

    export interface YourGeneratorSchema {
    name: string;
    directory?: string;
    withTests?: boolean;
    }

    Cette interface assure la sécurité des types dans l’implémentation :

    import { YourGeneratorSchema } from './schema';
    export default async function (tree: Tree, options: YourGeneratorSchema) {
    const { name, directory = 'src/components', withTests = true } = options;
    // ...
    }

    Implémentation d’un générateur

    Après avoir créé le générateur, implémentez sa logique dans generator.ts.

    Un générateur manipule un système de fichiers virtuel (Tree). Les modifications ne sont écrites sur le disque qu’après son exécution complète (sauf en mode “dry-run”).

    Opérations courantes :

    Lecture/écriture de fichiers

    // Lire un fichier
    const content = tree.read('chemin/vers/fichier.ts', 'utf-8');
    // Écrire un fichier
    tree.write('chemin/vers/nouveau-fichier.ts', 'export const hello = "world";');
    // Vérifier l'existence d'un fichier
    if (tree.exists('chemin/vers/fichier.ts')) {
    // ...
    }

    Génération de fichiers depuis des templates

    import { generateFiles, joinPathFragments } from '@nx/devkit';
    generateFiles(
    tree,
    joinPathFragments(__dirname, 'files'), // Dossier template
    'chemin/vers/sortie', // Dossier de sortie
    {
    name: options.name,
    nameCamelCase: camelCase(options.name),
    // ...
    },
    );

    Manipulation d’AST TypeScript

    Nous recommandons TSQuery :

    import { tsquery } from '@phenomnomnominal/tsquery';
    import * as ts from 'typescript';
    // Exemple : Incrémenter un numéro de version
    const sourceFile = tsquery.ast(tree.read('chemin/vers/version.ts', 'utf-8'));
    const nodes = tsquery.query(
    sourceFile,
    'VariableDeclaration:has(Identifier[name="VERSION"]) NumericLiteral',
    );
    if (nodes.length > 0) {
    const numericNode = nodes[0] as ts.NumericLiteral;
    const newVersion = Number(numericNode.text) + 1;
    const result = tsquery.replace(
    sourceFile,
    'VariableDeclaration:has(Identifier[name="VERSION"]) NumericLiteral',
    () => ts.factory.createNumericLiteral(newVersion),
    );
    tree.write(
    'chemin/vers/version.ts',
    ts
    .createPrinter({ newLine: ts.NewLineKind.LineFeed })
    .printNode(ts.EmitHint.Unspecified, result, sourceFile),
    );
    }

    Ajout de dépendances

    import { addDependenciesToPackageJson } from '@nx/devkit';
    addDependenciesToPackageJson(
    tree,
    { 'nouvelle-dependance': '^1.0.0' },
    { 'nouvelle-dev-dependance': '^2.0.0' },
    );

    Formatage des fichiers

    import { formatFiles } from '@nx/devkit';
    await formatFiles(tree);

    Manipulation de fichiers JSON

    import { readJson, updateJson } from '@nx/devkit';
    const packageJson = readJson(tree, 'package.json');
    updateJson(tree, 'tsconfig.json', (json) => ({
    compilerOptions: { ...json.compilerOptions, strict: true }
    }));

    Exécution du générateur

    Deux méthodes d’exécution :

    1. Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
    2. Ouvrez la console Nx dans VSCode
    3. Cliquez sur Generate (UI) dans la section "Common Nx Commands"
    4. Recherchez @my-project/nx-plugin - my-generator
    5. Remplissez les paramètres requis
      • Cliquez sur Generate

      Tests du générateur

      Exemple de tests unitaires :

      import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
      import { yourGenerator } from './generator';
      describe('your generator', () => {
      let tree;
      beforeEach(() => {
      tree = createTreeWithEmptyWorkspace();
      tree.write('src/existing-file.ts', 'export const existing = true;');
      });
      it('should generate expected files', async () => {
      await yourGenerator(tree, { name: 'test' });
      expect(tree.exists('src/test/file.ts')).toBeTruthy();
      expect(tree.read('src/test/file.ts', 'utf-8')).toMatchSnapshot();
      });
      it('should handle errors', async () => {
      await expect(yourGenerator(tree, { name: 'invalid' }))
      .rejects.toThrow('Message d\'erreur attendu');
      });
      });

      Points clés :

      • Utilisez createTreeWithEmptyWorkspace() pour simuler un système de fichiers
      • Testez la création et la modification de fichiers
      • Utilisez des snapshots pour les contenus complexes
      • Testez les cas d’erreur

      Contribution de générateurs à @aws/nx-plugin

      Le générateur ts#nx-generator peut aussi scaffold des générateurs dans @aws/nx-plugin :

      • Répertoirepackages/nx-plugin/src/<name>/
        • schema.json Schéma du générateur
        • schema.d.ts Types TypeScript
        • generator.ts Implémentation
        • generator.spec.ts Tests
      • Répertoiredocs/src/content/docs/guides/
        • <name>.mdx Documentation
      • packages/nx-plugin/generators.json Mise à jour de la configuration