workspace-utils 1.0.0 → 1.0.2

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.
Files changed (49) hide show
  1. package/dist/index.js +15464 -0
  2. package/dist/package.json +57 -0
  3. package/package.json +4 -1
  4. package/.github/workflows/mdbook.yml +0 -64
  5. package/.prettierignore +0 -22
  6. package/.prettierrc +0 -13
  7. package/docs/book.toml +0 -10
  8. package/docs/src/SUMMARY.md +0 -24
  9. package/docs/src/commands/build.md +0 -110
  10. package/docs/src/commands/dev.md +0 -118
  11. package/docs/src/commands/overview.md +0 -239
  12. package/docs/src/commands/run.md +0 -153
  13. package/docs/src/configuration.md +0 -249
  14. package/docs/src/examples.md +0 -567
  15. package/docs/src/installation.md +0 -148
  16. package/docs/src/introduction.md +0 -117
  17. package/docs/src/quick-start.md +0 -278
  18. package/docs/src/troubleshooting.md +0 -533
  19. package/index.ts +0 -84
  20. package/src/commands/build.ts +0 -158
  21. package/src/commands/dev.ts +0 -192
  22. package/src/commands/run.test.ts +0 -329
  23. package/src/commands/run.ts +0 -118
  24. package/src/core/dependency-graph.ts +0 -262
  25. package/src/core/process-runner.ts +0 -355
  26. package/src/core/workspace.test.ts +0 -404
  27. package/src/core/workspace.ts +0 -228
  28. package/src/package-managers/bun.test.ts +0 -209
  29. package/src/package-managers/bun.ts +0 -79
  30. package/src/package-managers/detector.test.ts +0 -199
  31. package/src/package-managers/detector.ts +0 -111
  32. package/src/package-managers/index.ts +0 -10
  33. package/src/package-managers/npm.ts +0 -79
  34. package/src/package-managers/pnpm.ts +0 -101
  35. package/src/package-managers/types.ts +0 -42
  36. package/src/utils/output.ts +0 -301
  37. package/src/utils/package-utils.ts +0 -243
  38. package/tests/bun-workspace/apps/web-app/package.json +0 -18
  39. package/tests/bun-workspace/bun.lockb +0 -0
  40. package/tests/bun-workspace/package.json +0 -18
  41. package/tests/bun-workspace/packages/shared-utils/package.json +0 -15
  42. package/tests/bun-workspace/packages/ui-components/package.json +0 -17
  43. package/tests/npm-workspace/package-lock.json +0 -0
  44. package/tests/npm-workspace/package.json +0 -18
  45. package/tests/npm-workspace/packages/core/package.json +0 -15
  46. package/tests/pnpm-workspace/package.json +0 -14
  47. package/tests/pnpm-workspace/packages/utils/package.json +0 -15
  48. package/tests/pnpm-workspace/pnpm-workspace.yaml +0 -3
  49. 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
- }