跳转到内容

贡献生成器

让我们为@aws/nx-plugin创建一个新的生成器,目标是为tRPC API生成新的过程(procedure)。

首先克隆插件仓库:

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: Adds a procedure to a tRPC API
  6. 点击 Generate

以下文件已自动生成:

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

更新模式文件添加所需属性:

{
"$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项目",
"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": "Adds a procedure to a tRPC API"
}
},
...

要实现tRPC API过程添加,需要完成两个步骤:

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

使用generateFiles工具创建TypeScript文件。我们可以定义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/v4';
export const <%- procedureNameCamelCase %> = publicProcedure
.input(z.object({
// TODO: 定义输入
}))
.output(z.object({
// TODO: 定义输出
}))
.<%- procedureType %>(async ({ input, ctx }) => {
// TODO: 实现逻辑
return {};
});

模板中引用了三个变量:

  • procedureNameCamelCase
  • procedureNameKebabCase
  • procedureType

我们需要将这些变量传递给generateFiles,并确定生成文件的目标目录(即用户选择的tRPC项目的源码根目录):

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;

接下来需要更新路由文件。我们使用TypeScript 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@~21.0.3 trpc-generator-test --pm=pnpm --preset=@aws/nx-plugin --ci=skip

生成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

在测试项目中链接本地@aws/nx-plugin

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

    成功运行后,应生成新过程并更新router.ts文件。

    支持通过点符号(如games.query)创建嵌套路由,生成反向命名的过程(如queryGames

    添加对非tRPC项目的防御性检查,参考api-connection生成器的实现

    编写测试用例:

    1. 使用createTreeUsingTsSolutionSetup()创建空工作区
    2. 添加预置文件(如project.jsonsrc/router.ts
    3. 运行生成器
    4. 验证文件变更

    更新现有冒烟测试,包含新生成器的验证