Salta ai contenuti

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:

Terminal window
git clone git@github.com:awslabs/nx-plugin-for-aws.git

Quindi, installiamo e compiliamo:

Terminal window
cd nx-plugin-for-aws
pnpm i
pnpm 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:

  1. Installa il Nx Console VSCode Plugin se non l'hai già fatto
  2. Apri la console Nx in VSCode
  3. Clicca su Generate (UI) nella sezione "Common Nx Commands"
  4. Cerca @aws/nx-plugin - ts#nx-generator
  5. Compila i parametri richiesti
    • pluginProject: @aws/nx-plugin
    • name: ts#trpc-api#procedure
    • directory: trpc/procedure
    • description: Aggiunge una procedura a un'API tRPC
  6. Clicca su Generate

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"]
}

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:

  1. Creare un file TypeScript per la nuova procedura
  2. 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:

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ò:

procedure/generator.ts
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.

procedure/generator.ts
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.

Terminal window
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:

Terminal window
npx create-nx-workspace@~20.6.3 trpc-generator-test --pm=pnpm --preset=ts --ci=skip --formatter=prettier

Quindi, generiamo un’API tRPC a cui aggiungere la procedura:

  1. Installa il Nx Console VSCode Plugin se non l'hai già fatto
  2. Apri la console Nx in VSCode
  3. Clicca su Generate (UI) nella sezione "Common Nx Commands"
  4. Cerca @aws/nx-plugin - ts#trpc-api
  5. Compila i parametri richiesti
    • apiName: test-api
  6. Clicca su Generate

Collega il nostro Nx Plugin for AWS locale

Nella tua codebase, colleghiamo il nostro @aws/nx-plugin locale:

Terminal window
cd path/to/trpc-generator-test
pnpm link path/to/nx-plugin-for-aws/dist/packages/nx-plugin

Esegui il nuovo Generatore

Proviamo il nuovo generatore:

  1. Installa il Nx Console VSCode Plugin se non l'hai già fatto
  2. Apri la console Nx in VSCode
  3. Clicca su Generate (UI) nella sezione "Common Nx Commands"
  4. Cerca @aws/nx-plugin - ts#trpc-api#procedure
  5. Compila i parametri richiesti
    • Clicca su Generate

    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:

    1. Crea un workspace tree vuoto usando createTreeUsingTsSolutionSetup()
    2. Aggiungi eventuali file che dovrebbero già esistere nel tree (es. project.json e src/router.ts per un backend tRPC)
    3. Esegui il generatore sotto test
    4. 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.