or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-integration.mdconfiguration-management.mdcore-build-system.mddevelopment-server.mdenvironment-system.mdenvironment-variables.mdindex.mdplugin-system.md

plugin-system.mddocs/

0

# Plugin System

1

2

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.

3

4

## Capabilities

5

6

### Plugin Definition

7

8

Define plugins with lifecycle hooks and configuration modification capabilities.

9

10

```typescript { .api }

11

/**

12

* Plugin interface for extending Rsbuild functionality

13

*/

14

interface RsbuildPlugin {

15

/** Unique plugin identifier */

16

name: string;

17

/** Plugin setup function called during initialization */

18

setup: (api: RsbuildPluginAPI) => MaybePromise<void>;

19

/** Conditional application based on command or context */

20

apply?: 'serve' | 'build' | ((config: any, context: any) => boolean);

21

/** Execution order enforcement */

22

enforce?: 'pre' | 'post';

23

/** Plugins that must run before this plugin */

24

pre?: string[];

25

/** Plugins that must run after this plugin */

26

post?: string[];

27

/** Plugin names to remove from the plugin list */

28

remove?: string[];

29

}

30

31

type MaybePromise<T> = T | Promise<T>;

32

```

33

34

**Usage Examples:**

35

36

```typescript

37

import type { RsbuildPlugin } from "@rsbuild/core";

38

39

// Basic plugin

40

const myPlugin = (): RsbuildPlugin => ({

41

name: "my-plugin",

42

setup(api) {

43

api.modifyRsbuildConfig((config) => {

44

config.source = config.source || {};

45

config.source.alias = {

46

...config.source.alias,

47

"@components": "./src/components",

48

};

49

});

50

},

51

});

52

53

// Conditional plugin (dev only)

54

const devOnlyPlugin = (): RsbuildPlugin => ({

55

name: "dev-only-plugin",

56

apply: "serve",

57

setup(api) {

58

api.onAfterStartDevServer(({ port, urls }) => {

59

console.log(`Dev server running on port ${port}`);

60

});

61

},

62

});

63

64

// Plugin with execution order

65

const criticalPlugin = (): RsbuildPlugin => ({

66

name: "critical-plugin",

67

enforce: "pre",

68

setup(api) {

69

// Runs before other plugins

70

},

71

});

72

73

// Plugin with dependencies

74

const dependentPlugin = (): RsbuildPlugin => ({

75

name: "dependent-plugin",

76

pre: ["rsbuild:css", "rsbuild:html"],

77

setup(api) {

78

// Runs after CSS and HTML plugins

79

},

80

});

81

```

82

83

### Plugin API

84

85

The plugin API provides access to configuration, hooks, and utilities.

86

87

```typescript { .api }

88

/**

89

* API interface provided to plugins during setup

90

*/

91

interface RsbuildPluginAPI {

92

/** Read-only build context */

93

context: RsbuildContext;

94

/** Logging utilities */

95

logger: Logger;

96

97

// Configuration modification hooks

98

modifyRsbuildConfig: ModifyRsbuildConfigHook;

99

modifyEnvironmentConfig: ModifyEnvironmentConfigHook;

100

modifyBundlerChain: ModifyBundlerChainHook;

101

modifyRspackConfig: ModifyRspackConfigHook;

102

modifyWebpackConfig?: ModifyWebpackConfigHook;

103

104

// HTML processing hooks

105

modifyHTML: ModifyHTMLHook;

106

modifyHTMLTags: ModifyHTMLTagsHook;

107

108

// Advanced processing hooks

109

transform: TransformHook;

110

processAssets: ProcessAssetsHook;

111

resolve: ResolveHook;

112

113

// Plugin communication

114

expose: <T = any>(id: string, api: T) => void;

115

useExposed: <T = any>(id: string) => T | undefined;

116

117

// Lifecycle hooks (inherited from RsbuildInstance)

118

onBeforeBuild: (fn: OnBeforeBuildFn) => void;

119

onAfterBuild: (fn: OnAfterBuildFn) => void;

120

onCloseBuild: (fn: OnCloseBuildFn) => void;

121

onBeforeDevCompile: (fn: OnBeforeDevCompileFn) => void;

122

onAfterDevCompile: (fn: OnAfterDevCompileFn) => void;

123

onDevCompileDone: (fn: OnDevCompileDoneFn) => void;

124

onBeforeStartDevServer: (fn: OnBeforeStartDevServerFn) => void;

125

onAfterStartDevServer: (fn: OnAfterStartDevServerFn) => void;

126

onCloseDevServer: (fn: OnCloseDevServerFn) => void;

127

onBeforeCreateCompiler: (fn: OnBeforeCreateCompilerFn) => void;

128

onAfterCreateCompiler: (fn: OnAfterCreateCompilerFn) => void;

129

onExit: (fn: OnExitFn) => void;

130

}

131

```

132

133

## Configuration Modification Hooks

134

135

### Modify Rsbuild Configuration

136

137

Modify the main Rsbuild configuration object.

138

139

```typescript { .api }

140

/**

141

* Hook to modify Rsbuild configuration

142

*/

143

interface ModifyRsbuildConfigHook {

144

(fn: ModifyRsbuildConfigFn): void;

145

}

146

147

type ModifyRsbuildConfigFn = (

148

config: RsbuildConfig,

149

utils: ModifyRsbuildConfigUtils

150

) => RsbuildConfig | void | Promise<RsbuildConfig | void>;

151

152

interface ModifyRsbuildConfigUtils {

153

mergeConfig: typeof mergeRsbuildConfig;

154

}

155

```

156

157

**Usage Examples:**

158

159

```typescript

160

const configPlugin = (): RsbuildPlugin => ({

161

name: "config-plugin",

162

setup(api) {

163

api.modifyRsbuildConfig((config, { mergeConfig }) => {

164

// Direct modification

165

config.output = config.output || {};

166

config.output.assetPrefix = "/static/";

167

168

// Using merge utility

169

return mergeConfig(config, {

170

source: {

171

alias: {

172

"@utils": "./src/utils",

173

},

174

},

175

performance: {

176

chunkSplit: {

177

strategy: "split-by-experience",

178

},

179

},

180

});

181

});

182

},

183

});

184

```

185

186

### Modify Environment Configuration

187

188

Modify environment-specific configuration.

189

190

```typescript { .api }

191

/**

192

* Hook to modify environment-specific configuration

193

*/

194

interface ModifyEnvironmentConfigHook {

195

(fn: ModifyEnvironmentConfigFn): void;

196

}

197

198

type ModifyEnvironmentConfigFn = (

199

config: EnvironmentConfig,

200

utils: ModifyEnvironmentConfigUtils

201

) => EnvironmentConfig | void | Promise<EnvironmentConfig | void>;

202

203

interface ModifyEnvironmentConfigUtils {

204

/** Environment name */

205

name: string;

206

/** Merge configuration utility */

207

mergeConfig: (config: EnvironmentConfig) => EnvironmentConfig;

208

}

209

```

210

211

### Modify Bundler Chain

212

213

Modify bundler configuration using rspack-chain or webpack-chain.

214

215

```typescript { .api }

216

/**

217

* Hook to modify bundler configuration via chain API

218

*/

219

interface ModifyBundlerChainHook {

220

(fn: ModifyBundlerChainFn): void;

221

}

222

223

type ModifyBundlerChainFn = (

224

chain: RspackChain,

225

utils: ModifyBundlerChainUtils

226

) => void | Promise<void>;

227

228

interface ModifyBundlerChainUtils {

229

env: string;

230

target: RsbuildTarget;

231

isDev: boolean;

232

isProd: boolean;

233

CHAIN_ID: ChainIdentifier;

234

bundler: BundlerType;

235

getCompiledPath: (name: string) => string;

236

}

237

238

type BundlerType = 'rspack' | 'webpack';

239

```

240

241

**Usage Examples:**

242

243

```typescript

244

const chainPlugin = (): RsbuildPlugin => ({

245

name: "chain-plugin",

246

setup(api) {

247

api.modifyBundlerChain((chain, { CHAIN_ID }) => {

248

// Add a new loader

249

chain.module

250

.rule("my-rule")

251

.test(/\.special$/)

252

.use("my-loader")

253

.loader("my-loader")

254

.options({

255

customOption: true,

256

});

257

258

// Modify existing plugin

259

chain.plugin(CHAIN_ID.PLUGIN.HTML).tap((args) => {

260

args[0].title = "My App";

261

return args;

262

});

263

264

// Add environment-specific configuration

265

if (utils.isDev) {

266

chain.devtool("eval-cheap-module-source-map");

267

}

268

});

269

},

270

});

271

```

272

273

### Modify Rspack Configuration

274

275

Directly modify the Rspack configuration object.

276

277

```typescript { .api }

278

/**

279

* Hook to modify Rspack configuration directly

280

*/

281

interface ModifyRspackConfigHook {

282

(fn: ModifyRspackConfigFn): void;

283

}

284

285

type ModifyRspackConfigFn = (

286

config: RspackConfig,

287

utils: ModifyRspackConfigUtils

288

) => RspackConfig | void | Promise<RspackConfig | void>;

289

290

interface ModifyRspackConfigUtils {

291

env: string;

292

target: RsbuildTarget;

293

isDev: boolean;

294

isProd: boolean;

295

rspack: typeof import('@rspack/core');

296

}

297

```

298

299

## HTML Processing Hooks

300

301

### Modify HTML Content

302

303

Modify the final HTML content before output.

304

305

```typescript { .api }

306

/**

307

* Hook to modify HTML content

308

*/

309

interface ModifyHTMLHook {

310

(fn: ModifyHTMLFn): void;

311

}

312

313

type ModifyHTMLFn = (

314

html: string,

315

context: ModifyHTMLContext

316

) => string | Promise<string>;

317

318

interface ModifyHTMLContext {

319

assetPrefix: string;

320

filename: string;

321

environment: EnvironmentContext;

322

}

323

```

324

325

### Modify HTML Tags

326

327

Modify HTML tags that are injected into the HTML.

328

329

```typescript { .api }

330

/**

331

* Hook to modify HTML tags

332

*/

333

interface ModifyHTMLTagsHook {

334

(fn: ModifyHTMLTagsFn): void;

335

}

336

337

type ModifyHTMLTagsFn = (

338

tags: HtmlTag[],

339

context: ModifyHTMLTagsContext

340

) => HtmlTag[] | Promise<HtmlTag[]>;

341

342

interface ModifyHTMLTagsContext {

343

assetPrefix: string;

344

filename: string;

345

environment: EnvironmentContext;

346

}

347

348

interface HtmlTag {

349

tag: string;

350

attrs?: Record<string, string | boolean | undefined>;

351

children?: string;

352

hash?: boolean | string;

353

}

354

```

355

356

**Usage Examples:**

357

358

```typescript

359

const htmlPlugin = (): RsbuildPlugin => ({

360

name: "html-plugin",

361

setup(api) {

362

// Modify HTML content

363

api.modifyHTML((html, { filename }) => {

364

if (filename.includes("index")) {

365

return html.replace(

366

"<head>",

367

"<head>\n <meta name=\"custom\" content=\"value\">"

368

);

369

}

370

return html;

371

});

372

373

// Modify HTML tags

374

api.modifyHTMLTags((tags, { environment }) => {

375

// Add custom script tag

376

tags.push({

377

tag: "script",

378

attrs: {

379

src: "/analytics.js",

380

async: true,

381

},

382

});

383

384

// Add environment-specific tags

385

if (environment.name === "production") {

386

tags.push({

387

tag: "meta",

388

attrs: {

389

name: "robots",

390

content: "index,follow",

391

},

392

});

393

}

394

395

return tags;

396

});

397

},

398

});

399

```

400

401

## Advanced Processing Hooks

402

403

### Transform Hook

404

405

Transform module code during the build process.

406

407

```typescript { .api }

408

/**

409

* Hook to transform module code

410

*/

411

interface TransformHook {

412

(descriptor: TransformDescriptor, handler: TransformHandler): void;

413

}

414

415

interface TransformDescriptor {

416

/** File pattern to match */

417

test?: RegExp | ((id: string) => boolean);

418

/** Target environments */

419

targets?: RsbuildTarget[];

420

/** Transform order */

421

order?: 'pre' | 'post';

422

}

423

424

type TransformHandler = (

425

context: TransformContext

426

) => TransformResult | Promise<TransformResult>;

427

428

interface TransformContext {

429

/** Module code */

430

code: string;

431

/** Module resource path with query */

432

resource: string;

433

/** Module resource path without query */

434

resourcePath: string;

435

/** Environment context */

436

environment: EnvironmentContext;

437

/** Add file dependency */

438

addDependency: (file: string) => void;

439

/** Emit file to output */

440

emitFile: (name: string, content: string) => void;

441

}

442

443

interface TransformResult {

444

/** Transformed code */

445

code: string;

446

/** Source map */

447

map?: string | object;

448

}

449

```

450

451

**Usage Examples:**

452

453

```typescript

454

const transformPlugin = (): RsbuildPlugin => ({

455

name: "transform-plugin",

456

setup(api) {

457

// Transform TypeScript files

458

api.transform(

459

{ test: /\.ts$/ },

460

async ({ code, resourcePath }) => {

461

// Add custom header to all TypeScript files

462

const header = `// Generated by custom plugin\n`;

463

return {

464

code: header + code,

465

};

466

}

467

);

468

469

// Transform specific file pattern

470

api.transform(

471

{

472

test: (id) => id.includes("api/"),

473

targets: ["web"],

474

},

475

({ code, addDependency }) => {

476

// Add runtime dependency

477

addDependency("./runtime-helpers.js");

478

479

// Transform API files

480

const transformedCode = code.replace(

481

/process\.env\.API_URL/g,

482

'"https://api.example.com"'

483

);

484

485

return { code: transformedCode };

486

}

487

);

488

},

489

});

490

```

491

492

### Process Assets Hook

493

494

Process build assets before emission.

495

496

```typescript { .api }

497

/**

498

* Hook to process assets before emission

499

*/

500

interface ProcessAssetsHook {

501

(descriptor: ProcessAssetsDescriptor, handler: ProcessAssetsHandler): void;

502

}

503

504

interface ProcessAssetsDescriptor {

505

/** Processing stage */

506

stage?: 'optimize' | 'optimize-count' | 'optimize-compatibility' | 'optimize-size' | 'dev-tooling' | 'optimize-inline' | 'summarize' | 'report';

507

}

508

509

type ProcessAssetsHandler = (

510

assets: Record<string, Source>

511

) => void | Promise<void>;

512

513

interface Source {

514

source(): string | Buffer;

515

size(): number;

516

map(): object | null;

517

}

518

```

519

520

### Resolve Hook

521

522

Intercept and modify module resolution.

523

524

```typescript { .api }

525

/**

526

* Hook to intercept module resolution

527

*/

528

interface ResolveHook {

529

(handler: ResolveHandler): void;

530

}

531

532

type ResolveHandler = (

533

data: ResolveData

534

) => ResolveResult | void | Promise<ResolveResult | void>;

535

536

interface ResolveData {

537

/** Module request */

538

request: string;

539

/** Context directory */

540

context: string;

541

/** Import kind */

542

kind: 'entry' | 'import' | 'require' | 'dynamic-import';

543

}

544

545

interface ResolveResult {

546

/** Resolved path */

547

path?: string;

548

/** External module */

549

external?: boolean;

550

}

551

```

552

553

## Plugin Communication

554

555

### Expose API

556

557

Expose plugin APIs for use by other plugins.

558

559

```typescript { .api }

560

/**

561

* Expose plugin API for use by other plugins

562

* @param id - Unique identifier for the exposed API

563

* @param api - API object to expose

564

*/

565

expose<T = any>(id: string, api: T): void;

566

```

567

568

### Use Exposed API

569

570

Use APIs exposed by other plugins.

571

572

```typescript { .api }

573

/**

574

* Use API exposed by another plugin

575

* @param id - Identifier of the exposed API

576

* @returns The exposed API or undefined if not found

577

*/

578

useExposed<T = any>(id: string): T | undefined;

579

```

580

581

**Usage Examples:**

582

583

```typescript

584

// Plugin that exposes an API

585

const providerPlugin = (): RsbuildPlugin => ({

586

name: "provider-plugin",

587

setup(api) {

588

const sharedAPI = {

589

getConfig: () => ({ theme: "dark" }),

590

transform: (input: string) => input.toUpperCase(),

591

};

592

593

api.expose("provider-api", sharedAPI);

594

},

595

});

596

597

// Plugin that uses the exposed API

598

const consumerPlugin = (): RsbuildPlugin => ({

599

name: "consumer-plugin",

600

enforce: "post", // Run after provider

601

setup(api) {

602

const providerAPI = api.useExposed<{

603

getConfig: () => any;

604

transform: (input: string) => string;

605

}>("provider-api");

606

607

if (providerAPI) {

608

const config = providerAPI.getConfig();

609

console.log("Theme:", config.theme);

610

611

api.modifyRsbuildConfig((config) => {

612

// Use the shared API

613

const transformed = providerAPI.transform("hello");

614

config.source = config.source || {};

615

config.source.define = {

616

...config.source.define,

617

TRANSFORMED_VALUE: `"${transformed}"`,

618

};

619

});

620

}

621

},

622

});

623

```

624

625

## Chain Identifiers

626

627

Predefined identifiers for common configuration elements when using the chain API.

628

629

```typescript { .api }

630

interface ChainIdentifier {

631

PLUGIN: {

632

HTML: string;

633

APP_ICON: string;

634

INLINE_CHUNK: string;

635

BUNDLE_ANALYZER: string;

636

// ... more plugin identifiers

637

};

638

RULE: {

639

JS: string;

640

TS: string;

641

CSS: string;

642

SASS: string;

643

LESS: string;

644

// ... more rule identifiers

645

};

646

USE: {

647

SWC: string;

648

CSS: string;

649

POSTCSS: string;

650

// ... more use identifiers

651

};

652

}

653

```

654

655

## Built-in Plugin Names

656

657

```typescript { .api }

658

const PLUGIN_CSS_NAME = 'rsbuild:css';

659

const PLUGIN_SWC_NAME = 'rsbuild:swc';

660

```

661

662

## Plugin Registration

663

664

```typescript { .api }

665

type RsbuildPlugins = (RsbuildPlugin | RsbuildPluginOptions)[];

666

667

interface RsbuildPluginOptions {

668

plugin: RsbuildPlugin;

669

options?: any;

670

}

671

```

672

673

**Usage Examples:**

674

675

```typescript

676

import { defineConfig } from "@rsbuild/core";

677

import { myPlugin, anotherPlugin } from "./plugins";

678

679

export default defineConfig({

680

plugins: [

681

myPlugin(),

682

anotherPlugin({ option: "value" }),

683

684

// Conditional plugin

685

process.env.NODE_ENV === "development" && devPlugin(),

686

687

// Plugin with options

688

{

689

plugin: complexPlugin(),

690

options: {

691

advanced: true,

692

},

693

},

694

].filter(Boolean), // Remove falsy values

695

});

696

```