Contribuisci a un Generatore
Creiamo un nuovo generatore per contribuire a @aws/nx-plugin
. Il nostro obiettivo sarà generare una nuova procedura per un’API tRPC.
Clona il Plugin
Innanzitutto, cloniamo il plugin:
git clone git@github.com:awslabs/nx-plugin-for-aws.git
Quindi, installiamo e compiliamo:
cd nx-plugin-for-awspnpm ipnpm nx run-many --target build --all
Crea un Generatore Vuoto
Creiamo il nuovo generatore in packages/nx-plugin/src/trpc/procedure
.
Forniamo un generatore per creare nuovi generatori, così potrai scaffoldare rapidamente il tuo nuovo generatore! Puoi eseguire questo generatore come segue:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#nx-generator
- Compila i parametri richiesti
- pluginProject: @aws/nx-plugin
- name: ts#trpc-api#procedure
- directory: trpc/procedure
- description: Aggiunge una procedura a un'API tRPC
- Clicca su
Generate
pnpm nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Aggiunge una procedura a un'API tRPC
yarn nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Aggiunge una procedura a un'API tRPC
npx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Aggiunge una procedura a un'API tRPC
bunx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Aggiunge una procedura a un'API tRPC
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Aggiunge una procedura a un'API tRPC --dry-run
yarn nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Aggiunge una procedura a un'API tRPC --dry-run
npx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Aggiunge una procedura a un'API tRPC --dry-run
bunx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Aggiunge una procedura a un'API tRPC --dry-run
Noterai che sono stati generati i seguenti file:
Directorypackages/nx-plugin/src/trpc/procedure
- schema.json Definisce gli input per il generatore
- schema.d.ts Un’interfaccia TypeScript corrispondente allo schema
- generator.ts Funzione eseguita da Nx come generatore
- generator.spec.ts Test per il generatore
Directorydocs/src/content/docs/guides/
- trpc-procedure.mdx Documentazione per il generatore
- packages/nx-plugin/generators.json Aggiornato per includere il generatore
Aggiorniamo lo schema per aggiungere le proprietà necessarie al generatore:
{ "$schema": "https://json-schema.org/schema", "$id": "tRPCProcedure", "title": "Aggiunge una procedura a un'API tRPC", "type": "object", "properties": { "project": { "type": "string", "description": "Progetto API tRPC", "x-prompt": "Seleziona il progetto API tRPC a cui aggiungere la procedura", "x-dropdown": "projects", "x-priority": "important" }, "procedure": { "description": "Nome della nuova procedura", "type": "string", "x-prompt": "Come vuoi chiamare la nuova procedura?", "x-priority": "important", }, "type": { "description": "Tipo di procedura da generare", "type": "string", "x-prompt": "Che tipo di procedura vuoi generare?", "x-priority": "important", "default": "query", "enum": ["query", "mutation"] } }, "required": ["project", "procedure"]}
export interface TrpcProcedureSchema { project: string; procedure: string; type: 'query' | 'mutation';}
Noterai che il generatore è già stato collegato in packages/nx-plugin/generators.json
:
... "generators": { ... "ts#trpc-api#procedure": { "factory": "./src/trpc/procedure/generator", "schema": "./src/trpc/procedure/schema.json", "description": "Aggiunge una procedura a un'API tRPC" } },...
Implementa il Generatore
Per aggiungere una procedura a un’API tRPC, dobbiamo fare due cose:
- Creare un file TypeScript per la nuova procedura
- Aggiungere la procedura al router
Crea la nuova Procedura
Per creare il file TypeScript della nuova procedura, utilizzeremo un’utility chiamata generateFiles
. Con questa, possiamo definire un template EJS che verrà renderizzato nel nostro generatore con variabili basate sulle opzioni selezionate dall’utente.
Per prima cosa, definiamo il template in 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: definisci l'input })) .output(z.object({ // TODO: definisci l'output })) .<%- procedureType %>(async ({ input, ctx }) => { // TODO: implementa! return {}; });
Nel template, abbiamo referenziato tre variabili:
procedureNameCamelCase
procedureNameKebabCase
procedureType
Dobbiamo assicurarci di passare queste variabili a generateFiles
, insieme alla directory in cui generare i file, ovvero la posizione dei file sorgente (cioè sourceRoot
) per il progetto tRPC selezionato dall’utente come input del generatore, che possiamo estrarre dalla configurazione del progetto.
Aggiorniamo il generatore per fare ciò:
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;
Aggiungi la Procedura al Router
Successivamente, vogliamo che il generatore colleghi la nuova procedura al router. Questo significa leggere e modificare il codice sorgente dell’utente!
Utilizziamo la manipolazione dell’AST di TypeScript per aggiornare le parti rilevanti del file sorgente. Alcuni helper come replace
e destructuredImport
semplificano questa operazione.
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;
Ora che abbiamo implementato il generatore, compiliamolo per assicurarci che sia disponibile per testarlo nel nostro progetto dungeon adventure.
pnpm nx run @aws/nx-plugin:compile
Test del Generatore
Per testare il generatore, collegheremo il nostro Nx Plugin for AWS locale a una codebase esistente.
Crea un Progetto di Test con un’API tRPC
In una directory separata, crea un nuovo workspace di test:
npx create-nx-workspace@~20.6.3 trpc-generator-test --pm=pnpm --preset=ts --ci=skip --formatter=prettier
npx create-nx-workspace@~20.6.3 trpc-generator-test --pm=yarn --preset=ts --ci=skip --formatter=prettier
npx create-nx-workspace@~20.6.3 trpc-generator-test --pm=npm --preset=ts --ci=skip --formatter=prettier
npx create-nx-workspace@~20.6.3 trpc-generator-test --pm=bun --preset=ts --ci=skip --formatter=prettier
Quindi, generiamo un’API tRPC a cui aggiungere la procedura:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#trpc-api
- Compila i parametri richiesti
- apiName: test-api
- Clicca su
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
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
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
Collega il nostro Nx Plugin for AWS locale
Nella tua codebase, colleghiamo il nostro @aws/nx-plugin
locale:
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
Esegui il nuovo Generatore
Proviamo il nuovo generatore:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)
nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - ts#trpc-api#procedure
- Compila i parametri richiesti
- Clicca su
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
Puoi anche eseguire una prova per vedere quali file verrebbero modificati
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
Se tutto è andato a buon fine, dovremmo aver generato una nuova procedura e aggiunto la procedura al nostro router in router.ts
.
Esercizi
Se sei arrivato fin qui e hai ancora tempo per sperimentare con i generatori Nx, ecco alcuni suggerimenti per aggiungere funzionalità al generatore di procedure:
1. Operazioni Annidate
Prova ad aggiornare il generatore per supportare router annidati:
- Accettando la notazione puntata per l’input
procedure
(es.games.query
) - Generando una procedura con un nome basato sulla notazione puntata invertita (es.
queryGames
) - Aggiungendo il router annidato appropriato (o aggiornandolo se esiste già!)
2. Validazione
Il nostro generatore dovrebbe prevenire potenziali problemi, come un utente che seleziona un project
che non è un’API tRPC. Dai un’occhiata al generatore api-connection
per un esempio.
3. Test Unitari
Scrivi alcuni test unitari per il generatore. Sono abbastanza semplici da implementare e seguono generalmente questo flusso:
- Crea un workspace tree vuoto usando
createTreeUsingTsSolutionSetup()
- Aggiungi eventuali file che dovrebbero già esistere nel tree (es.
project.json
esrc/router.ts
per un backend tRPC) - Esegui il generatore sotto test
- Verifica che le modifiche attese siano state apportate al tree
4. Test End-to-End
Attualmente, abbiamo un singolo “smoke test” che esegue tutti i generatori e verifica che la build abbia successo. Questo dovrebbe essere aggiornato per includere il nuovo generatore.