Skip to content

doc: add rsbuild plugin start doc #5455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions website/docs/zh/guide/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
"name": "debug",
"label": "调试"
},
{
"type": "dir",
"name": "plugin",
"label": "开发 Rsbuild 插件"
},
{
"type": "dir",
"name": "faq",
Expand Down
1 change: 1 addition & 0 deletions website/docs/zh/guide/plugin/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["start"]
252 changes: 252 additions & 0 deletions website/docs/zh/guide/plugin/start.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
# Rsbuild 插件开发指南

## 插件开发

启动一个基本的插件项目:

```typescript
import { RsbuildPlugin } from '@rsbuild/core';
export default function plugin(): RsbuildPlugin {
return {
name: 'plugin',
setup(api) {
// api 对象上的方法即是 plugin hooks,存在的方法可以再这里进行查找
// https://rsbuild.rs/plugins/dev/hooks
}
};
}
```

这时候在 rsbuild 配置(rsbuild.config.ts)中,就可以引入这个插件了:

```typescript
import { defineConfig } from '@rsbuild/core';
import customPlugin from './plugin';
export default defineConfig({
plugins: [customPlugin()],
});
```

## 为插件增加配置选项 PluginOptions

如下,增加一个 PluginOptions 的 inteface,可以作为参数传给 plugin 函数。建议同时导出 PluginOptions 类型,方便插件使用者阅读类型信息

```typescript
import { RsbuildPlugin } from '@rsbuild/core';
export interface PluginOptions {
foo: string;
}
export default function plugin(options: PluginOptions): RsbuildPlugin {
return {
name: 'plugin',
setup(api) {
// 这里可以通过 options 拿到用户传入的配置
console.log(options.foo);
}
}
}
```

## 了解插件的 API

Rsbuild api 一共提供了以下属性,这些 hook 会按照顺序执行。

- modifyRsbuildConfig
- modifyEnvironmentConfig
- onBeforeStartDevServer
- modifyBundlerChain
- modifyRspackConfig
- onBeforeCreateCompiler
- onAfterCreateCompiler
- onBeforeEnvironmentCompile
- onAfterStartDevServer
- modifyHTMLTags
- modifyHTML
- onAfterEnvironmentCompile
- onDevCompileDone
- onCloseDevServer
- onExit

一些注意事项:
1. 真实的 Rsbuild 实例会在 `modifyRsbuildConfig` `modifyEnvironmentConfig` `onBeforeStartDevServer` 之后创建,所以在这三个 API 内并没有明确的 mode 是 development 还是 production。在这三个 API 内可以通过 `process.node.NODE_ENV` 来判断是否为 development,在此之后的 API 中可以通过 hooks 参数中的 isDev isProd 来判断
2. Rsbuild 依赖的 Rspack 实例是在 `modifyRsbuildConfig` `modifyEnvironmentConfig` `onBeforeStartDevServer` `modifyRspackConfig` `modifyBundlerChain` `onBeforeCreateCompiler` 这些 API 之后创建的。所以在此之后的 API 才可以有 rspack 的实例


## 判断环境是 development 还是 production

可以通过 api.modifyRsbuildConfig 方法拿到 Rsbuild 的配置,然后根据配置的 mode 来判断环境:

```typescript
import { RsbuildPlugin } from '@rsbuild/core';
export default function plugin(): RsbuildPlugin {
return {
name: 'plugin',
setup(api) {
// 在 modifyRsbuildConfig 中还没有最终的 mode
api.modifyRsbuildConfig((config) => {
const isDev = process.env.NODE_ENV === 'development';
const isProd = process.env.NODE_ENV === 'production';
});
// 在 modifyRspackConfig 中可以通过 isDev 和 isProd 来判断环境
api.modifyRspackConfig((config, { isDev, isProd }) => {
if (isDev) {
console.log('development');
} else if (isProd) {
console.log('production');
}
});
}
}
}
```

## 修改 Rsbuild 的基本配置

### 思路

1. 通过阅读 [RsbuildConfig](https://rsbuild.rs/config/) 了解哪个配置项目可以满足插件的需求
2. 通过 `api.modifyRsbuildConfig` 方法修改 Rsbuild 的基本配置

### 例子 1: 修改输出目录

例如,我们需要开发一个插件,修改输出目录的位置:

1. 在 Rsbuild 配置中了解可以修改输出目录的配置项目,找到为 output.distPath 可以[参考文档](https://rsbuild.rs/config/output/dist-path)
2. 编写插件,通过 `api.modifyRsbuildConfig` 方法修改输出目录的位置,代码示例如下:

```typescript
import { RsbuildPlugin } from '@rsbuild/core';
interface OutputOptions {
path: string;
}
export default function plugin(options: OutputOptions): RsbuildPlugin {
return {
name: 'plugin',
setup(api) {
if(options.path) {
// userConfig 即为 Rsbuild 的配置
// 可以在这里查看配置的全部内容 https://rsbuild.rs/config/
api.modifyRsbuildConfig((userConfig, { mergeRsbuildConfig }) => {
const outputConfig: RsbuildConfig = {
output: {
distPath: {
root: options.path,
}
},
};

// outputConfig 会覆盖用户的配置
// 如果不希望覆盖用户的配置,可以使用 `mergeRsbuildConfig` 方法反过来传递
// 例如: mergeRsbuildConfig(outputConfig, userConfig)
return mergeRsbuildConfig(userConfig, outputConfig);
});
}
}
};
}
```
### 例子 2: 自定义页面入口

例如,我们需要开发一个插件,自定义页面入口:

1. 在 Rsbuild 配置中了解可以修改页面入口的配置项目,找到为 source.entry 可以[参考文档](https://rsbuild.rs/config/source/entry)
2. 编写插件,通过 `api.modifyRsbuildConfig` 方法修改页面入口的位置,代码示例如下:

```typescript
import { RsbuildPlugin } from '@rsbuild/core';

interface EntryOptions {
entry: string;
}
export default function plugin(options: EntryOptions): RsbuildPlugin {
return {
name: 'rsbuild-source-plugin',
setup(api) {
if(options.entry) {
api.modifyRsbuildConfig((userConfig, { mergeRsbuildConfig }) => {
let entry = "./src/index.ts"; // config you own entry, custom your logic here
const sourceConfig: RsbuildConfig = {
source:{
entry: entry || options.entry,
},
};

return mergeRsbuildConfig(userConfig, sourceConfig);
});
}
}
};
}
```


## 自定义 DevServer 的行为

可以通过 [onBeforeStartDevServer](https://rsbuild.rs/zh/plugins/dev/hooks#onbeforestartdevserver) 来自定义 DevServer 的行为。

例如,我们需要自定义 DevServer 来处理一些请求,代码示例如下:

```typescript

import { RsbuildPlugin } from '@rsbuild/core';
import { RequestHandler } from 'express';

type DevServerOptions = Record<string, string>;

export default function plugin(options: DevServerOptions): RsbuildPlugin {
return {
name: 'plugin',
setup(api) {
api.onBeforeStartDevServer(({ server }) => {
server.middlewares.use((req, res, next) => {
const urls = Object.keys(options);
if(urls.includes(req.url)) {
res.end(options[req.url]);
}else{
next();
}
});
});
},
};
}
```

该插件的使用例子:

```typescript
import staticContent from './plugin';

export default {
plugins: [
staticContent({
'/json': '{"name": "rsbuild"}',
})
]
}
```

## 插件的日志打印

默认情况下插件可以直接使用 `console.log` 和 `console.error` 等标准方法进行日志输出。如果你希望打印跟 Rsbuild 主题更接近的 log 可以使用 logger。

```typescript
import { logger } from "@rsbuild/core";

logger.log("hello world");
```

## 自定义 resolver

当插件有需求要自定义 resolver 的行为时,可以使用 @rspack/resolver 来创建新的 resolver,可以在[这里](https://github.com/web-infra-dev/rspack-resolver/blob/main/README.md)了解更多的信息

```typescript
import { ResolverFactory } from "@rspack/resolver";

const resolver = new ResolverFactory({
alias: [],
mainFields: ["main", "module"],
});

const indexJsPath = resolver.async(__dirname, "./index.js").path; // resolve specifier at an absolute path to a directory.
```
Loading