bun-workspaces 1.0.0-alpha.7 → 1.0.0-alpha.8

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.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Scott Morse
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 CHANGED
@@ -1,8 +1,12 @@
1
+ <img src="./packages/doc-website/src/docs/public/bw-eye.png" alt="bun-workspaces" width="50" />
2
+
1
3
  # bun-workspaces
2
4
 
3
- This is a CLI meant to help manage [Bun workspaces](https://bun.sh/docs/install/workspaces).
5
+ This is a CLI that works on top of native [Bun workspaces](https://bun.sh/docs/install/workspaces) with no additional setup required. Get metadata about your workspaces and scripts, and run scripts across your workspaces.
6
+
7
+ ### **[See Full Documentation Here](https://bunworkspaces.com)**
4
8
 
5
- ## Installation
9
+ ## Quick Start
6
10
 
7
11
  You can install the CLI in your project or simply use `bunx bun-workspaces`.
8
12
 
@@ -11,107 +15,25 @@ $ bun add --dev bun-workspaces
11
15
  $ bunx bun-workspaces --help
12
16
  ```
13
17
 
14
- ### Config file
15
-
16
- You can create a config file at `bw.json` in your project root, or you can pass a config file to the CLI with the `--configFile` (or `-c`) option.
17
-
18
- #### Example config
19
-
20
- In this config, "app-a" is an alias for package "@my-org/application-a" and "app-b" is an alias for package "@my-org/application-b".
21
-
22
- CLI log levels are `debug`, `info`, `warn`, and `error` or `silent`. The default log level is `info`. Commands that are intended to print specific output will still print at `silent`, such as `list-workspaces`, `list-scripts`, `workspace-info`, `script-info`, etc., but other logs will be suppressed.
23
-
24
- ```json
25
- {
26
- "workspaceAliases": {
27
- "app-a": "@my-org/application-a",
28
- "app-b": "@my-org/application-b"
29
- },
30
- "cli": {
31
- "logLevel": "warn"
32
- }
33
- }
34
- ```
35
-
36
- You can also pass a config file to the CLI with the `-c` or `--configFile` option.
37
-
38
- ### Examples
39
-
40
- You might consider making a shorter alias in your `.bashrc`, `.zshrc`, or similar shell configuration file, such as `alias bw="bunx bun-workspaces"`, for convenience.
41
-
42
18
  ```bash
43
19
  alias bw="bunx bun-workspaces"
44
20
 
45
- # List all workspaces
46
- bw list-workspaces
47
- bw ls
48
-
49
- # List workspace names only
50
- bw list-workspaces --name-only
51
-
52
- # Filter list of workspaces with wildcard
53
- bw list-workspaces "my-*"
54
-
55
- # List all workspace scripts
56
- bw list-scripts
57
-
58
- # List script names only
59
- bw list-scripts --name-only
60
-
61
- # Get info about a workspace
62
- bw workspace-info my-workspace
63
- bw info my-workspace
64
-
65
- # Get info about a script
66
- bw script-info my-script
67
-
68
- # Only print list of workspace names that have the script
69
- bw script-info my-script --workspaces-only
21
+ # Usage (--help can also be passed to any command)
22
+ bw help
23
+ bw --help
70
24
 
71
- # Get JSON output
25
+ # Get JSON metadata
72
26
  bw list-workspaces --json --pretty # optionally pretty print JSON
73
27
  bw list-scripts --json
74
28
  bw workspace-info my-workspace --json
75
29
  bw script-info my-script --json
76
30
 
77
- # Run a script for all
78
- # workspaces that have it
79
- # in their `scripts` field
31
+ # Run scripts across workspaces
80
32
  bw run my-script
81
-
82
- # By default, a prefix is added to the script output with the workspace name
83
- # This can be disabled with the --noPrefix option
84
- bw run my-script --noPrefix
85
-
86
- # Run a script for a specific workspace by its package.json name or alias from the config
87
33
  bw run my-script my-workspace
88
-
89
- # Run a script for multiple workspaces
90
34
  bw run my-script workspace-a workspace-b
91
-
92
- # Run a script for workspaces using wildcard (does not take into account workspace aliases)
93
35
  bw run my-script "my-workspace-*"
94
-
95
- # Run script in parallel for all workspaces
96
36
  bw run my-script --parallel
97
-
98
- # Append args to each script call
99
37
  bw run my-script --args "--my --args"
100
-
101
- # Use the workspace name in args
102
38
  bw run my-script --args "--my --args=<workspace>"
103
-
104
- # Help (--help can also be passed to any command)
105
- bw help
106
- bw --help
107
-
108
- # Pass --cwd to any command
109
- bw --cwd /path/to/your/project ls
110
- bw --cwd /path/to/your/project run-script my-script
111
-
112
- # Pass --configFile to any command
113
- bw --configFile /path/to/your/config.json ls
114
-
115
- # Pass --logLevel to any command (debug, info, warn, error, or silent)
116
- bw --logLevel silent run my-script
117
39
  ```
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "bun-workspaces",
3
- "version": "1.0.0-alpha.7",
3
+ "version": "1.0.0-alpha.8",
4
+ "license": "MIT",
4
5
  "main": "src/index.mjs",
5
6
  "types": "src/index.d.ts",
6
7
  "homepage": "https://github.com/ScottMorse/bun-workspaces",
@@ -10,4 +10,4 @@ export interface CreateCliProgramOptions {
10
10
  postInit?: (program: Command) => unknown;
11
11
  defaultCwd?: string;
12
12
  }
13
- export declare const createCliProgram: ({ handleError, postInit, defaultCwd, }?: CreateCliProgramOptions) => CliProgram;
13
+ export declare const createCli: ({ handleError, postInit, defaultCwd, }?: CreateCliProgramOptions) => CliProgram;
@@ -2,9 +2,9 @@ import { createCommand } from "commander";
2
2
  import package_0 from "../../package.json";
3
3
  import { getRequiredBunVersion, validateCurrentBunVersion } from "../internal/bunVersion.mjs";
4
4
  import { logger } from "../internal/logger.mjs";
5
- import { initializeWithGlobalOptions } from "./globalOptions.mjs";
6
- import { defineProjectCommands } from "./projectCommands.mjs";
7
- const createCliProgram = ({ handleError, postInit, defaultCwd = process.cwd() } = {})=>{
5
+ import { initializeWithGlobalOptions } from "./globalOptions/index.mjs";
6
+ import { defineProjectCommands } from "./projectCommands/index.mjs";
7
+ const createCli = ({ handleError, postInit, defaultCwd = process.cwd() } = {})=>{
8
8
  const run = async ({ argv = process.argv } = {})=>{
9
9
  const errorListener = handleError ?? ((error)=>{
10
10
  logger.error(error);
@@ -40,4 +40,4 @@ const createCliProgram = ({ handleError, postInit, defaultCwd = process.cwd() }
40
40
  run
41
41
  };
42
42
  };
43
- export { createCliProgram };
43
+ export { createCli };
@@ -0,0 +1,4 @@
1
+ import { type Command } from "commander";
2
+ export declare const initializeWithGlobalOptions: (program: Command, args: string[], defaultCwd: string) => {
3
+ project: import("../..").Project;
4
+ };
@@ -1,15 +1,21 @@
1
1
  import path from "path";
2
2
  import { Option } from "commander";
3
- import { loadConfigFile } from "../config/index.mjs";
4
- import { LOG_LEVELS, logger } from "../internal/logger.mjs";
5
- import { createProject } from "../project/index.mjs";
3
+ import { loadConfigFile } from "../../config/index.mjs";
4
+ import { logger } from "../../internal/logger.mjs";
5
+ import { createProject } from "../../project/index.mjs";
6
+ import { getCliGlobalOptionConfig } from "./globalOptionsConfig.mjs";
7
+ const addGlobalOption = (program, optionName, defaultOverride)=>{
8
+ const { mainOption, shortOption, description, param, values, defaultValue } = getCliGlobalOptionConfig(optionName);
9
+ const option = new Option(`${shortOption} ${mainOption}${param ? ` <${param}>` : ""}`, description).default(defaultOverride ?? defaultValue);
10
+ program.addOption(values?.length ? option.choices(values) : option);
11
+ };
6
12
  const getWorkingDirectory = (program, args, defaultCwd)=>{
7
- program.addOption(new Option("-d --cwd <path>", "Working directory").default(defaultCwd));
13
+ addGlobalOption(program, "cwd", defaultCwd);
8
14
  program.parseOptions(args);
9
15
  return program.opts().cwd;
10
16
  };
11
17
  const getConfig = (program, args)=>{
12
- program.addOption(new Option("-c --configFile <path>", "Config file"));
18
+ addGlobalOption(program, "configFile");
13
19
  program.parseOptions(args);
14
20
  return program.opts().configFile;
15
21
  };
@@ -17,22 +23,7 @@ const defineGlobalOptions = (program, args, defaultCwd)=>{
17
23
  const cwd = getWorkingDirectory(program, args, defaultCwd);
18
24
  const configFilePath = getConfig(program, args);
19
25
  const config = loadConfigFile(configFilePath, cwd);
20
- const globalOptions = {
21
- logLevel: {
22
- shortName: "l",
23
- description: "Log levels",
24
- defaultValue: config?.cli?.logLevel ?? logger.printLevel,
25
- values: [
26
- ...LOG_LEVELS,
27
- "silent"
28
- ],
29
- param: "level"
30
- }
31
- };
32
- Object.entries(globalOptions).forEach(([name, { shortName, description, param, values, defaultValue }])=>{
33
- const option = new Option(`-${shortName} --${name}${param ? ` <${param}>` : ""}`, description).default(defaultValue);
34
- program.addOption(values?.length ? option.choices(values) : option);
35
- });
26
+ addGlobalOption(program, "logLevel");
36
27
  return {
37
28
  cwd,
38
29
  config
@@ -0,0 +1,37 @@
1
+ import { type LogLevelSetting } from "../../internal/logger";
2
+ export interface CliGlobalOptions {
3
+ logLevel: LogLevelSetting;
4
+ cwd: string;
5
+ configFile?: string;
6
+ }
7
+ export interface CliGlobalOptionConfig {
8
+ mainOption: string;
9
+ shortOption: string;
10
+ description: string;
11
+ defaultValue: string;
12
+ values: LogLevelSetting[] | null;
13
+ param: string;
14
+ }
15
+ export type CliGlobalOptionName = keyof CliGlobalOptions;
16
+ export declare const getCliGlobalOptionConfig: (optionName: CliGlobalOptionName) => {
17
+ readonly mainOption: "--logLevel";
18
+ readonly shortOption: "-l";
19
+ readonly description: "Log levels";
20
+ readonly defaultValue: "info";
21
+ readonly values: ["debug", "info", "warn", "error", "silent"];
22
+ readonly param: "level";
23
+ } | {
24
+ readonly mainOption: "--cwd";
25
+ readonly shortOption: "-d";
26
+ readonly description: "Working directory";
27
+ readonly defaultValue: ".";
28
+ readonly values: null;
29
+ readonly param: "path";
30
+ } | {
31
+ readonly mainOption: "--configFile";
32
+ readonly shortOption: "-c";
33
+ readonly description: "Config file";
34
+ readonly defaultValue: "";
35
+ readonly values: null;
36
+ readonly param: "path";
37
+ };
@@ -0,0 +1,32 @@
1
+ import { LOG_LEVELS } from "../../internal/logger.mjs";
2
+ const CLI_GLOBAL_OPTIONS_CONFIG = {
3
+ logLevel: {
4
+ mainOption: "--logLevel",
5
+ shortOption: "-l",
6
+ description: "Log levels",
7
+ defaultValue: "info",
8
+ values: [
9
+ ...LOG_LEVELS,
10
+ "silent"
11
+ ],
12
+ param: "level"
13
+ },
14
+ cwd: {
15
+ mainOption: "--cwd",
16
+ shortOption: "-d",
17
+ description: "Working directory",
18
+ defaultValue: ".",
19
+ values: null,
20
+ param: "path"
21
+ },
22
+ configFile: {
23
+ mainOption: "--configFile",
24
+ shortOption: "-c",
25
+ description: "Config file",
26
+ defaultValue: "",
27
+ values: null,
28
+ param: "path"
29
+ }
30
+ };
31
+ const getCliGlobalOptionConfig = (optionName)=>CLI_GLOBAL_OPTIONS_CONFIG[optionName];
32
+ export { getCliGlobalOptionConfig };
@@ -0,0 +1,2 @@
1
+ export * from "./globalOptionsConfig";
2
+ export * from "./globalOptions";
@@ -0,0 +1,2 @@
1
+ export * from "./globalOptionsConfig.mjs";
2
+ export * from "./globalOptions.mjs";
@@ -1 +1 @@
1
- export { createCliProgram as createCli } from "./cli";
1
+ export { createCli } from "./createCli";
package/src/cli/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { createCliProgram } from "./cli.mjs";
2
- export { createCliProgram as createCli };
1
+ import { createCli } from "./createCli.mjs";
2
+ export { createCli };
@@ -0,0 +1,2 @@
1
+ export * from "./projectCommandHandlers";
2
+ export * from "./projectCommandsConfig";
@@ -0,0 +1,2 @@
1
+ export * from "./projectCommandHandlers.mjs";
2
+ export * from "./projectCommandsConfig.mjs";
@@ -0,0 +1,8 @@
1
+ import { type Command } from "commander";
2
+ import type { Project } from "../../project";
3
+ export interface ProjectCommandContext {
4
+ project: Project;
5
+ program: Command;
6
+ }
7
+ export declare const commandOutputLogger: import("../../internal/logger").Logger;
8
+ export declare const defineProjectCommands: (context: ProjectCommandContext) => void;
@@ -0,0 +1,180 @@
1
+ import { BunWorkspacesError } from "../../internal/error.mjs";
2
+ import { createLogger, logger } from "../../internal/logger.mjs";
3
+ import { getProjectCommandConfig } from "./projectCommandsConfig.mjs";
4
+ const createWorkspaceInfoLines = (workspace)=>[
5
+ `Workspace: ${workspace.name}`,
6
+ ` - Aliases: ${workspace.aliases.join(", ")}`,
7
+ ` - Path: ${workspace.path}`,
8
+ ` - Glob Match: ${workspace.matchPattern}`,
9
+ ` - Scripts: ${Object.keys(workspace.packageJson.scripts).sort().join(", ")}`
10
+ ];
11
+ const createScriptInfoLines = (script, workspaces)=>[
12
+ `Script: ${script}`,
13
+ ...workspaces.map((workspace)=>` - ${workspace.name}`)
14
+ ];
15
+ const createJsonLines = (data, options)=>JSON.stringify(data, null, options.pretty ? 2 : void 0).split("\n");
16
+ const commandOutputLogger = createLogger("");
17
+ commandOutputLogger.printLevel = "info";
18
+ const handleCommand = (commandName, handler)=>{
19
+ const config = getProjectCommandConfig(commandName);
20
+ return ({ program, project })=>{
21
+ program = program.command(config.command).aliases(config.aliases).description(config.description);
22
+ for (const option of Object.values(config.options))program.option(option.flags, option.description);
23
+ program = program.action((...actionArgs)=>handler({
24
+ program,
25
+ project
26
+ }, ...actionArgs));
27
+ return program;
28
+ };
29
+ };
30
+ const listWorkspaces = handleCommand("listWorkspaces", ({ project }, pattern, options)=>{
31
+ logger.debug(`Command: List workspaces (options: ${JSON.stringify(options)})`);
32
+ const lines = [];
33
+ const workspaces = pattern ? project.findWorkspacesByPattern(pattern) : project.workspaces;
34
+ if (options.json) lines.push(...createJsonLines(options.nameOnly ? workspaces.map(({ name })=>name) : workspaces, options));
35
+ else workspaces.forEach((workspace)=>{
36
+ if (options.nameOnly) lines.push(workspace.name);
37
+ else lines.push(...createWorkspaceInfoLines(workspace));
38
+ });
39
+ if (!lines.length) logger.info("No workspaces found");
40
+ if (lines.length) commandOutputLogger.info(lines.join("\n"));
41
+ });
42
+ const listScripts = handleCommand("listScripts", ({ project }, options)=>{
43
+ logger.debug(`Command: List scripts (options: ${JSON.stringify(options)})`);
44
+ const scripts = project.listScriptsWithWorkspaces();
45
+ const lines = [];
46
+ if (options.json) lines.push(...createJsonLines(options.nameOnly ? Object.keys(scripts) : Object.values(scripts).map(({ workspaces, ...rest })=>({
47
+ ...rest,
48
+ workspaces: workspaces.map(({ name })=>name)
49
+ })), options));
50
+ else {
51
+ Object.values(scripts).sort(({ name: nameA }, { name: nameB })=>nameA.localeCompare(nameB)).forEach(({ name, workspaces })=>{
52
+ if (options.nameOnly) lines.push(name);
53
+ else lines.push(...createScriptInfoLines(name, workspaces));
54
+ });
55
+ if (!lines.length) logger.info("No scripts found");
56
+ }
57
+ if (lines.length) commandOutputLogger.info(lines.join("\n"));
58
+ });
59
+ const workspaceInfo = handleCommand("workspaceInfo", ({ project }, workspaceName, options)=>{
60
+ logger.debug(`Command: Workspace info for ${workspaceName} (options: ${JSON.stringify(options)})`);
61
+ const workspace = project.findWorkspaceByNameOrAlias(workspaceName);
62
+ if (!workspace) return void logger.error(`Workspace not found: (options: ${JSON.stringify(workspaceName)})`);
63
+ commandOutputLogger.info((options.json ? createJsonLines(workspace, options) : createWorkspaceInfoLines(workspace)).join("\n"));
64
+ });
65
+ const scriptInfo = handleCommand("scriptInfo", ({ project }, script, options)=>{
66
+ logger.debug(`Command: Script info for ${script} (options: ${JSON.stringify(options)})`);
67
+ const scripts = project.listScriptsWithWorkspaces();
68
+ const scriptMetadata = scripts[script];
69
+ if (!scriptMetadata) return void logger.error(`Script not found: ${JSON.stringify(script)} (available: ${Object.keys(scripts).join(", ")})`);
70
+ commandOutputLogger.info((options.json ? createJsonLines(options.workspacesOnly ? scriptMetadata.workspaces.map(({ name })=>name) : {
71
+ name: scriptMetadata.name,
72
+ workspaces: scriptMetadata.workspaces.map(({ name })=>name)
73
+ }, options) : options.workspacesOnly ? scriptMetadata.workspaces.map(({ name })=>name) : createScriptInfoLines(script, scriptMetadata.workspaces)).join("\n"));
74
+ });
75
+ const runScript = handleCommand("runScript", async ({ project }, script, _workspaces, options)=>{
76
+ logger.debug(`Command: Run script ${JSON.stringify(script)} for ${_workspaces.length ? "workspaces " + _workspaces.join(", ") : "all workspaces"} (parallel: ${!!options.parallel}, args: ${JSON.stringify(options.args)})`);
77
+ const workspaces = _workspaces.length ? _workspaces.flatMap((workspacePattern)=>{
78
+ if (workspacePattern.includes("*")) return project.findWorkspacesByPattern(workspacePattern).filter(({ packageJson: { scripts } })=>scripts?.[script]).map(({ name })=>name);
79
+ return [
80
+ workspacePattern
81
+ ];
82
+ }).filter((workspace)=>project.workspaces.some(({ name })=>name === workspace)) : project.listWorkspacesWithScript(script).map(({ name })=>name);
83
+ if (!workspaces.length) {
84
+ if (1 === _workspaces.length && !_workspaces[0].includes("*")) {
85
+ const message = `Workspace not found: ${JSON.stringify(_workspaces[0])}`;
86
+ logger.error(message);
87
+ throw new Error(message);
88
+ }
89
+ const message = `No ${_workspaces.length ? "matching " : ""}workspaces found for script ${JSON.stringify(script)}`;
90
+ logger.error(message);
91
+ throw new Error(message);
92
+ }
93
+ const scriptCommands = workspaces.map((workspaceName)=>project.createScriptCommand({
94
+ scriptName: script,
95
+ workspaceName,
96
+ method: "cd",
97
+ args: options.args?.replace(/<workspace>/g, workspaceName) ?? ""
98
+ }));
99
+ const runCommand = async ({ command, scriptName, workspace })=>{
100
+ const commandLogger = createLogger(`${workspace.name}:${scriptName}`);
101
+ const splitCommand = command.command.split(/\s+/g);
102
+ commandLogger.debug(`Running script ${scriptName} in workspace ${workspace.name} (cwd: ${command.cwd}): ${splitCommand.join(" ")}`);
103
+ const isSilent = "silent" === logger.printLevel;
104
+ const proc = Bun.spawn(command.command.split(/\s+/g), {
105
+ cwd: command.cwd,
106
+ env: {
107
+ ...process.env,
108
+ FORCE_COLOR: "1"
109
+ },
110
+ stdout: isSilent ? "ignore" : "pipe",
111
+ stderr: isSilent ? "ignore" : "pipe"
112
+ });
113
+ const linePrefix = options.noPrefix ? "" : `[${workspace.name}:${scriptName}] `;
114
+ if (proc.stdout) for await (const chunk of proc.stdout)commandLogger.logOutput(chunk, "info", process.stdout, linePrefix);
115
+ if (proc.stderr) for await (const chunk of proc.stderr)commandLogger.logOutput(chunk, "error", process.stderr, linePrefix);
116
+ await proc.exited;
117
+ return {
118
+ scriptName,
119
+ workspace,
120
+ command,
121
+ success: 0 === proc.exitCode,
122
+ error: 0 === proc.exitCode ? null : new BunWorkspacesError(`Script exited with code ${proc.exitCode}`)
123
+ };
124
+ };
125
+ const results = [];
126
+ if (options.parallel) {
127
+ let i = 0;
128
+ for await (const result of (await Promise.allSettled(scriptCommands.map(runCommand)))){
129
+ if ("rejected" === result.status) results.push({
130
+ success: false,
131
+ workspaceName: workspaces[i],
132
+ error: result.reason
133
+ });
134
+ else results.push({
135
+ success: result.value.success,
136
+ workspaceName: workspaces[i],
137
+ error: result.value.error
138
+ });
139
+ i++;
140
+ }
141
+ } else {
142
+ let i = 0;
143
+ for (const command of scriptCommands){
144
+ try {
145
+ const result = await runCommand(command);
146
+ results.push({
147
+ success: result.success,
148
+ workspaceName: workspaces[i],
149
+ error: result.error
150
+ });
151
+ } catch (error) {
152
+ results.push({
153
+ success: false,
154
+ workspaceName: workspaces[i],
155
+ error: error
156
+ });
157
+ }
158
+ i++;
159
+ }
160
+ }
161
+ let failCount = 0;
162
+ results.forEach(({ success, workspaceName })=>{
163
+ if (!success) failCount++;
164
+ logger.info(`${success ? "✅" : "❌"} ${workspaceName}: ${script}`);
165
+ });
166
+ const s = 1 === results.length ? "" : "s";
167
+ if (failCount) {
168
+ const message = `${failCount} of ${results.length} script${s} failed`;
169
+ logger.error(message);
170
+ process.exit(1);
171
+ } else logger.info(`${results.length} script${s} ran successfully`);
172
+ });
173
+ const defineProjectCommands = (context)=>{
174
+ listWorkspaces(context);
175
+ listScripts(context);
176
+ workspaceInfo(context);
177
+ scriptInfo(context);
178
+ runScript(context);
179
+ };
180
+ export { commandOutputLogger, defineProjectCommands };
@@ -0,0 +1,191 @@
1
+ export interface ProjectCommandConfig {
2
+ command: string;
3
+ aliases: string[];
4
+ description: string;
5
+ options: Record<string, {
6
+ flags: string;
7
+ description: string;
8
+ }>;
9
+ }
10
+ export type ProjectCommandName = keyof typeof PROJECT_COMMANDS_CONFIG;
11
+ declare const PROJECT_COMMANDS_CONFIG: {
12
+ readonly listWorkspaces: {
13
+ readonly command: "list-workspaces [pattern]";
14
+ readonly aliases: ["ls", "list"];
15
+ readonly description: "List all workspaces";
16
+ readonly options: {
17
+ readonly nameOnly: {
18
+ readonly flags: "--name-only";
19
+ readonly description: "Only show workspace names";
20
+ };
21
+ readonly json: {
22
+ readonly flags: "--json";
23
+ readonly description: "Output as JSON";
24
+ };
25
+ readonly pretty: {
26
+ readonly flags: "--pretty";
27
+ readonly description: "Pretty print JSON";
28
+ };
29
+ };
30
+ };
31
+ readonly listScripts: {
32
+ readonly command: "list-scripts";
33
+ readonly aliases: [];
34
+ readonly description: "List all scripts available with their workspaces";
35
+ readonly options: {
36
+ readonly nameOnly: {
37
+ readonly flags: "--name-only";
38
+ readonly description: "Only show script names";
39
+ };
40
+ readonly json: {
41
+ readonly flags: "--json";
42
+ readonly description: "Output as JSON";
43
+ };
44
+ readonly pretty: {
45
+ readonly flags: "--pretty";
46
+ readonly description: "Pretty print JSON";
47
+ };
48
+ };
49
+ };
50
+ readonly workspaceInfo: {
51
+ readonly command: "workspace-info <workspace>";
52
+ readonly aliases: ["info"];
53
+ readonly description: "Show information about a workspace";
54
+ readonly options: {
55
+ readonly json: {
56
+ readonly flags: "--json";
57
+ readonly description: "Output as JSON";
58
+ };
59
+ readonly pretty: {
60
+ readonly flags: "--pretty";
61
+ readonly description: "Pretty print JSON";
62
+ };
63
+ };
64
+ };
65
+ readonly scriptInfo: {
66
+ readonly command: "script-info <script>";
67
+ readonly aliases: [];
68
+ readonly description: "Show information about a script";
69
+ readonly options: {
70
+ readonly workspacesOnly: {
71
+ readonly flags: "--workspaces-only";
72
+ readonly description: "Only show script's workspace names";
73
+ };
74
+ readonly json: {
75
+ readonly flags: "--json";
76
+ readonly description: "Output as JSON";
77
+ };
78
+ readonly pretty: {
79
+ readonly flags: "--pretty";
80
+ readonly description: "Pretty print JSON";
81
+ };
82
+ };
83
+ };
84
+ readonly runScript: {
85
+ readonly command: "run <script> [workspaces...]";
86
+ readonly aliases: [];
87
+ readonly description: "Run a script in all workspaces that have it in their \"scripts\" field in package.json";
88
+ readonly options: {
89
+ readonly parallel: {
90
+ readonly flags: "--parallel";
91
+ readonly description: "Run the scripts in parallel";
92
+ };
93
+ readonly args: {
94
+ readonly flags: "--args <args>";
95
+ readonly description: "Args to append to the script command";
96
+ };
97
+ readonly noPrefix: {
98
+ readonly flags: "--noPrefix";
99
+ readonly description: "Do not prefix the workspace name to the script output";
100
+ };
101
+ };
102
+ };
103
+ };
104
+ export declare const getProjectCommandConfig: (commandName: ProjectCommandName) => {
105
+ readonly command: "list-workspaces [pattern]";
106
+ readonly aliases: ["ls", "list"];
107
+ readonly description: "List all workspaces";
108
+ readonly options: {
109
+ readonly nameOnly: {
110
+ readonly flags: "--name-only";
111
+ readonly description: "Only show workspace names";
112
+ };
113
+ readonly json: {
114
+ readonly flags: "--json";
115
+ readonly description: "Output as JSON";
116
+ };
117
+ readonly pretty: {
118
+ readonly flags: "--pretty";
119
+ readonly description: "Pretty print JSON";
120
+ };
121
+ };
122
+ } | {
123
+ readonly command: "list-scripts";
124
+ readonly aliases: [];
125
+ readonly description: "List all scripts available with their workspaces";
126
+ readonly options: {
127
+ readonly nameOnly: {
128
+ readonly flags: "--name-only";
129
+ readonly description: "Only show script names";
130
+ };
131
+ readonly json: {
132
+ readonly flags: "--json";
133
+ readonly description: "Output as JSON";
134
+ };
135
+ readonly pretty: {
136
+ readonly flags: "--pretty";
137
+ readonly description: "Pretty print JSON";
138
+ };
139
+ };
140
+ } | {
141
+ readonly command: "workspace-info <workspace>";
142
+ readonly aliases: ["info"];
143
+ readonly description: "Show information about a workspace";
144
+ readonly options: {
145
+ readonly json: {
146
+ readonly flags: "--json";
147
+ readonly description: "Output as JSON";
148
+ };
149
+ readonly pretty: {
150
+ readonly flags: "--pretty";
151
+ readonly description: "Pretty print JSON";
152
+ };
153
+ };
154
+ } | {
155
+ readonly command: "script-info <script>";
156
+ readonly aliases: [];
157
+ readonly description: "Show information about a script";
158
+ readonly options: {
159
+ readonly workspacesOnly: {
160
+ readonly flags: "--workspaces-only";
161
+ readonly description: "Only show script's workspace names";
162
+ };
163
+ readonly json: {
164
+ readonly flags: "--json";
165
+ readonly description: "Output as JSON";
166
+ };
167
+ readonly pretty: {
168
+ readonly flags: "--pretty";
169
+ readonly description: "Pretty print JSON";
170
+ };
171
+ };
172
+ } | {
173
+ readonly command: "run <script> [workspaces...]";
174
+ readonly aliases: [];
175
+ readonly description: "Run a script in all workspaces that have it in their \"scripts\" field in package.json";
176
+ readonly options: {
177
+ readonly parallel: {
178
+ readonly flags: "--parallel";
179
+ readonly description: "Run the scripts in parallel";
180
+ };
181
+ readonly args: {
182
+ readonly flags: "--args <args>";
183
+ readonly description: "Args to append to the script command";
184
+ };
185
+ readonly noPrefix: {
186
+ readonly flags: "--noPrefix";
187
+ readonly description: "Do not prefix the workspace name to the script output";
188
+ };
189
+ };
190
+ };
191
+ export {};
@@ -0,0 +1,100 @@
1
+ const PROJECT_COMMANDS_CONFIG = {
2
+ listWorkspaces: {
3
+ command: "list-workspaces [pattern]",
4
+ aliases: [
5
+ "ls",
6
+ "list"
7
+ ],
8
+ description: "List all workspaces",
9
+ options: {
10
+ nameOnly: {
11
+ flags: "--name-only",
12
+ description: "Only show workspace names"
13
+ },
14
+ json: {
15
+ flags: "--json",
16
+ description: "Output as JSON"
17
+ },
18
+ pretty: {
19
+ flags: "--pretty",
20
+ description: "Pretty print JSON"
21
+ }
22
+ }
23
+ },
24
+ listScripts: {
25
+ command: "list-scripts",
26
+ aliases: [],
27
+ description: "List all scripts available with their workspaces",
28
+ options: {
29
+ nameOnly: {
30
+ flags: "--name-only",
31
+ description: "Only show script names"
32
+ },
33
+ json: {
34
+ flags: "--json",
35
+ description: "Output as JSON"
36
+ },
37
+ pretty: {
38
+ flags: "--pretty",
39
+ description: "Pretty print JSON"
40
+ }
41
+ }
42
+ },
43
+ workspaceInfo: {
44
+ command: "workspace-info <workspace>",
45
+ aliases: [
46
+ "info"
47
+ ],
48
+ description: "Show information about a workspace",
49
+ options: {
50
+ json: {
51
+ flags: "--json",
52
+ description: "Output as JSON"
53
+ },
54
+ pretty: {
55
+ flags: "--pretty",
56
+ description: "Pretty print JSON"
57
+ }
58
+ }
59
+ },
60
+ scriptInfo: {
61
+ command: "script-info <script>",
62
+ aliases: [],
63
+ description: "Show information about a script",
64
+ options: {
65
+ workspacesOnly: {
66
+ flags: "--workspaces-only",
67
+ description: "Only show script's workspace names"
68
+ },
69
+ json: {
70
+ flags: "--json",
71
+ description: "Output as JSON"
72
+ },
73
+ pretty: {
74
+ flags: "--pretty",
75
+ description: "Pretty print JSON"
76
+ }
77
+ }
78
+ },
79
+ runScript: {
80
+ command: "run <script> [workspaces...]",
81
+ aliases: [],
82
+ description: 'Run a script in all workspaces that have it in their "scripts" field in package.json',
83
+ options: {
84
+ parallel: {
85
+ flags: "--parallel",
86
+ description: "Run the scripts in parallel"
87
+ },
88
+ args: {
89
+ flags: "--args <args>",
90
+ description: "Args to append to the script command"
91
+ },
92
+ noPrefix: {
93
+ flags: "--noPrefix",
94
+ description: "Do not prefix the workspace name to the script output"
95
+ }
96
+ }
97
+ }
98
+ };
99
+ const getProjectCommandConfig = (commandName)=>PROJECT_COMMANDS_CONFIG[commandName];
100
+ export { getProjectCommandConfig };
@@ -1,3 +1,3 @@
1
1
  import { type BunWorkspacesConfig } from "./bunWorkspacesConfig";
2
2
  export declare const DEFAULT_CONFIG_FILE_PATH = "bw.json";
3
- export declare const loadConfigFile: (filePath?: string | undefined, rootDir?: string) => BunWorkspacesConfig | null;
3
+ export declare const loadConfigFile: (filePath?: string, rootDir?: string) => BunWorkspacesConfig | null;
@@ -1,14 +1,14 @@
1
1
  export declare const LIBRARY_CONSUMER_BUN_VERSION: string;
2
2
  export declare const BUILD_BUN_VERSION: string;
3
- export declare const getRequiredBunVersion: (build?: boolean | undefined) => string;
3
+ export declare const getRequiredBunVersion: (build?: boolean) => string;
4
4
  /**
5
5
  * Validates that the provided version satisfies the required Bun version
6
6
  * specified in the root `package.json`.
7
7
  */
8
- export declare const validateBunVersion: (version: string, build?: boolean | undefined) => boolean;
8
+ export declare const validateBunVersion: (version: string, build?: boolean) => boolean;
9
9
  /**
10
10
  *
11
11
  * Validates that the Bun version of the current script satisfies the
12
12
  * required Bun version specified in the root `package.json`.
13
13
  */
14
- export declare const validateCurrentBunVersion: (build?: boolean | undefined) => boolean;
14
+ export declare const validateCurrentBunVersion: (build?: boolean) => boolean;
@@ -1,9 +1,12 @@
1
+ const _process = "undefined" == typeof process ? {
2
+ env: {}
3
+ } : process;
1
4
  const RUNTIME_MODE_VALUES = [
2
5
  "development",
3
6
  "production",
4
7
  "test"
5
8
  ];
6
- const _RUNTIME_MODE = process.env._BW_RUNTIME_MODE || (process.env.NODE_ENV?.match(/test(ing)?/) ? "test" : "development" === process.env.NODE_ENV ? "development" : "production");
9
+ const _RUNTIME_MODE = _process.env._BW_RUNTIME_MODE || (process.env.NODE_ENV?.match(/test(ing)?/) ? "test" : "development" === process.env.NODE_ENV ? "development" : "production");
7
10
  const RUNTIME_MODE = RUNTIME_MODE_VALUES.includes(_RUNTIME_MODE) ? _RUNTIME_MODE : "production";
8
11
  if (RUNTIME_MODE !== _RUNTIME_MODE) console.error(`Env var RUNTIME_MODE has an invalid value: "${_RUNTIME_MODE}". Defaulting to "${RUNTIME_MODE}". Accepted values: ${RUNTIME_MODE_VALUES.join(", ")}.`);
9
12
  const IS_TEST = "test" === RUNTIME_MODE;
@@ -1 +1 @@
1
- export declare const ERRORS: import("../internal/error").DefinedErrors<"AliasConflict" | "AliasedWorkspaceNotFound" | "DuplicateWorkspaceName" | "InvalidPackageJson" | "InvalidScripts" | "InvalidWorkspaceName" | "InvalidWorkspacePattern" | "InvalidWorkspaces" | "NoWorkspaceName" | "PackageNotFound">;
1
+ export declare const ERRORS: import("../internal/error").DefinedErrors<"PackageNotFound" | "InvalidPackageJson" | "DuplicateWorkspaceName" | "InvalidWorkspaceName" | "NoWorkspaceName" | "InvalidScripts" | "InvalidWorkspaces" | "InvalidWorkspacePattern" | "AliasConflict" | "AliasedWorkspaceNotFound">;
@@ -8,10 +8,10 @@ export interface FindWorkspacesOptions {
8
8
  export declare const findWorkspaces: ({ rootDir, workspaceGlobs, workspaceAliases, }: FindWorkspacesOptions) => {
9
9
  workspaces: Workspace[];
10
10
  };
11
- export declare const validateWorkspaceAliases: (workspaces: Workspace[], workspaceAliases: Record<string, string> | undefined) => void;
11
+ export declare const validateWorkspaceAliases: (workspaces: Workspace[], workspaceAliases: ProjectConfig["workspaceAliases"]) => void;
12
12
  export declare const findWorkspacesFromPackage: ({ rootDir, workspaceAliases, }: ProjectConfig & {
13
13
  rootDir: string;
14
14
  }) => {
15
- workspaces: Workspace[];
16
15
  name: string;
16
+ workspaces: Workspace[];
17
17
  };
@@ -5,4 +5,4 @@ export type ResolvedPackageJsonContent = {
5
5
  scripts: Record<string, string>;
6
6
  } & Record<string, unknown>;
7
7
  export declare const scanWorkspaceGlob: (pattern: string, rootDir: string) => Generator<string, void, void>;
8
- export declare const resolvePackageJsonContent: (packageJsonPath: string, rootDir: string, validations: ("name" | "scripts" | "workspaces")[]) => ResolvedPackageJsonContent;
8
+ export declare const resolvePackageJsonContent: (packageJsonPath: string, rootDir: string, validations: ("workspaces" | "name" | "scripts")[]) => ResolvedPackageJsonContent;
@@ -1,11 +0,0 @@
1
- import { type Command } from "commander";
2
- import { type LogLevelSetting } from "../internal/logger";
3
- export interface CliGlobalOptions {
4
- logLevel: LogLevelSetting;
5
- cwd: string;
6
- configFile?: string;
7
- }
8
- export type CliGlobalOptionName = keyof CliGlobalOptions;
9
- export declare const initializeWithGlobalOptions: (program: Command, args: string[], defaultCwd: string) => {
10
- project: import("..").Project;
11
- };
@@ -1,8 +0,0 @@
1
- import { type Command } from "commander";
2
- import type { Project } from "../project";
3
- export interface ProjectCommandsContext {
4
- project: Project;
5
- program: Command;
6
- }
7
- export declare const commandOutputLogger: import("../internal/logger").Logger;
8
- export declare const defineProjectCommands: (context: ProjectCommandsContext) => void;
@@ -1,179 +0,0 @@
1
- import { BunWorkspacesError } from "../internal/error.mjs";
2
- import { createLogger, logger } from "../internal/logger.mjs";
3
- const createWorkspaceInfoLines = (workspace)=>[
4
- `Workspace: ${workspace.name}`,
5
- ` - Aliases: ${workspace.aliases.join(", ")}`,
6
- ` - Path: ${workspace.path}`,
7
- ` - Glob Match: ${workspace.matchPattern}`,
8
- ` - Scripts: ${Object.keys(workspace.packageJson.scripts).sort().join(", ")}`
9
- ];
10
- const createScriptInfoLines = (script, workspaces)=>[
11
- `Script: ${script}`,
12
- ...workspaces.map((workspace)=>` - ${workspace.name}`)
13
- ];
14
- const createJsonLines = (data, options)=>JSON.stringify(data, null, options.pretty ? 2 : void 0).split("\n");
15
- const commandOutputLogger = createLogger("");
16
- commandOutputLogger.printLevel = "info";
17
- const listWorkspaces = ({ program, project })=>{
18
- program.command("list-workspaces [pattern]").aliases([
19
- "ls",
20
- "list"
21
- ]).description("List all workspaces").option("--name-only", "Only show workspace names").option("--json", "Output as JSON").option("--pretty", "Pretty print JSON").action((pattern, options)=>{
22
- logger.debug(`Command: List workspaces (options: ${JSON.stringify(options)})`);
23
- const lines = [];
24
- const workspaces = pattern ? project.findWorkspacesByPattern(pattern) : project.workspaces;
25
- if (options.json) lines.push(...createJsonLines(options.nameOnly ? workspaces.map(({ name })=>name) : workspaces, options));
26
- else workspaces.forEach((workspace)=>{
27
- if (options.nameOnly) lines.push(workspace.name);
28
- else lines.push(...createWorkspaceInfoLines(workspace));
29
- });
30
- if (!lines.length) logger.info("No workspaces found");
31
- if (lines.length) commandOutputLogger.info(lines.join("\n"));
32
- });
33
- };
34
- const listScripts = ({ program, project })=>{
35
- program.command("list-scripts").description("List all scripts available with their workspaces").option("--name-only", "Only show script names").option("--json", "Output as JSON").option("--pretty", "Pretty print JSON").action((options)=>{
36
- logger.debug(`Command: List scripts (options: ${JSON.stringify(options)})`);
37
- const scripts = project.listScriptsWithWorkspaces();
38
- const lines = [];
39
- if (options.json) lines.push(...createJsonLines(options.nameOnly ? Object.keys(scripts) : Object.values(scripts).map(({ workspaces, ...rest })=>({
40
- ...rest,
41
- workspaces: workspaces.map(({ name })=>name)
42
- })), options));
43
- else {
44
- Object.values(scripts).sort(({ name: nameA }, { name: nameB })=>nameA.localeCompare(nameB)).forEach(({ name, workspaces })=>{
45
- if (options.nameOnly) lines.push(name);
46
- else lines.push(...createScriptInfoLines(name, workspaces));
47
- });
48
- if (!lines.length) logger.info("No scripts found");
49
- }
50
- if (lines.length) commandOutputLogger.info(lines.join("\n"));
51
- });
52
- };
53
- const workspaceInfo = ({ program, project })=>{
54
- program.command("workspace-info <workspace>").aliases([
55
- "info"
56
- ]).description("Show information about a workspace").option("--json", "Output as JSON").option("--pretty", "Pretty print JSON").action((workspaceName, options)=>{
57
- logger.debug(`Command: Workspace info for ${workspaceName} (options: ${JSON.stringify(options)})`);
58
- const workspace = project.findWorkspaceByNameOrAlias(workspaceName);
59
- if (!workspace) return void logger.error(`Workspace not found: (options: ${JSON.stringify(workspaceName)})`);
60
- commandOutputLogger.info((options.json ? createJsonLines(workspace, options) : createWorkspaceInfoLines(workspace)).join("\n"));
61
- });
62
- };
63
- const scriptInfo = ({ program, project })=>{
64
- program.command("script-info <script>").description("Show information about a script").option("--workspaces-only", "Only show script's workspace names").option("--json", "Output as JSON").option("--pretty", "Pretty print JSON").action((script, options)=>{
65
- logger.debug(`Command: Script info for ${script} (options: ${JSON.stringify(options)})`);
66
- const scripts = project.listScriptsWithWorkspaces();
67
- const scriptMetadata = scripts[script];
68
- if (!scriptMetadata) return void logger.error(`Script not found: ${JSON.stringify(script)} (available: ${Object.keys(scripts).join(", ")})`);
69
- commandOutputLogger.info((options.json ? createJsonLines(options.workspacesOnly ? scriptMetadata.workspaces.map(({ name })=>name) : {
70
- name: scriptMetadata.name,
71
- workspaces: scriptMetadata.workspaces.map(({ name })=>name)
72
- }, options) : options.workspacesOnly ? scriptMetadata.workspaces.map(({ name })=>name) : createScriptInfoLines(script, scriptMetadata.workspaces)).join("\n"));
73
- });
74
- };
75
- const runScript = ({ program, project })=>{
76
- program.command("run <script> [workspaces...]").description("Run a script in all workspaces").option("--parallel", "Run the scripts in parallel").option("--args <args>", "Args to append to the script command", "").option("--noPrefix", "Do not prefix the workspace name to the script output", "").action(async (script, _workspaces, options)=>{
77
- logger.debug(`Command: Run script ${JSON.stringify(script)} for ${_workspaces.length ? "workspaces " + _workspaces.join(", ") : "all workspaces"} (parallel: ${!!options.parallel}, method: ${JSON.stringify(options.method)}, args: ${JSON.stringify(options.args)})`);
78
- const workspaces = _workspaces.length ? _workspaces.flatMap((workspacePattern)=>{
79
- if (workspacePattern.includes("*")) return project.findWorkspacesByPattern(workspacePattern).filter(({ packageJson: { scripts } })=>scripts?.[script]).map(({ name })=>name);
80
- return [
81
- workspacePattern
82
- ];
83
- }) : project.listWorkspacesWithScript(script).map(({ name })=>name);
84
- if (!workspaces.length) program.error(`No ${_workspaces.length ? "matching " : ""}workspaces found for script ${JSON.stringify(script)}`);
85
- let scriptCommands;
86
- try {
87
- scriptCommands = workspaces.map((workspaceName)=>project.createScriptCommand({
88
- scriptName: script,
89
- workspaceName,
90
- method: "cd",
91
- args: options.args.replace(/<workspace>/g, workspaceName)
92
- }));
93
- } catch (error) {
94
- program.error(error.message);
95
- throw error;
96
- }
97
- const runCommand = async ({ command, scriptName, workspace })=>{
98
- const commandLogger = createLogger(`${workspace.name}:${scriptName}`);
99
- const splitCommand = command.command.split(/\s+/g);
100
- commandLogger.debug(`Running script ${scriptName} in workspace ${workspace.name} (cwd: ${command.cwd}): ${splitCommand.join(" ")}`);
101
- const isSilent = "silent" === logger.printLevel;
102
- const proc = Bun.spawn(command.command.split(/\s+/g), {
103
- cwd: command.cwd,
104
- env: {
105
- ...process.env,
106
- FORCE_COLOR: "1"
107
- },
108
- stdout: isSilent ? "ignore" : "pipe",
109
- stderr: isSilent ? "ignore" : "pipe"
110
- });
111
- const linePrefix = options.noPrefix ? "" : `[${workspace.name}:${scriptName}] `;
112
- if (proc.stdout) for await (const chunk of proc.stdout)commandLogger.logOutput(chunk, "info", process.stdout, linePrefix);
113
- if (proc.stderr) for await (const chunk of proc.stderr)commandLogger.logOutput(chunk, "error", process.stderr, linePrefix);
114
- await proc.exited;
115
- return {
116
- scriptName,
117
- workspace,
118
- command,
119
- success: 0 === proc.exitCode,
120
- error: 0 === proc.exitCode ? null : new BunWorkspacesError(`Script exited with code ${proc.exitCode}`)
121
- };
122
- };
123
- const results = [];
124
- if (options.parallel) {
125
- let i = 0;
126
- for await (const result of (await Promise.allSettled(scriptCommands.map(runCommand)))){
127
- if ("rejected" === result.status) results.push({
128
- success: false,
129
- workspaceName: workspaces[i],
130
- error: result.reason
131
- });
132
- else results.push({
133
- success: result.value.success,
134
- workspaceName: workspaces[i],
135
- error: result.value.error
136
- });
137
- i++;
138
- }
139
- } else {
140
- let i = 0;
141
- for (const command of scriptCommands){
142
- try {
143
- const result = await runCommand(command);
144
- results.push({
145
- success: result.success,
146
- workspaceName: workspaces[i],
147
- error: result.error
148
- });
149
- } catch (error) {
150
- results.push({
151
- success: false,
152
- workspaceName: workspaces[i],
153
- error: error
154
- });
155
- }
156
- i++;
157
- }
158
- }
159
- let failCount = 0;
160
- results.forEach(({ success, workspaceName })=>{
161
- if (!success) failCount++;
162
- logger.info(`${success ? "✅" : "❌"} ${workspaceName}: ${script}`);
163
- });
164
- const s = 1 === results.length ? "" : "s";
165
- if (failCount) {
166
- const message = `${failCount} of ${results.length} script${s} failed`;
167
- logger.error(message);
168
- process.exit(1);
169
- } else logger.info(`${results.length} script${s} ran successfully`);
170
- });
171
- };
172
- const defineProjectCommands = (context)=>{
173
- listWorkspaces(context);
174
- listScripts(context);
175
- workspaceInfo(context);
176
- scriptInfo(context);
177
- runScript(context);
178
- };
179
- export { commandOutputLogger, defineProjectCommands };