├── .github ├── renovate.json5 └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── README.zh-CN.md ├── biome.json ├── package.json ├── playground ├── package.json ├── rsbuild.config.ts └── src │ ├── index.css │ └── index.js ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── rslib.config.ts ├── src ├── common │ ├── getBaseData.ts │ ├── getProjects.ts │ ├── index.ts │ ├── isMonorepo.ts │ ├── pnpm.ts │ └── rush.ts ├── constants.ts ├── index.ts ├── plugin.ts ├── project-utils │ ├── filter.ts │ ├── getDependentProjects.ts │ └── index.ts ├── project.ts ├── types │ ├── index.ts │ ├── packageJson.ts │ └── rushJson.ts └── utils.ts ├── test ├── app │ ├── package.json │ ├── rsbuild.config.ts │ ├── src │ │ └── index.tsx │ └── tsconfig.json ├── components │ ├── package.json │ ├── src │ │ ├── card │ │ │ ├── index.css │ │ │ └── index.tsx │ │ └── index.tsx │ └── tsconfig.json ├── helper.ts ├── index.test.ts ├── utils │ ├── package.json │ ├── src │ │ ├── common │ │ │ ├── index.ts │ │ │ ├── toLowerCase.ts │ │ │ └── toUpperCase.ts │ │ └── index.ts │ └── tsconfig.json └── utils2 │ ├── package.json │ ├── src │ ├── common │ │ ├── index.ts │ │ ├── toLowerCase.ts │ │ └── toUpperCase.ts │ └── index.ts │ └── tsconfig.json └── tsconfig.json /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base", "schedule:monthly", "group:allNonMajor"], 4 | "rangeStrategy": "bump", 5 | "packageRules": [{ "depTypeList": ["peerDependencies"], "enabled": false }] 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This action will publish the package to npm and create a GitHub release. 2 | name: Release 3 | 4 | on: 5 | # Run `npm run bump` to bump the version and create a git tag. 6 | push: 7 | tags: 8 | - "v*" 9 | 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: write 14 | id-token: write 15 | 16 | jobs: 17 | publish: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Install Pnpm 24 | run: | 25 | npm install -g corepack@latest --force 26 | corepack enable 27 | 28 | - name: Setup Node.js 29 | uses: actions/setup-node@v4 30 | with: 31 | node-version: 22 32 | cache: "pnpm" 33 | 34 | - name: Install Dependencies 35 | run: pnpm install 36 | 37 | - name: Publish 38 | uses: JS-DevTools/npm-publish@v3 39 | with: 40 | token: ${{ secrets.RSBUILD_PLUGIN_NPM_TOKEN }} 41 | 42 | - name: Create GitHub Release 43 | uses: ncipollo/release-action@v1 44 | with: 45 | generateReleaseNotes: "true" 46 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on pull request events but only for the main branch 6 | pull_request: 7 | branches: [main] 8 | push: 9 | branches: [main] 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | test: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest, windows-latest] 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | 26 | - name: Install Pnpm 27 | run: | 28 | npm install -g corepack@latest --force 29 | corepack enable 30 | 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: 22 35 | cache: "pnpm" 36 | 37 | - name: Install Dependencies 38 | run: pnpm install && npx playwright install 39 | 40 | - name: Run Test 41 | run: pnpm run test 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local 2 | .DS_Store 3 | *.local 4 | *.log* 5 | 6 | # Dist 7 | node_modules 8 | dist/ 9 | test-results 10 | 11 | # IDE 12 | .vscode/* 13 | !.vscode/settings.json 14 | !.vscode/extensions.json 15 | .idea 16 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["biomejs.biome"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.useIgnoreFiles": true, 3 | "[json]": { 4 | "editor.defaultFormatter": "biomejs.biome" 5 | }, 6 | "[typescript]": { 7 | "editor.defaultFormatter": "biomejs.biome" 8 | }, 9 | "[javascript]": { 10 | "editor.defaultFormatter": "biomejs.biome" 11 | }, 12 | "[javascriptreact]": { 13 | "editor.defaultFormatter": "biomejs.biome" 14 | }, 15 | "[css]": { 16 | "editor.defaultFormatter": "esbenp.prettier-vscode" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Rspack Contrib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @rsbuild/plugin-source-build 2 | 3 | An Rsbuild plugin to provide support for monorepo source code referencing. 4 | 5 | `@rsbuild/plugin-source-build` allows referencing source code from other subdirectories of monorepo and performs the build and hot updates. 6 | 7 |

8 | 9 | npm version 10 | 11 | license 12 |

13 | 14 | English | [简体中文](./README.zh-CN.md) 15 | 16 | ## Usage 17 | 18 | Install: 19 | 20 | ```bash 21 | npm add @rsbuild/plugin-source-build -D 22 | ``` 23 | 24 | Add plugin to your `rsbuild.config.ts`: 25 | 26 | ```ts 27 | // rsbuild.config.ts 28 | import { pluginSourceBuild } from "@rsbuild/plugin-source-build"; 29 | 30 | export default { 31 | plugins: [pluginSourceBuild()], 32 | }; 33 | ``` 34 | 35 | ## Use Cases 36 | 37 | In a monorepo, there are two main ways for projects to reference each other: **artifact referencing and source code referencing**. Let's use a simple monorepo as an example to illustrate the use case of source code referencing. 38 | 39 | For example, the monorepo contains an app application and a lib: 40 | 41 | ```ts 42 | monorepo 43 | ├── app 44 | └── lib 45 | └── src 46 | └── index.ts 47 | ``` 48 | 49 | The app is built using Rsbuild and relies on some methods from the lib: 50 | 51 | ```json 52 | { 53 | "name": "app", 54 | "dependencies": { 55 | "lib": "workspace:*" 56 | } 57 | } 58 | ``` 59 | 60 | ### Artifact Referencing 61 | 62 | **When using artifact referencing, the current project references the artifacts built from other sub-projects.** 63 | 64 | In the example above, the lib is written in TypeScript. Typically, we need to build the lib's code in advance to generate JavaScript artifacts so that the app can reference it correctly. When the lib's code is updated, we need to rebuild it (or use `tsc --watch`) to ensure that the app can use the latest code. 65 | 66 | The advantages of this approach are: 67 | 68 | - The build processes of each sub-project are completely independent and can have different build configurations. 69 | - Build caching can be applied to individual sub-projects. 70 | 71 | The disadvantages are: 72 | 73 | - The HMR chain becomes longer during local development. 74 | - The process becomes cumbersome when a project contains multiple lib packages. 75 | 76 | ### Source Code Referencing 77 | 78 | **When using source code referencing, the current project references the source code of other sub-projects for building.** 79 | 80 | In the example mentioned earlier, when you register the `@rsbuild/plugin-source-build` and add the relevant configuration in the `lib` directory, Rsbuild will automatically reference the `src/index.ts` source code of the lib. This means that you don't need to build the lib's code in advance, and when the source code of the lib is updated, it can trigger automatic hot updates for the app. 81 | 82 | The advantages of this approach are: 83 | 84 | - The sub-project does not rely on a build tool and does not require build configurations. The code of the sub-project will be compiled by the build tool of the current project. 85 | - There is no need to execute the build process for the sub-projects in advance. 86 | - HMR is more efficient during local development. 87 | 88 | The disadvantages are: 89 | 90 | - The current project needs to support syntax features used by sub-projects and follow the same syntax specifications, such as using a consistent version of decorator syntax. If the current project and sub-projects require different build configurations, building from source code may not be suitable. 91 | - The current project requires compiling more code, which may result in longer build times. 92 | 93 | ### Configuring Sub-projects 94 | 95 | When the `@rsbuild/plugin-source-build` is registered, the Rsbuild will prioritize reading the file specified in the `source` field of the sub-project during the build process. Therefore, you need to configure the `source` field in the package.json file of the sub-project and point it to the source code file. 96 | 97 | For example, in the following example, when the lib package is referenced, the `./src/index.ts` file will be read for building: 98 | 99 | ```json title="package.json" 100 | { 101 | "name": "lib", 102 | "main": "./dist/index.js", 103 | "source": "./src/index.ts" 104 | } 105 | ``` 106 | 107 | If the sub-project uses [exports](https://nodejs.org/api/packages.html#package-entry-points) field, you also need to add the `source` field in the `exports` field. 108 | 109 | ```json title="package.json" 110 | { 111 | "name": "lib", 112 | "exports": { 113 | ".": { 114 | "source": "./src/index.ts", 115 | "default": "./dist/index.js" 116 | }, 117 | "./features": { 118 | "source": "./src/features/index.ts", 119 | "default": "./dist/features/index.js" 120 | } 121 | } 122 | } 123 | ``` 124 | 125 | ## Configure Project Reference 126 | 127 | In a TypeScript project, you need to use the capability provided by TypeScript called [Project Reference](https://typescriptlang.org/docs/handbook/project-references). It helps you develop source code more effectively. 128 | 129 | ### Introduction 130 | 131 | Project reference provides the following capabilities: 132 | 133 | - It allows TypeScript to correctly recognize the types of other sub-projects without the need to build them. 134 | - When you navigate the code in VS Code, it automatically takes you to the corresponding source code file of the module. 135 | - Rsbuild reads the project reference configuration and automatically recognizes the `tsconfig.compilerOptions.path` configuration of the sub-project, so that the use of aliases in the sub-project works correctly. 136 | 137 | ### Example 138 | 139 | In the example mentioned earlier, since the app project references the lib sub-project, we need to configure the `references` options in the app project's `tsconfig.json` to point to the relative directory of the lib: 140 | 141 | ```json title="app/tsconfig.json" 142 | { 143 | "references": [ 144 | { 145 | "path": "../lib" 146 | } 147 | ] 148 | } 149 | ``` 150 | 151 | At the same time, we need to set `composite` to `true` in the lib project's `tsconfig.json`: 152 | 153 | ```json title="lib/A/tsconfig.json" 154 | { 155 | "compilerOptions": { 156 | "composite": true 157 | }, 158 | } 159 | ``` 160 | 161 | After adding these two options, the project reference is already configured. You can restart VS Code to see the effects of the configuration. 162 | 163 | Note that the above example is a simplified one. In real monorepo projects, there may be more complex dependency relationships. You need to add a complete `references` configuration for the functionality to work correctly. 164 | 165 | > If you want to learn more about project reference, please refer to the official documentation on [TypeScript - Project References](https://typescriptlang.org/docs/handbook/project-references). 166 | 167 | ## Options 168 | 169 | ### sourceField 170 | 171 | - **Type:** `string` 172 | - **Default:** `'source'` 173 | 174 | Used to configure the resolve field of the source code files. 175 | 176 | For example, when configured as `my-source`: 177 | 178 | ```ts 179 | pluginSourceBuild({ 180 | sourceField: "my-source", 181 | }); 182 | ``` 183 | 184 | In `package.json`, the source code file path can be specified using `my-source`: 185 | 186 | ```json title="package.json" 187 | { 188 | "name": "lib", 189 | "main": "./dist/index.js", 190 | "my-source": "./src/index.ts", 191 | "exports": { 192 | ".": { 193 | "my-source": "./src/index.ts", 194 | "default": "./dist/index.js" 195 | } 196 | } 197 | } 198 | ``` 199 | 200 | ### resolvePriority 201 | 202 | - **Type:** `'source' | 'output'` 203 | - **Default:** `'source'` 204 | 205 | Used to control the priority of reading the source code or the output code. 206 | 207 | By default, `@rsbuild/plugin-source-build` will reading the source code first, for example, in the following example, it will read the `source` field. 208 | 209 | ```json title="package.json" 210 | { 211 | "name": "lib", 212 | "main": "./dist/index.js", 213 | "source": "./src/index.ts" 214 | } 215 | ``` 216 | 217 | When `resolvePriority` is set to `'output'`, `@rsbuild/plugin-source-build` will read the output code first, i.e., the code from the `main` or `module` field. 218 | 219 | ```ts 220 | pluginSourceBuild({ 221 | resolvePriority: "output", 222 | }); 223 | ``` 224 | 225 | - The `exports` field in package.json is not affected by `resolvePriority`. 226 | - The keys order in `exports` determines the resolving order, earlier declared keys having higher priority. 227 | 228 | ## Caveat 229 | 230 | When using `@rsbuild/plugin-source-build`, there are a few things to keep in mind: 231 | 232 | 1. Ensure that the current project can compile the syntax or features used in the sub-project. For example, if the sub-project uses Stylus to write CSS, the current app needs to support Stylus compilation. 233 | 2. Ensure that the current project has the same code syntax and features as the sub-project, such as consistent syntax versions for decorators. 234 | 3. Source code building may have some limitations. When encountering issues, you can remove the `source` field from the sub-project's package.json and debug using the built artifacts of the sub-project. 235 | 4. When `composite: true` is enabled, TypeScript will generate `*.tsbuildinfo` temporary files. You need to add these temporary files to the `.gitignore` file. 236 | 237 | ```text title=".gitignore" 238 | *.tsbuildinfo 239 | ``` 240 | 241 | ## License 242 | 243 | [MIT](./LICENSE). 244 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # @rsbuild/plugin-source-build 2 | 3 | 提供对 monorepo 源代码引用的支持。 4 | 5 | `@rsbuild/plugin-source-build` 用于 monorepo 开发场景,它支持从当前项目引用其他子目录的源代码,并完成构建和热更新。 6 | 7 |

8 | 9 | npm version 10 | 11 | license 12 |

13 | 14 | ## 使用 15 | 16 | 安装: 17 | 18 | ```bash 19 | npm add @rsbuild/plugin-source-build -D 20 | ``` 21 | 22 | 在 `rsbuild.config.ts` 里注册插件: 23 | 24 | ```ts 25 | // rsbuild.config.ts 26 | import { pluginSourceBuild } from "@rsbuild/plugin-source-build"; 27 | 28 | export default { 29 | plugins: [pluginSourceBuild()], 30 | }; 31 | ``` 32 | 33 | ## 使用场景 34 | 35 | 在 monorepo 中,子项目互相引用主要有两种方式 —— **产物引用和源码引用**。我们以一个最简单的 monorepo 为例子,来介绍源码引用的使用场景。 36 | 37 | 比如 monorepo 中包含了一个 app 应用和一个 lib 包: 38 | 39 | ```ts 40 | monorepo 41 | ├── app 42 | └── lib 43 | └── src 44 | └── index.ts 45 | ``` 46 | 47 | 其中,app 是基于 Rsbuild 构建的,app 依赖了 lib 中的一些方法: 48 | 49 | ```json 50 | { 51 | "name": "app", 52 | "dependencies": { 53 | "lib": "workspace:*" 54 | } 55 | } 56 | ``` 57 | 58 | ### 产物引用 59 | 60 | **产物引用指的是当前项目引用其他子项目构建后的产物。** 61 | 62 | 比如上述例子中的 lib 是使用 TypeScript 编写的,通常情况下,我们需要提前构建 lib 的代码,生成 JavaScript 产物,这样 app 才可以正确引用它。当 lib 代码更新时,还需要重新执行一次构建(或者使用 `tsc --watch`),否则 app 无法引用到最新的代码。 63 | 64 | 这种方式的优势在于: 65 | 66 | - 各个子项目的构建过程是完全独立的,可以拥有不同的构建配置。 67 | - 可以针对子项目进行构建缓存。 68 | 69 | 劣势在于: 70 | 71 | - 本地开发时 HMR 的链路变长。 72 | - 当一个项目中包含多个 lib 包时,以上过程会显得十分繁琐。 73 | 74 | ### 源码引用 75 | 76 | **源码引用指的是当前项目引用其他子项目的源码进行构建。** 77 | 78 | 比如上述例子,当你注册了 `@rsbuild/plugin-source-build`,并在 lib 中添加相关配置后,Rsbuild 会自动引用 lib 的 `src/index.ts` 源代码。这意味着,你不需要提前构建 lib 的代码,并且当 lib 的源代码更新时,也可以自动触发 app 的热更新。 79 | 80 | 这种方式的优势在于: 81 | 82 | - 子项目不依赖构建工具,也不需要添加构建配置,子项目的代码会被当前项目的构建工具编译。 83 | - 不需要提前执行子项目的构建流程。 84 | - 本地开发时 HMR 更高效。 85 | 86 | 劣势在于: 87 | 88 | - 当前项目需要支持子项目用到的语法特性,并且遵循相同的语法规范,比如使用一致的装饰器语法版本。如果当前项目和子项目需要使用不同的编译配置,则不适合使用 `@rsbuild/plugin-source-build`。 89 | - 当前项目需要编译更多的代码,因此构建时间可能会变长。 90 | 91 | ### 配置子项目 92 | 93 | 当注册 `@rsbuild/plugin-source-build`后,Rsbuild 在构建过程中,会优先读取子项目的 `source` 字段对应的文件。因此,你需要在子项目的 package.json 中配置 `source` 字段,并且指向源码文件路径。 94 | 95 | 比如以下例子,当 lib 包被引用时,会读取 `./src/index.ts` 文件进行构建: 96 | 97 | ```json title="package.json" 98 | { 99 | "name": "lib", 100 | "main": "./dist/index.js", 101 | "source": "./src/index.ts" 102 | } 103 | ``` 104 | 105 | 如果子项目使用了 [exports](https://nodejs.org/api/packages.html#package-entry-points) 配置,那么你同样需要在 `exports` 中增加 `source` 字段。 106 | 107 | ```json title="package.json" 108 | { 109 | "name": "lib", 110 | "exports": { 111 | ".": { 112 | "source": "./src/index.ts", 113 | "default": "./dist/index.js" 114 | }, 115 | "./features": { 116 | "source": "./src/features/index.ts", 117 | "default": "./dist/features/index.js" 118 | } 119 | } 120 | } 121 | ``` 122 | 123 | ## 配置 Project Reference 124 | 125 | 在 TypeScript 项目中,你需要使用 TypeScript 提供的 [Project Reference](https://typescriptlang.org/docs/handbook/project-references) 能力,它可以帮助你更好地使用源码开发。 126 | 127 | ### 介绍 128 | 129 | Project reference 提供了以下能力: 130 | 131 | - 使 TypeScript 可以正确识别其他子项目的类型,而无须对子项目进行构建。 132 | - 当你在 VS Code 内进行代码跳转时,VS Code 可以自动跳转到对应模块的源代码文件。 133 | - Rsbuild 会读取 project reference 配置,并自动识别子项目的 `tsconfig.compilerOptions.path` 配置,从而让子项目的别名可以正确生效。 134 | 135 | ### 示例 136 | 137 | 在上文的例子中,由于 app 引用了 lib 子项目,我们需要在 app 的 `tsconfig.json` 内配置 `references`,并指向 lib 对应的相对目录: 138 | 139 | ```json title="app/tsconfig.json" 140 | { 141 | "references": [ 142 | { 143 | "path": "../lib" 144 | } 145 | ] 146 | } 147 | ``` 148 | 149 | 同时,需要在 lib 子项目的 `tsconfig.json` 内配置 `composite` 为 `true`: 150 | 151 | ```json title="lib/A/tsconfig.json" 152 | { 153 | "compilerOptions": { 154 | "composite": true 155 | }, 156 | } 157 | ``` 158 | 159 | 添加以上两个选项后,project reference 就已经配置完成了,你可以重新启动 VS Code 来查看配置以后的效果。 160 | 161 | 注意以上只是一个最简单的例子,在实际的 monorepo 项目中,可能会有更复杂的依赖关系,你需要添加完整的 `references` 配置,才能使上述功能正确运作。 162 | 163 | > 如果你想了解更多关于 project reference 的内容,请阅读 [TypeScript - Project References](https://typescriptlang.org/docs/handbook/project-references) 官方文档。 164 | 165 | ## 选项 166 | 167 | ### sourceField 168 | 169 | - **类型:** `string` 170 | - **默认值:** `'source'` 171 | 172 | 用于配置源代码文件对应的解析字段。 173 | 174 | 比如配置为 `my-source`: 175 | 176 | ```ts 177 | pluginSourceBuild({ 178 | sourceField: "my-source", 179 | }); 180 | ``` 181 | 182 | 在 `package.json` 中,即可通过 `my-source` 指定源代码文件的路径: 183 | 184 | ```json title="package.json" 185 | { 186 | "name": "lib", 187 | "main": "./dist/index.js", 188 | "my-source": "./src/index.ts", 189 | "exports": { 190 | ".": { 191 | "my-source": "./src/index.ts", 192 | "default": "./dist/index.js" 193 | } 194 | } 195 | } 196 | ``` 197 | 198 | ### resolvePriority 199 | 200 | - **类型:** `'source' | 'output'` 201 | - **默认值:** `'source'` 202 | 203 | 用于控制优先读取源代码还是产物代码。 204 | 205 | 默认情况下,`@rsbuild/plugin-source-build`会优先读取源代码,比如在下面的例子中,它会读取 `source` 字段。 206 | 207 | ```json title="package.json" 208 | { 209 | "name": "lib", 210 | "main": "./dist/index.js", 211 | "source": "./src/index.ts" 212 | } 213 | ``` 214 | 215 | 当 `resolvePriority` 设置为 `'output'` 时,`@rsbuild/plugin-source-build`会优先读取产物代码,即 `main` 或 `module` 字段指向的代码。 216 | 217 | ```ts 218 | pluginSourceBuild({ 219 | resolvePriority: "output", 220 | }); 221 | ``` 222 | 223 | - package.json 中的 `exports` 字段不受 `resolvePriority` 的影响。 224 | - `exports` 中 key 的声明顺序决定了读取顺序,较早声明的 key 具有更高的优先级。 225 | 226 | ## 注意事项 227 | 228 | 在使用 `@rsbuild/plugin-source-build`的时候,需要注意几点: 229 | 230 | 1. 需要保证当前项目可以编译子项目里使用的语法或特性。比如子项目使用了 Stylus 来编写 CSS 样式,那就需要当前 app 支持 Stylus 编译。 231 | 2. 需要保证当前项目与子项目使用的代码语法特性相同,例如装饰器的语法版本一致。 232 | 3. 源码构建可能存在一些限制。如果在使用中遇到问题,你可以将子项目 package.json 中的 `source` 字段移除,使用子项目的构建产物进行调试。 233 | 4. 开启 `composite: true` 后,TypeScript 会生成 `*.tsbuildinfo` 临时文件,你需要将这些临时文件加入 .gitignore 中。 234 | 235 | ```text title=".gitignore" 236 | *.tsbuildinfo 237 | ``` 238 | 239 | ## License 240 | 241 | [MIT](./LICENSE). 242 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "vcs": { 7 | "enabled": true, 8 | "defaultBranch": "main", 9 | "clientKind": "git", 10 | "useIgnoreFile": true 11 | }, 12 | "formatter": { 13 | "indentStyle": "space" 14 | }, 15 | "javascript": { 16 | "formatter": { 17 | "quoteStyle": "single" 18 | } 19 | }, 20 | "css": { 21 | "formatter": { 22 | "enabled": true 23 | } 24 | }, 25 | "linter": { 26 | "enabled": true, 27 | "rules": { 28 | "recommended": true 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rsbuild/plugin-source-build", 3 | "version": "1.0.2", 4 | "description": "An Rsbuild plugin to provide support for monorepo source code referencing.", 5 | "repository": "https://github.com/rspack-contrib/rsbuild-plugin-source-build", 6 | "license": "MIT", 7 | "type": "module", 8 | "exports": { 9 | ".": { 10 | "types": "./dist/index.d.ts", 11 | "import": "./dist/index.js", 12 | "require": "./dist/index.cjs" 13 | } 14 | }, 15 | "main": "./dist/index.js", 16 | "module": "./dist/index.mjs", 17 | "types": "./dist/index.d.ts", 18 | "files": ["dist"], 19 | "scripts": { 20 | "build": "rslib build", 21 | "dev": "rslib build --watch", 22 | "lint": "biome check .", 23 | "lint:write": "biome check . --write", 24 | "prepare": "simple-git-hooks && npm run build", 25 | "test": "playwright test", 26 | "bump": "npx bumpp" 27 | }, 28 | "simple-git-hooks": { 29 | "pre-commit": "npx nano-staged" 30 | }, 31 | "nano-staged": { 32 | "*.{js,jsx,ts,tsx,mjs,cjs}": [ 33 | "biome check --write --no-errors-on-unmatched" 34 | ] 35 | }, 36 | "dependencies": { 37 | "fast-glob": "^3.3.3", 38 | "json5": "^2.2.3", 39 | "yaml": "^2.8.0" 40 | }, 41 | "devDependencies": { 42 | "@biomejs/biome": "^1.9.4", 43 | "@playwright/test": "^1.52.0", 44 | "@rsbuild/core": "^1.3.22", 45 | "@rsbuild/plugin-react": "^1.3.1", 46 | "@rsbuild/plugin-type-check": "^1.2.2", 47 | "@rslib/core": "^0.9.1", 48 | "@types/node": "^22.15.29", 49 | "@types/react": "^19.1.6", 50 | "@types/react-dom": "^19.1.5", 51 | "nano-staged": "^0.8.0", 52 | "playwright": "^1.52.0", 53 | "react": "^19.1.0", 54 | "react-dom": "^19.1.0", 55 | "simple-git-hooks": "^2.13.0", 56 | "typescript": "^5.8.3" 57 | }, 58 | "peerDependencies": { 59 | "@rsbuild/core": "1.x" 60 | }, 61 | "peerDependenciesMeta": { 62 | "@rsbuild/core": { 63 | "optional": true 64 | } 65 | }, 66 | "packageManager": "pnpm@10.11.0", 67 | "publishConfig": { 68 | "access": "public", 69 | "registry": "https://registry.npmjs.org/" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "npx rsbuild dev", 7 | "build": "npx rsbuild build" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /playground/rsbuild.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@rsbuild/core'; 2 | import { pluginSourceBuild } from '../src'; 3 | 4 | export default defineConfig({ 5 | plugins: [pluginSourceBuild()], 6 | }); 7 | -------------------------------------------------------------------------------- /playground/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | color: #fff; 4 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 5 | background-image: linear-gradient(to bottom, #020917, #101725); 6 | } 7 | 8 | .content { 9 | display: flex; 10 | min-height: 100vh; 11 | line-height: 1.1; 12 | text-align: center; 13 | flex-direction: column; 14 | justify-content: center; 15 | } 16 | 17 | .content h1 { 18 | font-size: 3.6rem; 19 | font-weight: 700; 20 | } 21 | 22 | .content p { 23 | font-size: 1.2rem; 24 | font-weight: 400; 25 | opacity: 0.5; 26 | } 27 | -------------------------------------------------------------------------------- /playground/src/index.js: -------------------------------------------------------------------------------- 1 | import './index.css'; 2 | 3 | document.querySelector('#root').innerHTML = ` 4 |
5 |

Vanilla Rsbuild

6 |

Start building amazing things with Rsbuild.

7 |
8 | `; 9 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | fast-glob: 12 | specifier: ^3.3.3 13 | version: 3.3.3 14 | json5: 15 | specifier: ^2.2.3 16 | version: 2.2.3 17 | yaml: 18 | specifier: ^2.8.0 19 | version: 2.8.0 20 | devDependencies: 21 | '@biomejs/biome': 22 | specifier: ^1.9.4 23 | version: 1.9.4 24 | '@playwright/test': 25 | specifier: ^1.52.0 26 | version: 1.52.0 27 | '@rsbuild/core': 28 | specifier: ^1.3.22 29 | version: 1.3.22 30 | '@rsbuild/plugin-react': 31 | specifier: ^1.3.1 32 | version: 1.3.1(@rsbuild/core@1.3.22) 33 | '@rsbuild/plugin-type-check': 34 | specifier: ^1.2.2 35 | version: 1.2.2(@rsbuild/core@1.3.22)(@rspack/core@1.3.12(@swc/helpers@0.5.17))(typescript@5.8.3) 36 | '@rslib/core': 37 | specifier: ^0.9.1 38 | version: 0.9.1(typescript@5.8.3) 39 | '@types/node': 40 | specifier: ^22.15.29 41 | version: 22.15.29 42 | '@types/react': 43 | specifier: ^19.1.6 44 | version: 19.1.6 45 | '@types/react-dom': 46 | specifier: ^19.1.5 47 | version: 19.1.5(@types/react@19.1.6) 48 | nano-staged: 49 | specifier: ^0.8.0 50 | version: 0.8.0 51 | playwright: 52 | specifier: ^1.52.0 53 | version: 1.52.0 54 | react: 55 | specifier: ^19.1.0 56 | version: 19.1.0 57 | react-dom: 58 | specifier: ^19.1.0 59 | version: 19.1.0(react@19.1.0) 60 | simple-git-hooks: 61 | specifier: ^2.13.0 62 | version: 2.13.0 63 | typescript: 64 | specifier: ^5.8.3 65 | version: 5.8.3 66 | 67 | playground: {} 68 | 69 | test/app: 70 | dependencies: 71 | '@e2e/source-build-components': 72 | specifier: workspace:* 73 | version: link:../components 74 | '@rsbuild/plugin-source-build': 75 | specifier: workspace:* 76 | version: link:../.. 77 | 78 | test/components: 79 | dependencies: 80 | '@e2e/source-build-utils': 81 | specifier: workspace:* 82 | version: link:../utils 83 | '@e2e/source-build-utils2': 84 | specifier: workspace:* 85 | version: link:../utils2 86 | 87 | test/utils: {} 88 | 89 | test/utils2: {} 90 | 91 | packages: 92 | 93 | '@ast-grep/napi-darwin-arm64@0.37.0': 94 | resolution: {integrity: sha512-QAiIiaAbLvMEg/yBbyKn+p1gX2/FuaC0SMf7D7capm/oG4xGMzdeaQIcSosF4TCxxV+hIH4Bz9e4/u7w6Bnk3Q==} 95 | engines: {node: '>= 10'} 96 | cpu: [arm64] 97 | os: [darwin] 98 | 99 | '@ast-grep/napi-darwin-x64@0.37.0': 100 | resolution: {integrity: sha512-zvcvdgekd4ySV3zUbUp8HF5nk5zqwiMXTuVzTUdl/w08O7JjM6XPOIVT+d2o/MqwM9rsXdzdergY5oY2RdhSPA==} 101 | engines: {node: '>= 10'} 102 | cpu: [x64] 103 | os: [darwin] 104 | 105 | '@ast-grep/napi-linux-arm64-gnu@0.37.0': 106 | resolution: {integrity: sha512-L7Sj0lXy8X+BqSMgr1LB8cCoWk0rericdeu+dC8/c8zpsav5Oo2IQKY1PmiZ7H8IHoFBbURLf8iklY9wsD+cyA==} 107 | engines: {node: '>= 10'} 108 | cpu: [arm64] 109 | os: [linux] 110 | 111 | '@ast-grep/napi-linux-arm64-musl@0.37.0': 112 | resolution: {integrity: sha512-LF9sAvYy6es/OdyJDO3RwkX3I82Vkfsng1sqUBcoWC1jVb1wX5YVzHtpQox9JrEhGl+bNp7FYxB4Qba9OdA5GA==} 113 | engines: {node: '>= 10'} 114 | cpu: [arm64] 115 | os: [linux] 116 | 117 | '@ast-grep/napi-linux-x64-gnu@0.37.0': 118 | resolution: {integrity: sha512-TViz5/klqre6aSmJzswEIjApnGjJzstG/SE8VDWsrftMBMYt2PTu3MeluZVwzSqDao8doT/P+6U11dU05UOgxw==} 119 | engines: {node: '>= 10'} 120 | cpu: [x64] 121 | os: [linux] 122 | 123 | '@ast-grep/napi-linux-x64-musl@0.37.0': 124 | resolution: {integrity: sha512-/BcCH33S9E3ovOAEoxYngUNXgb+JLg991sdyiNP2bSoYd30a9RHrG7CYwW6fMgua3ijQ474eV6cq9yZO1bCpXg==} 125 | engines: {node: '>= 10'} 126 | cpu: [x64] 127 | os: [linux] 128 | 129 | '@ast-grep/napi-win32-arm64-msvc@0.37.0': 130 | resolution: {integrity: sha512-TjQA4cFoIEW2bgjLkaL9yqT4XWuuLa5MCNd0VCDhGRDMNQ9+rhwi9eLOWRaap3xzT7g+nlbcEHL3AkVCD2+b3A==} 131 | engines: {node: '>= 10'} 132 | cpu: [arm64] 133 | os: [win32] 134 | 135 | '@ast-grep/napi-win32-ia32-msvc@0.37.0': 136 | resolution: {integrity: sha512-uNmVka8fJCdYsyOlF9aZqQMLTatEYBynjChVTzUfFMDfmZ0bihs/YTqJVbkSm8TZM7CUX82apvn50z/dX5iWRA==} 137 | engines: {node: '>= 10'} 138 | cpu: [ia32] 139 | os: [win32] 140 | 141 | '@ast-grep/napi-win32-x64-msvc@0.37.0': 142 | resolution: {integrity: sha512-vCiFOT3hSCQuHHfZ933GAwnPzmL0G04JxQEsBRfqONywyT8bSdDc/ECpAfr3S9VcS4JZ9/F6tkePKW/Om2Dq2g==} 143 | engines: {node: '>= 10'} 144 | cpu: [x64] 145 | os: [win32] 146 | 147 | '@ast-grep/napi@0.37.0': 148 | resolution: {integrity: sha512-Hb4o6h1Pf6yRUAX07DR4JVY7dmQw+RVQMW5/m55GoiAT/VRoKCWBtIUPPOnqDVhbx1Cjfil9b6EDrgJsUAujEQ==} 149 | engines: {node: '>= 10'} 150 | 151 | '@babel/code-frame@7.24.7': 152 | resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} 153 | engines: {node: '>=6.9.0'} 154 | 155 | '@babel/helper-validator-identifier@7.24.7': 156 | resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} 157 | engines: {node: '>=6.9.0'} 158 | 159 | '@babel/highlight@7.24.7': 160 | resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} 161 | engines: {node: '>=6.9.0'} 162 | 163 | '@biomejs/biome@1.9.4': 164 | resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} 165 | engines: {node: '>=14.21.3'} 166 | hasBin: true 167 | 168 | '@biomejs/cli-darwin-arm64@1.9.4': 169 | resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} 170 | engines: {node: '>=14.21.3'} 171 | cpu: [arm64] 172 | os: [darwin] 173 | 174 | '@biomejs/cli-darwin-x64@1.9.4': 175 | resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} 176 | engines: {node: '>=14.21.3'} 177 | cpu: [x64] 178 | os: [darwin] 179 | 180 | '@biomejs/cli-linux-arm64-musl@1.9.4': 181 | resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} 182 | engines: {node: '>=14.21.3'} 183 | cpu: [arm64] 184 | os: [linux] 185 | 186 | '@biomejs/cli-linux-arm64@1.9.4': 187 | resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} 188 | engines: {node: '>=14.21.3'} 189 | cpu: [arm64] 190 | os: [linux] 191 | 192 | '@biomejs/cli-linux-x64-musl@1.9.4': 193 | resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} 194 | engines: {node: '>=14.21.3'} 195 | cpu: [x64] 196 | os: [linux] 197 | 198 | '@biomejs/cli-linux-x64@1.9.4': 199 | resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} 200 | engines: {node: '>=14.21.3'} 201 | cpu: [x64] 202 | os: [linux] 203 | 204 | '@biomejs/cli-win32-arm64@1.9.4': 205 | resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} 206 | engines: {node: '>=14.21.3'} 207 | cpu: [arm64] 208 | os: [win32] 209 | 210 | '@biomejs/cli-win32-x64@1.9.4': 211 | resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} 212 | engines: {node: '>=14.21.3'} 213 | cpu: [x64] 214 | os: [win32] 215 | 216 | '@jridgewell/sourcemap-codec@1.5.0': 217 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 218 | 219 | '@jsonjoy.com/base64@1.1.2': 220 | resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} 221 | engines: {node: '>=10.0'} 222 | peerDependencies: 223 | tslib: '2' 224 | 225 | '@jsonjoy.com/json-pack@1.1.0': 226 | resolution: {integrity: sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==} 227 | engines: {node: '>=10.0'} 228 | peerDependencies: 229 | tslib: '2' 230 | 231 | '@jsonjoy.com/util@1.5.0': 232 | resolution: {integrity: sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==} 233 | engines: {node: '>=10.0'} 234 | peerDependencies: 235 | tslib: '2' 236 | 237 | '@module-federation/error-codes@0.14.0': 238 | resolution: {integrity: sha512-GGk+EoeSACJikZZyShnLshtq9E2eCrDWbRiB4QAFXCX4oYmGgFfzXlx59vMNwqTKPJWxkEGnPYacJMcr2YYjag==} 239 | 240 | '@module-federation/runtime-core@0.14.0': 241 | resolution: {integrity: sha512-fGE1Ro55zIFDp/CxQuRhKQ1pJvG7P0qvRm2N+4i8z++2bgDjcxnCKUqDJ8lLD+JfJQvUJf0tuSsJPgevzueD4g==} 242 | 243 | '@module-federation/runtime-tools@0.14.0': 244 | resolution: {integrity: sha512-y/YN0c2DKsLETE+4EEbmYWjqF9G6ZwgZoDIPkaQ9p0pQu0V4YxzWfQagFFxR0RigYGuhJKmSU/rtNoHq+qF8jg==} 245 | 246 | '@module-federation/runtime@0.14.0': 247 | resolution: {integrity: sha512-kR3cyHw/Y64SEa7mh4CHXOEQYY32LKLK75kJOmBroLNLO7/W01hMNAvGBYTedS7hWpVuefPk1aFZioy3q2VLdQ==} 248 | 249 | '@module-federation/sdk@0.14.0': 250 | resolution: {integrity: sha512-lg/OWRsh18hsyTCamOOhEX546vbDiA2O4OggTxxH2wTGr156N6DdELGQlYIKfRdU/0StgtQS81Goc0BgDZlx9A==} 251 | 252 | '@module-federation/webpack-bundler-runtime@0.14.0': 253 | resolution: {integrity: sha512-POWS6cKBicAAQ3DNY5X7XEUSfOfUsRaBNxbuwEfSGlrkTE9UcWheO06QP2ndHi8tHQuUKcIHi2navhPkJ+k5xg==} 254 | 255 | '@nodelib/fs.scandir@2.1.5': 256 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 257 | engines: {node: '>= 8'} 258 | 259 | '@nodelib/fs.stat@2.0.5': 260 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 261 | engines: {node: '>= 8'} 262 | 263 | '@nodelib/fs.walk@1.2.8': 264 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 265 | engines: {node: '>= 8'} 266 | 267 | '@playwright/test@1.52.0': 268 | resolution: {integrity: sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==} 269 | engines: {node: '>=18'} 270 | hasBin: true 271 | 272 | '@rsbuild/core@1.3.22': 273 | resolution: {integrity: sha512-FGB7m8Tn/uiOhvqk0lw+NRMyD+VYJ+eBqVfpn0X11spkJDiPWn8UkMRvfzCX4XFcNZwRKYuuKJaZK1DNU8UG+w==} 274 | engines: {node: '>=16.10.0'} 275 | hasBin: true 276 | 277 | '@rsbuild/plugin-react@1.3.1': 278 | resolution: {integrity: sha512-1PfE0CZDwiSIUFaMFOEprwsHK6oo29zU6DdtFH2D49uLcpUdOUvU1u2p00RCVO1CIgnAjRajLS7dnPdQUwFOuQ==} 279 | peerDependencies: 280 | '@rsbuild/core': 1.x 281 | 282 | '@rsbuild/plugin-type-check@1.2.2': 283 | resolution: {integrity: sha512-7hRPT9Vi5uXLkvjy9gGHttpCvK7afGXS7bukyf0XCYAWj6XMPJvUQpXBatVVdNdNfeYt0ffHo5GqiPz/eeCorQ==} 284 | peerDependencies: 285 | '@rsbuild/core': 1.x 286 | peerDependenciesMeta: 287 | '@rsbuild/core': 288 | optional: true 289 | 290 | '@rslib/core@0.9.1': 291 | resolution: {integrity: sha512-aa/LXYxr49lCNm/b0B4CQoTB5286MPglGsE5/YFoY0VwIcKJdMz0zBJ4DsPwh27/fcfDQvmN1+J9tzjt9TxQtQ==} 292 | engines: {node: '>=16.7.0'} 293 | hasBin: true 294 | peerDependencies: 295 | '@microsoft/api-extractor': ^7 296 | typescript: ^5 297 | peerDependenciesMeta: 298 | '@microsoft/api-extractor': 299 | optional: true 300 | typescript: 301 | optional: true 302 | 303 | '@rspack/binding-darwin-arm64@1.3.12': 304 | resolution: {integrity: sha512-8hKjVTBeWPqkMzFPNWIh72oU9O3vFy3e88wRjMPImDCXBiEYrKqGTTLd/J0SO+efdL3SBD1rX1IvdJpxCv6Yrw==} 305 | cpu: [arm64] 306 | os: [darwin] 307 | 308 | '@rspack/binding-darwin-x64@1.3.12': 309 | resolution: {integrity: sha512-Sj4m+mCUxL7oCpdu7OmWT7fpBM7hywk5CM9RDc3D7StaBZbvNtNftafCrTZzTYKuZrKmemTh5SFzT5Tz7tf6GA==} 310 | cpu: [x64] 311 | os: [darwin] 312 | 313 | '@rspack/binding-linux-arm64-gnu@1.3.12': 314 | resolution: {integrity: sha512-7MuOxf3/Mhv4mgFdLTvgnt/J+VouNR65DEhorth+RZm3LEWojgoFEphSAMAvpvAOpYSS68Sw4SqsOZi719ia2w==} 315 | cpu: [arm64] 316 | os: [linux] 317 | 318 | '@rspack/binding-linux-arm64-musl@1.3.12': 319 | resolution: {integrity: sha512-s6KKj20T9Z1bA8caIjU6EzJbwyDo1URNFgBAlafCT2UC6yX7flstDJJ38CxZacA9A2P24RuQK2/jPSZpWrTUFA==} 320 | cpu: [arm64] 321 | os: [linux] 322 | 323 | '@rspack/binding-linux-x64-gnu@1.3.12': 324 | resolution: {integrity: sha512-0w/sRREYbRgHgWvs2uMEJSLfvzbZkPHUg6CMcYQGNVK6axYRot6jPyKetyFYA9pR5fB5rsXegpnFaZaVrRIK2g==} 325 | cpu: [x64] 326 | os: [linux] 327 | 328 | '@rspack/binding-linux-x64-musl@1.3.12': 329 | resolution: {integrity: sha512-jEdxkPymkRxbijDRsBGdhopcbGXiXDg59lXqIRkVklqbDmZ/O6DHm7gImmlx5q9FoWbz0gqJuOKBz4JqWxjWVA==} 330 | cpu: [x64] 331 | os: [linux] 332 | 333 | '@rspack/binding-win32-arm64-msvc@1.3.12': 334 | resolution: {integrity: sha512-ZRvUCb3TDLClAqcTsl/o9UdJf0B5CgzAxgdbnYJbldyuyMeTUB4jp20OfG55M3C2Nute2SNhu2bOOp9Se5Ongw==} 335 | cpu: [arm64] 336 | os: [win32] 337 | 338 | '@rspack/binding-win32-ia32-msvc@1.3.12': 339 | resolution: {integrity: sha512-1TKPjuXStPJr14f3ZHuv40Xc/87jUXx10pzVtrPnw+f3hckECHrbYU/fvbVzZyuXbsXtkXpYca6ygCDRJAoNeQ==} 340 | cpu: [ia32] 341 | os: [win32] 342 | 343 | '@rspack/binding-win32-x64-msvc@1.3.12': 344 | resolution: {integrity: sha512-lCR0JfnYKpV+a6r2A2FdxyUKUS4tajePgpPJN5uXDgMGwrDtRqvx+d0BHhwjFudQVJq9VVbRaL89s2MQ6u+xYw==} 345 | cpu: [x64] 346 | os: [win32] 347 | 348 | '@rspack/binding@1.3.12': 349 | resolution: {integrity: sha512-4Ic8lV0+LCBfTlH5aIOujIRWZOtgmG223zC4L3o8WY/+ESAgpdnK6lSSMfcYgRanYLAy3HOmFIp20jwskMpbAg==} 350 | 351 | '@rspack/core@1.3.12': 352 | resolution: {integrity: sha512-mAPmV4LPPRgxpouUrGmAE4kpF1NEWJGyM5coebsjK/zaCMSjw3mkdxiU2b5cO44oIi0Ifv5iGkvwbdrZOvMyFA==} 353 | engines: {node: '>=16.0.0'} 354 | peerDependencies: 355 | '@swc/helpers': '>=0.5.1' 356 | peerDependenciesMeta: 357 | '@swc/helpers': 358 | optional: true 359 | 360 | '@rspack/lite-tapable@1.0.1': 361 | resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} 362 | engines: {node: '>=16.0.0'} 363 | 364 | '@rspack/plugin-react-refresh@1.4.3': 365 | resolution: {integrity: sha512-wZx4vWgy5oMEvgyNGd/oUKcdnKaccYWHCRkOqTdAPJC3WcytxhTX+Kady8ERurSBiLyQpoMiU3Iyd+F1Y2Arbw==} 366 | peerDependencies: 367 | react-refresh: '>=0.10.0 <1.0.0' 368 | webpack-hot-middleware: 2.x 369 | peerDependenciesMeta: 370 | webpack-hot-middleware: 371 | optional: true 372 | 373 | '@swc/helpers@0.5.17': 374 | resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} 375 | 376 | '@types/node@22.15.29': 377 | resolution: {integrity: sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==} 378 | 379 | '@types/react-dom@19.1.5': 380 | resolution: {integrity: sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==} 381 | peerDependencies: 382 | '@types/react': ^19.0.0 383 | 384 | '@types/react@19.1.6': 385 | resolution: {integrity: sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==} 386 | 387 | ansi-styles@3.2.1: 388 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 389 | engines: {node: '>=4'} 390 | 391 | anymatch@3.1.3: 392 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 393 | engines: {node: '>= 8'} 394 | 395 | balanced-match@1.0.2: 396 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 397 | 398 | binary-extensions@2.3.0: 399 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} 400 | engines: {node: '>=8'} 401 | 402 | brace-expansion@2.0.1: 403 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 404 | 405 | braces@3.0.3: 406 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 407 | engines: {node: '>=8'} 408 | 409 | caniuse-lite@1.0.30001720: 410 | resolution: {integrity: sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==} 411 | 412 | chalk@2.4.2: 413 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 414 | engines: {node: '>=4'} 415 | 416 | chokidar@3.6.0: 417 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 418 | engines: {node: '>= 8.10.0'} 419 | 420 | color-convert@1.9.3: 421 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 422 | 423 | color-name@1.1.3: 424 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 425 | 426 | core-js@3.42.0: 427 | resolution: {integrity: sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==} 428 | 429 | csstype@3.1.3: 430 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 431 | 432 | deepmerge@4.3.1: 433 | resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} 434 | engines: {node: '>=0.10.0'} 435 | 436 | error-stack-parser@2.1.4: 437 | resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} 438 | 439 | escape-string-regexp@1.0.5: 440 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 441 | engines: {node: '>=0.8.0'} 442 | 443 | fast-glob@3.3.3: 444 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 445 | engines: {node: '>=8.6.0'} 446 | 447 | fastq@1.17.1: 448 | resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} 449 | 450 | fdir@6.4.4: 451 | resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} 452 | peerDependencies: 453 | picomatch: ^3 || ^4 454 | peerDependenciesMeta: 455 | picomatch: 456 | optional: true 457 | 458 | fill-range@7.1.1: 459 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 460 | engines: {node: '>=8'} 461 | 462 | fsevents@2.3.2: 463 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 464 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 465 | os: [darwin] 466 | 467 | fsevents@2.3.3: 468 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 469 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 470 | os: [darwin] 471 | 472 | glob-parent@5.1.2: 473 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 474 | engines: {node: '>= 6'} 475 | 476 | has-flag@3.0.0: 477 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 478 | engines: {node: '>=4'} 479 | 480 | html-entities@2.6.0: 481 | resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} 482 | 483 | hyperdyperid@1.2.0: 484 | resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} 485 | engines: {node: '>=10.18'} 486 | 487 | is-binary-path@2.1.0: 488 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 489 | engines: {node: '>=8'} 490 | 491 | is-extglob@2.1.1: 492 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 493 | engines: {node: '>=0.10.0'} 494 | 495 | is-glob@4.0.3: 496 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 497 | engines: {node: '>=0.10.0'} 498 | 499 | is-number@7.0.0: 500 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 501 | engines: {node: '>=0.12.0'} 502 | 503 | jiti@2.4.2: 504 | resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} 505 | hasBin: true 506 | 507 | js-tokens@4.0.0: 508 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 509 | 510 | json5@2.2.3: 511 | resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} 512 | engines: {node: '>=6'} 513 | hasBin: true 514 | 515 | magic-string@0.30.17: 516 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 517 | 518 | memfs@4.14.0: 519 | resolution: {integrity: sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==} 520 | engines: {node: '>= 4.0.0'} 521 | 522 | merge2@1.4.1: 523 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 524 | engines: {node: '>= 8'} 525 | 526 | micromatch@4.0.8: 527 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 528 | engines: {node: '>=8.6'} 529 | 530 | minimatch@9.0.5: 531 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 532 | engines: {node: '>=16 || 14 >=14.17'} 533 | 534 | minimist@1.2.8: 535 | resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} 536 | 537 | nano-staged@0.8.0: 538 | resolution: {integrity: sha512-QSEqPGTCJbkHU2yLvfY6huqYPjdBrOaTMKatO1F8nCSrkQGXeKwtCiCnsdxnuMhbg3DTVywKaeWLGCE5oJpq0g==} 539 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 540 | hasBin: true 541 | 542 | normalize-path@3.0.0: 543 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 544 | engines: {node: '>=0.10.0'} 545 | 546 | picocolors@1.0.0: 547 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 548 | 549 | picocolors@1.1.1: 550 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 551 | 552 | picomatch@2.3.1: 553 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 554 | engines: {node: '>=8.6'} 555 | 556 | picomatch@4.0.2: 557 | resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} 558 | engines: {node: '>=12'} 559 | 560 | playwright-core@1.52.0: 561 | resolution: {integrity: sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==} 562 | engines: {node: '>=18'} 563 | hasBin: true 564 | 565 | playwright@1.52.0: 566 | resolution: {integrity: sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==} 567 | engines: {node: '>=18'} 568 | hasBin: true 569 | 570 | queue-microtask@1.2.3: 571 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 572 | 573 | react-dom@19.1.0: 574 | resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} 575 | peerDependencies: 576 | react: ^19.1.0 577 | 578 | react-refresh@0.17.0: 579 | resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} 580 | engines: {node: '>=0.10.0'} 581 | 582 | react@19.1.0: 583 | resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} 584 | engines: {node: '>=0.10.0'} 585 | 586 | readdirp@3.6.0: 587 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 588 | engines: {node: '>=8.10.0'} 589 | 590 | reduce-configs@1.1.0: 591 | resolution: {integrity: sha512-DQxy6liNadHfrLahZR7lMdc227NYVaQZhY5FMsxLEjX8X0SCuH+ESHSLCoz2yDZFq1/CLMDOAHdsEHwOEXKtvg==} 592 | 593 | reusify@1.0.4: 594 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 595 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 596 | 597 | rsbuild-plugin-dts@0.9.1: 598 | resolution: {integrity: sha512-04pkKrebuajsCpC8Vj2z4n6NFFxUYAdUdqSQRFGkGhdmururoDFYW0k9+ZQq9XrSQTlB01F/HFv5mAc0dwG/Qg==} 599 | engines: {node: '>=16.7.0'} 600 | peerDependencies: 601 | '@microsoft/api-extractor': ^7 602 | '@rsbuild/core': 1.x 603 | typescript: ^5 604 | peerDependenciesMeta: 605 | '@microsoft/api-extractor': 606 | optional: true 607 | typescript: 608 | optional: true 609 | 610 | run-parallel@1.2.0: 611 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 612 | 613 | scheduler@0.26.0: 614 | resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} 615 | 616 | simple-git-hooks@2.13.0: 617 | resolution: {integrity: sha512-N+goiLxlkHJlyaYEglFypzVNMaNplPAk5syu0+OPp/Bk6dwVoXF6FfOw2vO0Dp+JHsBaI+w6cm8TnFl2Hw6tDA==} 618 | hasBin: true 619 | 620 | stackframe@1.3.4: 621 | resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} 622 | 623 | strip-bom@3.0.0: 624 | resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} 625 | engines: {node: '>=4'} 626 | 627 | supports-color@5.5.0: 628 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 629 | engines: {node: '>=4'} 630 | 631 | thingies@1.21.0: 632 | resolution: {integrity: sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==} 633 | engines: {node: '>=10.18'} 634 | peerDependencies: 635 | tslib: ^2 636 | 637 | tinyglobby@0.2.14: 638 | resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} 639 | engines: {node: '>=12.0.0'} 640 | 641 | to-regex-range@5.0.1: 642 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 643 | engines: {node: '>=8.0'} 644 | 645 | tree-dump@1.0.2: 646 | resolution: {integrity: sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==} 647 | engines: {node: '>=10.0'} 648 | peerDependencies: 649 | tslib: '2' 650 | 651 | ts-checker-rspack-plugin@1.1.3: 652 | resolution: {integrity: sha512-VpB+L+F330T484qGp5KqyoU00PRlUlz4kO1ifBpQ5CkKXEFXye8nmeXlZ5rvZAXjFAMRFiG+sI9OewO6Bd9UvA==} 653 | engines: {node: '>=16.0.0'} 654 | peerDependencies: 655 | '@rspack/core': ^1.0.0 656 | typescript: '>=3.8.0' 657 | peerDependenciesMeta: 658 | '@rspack/core': 659 | optional: true 660 | 661 | tsconfig-paths@4.2.0: 662 | resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} 663 | engines: {node: '>=6'} 664 | 665 | tslib@2.8.1: 666 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 667 | 668 | typescript@5.8.3: 669 | resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 670 | engines: {node: '>=14.17'} 671 | hasBin: true 672 | 673 | undici-types@6.21.0: 674 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 675 | 676 | yaml@2.8.0: 677 | resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} 678 | engines: {node: '>= 14.6'} 679 | hasBin: true 680 | 681 | snapshots: 682 | 683 | '@ast-grep/napi-darwin-arm64@0.37.0': 684 | optional: true 685 | 686 | '@ast-grep/napi-darwin-x64@0.37.0': 687 | optional: true 688 | 689 | '@ast-grep/napi-linux-arm64-gnu@0.37.0': 690 | optional: true 691 | 692 | '@ast-grep/napi-linux-arm64-musl@0.37.0': 693 | optional: true 694 | 695 | '@ast-grep/napi-linux-x64-gnu@0.37.0': 696 | optional: true 697 | 698 | '@ast-grep/napi-linux-x64-musl@0.37.0': 699 | optional: true 700 | 701 | '@ast-grep/napi-win32-arm64-msvc@0.37.0': 702 | optional: true 703 | 704 | '@ast-grep/napi-win32-ia32-msvc@0.37.0': 705 | optional: true 706 | 707 | '@ast-grep/napi-win32-x64-msvc@0.37.0': 708 | optional: true 709 | 710 | '@ast-grep/napi@0.37.0': 711 | optionalDependencies: 712 | '@ast-grep/napi-darwin-arm64': 0.37.0 713 | '@ast-grep/napi-darwin-x64': 0.37.0 714 | '@ast-grep/napi-linux-arm64-gnu': 0.37.0 715 | '@ast-grep/napi-linux-arm64-musl': 0.37.0 716 | '@ast-grep/napi-linux-x64-gnu': 0.37.0 717 | '@ast-grep/napi-linux-x64-musl': 0.37.0 718 | '@ast-grep/napi-win32-arm64-msvc': 0.37.0 719 | '@ast-grep/napi-win32-ia32-msvc': 0.37.0 720 | '@ast-grep/napi-win32-x64-msvc': 0.37.0 721 | 722 | '@babel/code-frame@7.24.7': 723 | dependencies: 724 | '@babel/highlight': 7.24.7 725 | picocolors: 1.1.1 726 | 727 | '@babel/helper-validator-identifier@7.24.7': {} 728 | 729 | '@babel/highlight@7.24.7': 730 | dependencies: 731 | '@babel/helper-validator-identifier': 7.24.7 732 | chalk: 2.4.2 733 | js-tokens: 4.0.0 734 | picocolors: 1.1.1 735 | 736 | '@biomejs/biome@1.9.4': 737 | optionalDependencies: 738 | '@biomejs/cli-darwin-arm64': 1.9.4 739 | '@biomejs/cli-darwin-x64': 1.9.4 740 | '@biomejs/cli-linux-arm64': 1.9.4 741 | '@biomejs/cli-linux-arm64-musl': 1.9.4 742 | '@biomejs/cli-linux-x64': 1.9.4 743 | '@biomejs/cli-linux-x64-musl': 1.9.4 744 | '@biomejs/cli-win32-arm64': 1.9.4 745 | '@biomejs/cli-win32-x64': 1.9.4 746 | 747 | '@biomejs/cli-darwin-arm64@1.9.4': 748 | optional: true 749 | 750 | '@biomejs/cli-darwin-x64@1.9.4': 751 | optional: true 752 | 753 | '@biomejs/cli-linux-arm64-musl@1.9.4': 754 | optional: true 755 | 756 | '@biomejs/cli-linux-arm64@1.9.4': 757 | optional: true 758 | 759 | '@biomejs/cli-linux-x64-musl@1.9.4': 760 | optional: true 761 | 762 | '@biomejs/cli-linux-x64@1.9.4': 763 | optional: true 764 | 765 | '@biomejs/cli-win32-arm64@1.9.4': 766 | optional: true 767 | 768 | '@biomejs/cli-win32-x64@1.9.4': 769 | optional: true 770 | 771 | '@jridgewell/sourcemap-codec@1.5.0': {} 772 | 773 | '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': 774 | dependencies: 775 | tslib: 2.8.1 776 | 777 | '@jsonjoy.com/json-pack@1.1.0(tslib@2.8.1)': 778 | dependencies: 779 | '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) 780 | '@jsonjoy.com/util': 1.5.0(tslib@2.8.1) 781 | hyperdyperid: 1.2.0 782 | thingies: 1.21.0(tslib@2.8.1) 783 | tslib: 2.8.1 784 | 785 | '@jsonjoy.com/util@1.5.0(tslib@2.8.1)': 786 | dependencies: 787 | tslib: 2.8.1 788 | 789 | '@module-federation/error-codes@0.14.0': {} 790 | 791 | '@module-federation/runtime-core@0.14.0': 792 | dependencies: 793 | '@module-federation/error-codes': 0.14.0 794 | '@module-federation/sdk': 0.14.0 795 | 796 | '@module-federation/runtime-tools@0.14.0': 797 | dependencies: 798 | '@module-federation/runtime': 0.14.0 799 | '@module-federation/webpack-bundler-runtime': 0.14.0 800 | 801 | '@module-federation/runtime@0.14.0': 802 | dependencies: 803 | '@module-federation/error-codes': 0.14.0 804 | '@module-federation/runtime-core': 0.14.0 805 | '@module-federation/sdk': 0.14.0 806 | 807 | '@module-federation/sdk@0.14.0': {} 808 | 809 | '@module-federation/webpack-bundler-runtime@0.14.0': 810 | dependencies: 811 | '@module-federation/runtime': 0.14.0 812 | '@module-federation/sdk': 0.14.0 813 | 814 | '@nodelib/fs.scandir@2.1.5': 815 | dependencies: 816 | '@nodelib/fs.stat': 2.0.5 817 | run-parallel: 1.2.0 818 | 819 | '@nodelib/fs.stat@2.0.5': {} 820 | 821 | '@nodelib/fs.walk@1.2.8': 822 | dependencies: 823 | '@nodelib/fs.scandir': 2.1.5 824 | fastq: 1.17.1 825 | 826 | '@playwright/test@1.52.0': 827 | dependencies: 828 | playwright: 1.52.0 829 | 830 | '@rsbuild/core@1.3.22': 831 | dependencies: 832 | '@rspack/core': 1.3.12(@swc/helpers@0.5.17) 833 | '@rspack/lite-tapable': 1.0.1 834 | '@swc/helpers': 0.5.17 835 | core-js: 3.42.0 836 | jiti: 2.4.2 837 | 838 | '@rsbuild/plugin-react@1.3.1(@rsbuild/core@1.3.22)': 839 | dependencies: 840 | '@rsbuild/core': 1.3.22 841 | '@rspack/plugin-react-refresh': 1.4.3(react-refresh@0.17.0) 842 | react-refresh: 0.17.0 843 | transitivePeerDependencies: 844 | - webpack-hot-middleware 845 | 846 | '@rsbuild/plugin-type-check@1.2.2(@rsbuild/core@1.3.22)(@rspack/core@1.3.12(@swc/helpers@0.5.17))(typescript@5.8.3)': 847 | dependencies: 848 | deepmerge: 4.3.1 849 | json5: 2.2.3 850 | reduce-configs: 1.1.0 851 | ts-checker-rspack-plugin: 1.1.3(@rspack/core@1.3.12(@swc/helpers@0.5.17))(typescript@5.8.3) 852 | optionalDependencies: 853 | '@rsbuild/core': 1.3.22 854 | transitivePeerDependencies: 855 | - '@rspack/core' 856 | - typescript 857 | 858 | '@rslib/core@0.9.1(typescript@5.8.3)': 859 | dependencies: 860 | '@rsbuild/core': 1.3.22 861 | rsbuild-plugin-dts: 0.9.1(@rsbuild/core@1.3.22)(typescript@5.8.3) 862 | tinyglobby: 0.2.14 863 | optionalDependencies: 864 | typescript: 5.8.3 865 | 866 | '@rspack/binding-darwin-arm64@1.3.12': 867 | optional: true 868 | 869 | '@rspack/binding-darwin-x64@1.3.12': 870 | optional: true 871 | 872 | '@rspack/binding-linux-arm64-gnu@1.3.12': 873 | optional: true 874 | 875 | '@rspack/binding-linux-arm64-musl@1.3.12': 876 | optional: true 877 | 878 | '@rspack/binding-linux-x64-gnu@1.3.12': 879 | optional: true 880 | 881 | '@rspack/binding-linux-x64-musl@1.3.12': 882 | optional: true 883 | 884 | '@rspack/binding-win32-arm64-msvc@1.3.12': 885 | optional: true 886 | 887 | '@rspack/binding-win32-ia32-msvc@1.3.12': 888 | optional: true 889 | 890 | '@rspack/binding-win32-x64-msvc@1.3.12': 891 | optional: true 892 | 893 | '@rspack/binding@1.3.12': 894 | optionalDependencies: 895 | '@rspack/binding-darwin-arm64': 1.3.12 896 | '@rspack/binding-darwin-x64': 1.3.12 897 | '@rspack/binding-linux-arm64-gnu': 1.3.12 898 | '@rspack/binding-linux-arm64-musl': 1.3.12 899 | '@rspack/binding-linux-x64-gnu': 1.3.12 900 | '@rspack/binding-linux-x64-musl': 1.3.12 901 | '@rspack/binding-win32-arm64-msvc': 1.3.12 902 | '@rspack/binding-win32-ia32-msvc': 1.3.12 903 | '@rspack/binding-win32-x64-msvc': 1.3.12 904 | 905 | '@rspack/core@1.3.12(@swc/helpers@0.5.17)': 906 | dependencies: 907 | '@module-federation/runtime-tools': 0.14.0 908 | '@rspack/binding': 1.3.12 909 | '@rspack/lite-tapable': 1.0.1 910 | caniuse-lite: 1.0.30001720 911 | optionalDependencies: 912 | '@swc/helpers': 0.5.17 913 | 914 | '@rspack/lite-tapable@1.0.1': {} 915 | 916 | '@rspack/plugin-react-refresh@1.4.3(react-refresh@0.17.0)': 917 | dependencies: 918 | error-stack-parser: 2.1.4 919 | html-entities: 2.6.0 920 | react-refresh: 0.17.0 921 | 922 | '@swc/helpers@0.5.17': 923 | dependencies: 924 | tslib: 2.8.1 925 | 926 | '@types/node@22.15.29': 927 | dependencies: 928 | undici-types: 6.21.0 929 | 930 | '@types/react-dom@19.1.5(@types/react@19.1.6)': 931 | dependencies: 932 | '@types/react': 19.1.6 933 | 934 | '@types/react@19.1.6': 935 | dependencies: 936 | csstype: 3.1.3 937 | 938 | ansi-styles@3.2.1: 939 | dependencies: 940 | color-convert: 1.9.3 941 | 942 | anymatch@3.1.3: 943 | dependencies: 944 | normalize-path: 3.0.0 945 | picomatch: 2.3.1 946 | 947 | balanced-match@1.0.2: {} 948 | 949 | binary-extensions@2.3.0: {} 950 | 951 | brace-expansion@2.0.1: 952 | dependencies: 953 | balanced-match: 1.0.2 954 | 955 | braces@3.0.3: 956 | dependencies: 957 | fill-range: 7.1.1 958 | 959 | caniuse-lite@1.0.30001720: {} 960 | 961 | chalk@2.4.2: 962 | dependencies: 963 | ansi-styles: 3.2.1 964 | escape-string-regexp: 1.0.5 965 | supports-color: 5.5.0 966 | 967 | chokidar@3.6.0: 968 | dependencies: 969 | anymatch: 3.1.3 970 | braces: 3.0.3 971 | glob-parent: 5.1.2 972 | is-binary-path: 2.1.0 973 | is-glob: 4.0.3 974 | normalize-path: 3.0.0 975 | readdirp: 3.6.0 976 | optionalDependencies: 977 | fsevents: 2.3.3 978 | 979 | color-convert@1.9.3: 980 | dependencies: 981 | color-name: 1.1.3 982 | 983 | color-name@1.1.3: {} 984 | 985 | core-js@3.42.0: {} 986 | 987 | csstype@3.1.3: {} 988 | 989 | deepmerge@4.3.1: {} 990 | 991 | error-stack-parser@2.1.4: 992 | dependencies: 993 | stackframe: 1.3.4 994 | 995 | escape-string-regexp@1.0.5: {} 996 | 997 | fast-glob@3.3.3: 998 | dependencies: 999 | '@nodelib/fs.stat': 2.0.5 1000 | '@nodelib/fs.walk': 1.2.8 1001 | glob-parent: 5.1.2 1002 | merge2: 1.4.1 1003 | micromatch: 4.0.8 1004 | 1005 | fastq@1.17.1: 1006 | dependencies: 1007 | reusify: 1.0.4 1008 | 1009 | fdir@6.4.4(picomatch@4.0.2): 1010 | optionalDependencies: 1011 | picomatch: 4.0.2 1012 | 1013 | fill-range@7.1.1: 1014 | dependencies: 1015 | to-regex-range: 5.0.1 1016 | 1017 | fsevents@2.3.2: 1018 | optional: true 1019 | 1020 | fsevents@2.3.3: 1021 | optional: true 1022 | 1023 | glob-parent@5.1.2: 1024 | dependencies: 1025 | is-glob: 4.0.3 1026 | 1027 | has-flag@3.0.0: {} 1028 | 1029 | html-entities@2.6.0: {} 1030 | 1031 | hyperdyperid@1.2.0: {} 1032 | 1033 | is-binary-path@2.1.0: 1034 | dependencies: 1035 | binary-extensions: 2.3.0 1036 | 1037 | is-extglob@2.1.1: {} 1038 | 1039 | is-glob@4.0.3: 1040 | dependencies: 1041 | is-extglob: 2.1.1 1042 | 1043 | is-number@7.0.0: {} 1044 | 1045 | jiti@2.4.2: {} 1046 | 1047 | js-tokens@4.0.0: {} 1048 | 1049 | json5@2.2.3: {} 1050 | 1051 | magic-string@0.30.17: 1052 | dependencies: 1053 | '@jridgewell/sourcemap-codec': 1.5.0 1054 | 1055 | memfs@4.14.0: 1056 | dependencies: 1057 | '@jsonjoy.com/json-pack': 1.1.0(tslib@2.8.1) 1058 | '@jsonjoy.com/util': 1.5.0(tslib@2.8.1) 1059 | tree-dump: 1.0.2(tslib@2.8.1) 1060 | tslib: 2.8.1 1061 | 1062 | merge2@1.4.1: {} 1063 | 1064 | micromatch@4.0.8: 1065 | dependencies: 1066 | braces: 3.0.3 1067 | picomatch: 2.3.1 1068 | 1069 | minimatch@9.0.5: 1070 | dependencies: 1071 | brace-expansion: 2.0.1 1072 | 1073 | minimist@1.2.8: {} 1074 | 1075 | nano-staged@0.8.0: 1076 | dependencies: 1077 | picocolors: 1.0.0 1078 | 1079 | normalize-path@3.0.0: {} 1080 | 1081 | picocolors@1.0.0: {} 1082 | 1083 | picocolors@1.1.1: {} 1084 | 1085 | picomatch@2.3.1: {} 1086 | 1087 | picomatch@4.0.2: {} 1088 | 1089 | playwright-core@1.52.0: {} 1090 | 1091 | playwright@1.52.0: 1092 | dependencies: 1093 | playwright-core: 1.52.0 1094 | optionalDependencies: 1095 | fsevents: 2.3.2 1096 | 1097 | queue-microtask@1.2.3: {} 1098 | 1099 | react-dom@19.1.0(react@19.1.0): 1100 | dependencies: 1101 | react: 19.1.0 1102 | scheduler: 0.26.0 1103 | 1104 | react-refresh@0.17.0: {} 1105 | 1106 | react@19.1.0: {} 1107 | 1108 | readdirp@3.6.0: 1109 | dependencies: 1110 | picomatch: 2.3.1 1111 | 1112 | reduce-configs@1.1.0: {} 1113 | 1114 | reusify@1.0.4: {} 1115 | 1116 | rsbuild-plugin-dts@0.9.1(@rsbuild/core@1.3.22)(typescript@5.8.3): 1117 | dependencies: 1118 | '@ast-grep/napi': 0.37.0 1119 | '@rsbuild/core': 1.3.22 1120 | magic-string: 0.30.17 1121 | picocolors: 1.1.1 1122 | tinyglobby: 0.2.14 1123 | tsconfig-paths: 4.2.0 1124 | optionalDependencies: 1125 | typescript: 5.8.3 1126 | 1127 | run-parallel@1.2.0: 1128 | dependencies: 1129 | queue-microtask: 1.2.3 1130 | 1131 | scheduler@0.26.0: {} 1132 | 1133 | simple-git-hooks@2.13.0: {} 1134 | 1135 | stackframe@1.3.4: {} 1136 | 1137 | strip-bom@3.0.0: {} 1138 | 1139 | supports-color@5.5.0: 1140 | dependencies: 1141 | has-flag: 3.0.0 1142 | 1143 | thingies@1.21.0(tslib@2.8.1): 1144 | dependencies: 1145 | tslib: 2.8.1 1146 | 1147 | tinyglobby@0.2.14: 1148 | dependencies: 1149 | fdir: 6.4.4(picomatch@4.0.2) 1150 | picomatch: 4.0.2 1151 | 1152 | to-regex-range@5.0.1: 1153 | dependencies: 1154 | is-number: 7.0.0 1155 | 1156 | tree-dump@1.0.2(tslib@2.8.1): 1157 | dependencies: 1158 | tslib: 2.8.1 1159 | 1160 | ts-checker-rspack-plugin@1.1.3(@rspack/core@1.3.12(@swc/helpers@0.5.17))(typescript@5.8.3): 1161 | dependencies: 1162 | '@babel/code-frame': 7.24.7 1163 | '@rspack/lite-tapable': 1.0.1 1164 | chokidar: 3.6.0 1165 | is-glob: 4.0.3 1166 | memfs: 4.14.0 1167 | minimatch: 9.0.5 1168 | picocolors: 1.1.1 1169 | typescript: 5.8.3 1170 | optionalDependencies: 1171 | '@rspack/core': 1.3.12(@swc/helpers@0.5.17) 1172 | 1173 | tsconfig-paths@4.2.0: 1174 | dependencies: 1175 | json5: 2.2.3 1176 | minimist: 1.2.8 1177 | strip-bom: 3.0.0 1178 | 1179 | tslib@2.8.1: {} 1180 | 1181 | typescript@5.8.3: {} 1182 | 1183 | undici-types@6.21.0: {} 1184 | 1185 | yaml@2.8.0: {} 1186 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "test/**" 3 | - "playground" 4 | -------------------------------------------------------------------------------- /rslib.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@rslib/core'; 2 | 3 | export default defineConfig({ 4 | lib: [ 5 | { format: 'esm', syntax: 'es2021', dts: true }, 6 | { format: 'cjs', syntax: 'es2021' }, 7 | ], 8 | }); 9 | -------------------------------------------------------------------------------- /src/common/getBaseData.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import type { MonorepoAnalyzer } from '../types/index.js'; 3 | import type { GetProjectsFunc } from './getProjects.js'; 4 | import { type IsMonorepoFn, isMonorepo } from './isMonorepo.js'; 5 | 6 | export interface IMonorepoBaseData { 7 | isMonorepo: boolean; 8 | type: string; 9 | rootPath: string; 10 | getProjects?: GetProjectsFunc; 11 | } 12 | 13 | export const getMonorepoBaseData = async ( 14 | starFindPath: string, 15 | otherMonorepoAnalyzer?: Record, 16 | ): Promise => { 17 | let repoIsMonorepo = false; 18 | let findPath = starFindPath; 19 | let type = ''; 20 | let otherMonorepoChecks: Record | undefined; 21 | if (otherMonorepoAnalyzer) { 22 | otherMonorepoChecks = otherMonorepoChecks ?? {}; 23 | for (const [monoType, analyzer] of Object.entries(otherMonorepoAnalyzer)) { 24 | otherMonorepoChecks[monoType] = analyzer.check; 25 | } 26 | } 27 | 28 | while (true) { 29 | const result = await isMonorepo(findPath, otherMonorepoChecks); 30 | if (result.isMonorepo) { 31 | repoIsMonorepo = true; 32 | ({ type } = result); 33 | break; 34 | } 35 | 36 | // find system root path 37 | if (findPath === path.dirname(findPath)) { 38 | break; 39 | } 40 | 41 | findPath = path.dirname(findPath); 42 | } 43 | 44 | return { 45 | isMonorepo: repoIsMonorepo, 46 | rootPath: repoIsMonorepo ? findPath : '', 47 | type, 48 | getProjects: otherMonorepoAnalyzer?.[type]?.getProjects, 49 | }; 50 | }; 51 | -------------------------------------------------------------------------------- /src/common/getProjects.ts: -------------------------------------------------------------------------------- 1 | import type { Project } from '../project.js'; 2 | import type { IMonorepoBaseData } from './getBaseData.js'; 3 | import { getProjects as getPnpmMonorepoSubProjects } from './pnpm.js'; 4 | import { getProjects as getRushMonorepoSubProjects } from './rush.js'; 5 | 6 | export type GetProjectsFunc = ( 7 | rootPath: string, 8 | ) => Promise | Project[]; 9 | 10 | export const getMonorepoSubProjects = async ( 11 | monorepoBaseData: IMonorepoBaseData, 12 | ): Promise => { 13 | const { type, rootPath, getProjects } = monorepoBaseData; 14 | if (type === 'pnpm') { 15 | return getPnpmMonorepoSubProjects(rootPath); 16 | } 17 | 18 | if (type === 'rush') { 19 | return getRushMonorepoSubProjects(rootPath); 20 | } 21 | 22 | if (getProjects) { 23 | return getProjects(rootPath); 24 | } 25 | 26 | return []; 27 | }; 28 | -------------------------------------------------------------------------------- /src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getBaseData.js'; 2 | export * from './isMonorepo.js'; 3 | 4 | export { getMonorepoSubProjects } from './getProjects.js'; 5 | export { getProjects as getPnpmMonorepoSubProjects } from './pnpm.js'; 6 | export { getProjects as getRushMonorepoSubProjects } from './rush.js'; 7 | 8 | export type { GetProjectsFunc } from './getProjects.js'; 9 | -------------------------------------------------------------------------------- /src/common/isMonorepo.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import { PNPM_WORKSPACE_FILE, RUSH_JSON_FILE } from '../constants.js'; 4 | 5 | export type IsMonorepoFn = ( 6 | monorepoRootPath: string, 7 | ) => Promise | boolean; 8 | 9 | export type IsMonorepoResult = { 10 | isMonorepo: boolean; 11 | type: 'rush' | 'pnpm' | string; 12 | }; 13 | 14 | async function pathExists(path: string) { 15 | return fs.promises 16 | .access(path) 17 | .then(() => true) 18 | .catch(() => false); 19 | } 20 | 21 | export const isPnpmMonorepo: IsMonorepoFn = async ( 22 | monorepoRootPath: string, 23 | ) => { 24 | const existPnpmWorkspaceFile = await pathExists( 25 | path.join(monorepoRootPath, PNPM_WORKSPACE_FILE), 26 | ); 27 | 28 | return existPnpmWorkspaceFile; 29 | }; 30 | 31 | export const isRushMonorepo: IsMonorepoFn = async ( 32 | monorepoRootPath: string, 33 | ) => { 34 | const existRushJsonFile = await pathExists( 35 | path.join(monorepoRootPath, RUSH_JSON_FILE), 36 | ); 37 | return existRushJsonFile; 38 | }; 39 | 40 | export const isMonorepo = async ( 41 | monorepoRootPath: string, 42 | otherMonorepoChecks?: Record, 43 | ): Promise => { 44 | if (typeof otherMonorepoChecks === 'object') { 45 | for (const [monorepoType, monorepoCheck] of Object.entries( 46 | otherMonorepoChecks, 47 | )) { 48 | if ( 49 | typeof monorepoCheck === 'function' && 50 | (await monorepoCheck(monorepoRootPath)) 51 | ) { 52 | return { 53 | isMonorepo: true, 54 | type: monorepoType, 55 | }; 56 | } 57 | } 58 | } 59 | 60 | if (await isPnpmMonorepo(monorepoRootPath)) { 61 | return { 62 | isMonorepo: true, 63 | type: 'pnpm', 64 | }; 65 | } 66 | 67 | if (await isRushMonorepo(monorepoRootPath)) { 68 | return { 69 | isMonorepo: true, 70 | type: 'rush', 71 | }; 72 | } 73 | 74 | return { 75 | isMonorepo: false, 76 | type: '', 77 | }; 78 | }; 79 | -------------------------------------------------------------------------------- /src/common/pnpm.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import glob, { type Options as GlobOptions } from 'fast-glob'; 4 | import { PACKAGE_JSON, PNPM_WORKSPACE_FILE } from '../constants.js'; 5 | import { Project } from '../project.js'; 6 | import type { IPnpmWorkSpace } from '../types/index.js'; 7 | import { readPackageJson } from '../utils.js'; 8 | 9 | const getPatternsFromYaml = async (monorepoRoot: string): Promise => { 10 | const { parse } = await import('yaml'); 11 | const workspaceYamlFilePath = path.join(monorepoRoot, PNPM_WORKSPACE_FILE); 12 | const yamlContent = await fs.promises.readFile(workspaceYamlFilePath, 'utf8'); 13 | const pnpmWorkspace = parse(yamlContent) as IPnpmWorkSpace; 14 | return pnpmWorkspace.packages || []; 15 | }; 16 | 17 | const normalize = (results: string[]) => 18 | results.map((fp: string) => path.normalize(fp)); 19 | 20 | const getGlobOpts = (rootPath: string, patterns: string[]): GlobOptions => { 21 | const globOpts: GlobOptions = { 22 | cwd: rootPath, 23 | absolute: true, 24 | followSymbolicLinks: false, 25 | }; 26 | 27 | if (patterns.some((cfg: string) => cfg.includes('**') || cfg.includes('*'))) { 28 | globOpts.ignore = [ 29 | // allow globs like "packages/**" or "packages/*", 30 | // but avoid picking up node_modules/**/package.json and dist/**/package.json 31 | '**/dist/**', 32 | '**/node_modules/**', 33 | ]; 34 | } 35 | 36 | return globOpts; 37 | }; 38 | 39 | const makeFileFinder = (rootPath: string, patterns: string[]) => { 40 | const globOpts = getGlobOpts(rootPath, patterns); 41 | 42 | return async ( 43 | fileName: string, 44 | fileMapper: (filepath: string[]) => Promise, 45 | ) => { 46 | let result = await glob( 47 | patterns.map((globPath) => path.posix.join(globPath, fileName)), 48 | globOpts, 49 | ); 50 | 51 | // fast-glob does not respect pattern order, so we re-sort by absolute path 52 | result = result.sort(); 53 | // POSIX results always need to be normalized 54 | result = normalize(result); 55 | 56 | return fileMapper(result); 57 | }; 58 | }; 59 | 60 | const readPnpmProjects = async (monorepoRoot: string, patterns: string[]) => { 61 | const finder = makeFileFinder(monorepoRoot, patterns); 62 | const mapper = async (pkgJsonFilePath: string) => { 63 | const pkgJson = await readPackageJson(pkgJsonFilePath); 64 | return { 65 | dir: path.dirname(pkgJsonFilePath), 66 | manifest: pkgJson, 67 | }; 68 | }; 69 | 70 | const projects = await finder(PACKAGE_JSON, (filePaths) => 71 | Promise.all(filePaths.map(mapper)), 72 | ); 73 | 74 | return projects; 75 | }; 76 | 77 | export const getProjects = async (monorepoRoot: string): Promise => { 78 | const patterns = await getPatternsFromYaml(monorepoRoot); 79 | const pnpmProjects = await readPnpmProjects(monorepoRoot, patterns); 80 | 81 | return Promise.all( 82 | pnpmProjects 83 | .filter((p) => p.manifest.name) 84 | .map(async (p) => { 85 | const project = new Project(p.manifest.name, p.dir); 86 | await project.init(); 87 | return project; 88 | }), 89 | ); 90 | }; 91 | -------------------------------------------------------------------------------- /src/common/rush.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { Project } from '../project.js'; 3 | import { readRushJson } from '../utils.js'; 4 | 5 | export const getProjects = async (monorepoRoot: string): Promise => { 6 | const rushConfiguration = await readRushJson(monorepoRoot); 7 | const { projects = [] } = rushConfiguration; 8 | return Promise.all( 9 | projects.map(async (p) => { 10 | const project = new Project( 11 | p.packageName, 12 | path.join(monorepoRoot, p.projectFolder), 13 | ); 14 | await project.init(); 15 | return project; 16 | }), 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const PNPM_WORKSPACE_FILE = 'pnpm-workspace.yaml'; 2 | export const RUSH_JSON_FILE = 'rush.json'; 3 | export const PACKAGE_JSON = 'package.json'; 4 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | PLUGIN_SOURCE_BUILD_NAME, 3 | pluginSourceBuild, 4 | type PluginSourceBuildOptions, 5 | } from './plugin.js'; 6 | export { Project } from './project.js'; 7 | export { getMonorepoBaseData, getMonorepoSubProjects } from './common/index.js'; 8 | export type { MonorepoAnalyzer } from './types/index.js'; 9 | -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import type { RsbuildPlugin } from '@rsbuild/core'; 4 | import json5 from 'json5'; 5 | import { 6 | type ExtraMonorepoStrategies, 7 | filterByField, 8 | getDependentProjects, 9 | } from './project-utils/index.js'; 10 | import type { Project } from './project.js'; 11 | import type { TsConfig } from './types/index.js'; 12 | 13 | export const PLUGIN_SOURCE_BUILD_NAME = 'rsbuild:source-build'; 14 | 15 | export const getSourceInclude = async (options: { 16 | projects: Project[]; 17 | sourceField: string; 18 | }): Promise => { 19 | const { projects, sourceField } = options; 20 | 21 | const includes = []; 22 | for (const project of projects) { 23 | includes.push( 24 | ...project.getSourceEntryPaths({ field: sourceField, exports: true }), 25 | ); 26 | } 27 | 28 | return includes; 29 | }; 30 | 31 | export interface PluginSourceBuildOptions { 32 | /** 33 | * Used to configure the resolve field of the source code files. 34 | * @default 'source'' 35 | */ 36 | sourceField?: string; 37 | /** 38 | * Whether to read source code or output code first. 39 | * @default 'source' 40 | */ 41 | resolvePriority?: 'source' | 'output'; 42 | projectName?: string; 43 | extraMonorepoStrategies?: ExtraMonorepoStrategies; 44 | } 45 | 46 | export function pluginSourceBuild( 47 | options?: PluginSourceBuildOptions, 48 | ): RsbuildPlugin { 49 | const { 50 | projectName, 51 | sourceField = 'source', 52 | resolvePriority = 'source', 53 | extraMonorepoStrategies, 54 | } = options ?? {}; 55 | 56 | return { 57 | name: PLUGIN_SOURCE_BUILD_NAME, 58 | 59 | setup(api) { 60 | const projectRootPath = api.context.rootPath; 61 | 62 | let projects: Project[] | undefined; 63 | 64 | api.modifyEnvironmentConfig(async (config) => { 65 | projects = 66 | projects || 67 | (await getDependentProjects(projectName || projectRootPath, { 68 | cwd: projectRootPath, 69 | recursive: true, 70 | filter: filterByField(sourceField, true), 71 | extraMonorepoStrategies, 72 | })); 73 | 74 | const includes = await getSourceInclude({ 75 | projects, 76 | sourceField, 77 | }); 78 | 79 | config.source = config.source ?? {}; 80 | config.source.include = [...(config.source.include ?? []), ...includes]; 81 | }); 82 | 83 | api.modifyBundlerChain((chain, { CHAIN_ID }) => { 84 | for (const ruleId of [CHAIN_ID.RULE.TS, CHAIN_ID.RULE.JS]) { 85 | if (chain.module.rules.get(ruleId)) { 86 | const rule = chain.module.rule(ruleId); 87 | 88 | // https://rspack.dev/config/resolve 89 | // when source is not exist, other mainFields will effect. // source > Rspack default mainFields. 90 | rule.resolve.mainFields.merge( 91 | resolvePriority === 'source' 92 | ? [sourceField, '...'] 93 | : ['...', sourceField], 94 | ); 95 | 96 | // `conditionNames` is not affected by `resolvePriority`. 97 | // The priority is controlled by the order of fields declared in `exports`. 98 | rule.resolve.conditionNames.add('...').add(sourceField); 99 | } 100 | } 101 | }); 102 | 103 | const getReferences = async ( 104 | tsconfigPath: string, 105 | rspackReferences?: string[] | 'auto', 106 | ): Promise => { 107 | const references = new Set(); 108 | 109 | for (const project of projects || []) { 110 | const filePath = path.join(project.dir, 'tsconfig.json'); 111 | if (fs.existsSync(filePath)) { 112 | references.add(filePath); 113 | } 114 | } 115 | 116 | // Add references in the current project's tsconfig.json 117 | const tsconfig = json5.parse( 118 | fs.readFileSync(tsconfigPath, 'utf-8'), 119 | ); 120 | 121 | const userReferences = [ 122 | ...(Array.isArray(rspackReferences) ? rspackReferences : []), 123 | ...(tsconfig.references 124 | ? tsconfig.references.map((item) => item.path).filter(Boolean) 125 | : []), 126 | ]; 127 | 128 | if (userReferences.length) { 129 | const baseDir = path.dirname(tsconfigPath); 130 | for (const item of userReferences) { 131 | if (!item) { 132 | continue; 133 | } 134 | 135 | const absolutePath = path.isAbsolute(item) 136 | ? item 137 | : path.join(baseDir, item); 138 | 139 | references.add(absolutePath); 140 | } 141 | } 142 | 143 | // avoid self reference, it will break the resolver 144 | references.delete(tsconfigPath); 145 | 146 | return Array.from(references); 147 | }; 148 | 149 | if (api.context.bundlerType === 'rspack') { 150 | api.modifyRspackConfig(async (config, { environment }) => { 151 | const { tsconfigPath } = environment; 152 | if (!tsconfigPath) { 153 | return; 154 | } 155 | 156 | config.resolve ||= {}; 157 | 158 | const { tsConfig = { configFile: tsconfigPath } } = config.resolve; 159 | 160 | const configObject = 161 | typeof tsConfig === 'string' ? { configFile: tsConfig } : tsConfig; 162 | 163 | const references = await getReferences( 164 | tsconfigPath, 165 | configObject.references, 166 | ); 167 | 168 | config.resolve.tsConfig = { 169 | configFile: configObject?.configFile || tsconfigPath, 170 | references: references, 171 | }; 172 | }); 173 | } else { 174 | api.modifyBundlerChain(async (chain, { CHAIN_ID, environment }) => { 175 | const { TS_CONFIG_PATHS } = CHAIN_ID.RESOLVE_PLUGIN; 176 | const { tsconfigPath } = environment; 177 | 178 | if (!chain.resolve.plugins.has(TS_CONFIG_PATHS) || !tsconfigPath) { 179 | return; 180 | } 181 | 182 | const references = await getReferences(tsconfigPath); 183 | 184 | // set references config 185 | // https://github.com/dividab/tsconfig-paths-webpack-plugin#options 186 | chain.resolve.plugin(TS_CONFIG_PATHS).tap((options) => 187 | options.map((option) => ({ 188 | ...option, 189 | references, 190 | })), 191 | ); 192 | }); 193 | } 194 | }, 195 | }; 196 | } 197 | -------------------------------------------------------------------------------- /src/project-utils/filter.ts: -------------------------------------------------------------------------------- 1 | import type { Project } from '../project.js'; 2 | import type { ExportsConfig } from '../types/packageJson.js'; 3 | 4 | export type Filter = FilterFunction; 5 | export type FilterFunction = ( 6 | projects: Project[], 7 | ) => Project[] | Promise; 8 | 9 | function hasExportsSourceField( 10 | exportsConfig: ExportsConfig, 11 | sourceField: string, 12 | ) { 13 | return ( 14 | typeof exportsConfig[sourceField] === 'string' || 15 | Object.values(exportsConfig).some( 16 | (moduleRules) => 17 | typeof moduleRules === 'object' && 18 | typeof moduleRules[sourceField] === 'string', 19 | ) 20 | ); 21 | } 22 | 23 | export const filterByField = 24 | (fieldName: string, checkExports?: boolean): FilterFunction => 25 | (projects: Project[]) => { 26 | return projects.filter((p) => { 27 | return ( 28 | fieldName in p.metaData || 29 | (checkExports && 30 | hasExportsSourceField(p.metaData.exports || {}, fieldName)) 31 | ); 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /src/project-utils/getDependentProjects.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import { 4 | getMonorepoBaseData, 5 | getMonorepoSubProjects, 6 | } from '../common/index.js'; 7 | import type { Project } from '../project.js'; 8 | import type { MonorepoAnalyzer } from '../types/index.js'; 9 | import { readPackageJson } from '../utils.js'; 10 | import type { Filter } from './filter.js'; 11 | 12 | export type ExtraMonorepoStrategies = Record; 13 | 14 | export interface GetDependentProjectsOptions { 15 | // Find the start of the monorepo root path 16 | cwd?: string; 17 | recursive?: boolean; 18 | filter?: Filter; 19 | extraMonorepoStrategies?: ExtraMonorepoStrategies; 20 | } 21 | 22 | async function pathExists(path: string) { 23 | return fs.promises 24 | .access(path) 25 | .then(() => true) 26 | .catch(() => false); 27 | } 28 | 29 | const getDependentProjects = async ( 30 | projectNameOrRootPath: string, 31 | options: GetDependentProjectsOptions, 32 | ): Promise => { 33 | const { 34 | cwd = process.cwd(), 35 | recursive, 36 | filter, 37 | extraMonorepoStrategies, 38 | } = options; 39 | 40 | // check if first argument is projectRootPath. 41 | const currentProjectPkgJsonPath = path.join( 42 | projectNameOrRootPath, 43 | 'package.json', 44 | ); 45 | 46 | let projectName: string; 47 | if (await pathExists(currentProjectPkgJsonPath)) { 48 | ({ name: projectName } = await readPackageJson(currentProjectPkgJsonPath)); 49 | } else { 50 | projectName = projectNameOrRootPath; 51 | } 52 | 53 | const monoBaseData = await getMonorepoBaseData(cwd, extraMonorepoStrategies); 54 | if (!monoBaseData.isMonorepo) { 55 | return []; 56 | } 57 | 58 | const projects = await getMonorepoSubProjects(monoBaseData); 59 | const currentProject = projects.find( 60 | (project) => project.name === projectName, 61 | ); 62 | 63 | if (!currentProject) { 64 | return []; 65 | } 66 | 67 | let dependentProjects = currentProject.getDependentProjects(projects, { 68 | recursive, 69 | }); 70 | if (filter) { 71 | dependentProjects = await filter(dependentProjects); 72 | } 73 | 74 | return dependentProjects; 75 | }; 76 | 77 | export { getDependentProjects }; 78 | -------------------------------------------------------------------------------- /src/project-utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getDependentProjects.js'; 2 | export * from './filter.js'; 3 | -------------------------------------------------------------------------------- /src/project.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import { PACKAGE_JSON } from './constants.js'; 4 | import type { ExportsConfig, INodePackageJson } from './types/packageJson.js'; 5 | import { readPackageJson } from './utils.js'; 6 | 7 | export class Project { 8 | name: string; 9 | 10 | dir: string; 11 | 12 | metaData!: INodePackageJson; 13 | 14 | constructor(name: string, dir: string) { 15 | this.name = name; 16 | this.dir = dir; 17 | } 18 | 19 | async init(): Promise { 20 | this.metaData = await readPackageJson(path.join(this.dir, PACKAGE_JSON)); 21 | } 22 | 23 | getMetaData(): INodePackageJson { 24 | if (this.metaData === null) { 25 | throw new Error( 26 | 'The Project object needs to be initialized by executing the `init` function', 27 | ); 28 | } 29 | 30 | return this.metaData; 31 | } 32 | 33 | getDependentProjects( 34 | monorepoProjects: Project[], 35 | options?: { recursive?: boolean }, 36 | ): Project[] { 37 | const { recursive } = options ?? { recursive: false }; 38 | const allProjectMap = new Map(); 39 | for (const project of monorepoProjects) { 40 | allProjectMap.set(project.name, project); 41 | } 42 | 43 | if (!recursive) { 44 | return this.getDirectDependentProjects(allProjectMap); 45 | } 46 | 47 | const computedSet = new Set(); 48 | computedSet.add(this.name); 49 | 50 | const queue = this.getDirectDependentProjects(allProjectMap).filter( 51 | (p) => !computedSet.has(p.name), 52 | ); 53 | const result = []; 54 | 55 | while (queue.length > 0) { 56 | const item = queue.shift() as Project; 57 | if (computedSet.has(item.name)) { 58 | continue; 59 | } 60 | 61 | result.push(item); 62 | computedSet.add(item.name); 63 | const newDeps = item.getDirectDependentProjects(allProjectMap); 64 | 65 | if (newDeps.length > 0) { 66 | queue.push(...newDeps); 67 | } 68 | } 69 | 70 | return result; 71 | } 72 | 73 | getDirectDependentProjects(allProjectMap: Map): Project[] { 74 | const pkgJson = this.getMetaData(); 75 | const { dependencies = {}, devDependencies = {} } = pkgJson; 76 | const projects: Project[] = []; 77 | 78 | for (const d of Object.keys(dependencies)) { 79 | if (allProjectMap.has(d)) { 80 | projects.push(allProjectMap.get(d) as Project); 81 | } 82 | } 83 | 84 | for (const d of Object.keys(devDependencies)) { 85 | if (allProjectMap.has(d)) { 86 | projects.push(allProjectMap.get(d) as Project); 87 | } 88 | } 89 | return projects; 90 | } 91 | 92 | getSourceEntryPaths(options?: { 93 | field?: string; 94 | exports?: boolean; 95 | }): string[] { 96 | const { exports: checkExports = false, field: sourceField = 'source' } = 97 | options ?? {}; 98 | const pkgJson = this.getMetaData() as INodePackageJson & 99 | Record; 100 | 101 | // normalize strings 102 | const sourceDirs = pkgJson[sourceField] 103 | ? [path.normalize(pkgJson[sourceField])] 104 | : []; 105 | 106 | if (checkExports) { 107 | /** 108 | * analyze exports: 109 | * "exports": { 110 | * ".": { 111 | * "source": "./src/index.ts" 112 | * } 113 | * }, 114 | */ 115 | const exportsSourceDirs = this.#getExportsSourceDirs( 116 | pkgJson.exports ?? {}, 117 | sourceField, 118 | ); 119 | sourceDirs.push(...exportsSourceDirs); 120 | } 121 | 122 | if (!sourceDirs.length) { 123 | throw new Error( 124 | `"${sourceField}" field is not found in ${this.name} package.json`, 125 | ); 126 | } 127 | 128 | return this.#getCommonRootPaths(sourceDirs); 129 | } 130 | 131 | #getExportsSourceDirs(exportsConfig: ExportsConfig, sourceField: string) { 132 | const exportsSourceDirs: string[] = []; 133 | 134 | if (typeof exportsConfig[sourceField] === 'string') { 135 | exportsSourceDirs.push( 136 | path.normalize(exportsConfig[sourceField] as string), 137 | ); 138 | } 139 | 140 | for (const moduleRules of Object.values(exportsConfig)) { 141 | if ( 142 | typeof moduleRules === 'object' && 143 | typeof moduleRules[sourceField] === 'string' 144 | ) { 145 | exportsSourceDirs.push( 146 | path.normalize(moduleRules[sourceField] as string), 147 | ); 148 | } 149 | } 150 | 151 | // normalize strings 152 | return exportsSourceDirs; 153 | } 154 | 155 | /** 156 | * 157 | * @param paths normalize paths 158 | * @returns common root paths 159 | */ 160 | #getCommonRootPaths(paths: string[]) { 161 | const commonRootPathsSet = new Set(); 162 | for (const p of paths) { 163 | let dir: string; 164 | try { 165 | dir = fs.statSync(p).isDirectory() ? p : path.dirname(p); 166 | } catch { 167 | dir = path.dirname(p); 168 | } 169 | const rootPath = this.#getRootPath(dir); 170 | if (!commonRootPathsSet.has(rootPath)) { 171 | commonRootPathsSet.add(rootPath); 172 | } 173 | } 174 | 175 | return Array.from(commonRootPathsSet).map((p) => path.join(this.dir, p)); 176 | } 177 | 178 | #getRootPath(p: string) { 179 | return p.split(path.sep)[0]; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { GetProjectsFunc } from '../common/getProjects.js'; 2 | import type { IsMonorepoFn } from '../common/isMonorepo.js'; 3 | 4 | export * from './packageJson.js'; 5 | export * from './rushJson.js'; 6 | 7 | export interface MonorepoAnalyzer { 8 | check: IsMonorepoFn; 9 | getProjects: GetProjectsFunc; 10 | } 11 | 12 | export interface IPnpmWorkSpace { 13 | packages: string[]; 14 | } 15 | 16 | export type TsConfig = { 17 | references?: Array<{ path?: string }>; 18 | }; 19 | -------------------------------------------------------------------------------- /src/types/packageJson.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The following code is modified based on 3 | * https://github.com/microsoft/rushstack/blob/main/libraries/node-core-library/src/IPackageJson.ts 4 | * 5 | * MIT Licensed 6 | * Copyright (c) Microsoft Corporation 7 | * https://github.com/microsoft/rushstack/blob/main/LICENSE 8 | */ 9 | 10 | /** 11 | * This interface is part of the {@link IPackageJson} file format. It is used for the 12 | * "dependencies", "optionalDependencies", and "devDependencies" fields. 13 | * @public 14 | */ 15 | export interface IPackageJsonDependencyTable { 16 | /** 17 | * The key is the name of a dependency. The value is a Semantic Versioning (SemVer) 18 | * range specifier. 19 | */ 20 | [dependencyName: string]: string; 21 | } 22 | 23 | export interface IPackageJsonRepository { 24 | /** 25 | * The source control type for the repository that hosts the project. This is typically "git". 26 | */ 27 | type: string; 28 | 29 | /** 30 | * The URL of the repository that hosts the project. 31 | */ 32 | url: string; 33 | 34 | /** 35 | * If the project does not exist at the root of the repository, its path is specified here. 36 | */ 37 | directory?: string; 38 | } 39 | 40 | /** 41 | * This interface is part of the {@link IPackageJson} file format. It is used for the 42 | * "scripts" field. 43 | * @public 44 | */ 45 | export interface IPackageJsonScriptTable { 46 | /** 47 | * The key is the name of the script hook. The value is the script body which may 48 | * be a file path or shell script command. 49 | */ 50 | [scriptName: string]: string; 51 | } 52 | 53 | /** 54 | * This interface is part of the {@link IPackageJson} file format. It is used for the 55 | * "peerDependenciesMeta" field. 56 | * @public 57 | */ 58 | export interface IPeerDependenciesMetaTable { 59 | [dependencyName: string]: { 60 | optional?: boolean; 61 | }; 62 | } 63 | 64 | export type ExportsModuleRules = 65 | | string 66 | | Record 67 | | Record>; 68 | 69 | export interface ExportsConfig { 70 | [modulePath: string]: ExportsModuleRules; 71 | } 72 | 73 | export interface INodePackageJson { 74 | /** 75 | * The name of the package. 76 | */ 77 | name: string; 78 | 79 | /** 80 | * A version number conforming to the Semantic Versioning (SemVer) standard. 81 | */ 82 | version?: string; 83 | 84 | /** 85 | * Indicates whether this package is allowed to be published or not. 86 | */ 87 | private?: boolean; 88 | 89 | /** 90 | * A brief description of the package. 91 | */ 92 | description?: string; 93 | 94 | /** 95 | * The URL of the project's repository. 96 | */ 97 | repository?: string | IPackageJsonRepository; 98 | 99 | /** 100 | * The URL to the project's web page. 101 | */ 102 | homepage?: string; 103 | 104 | /** 105 | * The name of the license. 106 | */ 107 | license?: string; 108 | 109 | /** 110 | * The path to the module file that will act as the main entry point. 111 | */ 112 | main?: string; 113 | 114 | exports?: ExportsConfig; 115 | 116 | /** 117 | * The path to the TypeScript *.d.ts file describing the module file 118 | * that will act as the main entry point. 119 | */ 120 | types?: string; 121 | 122 | /** 123 | * Alias for `types` 124 | */ 125 | typings?: string; 126 | 127 | /** 128 | * The path to the TSDoc metadata file. 129 | * This is still being standardized: https://github.com/microsoft/tsdoc/issues/7#issuecomment-442271815 130 | * @beta 131 | */ 132 | tsdocMetadata?: string; 133 | 134 | /** 135 | * The main entry point for the package. 136 | */ 137 | bin?: string; 138 | 139 | /** 140 | * An array of dependencies that must always be installed for this package. 141 | */ 142 | dependencies?: IPackageJsonDependencyTable; 143 | 144 | /** 145 | * An array of optional dependencies that may be installed for this package. 146 | */ 147 | optionalDependencies?: IPackageJsonDependencyTable; 148 | 149 | /** 150 | * An array of dependencies that must only be installed for developers who will 151 | * build this package. 152 | */ 153 | devDependencies?: IPackageJsonDependencyTable; 154 | 155 | /** 156 | * An array of dependencies that must be installed by a consumer of this package, 157 | * but which will not be automatically installed by this package. 158 | */ 159 | peerDependencies?: IPackageJsonDependencyTable; 160 | 161 | /** 162 | * An array of metadata about peer dependencies. 163 | */ 164 | peerDependenciesMeta?: IPeerDependenciesMetaTable; 165 | 166 | /** 167 | * A table of script hooks that a package manager or build tool may invoke. 168 | */ 169 | scripts?: IPackageJsonScriptTable; 170 | 171 | /** 172 | * A table of package version resolutions. This feature is only implemented by the Yarn package manager. 173 | * 174 | * @remarks 175 | * See the {@link https://github.com/yarnpkg/rfcs/blob/master/implemented/0000-selective-versions-resolutions.md 176 | * | 0000-selective-versions-resolutions.md RFC} for details. 177 | */ 178 | resolutions?: Record; 179 | } 180 | 181 | export interface IPackageJson extends INodePackageJson { 182 | // Make the "version" field non-optional. 183 | /** {@inheritDoc INodePackageJson.version} */ 184 | version: string; 185 | } 186 | -------------------------------------------------------------------------------- /src/types/rushJson.ts: -------------------------------------------------------------------------------- 1 | // Castrated version of RushConfigurationProject 2 | export interface RushConfigurationProject { 3 | readonly packageName: string; 4 | // projectFolder is relative path 5 | readonly projectFolder: string; 6 | } 7 | 8 | export interface IRushConfig { 9 | projects?: RushConfigurationProject[]; 10 | } 11 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import json5 from 'json5'; 4 | import { RUSH_JSON_FILE } from './constants.js'; 5 | import type { INodePackageJson, IRushConfig } from './types/index.js'; 6 | 7 | export const readPackageJson = async ( 8 | pkgJsonFilePath: string, 9 | ): Promise => { 10 | return readJson(pkgJsonFilePath); 11 | }; 12 | 13 | export const readRushJson = async ( 14 | rushJsonFilePath: string, 15 | ): Promise => { 16 | const rushJson = readJson( 17 | rushJsonFilePath.includes(RUSH_JSON_FILE) 18 | ? rushJsonFilePath 19 | : path.join(rushJsonFilePath, RUSH_JSON_FILE), 20 | ); 21 | return rushJson; 22 | }; 23 | 24 | async function pathExists(path: string) { 25 | return fs.promises 26 | .access(path) 27 | .then(() => true) 28 | .catch(() => false); 29 | } 30 | 31 | export const readJson = async (jsonFileAbsPath: string): Promise => { 32 | if (!(await pathExists(jsonFileAbsPath))) { 33 | return {} as T; 34 | } 35 | 36 | const content = await fs.promises.readFile(jsonFileAbsPath, 'utf-8'); 37 | const json: T = json5.parse(content); 38 | return json; 39 | }; 40 | -------------------------------------------------------------------------------- /test/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@e2e/source-build-app", 3 | "private": true, 4 | "version": "1.0.0", 5 | "scripts": { 6 | "build": "rsbuild build" 7 | }, 8 | "dependencies": { 9 | "@e2e/source-build-components": "workspace:*", 10 | "@rsbuild/plugin-source-build": "workspace:*" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/app/rsbuild.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@rsbuild/core'; 2 | import { pluginReact } from '@rsbuild/plugin-react'; 3 | import { pluginSourceBuild } from '@rsbuild/plugin-source-build'; 4 | import { pluginTypeCheck } from '@rsbuild/plugin-type-check'; 5 | 6 | export default defineConfig({ 7 | plugins: [pluginSourceBuild(), pluginReact(), pluginTypeCheck()], 8 | }); 9 | -------------------------------------------------------------------------------- /test/app/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from '@e2e/source-build-components'; 2 | import ReactDOM from 'react-dom/client'; 3 | 4 | const rootElement = document.getElementById('root'); 5 | 6 | if (rootElement) { 7 | const root = ReactDOM.createRoot(rootElement); 8 | const App = () => ( 9 |
10 |
11 | 12 |
13 |
14 | ); 15 | 16 | root.render(); 17 | } 18 | -------------------------------------------------------------------------------- /test/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "lib": ["DOM", "ESNext"], 5 | "module": "ES2020", 6 | "strict": true, 7 | "isolatedModules": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "resolveJsonModule": true, 13 | "moduleResolution": "Bundler", 14 | "useDefineForClassFields": true, 15 | "declaration": false, 16 | "jsx": "react-jsx", 17 | "baseUrl": "./", 18 | // make type checker faster by not including types 19 | "types": [], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["src"], 25 | "references": [ 26 | { 27 | "path": "../components/tsconfig.json" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /test/components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@e2e/source-build-components", 3 | "private": true, 4 | "version": "1.0.0", 5 | "types": "./dist/types/index.d.ts", 6 | "exports": { 7 | ".": { 8 | "types": "./dist/types/index.d.ts", 9 | "source": "./src/index.tsx", 10 | "default": "./dist/index.js" 11 | }, 12 | "./card": { 13 | "types": "./dist/types/card/index.d.ts", 14 | "source": "./src/card/index.tsx", 15 | "default": "./dist/card/index.js" 16 | } 17 | }, 18 | "dependencies": { 19 | "@e2e/source-build-utils": "workspace:*", 20 | "@e2e/source-build-utils2": "workspace:*" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/components/src/card/index.css: -------------------------------------------------------------------------------- 1 | .card-comp { 2 | border: 1px solid #000; 3 | border-radius: 6px; 4 | } 5 | 6 | .card-comp > h2 { 7 | font-size: 32px; 8 | font-weight: bold; 9 | } 10 | 11 | .card-comp > article { 12 | display: block; 13 | font-size: 16px; 14 | background-color: aqua; 15 | } 16 | -------------------------------------------------------------------------------- /test/components/src/card/index.tsx: -------------------------------------------------------------------------------- 1 | import { strAdd } from '@e2e/source-build-utils'; 2 | import { toLowerCase } from '@e2e/source-build-utils2'; 3 | import './index.css'; 4 | 5 | export interface CardProps { 6 | title: string; 7 | content?: string; 8 | } 9 | 10 | export const Card = (props: CardProps) => { 11 | const { title, content = '' } = props; 12 | return ( 13 |
14 |

Card Comp Title: {toLowerCase(title)}

15 |
{strAdd('Card Comp Content:', content)}
16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /test/components/src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from '@/card/index'; 2 | -------------------------------------------------------------------------------- /test/components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "lib": ["DOM", "ESNext"], 5 | "module": "ES2020", 6 | "strict": true, 7 | "isolatedModules": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "resolveJsonModule": true, 13 | "moduleResolution": "Bundler", 14 | "useDefineForClassFields": true, 15 | "composite": true, 16 | "declaration": true, 17 | "declarationDir": "./dist/types", 18 | "jsx": "react-jsx", 19 | "baseUrl": "./", 20 | // make type checker faster by not including types 21 | "types": [], 22 | "paths": { 23 | "@/*": ["./src/*"], 24 | "@card/*": ["./src/card/*"] 25 | }, 26 | "rootDir": "src", 27 | "outDir": "./dist" 28 | }, 29 | "references": [ 30 | { 31 | "path": "../utils" 32 | }, 33 | { 34 | "path": "../utils2" 35 | } 36 | ], 37 | "include": ["src"] 38 | } 39 | -------------------------------------------------------------------------------- /test/helper.ts: -------------------------------------------------------------------------------- 1 | const portMap = new Map(); 2 | 3 | export function getRandomPort( 4 | defaultPort = Math.ceil(Math.random() * 30000) + 15000, 5 | ) { 6 | let port = defaultPort; 7 | while (true) { 8 | if (!portMap.get(port)) { 9 | portMap.set(port, 1); 10 | return port; 11 | } 12 | port++; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { dirname, join } from 'node:path'; 2 | import { fileURLToPath } from 'node:url'; 3 | import { expect, test } from '@playwright/test'; 4 | import { createRsbuild, loadConfig } from '@rsbuild/core'; 5 | 6 | const __dirname = dirname(fileURLToPath(import.meta.url)); 7 | 8 | test('should build succeed', async ({ page }) => { 9 | const cwd = join(__dirname, 'app'); 10 | const rsbuild = await createRsbuild({ 11 | cwd, 12 | rsbuildConfig: (await loadConfig({ cwd })).content, 13 | }); 14 | 15 | await rsbuild.build(); 16 | const { server, urls } = await rsbuild.preview(); 17 | 18 | await page.goto(urls[0]); 19 | 20 | const locator = page.locator('#root'); 21 | await expect(locator).toHaveText( 22 | 'Card Comp Title: appCARD COMP CONTENT:hello world', 23 | ); 24 | 25 | await server.close(); 26 | }); 27 | -------------------------------------------------------------------------------- /test/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@e2e/source-build-utils", 3 | "private": true, 4 | "version": "1.0.0", 5 | "types": "./dist/types/index.d.ts", 6 | "exports": { 7 | ".": { 8 | "types": "./dist/types/index.d.ts", 9 | "source": "./src/index.ts" 10 | }, 11 | "./common": { 12 | "types": "./dist/types/common/index.d.ts", 13 | "source": "./src/common/index.ts" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/utils/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './toUpperCase'; 2 | export * from './toLowerCase'; 3 | -------------------------------------------------------------------------------- /test/utils/src/common/toLowerCase.ts: -------------------------------------------------------------------------------- 1 | export const toLowerCase = (s: string) => s.toLowerCase(); 2 | -------------------------------------------------------------------------------- /test/utils/src/common/toUpperCase.ts: -------------------------------------------------------------------------------- 1 | export const toUpperCase = (s: string) => s.toUpperCase(); 2 | -------------------------------------------------------------------------------- /test/utils/src/index.ts: -------------------------------------------------------------------------------- 1 | import { toLowerCase, toUpperCase } from '@common/index'; 2 | 3 | export const strAdd = (str1: string, str2: string) => { 4 | return `${toUpperCase(str1)}${toLowerCase(str2)}`; 5 | }; 6 | -------------------------------------------------------------------------------- /test/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "lib": ["DOM", "ESNext"], 5 | "module": "ES2020", 6 | "strict": true, 7 | "isolatedModules": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "resolveJsonModule": true, 13 | "moduleResolution": "Bundler", 14 | "useDefineForClassFields": true, 15 | "composite": true, 16 | "declaration": true, 17 | "declarationDir": "./dist/types", 18 | "declarationMap": true, 19 | "jsx": "react-jsx", 20 | "baseUrl": "./", 21 | // make type checker faster by not including types 22 | "types": [], 23 | "paths": { 24 | "@/*": ["./src/*"], 25 | "@common/*": ["./src/common/*"] 26 | }, 27 | "outDir": "./dist", 28 | "rootDir": "./src" 29 | }, 30 | "include": ["src"] 31 | } 32 | -------------------------------------------------------------------------------- /test/utils2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@e2e/source-build-utils2", 3 | "private": true, 4 | "version": "1.0.0", 5 | "types": "./dist/types/index.d.ts", 6 | "exports": { 7 | "types": "./dist/types/index.d.ts", 8 | "source": "./src/index.ts" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/utils2/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './toUpperCase'; 2 | export * from './toLowerCase'; 3 | -------------------------------------------------------------------------------- /test/utils2/src/common/toLowerCase.ts: -------------------------------------------------------------------------------- 1 | export const toLowerCase = (s: string) => s.toLowerCase(); 2 | -------------------------------------------------------------------------------- /test/utils2/src/common/toUpperCase.ts: -------------------------------------------------------------------------------- 1 | export const toUpperCase = (s: string) => s.toUpperCase(); 2 | -------------------------------------------------------------------------------- /test/utils2/src/index.ts: -------------------------------------------------------------------------------- 1 | import { toLowerCase } from '@common/index'; 2 | 3 | export { toLowerCase }; 4 | -------------------------------------------------------------------------------- /test/utils2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "lib": ["DOM", "ESNext"], 5 | "module": "ES2020", 6 | "strict": true, 7 | "isolatedModules": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "resolveJsonModule": true, 13 | "moduleResolution": "Bundler", 14 | "useDefineForClassFields": true, 15 | "composite": true, 16 | "declaration": true, 17 | "declarationDir": "./dist/types", 18 | "declarationMap": true, 19 | "jsx": "react-jsx", 20 | "baseUrl": "./", 21 | // make type checker faster by not including types 22 | "types": [], 23 | "paths": { 24 | "@/*": ["./src/*"], 25 | "@common/*": ["./src/common/*"] 26 | }, 27 | "outDir": "./dist", 28 | "rootDir": "./src" 29 | }, 30 | "include": ["src"] 31 | } 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "baseUrl": "./", 5 | "target": "ES2020", 6 | "lib": ["DOM", "ESNext"], 7 | "module": "Node16", 8 | "strict": true, 9 | "declaration": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "resolveJsonModule": true, 14 | "moduleResolution": "Node16" 15 | }, 16 | "include": ["src"] 17 | } 18 | --------------------------------------------------------------------------------