diff --git a/website/docs/zh/guide/_meta.json b/website/docs/zh/guide/_meta.json index 28d0473032..e132bdc6c5 100644 --- a/website/docs/zh/guide/_meta.json +++ b/website/docs/zh/guide/_meta.json @@ -44,6 +44,11 @@ "name": "debug", "label": "调试" }, + { + "type": "dir", + "name": "plugin", + "label": "开发 Rsbuild 插件" + }, { "type": "dir", "name": "faq", diff --git a/website/docs/zh/guide/plugin/_meta.json b/website/docs/zh/guide/plugin/_meta.json new file mode 100644 index 0000000000..49cdeab83a --- /dev/null +++ b/website/docs/zh/guide/plugin/_meta.json @@ -0,0 +1 @@ +["start"] \ No newline at end of file diff --git a/website/docs/zh/guide/plugin/start.mdx b/website/docs/zh/guide/plugin/start.mdx new file mode 100644 index 0000000000..62d612a267 --- /dev/null +++ b/website/docs/zh/guide/plugin/start.mdx @@ -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; + +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. +``` \ No newline at end of file