Contribuir con un Generador
Vamos a crear un nuevo generador para contribuir a @aws/nx-plugin. Nuestro objetivo será generar un nuevo procedimiento para una API tRPC.
Clona el plugin
Sección titulada «Clona el plugin»Primero, clonemos el plugin:
git clone git@github.com:awslabs/nx-plugin-for-aws.gitLuego instala y construye:
cd nx-plugin-for-awspnpm ipnpm nx run-many --target build --allCrea un generador vacío
Sección titulada «Crea un generador vacío»Creemos el nuevo generador en packages/nx-plugin/src/trpc/procedure.
¡Proveemos un generador para crear nuevos generadores para que puedas estructurar rápidamente tu nuevo generador! Puedes ejecutar este generador de la siguiente manera:
- 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
- pluginProject: @aws/nx-plugin
- name: ts#trpc-api#procedure
- directory: trpc/procedure
- description: Adds a procedure to a tRPC API
- Haga clic en
Generate
pnpm nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC APIyarn nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC APInpx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC APIbunx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC APITambién puede realizar una ejecución en seco para ver qué archivos se cambiarían
pnpm nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API --dry-runyarn nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API --dry-runnpx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API --dry-runbunx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API --dry-runNotarás que se han generado los siguientes archivos:
Directorypackages/nx-plugin/src/trpc/procedure
- schema.json Define las entradas del generador
- schema.d.ts Interfaz TypeScript que coincide con el schema
- generator.ts Función que Nx ejecuta como generador
- generator.spec.ts Pruebas para el generador
Directorydocs/src/content/docs/guides/
- trpc-procedure.mdx Documentación del generador
- packages/nx-plugin/generators.json Actualizado para incluir el generador
Actualicemos el schema para agregar las propiedades que necesitaremos:
{ "$schema": "https://json-schema.org/schema", "$id": "tRPCProcedure", "title": "Adds a procedure to a tRPC API", "type": "object", "properties": { "project": { "type": "string", "description": "tRPC API project", "x-prompt": "Select the tRPC API project to add the procedure to", "x-dropdown": "projects", "x-priority": "important" }, "procedure": { "description": "The name of the new procedure", "type": "string", "x-prompt": "What would you like to call your new procedure?", "x-priority": "important", }, "type": { "description": "The type of procedure to generate", "type": "string", "x-prompt": "What type of procedure would you like to generate?", "x-priority": "important", "default": "query", "enum": ["query", "mutation"] } }, "required": ["project", "procedure"]}export interface TrpcProcedureSchema { project: string; procedure: string; type: 'query' | 'mutation';}Notarás que el generador ya ha sido registrado en packages/nx-plugin/generators.json:
... "generators": { ... "ts#trpc-api#procedure": { "factory": "./src/trpc/procedure/generator", "schema": "./src/trpc/procedure/schema.json", "description": "Adds a procedure to a tRPC API" } },...Implementa el generador
Sección titulada «Implementa el generador»Para agregar un procedimiento a una API tRPC, necesitamos hacer dos cosas:
- Crear un archivo TypeScript para el nuevo procedimiento
- Agregar el procedimiento al router
Crea el nuevo procedimiento
Sección titulada «Crea el nuevo procedimiento»Para crear el archivo TypeScript del nuevo procedimiento, usaremos una utilidad llamada generateFiles. Con esto, podemos definir una plantilla EJS que renderizaremos en nuestro generador con variables basadas en las opciones seleccionadas por el usuario.
Primero, definiremos la plantilla en packages/nx-plugin/src/trpc/procedure/files/procedures/__procedureNameKebabCase__.ts.template:
import { publicProcedure } from '../init.js';import { z } from 'zod';
export const <%- procedureNameCamelCase %> = publicProcedure .input(z.object({ // TODO: define input })) .output(z.object({ // TODO: define output })) .<%- procedureType %>(async ({ input, ctx }) => { // TODO: implement! return {}; });En la plantilla, hicimos referencia a tres variables:
procedureNameCamelCaseprocedureNameKebabCaseprocedureType
Así que necesitamos asegurarnos de pasar esas variables a generateFiles, junto con el directorio donde generar los archivos, específicamente la ubicación de los archivos fuente (es decir, sourceRoot) para el proyecto tRPC seleccionado por el usuario como entrada del generador, que podemos extraer de la configuración del proyecto.
Actualicemos el generador para hacer esto:
import { generateFiles, joinPathFragments, readProjectConfiguration, Tree,} from '@nx/devkit';import { TrpcProcedureSchema } from './schema';import { formatFilesInSubtree } from '../../utils/format';import camelCase from 'lodash.camelcase';import kebabCase from 'lodash.kebabcase';
export const trpcProcedureGenerator = async ( tree: Tree, options: TrpcProcedureSchema,) => { const projectConfig = readProjectConfiguration(tree, options.project);
const procedureNameCamelCase = camelCase(options.procedure); const procedureNameKebabCase = kebabCase(options.procedure);
generateFiles( tree, joinPathFragments(__dirname, 'files'), projectConfig.sourceRoot, { procedureNameCamelCase, procedureNameKebabCase, procedureType: options.type, }, );
await formatFilesInSubtree(tree);};
export default trpcProcedureGenerator;Agrega el procedimiento al router
Sección titulada «Agrega el procedimiento al router»Luego, queremos que el generador conecte el nuevo procedimiento al router. ¡Esto implica leer y actualizar el código fuente del usuario!
Usamos manipulación de AST de TypeScript para actualizar las partes relevantes del archivo fuente. Hay algunos helpers llamados replace y destructuredImport para facilitar esto.
import { generateFiles, joinPathFragments, readProjectConfiguration, Tree,} from '@nx/devkit';import { TrpcProcedureSchema } from './schema';import { formatFilesInSubtree } from '../../utils/format';import camelCase from 'lodash.camelcase';import kebabCase from 'lodash.kebabcase';import { destructuredImport, replace } from '../../utils/ast';import { factory, ObjectLiteralExpression } from 'typescript';
export const trpcProcedureGenerator = async ( tree: Tree, options: TrpcProcedureSchema,) => { const projectConfig = readProjectConfiguration(tree, options.project);
const procedureNameCamelCase = camelCase(options.procedure); const procedureNameKebabCase = kebabCase(options.procedure);
generateFiles( tree, joinPathFragments(__dirname, 'files'), projectConfig.sourceRoot, { procedureNameCamelCase, procedureNameKebabCase, procedureType: options.type, }, );
const routerPath = joinPathFragments(projectConfig.sourceRoot, 'router.ts');
destructuredImport( tree, routerPath, [procedureNameCamelCase], `./procedures/${procedureNameKebabCase}.js`, );
replace( tree, routerPath, 'CallExpression[expression.name="router"] > ObjectLiteralExpression', (node) => factory.createObjectLiteralExpression([ ...(node as ObjectLiteralExpression).properties, factory.createShorthandPropertyAssignment(procedureNameCamelCase), ]), );
await formatFilesInSubtree(tree);};
export default trpcProcedureGenerator;Ahora que hemos implementado el generador, compilémoslo para asegurarnos de que esté disponible para probarlo en nuestro proyecto de aventura dungeon.
pnpm nx run @aws/nx-plugin:compileProbando el generador
Sección titulada «Probando el generador»Para probar el generador, vincularemos nuestro Nx Plugin for AWS local a una base de código existente.
Crea un proyecto de prueba con una API tRPC
Sección titulada «Crea un proyecto de prueba con una API tRPC»En un directorio separado, crea un nuevo workspace de prueba:
npx create-nx-workspace@21.6.5 trpc-generator-test --pm=pnpm --preset=@aws/nx-plugin --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 trpc-generator-test --pm=yarn --preset=@aws/nx-plugin --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 trpc-generator-test --pm=npm --preset=@aws/nx-plugin --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 trpc-generator-test --pm=bun --preset=@aws/nx-plugin --ci=skip --aiAgentsLuego, generemos una API tRPC para agregar el procedimiento:
- 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#trpc-api - Complete los parámetros requeridos
- apiName: test-api
- Haga clic en
Generate
pnpm nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactiveyarn nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactivenpx nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactivebunx nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactiveTambién puede realizar una ejecución en seco para ver qué archivos se cambiarían
pnpm nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive --dry-runVincula nuestro Nx Plugin for AWS local
Sección titulada «Vincula nuestro Nx Plugin for AWS local»En tu base de código, vinculamos nuestro @aws/nx-plugin local:
cd path/to/trpc-generator-testpnpm link path/to/nx-plugin-for-aws/dist/packages/nx-plugincd path/to/trpc-generator-testyarn link path/to/nx-plugin-for-aws/dist/packages/nx-plugincd path/to/trpc-generator-testnpm link path/to/nx-plugin-for-aws/dist/packages/nx-plugincd path/to/nx-plugin-for-aws/dist/packages/nx-pluginbun linkcd path/to/trpc-generator-testbun link @aws/nx-pluginEjecuta el nuevo generador
Sección titulada «Ejecuta el nuevo generador»Probemos el nuevo generador:
- 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#trpc-api#procedure - Complete los parámetros requeridos
- Haga clic en
Generate
pnpm nx g @aws/nx-plugin:ts#trpc-api#procedureyarn nx g @aws/nx-plugin:ts#trpc-api#procedurenpx nx g @aws/nx-plugin:ts#trpc-api#procedurebunx nx g @aws/nx-plugin:ts#trpc-api#procedureTambién puede realizar una ejecución en seco para ver qué archivos se cambiarían
pnpm nx g @aws/nx-plugin:ts#trpc-api#procedure --dry-runyarn nx g @aws/nx-plugin:ts#trpc-api#procedure --dry-runnpx nx g @aws/nx-plugin:ts#trpc-api#procedure --dry-runbunx nx g @aws/nx-plugin:ts#trpc-api#procedure --dry-runSi tiene éxito, deberíamos haber generado un nuevo procedimiento y agregado el procedimiento a nuestro router en router.ts.
Ejercicios
Sección titulada «Ejercicios»Si has llegado hasta aquí y aún tienes tiempo para experimentar con generadores Nx, aquí hay algunas sugerencias de características para agregar al generador de procedimientos:
1. Operaciones anidadas
Sección titulada «1. Operaciones anidadas»Intenta actualizar el generador para soportar routers anidados:
- Aceptando notación de puntos para la entrada
procedure(ej.games.query) - Generando un procedimiento con nombre basado en la notación de puntos invertida (ej.
queryGames) - ¡Agregando el router anidado apropiado (o actualizándolo si ya existe)!
2. Validación
Sección titulada «2. Validación»Nuestro generador debería prevenir problemas potenciales, como que un usuario seleccione un project que no sea una API tRPC. Mira el generador api-connection para ver un ejemplo de esto.
3. Pruebas unitarias
Sección titulada «3. Pruebas unitarias»Escribe algunas pruebas unitarias para el generador. Son bastante sencillas de implementar y la mayoría sigue el flujo general:
- Crea un workspace tree vacío usando
createTreeUsingTsSolutionSetup() - Agrega cualquier archivo que deba existir previamente en el tree (ej.
project.jsonysrc/router.tspara un backend tRPC) - Ejecuta el generador bajo prueba
- Valida que los cambios esperados se realicen en el tree
4. Pruebas end-to-end
Sección titulada «4. Pruebas end-to-end»Actualmente tenemos una sola “prueba de humo” que ejecuta todos los generadores y verifica que la construcción tenga éxito. Esto debería actualizarse para incluir el nuevo generador.