跳转到内容

Nx Generator 生成器

向 TypeScript 项目添加一个 Nx Generator,帮助您自动化重复性任务,例如脚手架组件或强制执行特定的项目结构。

您可以通过两种方式生成一个 generator:

Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-generator
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-generator --dry-run
参数类型默认值描述
project 必需string-要添加生成器的 TypeScript 项目。我们推荐使用 ts#nx-plugin 生成器来创建此项目。
name 必需string-生成器名称
description string-生成器的描述
directory string-插件项目源文件夹中用于添加生成器的目录(默认:<name>)
preferInstallDependencies booleantrue是否在生成器运行后优先安装依赖项。设置为 false 可在批量运行多个生成器时延迟安装(如果后续生成器需要计算 Nx 项目图,仍会运行安装);在最后统一安装一次。

该 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 支持)。

在运行 ts#nx-generator 生成器时选择您的本地 nx-plugin 项目,并指定名称和可选的目录和描述。

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

您可以通过添加 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"
}

一个常见的模式是让用户从工作区中的现有项目中进行选择:

"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.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 后,您可以在 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 中执行的一些常见操作:

// Read a file
const content = tree.read('path/to/file.ts', 'utf-8');
// Write a file
tree.write('path/to/new-file.ts', 'export const hello = "world";');
// Check if a file exists
if (tree.exists('path/to/file.ts')) {
// Do something
}

您可以使用 @nx/devkit 中的 generateFiles 实用程序生成文件。这允许您以 EJS 语法定义模板,并替换变量。

import { generateFiles, joinPathFragments } from '@nx/devkit';
// Generate files from templates
generateFiles(
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 在 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 call
await applyGritQL(
tree,
'src/app.ts',
'`console.log($msg)` => `logger.info($msg)`',
);
// Add an element to an array only if not already present
await applyGritQL(
tree,
'src/plugins.ts',
'`plugins: [$items]` => `plugins: [$items, myPlugin()]` where { $items <: not contains `myPlugin` }',
);
// Check if a pattern exists before making changes
if (!(await matchGritQL(tree, filePath, '`import { Auth } from "./auth"`'))) {
// Add the import
}

GritQL 模式也适用于非 TypeScript 文件。在模式前加上 language <name> 以针对其他语言:

// Python: replace print statements with logging calls
await 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.json
addDependenciesToPackageJson(
tree,
{
'new-dependency': '^1.0.0',
},
{
'new-dev-dependency': '^2.0.0',
},
);
import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
// Format all files that were modified
await formatFilesInSubtree(tree, 'optional/path/to/format');
import { readJson, updateJson } from '@nx/devkit';
// Read a JSON file
const packageJson = readJson(tree, 'package.json');
// Update a JSON file
updateJson(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;
};

您可以以类似于上述的方式使用和扩展我们用于 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,
);
};

然后允许您编写如下模板:

files/my-operations.ts.template
export const myOperationNames = [
<%_ allOperations.forEach((op) => { _%>
'<%- op.name %>',
<%_ }); _%>
];

有关更复杂的示例模板,请参阅 GitHub 上的代码库

您可以通过两种方式运行您的 generator:

Terminal window
pnpm nx g @my-project/nx-plugin:my-generator
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @my-project/nx-plugin:my-generator --dry-run

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 优雅地失败

您还可以使用 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# and py# generators)

然后您可以开始实现您的 generator。