跳转到内容

贡献生成器

让我们创建一个新的生成器来贡献给 @aws/nx-plugin。我们的目标是为 tRPC API 生成一个新的过程。

首先,让我们克隆插件:

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

接下来,安装并构建:

Terminal window
cd nx-plugin-for-aws
pnpm i
pnpm nx run-many --target build --all

让我们在 packages/nx-plugin/src/trpc/procedure 中创建新的生成器。

我们提供了一个用于创建新生成器的生成器,这样您就可以快速搭建新生成器的脚手架!您可以按如下方式运行此生成器:

Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-generator --project=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-generator --project=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API --dry-run

您会注意到已为您生成了以下文件:

  • 文件夹packages/nx-plugin/src/trpc/procedure
    • schema.json 定义生成器的输入
    • schema.d.ts 与 schema 匹配的 TypeScript 接口
    • generator.ts Nx 作为生成器运行的函数
    • generator.spec.ts 生成器的测试
  • 文件夹docs/src/content/docs/guides/
    • trpc-procedure.mdx 生成器的文档
  • packages/nx-plugin/generators.json 已更新以包含生成器

让我们更新 schema 以添加生成器所需的属性:

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

您会注意到生成器已经在 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"
}
},
...

要向 tRPC API 添加过程,我们需要做两件事:

  1. 为新过程创建一个 TypeScript 文件
  2. 将过程添加到路由器

要为新过程创建 TypeScript 文件,我们将使用一个名为 generateFiles 的实用程序。使用它,我们可以定义一个 EJS 模板,我们可以在生成器中根据用户选择的选项使用变量来渲染它。

首先,我们将在 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 {};
});

在模板中,我们引用了三个变量:

  • procedureNameCamelCase
  • procedureNameKebabCase
  • procedureType

因此,我们需要确保将这些变量传递给 generateFiles,以及生成文件的目录,即用户选择作为生成器输入的 tRPC 项目的源文件位置(即 sourceRoot),我们可以从项目配置中提取它。

让我们更新生成器来做到这一点:

procedure/generator.ts
import {
generateFiles,
joinPathFragments,
readProjectConfiguration,
type Tree,
} from '@nx/devkit';
import type { TrpcProcedureSchema } from './schema.js';
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(import.meta.dirname, 'files'),
projectConfig.sourceRoot,
{
procedureNameCamelCase,
procedureNameKebabCase,
procedureType: options.type,
},
);
await formatFilesInSubtree(tree);
};
export default trpcProcedureGenerator;

接下来,我们希望生成器将新过程连接到路由器。这意味着读取和更新用户的源代码!

我们使用 GritQL 以声明方式搜索和转换源代码。addDestructuredImport 辅助函数添加命名导入,applyGritQL 应用 GritQL 模式将过程添加到路由器的对象字面量。

procedure/generator.ts
import {
generateFiles,
joinPathFragments,
readProjectConfiguration,
type Tree,
} from '@nx/devkit';
import type { TrpcProcedureSchema } from './schema.js';
import { formatFilesInSubtree } from '../../utils/format';
import camelCase from 'lodash.camelcase';
import kebabCase from 'lodash.kebabcase';
import { addDestructuredImport, applyGritQL } from '../../utils/ast';
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(import.meta.dirname, 'files'),
projectConfig.sourceRoot,
{
procedureNameCamelCase,
procedureNameKebabCase,
procedureType: options.type,
},
);
const routerPath = joinPathFragments(projectConfig.sourceRoot, 'router.ts');
await addDestructuredImport(
tree,
routerPath,
[procedureNameCamelCase],
`./procedures/${procedureNameKebabCase}.js`,
);
await applyGritQL(
tree,
routerPath,
`\`router({ $props })\` => \`router({ $props, ${procedureNameCamelCase} })\` where { $props <: not contains \`${procedureNameCamelCase}\` }`,
);
await formatFilesInSubtree(tree);
};
export default trpcProcedureGenerator;

现在我们已经实现了生成器,让我们编译它以确保它可供我们在地牢冒险项目中测试。

Terminal window
pnpm nx compile @aws/nx-plugin

要测试生成器,我们将把本地的 Nx Plugin for AWS 链接到现有代码库。

创建一个带有 tRPC API 的测试项目

Section titled “创建一个带有 tRPC API 的测试项目”

在单独的目录中,创建一个新的测试工作区:

Terminal window
pnpm create @aws/nx-workspace trpc-generator-test

接下来,让我们生成一个 tRPC API 来添加过程:

Terminal window
pnpm nx g @aws/nx-plugin:ts#api --name=test-api --framework=trpc --no-interactive
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:ts#api --name=test-api --framework=trpc --no-interactive --dry-run

在您的代码库中,让我们链接本地的 @aws/nx-plugin

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

让我们尝试新的生成器:

Terminal window
pnpm nx g @aws/nx-plugin:ts#trpc-api#procedure
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:ts#trpc-api#procedure --dry-run

如果成功,我们应该已经生成了一个新过程,并在 router.ts 中将过程添加到我们的路由器。

如果您已经走到这一步并且还有一些时间来尝试 Nx 生成器,这里有一些建议可以添加到过程生成器的功能:

尝试更新生成器以支持嵌套路由器,方法是:

  • 接受 procedure 输入的点表示法(例如 games.query
  • 根据反向点表示法生成具有名称的过程(例如 queryGames
  • 添加适当的嵌套路由器(或者如果已经存在则更新它!)

我们的生成器应该防范潜在问题,例如用户选择的 project 不是 tRPC API。查看 connection 生成器以获取此示例。

为生成器编写一些单元测试。这些相当简单易实现,大多数遵循一般流程:

  1. 使用 createTreeUsingTsSolutionSetup() 创建一个空的工作区树
  2. 在树中添加应该已经存在的任何文件(例如 tRPC 后端的 project.jsonsrc/router.ts
  3. 运行正在测试的生成器
  4. 验证对树进行了预期的更改

我们有一套”冒烟测试”,在新工作区中运行生成器并确保一切都能构建。至少,您的新生成器应该添加到两个生成器矩阵中,以便冒烟测试对其进行测试:

  • e2e/src/smoke-tests/generator-matrix.ts — 通过 CLI 运行每个生成器,一次一个调用,就像用户一样。
  • packages/nx-plugin/src/internal/test-matrix/generator.ts — 一个隐藏的生成器,它组合所有其他生成器以测试版本之间的迁移。

请注意,生成器矩阵仅运行生成器并构建工作区——它不会实例化任何基础设施。对于部署基础设施的生成器,请考虑扩展部署 e2e 测试(e2e/src/smoke-tests/cdk-deploy.spec.tsterraform-deploy.spec.ts)以实际部署您的资源(通过 cdk deploy / terraform apply),然后向 deploy-invocations.ts 添加一个断言,调用已部署的资源并验证其行为符合预期。

如果您的生成器提供本地开发服务器,请考虑将其添加到本地开发 e2e 测试(e2e/src/smoke-tests/local-dev.spec.ts)中,该测试启动 dev 目标并测试运行中的服务器。

本节包含一些在使用 Nx Plugin for AWS 时有帮助的通用指导。

构建新生成器或向现有生成器添加功能/修复的一个有用方法是_首先真实地构建它_。这样,您可以验证您的想法并快速迭代以实现所需的功能。在确定所需结果后,您可以更新生成器。

在实践中,此过程可能如下所示:

  1. 创建一个新工作区

    Terminal window
    pnpm create @aws/nx-workspace my-project
  2. 运行可能是新生成器/功能/修复的先决条件的任何生成器

  3. 提交您的更改(git commit

  4. 进行所需的更改并根据需要进行测试

  5. 使用更改的 git diff 来告知应该对 Nx Plugin for AWS 进行哪些更改

  6. 执行最后一次端到端测试(链接您的 @aws/nx-plugin)以确保您的生成器提供您需要的更改