Nx Generator 生成器
向 TypeScript 项目添加一个 Nx Generator,帮助您自动化重复性任务,例如脚手架组件或强制执行特定的项目结构。
生成一个 Generator
Section titled “生成一个 Generator”您可以通过两种方式生成一个 generator:
pnpm nx g @aws/nx-plugin:ts#nx-generatoryarn nx g @aws/nx-plugin:ts#nx-generatornpx nx g @aws/nx-plugin:ts#nx-generatorbunx nx g @aws/nx-plugin:ts#nx-generator- 安装 Nx Console VSCode Plugin 如果您尚未安装
- 在VSCode中打开Nx控制台
- 点击
Generate (UI)在"Common Nx Commands"部分 - 搜索
@aws/nx-plugin - ts#nx-generator - 填写必需参数
- 点击
Generate
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| project 必需 | string | - | 要添加生成器的 TypeScript 项目。我们推荐使用 ts#nx-plugin 生成器来创建此项目。 |
| name 必需 | string | - | 生成器名称 |
| description | string | - | 生成器的描述 |
| directory | string | - | 插件项目源文件夹中用于添加生成器的目录(默认:<name>) |
| preferInstallDependencies | boolean | true | 是否在生成器运行后优先安装依赖项。设置为 false 可在批量运行多个生成器时延迟安装(如果后续生成器需要计算 Nx 项目图,仍会运行安装);在最后统一安装一次。 |
Generator 输出
Section titled “Generator 输出”该 generator 将在给定的 project 中创建以下项目文件:
文件夹src/<name>/
- schema.json Schema for input to your generator
- schema.d.ts TypeScript types for your schema
- generator.ts Stub generator implementation
- generator.spec.ts Tests for your generator
- README.md Documentation for your generator
- generators.json Nx configuration to define your generators
- package.json Created or updated to add a “generators” entry
- tsconfig.json Updated to use CommonJS
项目修改
此 generator 将更新所选的 project 以使用 CommonJS,因为 Nx Generators 目前仅支持 CommonJS(参考此 GitHub issue 了解 ESM 支持)。
本地 Generators
Section titled “本地 Generators”在运行 ts#nx-generator 生成器时选择您的本地 nx-plugin 项目,并指定名称和可选的目录和描述。
定义 Schema
Section titled “定义 Schema”schema.json 文件定义了您的 generator 接受的选项。它遵循 JSON Schema 格式,并带有 Nx 特定的扩展。
schema.json 文件具有以下基本结构:
{ "$schema": "https://json-schema.org/schema", "$id": "YourGeneratorName", "title": "Your Generator Title", "description": "Description of what your generator does", "type": "object", "properties": { // Your generator options go here }, "required": ["requiredOption1", "requiredOption2"]}这是一个包含几个基本选项的简单示例:
{ "$schema": "https://json-schema.org/schema", "$id": "ComponentGenerator", "title": "Create a Component", "description": "Creates a new React component", "type": "object", "properties": { "name": { "type": "string", "description": "Component name", "x-priority": "important" }, "directory": { "type": "string", "description": "Directory where the component will be created", "default": "src/components" }, "withTests": { "type": "boolean", "description": "Whether to generate test files", "default": true } }, "required": ["name"]}交互式提示(CLI)
Section titled “交互式提示(CLI)”您可以通过添加 x-prompt 属性来自定义通过 CLI 运行 generator 时显示的提示:
"name": { "type": "string", "description": "Component name", "x-prompt": "What is the name of your component?"}对于布尔选项,您可以使用是/否提示:
"withTests": { "type": "boolean", "description": "Whether to generate test files", "x-prompt": "Would you like to generate test files?"}对于具有固定选项集的选项,使用 enum,以便用户可以从其中一个选项中进行选择。
"style": { "type": "string", "description": "The styling approach to use", "enum": ["css", "scss", "styled-components", "none"], "default": "css"}项目选择下拉菜单
Section titled “项目选择下拉菜单”一个常见的模式是让用户从工作区中的现有项目中进行选择:
"project": { "type": "string", "description": "The project to add the component to", "x-prompt": "Which project would you like to add the component to?", "x-dropdown": "projects"}x-dropdown: "projects" 属性告诉 Nx 用工作区中的所有项目填充下拉菜单。
您可以配置选项,以便在从命令行运行 generator 时作为位置参数传递:
"name": { "type": "string", "description": "Component name", "x-priority": "important", "$default": { "$source": "argv", "index": 0 }}这允许用户像 nx g your-generator my-component 这样运行您的 generator,而不是 nx g your-generator --name=my-component。
使用 x-priority 属性来指示哪些选项最重要:
"name": { "type": "string", "description": "Component name", "x-priority": "important"}选项可以具有 "important" 或 "internal" 的优先级。这有助于 Nx 在 Nx VSCode 扩展和 Nx CLI 中对属性进行排序。
您可以为选项提供默认值:
"directory": { "type": "string", "description": "Directory where the component will be created", "default": "src/components"}有关 schema 的更多详细信息,请参阅 Nx Generator Options 文档。
使用 schema.d.ts 的 TypeScript 类型
Section titled “使用 schema.d.ts 的 TypeScript 类型”除了 schema.json 之外,generator 还会创建一个 schema.d.ts 文件,为您的 generator 选项提供 TypeScript 类型:
export interface YourGeneratorSchema { name: string; directory?: string; withTests?: boolean;}此接口在您的 generator 实现中使用,以提供类型安全和代码补全:
import { YourGeneratorSchema } from './schema';
export default async function (tree: Tree, options: YourGeneratorSchema) { // TypeScript knows the types of all your options const { name, directory = 'src/components', withTests = true } = options; // ...}实现一个 Generator
Section titled “实现一个 Generator”在如上所述创建新的 generator 后,您可以在 generator.ts 中编写您的实现。
generator 是一个修改虚拟文件系统(Tree)的函数,读取和写入文件以进行所需的更改。除非在”dry-run”模式下运行,否则 Tree 的更改仅在 generator 完成执行后才会写入磁盘。一个空的 generator 如下所示:
export const myGenerator = async (tree: Tree, options: MyGeneratorSchema) => { // Use the tree to apply changes};
export default myGenerator;以下是您可能希望在 generator 中执行的一些常见操作:
读取和写入文件
Section titled “读取和写入文件”// Read a fileconst content = tree.read('path/to/file.ts', 'utf-8');
// Write a filetree.write('path/to/new-file.ts', 'export const hello = "world";');
// Check if a file existsif (tree.exists('path/to/file.ts')) { // Do something}从模板生成文件
Section titled “从模板生成文件”您可以使用 @nx/devkit 中的 generateFiles 实用程序生成文件。这允许您以 EJS 语法定义模板,并替换变量。
import { generateFiles, joinPathFragments } from '@nx/devkit';
// Generate files from templatesgenerateFiles( tree, joinPathFragments(import.meta.dirname, 'files'), // Template directory 'path/to/output', // Output directory { // Variables to replace in templates name: options.name, nameCamelCase: camelCase(options.name), nameKebabCase: kebabCase(options.name), // Add more variables as needed },);使用 GritQL 进行代码转换
Section titled “使用 GritQL 进行代码转换”您可以使用 GritQL 在 generator 中声明式地搜索和转换源代码。GritQL 支持多种语言,包括 TypeScript、JavaScript、Python、HCL(Terraform)等——因此您可以在整个技术栈中使用相同的模式语法。
Nx Plugin for AWS 公开了两个辅助函数:
applyGritQL(tree, filePath, pattern)— 将 GritQL 重写模式应用于文件,并返回Promise<boolean>,指示是否进行了更改matchGritQL(tree, filePath, pattern)— 检查 GritQL 模式是否在文件中的任何位置匹配,并返回Promise<boolean>
import { applyGritQL, matchGritQL } from '@aws/nx-plugin/sdk/utils/ast';
// Replace a function callawait applyGritQL( tree, 'src/app.ts', '`console.log($msg)` => `logger.info($msg)`',);
// Add an element to an array only if not already presentawait applyGritQL( tree, 'src/plugins.ts', '`plugins: [$items]` => `plugins: [$items, myPlugin()]` where { $items <: not contains `myPlugin` }',);
// Check if a pattern exists before making changesif (!(await matchGritQL(tree, filePath, '`import { Auth } from "./auth"`'))) { // Add the import}GritQL 模式也适用于非 TypeScript 文件。在模式前加上 language <name> 以针对其他语言:
// Python: replace print statements with logging callsawait applyGritQL( tree, 'src/handler.py', 'language python\n`print($msg)` => `logger.info($msg)`',);GritQL 模式使用反引号分隔的代码片段,其中 $metavariables 作为通配符。使用 => 进行重写,使用 where 子句进行条件判断。
import { addDependenciesToPackageJson } from '@nx/devkit';
// Add dependencies to package.jsonaddDependenciesToPackageJson( tree, { 'new-dependency': '^1.0.0', }, { 'new-dev-dependency': '^2.0.0', },);格式化生成的文件
Section titled “格式化生成的文件”import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
// Format all files that were modifiedawait formatFilesInSubtree(tree, 'optional/path/to/format');读取和更新 JSON 文件
Section titled “读取和更新 JSON 文件”import { readJson, updateJson } from '@nx/devkit';
// Read a JSON fileconst packageJson = readJson(tree, 'package.json');
// Update a JSON fileupdateJson(tree, 'tsconfig.json', (json) => { json.compilerOptions = { ...json.compilerOptions, strict: true, }; return json;});扩展来自 Nx Plugin for AWS 的 Generator
Section titled “扩展来自 Nx Plugin for AWS 的 Generator”您可以从 Nx Plugin for AWS 导入 generator,并根据需要扩展或组合它们,例如,您可能希望创建一个基于 TypeScript 项目构建的 generator:
import { tsProjectGenerator } from '@aws/nx-plugin/sdk/ts';
export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => { const callback = await tsProjectGenerator(tree, { ... });
// Extend the TypeScript project generator here
// Return the callback to ensure dependencies are installed. // You can wrap the callback if you wish to perform additional operations in the generator callback. return callback;};OpenAPI Generators
Section titled “OpenAPI Generators”您可以以类似于上述的方式使用和扩展我们用于 TypeScript 客户端和钩子的 generator:
import { openApiTsClientGenerator } from '@aws/nx-plugin/sdk/open-api';
export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => { await openApiTsClientGenerator(tree, { ... });
// Add additional files here};我们还公开了一个方法,允许您构建一个数据结构,该结构可用于迭代 OpenAPI 规范中的操作,从而实现您自己的代码生成,例如:
import { buildOpenApiCodeGenerationData } from '@aws/nx-plugin/sdk/open-api.js';
export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => { const data = await buildOpenApiCodeGenerationData(tree, 'path/to/spec.json');
generateFiles( tree, joinPathFragments(import.meta.dirname, 'files'), // Template directory 'path/to/output', // Output directory data, );};然后允许您编写如下模板:
export const myOperationNames = [<%_ allOperations.forEach((op) => { _%> '<%- op.name %>',<%_ }); _%>];有关更复杂的示例模板,请参阅 GitHub 上的代码库。
运行您的 Generator
Section titled “运行您的 Generator”您可以通过两种方式运行您的 generator:
pnpm nx g @my-project/nx-plugin:my-generatoryarn nx g @my-project/nx-plugin:my-generatornpx nx g @my-project/nx-plugin:my-generatorbunx nx g @my-project/nx-plugin:my-generator您还可以执行试运行以查看哪些文件会被更改
pnpm nx g @my-project/nx-plugin:my-generator --dry-runyarn nx g @my-project/nx-plugin:my-generator --dry-runnpx nx g @my-project/nx-plugin:my-generator --dry-runbunx nx g @my-project/nx-plugin:my-generator --dry-run- 安装 Nx Console VSCode Plugin 如果您尚未安装
- 在VSCode中打开Nx控制台
- 点击
Generate (UI)在"Common Nx Commands"部分 - 搜索
@my-project/nx-plugin - my-generator - 填写必需参数
- 点击
Generate
测试您的 Generator
Section titled “测试您的 Generator”generator 的单元测试很容易实现。这是一个典型的模式:
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';import { yourGenerator } from './generator.js';
describe('your generator', () => { let tree;
beforeEach(() => { // Create an empty workspace tree tree = createTreeWithEmptyWorkspace();
// Add any files that should already exist in the tree tree.write( 'project.json', JSON.stringify({ name: 'test-project', sourceRoot: 'src', }), );
tree.write('src/existing-file.ts', 'export const existing = true;'); });
it('should generate expected files', async () => { // Run the generator await yourGenerator(tree, { name: 'test', // Add other required options });
// Check that files were created expect(tree.exists('src/test/file.ts')).toBeTruthy();
// Check file content const content = tree.read('src/test/file.ts', 'utf-8'); expect(content).toContain('export const test');
// You can also use snapshots expect(tree.read('src/test/file.ts', 'utf-8')).toMatchSnapshot(); });
it('should update existing files', async () => { // Run the generator await yourGenerator(tree, { name: 'test', // Add other required options });
// Check that existing files were updated const content = tree.read('src/existing-file.ts', 'utf-8'); expect(content).toContain('import { test } from'); });
it('should handle errors', async () => { // Expect the generator to throw an error in certain conditions await expect( yourGenerator(tree, { name: 'invalid', // Add options that should cause an error }), ).rejects.toThrow('Expected error message'); });});测试 generator 的关键点:
- 使用
createTreeWithEmptyWorkspace()创建虚拟文件系统 - 在运行 generator 之前设置任何先决条件文件
- 测试新文件的创建和现有文件的更新
- 对复杂的文件内容使用快照
- 测试错误条件以确保您的 generator 优雅地失败
向 @aws/nx-plugin 贡献 Generators
Section titled “向 @aws/nx-plugin 贡献 Generators”您还可以使用 ts#nx-generator 在 @aws/nx-plugin 中搭建一个 generator。
当此 generator 在我们的存储库中运行时,它将为您生成以下文件:
文件夹packages/nx-plugin/src/<name>/
- schema.json Schema for input to your generator
- schema.d.ts TypeScript types for your schema
- generator.ts Generator implementation
- generator.spec.ts Tests for your generator
文件夹docs/src/content/docs/guides/
- <name>.mdx Documentation page for your generator
- packages/nx-plugin/generators.json Updated to include your generator
- packages/nx-plugin/sdk/<prefix>.ts Updated to expose your generator from the SDK (for
ts#andpy#generators)
然后您可以开始实现您的 generator。