The Rspack-based build tool providing high-performance bundling with comprehensive development features and plugin system.
—
Comprehensive plugin architecture with lifecycle hooks, configuration modification, and inter-plugin communication. Rsbuild's plugin system provides extensive customization capabilities through a rich set of hooks and utilities.
Define plugins with lifecycle hooks and configuration modification capabilities.
/**
* Plugin interface for extending Rsbuild functionality
*/
interface RsbuildPlugin {
/** Unique plugin identifier */
name: string;
/** Plugin setup function called during initialization */
setup: (api: RsbuildPluginAPI) => MaybePromise<void>;
/** Conditional application based on command or context */
apply?: 'serve' | 'build' | ((config: any, context: any) => boolean);
/** Execution order enforcement */
enforce?: 'pre' | 'post';
/** Plugins that must run before this plugin */
pre?: string[];
/** Plugins that must run after this plugin */
post?: string[];
/** Plugin names to remove from the plugin list */
remove?: string[];
}
type MaybePromise<T> = T | Promise<T>;Usage Examples:
import type { RsbuildPlugin } from "@rsbuild/core";
// Basic plugin
const myPlugin = (): RsbuildPlugin => ({
name: "my-plugin",
setup(api) {
api.modifyRsbuildConfig((config) => {
config.source = config.source || {};
config.source.alias = {
...config.source.alias,
"@components": "./src/components",
};
});
},
});
// Conditional plugin (dev only)
const devOnlyPlugin = (): RsbuildPlugin => ({
name: "dev-only-plugin",
apply: "serve",
setup(api) {
api.onAfterStartDevServer(({ port, urls }) => {
console.log(`Dev server running on port ${port}`);
});
},
});
// Plugin with execution order
const criticalPlugin = (): RsbuildPlugin => ({
name: "critical-plugin",
enforce: "pre",
setup(api) {
// Runs before other plugins
},
});
// Plugin with dependencies
const dependentPlugin = (): RsbuildPlugin => ({
name: "dependent-plugin",
pre: ["rsbuild:css", "rsbuild:html"],
setup(api) {
// Runs after CSS and HTML plugins
},
});The plugin API provides access to configuration, hooks, and utilities.
/**
* API interface provided to plugins during setup
*/
interface RsbuildPluginAPI {
/** Read-only build context */
context: RsbuildContext;
/** Logging utilities */
logger: Logger;
// Configuration modification hooks
modifyRsbuildConfig: ModifyRsbuildConfigHook;
modifyEnvironmentConfig: ModifyEnvironmentConfigHook;
modifyBundlerChain: ModifyBundlerChainHook;
modifyRspackConfig: ModifyRspackConfigHook;
modifyWebpackConfig?: ModifyWebpackConfigHook;
// HTML processing hooks
modifyHTML: ModifyHTMLHook;
modifyHTMLTags: ModifyHTMLTagsHook;
// Advanced processing hooks
transform: TransformHook;
processAssets: ProcessAssetsHook;
resolve: ResolveHook;
// Plugin communication
expose: <T = any>(id: string, api: T) => void;
useExposed: <T = any>(id: string) => T | undefined;
// Lifecycle hooks (inherited from RsbuildInstance)
onBeforeBuild: (fn: OnBeforeBuildFn) => void;
onAfterBuild: (fn: OnAfterBuildFn) => void;
onCloseBuild: (fn: OnCloseBuildFn) => void;
onBeforeDevCompile: (fn: OnBeforeDevCompileFn) => void;
onAfterDevCompile: (fn: OnAfterDevCompileFn) => void;
onDevCompileDone: (fn: OnDevCompileDoneFn) => void;
onBeforeStartDevServer: (fn: OnBeforeStartDevServerFn) => void;
onAfterStartDevServer: (fn: OnAfterStartDevServerFn) => void;
onCloseDevServer: (fn: OnCloseDevServerFn) => void;
onBeforeCreateCompiler: (fn: OnBeforeCreateCompilerFn) => void;
onAfterCreateCompiler: (fn: OnAfterCreateCompilerFn) => void;
onExit: (fn: OnExitFn) => void;
}Modify the main Rsbuild configuration object.
/**
* Hook to modify Rsbuild configuration
*/
interface ModifyRsbuildConfigHook {
(fn: ModifyRsbuildConfigFn): void;
}
type ModifyRsbuildConfigFn = (
config: RsbuildConfig,
utils: ModifyRsbuildConfigUtils
) => RsbuildConfig | void | Promise<RsbuildConfig | void>;
interface ModifyRsbuildConfigUtils {
mergeConfig: typeof mergeRsbuildConfig;
}Usage Examples:
const configPlugin = (): RsbuildPlugin => ({
name: "config-plugin",
setup(api) {
api.modifyRsbuildConfig((config, { mergeConfig }) => {
// Direct modification
config.output = config.output || {};
config.output.assetPrefix = "/static/";
// Using merge utility
return mergeConfig(config, {
source: {
alias: {
"@utils": "./src/utils",
},
},
performance: {
chunkSplit: {
strategy: "split-by-experience",
},
},
});
});
},
});Modify environment-specific configuration.
/**
* Hook to modify environment-specific configuration
*/
interface ModifyEnvironmentConfigHook {
(fn: ModifyEnvironmentConfigFn): void;
}
type ModifyEnvironmentConfigFn = (
config: EnvironmentConfig,
utils: ModifyEnvironmentConfigUtils
) => EnvironmentConfig | void | Promise<EnvironmentConfig | void>;
interface ModifyEnvironmentConfigUtils {
/** Environment name */
name: string;
/** Merge configuration utility */
mergeConfig: (config: EnvironmentConfig) => EnvironmentConfig;
}Modify bundler configuration using rspack-chain or webpack-chain.
/**
* Hook to modify bundler configuration via chain API
*/
interface ModifyBundlerChainHook {
(fn: ModifyBundlerChainFn): void;
}
type ModifyBundlerChainFn = (
chain: RspackChain,
utils: ModifyBundlerChainUtils
) => void | Promise<void>;
interface ModifyBundlerChainUtils {
env: string;
target: RsbuildTarget;
isDev: boolean;
isProd: boolean;
CHAIN_ID: ChainIdentifier;
bundler: BundlerType;
getCompiledPath: (name: string) => string;
}
type BundlerType = 'rspack' | 'webpack';Usage Examples:
const chainPlugin = (): RsbuildPlugin => ({
name: "chain-plugin",
setup(api) {
api.modifyBundlerChain((chain, { CHAIN_ID }) => {
// Add a new loader
chain.module
.rule("my-rule")
.test(/\.special$/)
.use("my-loader")
.loader("my-loader")
.options({
customOption: true,
});
// Modify existing plugin
chain.plugin(CHAIN_ID.PLUGIN.HTML).tap((args) => {
args[0].title = "My App";
return args;
});
// Add environment-specific configuration
if (utils.isDev) {
chain.devtool("eval-cheap-module-source-map");
}
});
},
});Directly modify the Rspack configuration object.
/**
* Hook to modify Rspack configuration directly
*/
interface ModifyRspackConfigHook {
(fn: ModifyRspackConfigFn): void;
}
type ModifyRspackConfigFn = (
config: RspackConfig,
utils: ModifyRspackConfigUtils
) => RspackConfig | void | Promise<RspackConfig | void>;
interface ModifyRspackConfigUtils {
env: string;
target: RsbuildTarget;
isDev: boolean;
isProd: boolean;
rspack: typeof import('@rspack/core');
}Modify the final HTML content before output.
/**
* Hook to modify HTML content
*/
interface ModifyHTMLHook {
(fn: ModifyHTMLFn): void;
}
type ModifyHTMLFn = (
html: string,
context: ModifyHTMLContext
) => string | Promise<string>;
interface ModifyHTMLContext {
assetPrefix: string;
filename: string;
environment: EnvironmentContext;
}Modify HTML tags that are injected into the HTML.
/**
* Hook to modify HTML tags
*/
interface ModifyHTMLTagsHook {
(fn: ModifyHTMLTagsFn): void;
}
type ModifyHTMLTagsFn = (
tags: HtmlTag[],
context: ModifyHTMLTagsContext
) => HtmlTag[] | Promise<HtmlTag[]>;
interface ModifyHTMLTagsContext {
assetPrefix: string;
filename: string;
environment: EnvironmentContext;
}
interface HtmlTag {
tag: string;
attrs?: Record<string, string | boolean | undefined>;
children?: string;
hash?: boolean | string;
}Usage Examples:
const htmlPlugin = (): RsbuildPlugin => ({
name: "html-plugin",
setup(api) {
// Modify HTML content
api.modifyHTML((html, { filename }) => {
if (filename.includes("index")) {
return html.replace(
"<head>",
"<head>\n <meta name=\"custom\" content=\"value\">"
);
}
return html;
});
// Modify HTML tags
api.modifyHTMLTags((tags, { environment }) => {
// Add custom script tag
tags.push({
tag: "script",
attrs: {
src: "/analytics.js",
async: true,
},
});
// Add environment-specific tags
if (environment.name === "production") {
tags.push({
tag: "meta",
attrs: {
name: "robots",
content: "index,follow",
},
});
}
return tags;
});
},
});Transform module code during the build process.
/**
* Hook to transform module code
*/
interface TransformHook {
(descriptor: TransformDescriptor, handler: TransformHandler): void;
}
interface TransformDescriptor {
/** File pattern to match */
test?: RegExp | ((id: string) => boolean);
/** Target environments */
targets?: RsbuildTarget[];
/** Transform order */
order?: 'pre' | 'post';
}
type TransformHandler = (
context: TransformContext
) => TransformResult | Promise<TransformResult>;
interface TransformContext {
/** Module code */
code: string;
/** Module resource path with query */
resource: string;
/** Module resource path without query */
resourcePath: string;
/** Environment context */
environment: EnvironmentContext;
/** Add file dependency */
addDependency: (file: string) => void;
/** Emit file to output */
emitFile: (name: string, content: string) => void;
}
interface TransformResult {
/** Transformed code */
code: string;
/** Source map */
map?: string | object;
}Usage Examples:
const transformPlugin = (): RsbuildPlugin => ({
name: "transform-plugin",
setup(api) {
// Transform TypeScript files
api.transform(
{ test: /\.ts$/ },
async ({ code, resourcePath }) => {
// Add custom header to all TypeScript files
const header = `// Generated by custom plugin\n`;
return {
code: header + code,
};
}
);
// Transform specific file pattern
api.transform(
{
test: (id) => id.includes("api/"),
targets: ["web"],
},
({ code, addDependency }) => {
// Add runtime dependency
addDependency("./runtime-helpers.js");
// Transform API files
const transformedCode = code.replace(
/process\.env\.API_URL/g,
'"https://api.example.com"'
);
return { code: transformedCode };
}
);
},
});Process build assets before emission.
/**
* Hook to process assets before emission
*/
interface ProcessAssetsHook {
(descriptor: ProcessAssetsDescriptor, handler: ProcessAssetsHandler): void;
}
interface ProcessAssetsDescriptor {
/** Processing stage */
stage?: 'optimize' | 'optimize-count' | 'optimize-compatibility' | 'optimize-size' | 'dev-tooling' | 'optimize-inline' | 'summarize' | 'report';
}
type ProcessAssetsHandler = (
assets: Record<string, Source>
) => void | Promise<void>;
interface Source {
source(): string | Buffer;
size(): number;
map(): object | null;
}Intercept and modify module resolution.
/**
* Hook to intercept module resolution
*/
interface ResolveHook {
(handler: ResolveHandler): void;
}
type ResolveHandler = (
data: ResolveData
) => ResolveResult | void | Promise<ResolveResult | void>;
interface ResolveData {
/** Module request */
request: string;
/** Context directory */
context: string;
/** Import kind */
kind: 'entry' | 'import' | 'require' | 'dynamic-import';
}
interface ResolveResult {
/** Resolved path */
path?: string;
/** External module */
external?: boolean;
}Expose plugin APIs for use by other plugins.
/**
* Expose plugin API for use by other plugins
* @param id - Unique identifier for the exposed API
* @param api - API object to expose
*/
expose<T = any>(id: string, api: T): void;Use APIs exposed by other plugins.
/**
* Use API exposed by another plugin
* @param id - Identifier of the exposed API
* @returns The exposed API or undefined if not found
*/
useExposed<T = any>(id: string): T | undefined;Usage Examples:
// Plugin that exposes an API
const providerPlugin = (): RsbuildPlugin => ({
name: "provider-plugin",
setup(api) {
const sharedAPI = {
getConfig: () => ({ theme: "dark" }),
transform: (input: string) => input.toUpperCase(),
};
api.expose("provider-api", sharedAPI);
},
});
// Plugin that uses the exposed API
const consumerPlugin = (): RsbuildPlugin => ({
name: "consumer-plugin",
enforce: "post", // Run after provider
setup(api) {
const providerAPI = api.useExposed<{
getConfig: () => any;
transform: (input: string) => string;
}>("provider-api");
if (providerAPI) {
const config = providerAPI.getConfig();
console.log("Theme:", config.theme);
api.modifyRsbuildConfig((config) => {
// Use the shared API
const transformed = providerAPI.transform("hello");
config.source = config.source || {};
config.source.define = {
...config.source.define,
TRANSFORMED_VALUE: `"${transformed}"`,
};
});
}
},
});Predefined identifiers for common configuration elements when using the chain API.
interface ChainIdentifier {
PLUGIN: {
HTML: string;
APP_ICON: string;
INLINE_CHUNK: string;
BUNDLE_ANALYZER: string;
// ... more plugin identifiers
};
RULE: {
JS: string;
TS: string;
CSS: string;
SASS: string;
LESS: string;
// ... more rule identifiers
};
USE: {
SWC: string;
CSS: string;
POSTCSS: string;
// ... more use identifiers
};
}const PLUGIN_CSS_NAME = 'rsbuild:css';
const PLUGIN_SWC_NAME = 'rsbuild:swc';type RsbuildPlugins = (RsbuildPlugin | RsbuildPluginOptions)[];
interface RsbuildPluginOptions {
plugin: RsbuildPlugin;
options?: any;
}Usage Examples:
import { defineConfig } from "@rsbuild/core";
import { myPlugin, anotherPlugin } from "./plugins";
export default defineConfig({
plugins: [
myPlugin(),
anotherPlugin({ option: "value" }),
// Conditional plugin
process.env.NODE_ENV === "development" && devPlugin(),
// Plugin with options
{
plugin: complexPlugin(),
options: {
advanced: true,
},
},
].filter(Boolean), // Remove falsy values
});Install with Tessl CLI
npx tessl i tessl/npm-rsbuild--core