Generador de Generadores Nx
Agrega un Generador Nx a un proyecto TypeScript, para ayudarte a automatizar tareas repetitivas como el scaffolding de componentes o la aplicación de estructuras de proyecto particulares.
Generar un Generador
Sección titulada «Generar un Generador»Puedes generar un generador de dos maneras:
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-generatorTambié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-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- 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
Opciones
Sección titulada «Opciones»| Parámetro | Tipo | Predeterminado | Descripción |
|---|---|---|---|
| project Requerido | string | - | Proyecto TypeScript al que agregar el generador. Recomendamos usar el generador ts#nx-plugin para crearlo. |
| name Requerido | string | - | Nombre del generador |
| description | string | - | Una descripción de tu generador |
| directory | string | - | El directorio dentro de la carpeta de código fuente del proyecto del plugin donde agregar el generador (predeterminado: <name>) |
| preferInstallDependencies | boolean | true | Si se prefiere instalar las dependencias después de que se ejecute el generador. Establecer en false para diferir la instalación al ejecutar múltiples generadores en lote (la instalación aún se ejecuta si es necesaria para que los generadores subsiguientes puedan calcular el grafo de proyectos de Nx); instalar una vez al final. |
Salida del Generador
Sección titulada «Salida del Generador»El generador creará los siguientes archivos de proyecto dentro del project dado:
Directoriosrc/<name>/
- schema.json Schema para la entrada de tu generador
- schema.d.ts Tipos TypeScript para tu schema
- generator.ts Implementación stub del generador
- generator.spec.ts Pruebas para tu generador
- README.md Documentación para tu generador
- generators.json Configuración Nx para definir tus generadores
- package.json Creado o actualizado para agregar una entrada “generators”
- tsconfig.json Actualizado para usar CommonJS
Modificación del Proyecto
Este generador actualizará el project seleccionado para usar CommonJS, ya que los Generadores Nx solo soportan CommonJS en la actualidad (consulta este issue de GitHub para soporte ESM).
Generadores Locales
Sección titulada «Generadores Locales»Selecciona tu proyecto local nx-plugin al ejecutar el generador ts#nx-generator, y especifica un nombre y opcionalmente un directorio y descripción.
Definir el Schema
Sección titulada «Definir el Schema»El archivo schema.json define las opciones que tu generador acepta. Sigue el formato JSON Schema con extensiones específicas de Nx.
Estructura Básica
Sección titulada «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": { // Your generator options go here }, "required": ["requiredOption1", "requiredOption2"]}Ejemplo Simple
Sección titulada «Ejemplo Simple»Aquí hay 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)
Sección titulada «Prompts Interactivos (CLI)»Puedes personalizar los prompts mostrados al ejecutar tu generador a través de 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 Desplegables
Sección titulada «Selecciones Desplegables»Para opciones con un conjunto fijo de opciones, usa enum para que los usuarios puedan seleccionar una de las opciones.
"style": { "type": "string", "description": "The styling approach to use", "enum": ["css", "scss", "styled-components", "none"], "default": "css"}Desplegable de Selección de Proyecto
Sección titulada «Desplegable de Selección de Proyecto»Un patrón común es permitir a los usuarios seleccionar de 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" le dice a Nx que llene el desplegable con todos los proyectos en el workspace.
Argumentos Posicionales
Sección titulada «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.
Establecer Prioridades
Sección titulada «Establecer 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 propiedades en la extensión Nx VSCode y Nx CLI.
Valores Predeterminados
Sección titulada «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
Sección titulada «Más Información»Para más detalles sobre schemas, consulta la documentación de Opciones de Generador Nx.
Tipos TypeScript con schema.d.ts
Sección titulada «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 de código:
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; // ...}Implementar un Generador
Sección titulada «Implementar 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 (el 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”. Un generador vacío se ve de la siguiente manera:
export const myGenerator = async (tree: Tree, options: MyGeneratorSchema) => { // Use the tree to apply changes};
export default myGenerator;Aquí hay algunas operaciones comunes que podrías querer realizar en tu generador:
Leer y Escribir Archivos
Sección titulada «Leer y Escribir Archivos»// 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}Generar Archivos desde Plantillas
Sección titulada «Generar Archivos desde Plantillas»Puedes generar archivos con la utilidad generateFiles de @nx/devkit. Esto te permite definir plantillas en sintaxis EJS, y sustituir 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 },);Transformaciones de Código con GritQL
Sección titulada «Transformaciones de Código con GritQL»Puedes usar GritQL para buscar y transformar código fuente de manera declarativa en tus generadores. GritQL soporta múltiples lenguajes incluyendo TypeScript, JavaScript, Python, HCL (Terraform), y más — por lo que puedes usar la misma sintaxis de patrón en toda tu pila.
El Nx Plugin for AWS expone dos helpers:
applyGritQL(tree, filePath, pattern)— aplica un patrón de reescritura GritQL a un archivo y devuelvePromise<boolean>indicando si se realizaron cambiosmatchGritQL(tree, filePath, pattern)— verifica si un patrón GritQL coincide en algún lugar de un archivo y devuelvePromise<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}Los patrones GritQL también funcionan en archivos que no son TypeScript. Prefija tu patrón con language <name> para apuntar a otros lenguajes:
// Python: replace print statements with logging callsawait applyGritQL( tree, 'src/handler.py', 'language python\n`print($msg)` => `logger.info($msg)`',);Los patrones GritQL usan fragmentos de código delimitados por backticks con $metavariables como comodines. Usa => para reescrituras y cláusulas where para condiciones.
Agregar Dependencias
Sección titulada «Agregar Dependencias»import { addDependenciesToPackageJson } from '@nx/devkit';
// Add dependencies to package.jsonaddDependenciesToPackageJson( tree, { 'new-dependency': '^1.0.0', }, { 'new-dev-dependency': '^2.0.0', },);Formatear Archivos Generados
Sección titulada «Formatear Archivos Generados»import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
// Format all files that were modifiedawait formatFilesInSubtree(tree, 'optional/path/to/format');Leer y Actualizar Archivos JSON
Sección titulada «Leer y Actualizar Archivos 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;});Extender un Generador del Nx Plugin for AWS
Sección titulada «Extender un Generador del Nx Plugin for AWS»Puedes importar generadores del Nx Plugin for AWS, y extenderlos o componerlos como desees, por ejemplo, podrías desear crear un generador que se base en un proyecto 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;};Generadores OpenAPI
Sección titulada «Generadores OpenAPI»Puedes usar y extender los generadores que usamos para clientes y hooks TypeScript de manera similar a lo anterior:
import { openApiTsClientGenerator } from '@aws/nx-plugin/sdk/open-api';
export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => { await openApiTsClientGenerator(tree, { ... });
// Add additional files here};También exponemos un método que te permite construir una estructura de datos que puede usarse para iterar sobre operaciones en una especificación OpenAPI y por lo tanto instrumentar tu propia generación de código, por ejemplo:
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, );};Lo cual luego te permite escribir plantillas como:
export const myOperationNames = [<%_ allOperations.forEach((op) => { _%> '<%- op.name %>',<%_ }); _%>];Consulta el código base en GitHub para plantillas de ejemplo más complejas.
Ejecutar tu Generador
Sección titulada «Ejecutar tu Generador»Puedes ejecutar tu generador de dos maneras:
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-generatorTambié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-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- 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
Probar tu Generador
Sección titulada «Probar tu Generador»Las pruebas unitarias para generadores son sencillas de implementar. Aquí hay un patrón típico:
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'); });});Puntos clave para probar generadores:
- Usa
createTreeWithEmptyWorkspace()para crear un sistema de archivos virtual - Configura cualquier archivo prerequisito antes de ejecutar el generador
- Prueba tanto la creación de nuevos archivos como las actualizaciones a archivos existentes
- Usa snapshots para contenido de archivos complejo
- Prueba condiciones de error para asegurar que tu generador falle de manera elegante
Contribuir Generadores a @aws/nx-plugin
Sección titulada «Contribuir Generadores a @aws/nx-plugin»También puedes usar ts#nx-generator para crear el scaffold de un generador dentro de @aws/nx-plugin.
Cuando este generador se ejecuta en nuestro repositorio, generará los siguientes archivos para ti:
Directoriopackages/nx-plugin/src/<name>/
- schema.json Schema para la entrada de tu generador
- schema.d.ts Tipos TypeScript para tu schema
- generator.ts Implementación del generador
- generator.spec.ts Pruebas para tu generador
Directoriodocs/src/content/docs/guides/
- <name>.mdx Página de documentación para tu generador
- packages/nx-plugin/generators.json Actualizado para incluir tu generador
- packages/nx-plugin/sdk/<prefix>.ts Actualizado para exponer tu generador desde el SDK (para generadores
ts#ypy#)
Luego puedes comenzar a implementar tu generador.