Aller au contenu

Contribuer à un Générateur

Créons un nouveau générateur pour contribuer à @aws/nx-plugin. Notre objectif sera de générer une nouvelle procédure pour une API tRPC.

Explorer le Plugin

Commencez par cloner le plugin :

Fenêtre de terminal
git clone git@github.com:awslabs/nx-plugin-for-aws.git

Ensuite, installez et compilez :

Fenêtre de terminal
cd nx-plugin-for-aws
pnpm i
pnpm nx run-many --target build --all

Créer un Générateur Vide

Créons le nouveau générateur dans packages/nx-plugin/src/trpc/procedure.

Nous fournissons un générateur pour créer de nouveaux générateurs, ce qui vous permet d’échafauder rapidement votre nouveau générateur ! Vous pouvez exécuter ce générateur comme suit :

  1. Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
  2. Ouvrez la console Nx dans VSCode
  3. Cliquez sur Generate (UI) dans la section "Common Nx Commands"
  4. Recherchez @aws/nx-plugin - ts#nx-generator
  5. Remplissez les paramètres requis
    • pluginProject: @aws/nx-plugin
    • name: ts#trpc-api#procedure
    • directory: trpc/procedure
    • description: Adds a procedure to a tRPC API
  6. Cliquez sur Generate

Vous remarquerez que les fichiers suivants ont été générés pour vous :

  • Répertoirepackages/nx-plugin/src/trpc/procedure
    • schema.json Définit les entrées du générateur
    • schema.d.ts Une interface TypeScript correspondant au schéma
    • generator.ts Fonction exécutée par Nx comme générateur
    • generator.spec.ts Tests pour le générateur
  • Répertoiredocs/src/content/docs/guides/
    • trpc-procedure.mdx Documentation du générateur
  • packages/nx-plugin/generators.json Mis à jour pour inclure le générateur

Mettons à jour le schéma pour ajouter les propriétés nécessaires au générateur :

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

Vous remarquerez que le générateur a déjà été configuré dans 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"
}
},
...

Implémenter le Générateur

Pour ajouter une procédure à une API tRPC, nous devons effectuer deux actions :

  1. Créer un fichier TypeScript pour la nouvelle procédure
  2. Ajouter la procédure au routeur

Créer la Nouvelle Procédure

Pour créer le fichier TypeScript de la nouvelle procédure, nous utiliserons un utilitaire appelé generateFiles. Avec celui-ci, nous pouvons définir un template EJS que nous rendrons dans notre générateur avec des variables basées sur les options sélectionnées par l’utilisateur.

Définissons d’abord le template dans 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: define input
}))
.output(z.object({
// TODO: define output
}))
.<%- procedureType %>(async ({ input, ctx }) => {
// TODO: implement!
return {};
});

Dans le template, nous avons référencé trois variables :

  • procedureNameCamelCase
  • procedureNameKebabCase
  • procedureType

Nous devons donc nous assurer de les passer à generateFiles, ainsi que le répertoire de génération des fichiers, à savoir l’emplacement des fichiers sources (c’est-à-dire sourceRoot) pour le projet tRPC sélectionné par l’utilisateur, que nous pouvons extraire de la configuration du projet.

Mettons à jour le générateur pour cela :

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;

Ajouter la Procédure au Routeur

Ensuite, nous voulons que le générateur connecte la nouvelle procédure au routeur. Cela implique de lire et de modifier le code source de l’utilisateur !

Nous utilisons la manipulation d’AST TypeScript pour modifier les parties pertinentes du fichier source. Des helpers comme replace et destructuredImport facilitent cette tâche.

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;

Maintenant que nous avons implémenté le générateur, compilons-le pour nous assurer qu’il est disponible pour le tester dans notre projet d’aventure.

Fenêtre de terminal
pnpm nx run @aws/nx-plugin:compile

Tester le Générateur

Pour tester le générateur, nous allons lier notre Nx Plugin for AWS local à une base de code existante.

Créer un Projet de Test avec une API tRPC

Dans un répertoire séparé, créez un nouveau workspace de test :

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

Ensuite, générons une API tRPC à laquelle ajouter la procédure :

  1. Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
  2. Ouvrez la console Nx dans VSCode
  3. Cliquez sur Generate (UI) dans la section "Common Nx Commands"
  4. Recherchez @aws/nx-plugin - ts#trpc-api
  5. Remplissez les paramètres requis
    • apiName: test-api
  6. Cliquez sur Generate

Lier notre Nx Plugin for AWS local

Dans votre base de code, lions notre @aws/nx-plugin local :

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

Exécuter le Nouveau Générateur

Essayons le nouveau générateur :

  1. Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
  2. Ouvrez la console Nx dans VSCode
  3. Cliquez sur Generate (UI) dans la section "Common Nx Commands"
  4. Recherchez @aws/nx-plugin - ts#trpc-api#procedure
  5. Remplissez les paramètres requis
    • Cliquez sur Generate

    En cas de succès, nous devrions avoir généré une nouvelle procédure et l’avoir ajoutée à notre routeur dans router.ts.

    Exercices

    Si vous êtes arrivé jusqu’ici et avez encore du temps pour expérimenter avec les générateurs Nx, voici quelques suggestions de fonctionnalités à ajouter au générateur de procédures :

    1. Opérations Imbriquées

    Essayez de modifier le générateur pour supporter les routeurs imbriqués en :

    • Acceptant une notation par points pour l’entrée procedure (ex: games.query)
    • Générant une procédure avec un nom basé sur la notation inversée (ex: queryGames)
    • Ajoutant le routeur imbriqué approprié (ou le mettant à jour s’il existe déjà)

    2. Validation

    Notre générateur devrait se prémunir contre des problèmes potentiels, comme un utilisateur sélectionnant un project qui n’est pas une API tRPC. Consultez le générateur api-connection pour un exemple.

    3. Tests Unitaires

    Écrivez des tests unitaires pour le générateur. Ils sont relativement simples à implémenter et suivent généralement le flux suivant :

    1. Créer un arbre de workspace vide avec createTreeUsingTsSolutionSetup()
    2. Ajouter les fichiers qui devraient déjà exister dans l’arbre (ex: project.json et src/router.ts pour un backend tRPC)
    3. Exécuter le générateur sous test
    4. Valider que les modifications attendues sont apportées à l’arbre

    4. Tests End-to-End

    Actuellement, nous avons un simple “smoke test” qui exécute tous les générateurs et vérifie que la compilation réussit. Celui-ci devrait être mis à jour pour inclure le nouveau générateur.