Bỏ qua để đến nội dung

Đóng góp một Generator

Hãy tạo một generator mới để đóng góp cho @aws/nx-plugin. Mục tiêu của chúng ta sẽ là tạo một procedure mới cho một tRPC API.

Đầu tiên, hãy clone plugin:

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

Tiếp theo, cài đặt và build:

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

Hãy tạo generator mới trong packages/nx-plugin/src/trpc/procedure.

Chúng tôi cung cấp một generator để tạo các generator mới nên bạn có thể nhanh chóng scaffold generator mới của mình! Bạn có thể chạy generator này như sau:

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#nx-generator
  5. Fill in the required parameters
    • pluginProject: @aws/nx-plugin
    • name: ts#trpc-api#procedure
    • directory: trpc/procedure
    • description: Adds a procedure to a tRPC API
  6. Click Generate

Bạn sẽ nhận thấy các file sau đã được tạo cho bạn:

  • Thư mụcpackages/nx-plugin/src/trpc/procedure
    • schema.json Định nghĩa đầu vào cho generator
    • schema.d.ts Một interface typescript khớp với schema
    • generator.ts Function mà Nx chạy như generator
    • generator.spec.ts Tests cho generator
  • Thư mụcdocs/src/content/docs/guides/
    • trpc-procedure.mdx Tài liệu cho generator
  • packages/nx-plugin/generators.json Được cập nhật để bao gồm generator

Hãy cập nhật schema để thêm các thuộc tính chúng ta cần cho generator:

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

Bạn sẽ nhận thấy generator đã được kết nối trong 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"
}
},
...

Để thêm một procedure vào tRPC API, chúng ta cần làm hai việc:

  1. Tạo một file TypeScript cho procedure mới
  2. Thêm procedure vào router

Để tạo file TypeScript cho procedure mới, chúng ta sẽ sử dụng một tiện ích gọi là generateFiles. Sử dụng công cụ này, chúng ta có thể định nghĩa một template EJS mà chúng ta có thể render trong generator với các biến dựa trên các options được người dùng chọn.

Đầu tiên, chúng ta sẽ định nghĩa template trong 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 {};
});

Trong template, chúng ta đã tham chiếu ba biến:

  • procedureNameCamelCase
  • procedureNameKebabCase
  • procedureType

Vì vậy, chúng ta cần đảm bảo truyền những biến đó cho generateFiles, cũng như thư mục để tạo file vào, cụ thể là vị trí của các file nguồn (tức là sourceRoot) cho dự án tRPC mà người dùng đã chọn làm đầu vào cho generator, mà chúng ta có thể trích xuất từ cấu hình dự án.

Hãy cập nhật generator để làm điều đó:

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;

Tiếp theo, chúng ta muốn generator kết nối procedure mới vào router. Điều này có nghĩa là đọc và cập nhật mã nguồn của người dùng!

Chúng ta sử dụng thao tác TypeScript AST để cập nhật các phần liên quan của file nguồn TypeScript. Có một số helper gọi là replacedestructuredImport để làm cho việc này dễ dàng hơn một chút.

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;

Bây giờ chúng ta đã triển khai generator, hãy compile nó để đảm bảo nó có sẵn để chúng ta kiểm tra trong dự án dungeon adventure.

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

Để kiểm thử generator, chúng ta sẽ liên kết Nx Plugin for AWS local của mình với một codebase hiện có.

Trong một thư mục riêng, tạo một test workspace mới:

Terminal window
npx create-nx-workspace@22.0.2 trpc-generator-test --pm=pnpm --preset=@aws/nx-plugin --ci=skip --aiAgents

Tiếp theo, hãy tạo một tRPC API để thêm procedure vào:

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#trpc-api
  5. Fill in the required parameters
    • apiName: test-api
  6. Click Generate

Liên kết Nx Plugin for AWS local của chúng ta

Phần tiêu đề “Liên kết Nx Plugin for AWS local của chúng ta”

Trong codebase của bạn, hãy liên kết @aws/nx-plugin local của chúng ta:

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

Hãy thử generator mới:

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#trpc-api#procedure
  5. Fill in the required parameters
    • Click Generate

    Nếu thành công, chúng ta đã tạo một procedure mới và thêm procedure vào router của chúng ta trong router.ts.

    Nếu bạn đã đến đây và vẫn còn thời gian để thử nghiệm với Nx generators, đây là một số gợi ý về các tính năng để thêm vào procedure generator:

    Thử cập nhật generator để hỗ trợ nested routers bằng cách:

    • Chấp nhận ký hiệu dấu chấm cho đầu vào procedure (ví dụ: games.query)
    • Tạo một procedure với tên dựa trên ký hiệu dấu chấm đảo ngược (ví dụ: queryGames)
    • Thêm nested router thích hợp (hoặc cập nhật nó nếu nó đã tồn tại!)

    Generator của chúng ta nên bảo vệ chống lại các vấn đề tiềm ẩn, chẳng hạn như người dùng chọn một project không phải là tRPC API. Hãy xem generator api-connection để xem ví dụ về điều này.

    Viết một số unit tests cho generator. Chúng khá đơn giản để triển khai, và hầu hết tuân theo luồng chung:

    1. Tạo một empty workspace tree bằng cách sử dụng createTreeUsingTsSolutionSetup()
    2. Thêm bất kỳ file nào đã tồn tại trong tree (ví dụ: project.jsonsrc/router.ts cho tRPC backend)
    3. Chạy generator đang được kiểm thử
    4. Xác thực các thay đổi mong đợi được thực hiện đối với tree

    Hiện tại, chúng ta có một “smoke test” duy nhất chạy tất cả các generators và đảm bảo rằng build thành công. Điều này nên được cập nhật để bao gồm generator mới.