跳转到内容

贡献生成器

让我们为@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目录下创建新生成器。

我们提供了一个生成器脚手架工具,可快速创建新生成器:

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - ts#nx-generator
  5. 填写必需参数
    • pluginProject: @aws/nx-plugin
    • name: ts#trpc-api#procedure
    • directory: trpc/procedure
    • description: 为tRPC API添加操作流程
  6. 点击 Generate

生成的文件结构如下:

  • 文件夹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.json添加必要属性:

{
"$schema": "https://json-schema.org/schema",
"$id": "tRPCProcedure",
"title": "为tRPC API添加操作流程",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "目标tRPC API项目",
"x-prompt": "选择要添加操作流程的tRPC API项目",
"x-dropdown": "projects",
"x-priority": "important"
},
"procedure": {
"description": "新操作流程名称",
"type": "string",
"x-prompt": "请输入新操作流程名称",
"x-priority": "important",
},
"type": {
"description": "要生成的操作类型",
"type": "string",
"x-prompt": "选择要生成的操作类型",
"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": "为tRPC API添加操作流程"
}
},
...

实现生成器

实现步骤如下:

  1. 创建新操作流程的TypeScript文件
  2. 将操作流程添加到路由

创建操作流程文件

使用generateFiles工具生成模板文件。模板位于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: 定义输入参数
}))
.output(z.object({
// TODO: 定义输出格式
}))
.<%- procedureType %>(async ({ input, ctx }) => {
// TODO: 实现业务逻辑
return {};
});

更新生成器逻辑:

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;

添加操作流程到路由

使用AST操作更新路由文件:

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;

编译生成器:

Terminal window
pnpm nx run @aws/nx-plugin:compile

测试生成器

创建测试项目

新建测试工作区:

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

生成测试用tRPC API:

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - ts#trpc-api
  5. 填写必需参数
    • apiName: test-api
  6. 点击 Generate

链接本地插件

在项目中链接本地插件:

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

运行生成器

执行新生成器:

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - ts#trpc-api#procedure
  5. 填写必需参数
    • 点击 Generate

    扩展练习

    1. 嵌套操作:支持通过点分隔符(如games.query)创建嵌套路由
    2. 输入校验:增加对非tRPC项目的选择防护
    3. 单元测试:编写生成器单元测试用例
    4. 端到端测试:更新现有测试用例包含新生成器