rsbuild-plugin-workspace-dev 0.0.2 → 0.1.1

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/README.md CHANGED
@@ -104,10 +104,11 @@ interface projects {
104
104
  */
105
105
  match?: (stdout: string) => boolean;
106
106
  /**
107
- * Whether to skip starting the current sub-project. Default is `false`.
108
- * Useful for sub-projects that do not need to be started.
109
- */
110
- skip?: boolean;
107
+ * Whether to skip starting the current sub-project. The default value is `false`, typically used to skip sub-projects that don't need to be started.
108
+ * When the value is `true`, pruning will be performed on the specified project, meaning that this project and all its direct and indirect dependencies will not be started by the plugin.
109
+ * When the value is `only`, starting the specified project will be skipped, but no pruning will be performed, meaning that the project's direct and indirect dependencies will still be started by the plugin.
110
+ */
111
+ skip?: boolean | 'only';
111
112
  }
112
113
 
113
114
 
package/README.zh-CN.md CHANGED
@@ -99,8 +99,10 @@ interface Projects {
99
99
  match?: (stdout: string) => boolean;
100
100
  /**
101
101
  * 是否跳过当前子项目的启动,默认值为 `false`,通常用于跳过一些不需要启动的子项目。
102
+ * 当值为 `true` 时,会从指定项目进行剪枝,这意味着该项目以及他的所有直接和间接依赖都不会被插件启动。
103
+ * 当值为 `only` 时,会跳过指定项目的启动,但不会进行剪枝,这意味着该项目的直接和间接依赖仍然会被插件启动。
102
104
  */
103
- skip?: boolean;
105
+ skip?: boolean | 'only';
104
106
  }
105
107
 
106
108
  // 例如,配置 lib1 子项目,用 build:watch 命令启动,匹配 watch success 日志
@@ -1,5 +1,4 @@
1
1
  export declare const PACKAGE_JSON = "package.json";
2
- export declare const DEBUG_LOG_TITLE = "[Rsbuild Workspace Dev Plugin]: ";
2
+ export declare const PLUGIN_LOG_TITLE = "[Rsbuild Workspace Dev Plugin]: ";
3
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
4
  export declare const TSUP_READY_MESSAGE = "Watching for changes in";
package/dist/index.cjs CHANGED
@@ -39,9 +39,8 @@ __webpack_require__.d(__webpack_exports__, {
39
39
  const external_chalk_namespaceObject = require("chalk");
40
40
  var external_chalk_default = /*#__PURE__*/ __webpack_require__.n(external_chalk_namespaceObject);
41
41
  const PACKAGE_JSON = 'package.json';
42
- const DEBUG_LOG_TITLE = '[Rsbuild Workspace Dev Plugin]: ';
42
+ const PLUGIN_LOG_TITLE = '[Rsbuild Workspace Dev Plugin]: ';
43
43
  const RSLIB_READY_MESSAGE = 'build complete, watching for changes';
44
- const MODERN_MODULE_READY_MESSAGE = 'Watching for file changes';
45
44
  const TSUP_READY_MESSAGE = 'Watching for changes in';
46
45
  const external_fs_namespaceObject = require("fs");
47
46
  var external_fs_default = /*#__PURE__*/ __webpack_require__.n(external_fs_namespaceObject);
@@ -107,10 +106,10 @@ class Logger {
107
106
  this.name = name;
108
107
  this.stdout = '';
109
108
  this.stderr = '';
110
- this.logTitle = DEBUG_LOG_TITLE;
109
+ this.logTitle = PLUGIN_LOG_TITLE;
111
110
  }
112
111
  }
113
- const debugLog = (msg, prefix = DEBUG_LOG_TITLE)=>{
112
+ const debugLog = (msg, prefix = PLUGIN_LOG_TITLE)=>{
114
113
  if (isDebug) console.log(prefix + msg);
115
114
  };
116
115
  const get_packages_namespaceObject = require("@manypkg/get-packages");
@@ -118,7 +117,6 @@ const external_child_process_namespaceObject = require("child_process");
118
117
  const external_graphlib_namespaceObject = require("graphlib");
119
118
  var external_graphlib_default = /*#__PURE__*/ __webpack_require__.n(external_graphlib_namespaceObject);
120
119
  const external_path_namespaceObject = require("path");
121
- var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
122
120
  function workspace_dev_define_property(obj, key, value) {
123
121
  if (key in obj) Object.defineProperty(obj, key, {
124
122
  value: value,
@@ -131,7 +129,7 @@ function workspace_dev_define_property(obj, key, value) {
131
129
  }
132
130
  class WorkspaceDevRunner {
133
131
  async init() {
134
- this.metaData = await readPackageJson(external_path_default().join(this.cwd, PACKAGE_JSON));
132
+ this.metaData = await readPackageJson((0, external_path_namespaceObject.join)(this.cwd, PACKAGE_JSON));
135
133
  this.buildDependencyGraph();
136
134
  debugLog(`Dependency graph:\nnodes: ${this.getNodes().join(', ')}\nedges: ${this.getEdges().map((edge)=>`${edge.v} -> ${edge.w}`).join(', ')}\n`);
137
135
  }
@@ -147,6 +145,8 @@ class WorkspaceDevRunner {
147
145
  packageJson,
148
146
  path: dir
149
147
  };
148
+ const skip = this.options.projects?.[name]?.skip;
149
+ if (true === skip) return;
150
150
  this.graph.setNode(name, node);
151
151
  this.visited[name] = false;
152
152
  this.visiting[name] = false;
@@ -159,12 +159,13 @@ class WorkspaceDevRunner {
159
159
  };
160
160
  for (const depName of Object.keys(deps)){
161
161
  const isInternalDep = this.packages.some((p)=>p.packageJson.name === depName);
162
- if (isInternalDep) {
162
+ const skip = this.options.projects?.[depName]?.skip;
163
+ if (isInternalDep) if (true !== skip) {
163
164
  this.graph.setEdge(packageName, depName);
164
165
  this.checkGraph();
165
166
  const depPackage = packages.find((pkg)=>pkg.packageJson.name === depName);
166
167
  if (!this.getNode(depName)) initNode(depPackage);
167
- }
168
+ } else debugLog(`Prune project ${depName} and its dependencies because it is marked as skip: true`);
168
169
  }
169
170
  };
170
171
  initNode(currentPackage);
@@ -172,8 +173,11 @@ class WorkspaceDevRunner {
172
173
  checkGraph() {
173
174
  const cycles = external_graphlib_default().alg.findCycles(this.graph);
174
175
  const nonSelfCycles = cycles.filter((c)=>1 !== c.length);
175
- debugLog(`cycles check: ${cycles}`);
176
- if (nonSelfCycles.length) throw new Error(`${DEBUG_LOG_TITLE}Dependency graph do not allow cycles.`);
176
+ const nonSkipCycles = nonSelfCycles.filter((group)=>{
177
+ const isSkip = group.some((node)=>this.options.projects?.[node]?.skip);
178
+ return !isSkip;
179
+ });
180
+ if (nonSkipCycles.length) throw new Error(`${PLUGIN_LOG_TITLE} Cycle dependency graph found: ${nonSkipCycles}, you should config projects in plugin options to skip someone, or fix the cycle dependency. Otherwise, a loop of dev will occur.`);
177
181
  }
178
182
  async start() {
179
183
  const promises = [];
@@ -185,7 +189,8 @@ class WorkspaceDevRunner {
185
189
  const canStart = dependencies.every((dep)=>{
186
190
  const selfStart = node === dep;
187
191
  const isVisiting = this.visiting[dep];
188
- const isVisited = selfStart || this.visited[dep];
192
+ const skipDep = this.options.projects?.[dep]?.skip;
193
+ const isVisited = selfStart || this.visited[dep] || skipDep;
189
194
  return isVisited && !isVisiting;
190
195
  });
191
196
  if (canStart && !this.visited[node] && !this.visiting[node]) {
@@ -198,22 +203,24 @@ class WorkspaceDevRunner {
198
203
  }
199
204
  visitNodes(node) {
200
205
  return new Promise((resolve)=>{
201
- const { name, path } = this.getNode(node);
206
+ const { name, path, packageJson } = this.getNode(node);
202
207
  const logger = new Logger({
203
208
  name
204
209
  });
205
210
  const config = this.options?.projects?.[name];
206
- if (config?.skip) {
211
+ const command = config?.command ? config.command : 'dev';
212
+ const scripts = packageJson.scripts || {};
213
+ if (config?.skip || !scripts[command]) {
207
214
  this.visited[node] = true;
208
215
  this.visiting[node] = false;
209
216
  debugLog(`Skip visit node: ${node}`);
210
- logger.emitLogOnce('stdout', `skip visit node: ${name}`);
217
+ logger.emitLogOnce('stdout', `Skip visit node: ${name}`);
211
218
  return this.start().then(()=>resolve());
212
219
  }
213
220
  this.visiting[node] = true;
214
221
  const child = (0, external_child_process_namespaceObject.spawn)('npm', [
215
222
  'run',
216
- config?.command ? config.command : 'dev'
223
+ command
217
224
  ], {
218
225
  cwd: path,
219
226
  env: {
@@ -229,7 +236,7 @@ class WorkspaceDevRunner {
229
236
  debugLog(content, `${name}: `);
230
237
  logger.appendLog('stdout', stdout);
231
238
  const match = config?.match;
232
- const matchResult = match ? match(stdout) : stdout.match(RSLIB_READY_MESSAGE) || stdout.match(MODERN_MODULE_READY_MESSAGE) || stdout.match(TSUP_READY_MESSAGE);
239
+ const matchResult = match ? match(stdout) : stdout.match(RSLIB_READY_MESSAGE) || stdout.match(TSUP_READY_MESSAGE);
233
240
  if (matchResult) {
234
241
  logger.flushStdout();
235
242
  this.matched[node] = true;
@@ -303,6 +310,16 @@ function pluginWorkspaceDev(options) {
303
310
  await runner.start();
304
311
  Logger.setEndBanner();
305
312
  });
313
+ api.onBeforeBuild(async ({ isWatch, isFirstCompile })=>{
314
+ if (!isWatch || !isFirstCompile) return;
315
+ const runner = new WorkspaceDevRunner({
316
+ cwd: rootPath,
317
+ ...options
318
+ });
319
+ await runner.init();
320
+ await runner.start();
321
+ Logger.setEndBanner();
322
+ });
306
323
  }
307
324
  };
308
325
  }
package/dist/index.js CHANGED
@@ -4,11 +4,10 @@ import json5 from "json5";
4
4
  import { getPackagesSync } from "@manypkg/get-packages";
5
5
  import { spawn } from "child_process";
6
6
  import graphlib, { Graph } from "graphlib";
7
- import path_0 from "path";
7
+ import { join } from "path";
8
8
  const PACKAGE_JSON = 'package.json';
9
- const DEBUG_LOG_TITLE = '[Rsbuild Workspace Dev Plugin]: ';
9
+ const PLUGIN_LOG_TITLE = '[Rsbuild Workspace Dev Plugin]: ';
10
10
  const RSLIB_READY_MESSAGE = 'build complete, watching for changes';
11
- const MODERN_MODULE_READY_MESSAGE = 'Watching for file changes';
12
11
  const TSUP_READY_MESSAGE = 'Watching for changes in';
13
12
  async function pathExists(path) {
14
13
  return fs.promises.access(path).then(()=>true).catch(()=>false);
@@ -70,10 +69,10 @@ class Logger {
70
69
  this.name = name;
71
70
  this.stdout = '';
72
71
  this.stderr = '';
73
- this.logTitle = DEBUG_LOG_TITLE;
72
+ this.logTitle = PLUGIN_LOG_TITLE;
74
73
  }
75
74
  }
76
- const debugLog = (msg, prefix = DEBUG_LOG_TITLE)=>{
75
+ const debugLog = (msg, prefix = PLUGIN_LOG_TITLE)=>{
77
76
  if (isDebug) console.log(prefix + msg);
78
77
  };
79
78
  function workspace_dev_define_property(obj, key, value) {
@@ -88,7 +87,7 @@ function workspace_dev_define_property(obj, key, value) {
88
87
  }
89
88
  class WorkspaceDevRunner {
90
89
  async init() {
91
- this.metaData = await readPackageJson(path_0.join(this.cwd, PACKAGE_JSON));
90
+ this.metaData = await readPackageJson(join(this.cwd, PACKAGE_JSON));
92
91
  this.buildDependencyGraph();
93
92
  debugLog(`Dependency graph:\nnodes: ${this.getNodes().join(', ')}\nedges: ${this.getEdges().map((edge)=>`${edge.v} -> ${edge.w}`).join(', ')}\n`);
94
93
  }
@@ -104,6 +103,8 @@ class WorkspaceDevRunner {
104
103
  packageJson,
105
104
  path: dir
106
105
  };
106
+ const skip = this.options.projects?.[name]?.skip;
107
+ if (true === skip) return;
107
108
  this.graph.setNode(name, node);
108
109
  this.visited[name] = false;
109
110
  this.visiting[name] = false;
@@ -116,12 +117,13 @@ class WorkspaceDevRunner {
116
117
  };
117
118
  for (const depName of Object.keys(deps)){
118
119
  const isInternalDep = this.packages.some((p)=>p.packageJson.name === depName);
119
- if (isInternalDep) {
120
+ const skip = this.options.projects?.[depName]?.skip;
121
+ if (isInternalDep) if (true !== skip) {
120
122
  this.graph.setEdge(packageName, depName);
121
123
  this.checkGraph();
122
124
  const depPackage = packages.find((pkg)=>pkg.packageJson.name === depName);
123
125
  if (!this.getNode(depName)) initNode(depPackage);
124
- }
126
+ } else debugLog(`Prune project ${depName} and its dependencies because it is marked as skip: true`);
125
127
  }
126
128
  };
127
129
  initNode(currentPackage);
@@ -129,8 +131,11 @@ class WorkspaceDevRunner {
129
131
  checkGraph() {
130
132
  const cycles = graphlib.alg.findCycles(this.graph);
131
133
  const nonSelfCycles = cycles.filter((c)=>1 !== c.length);
132
- debugLog(`cycles check: ${cycles}`);
133
- if (nonSelfCycles.length) throw new Error(`${DEBUG_LOG_TITLE}Dependency graph do not allow cycles.`);
134
+ const nonSkipCycles = nonSelfCycles.filter((group)=>{
135
+ const isSkip = group.some((node)=>this.options.projects?.[node]?.skip);
136
+ return !isSkip;
137
+ });
138
+ if (nonSkipCycles.length) throw new Error(`${PLUGIN_LOG_TITLE} Cycle dependency graph found: ${nonSkipCycles}, you should config projects in plugin options to skip someone, or fix the cycle dependency. Otherwise, a loop of dev will occur.`);
134
139
  }
135
140
  async start() {
136
141
  const promises = [];
@@ -142,7 +147,8 @@ class WorkspaceDevRunner {
142
147
  const canStart = dependencies.every((dep)=>{
143
148
  const selfStart = node === dep;
144
149
  const isVisiting = this.visiting[dep];
145
- const isVisited = selfStart || this.visited[dep];
150
+ const skipDep = this.options.projects?.[dep]?.skip;
151
+ const isVisited = selfStart || this.visited[dep] || skipDep;
146
152
  return isVisited && !isVisiting;
147
153
  });
148
154
  if (canStart && !this.visited[node] && !this.visiting[node]) {
@@ -155,22 +161,24 @@ class WorkspaceDevRunner {
155
161
  }
156
162
  visitNodes(node) {
157
163
  return new Promise((resolve)=>{
158
- const { name, path } = this.getNode(node);
164
+ const { name, path, packageJson } = this.getNode(node);
159
165
  const logger = new Logger({
160
166
  name
161
167
  });
162
168
  const config = this.options?.projects?.[name];
163
- if (config?.skip) {
169
+ const command = config?.command ? config.command : 'dev';
170
+ const scripts = packageJson.scripts || {};
171
+ if (config?.skip || !scripts[command]) {
164
172
  this.visited[node] = true;
165
173
  this.visiting[node] = false;
166
174
  debugLog(`Skip visit node: ${node}`);
167
- logger.emitLogOnce('stdout', `skip visit node: ${name}`);
175
+ logger.emitLogOnce('stdout', `Skip visit node: ${name}`);
168
176
  return this.start().then(()=>resolve());
169
177
  }
170
178
  this.visiting[node] = true;
171
179
  const child = spawn('npm', [
172
180
  'run',
173
- config?.command ? config.command : 'dev'
181
+ command
174
182
  ], {
175
183
  cwd: path,
176
184
  env: {
@@ -186,7 +194,7 @@ class WorkspaceDevRunner {
186
194
  debugLog(content, `${name}: `);
187
195
  logger.appendLog('stdout', stdout);
188
196
  const match = config?.match;
189
- const matchResult = match ? match(stdout) : stdout.match(RSLIB_READY_MESSAGE) || stdout.match(MODERN_MODULE_READY_MESSAGE) || stdout.match(TSUP_READY_MESSAGE);
197
+ const matchResult = match ? match(stdout) : stdout.match(RSLIB_READY_MESSAGE) || stdout.match(TSUP_READY_MESSAGE);
190
198
  if (matchResult) {
191
199
  logger.flushStdout();
192
200
  this.matched[node] = true;
@@ -260,6 +268,16 @@ function pluginWorkspaceDev(options) {
260
268
  await runner.start();
261
269
  Logger.setEndBanner();
262
270
  });
271
+ api.onBeforeBuild(async ({ isWatch, isFirstCompile })=>{
272
+ if (!isWatch || !isFirstCompile) return;
273
+ const runner = new WorkspaceDevRunner({
274
+ cwd: rootPath,
275
+ ...options
276
+ });
277
+ await runner.init();
278
+ await runner.start();
279
+ Logger.setEndBanner();
280
+ });
263
281
  }
264
282
  };
265
283
  }
@@ -0,0 +1,6 @@
1
+ import type { Package } from '@manypkg/get-packages';
2
+ export interface PackageWithScripts extends Package {
3
+ packageJson: Package['packageJson'] & {
4
+ scripts?: Record<string, string>;
5
+ };
6
+ }
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Package } from '@manypkg/get-packages';
1
+ import type { PackageWithScripts } from './types.js';
2
2
  export declare const readJson: <T>(jsonFileAbsPath: string) => Promise<T>;
3
- export declare const readPackageJson: (pkgJsonFilePath: string) => Promise<Package["packageJson"]>;
3
+ export declare const readPackageJson: (pkgJsonFilePath: string) => Promise<PackageWithScripts["packageJson"]>;
4
4
  export declare const isDebug: boolean;
@@ -5,7 +5,7 @@ export interface WorkspaceDevRunnerOptions {
5
5
  projects?: Record<string, {
6
6
  match?: (stdout: string) => boolean;
7
7
  command?: string;
8
- skip?: boolean;
8
+ skip?: boolean | 'only';
9
9
  }>;
10
10
  startCurrent?: boolean;
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rsbuild-plugin-workspace-dev",
3
- "version": "0.0.2",
3
+ "version": "0.1.1",
4
4
  "description": "An Rsbuild plugin to provides workspace recursive dev functionality for Monorepo topologies.",
5
5
  "repository": "https://github.com/rspack-contrib/rsbuild-plugin-workspace-dev",
6
6
  "license": "MIT",