Generador de Generadores de Nx
Agrega un Generador de Nx a un proyecto TypeScript, para ayudarte a automatizar tareas repetitivas como la creación de componentes o la implementación de estructuras de proyecto específicas.
Uso
Generar un Generador
Puedes generar un generador de dos formas:
- Instale el Nx Console VSCode Plugin si aún no lo ha hecho
- Abra la consola Nx en VSCode
- Haga clic en
Generate (UI)
en la sección "Common Nx Commands" - Busque
@aws/nx-plugin - ts#nx-generator
- Complete los parámetros requeridos
- Haga clic en
Generate
pnpm nx g @aws/nx-plugin:ts#nx-generator
yarn nx g @aws/nx-plugin:ts#nx-generator
npx nx g @aws/nx-plugin:ts#nx-generator
bunx nx g @aws/nx-plugin:ts#nx-generator
También puede realizar una ejecución en seco para ver qué archivos se cambiarían
pnpm nx g @aws/nx-plugin:ts#nx-generator --dry-run
yarn nx g @aws/nx-plugin:ts#nx-generator --dry-run
npx nx g @aws/nx-plugin:ts#nx-generator --dry-run
bunx nx g @aws/nx-plugin:ts#nx-generator --dry-run
Opciones
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>) |
Resultado del Generador
El generador creará los siguientes archivos de proyecto dentro del pluginProject
especificado:
Directorysrc/<name>/
- schema.json Esquema para la entrada de tu generador
- schema.d.ts Tipos TypeScript para tu esquema
- generator.ts Implementación base del generador
- generator.spec.ts Pruebas para tu generador
- generators.json Configuración de Nx para definir tus generadores
- package.json Creado o actualizado para agregar una entrada “generators”
- tsconfig.json Actualizado para usar CommonJS
Este generador actualizará el pluginProject
seleccionado para usar CommonJS, ya que los Generadores de Nx actualmente solo admiten CommonJS (consulta este issue de GitHub para el soporte de ESM).
Generadores Locales
Selecciona tu proyecto local nx-plugin
cuando ejecutes el generador ts#nx-generator
, y especifica un nombre junto con un directorio y descripción opcionales.
Definiendo el Esquema
El archivo schema.json
define las opciones que acepta tu generador. Sigue el formato JSON Schema con extensiones específicas de Nx.
Estructura Básica
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 de generador van aquí }, "required": ["requiredOption1", "requiredOption2"]}
Ejemplo Simple
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"]}
Prompts Interactivos (CLI)
Puedes personalizar los prompts mostrados al ejecutar tu generador mediante 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 de sí/no:
"withTests": { "type": "boolean", "description": "Whether to generate test files", "x-prompt": "Would you like to generate test files?"}
Selecciones en Menú Desplegable
Para opciones con un conjunto fijo de opciones, usa enum
para que los usuarios puedan seleccionar una de ellas.
"style": { "type": "string", "description": "The styling approach to use", "enum": ["css", "scss", "styled-components", "none"], "default": "css"}
Menú Desplegable de Selección de Proyecto
Un patrón común es permitir que los usuarios seleccionen entre proyectos existentes en el workspace:
"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 menú con todos los proyectos del workspace.
Argumentos Posicionales
Puedes configurar opciones para que se pasen como argumentos posicionales al ejecutar el generador desde la línea de comandos:
"name": { "type": "string", "description": "Component name", "x-priority": "important", "$default": { "$source": "argv", "index": 0 }}
Esto permite a los usuarios ejecutar tu generador como nx g your-generator my-component
en lugar de nx g your-generator --name=my-component
.
Estableciendo Prioridades
Usa la propiedad x-priority
para indicar qué opciones son más importantes:
"name": { "type": "string", "description": "Component name", "x-priority": "important"}
Las opciones pueden tener prioridades de "important"
o "internal"
. Esto ayuda a Nx a ordenar las propiedades en la extensión VSCode de Nx y en la CLI de Nx.
Valores Predeterminados
Puedes proporcionar valores predeterminados para las opciones:
"directory": { "type": "string", "description": "Directory where the component will be created", "default": "src/components"}
Más Información
Para más detalles sobre esquemas, consulta la documentación de Opciones de Generador de Nx.
Tipos TypeScript con schema.d.ts
Junto con schema.json
, el generador crea un archivo schema.d.ts
que proporciona tipos TypeScript para las opciones de tu generador:
export interface YourGeneratorSchema { name: string; directory?: string; withTests?: boolean;}
Esta interfaz se usa en la implementación de tu generador para proporcionar seguridad de tipos y autocompletado:
import { YourGeneratorSchema } from './schema';
export default async function (tree: Tree, options: YourGeneratorSchema) { // TypeScript conoce los tipos de todas tus opciones const { name, directory = 'src/components', withTests = true } = options; // ...}
Implementando un Generador
Después de crear el nuevo generador como se indicó anteriormente, puedes escribir tu implementación en generator.ts
.
Un generador es una función que muta un sistema de archivos virtual (Tree
), leyendo y escribiendo archivos para realizar los cambios deseados. Los cambios del Tree
solo se escriben en disco una vez que el generador termina de ejecutarse, a menos que se ejecute en modo “dry-run”.
Aquí hay algunas operaciones comunes que podrías realizar en tu generador:
Lectura y Escritura de Archivos
// Leer un archivoconst content = tree.read('path/to/file.ts', 'utf-8');
// Escribir un archivotree.write('path/to/new-file.ts', 'export const hello = "world";');
// Verificar si un archivo existeif (tree.exists('path/to/file.ts')) { // Hacer algo}
Generación de Archivos desde Plantillas
import { generateFiles, joinPathFragments } from '@nx/devkit';
// Generar archivos desde plantillasgenerateFiles( tree, joinPathFragments(__dirname, 'files'), // Directorio de plantillas 'path/to/output', // Directorio de salida { // Variables para reemplazar en plantillas name: options.name, nameCamelCase: camelCase(options.name), nameKebabCase: kebabCase(options.name), // Agrega más variables según sea necesario },);
Manipulación de AST (Árbol de Sintaxis Abstracta) de TypeScript
Recomendamos instalar TSQuery para ayudar con la manipulación de AST.
import { tsquery } from '@phenomnomnominal/tsquery';import * as ts from 'typescript';
// Ejemplo: Incrementar número de versión en un archivo
// Parsear el contenido del archivo en un AST de TypeScriptconst sourceFile = tsquery.ast(tree.read('path/to/version.ts', 'utf-8'));
// Encontrar nodos que coincidan con el selectorconst nodes = tsquery.query( sourceFile, 'VariableDeclaration:has(Identifier[name="VERSION"]) NumericLiteral',);
if (nodes.length > 0) { // Obtener el nodo del literal numérico const numericNode = nodes[0] as ts.NumericLiteral;
// Obtener el número de versión actual e incrementarlo const currentVersion = Number(numericNode.text); const newVersion = currentVersion + 1;
// Reemplazar el nodo en el AST const result = tsquery.replace( sourceFile, 'VariableDeclaration:has(Identifier[name="VERSION"]) NumericLiteral', () => ts.factory.createNumericLiteral(newVersion), );
// Escribir el contenido actualizado en el árbol tree.write( 'path/to/version.ts', ts .createPrinter({ newLine: ts.NewLineKind.LineFeed, }) .printNode(ts.EmitHint.Unspecified, result, sourceFile), );}
Agregar Dependencias
import { addDependenciesToPackageJson } from '@nx/devkit';
// Agregar dependencias a package.jsonaddDependenciesToPackageJson( tree, { 'new-dependency': '^1.0.0', }, { 'new-dev-dependency': '^2.0.0', },);
Formatear Archivos Generados
import { formatFiles } from '@nx/devkit';
// Formatear todos los archivos modificadosawait formatFiles(tree);
Leer y Actualizar Archivos JSON
import { readJson, updateJson } from '@nx/devkit';
// Leer un archivo JSONconst packageJson = readJson(tree, 'package.json');
// Actualizar un archivo JSONupdateJson(tree, 'tsconfig.json', (json) => { json.compilerOptions = { ...json.compilerOptions, strict: true, }; return json;});
Ejecutando Tu Generador
Puedes ejecutar tu generador de dos formas:
- Instale el Nx Console VSCode Plugin si aún no lo ha hecho
- Abra la consola Nx en VSCode
- Haga clic en
Generate (UI)
en la sección "Common Nx Commands" - Busque
@my-project/nx-plugin - my-generator
- Complete los parámetros requeridos
- Haga clic en
Generate
pnpm nx g @my-project/nx-plugin:my-generator
yarn nx g @my-project/nx-plugin:my-generator
npx nx g @my-project/nx-plugin:my-generator
bunx nx g @my-project/nx-plugin:my-generator
También puede realizar una ejecución en seco para ver qué archivos se cambiarían
pnpm nx g @my-project/nx-plugin:my-generator --dry-run
yarn nx g @my-project/nx-plugin:my-generator --dry-run
npx nx g @my-project/nx-plugin:my-generator --dry-run
bunx nx g @my-project/nx-plugin:my-generator --dry-run
Probando Tu Generador
Las pruebas unitarias para generadores son sencillas de implementar. Aquí un patrón típico:
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';import { yourGenerator } from './generator';
describe('your generator', () => { let tree;
beforeEach(() => { // Crear un árbol de workspace vacío tree = createTreeWithEmptyWorkspace();
// Agregar archivos que deban existir previamente 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 () => { // Ejecutar el generador await yourGenerator(tree, { name: 'test', // Agregar otras opciones requeridas });
// Verificar creación de archivos expect(tree.exists('src/test/file.ts')).toBeTruthy();
// Verificar contenido de archivo const content = tree.read('src/test/file.ts', 'utf-8'); expect(content).toContain('export const test');
// También puedes usar snapshots expect(tree.read('src/test/file.ts', 'utf-8')).toMatchSnapshot(); });
it('should update existing files', async () => { // Ejecutar el generador await yourGenerator(tree, { name: 'test', // Agregar otras opciones requeridas });
// Verificar actualización de archivos existentes const content = tree.read('src/existing-file.ts', 'utf-8'); expect(content).toContain('import { test } from'); });
it('should handle errors', async () => { // Esperar que el generador lance un error en ciertas condiciones await expect( yourGenerator(tree, { name: 'invalid', // Agregar opciones que causen un error }), ).rejects.toThrow('Expected error message'); });});
Puntos clave para probar generadores:
- Usa
createTreeWithEmptyWorkspace()
para crear un sistema de archivos virtual - Configura archivos prerrequisitos antes de ejecutar el generador
- Prueba tanto la creación de nuevos archivos como actualizaciones a existentes
- Usa snapshots para contenido de archivos complejos
- Prueba condiciones de error para asegurar un fallo controlado
Contribuyendo Generadores a @aws/nx-plugin
También puedes usar ts#nx-generator
para crear un generador dentro de @aws/nx-plugin
.
Cuando este generador se ejecuta en nuestro repositorio, generará los siguientes archivos:
Directorypackages/nx-plugin/src/<name>/
- schema.json Esquema para la entrada de tu generador
- schema.d.ts Tipos TypeScript para tu esquema
- generator.ts Implementación del generador
- generator.spec.ts Pruebas para tu generador
Directorydocs/src/content/docs/guides/
- <name>.mdx Página de documentación para tu generador
- packages/nx-plugin/generators.json Actualizado para incluir tu generador
Podrás entonces comenzar a implementar tu generador.