Skip to content

Commit

Permalink
feat: support nested plugins (#3348)
Browse files Browse the repository at this point in the history
Co-authored-by: neverland <[email protected]>
  • Loading branch information
9aoy and chenjiahan authored Sep 2, 2024
1 parent 1df6397 commit 71f8ae4
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 6 deletions.
19 changes: 17 additions & 2 deletions packages/core/src/createRsbuild.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isPromise } from 'node:util/types';
import { createContext } from './createContext';
import { getNodeEnv, pick, setNodeEnv } from './helpers';
import { initPluginAPI } from './initPlugins';
Expand All @@ -9,11 +10,14 @@ import type {
Build,
CreateDevServer,
CreateRsbuildOptions,
Falsy,
InternalContext,
PluginManager,
PreviewServerOptions,
ResolvedCreateRsbuildOptions,
RsbuildInstance,
RsbuildPlugin,
RsbuildPlugins,
RsbuildProvider,
StartDevServer,
} from './types';
Expand Down Expand Up @@ -199,8 +203,19 @@ export async function createRsbuild(
]),
};

const getFlattenedPlugins = async (pluginOptions: RsbuildPlugins) => {
let plugins = pluginOptions;
do {
plugins = (await Promise.all(plugins)).flat(
Number.POSITIVE_INFINITY as 1,
);
} while (plugins.some((v) => isPromise(v)));

return plugins as Array<RsbuildPlugin | Falsy>;
};

if (rsbuildConfig.plugins) {
const plugins = await Promise.all(rsbuildConfig.plugins);
const plugins = await getFlattenedPlugins(rsbuildConfig.plugins);
rsbuild.addPlugins(plugins);
}

Expand All @@ -212,7 +227,7 @@ export async function createRsbuild(
!rsbuildOptions.environment ||
rsbuildOptions.environment.includes(name);
if (config.plugins && isEnvironmentEnabled) {
const plugins = await Promise.all(config.plugins);
const plugins = await getFlattenedPlugins(config.plugins);
rsbuild.addPlugins(plugins, {
environment: name,
});
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/types/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ type LooseRsbuildPlugin = Omit<RsbuildPlugin, 'setup'> & {
export type RsbuildPlugins = (
| LooseRsbuildPlugin
| Falsy
| Promise<LooseRsbuildPlugin | Falsy>
| Promise<LooseRsbuildPlugin | Falsy | RsbuildPlugins>
| RsbuildPlugins
)[];

export type GetRsbuildConfig = {
Expand Down
41 changes: 41 additions & 0 deletions packages/core/tests/builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,44 @@ describe('should use rspack as default bundler', () => {
process.env.NODE_ENV = NODE_ENV;
});
});

describe('plugins', () => {
it('should apply nested plugins correctly', async () => {
function myPlugin() {
return [
{
name: 'plugin-foo',
setup() {},
},
{
name: 'plugin-bar',
setup() {},
},
];
}

const rsbuild = await createRsbuild({
rsbuildConfig: {
source: {
entry: {
index: './src/index.js',
},
},
plugins: [
myPlugin(),
Promise.resolve([
{
name: 'plugin-zoo',
setup() {},
},
]),
],
},
});

expect(rsbuild.isPluginExists('plugin-foo')).toBeTruthy();
expect(rsbuild.isPluginExists('plugin-bar')).toBeTruthy();
expect(rsbuild.isPluginExists('plugin-zoo')).toBeTruthy();
expect(rsbuild.isPluginExists('plugin-404')).toBeFalsy();
});
});
15 changes: 14 additions & 1 deletion scripts/test-helper/src/rsbuild.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { isPromise } from 'node:util/types';
import type {
BundlerPluginInstance,
CreateRsbuildOptions,
RsbuildInstance,
RsbuildPlugin,
RsbuildPlugins,
Rspack,
} from '@rsbuild/core';
Expand Down Expand Up @@ -53,10 +55,21 @@ export async function createStubRsbuild({

const rsbuild = await createRsbuild(rsbuildOptions);

const getFlattenedPlugins = async (pluginOptions: RsbuildPlugins) => {
let plugins = pluginOptions;
do {
plugins = (await Promise.all(plugins)).flat(
Number.POSITIVE_INFINITY as 1,
);
} while (plugins.some((v) => isPromise(v)));

return plugins as Array<RsbuildPlugin | false | null | undefined>;
};

if (plugins) {
// remove all builtin plugins
rsbuild.removePlugins(rsbuild.getPlugins().map((item) => item.name));
rsbuild.addPlugins(await Promise.all(plugins));
rsbuild.addPlugins(await getFlattenedPlugins(plugins));
}

const unwrapConfig = async (index = 0) => {
Expand Down
20 changes: 19 additions & 1 deletion website/docs/en/config/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ type Falsy = false | null | undefined;
type RsbuildPlugins = (
| RsbuildPlugin
| Falsy
| Promise<RsbuildPlugin | Falsy>
| Promise<RsbuildPlugin | Falsy | RsbuildPlugins>
| RsbuildPlugins
)[];
```

Expand Down Expand Up @@ -47,6 +48,23 @@ By default, plugins are executed in the order they appear in the `plugins` array

When a plugin internally uses fields that control the order, such as `pre` and `post`, the execution order is adjusted based on them. See [Pre Plugins](/plugins/dev/core#pre-pluginss) for more details.

## Nested Plugins

Rsbuild also supports adding nested plugins. You can pass in an array containing multiple plugins, similar to a plugin preset collection. This is particularly useful for implementing complex functionalities that require a combination of multiple plugins (such as framework integration).

```ts title="rsbuild.config.ts"
function myPlugin() {
return [
fooPlugin(),
barPlugin()
];
}

export default {
plugins: [myPlugin()]
}
```

## Local Plugins

If your local code repository contains Rsbuild plugins, you can import them using relative paths.
Expand Down
20 changes: 19 additions & 1 deletion website/docs/zh/config/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ type Falsy = false | null | undefined;
type RsbuildPlugins = (
| RsbuildPlugin
| Falsy
| Promise<RsbuildPlugin | Falsy>
| Promise<RsbuildPlugin | Falsy | RsbuildPlugins>
| RsbuildPlugins
)[];
```

Expand Down Expand Up @@ -47,6 +48,23 @@ export default defineConfig({

当插件内部使用了控制顺序的相关字段,比如 `pre``post` 时,执行顺序会基于它们进行调整,详见 [前置插件](/plugins/dev/core#前置插件)

## 嵌套插件

Rsbuild 还支持添加嵌套插件,你可以传入一个包含多个插件的数组,类似于一个插件预设集合,这对于实现需要多个插件组合的复杂功能(例如框架集成)很有帮助。

```ts title="rsbuild.config.ts"
function myPlugin() {
return [
fooPlugin(),
barPlugin()
];
}

export default {
plugins: [myPlugin()]
}
```

## 本地插件

如果本地代码仓库中包含了一些 Rsbuild 插件,你可以直接通过相对路径引入。
Expand Down

0 comments on commit 71f8ae4

Please sign in to comment.