workspace-utils 1.0.0 ā 1.0.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/dist/index.js +15460 -0
- package/dist/package.json +57 -0
- package/package.json +4 -1
- package/.github/workflows/mdbook.yml +0 -64
- package/.prettierignore +0 -22
- package/.prettierrc +0 -13
- package/docs/book.toml +0 -10
- package/docs/src/SUMMARY.md +0 -24
- package/docs/src/commands/build.md +0 -110
- package/docs/src/commands/dev.md +0 -118
- package/docs/src/commands/overview.md +0 -239
- package/docs/src/commands/run.md +0 -153
- package/docs/src/configuration.md +0 -249
- package/docs/src/examples.md +0 -567
- package/docs/src/installation.md +0 -148
- package/docs/src/introduction.md +0 -117
- package/docs/src/quick-start.md +0 -278
- package/docs/src/troubleshooting.md +0 -533
- package/index.ts +0 -84
- package/src/commands/build.ts +0 -158
- package/src/commands/dev.ts +0 -192
- package/src/commands/run.test.ts +0 -329
- package/src/commands/run.ts +0 -118
- package/src/core/dependency-graph.ts +0 -262
- package/src/core/process-runner.ts +0 -355
- package/src/core/workspace.test.ts +0 -404
- package/src/core/workspace.ts +0 -228
- package/src/package-managers/bun.test.ts +0 -209
- package/src/package-managers/bun.ts +0 -79
- package/src/package-managers/detector.test.ts +0 -199
- package/src/package-managers/detector.ts +0 -111
- package/src/package-managers/index.ts +0 -10
- package/src/package-managers/npm.ts +0 -79
- package/src/package-managers/pnpm.ts +0 -101
- package/src/package-managers/types.ts +0 -42
- package/src/utils/output.ts +0 -301
- package/src/utils/package-utils.ts +0 -243
- package/tests/bun-workspace/apps/web-app/package.json +0 -18
- package/tests/bun-workspace/bun.lockb +0 -0
- package/tests/bun-workspace/package.json +0 -18
- package/tests/bun-workspace/packages/shared-utils/package.json +0 -15
- package/tests/bun-workspace/packages/ui-components/package.json +0 -17
- package/tests/npm-workspace/package-lock.json +0 -0
- package/tests/npm-workspace/package.json +0 -18
- package/tests/npm-workspace/packages/core/package.json +0 -15
- package/tests/pnpm-workspace/package.json +0 -14
- package/tests/pnpm-workspace/packages/utils/package.json +0 -15
- package/tests/pnpm-workspace/pnpm-workspace.yaml +0 -3
- package/tsconfig.json +0 -29
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
2
|
-
import pc from 'picocolors';
|
|
3
|
-
|
|
4
|
-
export interface ProcessOptions {
|
|
5
|
-
cwd: string;
|
|
6
|
-
env?: Record<string, string>;
|
|
7
|
-
stdio?: 'inherit' | 'pipe';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface LogOptions {
|
|
11
|
-
prefix: string;
|
|
12
|
-
color: string;
|
|
13
|
-
showTimestamp?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ProcessResult {
|
|
17
|
-
success: boolean;
|
|
18
|
-
exitCode: number;
|
|
19
|
-
packageName: string;
|
|
20
|
-
command: string;
|
|
21
|
-
duration: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class ProcessRunner {
|
|
25
|
-
private static colorPalette: string[] = [
|
|
26
|
-
'red',
|
|
27
|
-
'green',
|
|
28
|
-
'yellow',
|
|
29
|
-
'blue',
|
|
30
|
-
'magenta',
|
|
31
|
-
'cyan',
|
|
32
|
-
'gray',
|
|
33
|
-
'redBright',
|
|
34
|
-
'greenBright',
|
|
35
|
-
'yellowBright',
|
|
36
|
-
'blueBright',
|
|
37
|
-
'magentaBright',
|
|
38
|
-
'cyanBright',
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
private static assignedColors = new Map<string, string>();
|
|
42
|
-
private static colorIndex = 0;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Get a consistent color for a package
|
|
46
|
-
*/
|
|
47
|
-
static getPackageColor(packageName: string): string {
|
|
48
|
-
if (!this.assignedColors.has(packageName)) {
|
|
49
|
-
const colorIndex = this.colorIndex % this.colorPalette.length;
|
|
50
|
-
const color = this.colorPalette[colorIndex] || 'white';
|
|
51
|
-
this.assignedColors.set(packageName, color);
|
|
52
|
-
this.colorIndex++;
|
|
53
|
-
}
|
|
54
|
-
const color = this.assignedColors.get(packageName);
|
|
55
|
-
return color || 'white';
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Get color function from picocolors
|
|
60
|
-
*/
|
|
61
|
-
private static getColorFn(color: string) {
|
|
62
|
-
switch (color) {
|
|
63
|
-
case 'red':
|
|
64
|
-
return pc.red;
|
|
65
|
-
case 'green':
|
|
66
|
-
return pc.green;
|
|
67
|
-
case 'yellow':
|
|
68
|
-
return pc.yellow;
|
|
69
|
-
case 'blue':
|
|
70
|
-
return pc.blue;
|
|
71
|
-
case 'magenta':
|
|
72
|
-
return pc.magenta;
|
|
73
|
-
case 'cyan':
|
|
74
|
-
return pc.cyan;
|
|
75
|
-
case 'gray':
|
|
76
|
-
return pc.gray;
|
|
77
|
-
case 'redBright':
|
|
78
|
-
return pc.redBright;
|
|
79
|
-
case 'greenBright':
|
|
80
|
-
return pc.greenBright;
|
|
81
|
-
case 'yellowBright':
|
|
82
|
-
return pc.yellowBright;
|
|
83
|
-
case 'blueBright':
|
|
84
|
-
return pc.blueBright;
|
|
85
|
-
case 'magentaBright':
|
|
86
|
-
return pc.magentaBright;
|
|
87
|
-
case 'cyanBright':
|
|
88
|
-
return pc.cyanBright;
|
|
89
|
-
default:
|
|
90
|
-
return pc.white;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Run a single command with real-time log streaming
|
|
96
|
-
*/
|
|
97
|
-
static async runCommand(
|
|
98
|
-
command: string,
|
|
99
|
-
args: string[],
|
|
100
|
-
options: ProcessOptions,
|
|
101
|
-
logOptions: LogOptions
|
|
102
|
-
): Promise<ProcessResult> {
|
|
103
|
-
const startTime = Date.now();
|
|
104
|
-
const fullCommand = [command, ...args].join(' ');
|
|
105
|
-
|
|
106
|
-
const colorFn = this.getColorFn(logOptions.color);
|
|
107
|
-
console.log(`[${colorFn(logOptions.prefix)}] ` + pc.gray(`Starting: ${fullCommand}`));
|
|
108
|
-
|
|
109
|
-
return new Promise(resolve => {
|
|
110
|
-
const childProcess = spawn(command, args, {
|
|
111
|
-
cwd: options.cwd,
|
|
112
|
-
env: { ...process.env, ...options.env },
|
|
113
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Stream stdout with prefix and color
|
|
117
|
-
if (childProcess.stdout) {
|
|
118
|
-
childProcess.stdout.on('data', data => {
|
|
119
|
-
const lines = data.toString().split('\n');
|
|
120
|
-
lines.forEach((line: string) => {
|
|
121
|
-
if (line.trim()) {
|
|
122
|
-
this.logLine(line, logOptions, false);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Stream stderr with prefix and color
|
|
129
|
-
if (childProcess.stderr) {
|
|
130
|
-
childProcess.stderr.on('data', data => {
|
|
131
|
-
const lines = data.toString().split('\n');
|
|
132
|
-
lines.forEach((line: string) => {
|
|
133
|
-
if (line.trim()) {
|
|
134
|
-
this.logLine(line, logOptions, true);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
childProcess.on('close', exitCode => {
|
|
141
|
-
const duration = Date.now() - startTime;
|
|
142
|
-
const code = exitCode || 0;
|
|
143
|
-
|
|
144
|
-
const result: ProcessResult = {
|
|
145
|
-
success: code === 0,
|
|
146
|
-
exitCode: code,
|
|
147
|
-
packageName: logOptions.prefix,
|
|
148
|
-
command: fullCommand,
|
|
149
|
-
duration,
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const colorFn = this.getColorFn(logOptions.color);
|
|
153
|
-
if (code === 0) {
|
|
154
|
-
console.log(
|
|
155
|
-
`[${colorFn(logOptions.prefix)}] ` + pc.green(`ā
Completed in ${duration}ms`)
|
|
156
|
-
);
|
|
157
|
-
} else {
|
|
158
|
-
console.log(
|
|
159
|
-
`[${colorFn(logOptions.prefix)}] ` +
|
|
160
|
-
pc.red(`ā Failed with exit code ${code} (${duration}ms)`)
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
resolve(result);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
childProcess.on('error', error => {
|
|
168
|
-
const duration = Date.now() - startTime;
|
|
169
|
-
const colorFn = this.getColorFn(logOptions.color);
|
|
170
|
-
console.error(`[${colorFn(logOptions.prefix)}] ` + pc.red(`š„ Error: ${error.message}`));
|
|
171
|
-
|
|
172
|
-
resolve({
|
|
173
|
-
success: false,
|
|
174
|
-
exitCode: 1,
|
|
175
|
-
packageName: logOptions.prefix,
|
|
176
|
-
command: fullCommand,
|
|
177
|
-
duration,
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Log a single line with prefix and color
|
|
185
|
-
*/
|
|
186
|
-
private static logLine(line: string, logOptions: LogOptions, isError = false): void {
|
|
187
|
-
const timestamp = logOptions.showTimestamp ? pc.dim(`[${new Date().toISOString()}] `) : '';
|
|
188
|
-
|
|
189
|
-
// Only color the package name in brackets, not the entire line
|
|
190
|
-
const colorFn = this.getColorFn(logOptions.color);
|
|
191
|
-
const coloredPrefix = `[${colorFn(logOptions.prefix)}] `;
|
|
192
|
-
|
|
193
|
-
// Apply error color only to the actual log content if it's an error
|
|
194
|
-
const logContent = isError ? pc.red(line) : line;
|
|
195
|
-
|
|
196
|
-
console.log(timestamp + coloredPrefix + logContent);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Run multiple commands in parallel with concurrency limit
|
|
201
|
-
*/
|
|
202
|
-
static async runParallel(
|
|
203
|
-
commands: Array<{
|
|
204
|
-
command: string;
|
|
205
|
-
args: string[];
|
|
206
|
-
options: ProcessOptions;
|
|
207
|
-
logOptions: LogOptions;
|
|
208
|
-
}>,
|
|
209
|
-
concurrency = 4
|
|
210
|
-
): Promise<ProcessResult[]> {
|
|
211
|
-
const results: ProcessResult[] = [];
|
|
212
|
-
const executing: Promise<ProcessResult>[] = [];
|
|
213
|
-
|
|
214
|
-
for (let i = 0; i < commands.length; i++) {
|
|
215
|
-
const cmd = commands[i];
|
|
216
|
-
if (!cmd) continue;
|
|
217
|
-
|
|
218
|
-
// Start the command
|
|
219
|
-
const promise = this.runCommand(cmd.command, cmd.args, cmd.options, cmd.logOptions);
|
|
220
|
-
|
|
221
|
-
executing.push(promise);
|
|
222
|
-
|
|
223
|
-
// If we've reached the concurrency limit or this is the last command
|
|
224
|
-
if (executing.length >= concurrency || i === commands.length - 1) {
|
|
225
|
-
// Wait for at least one to complete
|
|
226
|
-
const completedIndex = await this.waitForAny(executing);
|
|
227
|
-
const completedPromise = executing[completedIndex];
|
|
228
|
-
if (completedPromise) {
|
|
229
|
-
const completed = await completedPromise;
|
|
230
|
-
results.push(completed);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Remove completed promise from executing array
|
|
234
|
-
executing.splice(completedIndex, 1);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Wait for all remaining commands to complete
|
|
239
|
-
const remainingResults = await Promise.all(executing);
|
|
240
|
-
results.push(...remainingResults);
|
|
241
|
-
|
|
242
|
-
return results;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Wait for any promise to complete and return its index
|
|
247
|
-
*/
|
|
248
|
-
private static async waitForAny(promises: Promise<ProcessResult>[]): Promise<number> {
|
|
249
|
-
return new Promise(resolve => {
|
|
250
|
-
promises.forEach((promise, index) => {
|
|
251
|
-
promise.then(() => resolve(index));
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Run commands sequentially (one after another)
|
|
258
|
-
*/
|
|
259
|
-
static async runSequential(
|
|
260
|
-
commands: Array<{
|
|
261
|
-
command: string;
|
|
262
|
-
args: string[];
|
|
263
|
-
options: ProcessOptions;
|
|
264
|
-
logOptions: LogOptions;
|
|
265
|
-
}>
|
|
266
|
-
): Promise<ProcessResult[]> {
|
|
267
|
-
const results: ProcessResult[] = [];
|
|
268
|
-
|
|
269
|
-
for (const cmd of commands) {
|
|
270
|
-
const result = await this.runCommand(cmd.command, cmd.args, cmd.options, cmd.logOptions);
|
|
271
|
-
|
|
272
|
-
results.push(result);
|
|
273
|
-
|
|
274
|
-
// Stop on first failure unless explicitly configured to continue
|
|
275
|
-
if (!result.success) {
|
|
276
|
-
console.log(pc.red(`\nā Stopping execution due to failure in ${result.packageName}`));
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return results;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Run commands in batches (for dependency-aware execution)
|
|
286
|
-
*/
|
|
287
|
-
static async runBatches(
|
|
288
|
-
batches: Array<
|
|
289
|
-
Array<{
|
|
290
|
-
command: string;
|
|
291
|
-
args: string[];
|
|
292
|
-
options: ProcessOptions;
|
|
293
|
-
logOptions: LogOptions;
|
|
294
|
-
}>
|
|
295
|
-
>,
|
|
296
|
-
concurrency = 4
|
|
297
|
-
): Promise<ProcessResult[]> {
|
|
298
|
-
const allResults: ProcessResult[] = [];
|
|
299
|
-
|
|
300
|
-
for (let i = 0; i < batches.length; i++) {
|
|
301
|
-
const batch = batches[i];
|
|
302
|
-
if (!batch) continue;
|
|
303
|
-
|
|
304
|
-
console.log(
|
|
305
|
-
pc.blue(`\nš Running batch ${i + 1}/${batches.length} (${batch.length} packages)`)
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
// Run all commands in this batch in parallel
|
|
309
|
-
const batchResults = await this.runParallel(batch, concurrency);
|
|
310
|
-
allResults.push(...batchResults);
|
|
311
|
-
|
|
312
|
-
// Check if any command in this batch failed
|
|
313
|
-
const failures = batchResults.filter(r => !r.success);
|
|
314
|
-
if (failures.length > 0) {
|
|
315
|
-
console.log(pc.red(`\nā Batch ${i + 1} failed. The following packages failed:`));
|
|
316
|
-
failures.forEach(f => {
|
|
317
|
-
console.log(pc.red(` ⢠${f.packageName}: ${f.command}`));
|
|
318
|
-
});
|
|
319
|
-
console.log(pc.red(`\nStopping execution due to batch failure.`));
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
console.log(pc.green(`ā
Batch ${i + 1} completed successfully`));
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return allResults;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Print execution summary
|
|
331
|
-
*/
|
|
332
|
-
static printSummary(results: ProcessResult[]): void {
|
|
333
|
-
const successful = results.filter(r => r.success);
|
|
334
|
-
const failed = results.filter(r => !r.success);
|
|
335
|
-
const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
|
|
336
|
-
|
|
337
|
-
console.log(pc.bold('\nš Execution Summary:'));
|
|
338
|
-
console.log(pc.green(`ā
Successful: ${successful.length}`));
|
|
339
|
-
|
|
340
|
-
if (failed.length > 0) {
|
|
341
|
-
console.log(pc.red(`ā Failed: ${failed.length}`));
|
|
342
|
-
console.log(pc.red('\nFailed packages:'));
|
|
343
|
-
failed.forEach(f => {
|
|
344
|
-
console.log(pc.red(` ⢠${f.packageName} (exit code ${f.exitCode})`));
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
console.log(pc.blue(`ā±ļø Total duration: ${totalDuration}ms`));
|
|
349
|
-
|
|
350
|
-
if (successful.length > 0) {
|
|
351
|
-
const avgDuration = Math.round(totalDuration / successful.length);
|
|
352
|
-
console.log(pc.dim(`š Average duration: ${avgDuration}ms`));
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|