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.git
Luego instala y construye:
cd nx-plugin-for-awspnpm ipnpm nx run-many --target build --all
Crea 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 API
yarn 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
npx 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
bunx 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
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 --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API --dry-run
yarn 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-run
npx 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-run
bunx 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-run
Notará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:
procedureNameCamelCase
procedureNameKebabCase
procedureType
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:compile
Probando 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.0.3 trpc-generator-test --pm=pnpm --preset=@aws/nx-plugin --ci=skip
npx create-nx-workspace@~21.0.3 trpc-generator-test --pm=yarn --preset=@aws/nx-plugin --ci=skip
npx create-nx-workspace@~21.0.3 trpc-generator-test --pm=npm --preset=@aws/nx-plugin --ci=skip
npx create-nx-workspace@~21.0.3 trpc-generator-test --pm=bun --preset=@aws/nx-plugin --ci=skip
Luego, 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-interactive
yarn nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive
npx nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive
bunx nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive
Tambié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-run
yarn nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#trpc-api --apiName=test-api --no-interactive --dry-run
Vincula 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-plugin
cd path/to/trpc-generator-testyarn link path/to/nx-plugin-for-aws/dist/packages/nx-plugin
cd path/to/trpc-generator-testnpm link path/to/nx-plugin-for-aws/dist/packages/nx-plugin
cd path/to/nx-plugin-for-aws/dist/packages/nx-pluginbun linkcd path/to/trpc-generator-testbun link @aws/nx-plugin
Ejecuta 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#procedure
yarn nx g @aws/nx-plugin:ts#trpc-api#procedure
npx nx g @aws/nx-plugin:ts#trpc-api#procedure
bunx nx g @aws/nx-plugin:ts#trpc-api#procedure
Tambié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-run
yarn nx g @aws/nx-plugin:ts#trpc-api#procedure --dry-run
npx nx g @aws/nx-plugin:ts#trpc-api#procedure --dry-run
bunx nx g @aws/nx-plugin:ts#trpc-api#procedure --dry-run
Si 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.json
ysrc/router.ts
para 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.