bun-workspaces 1.0.0-alpha.21 → 1.0.0-alpha.23
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 +28 -15
- package/bin/cli.js +0 -0
- package/package.json +2 -2
- package/src/cli/projectCommands/handleRunScript.mjs +22 -4
- package/src/cli/projectCommands/projectCommandsConfig.d.ts +12 -4
- package/src/cli/projectCommands/projectCommandsConfig.mjs +7 -2
- package/src/config/userEnvVars.d.ts +8 -0
- package/src/config/userEnvVars.mjs +8 -0
- package/src/index.d.ts +5 -0
- package/src/internal/env.mjs +1 -1
- package/src/project/implementations/fileSystemProject.d.ts +13 -4
- package/src/project/implementations/fileSystemProject.mjs +11 -3
- package/src/project/runScript/index.d.ts +2 -0
- package/src/project/runScript/index.mjs +3 -1
- package/src/project/runScript/outputChunk.d.ts +22 -0
- package/src/project/runScript/outputChunk.mjs +30 -0
- package/src/project/runScript/parallel.d.ts +12 -0
- package/src/project/runScript/parallel.mjs +52 -0
- package/src/project/runScript/runScript.d.ts +1 -9
- package/src/project/runScript/runScript.mjs +3 -8
- package/src/project/runScript/runScripts.d.ts +7 -6
- package/src/project/runScript/runScripts.mjs +54 -25
package/README.md
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
<img src="./packages/doc-website/src/docs/public/bw-eye.png" alt="bun-workspaces" width="50" />
|
|
2
2
|
|
|
3
|
+
### [📖 **See Full Documentation Here**: _https://bunworkspaces.com_](https://bunworkspaces.com)
|
|
4
|
+
|
|
3
5
|
# bun-workspaces
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
A CLI and API to enhance your monorepo development with Bun's [native workspaces](https://bun.sh/docs/install/workspaces) feature for nested JavaScript/TypeScript packages.
|
|
8
|
+
|
|
9
|
+
- Works right away, with no boilerplate required 🍔🍴
|
|
10
|
+
- Get metadata about your monorepo 🤖
|
|
11
|
+
- Run package.json scripts across workspaces 📋
|
|
12
|
+
- Supports running inline scripts as well ⌨️
|
|
6
13
|
|
|
7
|
-
|
|
14
|
+
This tool lets you decide the complexity of how you use it.
|
|
15
|
+
To get started, all you need is a normal project using [Bun's native workspaces](https://bun.sh/docs/install/workspaces) feature for nested JavaScript/TypeScript packages.
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
Think of this as a power suit you can snap onto native workspaces, rather than another monorepo framework.
|
|
10
18
|
|
|
19
|
+
Start running some [CLI commands](https://bunworkspaces.com/cli) right away in your repo, or take full advantage of the [scripting API](https://bunworkspaces.com/api) and its features.
|
|
20
|
+
<br/><br/>
|
|
11
21
|
<a href="https://buymeacoffee.com/scottmorse">
|
|
12
|
-
<img src="./packages/doc-website/src/docs/public/bmac-logo-circle.png" alt="Link to Buy Me A Coffee" width="
|
|
22
|
+
<img src="./packages/doc-website/src/docs/public/bmac-logo-circle.png" alt="Link to Buy Me A Coffee" width="50" />
|
|
13
23
|
</a>
|
|
14
24
|
|
|
15
25
|
## Quick Start
|
|
@@ -37,7 +47,7 @@ bw list-workspaces
|
|
|
37
47
|
bw ls --json --pretty # Output as formatted JSON
|
|
38
48
|
|
|
39
49
|
# Run the lint script for all workspaces
|
|
40
|
-
# that have it in their "scripts" field
|
|
50
|
+
# that have it in their package.json "scripts" field
|
|
41
51
|
bw run-script lint
|
|
42
52
|
|
|
43
53
|
# run is an alias for run-script
|
|
@@ -47,14 +57,15 @@ bw run lint "my-workspace-*" # Run for matching workspace names
|
|
|
47
57
|
bw run lint --parallel # Run at the same time
|
|
48
58
|
bw run lint --args="--my-appended-args" # Add args to each script call
|
|
49
59
|
bw run lint --args="--my-arg=<workspaceName>" # Use the workspace name in args
|
|
50
|
-
|
|
51
|
-
# Run an inline command from the workspace directory
|
|
52
|
-
bw run "echo 'this is my inline script for <workspaceName>'" --inline
|
|
60
|
+
bw run "bun build" --inline --inline-name=build # Run an inline command
|
|
53
61
|
|
|
54
62
|
# Show usage (you can pass --help to any command)
|
|
55
63
|
bw help
|
|
56
64
|
bw --help
|
|
57
65
|
|
|
66
|
+
# Show version
|
|
67
|
+
bw --version
|
|
68
|
+
|
|
58
69
|
# Pass --cwd to any command
|
|
59
70
|
bw --cwd=/path/to/your/project ls
|
|
60
71
|
bw --cwd=/path/to/your/project run my-script
|
|
@@ -93,10 +104,11 @@ const runSingleScript = async () => {
|
|
|
93
104
|
});
|
|
94
105
|
|
|
95
106
|
// Get a stream of the script subprocess's output
|
|
96
|
-
for await (const
|
|
97
|
-
console.log(
|
|
98
|
-
console.log(
|
|
99
|
-
console.log(
|
|
107
|
+
for await (const chunk of output) {
|
|
108
|
+
console.log(chunk.raw); // The raw output content (Uint8Array)
|
|
109
|
+
console.log(chunk.decode()); // The output chunk's content (string)
|
|
110
|
+
console.log(chunk.decode({ stripAnsi: true })); // Text with ANSI codes sanitized
|
|
111
|
+
console.log(chunk.streamName); // The output stream, "stdout" or "stderr"
|
|
100
112
|
}
|
|
101
113
|
|
|
102
114
|
// Get data about the script execution after it exits
|
|
@@ -122,8 +134,9 @@ const runManyScripts = async () => {
|
|
|
122
134
|
|
|
123
135
|
// Get a stream of script output
|
|
124
136
|
for await (const { outputChunk, scriptMetadata } of output) {
|
|
125
|
-
console.log(outputChunk.
|
|
126
|
-
console.log(outputChunk.
|
|
137
|
+
console.log(outputChunk.raw); // The raw output content (Uint8Array)
|
|
138
|
+
console.log(outputChunk.decode()); // the output chunk's content (string)
|
|
139
|
+
console.log(outputChunk.decode({ stripAnsi: true })); // text with ANSI codes sanitized (string)
|
|
127
140
|
console.log(outputChunk.streamName); // "stdout" or "stderr"
|
|
128
141
|
|
|
129
142
|
// The metadata can distinguish which workspace script
|
|
@@ -155,4 +168,4 @@ const runManyScripts = async () => {
|
|
|
155
168
|
};
|
|
156
169
|
```
|
|
157
170
|
|
|
158
|
-
_`bun-workspaces` is independent from the [Bun](https://bun.sh) project and is not affiliated with or endorsed by
|
|
171
|
+
_`bun-workspaces` is independent from the [Bun](https://bun.sh) project and is not affiliated with or endorsed by Anthropic. This project aims to enhance enhance the experience of Bun for its users._
|
package/bin/cli.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-workspaces",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.23",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "src/index.mjs",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"custom": {
|
|
17
17
|
"bunVersion": {
|
|
18
|
-
"build": "1.3.
|
|
18
|
+
"build": "1.3.4",
|
|
19
19
|
"libraryConsumer": "^1.1.x"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
@@ -64,16 +64,34 @@ const runScript = handleCommand(
|
|
|
64
64
|
const { output, summary } = project.runScriptAcrossWorkspaces({
|
|
65
65
|
workspacePatterns: workspaces.map(({ name }) => name),
|
|
66
66
|
script,
|
|
67
|
-
inline: options.inline
|
|
67
|
+
inline: options.inline
|
|
68
|
+
? options.inlineName
|
|
69
|
+
? {
|
|
70
|
+
scriptName: options.inlineName,
|
|
71
|
+
}
|
|
72
|
+
: true
|
|
73
|
+
: undefined,
|
|
68
74
|
args: options.args,
|
|
69
|
-
parallel:
|
|
75
|
+
parallel:
|
|
76
|
+
typeof options.parallel === "boolean" ||
|
|
77
|
+
typeof options.parallel === "undefined"
|
|
78
|
+
? options.parallel
|
|
79
|
+
: options.parallel === "true"
|
|
80
|
+
? true
|
|
81
|
+
: options.parallel === "false"
|
|
82
|
+
? false
|
|
83
|
+
: {
|
|
84
|
+
max: options.parallel,
|
|
85
|
+
},
|
|
70
86
|
});
|
|
71
|
-
const scriptName = options.inline
|
|
87
|
+
const scriptName = options.inline
|
|
88
|
+
? options.inlineName || "(inline)"
|
|
89
|
+
: script;
|
|
72
90
|
const handleOutput = async () => {
|
|
73
91
|
if (logger.printLevel === "silent") return;
|
|
74
92
|
for await (const { outputChunk, scriptMetadata } of output) {
|
|
75
93
|
commandOutputLogger.logOutput(
|
|
76
|
-
outputChunk.
|
|
94
|
+
outputChunk.decode(),
|
|
77
95
|
"info",
|
|
78
96
|
process[outputChunk.streamName],
|
|
79
97
|
options.prefix
|
|
@@ -90,8 +90,8 @@ declare const CLI_PROJECT_COMMANDS_CONFIG: {
|
|
|
90
90
|
readonly description: 'Run a script in all workspaces that have it in their "scripts" field in package.json';
|
|
91
91
|
readonly options: {
|
|
92
92
|
readonly parallel: {
|
|
93
|
-
readonly flags: "--parallel";
|
|
94
|
-
readonly description: "Run the scripts in parallel";
|
|
93
|
+
readonly flags: "--parallel [max]";
|
|
94
|
+
readonly description: "Run the scripts in parallel. Pass an optional number, percentage, or keyword: 'default', 'auto', 'unbounded'";
|
|
95
95
|
};
|
|
96
96
|
readonly args: {
|
|
97
97
|
readonly flags: "--args <args>";
|
|
@@ -105,6 +105,10 @@ declare const CLI_PROJECT_COMMANDS_CONFIG: {
|
|
|
105
105
|
readonly flags: "--inline";
|
|
106
106
|
readonly description: "Run the script as an inline command from the workspace directory";
|
|
107
107
|
};
|
|
108
|
+
readonly inlineName: {
|
|
109
|
+
readonly flags: "--inline-name <name>";
|
|
110
|
+
readonly description: "An optional name for the script when --inline is passed";
|
|
111
|
+
};
|
|
108
112
|
readonly jsonOutfile: {
|
|
109
113
|
readonly flags: "--json-outfile <file>";
|
|
110
114
|
readonly description: "Output results in a JSON file";
|
|
@@ -193,8 +197,8 @@ export declare const getProjectCommandConfig: (
|
|
|
193
197
|
readonly description: 'Run a script in all workspaces that have it in their "scripts" field in package.json';
|
|
194
198
|
readonly options: {
|
|
195
199
|
readonly parallel: {
|
|
196
|
-
readonly flags: "--parallel";
|
|
197
|
-
readonly description: "Run the scripts in parallel";
|
|
200
|
+
readonly flags: "--parallel [max]";
|
|
201
|
+
readonly description: "Run the scripts in parallel. Pass an optional number, percentage, or keyword: 'default', 'auto', 'unbounded'";
|
|
198
202
|
};
|
|
199
203
|
readonly args: {
|
|
200
204
|
readonly flags: "--args <args>";
|
|
@@ -208,6 +212,10 @@ export declare const getProjectCommandConfig: (
|
|
|
208
212
|
readonly flags: "--inline";
|
|
209
213
|
readonly description: "Run the script as an inline command from the workspace directory";
|
|
210
214
|
};
|
|
215
|
+
readonly inlineName: {
|
|
216
|
+
readonly flags: "--inline-name <name>";
|
|
217
|
+
readonly description: "An optional name for the script when --inline is passed";
|
|
218
|
+
};
|
|
211
219
|
readonly jsonOutfile: {
|
|
212
220
|
readonly flags: "--json-outfile <file>";
|
|
213
221
|
readonly description: "Output results in a JSON file";
|
|
@@ -79,8 +79,9 @@ const CLI_PROJECT_COMMANDS_CONFIG = {
|
|
|
79
79
|
'Run a script in all workspaces that have it in their "scripts" field in package.json',
|
|
80
80
|
options: {
|
|
81
81
|
parallel: {
|
|
82
|
-
flags: "--parallel",
|
|
83
|
-
description:
|
|
82
|
+
flags: "--parallel [max]",
|
|
83
|
+
description:
|
|
84
|
+
"Run the scripts in parallel. Pass an optional number, percentage, or keyword: 'default', 'auto', 'unbounded'",
|
|
84
85
|
},
|
|
85
86
|
args: {
|
|
86
87
|
flags: "--args <args>",
|
|
@@ -95,6 +96,10 @@ const CLI_PROJECT_COMMANDS_CONFIG = {
|
|
|
95
96
|
description:
|
|
96
97
|
"Run the script as an inline command from the workspace directory",
|
|
97
98
|
},
|
|
99
|
+
inlineName: {
|
|
100
|
+
flags: "--inline-name <name>",
|
|
101
|
+
description: "An optional name for the script when --inline is passed",
|
|
102
|
+
},
|
|
98
103
|
jsonOutfile: {
|
|
99
104
|
flags: "--json-outfile <file>",
|
|
100
105
|
description: "Output results in a JSON file",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const USER_ENV_VARS: {
|
|
2
|
+
readonly parallelMaxDefault: "BW_PARALLEL_MAX_DEFAULT";
|
|
3
|
+
};
|
|
4
|
+
export type UserEnvVarName = keyof typeof USER_ENV_VARS;
|
|
5
|
+
export declare const getUserEnvVar: (key: UserEnvVarName) => string | undefined;
|
|
6
|
+
export declare const getUserEnvVarName: (
|
|
7
|
+
key: UserEnvVarName,
|
|
8
|
+
) => "BW_PARALLEL_MAX_DEFAULT";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// CONCATENATED MODULE: ./src/config/userEnvVars.ts
|
|
2
|
+
const USER_ENV_VARS = {
|
|
3
|
+
parallelMaxDefault: "BW_PARALLEL_MAX_DEFAULT",
|
|
4
|
+
};
|
|
5
|
+
const getUserEnvVar = (key) => process.env[USER_ENV_VARS[key]];
|
|
6
|
+
const getUserEnvVarName = (key) => USER_ENV_VARS[key];
|
|
7
|
+
|
|
8
|
+
export { USER_ENV_VARS, getUserEnvVar, getUserEnvVarName };
|
package/src/index.d.ts
CHANGED
|
@@ -13,10 +13,15 @@ export {
|
|
|
13
13
|
type WorkspaceScriptCommandMethod,
|
|
14
14
|
type RunWorkspaceScriptOptions,
|
|
15
15
|
type RunWorkspaceScriptResult,
|
|
16
|
+
type InlineScriptOptions,
|
|
16
17
|
type RunScriptAcrossWorkspacesOptions,
|
|
17
18
|
type RunScriptAcrossWorkspacesResult,
|
|
18
19
|
type OutputChunk,
|
|
19
20
|
type OutputStreamName,
|
|
21
|
+
type PercentageValue,
|
|
22
|
+
type ParallelMaxValue,
|
|
23
|
+
type ParallelOption,
|
|
24
|
+
type RunScriptsParallelOptions,
|
|
20
25
|
} from "./project";
|
|
21
26
|
export { type Workspace } from "./workspaces";
|
|
22
27
|
export { type SimpleAsyncIterable } from "./internal/types";
|
package/src/internal/env.mjs
CHANGED
|
@@ -19,7 +19,7 @@ const RUNTIME_MODE = RUNTIME_MODE_VALUES.includes(_RUNTIME_MODE)
|
|
|
19
19
|
if (RUNTIME_MODE !== _RUNTIME_MODE) {
|
|
20
20
|
// eslint-disable-next-line no-console
|
|
21
21
|
console.error(
|
|
22
|
-
`Env var
|
|
22
|
+
`Env var _BW_RUNTIME_MODE has an invalid value: "${_RUNTIME_MODE}". Defaulting to "${RUNTIME_MODE}". Accepted values: ${RUNTIME_MODE_VALUES.join(", ")}.`,
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
25
|
const IS_TEST = RUNTIME_MODE === "test";
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { Simplify } from "../../internal/types";
|
|
2
2
|
import { type Workspace } from "../../workspaces";
|
|
3
3
|
import type { Project } from "../project";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type RunScriptResult,
|
|
6
|
+
type RunScriptsParallelOptions,
|
|
7
|
+
type RunScriptsResult,
|
|
8
|
+
} from "../runScript";
|
|
5
9
|
import { ProjectBase } from "./projectBase";
|
|
6
10
|
/** Arguments for {@link createFileSystemProject} */
|
|
7
11
|
export type CreateFileSystemProjectOptions = {
|
|
@@ -14,6 +18,10 @@ export type CreateFileSystemProjectOptions = {
|
|
|
14
18
|
*/
|
|
15
19
|
name?: string;
|
|
16
20
|
};
|
|
21
|
+
export interface InlineScriptOptions {
|
|
22
|
+
/** A name to act as a label for the inline script */
|
|
23
|
+
scriptName: string;
|
|
24
|
+
}
|
|
17
25
|
/** Arguments for `FileSystemProject.runWorkspaceScript` */
|
|
18
26
|
export type RunWorkspaceScriptOptions = {
|
|
19
27
|
/** The name of the workspace to run the script in */
|
|
@@ -21,7 +29,7 @@ export type RunWorkspaceScriptOptions = {
|
|
|
21
29
|
/** The name of the script to run, or an inline command when `inline` is true */
|
|
22
30
|
script: string;
|
|
23
31
|
/** Whether to run the script as an inline command */
|
|
24
|
-
inline?: boolean;
|
|
32
|
+
inline?: boolean | InlineScriptOptions;
|
|
25
33
|
/** The arguments to append to the script command */
|
|
26
34
|
args?: string;
|
|
27
35
|
};
|
|
@@ -33,6 +41,7 @@ export type RunWorkspaceScriptMetadata = {
|
|
|
33
41
|
export type RunWorkspaceScriptResult = Simplify<
|
|
34
42
|
RunScriptResult<RunWorkspaceScriptMetadata>
|
|
35
43
|
>;
|
|
44
|
+
export type ParallelOption = boolean | RunScriptsParallelOptions;
|
|
36
45
|
/** Arguments for `FileSystemProject.runScriptAcrossWorkspaces` */
|
|
37
46
|
export type RunScriptAcrossWorkspacesOptions = {
|
|
38
47
|
/** Workspace names, aliases, or patterns including a wildcard */
|
|
@@ -40,11 +49,11 @@ export type RunScriptAcrossWorkspacesOptions = {
|
|
|
40
49
|
/** The name of the script to run, or an inline command when `inline` is true */
|
|
41
50
|
script: string;
|
|
42
51
|
/** Whether to run the script as an inline command */
|
|
43
|
-
inline?: boolean;
|
|
52
|
+
inline?: boolean | InlineScriptOptions;
|
|
44
53
|
/** The arguments to append to the script command. `<workspaceName>` will be replaced with the workspace name */
|
|
45
54
|
args?: string;
|
|
46
55
|
/** Whether to run the scripts in parallel (series by default) */
|
|
47
|
-
parallel?:
|
|
56
|
+
parallel?: ParallelOption;
|
|
48
57
|
};
|
|
49
58
|
/** Result of `FileSystemProject.runScriptAcrossWorkspaces` */
|
|
50
59
|
export type RunScriptAcrossWorkspacesResult = Simplify<
|
|
@@ -69,13 +69,17 @@ class _FileSystemProject extends ProjectBase {
|
|
|
69
69
|
logger.debug(
|
|
70
70
|
`Running script ${options.script} in workspace: ${workspace.name}`,
|
|
71
71
|
);
|
|
72
|
+
const inlineScriptName =
|
|
73
|
+
typeof options.inline === "object"
|
|
74
|
+
? (options.inline?.scriptName ?? "")
|
|
75
|
+
: "";
|
|
72
76
|
const scriptRuntimeMetadata = {
|
|
73
77
|
projectPath: this.rootDirectory,
|
|
74
78
|
projectName: this.name,
|
|
75
79
|
workspacePath: resolveWorkspacePath(this, workspace),
|
|
76
80
|
workspaceRelativePath: workspace.path,
|
|
77
81
|
workspaceName: workspace.name,
|
|
78
|
-
scriptName: options.inline ?
|
|
82
|
+
scriptName: options.inline ? inlineScriptName : options.script,
|
|
79
83
|
};
|
|
80
84
|
const args = interpolateScriptRuntimeMetadata(
|
|
81
85
|
options.args ?? "",
|
|
@@ -134,13 +138,17 @@ class _FileSystemProject extends ProjectBase {
|
|
|
134
138
|
);
|
|
135
139
|
return runScripts({
|
|
136
140
|
scripts: workspaces.map((workspace) => {
|
|
141
|
+
const inlineScriptName =
|
|
142
|
+
typeof options.inline === "object"
|
|
143
|
+
? (options.inline?.scriptName ?? "")
|
|
144
|
+
: "";
|
|
137
145
|
const scriptRuntimeMetadata = {
|
|
138
146
|
projectPath: this.rootDirectory,
|
|
139
147
|
projectName: this.name,
|
|
140
148
|
workspacePath: resolveWorkspacePath(this, workspace),
|
|
141
149
|
workspaceRelativePath: workspace.path,
|
|
142
150
|
workspaceName: workspace.name,
|
|
143
|
-
scriptName: options.inline ?
|
|
151
|
+
scriptName: options.inline ? inlineScriptName : options.script,
|
|
144
152
|
};
|
|
145
153
|
const args = interpolateScriptRuntimeMetadata(
|
|
146
154
|
options.args ?? "",
|
|
@@ -170,7 +178,7 @@ class _FileSystemProject extends ProjectBase {
|
|
|
170
178
|
env: createScriptRuntimeEnvVars(scriptRuntimeMetadata),
|
|
171
179
|
};
|
|
172
180
|
}),
|
|
173
|
-
parallel:
|
|
181
|
+
parallel: options.parallel ?? false,
|
|
174
182
|
});
|
|
175
183
|
}
|
|
176
184
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export * from "./runScript.mjs";
|
|
2
2
|
export * from "./runScripts.mjs";
|
|
3
|
-
export * from "./scriptCommand.mjs";
|
|
3
|
+
export * from "./scriptCommand.mjs";
|
|
4
|
+
export * from "./outputChunk.mjs";
|
|
5
|
+
export * from "./parallel.mjs"; // CONCATENATED MODULE: ./src/project/runScript/index.ts
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OutputStreamName } from "./runScript";
|
|
2
|
+
export interface DecodeOptions {
|
|
3
|
+
/** Whether to strip ANSI escape codes */
|
|
4
|
+
stripAnsi?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/** Output captured from a script subprocess */
|
|
7
|
+
export interface OutputChunk {
|
|
8
|
+
/** The source of the output, `"stdout"` or `"stderr"` */
|
|
9
|
+
streamName: OutputStreamName;
|
|
10
|
+
/** Raw output text. Pass `true` to strip ANSI escape codes. */
|
|
11
|
+
decode(options?: DecodeOptions): string;
|
|
12
|
+
/** The raw output content */
|
|
13
|
+
raw: Uint8Array<ArrayBuffer>;
|
|
14
|
+
/** @deprecated Use `decode()` instead */
|
|
15
|
+
text: string;
|
|
16
|
+
/** @deprecated Use `decode({ stripAnsi: true })` instead */
|
|
17
|
+
textNoAnsi: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const createOutputChunk: (
|
|
20
|
+
streamName: OutputStreamName,
|
|
21
|
+
raw: Uint8Array<ArrayBuffer>,
|
|
22
|
+
) => OutputChunk;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { sanitizeAnsi } from "../../internal/regex.mjs"; // CONCATENATED MODULE: external "../../internal/regex.mjs"
|
|
2
|
+
// CONCATENATED MODULE: ./src/project/runScript/outputChunk.ts
|
|
3
|
+
|
|
4
|
+
class _OutputChunk {
|
|
5
|
+
streamName;
|
|
6
|
+
raw;
|
|
7
|
+
constructor(streamName, raw) {
|
|
8
|
+
this.streamName = streamName;
|
|
9
|
+
this.raw = raw;
|
|
10
|
+
}
|
|
11
|
+
decode(options) {
|
|
12
|
+
const { stripAnsi = false } = options ?? {};
|
|
13
|
+
const text = new TextDecoder().decode(this.raw);
|
|
14
|
+
return stripAnsi ? sanitizeAnsi(text) : text;
|
|
15
|
+
}
|
|
16
|
+
/** @deprecated Use `decode()` instead */ get text() {
|
|
17
|
+
// TODO remove in future major release
|
|
18
|
+
return this.decode();
|
|
19
|
+
}
|
|
20
|
+
/** @deprecated Use `decode(true)` instead */ get textNoAnsi() {
|
|
21
|
+
// TODO remove in future major release
|
|
22
|
+
return this.decode({
|
|
23
|
+
stripAnsi: true,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const createOutputChunk = (streamName, raw) =>
|
|
28
|
+
new _OutputChunk(streamName, raw);
|
|
29
|
+
|
|
30
|
+
export { createOutputChunk };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type PercentageValue = `${number}%`;
|
|
2
|
+
export type ParallelMaxValue =
|
|
3
|
+
| number
|
|
4
|
+
| "auto"
|
|
5
|
+
| "default"
|
|
6
|
+
| "unbounded"
|
|
7
|
+
| PercentageValue;
|
|
8
|
+
/** Should always return at least 1 */
|
|
9
|
+
export declare const determineParallelMax: (
|
|
10
|
+
value: ParallelMaxValue,
|
|
11
|
+
fromEnvVar?: boolean,
|
|
12
|
+
) => number;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { availableParallelism } from "node:os";
|
|
2
|
+
import { getUserEnvVar, getUserEnvVarName } from "../../config/userEnvVars.mjs";
|
|
3
|
+
import { BunWorkspacesError } from "../../internal/error.mjs"; // CONCATENATED MODULE: external "node:os"
|
|
4
|
+
// CONCATENATED MODULE: external "../../config/userEnvVars.mjs"
|
|
5
|
+
// CONCATENATED MODULE: external "../../internal/error.mjs"
|
|
6
|
+
// CONCATENATED MODULE: ./src/project/runScript/parallel.ts
|
|
7
|
+
|
|
8
|
+
/** Should always return at least 1 */ const determineParallelMax = (
|
|
9
|
+
value,
|
|
10
|
+
fromEnvVar = false,
|
|
11
|
+
) => {
|
|
12
|
+
const errorMessageSuffix = fromEnvVar
|
|
13
|
+
? ` (set by env var ${getUserEnvVarName("parallelMaxDefault")})`
|
|
14
|
+
: "";
|
|
15
|
+
if (!isNaN(Number(value))) {
|
|
16
|
+
value = Math.floor(Number(value));
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === "number") {
|
|
19
|
+
if (value < 1 || isNaN(value)) {
|
|
20
|
+
throw new BunWorkspacesError(
|
|
21
|
+
`Parallel max value must be at least 1${errorMessageSuffix}`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return Math.floor(value);
|
|
25
|
+
}
|
|
26
|
+
if (value === "default") {
|
|
27
|
+
const defaultMax = getUserEnvVar("parallelMaxDefault")?.trim();
|
|
28
|
+
if (defaultMax === "default") return determineParallelMax("auto");
|
|
29
|
+
return determineParallelMax(defaultMax ?? "auto", true);
|
|
30
|
+
}
|
|
31
|
+
if (value === "unbounded") {
|
|
32
|
+
return Infinity;
|
|
33
|
+
}
|
|
34
|
+
const cpuCount = Math.max(1, availableParallelism());
|
|
35
|
+
if (value === "auto") {
|
|
36
|
+
return cpuCount;
|
|
37
|
+
}
|
|
38
|
+
if (value.endsWith("%")) {
|
|
39
|
+
const percentage = parseFloat(value.slice(0, -1));
|
|
40
|
+
if (isNaN(percentage) || percentage <= 0 || percentage > 100) {
|
|
41
|
+
throw new BunWorkspacesError(
|
|
42
|
+
`Parallel max value must be a number greater than 0 and less than or equal to 100${errorMessageSuffix}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return Math.max(1, Math.floor((cpuCount * percentage) / 100));
|
|
46
|
+
}
|
|
47
|
+
throw new BunWorkspacesError(
|
|
48
|
+
`Invalid parallel max value: ${JSON.stringify(value)}${errorMessageSuffix}`,
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export { determineParallelMax };
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import type { SimpleAsyncIterable } from "../../internal/types";
|
|
2
|
+
import { type OutputChunk } from "./outputChunk";
|
|
2
3
|
import type { ScriptCommand } from "./scriptCommand";
|
|
3
4
|
export type OutputStreamName = "stdout" | "stderr";
|
|
4
|
-
/** Output captured from a script subprocess */
|
|
5
|
-
export type OutputChunk = {
|
|
6
|
-
/** The source of the output, `"stdout"` or `"stderr"` */
|
|
7
|
-
streamName: OutputStreamName;
|
|
8
|
-
/** Raw output text */
|
|
9
|
-
text: string;
|
|
10
|
-
/** Stripped of ANSI escape codes */
|
|
11
|
-
textNoAnsi: string;
|
|
12
|
-
};
|
|
13
5
|
export type RunScriptExit<ScriptMetadata extends object = object> = {
|
|
14
6
|
exitCode: number;
|
|
15
7
|
signal: NodeJS.Signals | null;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { mergeAsyncIterables } from "../../internal/mergeAsyncIterables.mjs";
|
|
2
2
|
import { IS_WINDOWS } from "../../internal/os.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { createOutputChunk } from "./outputChunk.mjs"; // CONCATENATED MODULE: external "../../internal/mergeAsyncIterables.mjs"
|
|
4
4
|
// CONCATENATED MODULE: external "../../internal/os.mjs"
|
|
5
|
-
// CONCATENATED MODULE: external "
|
|
5
|
+
// CONCATENATED MODULE: external "./outputChunk.mjs"
|
|
6
6
|
// CONCATENATED MODULE: ./src/project/runScript/runScript.ts
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -28,12 +28,7 @@ import { sanitizeAnsi } from "../../internal/regex.mjs"; // CONCATENATED MODULE:
|
|
|
28
28
|
const stream = proc[streamName];
|
|
29
29
|
if (stream) {
|
|
30
30
|
for await (const chunk of stream) {
|
|
31
|
-
|
|
32
|
-
yield {
|
|
33
|
-
streamName,
|
|
34
|
-
text,
|
|
35
|
-
textNoAnsi: sanitizeAnsi(text),
|
|
36
|
-
};
|
|
31
|
+
yield createOutputChunk(streamName, chunk);
|
|
37
32
|
}
|
|
38
33
|
}
|
|
39
34
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { SimpleAsyncIterable } from "../../internal/types";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type RunScriptResult,
|
|
6
|
-
} from "./runScript";
|
|
2
|
+
import type { OutputChunk } from "./outputChunk";
|
|
3
|
+
import { type ParallelMaxValue } from "./parallel";
|
|
4
|
+
import { type RunScriptExit, type RunScriptResult } from "./runScript";
|
|
7
5
|
import { type ScriptCommand } from "./scriptCommand";
|
|
8
6
|
export type RunScriptsScript<ScriptMetadata extends object = object> = {
|
|
9
7
|
scriptCommand: ScriptCommand;
|
|
@@ -36,9 +34,12 @@ export type RunScriptsResult<ScriptMetadata extends object = object> = {
|
|
|
36
34
|
/** Resolves with a results summary after all scripts have exited */
|
|
37
35
|
summary: Promise<RunScriptsSummary<ScriptMetadata>>;
|
|
38
36
|
};
|
|
37
|
+
export type RunScriptsParallelOptions = {
|
|
38
|
+
max: ParallelMaxValue;
|
|
39
|
+
};
|
|
39
40
|
export type RunScriptsOptions<ScriptMetadata extends object = object> = {
|
|
40
41
|
scripts: RunScriptsScript<ScriptMetadata>[];
|
|
41
|
-
parallel: boolean;
|
|
42
|
+
parallel: boolean | RunScriptsParallelOptions;
|
|
42
43
|
};
|
|
43
44
|
/** Run a list of scripts */
|
|
44
45
|
export declare const runScripts: <ScriptMetadata extends object = object>({
|
|
@@ -1,49 +1,85 @@
|
|
|
1
1
|
import { createAsyncIterableQueue } from "../../internal/asyncIterableQueue.mjs";
|
|
2
|
+
import { logger } from "../../internal/logger.mjs";
|
|
3
|
+
import { determineParallelMax } from "./parallel.mjs";
|
|
2
4
|
import { runScript } from "./runScript.mjs"; // CONCATENATED MODULE: external "../../internal/asyncIterableQueue.mjs"
|
|
5
|
+
// CONCATENATED MODULE: external "../../internal/logger.mjs"
|
|
6
|
+
// CONCATENATED MODULE: external "./parallel.mjs"
|
|
3
7
|
// CONCATENATED MODULE: external "./runScript.mjs"
|
|
4
8
|
// CONCATENATED MODULE: ./src/project/runScript/runScripts.ts
|
|
5
9
|
|
|
6
10
|
/** Run a list of scripts */ const runScripts = ({ scripts, parallel }) => {
|
|
7
11
|
const startTime = new Date();
|
|
8
|
-
const
|
|
12
|
+
const scriptTriggers = scripts.map((_, index) => {
|
|
9
13
|
let trigger = () => {
|
|
10
14
|
void 0;
|
|
11
15
|
};
|
|
12
|
-
let result = {};
|
|
13
16
|
const promise = new Promise((res) => {
|
|
14
17
|
trigger = () => res(result);
|
|
15
18
|
});
|
|
16
|
-
result = {
|
|
17
|
-
promise,
|
|
18
|
-
trigger,
|
|
19
|
-
index,
|
|
20
|
-
};
|
|
21
|
-
return {
|
|
19
|
+
const result = {
|
|
22
20
|
promise,
|
|
23
21
|
trigger,
|
|
24
22
|
index,
|
|
25
23
|
};
|
|
24
|
+
return result;
|
|
26
25
|
});
|
|
27
26
|
const outputQueue = createAsyncIterableQueue();
|
|
28
27
|
const scriptResults = scripts.map(() => null);
|
|
29
|
-
|
|
28
|
+
const parallelMax =
|
|
29
|
+
parallel === false
|
|
30
|
+
? 1
|
|
31
|
+
: determineParallelMax(
|
|
32
|
+
typeof parallel === "boolean" ? "default" : parallel.max,
|
|
33
|
+
);
|
|
34
|
+
const parallelBatchSize = Math.min(parallelMax, scripts.length);
|
|
35
|
+
const recommendedParallelMax = determineParallelMax("auto");
|
|
36
|
+
if (
|
|
37
|
+
parallel &&
|
|
38
|
+
parallelBatchSize > recommendedParallelMax &&
|
|
39
|
+
process.env._BW_IS_INTERNAL_TEST !== "true"
|
|
40
|
+
) {
|
|
41
|
+
logger.warn(
|
|
42
|
+
`Number of scripts to run in parallel (${parallelBatchSize}) is greater than the available CPUs (${recommendedParallelMax})`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
let runningScriptCount = 0;
|
|
46
|
+
let nextScriptIndex = 0;
|
|
47
|
+
const queueScript = (index) => {
|
|
48
|
+
if (runningScriptCount >= parallelMax) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
30
51
|
const scriptResult = {
|
|
31
52
|
...scripts[index],
|
|
32
|
-
result: runScript(
|
|
53
|
+
result: runScript({
|
|
54
|
+
...scripts[index],
|
|
55
|
+
env: {
|
|
56
|
+
...scripts[index].env,
|
|
57
|
+
_BW_PARALLEL_MAX: parallelMax.toString(),
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
33
60
|
};
|
|
34
61
|
scriptResults[index] = scriptResult;
|
|
35
|
-
|
|
62
|
+
scriptTriggers[index].trigger();
|
|
63
|
+
runningScriptCount++;
|
|
64
|
+
nextScriptIndex++;
|
|
65
|
+
scriptResults[index].result.exit.then(() => {
|
|
66
|
+
runningScriptCount--;
|
|
67
|
+
if (nextScriptIndex < scripts.length) {
|
|
68
|
+
queueScript(nextScriptIndex);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
36
71
|
return scriptResult;
|
|
37
|
-
}
|
|
38
|
-
const
|
|
72
|
+
};
|
|
73
|
+
const handleScriptProcesses = async () => {
|
|
39
74
|
const outputReaders = [];
|
|
75
|
+
const scriptExits = [];
|
|
40
76
|
let pendingScriptCount = scripts.length;
|
|
41
77
|
while (pendingScriptCount > 0) {
|
|
42
78
|
const { index } = await Promise.race(
|
|
43
|
-
|
|
79
|
+
scriptTriggers.map((trigger) => trigger.promise),
|
|
44
80
|
);
|
|
45
81
|
pendingScriptCount--;
|
|
46
|
-
|
|
82
|
+
scriptTriggers[index].promise = new Promise(() => {
|
|
47
83
|
void 0;
|
|
48
84
|
});
|
|
49
85
|
outputReaders.push(
|
|
@@ -58,18 +94,12 @@ import { runScript } from "./runScript.mjs"; // CONCATENATED MODULE: external ".
|
|
|
58
94
|
);
|
|
59
95
|
}
|
|
60
96
|
await Promise.all(outputReaders);
|
|
97
|
+
await Promise.all(scriptExits);
|
|
61
98
|
outputQueue.close();
|
|
62
99
|
};
|
|
63
100
|
const awaitSummary = async () => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
scripts.map((_, index) => triggerScript(index).result.exit),
|
|
67
|
-
);
|
|
68
|
-
} else {
|
|
69
|
-
for (let index = 0; index < scripts.length; index++) {
|
|
70
|
-
await triggerScript(index).result.exit;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
101
|
+
scripts.forEach((_, index) => queueScript(index));
|
|
102
|
+
await handleScriptProcesses();
|
|
73
103
|
const scriptExitResults = await Promise.all(
|
|
74
104
|
scripts.map((_, index) => scriptResults[index].result.exit),
|
|
75
105
|
);
|
|
@@ -85,7 +115,6 @@ import { runScript } from "./runScript.mjs"; // CONCATENATED MODULE: external ".
|
|
|
85
115
|
scriptResults: scriptExitResults,
|
|
86
116
|
};
|
|
87
117
|
};
|
|
88
|
-
awaitScriptResults();
|
|
89
118
|
return {
|
|
90
119
|
output: outputQueue,
|
|
91
120
|
summary: awaitSummary(),
|