diff --git a/README.md b/README.md
index 5d3d8001..079a17a1 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ Currently supports:
- [Webpack](https://webpack.js.org/)
- [esbuild](https://esbuild.github.io/)
- [Rspack](https://www.rspack.dev/)
+- [Rsbuild](https://rsbuild.rs/)
- [Rolldown](https://rolldown.rs/)
- [Farm](https://www.farmfe.org/)
- [Bun](https://bun.com/)
diff --git a/docs/.vitepress/assets/rsbuild.svg b/docs/.vitepress/assets/rsbuild.svg
new file mode 100644
index 00000000..78a54f23
--- /dev/null
+++ b/docs/.vitepress/assets/rsbuild.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/.vitepress/assets/rspack.svg b/docs/.vitepress/assets/rspack.svg
index a1342a58..545f982d 100644
--- a/docs/.vitepress/assets/rspack.svg
+++ b/docs/.vitepress/assets/rspack.svg
@@ -1 +1 @@
-
+
\ No newline at end of file
diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css
index ff8bdd9c..3b771100 100644
--- a/docs/.vitepress/theme/style.css
+++ b/docs/.vitepress/theme/style.css
@@ -102,10 +102,6 @@ img[src*="features"] {
width: 48px;
}
-img[src*="/features/rspack"] {
- margin-bottom: 28px!important;
-}
-
details > summary:hover {
cursor: pointer;
user-select: none;
diff --git a/docs/guide/index.md b/docs/guide/index.md
index 82f8311c..822f1578 100644
--- a/docs/guide/index.md
+++ b/docs/guide/index.md
@@ -16,6 +16,7 @@ lastUpdated: false
- [webpack](https://webpack.js.org/)
- [esbuild](https://esbuild.github.io/)
- [Rspack](https://www.rspack.dev/)
+- [Rsbuild](https://rsbuild.rs/)
- [Rolldown](https://rolldown.rs/)
- [Farm](https://www.farmfe.org/)
- [Bun](https://bun.com/)
@@ -131,6 +132,20 @@ module.exports = {
}
```
+```ts [Rsbuild]
+// rsbuild.config.ts
+import { defineConfig } from '@rsbuild/core'
+import Starter from 'unplugin-starter/rsbuild'
+
+export default defineConfig({
+ plugins: [
+ Starter({
+ /* options */
+ }),
+ ],
+})
+```
+
```js [esbuild]
// esbuild.config.js
import { build } from 'esbuild'
@@ -209,18 +224,18 @@ export default defineConfig({
## Supported Hooks
-| Hook | Rollup | Vite | webpack | esbuild | Rspack | Farm | Rolldown | Bun |
-| --------------------------------------------------------------------------------- | :-------------: | :--: | :-----: | :-------------: | :-------------: | :--: | :------: | :-------------: |
-| [`enforce`](https://vite.dev/guide/api-plugin.html#plugin-ordering) | ❌ 1 | ✅ | ✅ | ❌ 1 | ✅ | ✅ | ✅ | ❌ |
-| [`buildStart`](https://rollupjs.org/plugin-development/#buildstart) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
-| [`resolveId`](https://rollupjs.org/plugin-development/#resolveid) | ✅ | ✅ | ✅ | ✅ | ✅ 5 | ✅ | ✅ | ✅ |
-| ~~`loadInclude`~~2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
-| [`load`](https://rollupjs.org/plugin-development/#load) | ✅ | ✅ | ✅ | ✅ 3 | ✅ | ✅ | ✅ | ✅ |
-| ~~`transformInclude`~~2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
-| [`transform`](https://rollupjs.org/plugin-development/#transform) | ✅ | ✅ | ✅ | ✅ 3 | ✅ | ✅ | ✅ | ✅ |
-| [`watchChange`](https://rollupjs.org/plugin-development/#watchchange) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |
-| [`buildEnd`](https://rollupjs.org/plugin-development/#buildend) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 6 |
-| [`writeBundle`](https://rollupjs.org/plugin-development/#writebundle)4 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 6 |
+| Hook | Rollup | Vite | webpack | esbuild | Rspack | Rsbuild | Farm | Rolldown | Bun |
+| --------------------------------------------------------------------------------- | :-------------: | :--: | :-----: | :-------------: | :-------------: | :-------------: | :--: | :------: | :-------------: |
+| [`enforce`](https://vite.dev/guide/api-plugin.html#plugin-ordering) | ❌ 1 | ✅ | ✅ | ❌ 1 | ✅ | ✅ | ✅ | ✅ | ❌ |
+| [`buildStart`](https://rollupjs.org/plugin-development/#buildstart) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`resolveId`](https://rollupjs.org/plugin-development/#resolveid) | ✅ | ✅ | ✅ | ✅ | ✅ 5 | ✅ 5 | ✅ | ✅ | ✅ |
+| ~~`loadInclude`~~2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`load`](https://rollupjs.org/plugin-development/#load) | ✅ | ✅ | ✅ | ✅ 3 | ✅ | ✅ | ✅ | ✅ | ✅ |
+| ~~`transformInclude`~~2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`transform`](https://rollupjs.org/plugin-development/#transform) | ✅ | ✅ | ✅ | ✅ 3 | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`watchChange`](https://rollupjs.org/plugin-development/#watchchange) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
+| [`buildEnd`](https://rollupjs.org/plugin-development/#buildend) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 6 |
+| [`writeBundle`](https://rollupjs.org/plugin-development/#writebundle)4 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ 6 |
::: details Notice
@@ -230,7 +245,7 @@ export default defineConfig({
In Rollup, this hook has been polyfilled to match the behaviors. See the following usage examples for reference.
3. Although esbuild can handle both JavaScript and CSS and many other file formats, you can only return JavaScript in `load` and `transform` results.
4. Currently, `writeBundle` is only serves as a hook for the timing. It doesn't pass any arguments.
-5. Rspack supports `resolveId` with a minimum required version of v1.0.0-alpha.1.
+5. Rspack and Rsbuild support `resolveId` with a minimum required Rspack version of v1.0.0-alpha.1.
6. Bun supports `buildEnd` and `writeBundle` with a minimum required version of v1.3.0.
:::
@@ -268,6 +283,7 @@ export const rollupPlugin = unplugin.rollup
export const rolldownPlugin = unplugin.rolldown
export const webpackPlugin = unplugin.webpack
export const rspackPlugin = unplugin.rspack
+export const rsbuildPlugin = unplugin.rsbuild
export const esbuildPlugin = unplugin.esbuild
export const farmPlugin = unplugin.farm
export const bunPlugin = unplugin.bun
@@ -307,14 +323,14 @@ More details can be found in the [Rolldown's documentation](https://rolldown.rs/
## Supported Context
-| Context | Rollup | Vite | webpack | esbuild | Rspack | Farm | Rolldown | Bun |
-| ------------------------------------------------------------------------------------- | :----: | :--: | :-----: | :-----: | :----: | :--: | :------: | :-: |
-| [`this.parse`](https://rollupjs.org/plugin-development/#this-parse)1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
-| [`this.addWatchFile`](https://rollupjs.org/plugin-development/#this-addwatchfile) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
-| [`this.emitFile`](https://rollupjs.org/plugin-development/#this-emitfile)2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
-| [`this.getWatchFiles`](https://rollupjs.org/plugin-development/#this-getwatchfiles) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
-| [`this.warn`](https://rollupjs.org/plugin-development/#this-warn) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
-| [`this.error`](https://rollupjs.org/plugin-development/#this-error) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Context | Rollup | Vite | webpack | esbuild | Rspack | Rsbuild | Farm | Rolldown | Bun |
+| ------------------------------------------------------------------------------------- | :----: | :--: | :-----: | :-----: | :----: | :-----: | :--: | :------: | :-: |
+| [`this.parse`](https://rollupjs.org/plugin-development/#this-parse)1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`this.addWatchFile`](https://rollupjs.org/plugin-development/#this-addwatchfile) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`this.emitFile`](https://rollupjs.org/plugin-development/#this-emitfile)2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`this.getWatchFiles`](https://rollupjs.org/plugin-development/#this-getwatchfiles) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`this.warn`](https://rollupjs.org/plugin-development/#this-warn) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| [`this.error`](https://rollupjs.org/plugin-development/#this-error) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
::: info Notice
@@ -329,9 +345,9 @@ More details can be found in the [Rolldown's documentation](https://rolldown.rs/
### Bundler Supported
-| Rollup | Vite | webpack | Rspack | esbuild | Farm | Rolldown | Bun |
-| :--------------------: | :--: | :-----: | :----: | :-----: | :--: | :------: | :-: |
-| ✅ `>=3.1`1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Rollup | Vite | webpack | Rspack | Rsbuild | esbuild | Farm | Rolldown | Bun |
+| :--------------------: | :--: | :-----: | :----: | :-----: | :-----: | :--: | :------: | :-: |
+| ✅ `>=3.1`1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
::: details Notice
@@ -386,7 +402,7 @@ export const unpluginFactory: UnpluginFactory = (
options,
meta,
) => {
- console.log(meta.framework) // vite | rollup | webpack | esbuild | rspack | farm | bun
+ console.log(meta.framework) // vite | rollup | webpack | esbuild | rspack | rsbuild | farm | bun
return {
name: 'unplugin-starter',
@@ -417,6 +433,12 @@ export const unpluginFactory: UnpluginFactory = (
rspack(compiler) {
// Configure Rspack compiler
},
+ rsbuild: {
+ // Rsbuild plugin
+ setup(api) {
+ // Configure Rsbuild
+ },
+ },
esbuild: {
// Change the filter of onResolve and onLoad
// onResolveFilter?: RegExp,
@@ -464,19 +486,26 @@ export const unpluginFactory = defineUnplugin((options, meta) => {
const bundlerVersion = meta.versions.rollup ?? meta.versions.rolldown
console.log(`Vite is using bundler version: ${bundlerVersion}`)
}
+
+ // For Rsbuild, you can also access the underlying Rspack version
+ if (meta.framework === 'rsbuild') {
+ const rspackVersion = meta.versions.rspack
+ console.log(`Rsbuild is using Rspack version: ${rspackVersion}`)
+ }
}
}
})
```
-| Rollup | Vite | webpack | Rspack | esbuild | Farm | Rolldown | Unloader | Bun |
-| :------------: | :--------------: | :-----: | :----: | :-----: | :--: | :------------: | :------------: | :-: |
-| ✅1 | ✅1,2 | ✅ | ✅ | ❌ | ❌ | ✅1 | ✅1 | ✅ |
+| Rollup | Vite | webpack | Rspack | Rsbuild | esbuild | Farm | Rolldown | Unloader | Bun |
+| :------------: | :--------------: | :-----: | :----: | :------------: | :-----: | :--: | :------------: | :------------: | :-: |
+| ✅1 | ✅1,2 | ✅ | ✅ | ✅3 | ❌ | ❌ | ✅1 | ✅1 | ✅ |
::: details Notice
1. For Rollup-compatible hosts (`vite`, `rollup`, `rolldown`, `unloader`), framework versions are only available after the `buildStart` hook. The `unplugin` version is always available.
2. Vite requires **v7.0.0 or later** to provide `viteVersion` ([vitejs/vite#20088](https://github.com/vitejs/vite/pull/20088)). On earlier versions, `meta.versions.vite` will be `undefined`. Vite also provides the underlying bundler version (`rollup` or `rolldown`).
+3. Rsbuild also provides the underlying Rspack version via `meta.versions.rspack`.
:::
@@ -492,6 +521,7 @@ import {
createFarmPlugin,
createRolldownPlugin,
createRollupPlugin,
+ createRsbuildPlugin,
createRspackPlugin,
createVitePlugin,
createWebpackPlugin,
@@ -503,6 +533,7 @@ const rolldownPlugin = createRolldownPlugin(/* factory */)
const esbuildPlugin = createEsbuildPlugin(/* factory */)
const webpackPlugin = createWebpackPlugin(/* factory */)
const rspackPlugin = createRspackPlugin(/* factory */)
+const rsbuildPlugin = createRsbuildPlugin(/* factory */)
const farmPlugin = createFarmPlugin(/* factory */)
const bunPlugin = createBunPlugin(/* factory */)
```
diff --git a/docs/index.md b/docs/index.md
index 663cbcfc..f4d41c2e 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -5,7 +5,7 @@ sidebar: false
hero:
name: Unplugin
text: The Unified
Plugin System
- tagline: Supports Vite, Rollup, webpack, esbuild, Bun, and every framework built on top of them.
+ tagline: Supports Vite, Rollup, webpack, Rspack, Rsbuild, esbuild, Bun, and every framework built on top of them.
image:
light: /logo_light.svg
dark: /logo_dark.svg
@@ -50,7 +50,13 @@ features:
details: A fast Rust-based web bundler.
link: https://www.rspack.dev/
icon:
- src: /features/rspack.png
+ src: /features/rspack.svg
+
+ - title: Rsbuild
+ details: A Rspack-based build tool.
+ link: https://rsbuild.rs/
+ icon:
+ src: /features/rsbuild.svg
- title: Farm
details: Extremely fast web build tool written in Rust
diff --git a/docs/public/features/rsbuild.svg b/docs/public/features/rsbuild.svg
new file mode 100644
index 00000000..78a54f23
--- /dev/null
+++ b/docs/public/features/rsbuild.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/public/features/rspack.png b/docs/public/features/rspack.png
deleted file mode 100644
index c59eb6fd..00000000
Binary files a/docs/public/features/rspack.png and /dev/null differ
diff --git a/docs/public/features/rspack.svg b/docs/public/features/rspack.svg
new file mode 100644
index 00000000..545f982d
--- /dev/null
+++ b/docs/public/features/rspack.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/vite.config.ts b/docs/vite.config.ts
index 508277ee..49ddaeb3 100644
--- a/docs/vite.config.ts
+++ b/docs/vite.config.ts
@@ -20,6 +20,7 @@ export default defineConfig({
customIcon: {
farm: localIconLoader(import.meta.url, '.vitepress/assets/farm.svg'),
rolldown: localIconLoader(import.meta.url, '.vitepress/assets/rolldown.svg'),
+ rsbuild: localIconLoader(import.meta.url, '.vitepress/assets/rsbuild.svg'),
rspack: localIconLoader(import.meta.url, '.vitepress/assets/rspack.svg'),
},
}),
diff --git a/package.json b/package.json
index a6417d84..7dca81cf 100644
--- a/package.json
+++ b/package.json
@@ -97,6 +97,7 @@
"@arethetypeswrong/core": "catalog:dev",
"@farmfe/cli": "catalog:test",
"@farmfe/core": "catalog:peer",
+ "@rsbuild/core": "catalog:peer",
"@rspack/cli": "catalog:test",
"@rspack/core": "catalog:peer",
"@types/node": "catalog:dev",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b96fecfa..6f505583 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -107,6 +107,9 @@ catalogs:
'@farmfe/core':
specifier: ^1.7.11
version: 1.7.11
+ '@rsbuild/core':
+ specifier: ^2.0.14
+ version: 2.1.1
'@rspack/core':
specifier: ^2.0.8
version: 2.0.8
@@ -187,12 +190,15 @@ importers:
'@farmfe/core':
specifier: catalog:peer
version: 1.7.11(@types/node@26.0.0)
+ '@rsbuild/core':
+ specifier: catalog:peer
+ version: 2.1.1(core-js@3.49.0)
'@rspack/cli':
specifier: catalog:test
- version: 2.0.8(@rspack/core@2.0.8)
+ version: 2.0.8(@rspack/core@2.0.8(@swc/helpers@0.5.23))
'@rspack/core':
specifier: catalog:peer
- version: 2.0.8
+ version: 2.0.8(@swc/helpers@0.5.23)
'@types/node':
specifier: catalog:dev
version: 26.0.0
@@ -1744,62 +1750,140 @@ packages:
cpu: [x64]
os: [win32]
+ '@rsbuild/core@2.1.1':
+ resolution: {integrity: sha512-s6Cc+pHCJK9RP6Yk4LwsQWHxxxasrk5kmo9nkZK7j1iObp4w2PLivsUdP6HtkeoXuxcnK3QKH8/r49plNBjaQw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ core-js: '>= 3.0.0'
+ peerDependenciesMeta:
+ core-js:
+ optional: true
+
'@rspack/binding-darwin-arm64@2.0.8':
resolution: {integrity: sha512-vCgbgH7B7qom+uID+RCZsTCOYFb9wC4/4+1U6rMfytrXGVJ72eNQs2tbdjOl0lb18CT3N/n+VkWynUiLk84GwA==}
cpu: [arm64]
os: [darwin]
+ '@rspack/binding-darwin-arm64@2.1.1':
+ resolution: {integrity: sha512-6K8jA3pZphBwLt5DU78wl0IzY1myHrqNSvFVCd9CHa+szlOwv2iMQpZdYdpupIBxwiZ39rrDyQKtoZw7lsAG8w==}
+ cpu: [arm64]
+ os: [darwin]
+
'@rspack/binding-darwin-x64@2.0.8':
resolution: {integrity: sha512-satPm2PD4B7jDTVlVAdvMVdUszwLvWUEnUDzLb77mvVkezKNDZmuhb+e8s+FfKs8hJpNbZ9VAejuA2rr8o985w==}
cpu: [x64]
os: [darwin]
+ '@rspack/binding-darwin-x64@2.1.1':
+ resolution: {integrity: sha512-RdqtEfIbSe5ucLNVvMac1k88UwmL4Gil8uXqlEFcSZ3VC2jTOkecadd+B0RYqJ+PirSInrRmKm1cr0740zppOA==}
+ cpu: [x64]
+ os: [darwin]
+
'@rspack/binding-linux-arm64-gnu@2.0.8':
resolution: {integrity: sha512-pSI+npPQE/uDtiboqvcOIRJbEV2+B+H1xffmko/gw50la92oTUW60kVULFwsb6L0+GVCzIcwX3yq60GtYIn+Ug==}
cpu: [arm64]
os: [linux]
libc: [glibc]
+ '@rspack/binding-linux-arm64-gnu@2.1.1':
+ resolution: {integrity: sha512-ROKtqXoLpbZO1vYEcNxQg6COvRhdlJcoib1Z4pzG1nIyctEAUuFmnD7pIvJiVe6M7YuJNXW+1p0BhpptT2XEUQ==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
'@rspack/binding-linux-arm64-musl@2.0.8':
resolution: {integrity: sha512-igjJ43yxWQ72GZqjDDZSSHax9/Vg+6rLMmOvFglTJUkQpB4Tyvu/YjW+WRjYj2xRw6blOjLxUSJWASvuSqqlvg==}
cpu: [arm64]
os: [linux]
libc: [musl]
+ '@rspack/binding-linux-arm64-musl@2.1.1':
+ resolution: {integrity: sha512-QdQpG9dK0GfPASjBd/8rUwDNW0ShfFfYJfcoGLNTM6A8EqmgvWwklXDiCVF5dXALbZIksAuRznIKnLDsUoXi9Q==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rspack/binding-linux-riscv64-gnu@2.1.1':
+ resolution: {integrity: sha512-LGdYCAvblZp2qtHk9UseYftIRyaBzSWqEzd1toaS08sYESXERm+YBbrYKKiyq1yXiU1L7BkhMUxiWc3GCYmKcA==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rspack/binding-linux-riscv64-musl@2.1.1':
+ resolution: {integrity: sha512-87phiiPGFT1p4gy3Oz6YdNijCPXdjJy43LFdE8AFiZsV4ChFXQPqnVCF2aMRLybYa51A+9wGYlTvxt09yUQUww==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
'@rspack/binding-linux-x64-gnu@2.0.8':
resolution: {integrity: sha512-zrkoEOnqj1hOEBO5T2I/2Ts2HSJsYFh1qXwMpK4dMJFGGNWDfNeUa6/LF5uq3VINF3JUl7RL47AgrucoSZJXPA==}
cpu: [x64]
os: [linux]
libc: [glibc]
+ '@rspack/binding-linux-x64-gnu@2.1.1':
+ resolution: {integrity: sha512-om0/PvZzxISsnyz3hdmI3xBi3Qsn6faRGrfp6WJpGHa5JYYaRVWz1MJ9fe+/cQE9ON9r32HpMOeuataCLv+Owg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
'@rspack/binding-linux-x64-musl@2.0.8':
resolution: {integrity: sha512-6CtDaGZjNDvJd9TBp7a9zABbrPORO21W96+3ZcGBn0YNUPUk4ARxIxrTTpeJ/1F41QDM8AYIkGDdqEYMqTYBsA==}
cpu: [x64]
os: [linux]
libc: [musl]
+ '@rspack/binding-linux-x64-musl@2.1.1':
+ resolution: {integrity: sha512-YgAyTOM5ZWvWT0Exwcg7QkGUv9PsHT4yIsEyJbH+a99uAKliGMwkHIOP/aJgrCF8G+lTfigY2wyl9cWo4TA4aw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
'@rspack/binding-wasm32-wasi@2.0.8':
resolution: {integrity: sha512-Yf4SiqTUroT5Ju+te0YAY2xxKOb35tECsO21v7hYyGa705wrgoAK/MmF7enOvs9GR1iZIqgiLD/wxsIxl8GjJw==}
cpu: [wasm32]
+ '@rspack/binding-wasm32-wasi@2.1.1':
+ resolution: {integrity: sha512-OicgaB3Dcd+KXsH/I3DkJG7XQyREJScg/jLkCtycFn9BhT0IVJvgi37F8QNJKl8Bp9YZIGehsqK9HpQWav6SLw==}
+ cpu: [wasm32]
+
'@rspack/binding-win32-arm64-msvc@2.0.8':
resolution: {integrity: sha512-8NCuiQsAhXrwRBy57QZoypqrws/zLBkaQVGiB8hksr6v++8hNigNjqpQARLbd0iyMuHsQQ++8+auGk6xlDXmzw==}
cpu: [arm64]
os: [win32]
+ '@rspack/binding-win32-arm64-msvc@2.1.1':
+ resolution: {integrity: sha512-fwL4P3SsC0r/vVGgnmexnc4vkvf+9vWMcGw5fdlPBJNR/zWlZRSXR5V6eXcHrpYVLhBsLYvZYE3NZa9paIUGxw==}
+ cpu: [arm64]
+ os: [win32]
+
'@rspack/binding-win32-ia32-msvc@2.0.8':
resolution: {integrity: sha512-bxiekytbX7V9KFAra+HkwtNWC6pYfHEBBZFpiT0xUs3mCFOmAAFVBsBSQsoCP9AdCEXoMAvNdnrHNw3iov4OZw==}
cpu: [ia32]
os: [win32]
+ '@rspack/binding-win32-ia32-msvc@2.1.1':
+ resolution: {integrity: sha512-j3BISXbjPyI57HeHxW9Jx+UP7RebNQ4t62uCbRP29caf9aghYJP+ZA5nJ2X7pol7N5Je2/V6WNahuNd0zQFDOA==}
+ cpu: [ia32]
+ os: [win32]
+
'@rspack/binding-win32-x64-msvc@2.0.8':
resolution: {integrity: sha512-7zPs8YCe/ZVJTwd+5lpB0CP0tkn2pONf/T1ycmVY76u21Nrwt8mXQGc/2yH2eWP4B7fikYBr3hGr7mpR2fajqQ==}
cpu: [x64]
os: [win32]
+ '@rspack/binding-win32-x64-msvc@2.1.1':
+ resolution: {integrity: sha512-aPNNFuZT5iBM8+S9J4P5ywJbf3tUvZN3Ppe6mXJ8/jTVDUDhe0YVj3AgD/AuxnUvl+yHrLfQkN1nNwUfIcomfA==}
+ cpu: [x64]
+ os: [win32]
+
'@rspack/binding@2.0.8':
resolution: {integrity: sha512-3uZ+y8aQxq33ty2srMxg2Nu0XuBI6vVrG50rkDaXqwWqOohfgGUSfFuQK7EnSUNy4aFUQlCG6NHialQHJov0wg==}
+ '@rspack/binding@2.1.1':
+ resolution: {integrity: sha512-6062hZP33fn1w51ClpQnum19GGXl/3eS754xrRYbQ7wwBNZk94HVwfSyiqcNCtY/pB1lsFjnaTKiW/Q+jv0axQ==}
+
'@rspack/cli@2.0.8':
resolution: {integrity: sha512-b7MrVE9kvCs8L7hhPdHR6AvQ5wyD3KeBYD+DZMynt+545rvJYnT11LVNjqQJqAYHrgBTmf+9CNPYmeJvEZesWw==}
hasBin: true
@@ -1822,6 +1906,18 @@ packages:
'@swc/helpers':
optional: true
+ '@rspack/core@2.1.1':
+ resolution: {integrity: sha512-Rgz6xZcD0rm7ejZhYNi13qvW2dSnIQQt/7D3xbYoT5hOU838JbkF0P3SAMFGKE+FyLZswnyRpxGfnYHZQqDmJA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0
+ '@swc/helpers': ^0.5.23
+ peerDependenciesMeta:
+ '@module-federation/runtime-tools':
+ optional: true
+ '@swc/helpers':
+ optional: true
+
'@shikijs/core@3.23.0':
resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==}
@@ -1900,6 +1996,9 @@ packages:
'@swc/helpers@0.5.21':
resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==}
+ '@swc/helpers@0.5.23':
+ resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==}
+
'@tybys/wasm-util@0.10.2':
resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
@@ -6538,24 +6637,57 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.62.2':
optional: true
+ '@rsbuild/core@2.1.1(core-js@3.49.0)':
+ dependencies:
+ '@rspack/core': 2.1.1(@swc/helpers@0.5.23)
+ '@swc/helpers': 0.5.23
+ optionalDependencies:
+ core-js: 3.49.0
+ transitivePeerDependencies:
+ - '@module-federation/runtime-tools'
+
'@rspack/binding-darwin-arm64@2.0.8':
optional: true
+ '@rspack/binding-darwin-arm64@2.1.1':
+ optional: true
+
'@rspack/binding-darwin-x64@2.0.8':
optional: true
+ '@rspack/binding-darwin-x64@2.1.1':
+ optional: true
+
'@rspack/binding-linux-arm64-gnu@2.0.8':
optional: true
+ '@rspack/binding-linux-arm64-gnu@2.1.1':
+ optional: true
+
'@rspack/binding-linux-arm64-musl@2.0.8':
optional: true
+ '@rspack/binding-linux-arm64-musl@2.1.1':
+ optional: true
+
+ '@rspack/binding-linux-riscv64-gnu@2.1.1':
+ optional: true
+
+ '@rspack/binding-linux-riscv64-musl@2.1.1':
+ optional: true
+
'@rspack/binding-linux-x64-gnu@2.0.8':
optional: true
+ '@rspack/binding-linux-x64-gnu@2.1.1':
+ optional: true
+
'@rspack/binding-linux-x64-musl@2.0.8':
optional: true
+ '@rspack/binding-linux-x64-musl@2.1.1':
+ optional: true
+
'@rspack/binding-wasm32-wasi@2.0.8':
dependencies:
'@emnapi/core': 1.10.0
@@ -6563,15 +6695,31 @@ snapshots:
'@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
optional: true
+ '@rspack/binding-wasm32-wasi@2.1.1':
+ dependencies:
+ '@emnapi/core': 1.11.1
+ '@emnapi/runtime': 1.11.1
+ '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ optional: true
+
'@rspack/binding-win32-arm64-msvc@2.0.8':
optional: true
+ '@rspack/binding-win32-arm64-msvc@2.1.1':
+ optional: true
+
'@rspack/binding-win32-ia32-msvc@2.0.8':
optional: true
+ '@rspack/binding-win32-ia32-msvc@2.1.1':
+ optional: true
+
'@rspack/binding-win32-x64-msvc@2.0.8':
optional: true
+ '@rspack/binding-win32-x64-msvc@2.1.1':
+ optional: true
+
'@rspack/binding@2.0.8':
optionalDependencies:
'@rspack/binding-darwin-arm64': 2.0.8
@@ -6585,13 +6733,36 @@ snapshots:
'@rspack/binding-win32-ia32-msvc': 2.0.8
'@rspack/binding-win32-x64-msvc': 2.0.8
- '@rspack/cli@2.0.8(@rspack/core@2.0.8)':
+ '@rspack/binding@2.1.1':
+ optionalDependencies:
+ '@rspack/binding-darwin-arm64': 2.1.1
+ '@rspack/binding-darwin-x64': 2.1.1
+ '@rspack/binding-linux-arm64-gnu': 2.1.1
+ '@rspack/binding-linux-arm64-musl': 2.1.1
+ '@rspack/binding-linux-riscv64-gnu': 2.1.1
+ '@rspack/binding-linux-riscv64-musl': 2.1.1
+ '@rspack/binding-linux-x64-gnu': 2.1.1
+ '@rspack/binding-linux-x64-musl': 2.1.1
+ '@rspack/binding-wasm32-wasi': 2.1.1
+ '@rspack/binding-win32-arm64-msvc': 2.1.1
+ '@rspack/binding-win32-ia32-msvc': 2.1.1
+ '@rspack/binding-win32-x64-msvc': 2.1.1
+
+ '@rspack/cli@2.0.8(@rspack/core@2.0.8(@swc/helpers@0.5.23))':
dependencies:
- '@rspack/core': 2.0.8
+ '@rspack/core': 2.0.8(@swc/helpers@0.5.23)
- '@rspack/core@2.0.8':
+ '@rspack/core@2.0.8(@swc/helpers@0.5.23)':
dependencies:
'@rspack/binding': 2.0.8
+ optionalDependencies:
+ '@swc/helpers': 0.5.23
+
+ '@rspack/core@2.1.1(@swc/helpers@0.5.23)':
+ dependencies:
+ '@rspack/binding': 2.1.1
+ optionalDependencies:
+ '@swc/helpers': 0.5.23
'@shikijs/core@3.23.0':
dependencies:
@@ -6716,6 +6887,10 @@ snapshots:
dependencies:
tslib: 2.8.1
+ '@swc/helpers@0.5.23':
+ dependencies:
+ tslib: 2.8.1
+
'@tybys/wasm-util@0.10.2':
dependencies:
tslib: 2.8.1
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 0aa6e852..e74529fb 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -49,6 +49,7 @@ catalogs:
peer:
'@farmfe/core': ^1.7.11
+ '@rsbuild/core': ^2.0.14
'@rspack/core': ^2.0.8
bun-types-no-globals: ^1.3.11
esbuild: ^0.28.1
diff --git a/scripts/buildFixtures.ts b/scripts/buildFixtures.ts
index 73017b6e..73059bc7 100644
--- a/scripts/buildFixtures.ts
+++ b/scripts/buildFixtures.ts
@@ -45,6 +45,10 @@ for (const name of fixtures) {
execSync('npx rspack --version', { cwd: path, stdio: 'inherit' })
execSync('npx rspack', { cwd: path, stdio: 'inherit' })
+ console.log(c.cyanBright.inverse.bold`\n Rsbuild `, name, '\n')
+ execSync('npx rsbuild --version', { cwd: path, stdio: 'inherit' })
+ execSync('npx rsbuild build', { cwd: path, stdio: 'inherit' })
+
console.log(c.magenta.inverse.bold`\n Farm `, name, '\n')
execSync('npx farm --version', { cwd: path, stdio: 'inherit' })
execSync('npx farm build', { cwd: path, stdio: 'inherit' })
diff --git a/src/define.ts b/src/define.ts
index 540b6617..b302eff4 100644
--- a/src/define.ts
+++ b/src/define.ts
@@ -4,6 +4,7 @@ import { getEsbuildPlugin } from './esbuild'
import { getFarmPlugin } from './farm'
import { getRolldownPlugin } from './rolldown'
import { getRollupPlugin } from './rollup'
+import { getRsbuildPlugin } from './rsbuild'
import { getRspackPlugin } from './rspack'
import { getUnloaderPlugin } from './unloader'
import { getVitePlugin } from './vite'
@@ -31,6 +32,9 @@ export function createUnplugin(
get rspack() {
return getRspackPlugin(factory)
},
+ get rsbuild() {
+ return getRsbuildPlugin(factory)
+ },
get farm() {
return getFarmPlugin(factory)
},
@@ -82,6 +86,12 @@ export function createRspackPlugin(
+ factory: UnpluginFactory,
+): UnpluginInstance['rsbuild'] {
+ return getRsbuildPlugin(factory)
+}
+
export function createFarmPlugin(
factory: UnpluginFactory,
): UnpluginInstance['farm'] {
diff --git a/src/rsbuild/index.ts b/src/rsbuild/index.ts
new file mode 100644
index 00000000..d69cb56b
--- /dev/null
+++ b/src/rsbuild/index.ts
@@ -0,0 +1,54 @@
+import type { RsbuildPlugin } from '@rsbuild/core'
+import type {
+ UnpluginContextMeta,
+ UnpluginFactory,
+ UnpluginInstance,
+ UnpluginOptions,
+} from '../types'
+import { version as unpluginVersion } from '../../package.json'
+import { getRspackPluginFromRaw } from '../rspack'
+import { toArray } from '../utils/general'
+
+export function getRsbuildPlugin, Nested extends boolean = boolean>(
+ factory: UnpluginFactory,
+): UnpluginInstance['rsbuild']
+export function getRsbuildPlugin>(
+ factory: UnpluginFactory,
+) {
+ return (userOptions?: UserOptions) => {
+ const meta: UnpluginContextMeta = {
+ framework: 'rsbuild',
+ versions: { unplugin: unpluginVersion },
+ }
+ const rawPlugins = toArray(factory(userOptions!, meta))
+ const plugins = rawPlugins.map(rawPlugin => toRsbuildPlugin(rawPlugin, meta))
+
+ return plugins.length === 1 ? plugins[0] : plugins
+ }
+}
+
+function toRsbuildPlugin(
+ rawPlugin: UnpluginOptions,
+ meta: UnpluginContextMeta,
+): RsbuildPlugin {
+ const rsbuildOptions = rawPlugin.rsbuild
+
+ return {
+ ...rsbuildOptions,
+ name: rsbuildOptions?.name ?? rawPlugin.name,
+ enforce: rsbuildOptions?.enforce ?? rawPlugin.enforce,
+ async setup(api) {
+ meta.versions = {
+ ...meta.versions,
+ rsbuild: api.context.version,
+ unplugin: meta.versions.unplugin ?? unpluginVersion,
+ }
+
+ api.modifyRspackConfig((config) => {
+ config.plugins.push(getRspackPluginFromRaw([rawPlugin], meta))
+ })
+
+ await rsbuildOptions?.setup?.(api)
+ },
+ }
+}
diff --git a/src/rspack/index.ts b/src/rspack/index.ts
index bc09da0e..2fdefc1b 100644
--- a/src/rspack/index.ts
+++ b/src/rspack/index.ts
@@ -1,10 +1,11 @@
-import type { RspackPluginInstance } from '@rspack/core'
+import type { Compiler, RspackPluginInstance } from '@rspack/core'
import type {
ResolvedUnpluginOptions,
UnpluginContext,
UnpluginContextMeta,
UnpluginFactory,
UnpluginInstance,
+ UnpluginOptions,
} from '../types'
import fs from 'node:fs'
import { resolve } from 'node:path'
@@ -27,206 +28,234 @@ const LOAD_LOADER = resolve(
import.meta.dev ? '../../dist/rspack/loaders/load.mjs' : 'rspack/loaders/load.mjs',
)
+function getRspackVersion(compiler: Compiler): string {
+ return compiler.rspack.rspackVersion ?? compiler.rspack.version
+}
+
export function getRspackPlugin>(
factory: UnpluginFactory,
): UnpluginInstance['rspack'] {
return (userOptions?: UserOptions): RspackPluginInstance => {
return {
apply(compiler) {
- // We need the prefix of virtual modules to be an absolute path so rspack lets us load them (even if it's made up)
- // In the loader we strip the made up prefix path again
- const VIRTUAL_MODULE_PREFIX = resolve(compiler.options.context ?? process.cwd(), 'node_modules/.virtual', compiler.rspack.experiments.VirtualModulesPlugin ? '' : process.pid.toString())
-
- const version = (compiler as any).rspack?.rspackVersion ?? (compiler as any).rspack?.version
const meta: UnpluginContextMeta = {
framework: 'rspack',
- versions: { rspack: version, unplugin: unpluginVersion },
+ versions: { rspack: getRspackVersion(compiler), unplugin: unpluginVersion },
rspack: {
compiler,
},
}
const rawPlugins = toArray(factory(userOptions!, meta))
- for (const rawPlugin of rawPlugins) {
- const plugin = Object.assign(
- rawPlugin,
- {
- __unpluginMeta: meta,
- __virtualModulePrefix: VIRTUAL_MODULE_PREFIX,
+ applyRspackPlugins(compiler, rawPlugins, meta)
+ },
+ }
+ }
+}
+
+export function getRspackPluginFromRaw(
+ rawPlugins: UnpluginOptions[],
+ meta: UnpluginContextMeta,
+): RspackPluginInstance {
+ return {
+ apply(compiler) {
+ applyRspackPlugins(compiler, rawPlugins, meta)
+ },
+ }
+}
+
+function applyRspackPlugins(
+ compiler: Compiler,
+ rawPlugins: UnpluginOptions[],
+ meta: UnpluginContextMeta,
+) {
+ // We need the prefix of virtual modules to be an absolute path so rspack lets us load them (even if it's made up)
+ // In the loader we strip the made up prefix path again
+ const VIRTUAL_MODULE_PREFIX = resolve(compiler.options.context ?? process.cwd(), 'node_modules/.virtual', compiler.rspack.experiments.VirtualModulesPlugin ? '' : process.pid.toString())
+
+ meta.versions = {
+ ...meta.versions,
+ rspack: getRspackVersion(compiler),
+ unplugin: meta.versions.unplugin ?? unpluginVersion,
+ }
+ if (meta.framework === 'rspack')
+ meta.rspack.compiler = compiler
+
+ for (const rawPlugin of rawPlugins) {
+ const plugin = Object.assign(
+ {},
+ rawPlugin,
+ {
+ __unpluginMeta: meta,
+ __virtualModulePrefix: VIRTUAL_MODULE_PREFIX,
+ },
+ ) as ResolvedUnpluginOptions
+
+ const externalModules = new Set()
+
+ // resolveId hook
+ if (plugin.resolveId) {
+ // rspack >= 1.5.0: use native virtual modules plugin
+ // rspack < 1.5.0: use fake virtual modules plugin
+ const vfs = compiler.rspack.experiments.VirtualModulesPlugin
+ ? new compiler.rspack.experiments.VirtualModulesPlugin()
+ : new FakeVirtualModulesPlugin(plugin)
+ vfs.apply(compiler)
+ const vfsModules = new Map>()
+ plugin.__vfsModules = vfsModules
+ plugin.__vfs = vfs as any
+
+ compiler.hooks.compilation.tap(plugin.name, (compilation, { normalModuleFactory }) => {
+ normalModuleFactory.hooks.resolve.tapPromise(plugin.name, async (resolveData) => {
+ const id = normalizeAbsolutePath(resolveData.request)
+
+ const requestContext = resolveData.contextInfo
+ let importer = requestContext.issuer !== '' ? requestContext.issuer : undefined
+ const isEntry = requestContext.issuer === ''
+
+ if (importer?.startsWith(plugin.__virtualModulePrefix))
+ importer = decodeURIComponent(importer.slice(plugin.__virtualModulePrefix.length))
+
+ const context = createBuildContext(compiler, compilation)
+ let error: Error | undefined
+ const pluginContext: UnpluginContext = {
+ error(msg) {
+ if (error == null)
+ error = normalizeMessage(msg)
+ else
+ console.error(`unplugin/rspack: multiple errors returned from resolveId hook: ${msg}`)
+ },
+ warn(msg) {
+ console.warn(`unplugin/rspack: warning from resolveId hook: ${msg}`)
},
- ) as ResolvedUnpluginOptions
-
- const externalModules = new Set()
-
- // resolveId hook
- if (plugin.resolveId) {
- const createPlugin = (plugin: ResolvedUnpluginOptions) => {
- // rspack >= 1.5.0: use native virtual modules plugin
- if (compiler.rspack.experiments.VirtualModulesPlugin)
- return new compiler.rspack.experiments.VirtualModulesPlugin()
- // rspack < 1.5.0: use fake virtual modules plugin
- return new FakeVirtualModulesPlugin(plugin)
- }
- const vfs = createPlugin(plugin)
- vfs.apply(compiler)
- const vfsModules = new Map>()
- plugin.__vfsModules = vfsModules
- plugin.__vfs = vfs as any
-
- compiler.hooks.compilation.tap(plugin.name, (compilation, { normalModuleFactory }) => {
- normalModuleFactory.hooks.resolve.tapPromise(plugin.name, async (resolveData) => {
- const id = normalizeAbsolutePath(resolveData.request)
-
- const requestContext = resolveData.contextInfo
- let importer = requestContext.issuer !== '' ? requestContext.issuer : undefined
- const isEntry = requestContext.issuer === ''
-
- if (importer?.startsWith(plugin.__virtualModulePrefix))
- importer = decodeURIComponent(importer.slice(plugin.__virtualModulePrefix.length))
-
- const context = createBuildContext(compiler, compilation)
- let error: Error | undefined
- const pluginContext: UnpluginContext = {
- error(msg) {
- if (error == null)
- error = normalizeMessage(msg)
- else
- console.error(`unplugin/rspack: multiple errors returned from resolveId hook: ${msg}`)
- },
- warn(msg) {
- console.warn(`unplugin/rspack: warning from resolveId hook: ${msg}`)
- },
- }
-
- const { handler, filter } = normalizeObjectHook('resolveId', plugin.resolveId!)
- if (!filter(id))
- return
-
- const resolveIdResult = await handler.call!({ ...context, ...pluginContext }, id, importer, { isEntry })
-
- if (error != null)
- throw error
- if (resolveIdResult == null)
- return
-
- let resolved = typeof resolveIdResult === 'string' ? resolveIdResult : resolveIdResult.id
-
- const isExternal = typeof resolveIdResult === 'string' ? false : resolveIdResult.external === true
- if (isExternal)
- externalModules.add(resolved)
-
- let isVirtual = true
- try {
- // use the compiler's inputFileSystem if available, otherwise use the node:fs module
- (compiler.inputFileSystem?.statSync ?? fs.statSync)(resolved)
- isVirtual = false
- }
- catch {
- // already resolved virtual modules are not virtual themselves
- isVirtual = !isVirtualModuleId(resolved, plugin)
- }
-
- // If the resolved module does not exist,
- // we treat it as a virtual module
- if (isVirtual) {
- const encodedVirtualPath = encodeVirtualModuleId(resolved, plugin)
-
- if (!vfsModules.has(resolved)) {
- const fsPromise = Promise.resolve(vfs.writeModule(encodedVirtualPath, ''))
- vfsModules.set(resolved, fsPromise)
- await fsPromise
- }
- else {
- // Ensure that the module is written to the virtual file system
- // before we use it.
- await vfsModules.get(resolved)
- }
-
- resolved = encodedVirtualPath
- }
-
- resolveData.request = resolved
- })
- })
}
- // load hook
- if (plugin.load) {
- compiler.options.module.rules.unshift({
- enforce: plugin.enforce,
- include(id) {
- if (isVirtualModuleId(id, plugin))
- id = decodeVirtualModuleId(id, plugin)
-
- // load include filter
- if (plugin.loadInclude && !plugin.loadInclude(id))
- return false
-
- const { filter } = normalizeObjectHook('load', plugin.load!)
- if (!filter(id))
- return false
-
- // Don't run load hook for external modules
- return !externalModules.has(id)
- },
- use: [{
- loader: LOAD_LOADER,
- options: {
- plugin,
- },
- }],
- type: 'javascript/auto',
- })
- }
+ const { handler, filter } = normalizeObjectHook('resolveId', plugin.resolveId!)
+ if (!filter(id))
+ return
- // transform hook
- if (plugin.transform) {
- compiler.options.module.rules.unshift({
- enforce: plugin.enforce,
- use(data) {
- return transformUse(data, plugin, TRANSFORM_LOADER)
- },
- })
- }
+ const resolveIdResult = await handler.call!({ ...context, ...pluginContext }, id, importer, { isEntry })
+
+ if (error != null)
+ throw error
+ if (resolveIdResult == null)
+ return
- if (plugin.rspack)
- plugin.rspack(compiler)
-
- if (plugin.watchChange || plugin.buildStart) {
- compiler.hooks.make.tapPromise(plugin.name, async (compilation) => {
- const context = createBuildContext(compiler, compilation)
- if (plugin.watchChange && (compiler.modifiedFiles || compiler.removedFiles)) {
- const promises: Promise[] = []
- if (compiler.modifiedFiles) {
- compiler.modifiedFiles.forEach(file =>
- promises.push(Promise.resolve(plugin.watchChange!.call(context, file, { event: 'update' }))),
- )
- }
- if (compiler.removedFiles) {
- compiler.removedFiles.forEach(file =>
- promises.push(Promise.resolve(plugin.watchChange!.call(context, file, { event: 'delete' }))),
- )
- }
- await Promise.all(promises)
- }
-
- if (plugin.buildStart)
- return await plugin.buildStart.call(context)
- })
+ let resolved = typeof resolveIdResult === 'string' ? resolveIdResult : resolveIdResult.id
+
+ const isExternal = typeof resolveIdResult === 'string' ? false : resolveIdResult.external === true
+ if (isExternal)
+ externalModules.add(resolved)
+
+ let isVirtual = true
+ try {
+ // use the compiler's inputFileSystem if available, otherwise use the node:fs module
+ (compiler.inputFileSystem?.statSync ?? fs.statSync)(resolved)
+ isVirtual = false
}
+ catch {
+ // already resolved virtual modules are not virtual themselves
+ isVirtual = !isVirtualModuleId(resolved, plugin)
+ }
+
+ // If the resolved module does not exist,
+ // we treat it as a virtual module
+ if (isVirtual) {
+ const encodedVirtualPath = encodeVirtualModuleId(resolved, plugin)
+
+ if (!vfsModules.has(resolved)) {
+ const fsPromise = Promise.resolve(vfs.writeModule(encodedVirtualPath, ''))
+ vfsModules.set(resolved, fsPromise)
+ await fsPromise
+ }
+ else {
+ // Ensure that the module is written to the virtual file system
+ // before we use it.
+ await vfsModules.get(resolved)
+ }
- if (plugin.buildEnd) {
- compiler.hooks.emit.tapPromise(plugin.name, async (compilation) => {
- await plugin.buildEnd!.call(createBuildContext(compiler, compilation))
- })
+ resolved = encodedVirtualPath
}
- if (plugin.writeBundle) {
- compiler.hooks.afterEmit.tapPromise(plugin.name, async () => {
- await plugin.writeBundle!()
- })
+ resolveData.request = resolved
+ })
+ })
+ }
+
+ // load hook
+ if (plugin.load) {
+ compiler.options.module.rules.unshift({
+ enforce: plugin.enforce,
+ include(id) {
+ if (isVirtualModuleId(id, plugin))
+ id = decodeVirtualModuleId(id, plugin)
+
+ // load include filter
+ if (plugin.loadInclude && !plugin.loadInclude(id))
+ return false
+
+ const { filter } = normalizeObjectHook('load', plugin.load!)
+ if (!filter(id))
+ return false
+
+ // Don't run load hook for external modules
+ return !externalModules.has(id)
+ },
+ use: [{
+ loader: LOAD_LOADER,
+ options: {
+ plugin,
+ },
+ }],
+ type: 'javascript/auto',
+ })
+ }
+
+ // transform hook
+ if (plugin.transform) {
+ compiler.options.module.rules.unshift({
+ enforce: plugin.enforce,
+ use(data) {
+ return transformUse(data, plugin, TRANSFORM_LOADER)
+ },
+ })
+ }
+
+ if (meta.framework === 'rspack' && plugin.rspack)
+ plugin.rspack(compiler)
+
+ if (plugin.watchChange || plugin.buildStart) {
+ compiler.hooks.make.tapPromise(plugin.name, async (compilation) => {
+ const context = createBuildContext(compiler, compilation)
+ if (plugin.watchChange && (compiler.modifiedFiles || compiler.removedFiles)) {
+ const promises: Promise[] = []
+ if (compiler.modifiedFiles) {
+ compiler.modifiedFiles.forEach(file =>
+ promises.push(Promise.resolve(plugin.watchChange!.call(context, file, { event: 'update' }))),
+ )
+ }
+ if (compiler.removedFiles) {
+ compiler.removedFiles.forEach(file =>
+ promises.push(Promise.resolve(plugin.watchChange!.call(context, file, { event: 'delete' }))),
+ )
}
+ await Promise.all(promises)
}
- },
+
+ if (plugin.buildStart)
+ return await plugin.buildStart.call(context)
+ })
+ }
+
+ if (plugin.buildEnd) {
+ compiler.hooks.emit.tapPromise(plugin.name, async (compilation) => {
+ await plugin.buildEnd!.call(createBuildContext(compiler, compilation))
+ })
+ }
+
+ if (plugin.writeBundle) {
+ compiler.hooks.afterEmit.tapPromise(plugin.name, async () => {
+ await plugin.writeBundle!()
+ })
}
}
}
diff --git a/src/types.ts b/src/types.ts
index aa52f5e8..97f2d2e1 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,4 +1,5 @@
import type { CompilationContext as FarmCompilationContext, JsPlugin as FarmPlugin } from '@farmfe/core'
+import type { RsbuildPlugin } from '@rsbuild/core'
import type { Compilation as RspackCompilation, Compiler as RspackCompiler, LoaderContext as RspackLoaderContext, RspackPluginInstance } from '@rspack/core'
import type { Loader as BunLoader, BunPlugin, PluginBuilder as BunPluginBuilder } from 'bun'
import type { BuildOptions, Plugin as EsbuildPlugin, Loader, PluginBuild } from 'esbuild'
@@ -14,6 +15,7 @@ export type {
EsbuildPlugin,
RolldownPlugin,
RollupPlugin,
+ RsbuildPlugin,
RspackCompiler,
RspackPluginInstance,
UnloaderPlugin,
@@ -133,6 +135,7 @@ export interface UnpluginOptions {
rollup?: Partial | undefined
webpack?: ((compiler: WebpackCompiler) => void) | undefined
rspack?: ((compiler: RspackCompiler) => void) | undefined
+ rsbuild?: Partial | undefined
vite?: Partial | undefined
unloader?: Partial | undefined
rolldown?: Partial | undefined
@@ -172,6 +175,7 @@ export interface UnpluginInstance
rolldown: UnpluginFactoryOutput : RolldownPlugin>
webpack: UnpluginFactoryOutput
rspack: UnpluginFactoryOutput
+ rsbuild: UnpluginFactoryOutput : RsbuildPlugin>
esbuild: UnpluginFactoryOutput
unloader: UnpluginFactoryOutput : UnloaderPlugin>
farm: UnpluginFactoryOutput
@@ -179,7 +183,7 @@ export interface UnpluginInstance
raw: UnpluginFactory
}
-export type SupportedFramework = 'rollup' | 'vite' | 'rolldown' | 'farm' | 'unloader' | 'webpack' | 'rspack' | 'esbuild' | 'bun'
+export type SupportedFramework = 'rollup' | 'vite' | 'rolldown' | 'farm' | 'unloader' | 'webpack' | 'rspack' | 'rsbuild' | 'esbuild' | 'bun'
export type UnpluginContextMeta = Partial & {
/**
@@ -209,6 +213,8 @@ export type UnpluginContextMeta = Partial & {
} | {
framework: 'rspack'
rspack: { compiler: RspackCompiler }
+} | {
+ framework: 'rsbuild'
})
export interface UnpluginMessage {
diff --git a/test/fixtures/load/__test__/build.test.ts b/test/fixtures/load/__test__/build.test.ts
index 504287d7..ca47e602 100644
--- a/test/fixtures/load/__test__/build.test.ts
+++ b/test/fixtures/load/__test__/build.test.ts
@@ -34,6 +34,12 @@ describe('load-called-before-transform', () => {
expect(content).toContain('it is a msg -> through the load hook -> transform-[Injected Rspack]')
})
+ it('rsbuild', async () => {
+ const content = await fs.readFile(r('rsbuild/main.js'), 'utf-8')
+
+ expect(content).toContain('it is a msg -> through the load hook -> transform-[Injected Rsbuild]')
+ })
+
it('farm', async () => {
const content = await fs.readFile(r('farm/main.js'), 'utf-8')
diff --git a/test/fixtures/load/rsbuild.config.mjs b/test/fixtures/load/rsbuild.config.mjs
new file mode 100644
index 00000000..b751688b
--- /dev/null
+++ b/test/fixtures/load/rsbuild.config.mjs
@@ -0,0 +1,30 @@
+import { resolve } from 'node:path'
+import { defineConfig } from '@rsbuild/core'
+import unplugin from './unplugin.js'
+
+const { rsbuild } = unplugin
+
+export default defineConfig({
+ source: {
+ entry: {
+ main: resolve(import.meta.dirname, 'src/main.js'),
+ },
+ },
+ output: {
+ distPath: {
+ root: resolve(import.meta.dirname, 'dist/rsbuild'),
+ js: '',
+ },
+ filename: {
+ js: '[name].js',
+ },
+ filenameHash: false,
+ sourceMap: {
+ js: 'source-map',
+ },
+ },
+ plugins: [rsbuild({ msg: 'Rsbuild' })],
+ tools: {
+ htmlPlugin: false,
+ },
+})
diff --git a/test/fixtures/transform/__test__/build.test.ts b/test/fixtures/transform/__test__/build.test.ts
index fb3618a8..68b50d15 100644
--- a/test/fixtures/transform/__test__/build.test.ts
+++ b/test/fixtures/transform/__test__/build.test.ts
@@ -46,6 +46,14 @@ describe('transform build', () => {
expect(content).toContain('QUERY: [Injected Post Rspack]')
})
+ it('rsbuild', async () => {
+ const content = await fs.readFile(r('rsbuild/main.js'), 'utf-8')
+
+ expect(content).toContain('NON-TARGET: __UNPLUGIN__')
+ expect(content).toContain('TARGET: [Injected Post Rsbuild]')
+ expect(content).toContain('QUERY: [Injected Post Rsbuild]')
+ })
+
it('farm', async () => {
const content = await fs.readFile(r('farm/main.js'), 'utf-8')
diff --git a/test/fixtures/transform/rsbuild.config.mjs b/test/fixtures/transform/rsbuild.config.mjs
new file mode 100644
index 00000000..b751688b
--- /dev/null
+++ b/test/fixtures/transform/rsbuild.config.mjs
@@ -0,0 +1,30 @@
+import { resolve } from 'node:path'
+import { defineConfig } from '@rsbuild/core'
+import unplugin from './unplugin.js'
+
+const { rsbuild } = unplugin
+
+export default defineConfig({
+ source: {
+ entry: {
+ main: resolve(import.meta.dirname, 'src/main.js'),
+ },
+ },
+ output: {
+ distPath: {
+ root: resolve(import.meta.dirname, 'dist/rsbuild'),
+ js: '',
+ },
+ filename: {
+ js: '[name].js',
+ },
+ filenameHash: false,
+ sourceMap: {
+ js: 'source-map',
+ },
+ },
+ plugins: [rsbuild({ msg: 'Rsbuild' })],
+ tools: {
+ htmlPlugin: false,
+ },
+})
diff --git a/test/fixtures/virtual-module/__test__/build.test.ts b/test/fixtures/virtual-module/__test__/build.test.ts
index 2d09bbe2..b1007b7e 100644
--- a/test/fixtures/virtual-module/__test__/build.test.ts
+++ b/test/fixtures/virtual-module/__test__/build.test.ts
@@ -34,6 +34,13 @@ describe('virtual-module build', () => {
expect(content).toContain('VIRTUAL:TWO')
})
+ it('rsbuild', async () => {
+ const content = await fs.readFile(r('rsbuild/main.js'), 'utf-8')
+
+ expect(content).toContain('VIRTUAL:ONE')
+ expect(content).toContain('VIRTUAL:TWO')
+ })
+
it('esbuild', async () => {
const content = await fs.readFile(r('esbuild/main.js'), 'utf-8')
diff --git a/test/fixtures/virtual-module/rsbuild.config.mjs b/test/fixtures/virtual-module/rsbuild.config.mjs
new file mode 100644
index 00000000..30e0a183
--- /dev/null
+++ b/test/fixtures/virtual-module/rsbuild.config.mjs
@@ -0,0 +1,30 @@
+import { resolve } from 'node:path'
+import { defineConfig } from '@rsbuild/core'
+import unplugin from './unplugin.js'
+
+const { rsbuild } = unplugin
+
+export default defineConfig({
+ source: {
+ entry: {
+ main: resolve(import.meta.dirname, 'src/main.js'),
+ },
+ },
+ output: {
+ distPath: {
+ root: resolve(import.meta.dirname, 'dist/rsbuild'),
+ js: '',
+ },
+ filename: {
+ js: '[name].js',
+ },
+ filenameHash: false,
+ sourceMap: {
+ js: 'source-map',
+ },
+ },
+ plugins: [rsbuild()],
+ tools: {
+ htmlPlugin: false,
+ },
+})
diff --git a/test/unit-tests/framework-version/index.test.ts b/test/unit-tests/framework-version/index.test.ts
index 945313b0..f1fdb3ac 100644
--- a/test/unit-tests/framework-version/index.test.ts
+++ b/test/unit-tests/framework-version/index.test.ts
@@ -4,7 +4,7 @@ import { register } from 'unloader'
import { describe, expect, it } from 'vitest'
import { createUnplugin } from '../../../src/define'
import { onlyBun } from '../../utils'
-import { build } from '../utils'
+import { build, toArray } from '../utils'
const require = createRequire(import.meta.url)
const rollupVersion: string = require('rollup/package.json').version
@@ -13,6 +13,10 @@ const rolldownVersion: string = require('rolldown/package.json').version
const unloaderVersion: string = require('unloader/package.json').version
const webpackVersion: string = require('webpack/package.json').version
const rspackVersion: string = require('@rspack/core/package.json').version
+const rsbuildVersion: string = require('@rsbuild/core/package.json').version
+
+const rsbuildPath = path.dirname(require.resolve('@rsbuild/core/package.json'))
+const rsbuildRspackVersion: string = require(require.resolve('@rspack/core/package.json', { paths: [rsbuildPath] })).version
const entry = path.resolve(__dirname, '../filter/test-src/entry.js')
const runWithRegisterHooks = typeof (nodeModule as any).registerHooks === 'function' ? it : it.skip
@@ -176,6 +180,204 @@ describe('framework versions', () => {
expect(allVersions).toMatchObject({ rspack: rspackVersion, unplugin: expect.any(String) })
}, 20_000)
+ it('rsbuild sets versions.rsbuild and versions.rspack', async () => {
+ let framework: string | undefined
+ let versionsRsbuild: string | undefined
+ let versionsRspack: string | undefined
+ let allVersions: Partial> | undefined
+ let rsbuildSetupCalled = false
+ const plugin = createUnplugin((_options, meta) => {
+ framework = meta.framework
+ return {
+ name: 'framework-versions-rsbuild',
+ buildStart() {
+ versionsRsbuild = meta.versions.rsbuild
+ versionsRspack = meta.versions.rspack
+ allVersions = meta.versions
+ },
+ rsbuild: {
+ setup() {
+ rsbuildSetupCalled = true
+ },
+ },
+ }
+ })
+
+ await build.rsbuild({
+ config: {
+ source: {
+ entry: {
+ main: entry,
+ },
+ },
+ output: {
+ distPath: {
+ root: path.resolve(__dirname, '../../../temp/rsbuild-framework-version'),
+ js: '',
+ },
+ filename: {
+ js: '[name].js',
+ },
+ filenameHash: false,
+ },
+ plugins: [plugin.rsbuild()],
+ tools: {
+ htmlPlugin: false,
+ },
+ },
+ })
+
+ expect(framework).toBe('rsbuild')
+ expect(rsbuildSetupCalled).toBe(true)
+ expect(versionsRsbuild).toBe(rsbuildVersion)
+ expect(versionsRspack).toBe(rsbuildRspackVersion)
+ expect(allVersions).toMatchObject({
+ rsbuild: rsbuildVersion,
+ rspack: rsbuildRspackVersion,
+ unplugin: expect.any(String),
+ })
+ }, 20_000)
+
+ it('rsbuild does not call the rspack-specific hook', async () => {
+ let buildStartCalled = false
+ let rspackCalled = false
+ const plugin = createUnplugin(() => ({
+ name: 'rsbuild-with-rspack-hook',
+ buildStart() {
+ buildStartCalled = true
+ },
+ rspack() {
+ rspackCalled = true
+ },
+ }))
+
+ await build.rsbuild({
+ config: {
+ source: {
+ entry: {
+ main: entry,
+ },
+ },
+ output: {
+ distPath: {
+ root: path.resolve(__dirname, '../../../temp/rsbuild-rspack-hook'),
+ js: '',
+ },
+ filename: {
+ js: '[name].js',
+ },
+ filenameHash: false,
+ },
+ plugins: [plugin.rsbuild()],
+ tools: {
+ htmlPlugin: false,
+ },
+ },
+ })
+
+ expect(buildStartCalled).toBe(true)
+ expect(rspackCalled).toBe(false)
+ }, 20_000)
+
+ it('rsbuild respects apply for nested plugins', async () => {
+ const setupCalls: string[] = []
+ const buildStartCalls: string[] = []
+ const plugin = createUnplugin(() => [
+ {
+ name: 'rsbuild-nested-build',
+ buildStart() {
+ buildStartCalls.push('build')
+ },
+ rsbuild: {
+ apply: 'build',
+ setup() {
+ setupCalls.push('build')
+ },
+ },
+ },
+ {
+ name: 'rsbuild-nested-serve',
+ buildStart() {
+ buildStartCalls.push('serve')
+ },
+ rsbuild: {
+ apply: 'serve',
+ setup() {
+ setupCalls.push('serve')
+ },
+ },
+ },
+ ])
+
+ await build.rsbuild({
+ config: {
+ source: {
+ entry: {
+ main: entry,
+ },
+ },
+ output: {
+ distPath: {
+ root: path.resolve(__dirname, '../../../temp/rsbuild-nested-apply'),
+ js: '',
+ },
+ filename: {
+ js: '[name].js',
+ },
+ filenameHash: false,
+ },
+ plugins: plugin.rsbuild(),
+ tools: {
+ htmlPlugin: false,
+ },
+ },
+ })
+
+ expect(setupCalls).toEqual(['build'])
+ expect(buildStartCalls).toEqual(['build'])
+ }, 20_000)
+
+ it('rsbuild runs a single nested plugin', async () => {
+ const buildStartCalls: string[] = []
+ const plugin = createUnplugin(() => [
+ {
+ name: 'rsbuild-single-nested',
+ buildStart() {
+ buildStartCalls.push('build')
+ },
+ },
+ ])
+
+ const rsbuildPlugin = plugin.rsbuild()
+ expect(Array.isArray(rsbuildPlugin)).toBe(false)
+
+ await build.rsbuild({
+ config: {
+ source: {
+ entry: {
+ main: entry,
+ },
+ },
+ output: {
+ distPath: {
+ root: path.resolve(__dirname, '../../../temp/rsbuild-single-nested'),
+ js: '',
+ },
+ filename: {
+ js: '[name].js',
+ },
+ filenameHash: false,
+ },
+ plugins: toArray(rsbuildPlugin),
+ tools: {
+ htmlPlugin: false,
+ },
+ },
+ })
+
+ expect(buildStartCalls).toEqual(['build'])
+ }, 20_000)
+
onlyBun('bun sets versions.bun', async () => {
let hostVersion: string | undefined
let versionsBun: string | undefined
diff --git a/test/unit-tests/utils.ts b/test/unit-tests/utils.ts
index f29c1668..6fac28d5 100644
--- a/test/unit-tests/utils.ts
+++ b/test/unit-tests/utils.ts
@@ -1,3 +1,4 @@
+import * as rsbuild from '@rsbuild/core'
import * as rspack from '@rspack/core'
import * as esbuild from 'esbuild'
import * as rolldown from 'rolldown'
@@ -13,6 +14,12 @@ export const rolldownBuild: typeof rolldown.build = rolldown.build
export const esbuildBuild: typeof esbuild.build = esbuild.build
export const webpackBuild: typeof webpack.webpack = webpack.webpack || (webpack as any).default || webpack
export const rspackBuild: typeof rspack.rspack = rspack.rspack
+export async function rsbuildBuild(options?: rsbuild.CreateRsbuildOptions, buildOptions?: rsbuild.BuildOptions): Promise {
+ const instance = await rsbuild.createRsbuild(options)
+ const result = await instance.build(buildOptions)
+ await result.close()
+ return result
+}
export const bunBuild: typeof Bun.build = typeof Bun !== 'undefined'
? Bun.build
: () => {
@@ -24,6 +31,7 @@ export const webpackVersion: string = ((webpack as any).default || webpack).vers
export const build: {
webpack: typeof webpack.webpack
rspack: typeof rspackBuild
+ rsbuild: typeof rsbuildBuild
rollup: typeof rollupBuild
rolldown: typeof rolldownBuild
vite: typeof viteBuild
@@ -32,6 +40,7 @@ export const build: {
} = {
webpack: webpackBuild,
rspack: rspackBuild,
+ rsbuild: rsbuildBuild,
rollup: rollupBuild,
rolldown: rolldownBuild,
vite(config) {
diff --git a/tsdown.config.ts b/tsdown.config.ts
index 9e25b671..969f6bbd 100644
--- a/tsdown.config.ts
+++ b/tsdown.config.ts
@@ -14,6 +14,7 @@ export default defineConfig({
'rollup',
'esbuild',
'@farmfe/core',
+ '@rsbuild/core',
'@rspack/core',
'rolldown',
'unloader',