Saltearse al contenido

Generador de Generadores de Nx

Agrega un Nx Generator a un proyecto TypeScript, para ayudarte a automatizar tareas repetitivas como la creación de componentes o la aplicación de estructuras de proyecto específicas.

Puedes generar un generator de dos formas:

  1. Instale el Nx Console VSCode Plugin si aún no lo ha hecho
  2. Abra la consola Nx en VSCode
  3. Haga clic en Generate (UI) en la sección "Common Nx Commands"
  4. Busque @aws/nx-plugin - ts#nx-generator
  5. Complete los parámetros requeridos
    • Haga clic en Generate
    Parámetro Tipo Predeterminado Descripción
    pluginProject Requerido string - TypeScript project to add the generator to. We recommend creating a ts#project in a top-level 'tools' directory.
    name Requerido 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>)

    El generator creará los siguientes archivos en el proyecto pluginProject seleccionado:

    • Directorysrc/<name>/
      • schema.json Esquema para la entrada del generator
      • schema.d.ts Tipos TypeScript para el esquema
      • generator.ts Implementación base del generator
      • generator.spec.ts Pruebas para el generator
    • generators.json Configuración de Nx para definir tus generators
    • package.json Creado o actualizado para agregar entrada “generators”
    • tsconfig.json Actualizado para usar CommonJS

    Este generator actualizará el pluginProject seleccionado para usar CommonJS, ya que Nx Generators actualmente solo soporta CommonJS (consulta este issue de GitHub para soporte ESM).

    Selecciona tu proyecto local nx-plugin al ejecutar el generator ts#nx-generator, y especifica un nombre junto con un directorio y descripción opcionales.

    El archivo schema.json define las opciones que acepta tu generator. Sigue el formato JSON Schema con extensiones específicas de Nx.

    Un archivo schema.json tiene la siguiente estructura básica:

    {
    "$schema": "https://json-schema.org/schema",
    "$id": "YourGeneratorName",
    "title": "Your Generator Title",
    "description": "Description of what your generator does",
    "type": "object",
    "properties": {
    // Tus opciones del generator van aquí
    },
    "required": ["requiredOption1", "requiredOption2"]
    }

    Aquí un ejemplo simple con algunas opciones básicas:

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

    Puedes personalizar los prompts mostrados al ejecutar tu generator desde la CLI agregando la propiedad x-prompt:

    "name": {
    "type": "string",
    "description": "Component name",
    "x-prompt": "What is the name of your component?"
    }

    Para opciones booleanas, puedes usar un prompt sí/no:

    "withTests": {
    "type": "boolean",
    "description": "Whether to generate test files",
    "x-prompt": "Would you like to generate test files?"
    }

    Para opciones con un conjunto fijo de opciones, usa enum para que los usuarios puedan seleccionar:

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

    Un patrón común es permitir a los usuarios seleccionar entre proyectos existentes:

    "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 propiedad x-dropdown: "projects" indica a Nx que llene el dropdown con todos los proyectos del workspace.

    Puedes configurar opciones para pasarse como argumentos posicionales al ejecutar el generator desde CLI:

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

    Esto permite ejecutar el generator como nx g your-generator my-component en lugar de nx g your-generator --name=my-component.

    Usa la propiedad x-priority para indicar opciones importantes:

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

    Las prioridades pueden ser "important" o "internal". Esto ayuda a Nx a ordenar propiedades en la extensión VSCode y CLI.

    Puedes proveer valores predeterminados:

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

    Para más detalles sobre esquemas, consulta la documentación de Nx Generator Options.

    Junto con schema.json, el generator crea un archivo schema.d.ts con tipos TypeScript para las opciones:

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

    Esta interfaz se usa en la implementación del generator para seguridad de tipos:

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

    Tras crear el generator, puedes escribir su implementación en generator.ts.

    Un generator es una función que modifica un sistema de archivos virtual (Tree), leyendo y escribiendo archivos. Los cambios se escriben al disco solo al finalizar, a menos que se ejecute en modo “dry-run”.

    Operaciones comunes en un generator:

    // Leer archivo
    const content = tree.read('path/to/file.ts', 'utf-8');
    // Escribir archivo
    tree.write('path/to/new-file.ts', 'export const hello = "world";');
    // Verificar existencia
    if (tree.exists('path/to/file.ts')) {
    // Hacer algo
    }

    Puedes generar archivos con generateFiles de @nx/devkit, usando plantillas EJS:

    import { generateFiles, joinPathFragments } from '@nx/devkit';
    // Generar archivos desde plantillas
    generateFiles(
    tree,
    joinPathFragments(__dirname, 'files'), // Directorio de plantillas
    'path/to/output', // Directorio de salida
    {
    // Variables para sustituir
    name: options.name,
    nameCamelCase: camelCase(options.name),
    nameKebabCase: kebabCase(options.name),
    },
    );

    Puedes usar tsAstReplace del Nx Plugin para AWS para modificar ASTs:

    import { tsAstReplace } from '@aws/nx-plugin/sdk/utils/ast';
    import * as ts from 'typescript';
    // Ejemplo: Incrementar versión
    tsAstReplace(
    tree,
    'path/to/version.ts',
    'VariableDeclaration:has(Identifier[name="VERSION"]) NumericLiteral',
    (node: ts.NumericLiteral) =>
    ts.factory.createNumericLiteral(Number(node.text) + 1));
    import { addDependenciesToPackageJson } from '@nx/devkit';
    // Agregar dependencias a package.json
    addDependenciesToPackageJson(
    tree,
    {
    'new-dependency': '^1.0.0',
    },
    {
    'new-dev-dependency': '^2.0.0',
    },
    );
    import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
    // Formatear archivos modificados
    await formatFilesInSubtree(tree, 'optional/path/to/format');
    import { readJson, updateJson } from '@nx/devkit';
    // Leer JSON
    const packageJson = readJson(tree, 'package.json');
    // Actualizar JSON
    updateJson(tree, 'tsconfig.json', (json) => {
    json.compilerOptions = {
    ...json.compilerOptions,
    strict: true,
    };
    return json;
    });

    Puedes importar y extender generators existentes:

    import { tsProjectGenerator } from '@aws/nx-plugin/sdk/ts';
    export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => {
    const callback = await tsProjectGenerator(tree, { ... });
    // Extender el generator de proyecto TypeScript aquí
    return callback;
    };

    Puedes usar generators para clientes TypeScript:

    import { openApiTsClientGenerator } from '@aws/nx-plugin/sdk/open-api';
    export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => {
    await openApiTsClientGenerator(tree, { ... });
    // Agregar archivos adicionales
    };

    También puedes generar datos para iterar sobre operaciones OpenAPI:

    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'),
    'path/to/output',
    data,
    );
    };

    Ejemplo de plantilla EJS:

    files/my-operations.ts.template
    export const myOperationNames = [
    <%_ allOperations.forEach((op) => { _%>
    '<%- op.name %>',
    <%_ }); _%>
    ];

    Consulta el repositorio en GitHub para ejemplos más complejos.

    Puedes ejecutar tu generator de dos formas:

    1. Instale el Nx Console VSCode Plugin si aún no lo ha hecho
    2. Abra la consola Nx en VSCode
    3. Haga clic en Generate (UI) en la sección "Common Nx Commands"
    4. Busque @my-project/nx-plugin - my-generator
    5. Complete los parámetros requeridos
      • Haga clic en Generate

      Las pruebas unitarias para generators son sencillas:

      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();
      const content = tree.read('src/test/file.ts', 'utf-8');
      expect(content).toContain('export const test');
      });
      it('should handle errors', async () => {
      await expect(
      yourGenerator(tree, { name: 'invalid' })
      ).rejects.toThrow('Expected error message');
      });
      });

      Puntos clave para pruebas:

      • Usar createTreeWithEmptyWorkspace()
      • Configurar archivos preexistentes
      • Verificar creación y modificación de archivos
      • Usar snapshots para contenido complejo
      • Probar condiciones de error

      Puedes usar ts#nx-generator para crear generators dentro de @aws/nx-plugin.

      Al ejecutarlo en nuestro repositorio, generará:

      • Directorypackages/nx-plugin/src/<name>/
        • schema.json
        • schema.d.ts
        • generator.ts
        • generator.spec.ts
      • Directorydocs/src/content/docs/guides/
        • <name>.mdx
      • packages/nx-plugin/generators.json Actualizado