From 3100900820a1b17594ef3fc5c1cce36542201333 Mon Sep 17 00:00:00 2001 From: Yaroslav Slepukhin Date: Sun, 14 Jun 2026 21:08:24 +0200 Subject: [PATCH 1/4] feat: add rsbuild adapter --- README.md | 1 + docs/.vitepress/assets/rsbuild.svg | 1 + docs/.vitepress/assets/rspack.svg | 2 +- docs/.vitepress/theme/style.css | 4 - docs/guide/index.md | 87 ++-- docs/index.md | 10 +- docs/public/features/rsbuild.svg | 1 + docs/public/features/rspack.png | Bin 7828 -> 0 bytes docs/public/features/rspack.svg | 1 + docs/vite.config.ts | 1 + package.json | 1 + pnpm-lock.yaml | 165 +++++++- pnpm-workspace.yaml | 1 + scripts/buildFixtures.ts | 4 + src/define.ts | 10 + src/rsbuild/index.ts | 55 +++ src/rspack/index.ts | 388 ++++++++++-------- src/types.ts | 8 +- test/fixtures/load/__test__/build.test.ts | 6 + test/fixtures/load/rsbuild.config.mjs | 30 ++ .../fixtures/transform/__test__/build.test.ts | 8 + test/fixtures/transform/rsbuild.config.mjs | 30 ++ .../virtual-module/__test__/build.test.ts | 7 + .../virtual-module/rsbuild.config.mjs | 30 ++ .../framework-version/index.test.ts | 204 ++++++++- test/unit-tests/utils.ts | 9 + tsdown.config.ts | 1 + 27 files changed, 847 insertions(+), 218 deletions(-) create mode 100644 docs/.vitepress/assets/rsbuild.svg create mode 100644 docs/public/features/rsbuild.svg delete mode 100644 docs/public/features/rspack.png create mode 100644 docs/public/features/rspack.svg create mode 100644 src/rsbuild/index.ts create mode 100644 test/fixtures/load/rsbuild.config.mjs create mode 100644 test/fixtures/transform/rsbuild.config.mjs create mode 100644 test/fixtures/virtual-module/rsbuild.config.mjs 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 c59eb6fdfed39c3a226721581c34f429092288e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7828 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 \ 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 15579153..264f6cef 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,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 fca8c15b..c5daa035 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,6 +113,9 @@ catalogs: '@farmfe/core': specifier: ^1.7.11 version: 1.7.11 + '@rsbuild/core': + specifier: ^2.0.14 + version: 2.0.14 '@rspack/core': specifier: ^2.0.2 version: 2.0.2 @@ -187,12 +190,15 @@ importers: '@farmfe/core': specifier: catalog:peer version: 1.7.11(@types/node@25.6.2) + '@rsbuild/core': + specifier: catalog:peer + version: 2.0.14(core-js@3.49.0) '@rspack/cli': specifier: catalog:test - version: 2.0.2(@rspack/core@2.0.2(@swc/helpers@0.5.21)) + version: 2.0.2(@rspack/core@2.0.2(@swc/helpers@0.5.23)) '@rspack/core': specifier: catalog:peer - version: 2.0.2(@swc/helpers@0.5.21) + version: 2.0.2(@swc/helpers@0.5.23) '@types/node': specifier: catalog:dev version: 25.6.2 @@ -1728,62 +1734,128 @@ packages: cpu: [x64] os: [win32] + '@rsbuild/core@2.0.14': + resolution: {integrity: sha512-SSPer6vM+v82CV6JXIOzyyT++A1ECgsVvvUuveD5rfGxX/W58vWGnB+zWcYbMfYOjTntD+9ve6QGGh6kBjPx6A==} + 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.2': resolution: {integrity: sha512-0o7lbgBBsDlICWdjIH0q3e0BsSco4GRiImHWVfZSVEG+q2+ykZJvSvYCVhPM1Co375Z0S3VMPa/8SjcY1FHwlw==} cpu: [arm64] os: [darwin] + '@rspack/binding-darwin-arm64@2.0.8': + resolution: {integrity: sha512-vCgbgH7B7qom+uID+RCZsTCOYFb9wC4/4+1U6rMfytrXGVJ72eNQs2tbdjOl0lb18CT3N/n+VkWynUiLk84GwA==} + cpu: [arm64] + os: [darwin] + '@rspack/binding-darwin-x64@2.0.2': resolution: {integrity: sha512-tOwxZpoPlTlRs/w6UyUinXJ4TYRVHMlR7+eQxO1R3muKpixvhXQjtvoaY16HuFyTVky5F0IfOoWr3x9FEsgdLg==} cpu: [x64] os: [darwin] + '@rspack/binding-darwin-x64@2.0.8': + resolution: {integrity: sha512-satPm2PD4B7jDTVlVAdvMVdUszwLvWUEnUDzLb77mvVkezKNDZmuhb+e8s+FfKs8hJpNbZ9VAejuA2rr8o985w==} + cpu: [x64] + os: [darwin] + '@rspack/binding-linux-arm64-gnu@2.0.2': resolution: {integrity: sha512-1ZD4YFhG1rmgqj+W8hfwHyKV8xDxGsc/3KgU0FwmiVEX7JfzhCkgBO/xlCG79kRKSrzuVzt4icO/G3cCKn0pag==} cpu: [arm64] os: [linux] libc: [glibc] + '@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-musl@2.0.2': resolution: {integrity: sha512-/PtTkM/DsDLjeuXTmeJeRfbjCDbcL9jvoVgZrgxYFZ28y2cdLvbChbW9uigOzs5dQEs1CIBQXMTTj7KhdBTuQg==} cpu: [arm64] os: [linux] libc: [musl] + '@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-x64-gnu@2.0.2': resolution: {integrity: sha512-bBjsZxMHRaPo6X9SokApm6ucs+UhXtAJFyJJyuk2BH4XJsLeCU9Dz1vMwioeohFbJUUeTASVPm6/BL+RhSaunw==} cpu: [x64] os: [linux] libc: [glibc] + '@rspack/binding-linux-x64-gnu@2.0.8': + resolution: {integrity: sha512-zrkoEOnqj1hOEBO5T2I/2Ts2HSJsYFh1qXwMpK4dMJFGGNWDfNeUa6/LF5uq3VINF3JUl7RL47AgrucoSZJXPA==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rspack/binding-linux-x64-musl@2.0.2': resolution: {integrity: sha512-HjlpInqzabDNkhVsUJpsHPqa9QYVWBViJoyWNjzXCAW0vKMDvwaphyUvokSinX8FGTlZi/sr5UEaHJo6XtQ35g==} cpu: [x64] os: [linux] libc: [musl] + '@rspack/binding-linux-x64-musl@2.0.8': + resolution: {integrity: sha512-6CtDaGZjNDvJd9TBp7a9zABbrPORO21W96+3ZcGBn0YNUPUk4ARxIxrTTpeJ/1F41QDM8AYIkGDdqEYMqTYBsA==} + cpu: [x64] + os: [linux] + libc: [musl] + '@rspack/binding-wasm32-wasi@2.0.2': resolution: {integrity: sha512-YaRYNFLJRpkGfYjSWR7n9f+nQKtrlmrrffpAn/blc2geHcRvXoBc5SCs1idPtsLhj7H9qWWhs7ucjyHy4csWFg==} cpu: [wasm32] + '@rspack/binding-wasm32-wasi@2.0.8': + resolution: {integrity: sha512-Yf4SiqTUroT5Ju+te0YAY2xxKOb35tECsO21v7hYyGa705wrgoAK/MmF7enOvs9GR1iZIqgiLD/wxsIxl8GjJw==} + cpu: [wasm32] + '@rspack/binding-win32-arm64-msvc@2.0.2': resolution: {integrity: sha512-d/3kTEKq+asLjRFPO96t+wfWiM7DLN76VQEPDD9bc1kdsZXlVJBuvyXfsgK8bbEvKplWXYcSsokhmEnuXrLOpg==} cpu: [arm64] os: [win32] + '@rspack/binding-win32-arm64-msvc@2.0.8': + resolution: {integrity: sha512-8NCuiQsAhXrwRBy57QZoypqrws/zLBkaQVGiB8hksr6v++8hNigNjqpQARLbd0iyMuHsQQ++8+auGk6xlDXmzw==} + cpu: [arm64] + os: [win32] + '@rspack/binding-win32-ia32-msvc@2.0.2': resolution: {integrity: sha512-161cWineq3RW+Jdm1FAfSpXeUtYWvhB3kAbm46vNT9h/YYz+spwsFMvveAZ1nsVSVL0IC5lDBGUte7yUAY8K2g==} cpu: [ia32] os: [win32] + '@rspack/binding-win32-ia32-msvc@2.0.8': + resolution: {integrity: sha512-bxiekytbX7V9KFAra+HkwtNWC6pYfHEBBZFpiT0xUs3mCFOmAAFVBsBSQsoCP9AdCEXoMAvNdnrHNw3iov4OZw==} + cpu: [ia32] + os: [win32] + '@rspack/binding-win32-x64-msvc@2.0.2': resolution: {integrity: sha512-y7Q0S1FE+OlkL5GMqLG0PwxrPw6E1r892KhGrGKE1Vdufe5YTEx6xTPxzZ+b7N2KPD7s9G1/iJmWHQxb1+Bjkg==} cpu: [x64] os: [win32] + '@rspack/binding-win32-x64-msvc@2.0.8': + resolution: {integrity: sha512-7zPs8YCe/ZVJTwd+5lpB0CP0tkn2pONf/T1ycmVY76u21Nrwt8mXQGc/2yH2eWP4B7fikYBr3hGr7mpR2fajqQ==} + cpu: [x64] + os: [win32] + '@rspack/binding@2.0.2': resolution: {integrity: sha512-0kZPplW9GWx8mfC6DfsaRY3QBIYPuUs42JfmSM6aSb8tMHZAXQeLeMB8M+h8i4SeI+aFtCgO6UuYGtyWf7+L+A==} + '@rspack/binding@2.0.8': + resolution: {integrity: sha512-3uZ+y8aQxq33ty2srMxg2Nu0XuBI6vVrG50rkDaXqwWqOohfgGUSfFuQK7EnSUNy4aFUQlCG6NHialQHJov0wg==} + '@rspack/cli@2.0.2': resolution: {integrity: sha512-rwuzo/V59ck/ChNkxMEEjuXLFs8nPJ37K0PMuUeIMmzFJNdUcPLxSV3qaV9azDLGcyq4XwcyQvexH3okWbfUfQ==} hasBin: true @@ -1806,6 +1878,18 @@ packages: '@swc/helpers': optional: true + '@rspack/core@2.0.8': + resolution: {integrity: sha512-+NLGJf8gZxihDmMFzjlly3toc2SMjeDmuvz0/Cai9AMdV4F+Pqcnt2BA9V4e3SY2jmhJQtPwgyyLtR1RiJO77g==} + 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==} @@ -1884,6 +1968,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==} @@ -6502,24 +6589,51 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.3': optional: true + '@rsbuild/core@2.0.14(core-js@3.49.0)': + dependencies: + '@rspack/core': 2.0.8(@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.2': optional: true + '@rspack/binding-darwin-arm64@2.0.8': + optional: true + '@rspack/binding-darwin-x64@2.0.2': optional: true + '@rspack/binding-darwin-x64@2.0.8': + optional: true + '@rspack/binding-linux-arm64-gnu@2.0.2': optional: true + '@rspack/binding-linux-arm64-gnu@2.0.8': + optional: true + '@rspack/binding-linux-arm64-musl@2.0.2': optional: true + '@rspack/binding-linux-arm64-musl@2.0.8': + optional: true + '@rspack/binding-linux-x64-gnu@2.0.2': optional: true + '@rspack/binding-linux-x64-gnu@2.0.8': + optional: true + '@rspack/binding-linux-x64-musl@2.0.2': optional: true + '@rspack/binding-linux-x64-musl@2.0.8': + optional: true + '@rspack/binding-wasm32-wasi@2.0.2': dependencies: '@emnapi/core': 1.10.0 @@ -6527,15 +6641,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.0.8': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + '@rspack/binding-win32-arm64-msvc@2.0.2': optional: true + '@rspack/binding-win32-arm64-msvc@2.0.8': + optional: true + '@rspack/binding-win32-ia32-msvc@2.0.2': optional: true + '@rspack/binding-win32-ia32-msvc@2.0.8': + optional: true + '@rspack/binding-win32-x64-msvc@2.0.2': optional: true + '@rspack/binding-win32-x64-msvc@2.0.8': + optional: true + '@rspack/binding@2.0.2': optionalDependencies: '@rspack/binding-darwin-arm64': 2.0.2 @@ -6549,15 +6679,34 @@ snapshots: '@rspack/binding-win32-ia32-msvc': 2.0.2 '@rspack/binding-win32-x64-msvc': 2.0.2 - '@rspack/cli@2.0.2(@rspack/core@2.0.2(@swc/helpers@0.5.21))': + '@rspack/binding@2.0.8': + optionalDependencies: + '@rspack/binding-darwin-arm64': 2.0.8 + '@rspack/binding-darwin-x64': 2.0.8 + '@rspack/binding-linux-arm64-gnu': 2.0.8 + '@rspack/binding-linux-arm64-musl': 2.0.8 + '@rspack/binding-linux-x64-gnu': 2.0.8 + '@rspack/binding-linux-x64-musl': 2.0.8 + '@rspack/binding-wasm32-wasi': 2.0.8 + '@rspack/binding-win32-arm64-msvc': 2.0.8 + '@rspack/binding-win32-ia32-msvc': 2.0.8 + '@rspack/binding-win32-x64-msvc': 2.0.8 + + '@rspack/cli@2.0.2(@rspack/core@2.0.2(@swc/helpers@0.5.23))': dependencies: - '@rspack/core': 2.0.2(@swc/helpers@0.5.21) + '@rspack/core': 2.0.2(@swc/helpers@0.5.23) - '@rspack/core@2.0.2(@swc/helpers@0.5.21)': + '@rspack/core@2.0.2(@swc/helpers@0.5.23)': dependencies: '@rspack/binding': 2.0.2 optionalDependencies: - '@swc/helpers': 0.5.21 + '@swc/helpers': 0.5.23 + + '@rspack/core@2.0.8(@swc/helpers@0.5.23)': + dependencies: + '@rspack/binding': 2.0.8 + optionalDependencies: + '@swc/helpers': 0.5.23 '@shikijs/core@3.23.0': dependencies: @@ -6682,6 +6831,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 131d94b2..a70775de 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -51,6 +51,7 @@ catalogs: peer: '@farmfe/core': ^1.7.11 + '@rsbuild/core': ^2.0.14 '@rspack/core': ^2.0.2 rolldown: ^1.0.0 rollup: ^4.60.3 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..2a64e52b --- /dev/null +++ b/src/rsbuild/index.ts @@ -0,0 +1,55 @@ +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 ||= [] + config.plugins.push(getRspackPluginFromRaw([rawPlugin], meta, { applyRspackHook: false })) + }) + + await rsbuildOptions?.setup?.(api) + }, + } +} diff --git a/src/rspack/index.ts b/src/rspack/index.ts index bc09da0e..2012ffa2 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,16 +28,16 @@ const LOAD_LOADER = resolve( import.meta.dev ? '../../dist/rspack/loaders/load.mjs' : 'rspack/loaders/load.mjs', ) +interface ApplyRspackPluginsOptions { + applyRspackHook?: boolean +} + 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', @@ -46,187 +47,224 @@ export function getRspackPlugin>( }, } 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, + options?: ApplyRspackPluginsOptions, +): RspackPluginInstance { + return { + apply(compiler) { + applyRspackPlugins(compiler, rawPlugins, meta, options) + }, + } +} + +function applyRspackPlugins( + compiler: Compiler, + rawPlugins: UnpluginOptions[], + meta: UnpluginContextMeta, + options: ApplyRspackPluginsOptions = {}, +) { + const { applyRspackHook = true } = options + + // 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 + meta.versions = { + ...meta.versions, + rspack: version, + 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) { + 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}`) }, - ) 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 (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 (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 (plugin.buildEnd) { - compiler.hooks.emit.tapPromise(plugin.name, async (compilation) => { - await plugin.buildEnd!.call(createBuildContext(compiler, compilation)) - }) + // 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 } - 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 (applyRspackHook && 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 83ab4311..0627eb56 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 { 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 @@ -169,6 +172,7 @@ export interface UnpluginInstance rolldown: UnpluginFactoryOutput : RolldownPlugin> webpack: UnpluginFactoryOutput rspack: UnpluginFactoryOutput + rsbuild: UnpluginFactoryOutput : RsbuildPlugin> esbuild: UnpluginFactoryOutput unloader: UnpluginFactoryOutput : UnloaderPlugin> farm: UnpluginFactoryOutput @@ -176,7 +180,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 & { /** @@ -206,6 +210,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 e98e484e..7f92f666 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', From 2188e424c8f1cb5b832370f7d6d6a2936ecf5978 Mon Sep 17 00:00:00 2001 From: Yaroslav Slepukhin Date: Sun, 21 Jun 2026 14:46:15 +0200 Subject: [PATCH 2/4] fix(rsbuild): ensure plugins array is initialized before modification --- src/rsbuild/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rsbuild/index.ts b/src/rsbuild/index.ts index 2a64e52b..c92a6916 100644 --- a/src/rsbuild/index.ts +++ b/src/rsbuild/index.ts @@ -45,7 +45,6 @@ function toRsbuildPlugin( } api.modifyRspackConfig((config) => { - config.plugins ||= [] config.plugins.push(getRspackPluginFromRaw([rawPlugin], meta, { applyRspackHook: false })) }) From baef83d18615275516c6d5cfe2b16bb7bc8b04eb Mon Sep 17 00:00:00 2001 From: Yaroslav Slepukhin Date: Sun, 21 Jun 2026 14:47:02 +0200 Subject: [PATCH 3/4] refactor(rspack): extract getRspackVersion function for version retrieval --- src/rspack/index.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/rspack/index.ts b/src/rspack/index.ts index 2012ffa2..2940126a 100644 --- a/src/rspack/index.ts +++ b/src/rspack/index.ts @@ -32,16 +32,19 @@ interface ApplyRspackPluginsOptions { applyRspackHook?: boolean } +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) { - 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, }, @@ -77,10 +80,9 @@ function applyRspackPlugins( // 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 meta.versions = { ...meta.versions, - rspack: version, + rspack: getRspackVersion(compiler), unplugin: meta.versions.unplugin ?? unpluginVersion, } if (meta.framework === 'rspack') @@ -100,14 +102,11 @@ function applyRspackPlugins( // 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) + // 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 From 5046ccf6602abcfb016952b6e04b8954cc5e898a Mon Sep 17 00:00:00 2001 From: Yaroslav Slepukhin Date: Sun, 21 Jun 2026 14:55:28 +0200 Subject: [PATCH 4/4] refactor(rspack): remove applyRspackHook option and simplify plugin application --- src/rsbuild/index.ts | 2 +- src/rspack/index.ts | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/rsbuild/index.ts b/src/rsbuild/index.ts index c92a6916..d69cb56b 100644 --- a/src/rsbuild/index.ts +++ b/src/rsbuild/index.ts @@ -45,7 +45,7 @@ function toRsbuildPlugin( } api.modifyRspackConfig((config) => { - config.plugins.push(getRspackPluginFromRaw([rawPlugin], meta, { applyRspackHook: false })) + config.plugins.push(getRspackPluginFromRaw([rawPlugin], meta)) }) await rsbuildOptions?.setup?.(api) diff --git a/src/rspack/index.ts b/src/rspack/index.ts index 2940126a..2fdefc1b 100644 --- a/src/rspack/index.ts +++ b/src/rspack/index.ts @@ -28,10 +28,6 @@ const LOAD_LOADER = resolve( import.meta.dev ? '../../dist/rspack/loaders/load.mjs' : 'rspack/loaders/load.mjs', ) -interface ApplyRspackPluginsOptions { - applyRspackHook?: boolean -} - function getRspackVersion(compiler: Compiler): string { return compiler.rspack.rspackVersion ?? compiler.rspack.version } @@ -59,11 +55,10 @@ export function getRspackPlugin>( export function getRspackPluginFromRaw( rawPlugins: UnpluginOptions[], meta: UnpluginContextMeta, - options?: ApplyRspackPluginsOptions, ): RspackPluginInstance { return { apply(compiler) { - applyRspackPlugins(compiler, rawPlugins, meta, options) + applyRspackPlugins(compiler, rawPlugins, meta) }, } } @@ -72,10 +67,7 @@ function applyRspackPlugins( compiler: Compiler, rawPlugins: UnpluginOptions[], meta: UnpluginContextMeta, - options: ApplyRspackPluginsOptions = {}, ) { - const { applyRspackHook = true } = options - // 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()) @@ -228,7 +220,7 @@ function applyRspackPlugins( }) } - if (applyRspackHook && plugin.rspack) + if (meta.framework === 'rspack' && plugin.rspack) plugin.rspack(compiler) if (plugin.watchChange || plugin.buildStart) {