Contribua com um Gerador
Vamos criar um novo gerador para contribuir com o @aws/nx-plugin
. Nosso objetivo será gerar um novo procedimento para uma API tRPC.
Verifique o Plugin
Seção intitulada “Verifique o Plugin”Primeiro, vamos clonar o plugin:
git clone git@github.com:awslabs/nx-plugin-for-aws.git
Em seguida, instale e construa:
cd nx-plugin-for-awspnpm ipnpm nx run-many --target build --all
Crie um Gerador Vazio
Seção intitulada “Crie um Gerador Vazio”Vamos criar o novo gerador em packages/nx-plugin/src/trpc/procedure
.
Fornecemos um gerador para criar novos geradores, permitindo que você scaffold seu novo gerador rapidamente! Você pode executar este gerador da seguinte forma:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)
na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#nx-generator
- Preencha os parâmetros obrigatórios
- pluginProject: @aws/nx-plugin
- name: ts#trpc-api#procedure
- directory: trpc/procedure
- description: Adiciona um procedimento a uma API tRPC
- Clique em
Generate
pnpm nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adiciona um procedimento a uma API tRPC
yarn nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adiciona um procedimento a uma API tRPC
npx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adiciona um procedimento a uma API tRPC
bunx nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adiciona um procedimento a uma API tRPC
Você também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adiciona um procedimento a uma 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=Adiciona um procedimento a uma 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=Adiciona um procedimento a uma 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=Adiciona um procedimento a uma API tRPC --dry-run
Você notará que os seguintes arquivos foram gerados para você:
Directorypackages/nx-plugin/src/trpc/procedure
- schema.json Define a entrada para o gerador
- schema.d.ts Uma interface TypeScript que corresponde ao schema
- generator.ts Função que o Nx executa como gerador
- generator.spec.ts Testes para o gerador
Directorydocs/src/content/docs/guides/
- trpc-procedure.mdx Documentação para o gerador
- packages/nx-plugin/generators.json Atualizado para incluir o gerador
Vamos atualizar o schema para adicionar as propriedades necessárias para o gerador:
{ "$schema": "https://json-schema.org/schema", "$id": "tRPCProcedure", "title": "Adiciona um procedimento a uma API tRPC", "type": "object", "properties": { "project": { "type": "string", "description": "Projeto da API tRPC", "x-prompt": "Selecione o projeto da API tRPC para adicionar o procedimento", "x-dropdown": "projects", "x-priority": "important" }, "procedure": { "description": "Nome do novo procedimento", "type": "string", "x-prompt": "Como você gostaria de chamar seu novo procedimento?", "x-priority": "important", }, "type": { "description": "Tipo de procedimento a ser gerado", "type": "string", "x-prompt": "Que tipo de procedimento você gostaria de gerar?", "x-priority": "important", "default": "query", "enum": ["query", "mutation"] } }, "required": ["project", "procedure"]}
export interface TrpcProcedureSchema { project: string; procedure: string; type: 'query' | 'mutation';}
Você notará que o gerador já foi conectado em packages/nx-plugin/generators.json
:
... "generators": { ... "ts#trpc-api#procedure": { "factory": "./src/trpc/procedure/generator", "schema": "./src/trpc/procedure/schema.json", "description": "Adiciona um procedimento a uma API tRPC" } },...
Implemente o Gerador
Seção intitulada “Implemente o Gerador”Para adicionar um procedimento a uma API tRPC, precisamos fazer duas coisas:
- Criar um arquivo TypeScript para o novo procedimento
- Adicionar o procedimento ao router
Crie o Novo Procedimento
Seção intitulada “Crie o Novo Procedimento”Para criar o arquivo TypeScript do novo procedimento, usaremos um utilitário chamado generateFiles
. Com ele, podemos definir um template EJS que será renderizado em nosso gerador com variáveis baseadas nas opções selecionadas pelo usuário.
Primeiro, definiremos o template em 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: definir input })) .output(z.object({ // TODO: definir output })) .<%- procedureType %>(async ({ input, ctx }) => { // TODO: implementar! return {}; });
No template, referenciamos três variáveis:
procedureNameCamelCase
procedureNameKebabCase
procedureType
Portanto, precisamos garantir que passamos essas variáveis para o generateFiles
, bem como o diretório para gerar os arquivos, ou seja, a localização dos arquivos fonte (i.e. sourceRoot
) do projeto tRPC selecionado pelo usuário como entrada do gerador, que podemos extrair da configuração do projeto.
Atualizaremos o gerador para fazer isso:
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;
Adicione o Procedimento ao Router
Seção intitulada “Adicione o Procedimento ao Router”Em seguida, queremos que o gerador conecte o novo procedimento ao router. Isso significa ler e atualizar o código fonte do usuário!
Usamos manipulação de AST do TypeScript para atualizar as partes relevantes do arquivo fonte. Existem alguns helpers chamados replace
e destructuredImport
para facilitar isso.
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;
Agora que implementamos o gerador, vamos compilá-lo para garantir que esteja disponível para testarmos em nosso projeto dungeon adventure.
pnpm nx run @aws/nx-plugin:compile
Testando o Gerador
Seção intitulada “Testando o Gerador”Para testar o gerador, vincularemos nosso Nx Plugin for AWS local a uma codebase existente.
Crie um Projeto de Teste com uma API tRPC
Seção intitulada “Crie um Projeto de Teste com uma API tRPC”Em um diretório separado, crie um novo workspace de teste:
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
Em seguida, vamos gerar uma API tRPC para adicionar o procedimento:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)
na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#trpc-api
- Preencha os parâmetros obrigatórios
- apiName: test-api
- Clique em
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
Você também pode realizar uma execução simulada para ver quais arquivos seriam alterados
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
Vincule nosso Nx Plugin for AWS local
Seção intitulada “Vincule nosso Nx Plugin for AWS local”Em sua codebase, vamos vincular nosso @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
Execute o Novo Gerador
Seção intitulada “Execute o Novo Gerador”Vamos testar o novo gerador:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)
na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - ts#trpc-api#procedure
- Preencha os parâmetros obrigatórios
- Clique em
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
Você também pode realizar uma execução simulada para ver quais arquivos seriam alterados
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 bem-sucedido, devemos ter gerado um novo procedimento e adicionado o procedimento ao nosso router em router.ts
.
Exercícios
Seção intitulada “Exercícios”Se você chegou até aqui e ainda tem tempo para experimentar com geradores Nx, aqui estão algumas sugestões de funcionalidades para adicionar ao gerador de procedimentos:
1. Operações Aninhadas
Seção intitulada “1. Operações Aninhadas”Tente atualizar o gerador para suportar routers aninhados:
- Aceitando notação de ponto para a entrada
procedure
(ex:games.query
) - Gerando um procedimento com nome baseado na notação de ponto invertida (ex:
queryGames
) - Adicionando o router aninhado apropriado (ou atualizando-o se já existir!)
2. Validação
Seção intitulada “2. Validação”Nosso gerador deve prevenir problemas potenciais, como um usuário selecionar um project
que não é uma API tRPC. Consulte o gerador api-connection
para um exemplo disso.
3. Testes Unitários
Seção intitulada “3. Testes Unitários”Escreva alguns testes unitários para o gerador. Eles são relativamente simples de implementar e seguem o fluxo geral:
- Criar uma workspace tree vazia usando
createTreeUsingTsSolutionSetup()
- Adicionar quaisquer arquivos que já deveriam existir na tree (ex:
project.json
esrc/router.ts
para um backend tRPC) - Executar o gerador em teste
- Validar que as alterações esperadas foram feitas na tree
4. Testes End to End
Seção intitulada “4. Testes End to End”Atualmente, temos um único “smoke test” que executa todos os geradores e verifica se a build é bem-sucedida. Isso deve ser atualizado para incluir o novo gerador.