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
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
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
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
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
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
Para testar o gerador, vincularemos nosso Nx Plugin for AWS local a uma codebase existente.
Crie um Projeto de Teste com uma API tRPC
Em um diretório separado, crie um novo workspace de teste:
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
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
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
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 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
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
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
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
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.