介绍
大约三周前,我开始了一项旅程,目标是开发一个支持 single-spa 微前端项目的 Nx 插件。在找不到现有插件的情况下,我决定自己创建一个,以便能够轻松使用 Nx 生成项目。为了实现这一目标,我研究了现有的 Nx 插件,特别是 @nx/extension 和 nx 存储库,这使我理解了其中的关键组件:生成器和执行器。由于我只需要生成器,所以我将注意力集中在这个方面。
设置 Nx 插件工作空间
在深入编码之前,我为开发插件创建了一个 Nx 工作空间:
bashCopy code
npx create-nx-plugin@latest
这个命令引导我完成设置过程,允许我为工作空间命名。随后,我创建了一个名为 nx-react-sspa 的包,其中包含了我打算使用插件生成应用程序的相关代码,位于 ./nx-react-sspa/src/generators/application。
理解和扩展模式
生成器中两个关键文件是 schema.d.ts 和 schema.json。在 schema.d.ts 中,我扩展了原始的 @nx/react 模式以满足我的需求:
typescriptCopy code
import { Schema as BaseSchema, NormalizedSchema as BaseNormalizedSchema } from '@nx/react/src/generators/application/schema';
export interface ReactSinglespaGeneratorSchema extends BaseSchema {
organization: string;
}
在 schema.json 中,我定义了问题和选项,具体指定了项目名称和 CSS 预处理器偏好等属性。
创建模板文件
模板文件放置在生成器的子目录中,具体是 ./nx-react-sspa/src/generators/application/files。
实施生成器
主要的生成器文件名为 generator.ts,对于生成过程非常关键。我选择在现有 Nx 生成器的基础上进行开发,而不是完全开发一个新的生成过程。然而,我最初的错误是试图使用 Node.js 功能向文件系统添加文件。这导致由 Nx 在生成过程中设置的锁定引发错误。
为了解决这个问题,我使用了内置函数并开发了一个示例文件生成函数:
typescriptCopy code
export function addSingleSpaFiles(
tree: Tree,
options: NormalizedReactSinglespaGeneratorSchema
) {
// ...(为了简洁省略部分内容)
generateFiles(tree, srcPath, targetPath, templateOptions);
}
在主要的 generator.ts 文件中,我集成了原始的应用程序生成器,并添加了我的自定义文件生成:
typescriptCopy code
import { applicationGenerator } from '@nx/react';
export async function appGenerator(
tree: Tree,
options: ReactSinglespaGeneratorSchema
): Promise<GeneratorCallback> {
// ...(为了简洁省略部分内容)
const originalAppGen = await applicationGenerator(tree, rest);
tasks.push(originalAppGen);
addSingleSpaFiles(tree, normalizedOptions);
// ...(为了简洁省略部分内容)
return runTasksInSerial(...tasks);
}
结论
通过这些实现,我编译并执行了插件,轻松地创建了新项目
以上内容由ChatGPT翻译
Introduction
About three weeks ago, I embarked on a journey to develop an Nx plugin supporting single-spa micro-frontend projects. Not finding an existing plugin, I decided to create my own for generating projects effortlessly with Nx. To achieve this, I studied existing Nx plugins, particularly the @nx/extension and nx repositories, which led me to understand the essential components: generators and executors. Since I only needed generators, my focus centered on that aspect.
Setting Up the Nx Plugin Workspace
Before diving into coding, I created an Nx workspace for developing the plugin:
npx create-nx-plugin@latest
This command guided me through the setup process, allowing me to name my workspace. Subsequently, I created my package, named nx-react-sspa, where I intended to generate applications using the plugin. The relevant code for this resided in ./nx-react-sspa/src/generators/application.
Understanding and Extending Schemas
Two crucial files in the generator were schema.d.ts and schema.json. In schema.d.ts, I extended the original @nx/react schema for my needs:
import { Schema as BaseSchema, NormalizedSchema as BaseNormalizedSchema } from '@nx/react/src/generators/application/schema';
export interface ReactSinglespaGeneratorSchema extends BaseSchema {
organization: string;
}
In schema.json, I defined the questions and options, specifying attributes like project name and CSS preprocessor preferences.
Creating Template Files
Template files were placed in a sub-directory of the generator, specifically ./nx-react-sspa/src/generators/application/files.
Implementing the Generator
The main generator file, named generator.ts, was crucial for the generation process. Instead of developing an entirely new generation process, I opted to build on the existing Nx generator. However, my initial mistake was attempting to add files to the file system using Node.js features. This led to errors due to locks set by Nx during the generation process.
To overcome this, I utilized built-in functions and developed a sample file generation function:
export function addSingleSpaFiles(
tree: Tree,
options: NormalizedReactSinglespaGeneratorSchema
) {
// ... (omitted for brevity)
generateFiles(tree, srcPath, targetPath, templateOptions);
}
In the main generator.ts file, I integrated the original application generator and added my custom file generation:
import { applicationGenerator } from '@nx/react';
export async function appGenerator(
tree: Tree,
options: ReactSinglespaGeneratorSchema
): Promise<GeneratorCallback> {
// ... (omitted for brevity)
const originalAppGen = await applicationGenerator(tree, rest);
tasks.push(originalAppGen);
addSingleSpaFiles(tree, normalizedOptions);
// ... (omitted for brevity)
return runTasksInSerial(...tasks);
}
Conclusion
With these implementations in place, I compiled and executed the plugin to create new projects effortlessly.