rsbuild-plugin-workspace-dev 0.0.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/README.zh-CN.md +141 -0
- package/dist/constant.d.ts +5 -0
- package/dist/index.cjs +319 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +268 -0
- package/dist/logger.d.ts +17 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/utils.d.ts +3 -0
- package/dist/workspace-dev.d.ts +34 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# rsbuild-plugin-workspace-dev
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# rsbuild-plugin-workspace-dev
|
|
2
|
+
|
|
3
|
+
提供按拓扑顺序启动 monorepo 子项目的能力。
|
|
4
|
+
|
|
5
|
+
`rsbuild-plugin-workspace-dev` 用于 monorepo 开发场景,它支持从当前项目开始计算依赖关系生成拓扑图,按拓扑顺序启动子项目。
|
|
6
|
+
|
|
7
|
+
<p>
|
|
8
|
+
<a href="https://npmjs.com/package/rsbuild-plugin-workspace-dev">
|
|
9
|
+
<img src="https://img.shields.io/npm/v/rsbuild-plugin-workspace-dev?style=flat-square&colorA=564341&colorB=EDED91" alt="npm version" />
|
|
10
|
+
</a>
|
|
11
|
+
<img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="license" />
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
## 使用
|
|
15
|
+
|
|
16
|
+
安装:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm add rsbuild-plugin-workspace-dev -D
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
在 `rsbuild.config.ts` 里注册插件:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// rsbuild.config.ts
|
|
26
|
+
import { pluginWorkspaceDev } from "rsbuild-plugin-workspace-dev";
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
plugins: [pluginWorkspaceDev()],
|
|
30
|
+
};
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 使用场景
|
|
34
|
+
|
|
35
|
+
在 monorepo 中,一个项目可能依赖多个子项目,而子项目之间也可能存在依赖关系。
|
|
36
|
+
|
|
37
|
+
比如 monorepo 中包含了一个 app 应用和多个 lib 包:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
monorepo
|
|
41
|
+
├── app
|
|
42
|
+
└── lib1
|
|
43
|
+
└── lib2
|
|
44
|
+
└── lib3
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
其中,app 是基于 Rsbuild 构建的, lib 是基于 Rslib 构建的。app 依赖了 lib1 和 lib2:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"name": "app",
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"lib1": "workspace:*",
|
|
54
|
+
"lib2": "workspace:*"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
lib2 依赖了 lib3:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"name": "lib2",
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"lib3": "workspace:*"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
此时在 app 下执行 `pnpm dev` 后,会按照拓扑顺序先启动 lib1 和 lib3,再启动 lib2,最后启动 app。此处启动 lib 指的是执行 lib 的 dev 命令
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"scripts": {
|
|
73
|
+
"dev": "rslib build --watch"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
识别子项目是否启动完成是通过匹配子项目日志实现的,默认支持匹配 Rslib、tsup 子项目,同时支持手动配置 match 匹配日志。
|
|
78
|
+
|
|
79
|
+
## 选项
|
|
80
|
+
|
|
81
|
+
### projectConfig
|
|
82
|
+
用于子项目的启动项配置和自定义日志匹配逻辑。
|
|
83
|
+
|
|
84
|
+
- **类型:**:
|
|
85
|
+
```
|
|
86
|
+
interface ProjectConfig {
|
|
87
|
+
/**
|
|
88
|
+
* 自定义子项目启动命令,默认值为 `dev`, 即执行 `npm run dev`。
|
|
89
|
+
*/
|
|
90
|
+
command?: string;
|
|
91
|
+
/**
|
|
92
|
+
* 自定义子项目启动完成匹配逻辑,默认支持 `Rsbuild`、`tsup` 子项目的日志匹配逻辑。
|
|
93
|
+
*/
|
|
94
|
+
match?: (stdout: string) => boolean;
|
|
95
|
+
/**
|
|
96
|
+
* 是否跳过当前子项目的启动,默认值为 `false`,通常用于跳过一些不需要启动的子项目。
|
|
97
|
+
*/
|
|
98
|
+
skip?: boolean;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### ignoreSelf
|
|
103
|
+
|
|
104
|
+
- **类型:** `boolean`
|
|
105
|
+
- **默认值:** `true`
|
|
106
|
+
-
|
|
107
|
+
是否忽略当前项目的启动,默认值为 `true`。一般无需手动配置,当前项目通常由用户手动执行 dev 启动,无需插件干预。
|
|
108
|
+
考虑如下场景,docs 和 lib 是在同一个项目中,而 docs 需要调试 lib 的产物,此时需要启动 `pnpm doc` 命令,而 lib 则需要启动 `pnpm dev` 命令,配置该选项到 rspress 配置中后,启动 `pnpm doc` 时会自动执行 `pnpm dev` 命令,用于启动 lib 子项目。
|
|
109
|
+
```
|
|
110
|
+
├── docs
|
|
111
|
+
│ └── index.mdx
|
|
112
|
+
├── package.json
|
|
113
|
+
├── src
|
|
114
|
+
│ └── Button.tsx
|
|
115
|
+
├── rslib.config.ts
|
|
116
|
+
├── rspress.config.ts
|
|
117
|
+
```
|
|
118
|
+
```
|
|
119
|
+
"scripts": {
|
|
120
|
+
"dev": "rslib build --watch",
|
|
121
|
+
"doc": "rspress dev"
|
|
122
|
+
},
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### cwd
|
|
126
|
+
|
|
127
|
+
- **类型:** `string`
|
|
128
|
+
- **默认值:** `process.cwd()`
|
|
129
|
+
|
|
130
|
+
用于配置当前工作目录,默认值为当前项目目录,一般无需配置。
|
|
131
|
+
|
|
132
|
+
### workspaceFileDir
|
|
133
|
+
|
|
134
|
+
- **类型:** `string`
|
|
135
|
+
- **默认值:** `process.cwd()`
|
|
136
|
+
|
|
137
|
+
用于配置 workspace 文件目录,默认值为当前项目目录,一般无需配置。
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
[MIT](./LICENSE).
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const PACKAGE_JSON = "package.json";
|
|
2
|
+
export declare const DEBUG_LOG_TITLE = "[Rsbuild Workspace Dev Plugin]: ";
|
|
3
|
+
export declare const RSLIB_READY_MESSAGE = "build complete, watching for changes";
|
|
4
|
+
export declare const MODERN_MODULE_READY_MESSAGE = "Watching for file changes";
|
|
5
|
+
export declare const TSUP_READY_MESSAGE = "Watching for changes in";
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
(()=>{
|
|
13
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
14
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: definition[key]
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
})();
|
|
20
|
+
(()=>{
|
|
21
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
22
|
+
})();
|
|
23
|
+
(()=>{
|
|
24
|
+
__webpack_require__.r = (exports1)=>{
|
|
25
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
26
|
+
value: 'Module'
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
29
|
+
value: true
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
})();
|
|
33
|
+
var __webpack_exports__ = {};
|
|
34
|
+
__webpack_require__.r(__webpack_exports__);
|
|
35
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
36
|
+
pluginWorkspaceDev: ()=>pluginWorkspaceDev,
|
|
37
|
+
WorkspaceDevRunner: ()=>WorkspaceDevRunner
|
|
38
|
+
});
|
|
39
|
+
const external_chalk_namespaceObject = require("chalk");
|
|
40
|
+
var external_chalk_default = /*#__PURE__*/ __webpack_require__.n(external_chalk_namespaceObject);
|
|
41
|
+
const PACKAGE_JSON = 'package.json';
|
|
42
|
+
const DEBUG_LOG_TITLE = '[Rsbuild Workspace Dev Plugin]: ';
|
|
43
|
+
const RSLIB_READY_MESSAGE = 'build complete, watching for changes';
|
|
44
|
+
const MODERN_MODULE_READY_MESSAGE = 'Watching for file changes';
|
|
45
|
+
const TSUP_READY_MESSAGE = 'Watching for changes in';
|
|
46
|
+
function _define_property(obj, key, value) {
|
|
47
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
48
|
+
value: value,
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
writable: true
|
|
52
|
+
});
|
|
53
|
+
else obj[key] = value;
|
|
54
|
+
return obj;
|
|
55
|
+
}
|
|
56
|
+
const logMap = {
|
|
57
|
+
["stdout"]: 'log',
|
|
58
|
+
["stderr"]: 'error'
|
|
59
|
+
};
|
|
60
|
+
class Logger {
|
|
61
|
+
appendLog(type, log) {
|
|
62
|
+
this[type] += log;
|
|
63
|
+
}
|
|
64
|
+
emitLog(type) {
|
|
65
|
+
console[logMap[type]](this[type]);
|
|
66
|
+
}
|
|
67
|
+
emitLogOnce(type, log) {
|
|
68
|
+
const logWithName = `${external_chalk_default().hex('#808080').bold(this.name)}: ${log}`;
|
|
69
|
+
console[logMap[type]](logWithName);
|
|
70
|
+
}
|
|
71
|
+
reset(type) {
|
|
72
|
+
this[type] = '';
|
|
73
|
+
}
|
|
74
|
+
setBanner(name) {
|
|
75
|
+
const startBanner = `\n------------ ${external_chalk_default().hex('#c95ab3').bold('log start:')} ${external_chalk_default().green(name)} ${'-'.repeat(Math.max(50 - name.toString().length, 5))}`;
|
|
76
|
+
this.stdout = startBanner + this.stdout;
|
|
77
|
+
}
|
|
78
|
+
flushStdout() {
|
|
79
|
+
this.setBanner(this.name);
|
|
80
|
+
this.emitLog("stdout");
|
|
81
|
+
}
|
|
82
|
+
static setEndBanner() {
|
|
83
|
+
const endBanner = `------------ ${external_chalk_default().hex('#c95ab3').bold('log end:')} ${external_chalk_default().green('all sub project have been started.')} ------------\n`;
|
|
84
|
+
console.log(endBanner);
|
|
85
|
+
}
|
|
86
|
+
constructor({ name }){
|
|
87
|
+
_define_property(this, "stdout", void 0);
|
|
88
|
+
_define_property(this, "stderr", void 0);
|
|
89
|
+
_define_property(this, "name", void 0);
|
|
90
|
+
_define_property(this, "logTitle", void 0);
|
|
91
|
+
this.name = name;
|
|
92
|
+
this.stdout = '';
|
|
93
|
+
this.stderr = '';
|
|
94
|
+
this.logTitle = DEBUG_LOG_TITLE;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const debugLog = (msg)=>{
|
|
98
|
+
const isDebug = 'rsbuild' === process.env.DEBUG || '*' === process.env.DEBUG;
|
|
99
|
+
if (isDebug) console.log(DEBUG_LOG_TITLE + msg);
|
|
100
|
+
};
|
|
101
|
+
const get_packages_namespaceObject = require("@manypkg/get-packages");
|
|
102
|
+
const external_child_process_namespaceObject = require("child_process");
|
|
103
|
+
const external_graphlib_namespaceObject = require("graphlib");
|
|
104
|
+
var external_graphlib_default = /*#__PURE__*/ __webpack_require__.n(external_graphlib_namespaceObject);
|
|
105
|
+
const external_path_namespaceObject = require("path");
|
|
106
|
+
var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
|
|
107
|
+
const external_fs_namespaceObject = require("fs");
|
|
108
|
+
var external_fs_default = /*#__PURE__*/ __webpack_require__.n(external_fs_namespaceObject);
|
|
109
|
+
const external_json5_namespaceObject = require("json5");
|
|
110
|
+
var external_json5_default = /*#__PURE__*/ __webpack_require__.n(external_json5_namespaceObject);
|
|
111
|
+
async function pathExists(path) {
|
|
112
|
+
return external_fs_default().promises.access(path).then(()=>true).catch(()=>false);
|
|
113
|
+
}
|
|
114
|
+
const readJson = async (jsonFileAbsPath)=>{
|
|
115
|
+
if (!await pathExists(jsonFileAbsPath)) return {};
|
|
116
|
+
const content = await external_fs_default().promises.readFile(jsonFileAbsPath, 'utf-8');
|
|
117
|
+
const json = external_json5_default().parse(content);
|
|
118
|
+
return json;
|
|
119
|
+
};
|
|
120
|
+
const readPackageJson = async (pkgJsonFilePath)=>readJson(pkgJsonFilePath);
|
|
121
|
+
function workspace_dev_define_property(obj, key, value) {
|
|
122
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
123
|
+
value: value,
|
|
124
|
+
enumerable: true,
|
|
125
|
+
configurable: true,
|
|
126
|
+
writable: true
|
|
127
|
+
});
|
|
128
|
+
else obj[key] = value;
|
|
129
|
+
return obj;
|
|
130
|
+
}
|
|
131
|
+
class WorkspaceDevRunner {
|
|
132
|
+
async init() {
|
|
133
|
+
this.metaData = await readPackageJson(external_path_default().join(this.cwd, PACKAGE_JSON));
|
|
134
|
+
this.buildDependencyGraph();
|
|
135
|
+
debugLog(`Dependency graph:\nnodes: ${this.getNodes().join(', ')}\nedges: ${this.getEdges().map((edge)=>`${edge.v} -> ${edge.w}`).join(', ')}\n`);
|
|
136
|
+
}
|
|
137
|
+
buildDependencyGraph() {
|
|
138
|
+
const { packages } = (0, get_packages_namespaceObject.getPackagesSync)(this.workspaceFileDir);
|
|
139
|
+
const currentPackage = packages.find((pkg)=>pkg.packageJson.name === this.metaData.name);
|
|
140
|
+
this.packages = packages;
|
|
141
|
+
const initNode = (pkg)=>{
|
|
142
|
+
const { packageJson, dir } = pkg;
|
|
143
|
+
const { name, dependencies, devDependencies, peerDependencies } = packageJson;
|
|
144
|
+
const node = {
|
|
145
|
+
name,
|
|
146
|
+
packageJson,
|
|
147
|
+
path: dir
|
|
148
|
+
};
|
|
149
|
+
this.graph.setNode(name, node);
|
|
150
|
+
this.visited[name] = false;
|
|
151
|
+
this.visiting[name] = false;
|
|
152
|
+
this.matched[name] = false;
|
|
153
|
+
const packageName = name;
|
|
154
|
+
const deps = {
|
|
155
|
+
...dependencies,
|
|
156
|
+
...devDependencies,
|
|
157
|
+
...peerDependencies
|
|
158
|
+
};
|
|
159
|
+
for (const depName of Object.keys(deps)){
|
|
160
|
+
const isInternalDep = this.packages.some((p)=>p.packageJson.name === depName);
|
|
161
|
+
if (isInternalDep) {
|
|
162
|
+
this.graph.setEdge(packageName, depName);
|
|
163
|
+
this.checkGraph();
|
|
164
|
+
const depPackage = packages.find((pkg)=>pkg.packageJson.name === depName);
|
|
165
|
+
if (!this.getNode(depName)) initNode(depPackage);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
initNode(currentPackage);
|
|
170
|
+
}
|
|
171
|
+
checkGraph() {
|
|
172
|
+
const cycles = external_graphlib_default().alg.findCycles(this.graph);
|
|
173
|
+
const nonSelfCycles = cycles.filter((c)=>1 !== c.length);
|
|
174
|
+
debugLog(`cycles check: ${cycles}`);
|
|
175
|
+
if (nonSelfCycles.length) throw new Error(`${DEBUG_LOG_TITLE}Dependency graph do not allow cycles.`);
|
|
176
|
+
}
|
|
177
|
+
async start() {
|
|
178
|
+
const promises = [];
|
|
179
|
+
const allNodes = this.getNodes();
|
|
180
|
+
const filterSelfNodes = allNodes.filter((node)=>node !== this.metaData.name);
|
|
181
|
+
const nodes = this.options.ignoreSelf ? filterSelfNodes : allNodes;
|
|
182
|
+
for (const node of nodes){
|
|
183
|
+
const dependencies = this.getDependencies(node) || [];
|
|
184
|
+
const canStart = dependencies.every((dep)=>{
|
|
185
|
+
const selfStart = node === dep;
|
|
186
|
+
const isVisiting = this.visiting[dep];
|
|
187
|
+
const isVisited = selfStart || this.visited[dep];
|
|
188
|
+
return isVisited && !isVisiting;
|
|
189
|
+
});
|
|
190
|
+
if (canStart && !this.visited[node] && !this.visiting[node]) {
|
|
191
|
+
debugLog(`Start visit node: ${node}`);
|
|
192
|
+
const visitPromise = this.visitNodes(node);
|
|
193
|
+
promises.push(visitPromise);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
await Promise.all(promises);
|
|
197
|
+
}
|
|
198
|
+
visitNodes(node) {
|
|
199
|
+
return new Promise((resolve)=>{
|
|
200
|
+
const { name, path } = this.getNode(node);
|
|
201
|
+
const logger = new Logger({
|
|
202
|
+
name
|
|
203
|
+
});
|
|
204
|
+
const config = this.options?.projectConfig?.[name];
|
|
205
|
+
if (config?.skip) {
|
|
206
|
+
this.visited[node] = true;
|
|
207
|
+
this.visiting[node] = false;
|
|
208
|
+
debugLog(`Skip visit node: ${node}`);
|
|
209
|
+
logger.flushStdout();
|
|
210
|
+
logger.emitLogOnce('stdout', `skip visit node: ${name}`);
|
|
211
|
+
return this.start().then(()=>resolve());
|
|
212
|
+
}
|
|
213
|
+
this.visiting[node] = true;
|
|
214
|
+
const child = (0, external_child_process_namespaceObject.spawn)('npm', [
|
|
215
|
+
'run',
|
|
216
|
+
config?.command ? config.command : 'dev'
|
|
217
|
+
], {
|
|
218
|
+
cwd: path,
|
|
219
|
+
env: {
|
|
220
|
+
...process.env,
|
|
221
|
+
FORCE_COLOR: '3'
|
|
222
|
+
},
|
|
223
|
+
shell: true
|
|
224
|
+
});
|
|
225
|
+
child.stdout.on('data', async (data)=>{
|
|
226
|
+
const stdout = data.toString();
|
|
227
|
+
if (this.matched[node]) {
|
|
228
|
+
const content = data.toString().replace(/\n$/, '');
|
|
229
|
+
logger.emitLogOnce('stdout', content);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
logger.appendLog('stdout', stdout);
|
|
233
|
+
const match = config?.match;
|
|
234
|
+
const matchResult = match ? match(stdout) : stdout.match(RSLIB_READY_MESSAGE) || stdout.match(MODERN_MODULE_READY_MESSAGE) || stdout.match(TSUP_READY_MESSAGE);
|
|
235
|
+
if (matchResult) {
|
|
236
|
+
logger.flushStdout();
|
|
237
|
+
this.matched[node] = true;
|
|
238
|
+
this.visited[node] = true;
|
|
239
|
+
this.visiting[node] = false;
|
|
240
|
+
await this.start();
|
|
241
|
+
resolve();
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
child.stderr.on('data', (data)=>{
|
|
245
|
+
const stderr = data.toString();
|
|
246
|
+
logger.emitLogOnce('stderr', stderr);
|
|
247
|
+
});
|
|
248
|
+
child.on('close', ()=>{});
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
getDependencyGraph() {
|
|
252
|
+
return this.graph;
|
|
253
|
+
}
|
|
254
|
+
getNodes() {
|
|
255
|
+
return this.graph.nodes();
|
|
256
|
+
}
|
|
257
|
+
getEdges() {
|
|
258
|
+
return this.graph.edges();
|
|
259
|
+
}
|
|
260
|
+
getNode(name) {
|
|
261
|
+
return this.graph.node(name);
|
|
262
|
+
}
|
|
263
|
+
getDependents(packageName) {
|
|
264
|
+
return this.graph.predecessors(packageName);
|
|
265
|
+
}
|
|
266
|
+
getDependencies(packageName) {
|
|
267
|
+
return this.graph.successors(packageName);
|
|
268
|
+
}
|
|
269
|
+
constructor(options){
|
|
270
|
+
workspace_dev_define_property(this, "options", void 0);
|
|
271
|
+
workspace_dev_define_property(this, "cwd", void 0);
|
|
272
|
+
workspace_dev_define_property(this, "workspaceFileDir", void 0);
|
|
273
|
+
workspace_dev_define_property(this, "packages", []);
|
|
274
|
+
workspace_dev_define_property(this, "graph", void 0);
|
|
275
|
+
workspace_dev_define_property(this, "visited", void 0);
|
|
276
|
+
workspace_dev_define_property(this, "visiting", void 0);
|
|
277
|
+
workspace_dev_define_property(this, "matched", void 0);
|
|
278
|
+
workspace_dev_define_property(this, "metaData", void 0);
|
|
279
|
+
this.options = {
|
|
280
|
+
ignoreSelf: true,
|
|
281
|
+
...options
|
|
282
|
+
};
|
|
283
|
+
this.cwd = options.cwd || process.cwd();
|
|
284
|
+
this.workspaceFileDir = options.workspaceFileDir || this.cwd;
|
|
285
|
+
this.packages = [];
|
|
286
|
+
this.visited = {};
|
|
287
|
+
this.visiting = {};
|
|
288
|
+
this.matched = {};
|
|
289
|
+
this.graph = new external_graphlib_namespaceObject.Graph({
|
|
290
|
+
directed: true
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function pluginWorkspaceDev(options) {
|
|
295
|
+
return {
|
|
296
|
+
name: 'rsbuild-plugin-workspace-dev',
|
|
297
|
+
async setup (api) {
|
|
298
|
+
const rootPath = api.context.rootPath;
|
|
299
|
+
api.onBeforeStartDevServer(async ()=>{
|
|
300
|
+
const runner = new WorkspaceDevRunner({
|
|
301
|
+
cwd: rootPath,
|
|
302
|
+
...options
|
|
303
|
+
});
|
|
304
|
+
await runner.init();
|
|
305
|
+
await runner.start();
|
|
306
|
+
Logger.setEndBanner();
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
exports.WorkspaceDevRunner = __webpack_exports__.WorkspaceDevRunner;
|
|
312
|
+
exports.pluginWorkspaceDev = __webpack_exports__.pluginWorkspaceDev;
|
|
313
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
314
|
+
"WorkspaceDevRunner",
|
|
315
|
+
"pluginWorkspaceDev"
|
|
316
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
317
|
+
Object.defineProperty(exports, '__esModule', {
|
|
318
|
+
value: true
|
|
319
|
+
});
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { getPackagesSync } from "@manypkg/get-packages";
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import graphlib, { Graph } from "graphlib";
|
|
5
|
+
import path_0 from "path";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import json5 from "json5";
|
|
8
|
+
const PACKAGE_JSON = 'package.json';
|
|
9
|
+
const DEBUG_LOG_TITLE = '[Rsbuild Workspace Dev Plugin]: ';
|
|
10
|
+
const RSLIB_READY_MESSAGE = 'build complete, watching for changes';
|
|
11
|
+
const MODERN_MODULE_READY_MESSAGE = 'Watching for file changes';
|
|
12
|
+
const TSUP_READY_MESSAGE = 'Watching for changes in';
|
|
13
|
+
function _define_property(obj, key, value) {
|
|
14
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
15
|
+
value: value,
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true
|
|
19
|
+
});
|
|
20
|
+
else obj[key] = value;
|
|
21
|
+
return obj;
|
|
22
|
+
}
|
|
23
|
+
const logMap = {
|
|
24
|
+
["stdout"]: 'log',
|
|
25
|
+
["stderr"]: 'error'
|
|
26
|
+
};
|
|
27
|
+
class Logger {
|
|
28
|
+
appendLog(type, log) {
|
|
29
|
+
this[type] += log;
|
|
30
|
+
}
|
|
31
|
+
emitLog(type) {
|
|
32
|
+
console[logMap[type]](this[type]);
|
|
33
|
+
}
|
|
34
|
+
emitLogOnce(type, log) {
|
|
35
|
+
const logWithName = `${chalk.hex('#808080').bold(this.name)}: ${log}`;
|
|
36
|
+
console[logMap[type]](logWithName);
|
|
37
|
+
}
|
|
38
|
+
reset(type) {
|
|
39
|
+
this[type] = '';
|
|
40
|
+
}
|
|
41
|
+
setBanner(name) {
|
|
42
|
+
const startBanner = `\n------------ ${chalk.hex('#c95ab3').bold('log start:')} ${chalk.green(name)} ${'-'.repeat(Math.max(50 - name.toString().length, 5))}`;
|
|
43
|
+
this.stdout = startBanner + this.stdout;
|
|
44
|
+
}
|
|
45
|
+
flushStdout() {
|
|
46
|
+
this.setBanner(this.name);
|
|
47
|
+
this.emitLog("stdout");
|
|
48
|
+
}
|
|
49
|
+
static setEndBanner() {
|
|
50
|
+
const endBanner = `------------ ${chalk.hex('#c95ab3').bold('log end:')} ${chalk.green('all sub project have been started.')} ------------\n`;
|
|
51
|
+
console.log(endBanner);
|
|
52
|
+
}
|
|
53
|
+
constructor({ name }){
|
|
54
|
+
_define_property(this, "stdout", void 0);
|
|
55
|
+
_define_property(this, "stderr", void 0);
|
|
56
|
+
_define_property(this, "name", void 0);
|
|
57
|
+
_define_property(this, "logTitle", void 0);
|
|
58
|
+
this.name = name;
|
|
59
|
+
this.stdout = '';
|
|
60
|
+
this.stderr = '';
|
|
61
|
+
this.logTitle = DEBUG_LOG_TITLE;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const debugLog = (msg)=>{
|
|
65
|
+
const isDebug = 'rsbuild' === process.env.DEBUG || '*' === process.env.DEBUG;
|
|
66
|
+
if (isDebug) console.log(DEBUG_LOG_TITLE + msg);
|
|
67
|
+
};
|
|
68
|
+
async function pathExists(path) {
|
|
69
|
+
return fs.promises.access(path).then(()=>true).catch(()=>false);
|
|
70
|
+
}
|
|
71
|
+
const readJson = async (jsonFileAbsPath)=>{
|
|
72
|
+
if (!await pathExists(jsonFileAbsPath)) return {};
|
|
73
|
+
const content = await fs.promises.readFile(jsonFileAbsPath, 'utf-8');
|
|
74
|
+
const json = json5.parse(content);
|
|
75
|
+
return json;
|
|
76
|
+
};
|
|
77
|
+
const readPackageJson = async (pkgJsonFilePath)=>readJson(pkgJsonFilePath);
|
|
78
|
+
function workspace_dev_define_property(obj, key, value) {
|
|
79
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
80
|
+
value: value,
|
|
81
|
+
enumerable: true,
|
|
82
|
+
configurable: true,
|
|
83
|
+
writable: true
|
|
84
|
+
});
|
|
85
|
+
else obj[key] = value;
|
|
86
|
+
return obj;
|
|
87
|
+
}
|
|
88
|
+
class WorkspaceDevRunner {
|
|
89
|
+
async init() {
|
|
90
|
+
this.metaData = await readPackageJson(path_0.join(this.cwd, PACKAGE_JSON));
|
|
91
|
+
this.buildDependencyGraph();
|
|
92
|
+
debugLog(`Dependency graph:\nnodes: ${this.getNodes().join(', ')}\nedges: ${this.getEdges().map((edge)=>`${edge.v} -> ${edge.w}`).join(', ')}\n`);
|
|
93
|
+
}
|
|
94
|
+
buildDependencyGraph() {
|
|
95
|
+
const { packages } = getPackagesSync(this.workspaceFileDir);
|
|
96
|
+
const currentPackage = packages.find((pkg)=>pkg.packageJson.name === this.metaData.name);
|
|
97
|
+
this.packages = packages;
|
|
98
|
+
const initNode = (pkg)=>{
|
|
99
|
+
const { packageJson, dir } = pkg;
|
|
100
|
+
const { name, dependencies, devDependencies, peerDependencies } = packageJson;
|
|
101
|
+
const node = {
|
|
102
|
+
name,
|
|
103
|
+
packageJson,
|
|
104
|
+
path: dir
|
|
105
|
+
};
|
|
106
|
+
this.graph.setNode(name, node);
|
|
107
|
+
this.visited[name] = false;
|
|
108
|
+
this.visiting[name] = false;
|
|
109
|
+
this.matched[name] = false;
|
|
110
|
+
const packageName = name;
|
|
111
|
+
const deps = {
|
|
112
|
+
...dependencies,
|
|
113
|
+
...devDependencies,
|
|
114
|
+
...peerDependencies
|
|
115
|
+
};
|
|
116
|
+
for (const depName of Object.keys(deps)){
|
|
117
|
+
const isInternalDep = this.packages.some((p)=>p.packageJson.name === depName);
|
|
118
|
+
if (isInternalDep) {
|
|
119
|
+
this.graph.setEdge(packageName, depName);
|
|
120
|
+
this.checkGraph();
|
|
121
|
+
const depPackage = packages.find((pkg)=>pkg.packageJson.name === depName);
|
|
122
|
+
if (!this.getNode(depName)) initNode(depPackage);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
initNode(currentPackage);
|
|
127
|
+
}
|
|
128
|
+
checkGraph() {
|
|
129
|
+
const cycles = graphlib.alg.findCycles(this.graph);
|
|
130
|
+
const nonSelfCycles = cycles.filter((c)=>1 !== c.length);
|
|
131
|
+
debugLog(`cycles check: ${cycles}`);
|
|
132
|
+
if (nonSelfCycles.length) throw new Error(`${DEBUG_LOG_TITLE}Dependency graph do not allow cycles.`);
|
|
133
|
+
}
|
|
134
|
+
async start() {
|
|
135
|
+
const promises = [];
|
|
136
|
+
const allNodes = this.getNodes();
|
|
137
|
+
const filterSelfNodes = allNodes.filter((node)=>node !== this.metaData.name);
|
|
138
|
+
const nodes = this.options.ignoreSelf ? filterSelfNodes : allNodes;
|
|
139
|
+
for (const node of nodes){
|
|
140
|
+
const dependencies = this.getDependencies(node) || [];
|
|
141
|
+
const canStart = dependencies.every((dep)=>{
|
|
142
|
+
const selfStart = node === dep;
|
|
143
|
+
const isVisiting = this.visiting[dep];
|
|
144
|
+
const isVisited = selfStart || this.visited[dep];
|
|
145
|
+
return isVisited && !isVisiting;
|
|
146
|
+
});
|
|
147
|
+
if (canStart && !this.visited[node] && !this.visiting[node]) {
|
|
148
|
+
debugLog(`Start visit node: ${node}`);
|
|
149
|
+
const visitPromise = this.visitNodes(node);
|
|
150
|
+
promises.push(visitPromise);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
await Promise.all(promises);
|
|
154
|
+
}
|
|
155
|
+
visitNodes(node) {
|
|
156
|
+
return new Promise((resolve)=>{
|
|
157
|
+
const { name, path } = this.getNode(node);
|
|
158
|
+
const logger = new Logger({
|
|
159
|
+
name
|
|
160
|
+
});
|
|
161
|
+
const config = this.options?.projectConfig?.[name];
|
|
162
|
+
if (config?.skip) {
|
|
163
|
+
this.visited[node] = true;
|
|
164
|
+
this.visiting[node] = false;
|
|
165
|
+
debugLog(`Skip visit node: ${node}`);
|
|
166
|
+
logger.flushStdout();
|
|
167
|
+
logger.emitLogOnce('stdout', `skip visit node: ${name}`);
|
|
168
|
+
return this.start().then(()=>resolve());
|
|
169
|
+
}
|
|
170
|
+
this.visiting[node] = true;
|
|
171
|
+
const child = spawn('npm', [
|
|
172
|
+
'run',
|
|
173
|
+
config?.command ? config.command : 'dev'
|
|
174
|
+
], {
|
|
175
|
+
cwd: path,
|
|
176
|
+
env: {
|
|
177
|
+
...process.env,
|
|
178
|
+
FORCE_COLOR: '3'
|
|
179
|
+
},
|
|
180
|
+
shell: true
|
|
181
|
+
});
|
|
182
|
+
child.stdout.on('data', async (data)=>{
|
|
183
|
+
const stdout = data.toString();
|
|
184
|
+
if (this.matched[node]) {
|
|
185
|
+
const content = data.toString().replace(/\n$/, '');
|
|
186
|
+
logger.emitLogOnce('stdout', content);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
logger.appendLog('stdout', stdout);
|
|
190
|
+
const match = config?.match;
|
|
191
|
+
const matchResult = match ? match(stdout) : stdout.match(RSLIB_READY_MESSAGE) || stdout.match(MODERN_MODULE_READY_MESSAGE) || stdout.match(TSUP_READY_MESSAGE);
|
|
192
|
+
if (matchResult) {
|
|
193
|
+
logger.flushStdout();
|
|
194
|
+
this.matched[node] = true;
|
|
195
|
+
this.visited[node] = true;
|
|
196
|
+
this.visiting[node] = false;
|
|
197
|
+
await this.start();
|
|
198
|
+
resolve();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
child.stderr.on('data', (data)=>{
|
|
202
|
+
const stderr = data.toString();
|
|
203
|
+
logger.emitLogOnce('stderr', stderr);
|
|
204
|
+
});
|
|
205
|
+
child.on('close', ()=>{});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
getDependencyGraph() {
|
|
209
|
+
return this.graph;
|
|
210
|
+
}
|
|
211
|
+
getNodes() {
|
|
212
|
+
return this.graph.nodes();
|
|
213
|
+
}
|
|
214
|
+
getEdges() {
|
|
215
|
+
return this.graph.edges();
|
|
216
|
+
}
|
|
217
|
+
getNode(name) {
|
|
218
|
+
return this.graph.node(name);
|
|
219
|
+
}
|
|
220
|
+
getDependents(packageName) {
|
|
221
|
+
return this.graph.predecessors(packageName);
|
|
222
|
+
}
|
|
223
|
+
getDependencies(packageName) {
|
|
224
|
+
return this.graph.successors(packageName);
|
|
225
|
+
}
|
|
226
|
+
constructor(options){
|
|
227
|
+
workspace_dev_define_property(this, "options", void 0);
|
|
228
|
+
workspace_dev_define_property(this, "cwd", void 0);
|
|
229
|
+
workspace_dev_define_property(this, "workspaceFileDir", void 0);
|
|
230
|
+
workspace_dev_define_property(this, "packages", []);
|
|
231
|
+
workspace_dev_define_property(this, "graph", void 0);
|
|
232
|
+
workspace_dev_define_property(this, "visited", void 0);
|
|
233
|
+
workspace_dev_define_property(this, "visiting", void 0);
|
|
234
|
+
workspace_dev_define_property(this, "matched", void 0);
|
|
235
|
+
workspace_dev_define_property(this, "metaData", void 0);
|
|
236
|
+
this.options = {
|
|
237
|
+
ignoreSelf: true,
|
|
238
|
+
...options
|
|
239
|
+
};
|
|
240
|
+
this.cwd = options.cwd || process.cwd();
|
|
241
|
+
this.workspaceFileDir = options.workspaceFileDir || this.cwd;
|
|
242
|
+
this.packages = [];
|
|
243
|
+
this.visited = {};
|
|
244
|
+
this.visiting = {};
|
|
245
|
+
this.matched = {};
|
|
246
|
+
this.graph = new Graph({
|
|
247
|
+
directed: true
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function pluginWorkspaceDev(options) {
|
|
252
|
+
return {
|
|
253
|
+
name: 'rsbuild-plugin-workspace-dev',
|
|
254
|
+
async setup (api) {
|
|
255
|
+
const rootPath = api.context.rootPath;
|
|
256
|
+
api.onBeforeStartDevServer(async ()=>{
|
|
257
|
+
const runner = new WorkspaceDevRunner({
|
|
258
|
+
cwd: rootPath,
|
|
259
|
+
...options
|
|
260
|
+
});
|
|
261
|
+
await runner.init();
|
|
262
|
+
await runner.start();
|
|
263
|
+
Logger.setEndBanner();
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
export { WorkspaceDevRunner, pluginWorkspaceDev };
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare class Logger {
|
|
2
|
+
stdout: string;
|
|
3
|
+
stderr: string;
|
|
4
|
+
name: string;
|
|
5
|
+
logTitle: string;
|
|
6
|
+
constructor({ name, }: {
|
|
7
|
+
name: string;
|
|
8
|
+
});
|
|
9
|
+
appendLog(type: 'stdout' | 'stderr', log: string): void;
|
|
10
|
+
emitLog(type: 'stdout' | 'stderr'): void;
|
|
11
|
+
emitLogOnce(type: 'stdout' | 'stderr', log: string): void;
|
|
12
|
+
reset(type: 'stdout' | 'stderr'): void;
|
|
13
|
+
setBanner(name: string): void;
|
|
14
|
+
flushStdout(): void;
|
|
15
|
+
static setEndBanner(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare const debugLog: (msg: string) => void;
|
package/dist/plugin.d.ts
ADDED
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import graphlib from 'graphlib';
|
|
2
|
+
export interface WorkspaceDevRunnerOptions {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
workspaceFileDir?: string;
|
|
5
|
+
projectConfig?: Record<string, {
|
|
6
|
+
match?: (stdout: string) => boolean;
|
|
7
|
+
command?: string;
|
|
8
|
+
skip?: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
ignoreSelf?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare class WorkspaceDevRunner {
|
|
13
|
+
private options;
|
|
14
|
+
private cwd;
|
|
15
|
+
private workspaceFileDir;
|
|
16
|
+
private packages;
|
|
17
|
+
private graph;
|
|
18
|
+
private visited;
|
|
19
|
+
private visiting;
|
|
20
|
+
private matched;
|
|
21
|
+
private metaData;
|
|
22
|
+
constructor(options: WorkspaceDevRunnerOptions);
|
|
23
|
+
init(): Promise<void>;
|
|
24
|
+
buildDependencyGraph(): void;
|
|
25
|
+
checkGraph(): void;
|
|
26
|
+
start(): Promise<void>;
|
|
27
|
+
visitNodes(node: string): Promise<void>;
|
|
28
|
+
getDependencyGraph(): graphlib.Graph;
|
|
29
|
+
getNodes(): string[];
|
|
30
|
+
getEdges(): graphlib.Edge[];
|
|
31
|
+
getNode(name: string): any;
|
|
32
|
+
getDependents(packageName: string): void | string[];
|
|
33
|
+
getDependencies(packageName: string): void | string[];
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rsbuild-plugin-workspace-dev",
|
|
3
|
+
"version": "0.0.1-beta.0",
|
|
4
|
+
"description": "An Rsbuild plugin to provides workspace recursive dev functionality for Monorepo topologies.",
|
|
5
|
+
"repository": "https://github.com/rspack-contrib/rsbuild-plugin-workspace-dev",
|
|
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": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"simple-git-hooks": {
|
|
22
|
+
"pre-commit": "npx nano-staged"
|
|
23
|
+
},
|
|
24
|
+
"nano-staged": {
|
|
25
|
+
"*.{js,jsx,ts,tsx,mjs,cjs}": [
|
|
26
|
+
"biome check --write --no-errors-on-unmatched"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"fast-glob": "^3.3.3",
|
|
31
|
+
"json5": "^2.2.3",
|
|
32
|
+
"yaml": "^2.8.1",
|
|
33
|
+
"@manypkg/get-packages": "^3.1.0",
|
|
34
|
+
"graphlib": "^2.1.8",
|
|
35
|
+
"chalk": "^5.6.2"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@biomejs/biome": "^2.3.3",
|
|
39
|
+
"@playwright/test": "^1.55.1",
|
|
40
|
+
"@rsbuild/core": "^1.6.1",
|
|
41
|
+
"@rsbuild/plugin-react": "^1.4.1",
|
|
42
|
+
"@rsbuild/plugin-type-check": "^1.3.0",
|
|
43
|
+
"@rslib/core": "^0.17.0",
|
|
44
|
+
"@types/node": "^22.18.8",
|
|
45
|
+
"@types/react": "^19.1.16",
|
|
46
|
+
"@types/react-dom": "^19.1.9",
|
|
47
|
+
"playwright": "^1.55.1",
|
|
48
|
+
"react": "^19.1.1",
|
|
49
|
+
"react-dom": "^19.1.1",
|
|
50
|
+
"simple-git-hooks": "^2.13.1",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"@types/graphlib": "^2.1.12",
|
|
53
|
+
"@rstest/core": "^0.6.6"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@rsbuild/core": "1.x"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"@rsbuild/core": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public",
|
|
65
|
+
"registry": "https://registry.npmjs.org/"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "rslib build",
|
|
69
|
+
"dev": "rslib build --watch",
|
|
70
|
+
"lint": "biome check .",
|
|
71
|
+
"lint:write": "biome check . --write",
|
|
72
|
+
"test": "pnpm run test:unit && pnpm run test:e2e",
|
|
73
|
+
"test:unit": "rstest --config=./test/rstest.config.ts",
|
|
74
|
+
"test:e2e": "playwright test --config=./test/playwright.config.ts",
|
|
75
|
+
"bump": "npx bumpp"
|
|
76
|
+
}
|
|
77
|
+
}
|