Plugin Core
This section describes the core plugin types and APIs.
RsbuildPlugin
The type of the plugin object. The plugin object contains the following properties:
name
: The name of the plugin, a unique identifier.
setup
: The setup function of the plugin, which can be an async function. This function is called once when the plugin is initialized. The plugin API provides the context info, utility functions and lifecycle hooks. For a complete introduction to lifecycle hooks, please read the Plugin Hooks chapter.
pre
: Declare the names of pre-plugins, which will be executed before the current plugin.
post
: Declare the names of post-plugins, which will be executed after the current plugin.
remove
: Declare the plugins that need to be removed, you can pass an array of plugin names.
The type of the plugin object, which contains the following properties:
type RsbuildPlugin = {
name: string;
pre?: string[];
post?: string[];
remove?: string[];
setup: (api: RsbuildPluginAPI) => Promise<void> | void;
};
You can import this type from @rsbuild/core
:
import type { RsbuildPlugin } from '@rsbuild/core';
export default (): RsbuildPlugin => ({
name: 'plugin-foo',
pre: ['plugin-bar'],
setup: (api) => {
api.onAfterBuild(() => {
console.log('after build!');
});
},
});
Pre-Plugins
By default, plugins are executed in the order they are added. You can declare pre-execution plugins using the pre
field.
For example, consider the following two plugins:
const pluginFoo = {
name: 'plugin-foo',
};
const pluginBar = {
name: 'plugin-bar',
pre: ['plugin-foo'],
};
The Bar plugin is configured with the Foo plugin in its pre
field, so the Foo plugin will always be executed before the Bar plugin.
Post-Plugins
Similarly, you can declare post-execution plugins using the post
field.
const pluginFoo = {
name: 'plugin-foo',
};
const pluginBar = {
name: 'plugin-bar',
post: ['plugin-foo'],
};
The Bar plugin is configured with the Foo plugin in its post
field, so the Foo plugin will always be executed after the Bar plugin.
Removing Plugins
You can remove other plugins within a plugin using the remove
field.
const pluginFoo = {
name: 'plugin-foo',
};
const pluginBar = {
name: 'plugin-bar',
remove: ['plugin-foo'],
};
For example, if you register both the Foo and Bar plugins mentioned above, the Foo plugin will not take effect because the Bar plugin declares the removal of the Foo plugin.
api.context
api.context
is a read-only object that provides some context information.
The content of api.context
is exactly the same as rsbuild.context
, please refer to rsbuild.context.
const pluginFoo = () => ({
setup(api) {
console.log(api.context.distPath);
},
});
api.getRsbuildConfig
Get the Rsbuild config, this method must be called after the modifyRsbuildConfig
hook is executed.
type GetRsbuildConfig = {
(): Readonly<RsbuildConfig>;
(type: 'original' | 'current'): Readonly<RsbuildConfig>;
(type: 'normalized'): NormalizedConfig;
};
You can specify the type of Rsbuild config to read by using the type
parameter:
// Get the original Rsbuild config defined by the user.
getRsbuildConfig('original');
// Get the current Rsbuild config.
// The content of this config will change at different execution stages of Rsbuild.
// For example, the content of the current Rsbuild config will be modified after running the `modifyRsbuildConfig` hook.
getRsbuildConfig('current');
// Get the normalized Rsbuild config.
// This method must be called after the `modifyRsbuildConfig` hook has been executed.
// It is equivalent to the `getNormalizedConfig` method.
getRsbuildConfig('normalized');
const pluginFoo = () => ({
setup(api) {
const config = api.getRsbuildConfig();
console.log(config.html?.title);
},
});
api.getNormalizedConfig
Get the normalized Rsbuild config, this method must be called after the modifyRsbuildConfig
hook is executed.
Compared with the api.getRsbuildConfig
method, the config returned by this method has been normalized, and the type definition of the config will be narrowed. For example, the undefined
type of config.html
will be removed.
It is recommended to use this method to get the Rsbuild config.
function GetNormalizedConfig(): Readonly<NormalizedConfig>;
const pluginFoo = () => ({
setup(api) {
const config = api.getNormalizedConfig();
console.log(config.html.title);
},
});
api.isPluginExists
Determines whether a plugin has been registered.
function IsPluginExists(pluginName: string): boolean;
export default () => ({
setup: (api) => {
console.log(api.isPluginExists('plugin-foo'));
},
});
api.getHTMLPaths
Get path information for all HTML assets.
This method will return an object, the key is the entry name and the value is the relative path of the HTML file in the dist directory.
function GetHTMLPaths(): Record<string, string>;
const pluginFoo = () => ({
setup(api) {
api.modifyRspackConfig(() => {
const htmlPaths = api.getHTMLPaths();
console.log(htmlPaths); // { index: 'index.html' };
});
},
});
api.expose
Used for plugin communication.
api.export
can explicitly expose some properties or methods of the current plugin, and other plugins can get these APIs through api.useExposed
.
/**
* @param id Unique identifier, using Symbol can avoid naming conflicts
* @param api Properties or methods to be exposed, it is recommended to use object format
*/
function expose<T = any>(id: string | symbol, api: T): void;
const pluginParent = () => ({
name: 'plugin-parent',
setup(api) {
api.expose('plugin-parent', {
value: 1,
double: (val: number) => val * 2,
});
},
});
api.useExposed
Used for plugin communication.
api.useExposed
can get the properties or methods exposed by other plugins.
/**
* @param id Unique identifier
* @returns The properties or methods obtained
*/
function useExposed<T = any>(id: string | symbol): T | undefined;
const pluginChild = () => ({
name: 'plugin-child',
pre: ['plugin-parent'],
setup(api) {
const parentApi = api.useExposed('plugin-parent');
if (parentApi) {
console.log(parentApi.value); // -> 1
console.log(parentApi.double(1)); // -> 2
}
},
});
Identifiers
You can use Symbol as a unique identifier to avoid potential naming conflicts:
// pluginParent.ts
export const PARENT_API_ID = Symbol('plugin-parent');
const pluginParent = () => ({
name: 'plugin-parent',
setup(api) {
api.expose(PARENT_API_ID, {
// some api
});
},
});
// pluginChild.ts
import { PARENT_API_ID } from './pluginParent';
const pluginChild = () => ({
name: 'plugin-child',
setup(api) {
const parentApi = api.useExposed(PARENT_API_ID);
if (parentApi) {
console.log(parentApi);
}
},
});
Type Declaration
You can declare types through the generics of the function:
// pluginParent.ts
export type ParentAPI = {
// ...
};
// pluginChild.ts
import type { ParentAPI } from './pluginParent';
const pluginChild = () => ({
name: 'plugin-child',
setup(api) {
const parentApi = api.useExposed<ParentAPI>(PARENT_API_ID);
if (parentApi) {
console.log(parentApi);
}
},
});
Execution Order
When communicating between plugins, you need to be aware of the order in which the plugins are executed.
For example, in the above example, if pluginParent
is not registered, or registers after pluginChild
, then api.useExposed('plugin-parent')
will return an undefined
.
You can use the pre
, post
options of the plugin object, and the order
option of the plugin hook to ensure the order is correct.