Salta ai contenuti

Generatore di Generatori Nx

Aggiunge un Nx Generator a un progetto TypeScript, per aiutarti ad automatizzare task ripetitivi come la generazione di componenti o l’imposizione di strutture di progetto specifiche.

Utilizzo

Genera un Generatore

Puoi generare un generatore in due modi:

  1. Installa il Nx Console VSCode Plugin se non l'hai già fatto
  2. Apri la console Nx in VSCode
  3. Clicca su Generate (UI) nella sezione "Common Nx Commands"
  4. Cerca @aws/nx-plugin - ts#nx-generator
  5. Compila i parametri richiesti
    • Clicca su Generate

    Opzioni

    Parametro Tipo Predefinito Descrizione
    pluginProject Obbligatorio string - TypeScript project to add the generator to. We recommend creating a ts#project in a top-level 'tools' directory.
    name Obbligatorio 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>)

    Output del Generatore

    Il generatore creerà i seguenti file all’interno del progetto pluginProject specificato:

    • Directorysrc/<name>/
      • schema.json Schema per l’input del generatore
      • schema.d.ts Tipi TypeScript per lo schema
      • generator.ts Implementazione stub del generatore
      • generator.spec.ts Test per il generatore
    • generators.json Configurazione Nx per definire i generatori
    • package.json Creato o aggiornato con una voce “generators”
    • tsconfig.json Aggiornato per utilizzare CommonJS

    Questo generatore aggiornerà il progetto pluginProject selezionato per utilizzare CommonJS, poiché gli Nx Generator supportano solo CommonJS al momento (fai riferimento a questa issue GitHub per il supporto ESM).

    Generator Locali

    Seleziona il tuo progetto locale nx-plugin quando esegui il generatore ts#nx-generator, specificando un nome e opzionalmente una directory e una descrizione.

    Definizione dello Schema

    Il file schema.json definisce le opzioni accettate dal generatore. Segue il formato JSON Schema con estensioni specifiche per Nx.

    Struttura Base

    Un file schema.json ha la seguente struttura base:

    {
    "$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"]
    }

    Esempio Semplice

    Ecco un esempio semplice con alcune opzioni 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"]
    }

    Prompt Interattivi (CLI)

    Puoi personalizzare i prompt mostrati durante l’esecuzione del generatore via CLI aggiungendo la proprietà x-prompt:

    "name": {
    "type": "string",
    "description": "Component name",
    "x-prompt": "Qual è il nome del componente?"
    }

    Per opzioni booleane, puoi usare un prompt sì/no:

    "withTests": {
    "type": "boolean",
    "description": "Whether to generate test files",
    "x-prompt": "Vuoi generare i file di test?"
    }

    Selezione a Tendina

    Per opzioni con un set fisso di scelte, usa enum per permettere agli utenti di selezionare un’opzione.

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

    Selezione Progetto a Tendina

    Un pattern comune è permettere agli utenti di selezionare tra progetti esistenti nel workspace:

    "project": {
    "type": "string",
    "description": "The project to add the component to",
    "x-prompt": "A quale progetto vuoi aggiungere il componente?",
    "x-dropdown": "projects"
    }

    La proprietà x-dropdown: "projects" indica a Nx di popolare la tendina con tutti i progetti nel workspace.

    Argomenti Posizionali

    Puoi configurare opzioni da passare come argomenti posizionali quando si esegue il generatore da riga di comando:

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

    Questo permette agli utenti di eseguire il generatore con nx g your-generator my-component invece di nx g your-generator --name=my-component.

    Impostazione Priorità

    Usa la proprietà x-priority per indicare le opzioni più importanti:

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

    Le opzioni possono avere priorità "important" o "internal". Questo aiuta Nx a ordinare le proprietà nell’estensione VSCode e nella CLI Nx.

    Valori Predefiniti

    Puoi fornire valori predefiniti per le opzioni:

    "directory": {
    "type": "string",
    "description": "Directory where the component will be created",
    "default": "src/components"
    }

    Ulteriori Informazioni

    Per maggiori dettagli sugli schemi, consulta la documentazione Nx Generator Options.

    Tipi TypeScript con schema.d.ts

    Oltre a schema.json, il generatore crea un file schema.d.ts che fornisce tipi TypeScript per le opzioni:

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

    Questa interfaccia viene utilizzata nell’implementazione del generatore per type safety e completamento del codice:

    import { YourGeneratorSchema } from './schema';
    export default async function (tree: Tree, options: YourGeneratorSchema) {
    // TypeScript conosce i tipi di tutte le opzioni
    const { name, directory = 'src/components', withTests = true } = options;
    // ...
    }

    Implementazione di un Generatore

    Dopo aver creato il nuovo generatore come sopra, puoi scrivere l’implementazione in generator.ts.

    Un generatore è una funzione che modifica un filesystem virtuale (Tree), leggendo e scrivendo file per apportare le modifiche desiderate. Le modifiche al Tree vengono scritte su disco solo al termine dell’esecuzione, a meno che non sia eseguito in modalità “dry-run”.

    Ecco alcune operazioni comuni che potresti voler eseguire nel generatore:

    Lettura e Scrittura di File

    // Leggi un file
    const content = tree.read('path/to/file.ts', 'utf-8');
    // Scrivi un file
    tree.write('path/to/new-file.ts', 'export const hello = "world";');
    // Verifica se un file esiste
    if (tree.exists('path/to/file.ts')) {
    // Fai qualcosa
    }

    Generazione File da Template

    import { generateFiles, joinPathFragments } from '@nx/devkit';
    // Genera file da template
    generateFiles(
    tree,
    joinPathFragments(__dirname, 'files'), // Directory template
    'path/to/output', // Directory di output
    {
    // Variabili da sostituire nei template
    name: options.name,
    nameCamelCase: camelCase(options.name),
    nameKebabCase: kebabCase(options.name),
    // Aggiungi altre variabili se necessario
    },
    );

    Manipolazione AST (Abstract Syntax Tree) TypeScript

    Consigliamo di installare TSQuery per aiutare con la manipolazione AST.

    import { tsquery } from '@phenomnomnominal/tsquery';
    import * as ts from 'typescript';
    // Esempio: Incrementa numero di versione in un file
    // Analizza il contenuto del file in un AST TypeScript
    const sourceFile = tsquery.ast(tree.read('path/to/version.ts', 'utf-8'));
    // Trova nodi corrispondenti al selettore
    const nodes = tsquery.query(
    sourceFile,
    'VariableDeclaration:has(Identifier[name="VERSION"]) NumericLiteral',
    );
    if (nodes.length > 0) {
    // Ottieni il nodo del literal numerico
    const numericNode = nodes[0] as ts.NumericLiteral;
    // Ottieni la versione corrente e incrementala
    const currentVersion = Number(numericNode.text);
    const newVersion = currentVersion + 1;
    // Sostituisci il nodo nell'AST
    const result = tsquery.replace(
    sourceFile,
    'VariableDeclaration:has(Identifier[name="VERSION"]) NumericLiteral',
    () => ts.factory.createNumericLiteral(newVersion),
    );
    // Scrivi il contenuto aggiornato nel tree
    tree.write(
    'path/to/version.ts',
    ts
    .createPrinter({
    newLine: ts.NewLineKind.LineFeed,
    })
    .printNode(ts.EmitHint.Unspecified, result, sourceFile),
    );
    }

    Aggiunta Dipendenze

    import { addDependenciesToPackageJson } from '@nx/devkit';
    // Aggiungi dipendenze a package.json
    addDependenciesToPackageJson(
    tree,
    {
    'new-dependency': '^1.0.0',
    },
    {
    'new-dev-dependency': '^2.0.0',
    },
    );

    Formattazione File Generati

    import { formatFiles } from '@nx/devkit';
    // Formatta tutti i file modificati
    await formatFiles(tree);

    Lettura e Aggiornamento File JSON

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

    Esecuzione del Generatore

    Puoi eseguire il generatore in due modi:

    1. Installa il Nx Console VSCode Plugin se non l'hai già fatto
    2. Apri la console Nx in VSCode
    3. Clicca su Generate (UI) nella sezione "Common Nx Commands"
    4. Cerca @my-project/nx-plugin - my-generator
    5. Compila i parametri richiesti
      • Clicca su Generate

      Test del Generatore

      I test unitari per i generatori sono semplici da implementare. Ecco un pattern tipico:

      import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
      import { yourGenerator } from './generator';
      describe('your generator', () => {
      let tree;
      beforeEach(() => {
      // Crea un workspace tree vuoto
      tree = createTreeWithEmptyWorkspace();
      // Aggiungi eventuali file che devono esistere nel tree
      tree.write(
      'project.json',
      JSON.stringify({
      name: 'test-project',
      sourceRoot: 'src',
      }),
      );
      tree.write('src/existing-file.ts', 'export const existing = true;');
      });
      it('dovrebbe generare i file attesi', async () => {
      // Esegui il generatore
      await yourGenerator(tree, {
      name: 'test',
      // Aggiungi altre opzioni richieste
      });
      // Verifica che i file siano stati creati
      expect(tree.exists('src/test/file.ts')).toBeTruthy();
      // Controlla il contenuto del file
      const content = tree.read('src/test/file.ts', 'utf-8');
      expect(content).toContain('export const test');
      // Puoi anche usare snapshot
      expect(tree.read('src/test/file.ts', 'utf-8')).toMatchSnapshot();
      });
      it('dovrebbe aggiornare file esistenti', async () => {
      // Esegui il generatore
      await yourGenerator(tree, {
      name: 'test',
      // Aggiungi altre opzioni richieste
      });
      // Verifica aggiornamenti a file esistenti
      const content = tree.read('src/existing-file.ts', 'utf-8');
      expect(content).toContain('import { test } from');
      });
      it('dovrebbe gestire errori', async () => {
      // Verifica che il generatore generi un errore in certe condizioni
      await expect(
      yourGenerator(tree, {
      name: 'invalid',
      // Aggiungi opzioni che causano errore
      }),
      ).rejects.toThrow('Messaggio di errore atteso');
      });
      });

      Punti chiave per testare i generatori:

      • Usa createTreeWithEmptyWorkspace() per creare un filesystem virtuale
      • Prepara eventuali file prerequisiti prima di eseguire il generatore
      • Testa sia la creazione di nuovi file che l’aggiornamento di esistenti
      • Usa snapshot per contenuti complessi
      • Testa condizioni di errore per garantire un fallimento controllato

      Contribuire a Generator per @aws/nx-plugin

      Puoi usare ts#nx-generator anche per scaffoldare un generatore all’interno di @aws/nx-plugin.

      Quando eseguito nel nostro repository, questo generatore creerà i seguenti file:

      • Directorypackages/nx-plugin/src/<name>/
        • schema.json Schema per l’input del generatore
        • schema.d.ts Tipi TypeScript per lo schema
        • generator.ts Implementazione del generatore
        • generator.spec.ts Test per il generatore
      • Directorydocs/src/content/docs/guides/
        • <name>.mdx Pagina di documentazione per il generatore
      • packages/nx-plugin/generators.json Aggiornato per includere il generatore

      Potrai quindi iniziare a implementare il generatore.