bun-workspaces 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-workspaces",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A monorepo management tool for Bun, with a CLI and API to enhance Bun's native workspaces.",
5
5
  "license": "MIT",
6
6
  "main": "src/index.mjs",
@@ -1,15 +1,19 @@
1
1
  import { type Command } from "commander";
2
2
  import type { FileSystemProject } from "../../project/implementations/fileSystemProject";
3
3
  import type { Workspace } from "../../workspaces";
4
+ import type { WriteOutputOptions } from "../createCli";
5
+ import type { CliMiddleware } from "../middleware";
4
6
  import {
5
7
  type CliGlobalCommandName,
6
8
  type CliProjectCommandName,
7
9
  } from "./commandsConfig";
8
10
  /** @todo DRY use of output text in cases such as having no workspaces/scripts */
9
- export interface GlobalCommandContext {
11
+ export type GlobalCommandContext = {
10
12
  program: Command;
11
13
  postTerminatorArgs: string[];
12
- }
14
+ middleware: CliMiddleware;
15
+ outputWriters: Required<WriteOutputOptions>;
16
+ };
13
17
  export type ProjectCommandContext = GlobalCommandContext & {
14
18
  project: FileSystemProject;
15
19
  projectError: Error | null;
@@ -47,8 +47,17 @@ const handleCommand = (commandName, handler) => (context) => {
47
47
  program = program.action(async (...actionArgs) => {
48
48
  try {
49
49
  logger.debug(`Handling command: ${commandName}`);
50
+ const middlewareContext = {
51
+ commanderProgram: program,
52
+ commandName,
53
+ commandContext: context,
54
+ commanderActionArgs: actionArgs,
55
+ };
56
+ program = context.middleware.preHandleCommand(middlewareContext);
50
57
  await handler(context, ...actionArgs);
58
+ program = context.middleware.postHandleCommand(middlewareContext);
51
59
  } catch (error) {
60
+ context.middleware.catchError(error);
52
61
  if (error instanceof BunWorkspacesError) {
53
62
  logger.error(error.message);
54
63
  process.exit(1);
@@ -64,6 +73,7 @@ const handleProjectCommand = (commandName, handler) => (context) =>
64
73
  handleCommand(commandName, async (context, ...actionArgs) => {
65
74
  const { projectError } = context;
66
75
  if (projectError) {
76
+ context.middleware.catchError(projectError);
67
77
  logger.error(projectError.message);
68
78
  process.exit(1);
69
79
  }
@@ -28,7 +28,7 @@ import { renderPlainOutput } from "./output/renderPlainOutput.mjs"; // CONCATENA
28
28
  const runScript = handleProjectCommand(
29
29
  "runScript",
30
30
  async (
31
- { project, postTerminatorArgs },
31
+ { project, postTerminatorArgs, outputWriters },
32
32
  positionalScript,
33
33
  positionalWorkspacePatterns,
34
34
  options,
@@ -146,14 +146,15 @@ const runScript = handleProjectCommand(
146
146
  summary,
147
147
  scriptEventTarget,
148
148
  groupedLines,
149
+ outputWriters,
149
150
  ),
150
151
  prefixed: () =>
151
- renderPlainOutput(output, {
152
+ renderPlainOutput(output, outputWriters, {
152
153
  prefix: true,
153
154
  stripDisruptiveControls,
154
155
  }),
155
156
  plain: () =>
156
- renderPlainOutput(output, {
157
+ renderPlainOutput(output, outputWriters, {
157
158
  prefix: false,
158
159
  stripDisruptiveControls,
159
160
  }),
@@ -241,8 +242,6 @@ const runScript = handleProjectCommand(
241
242
  }
242
243
  if (exitResults.failureCount) {
243
244
  process.exit(1);
244
- } else {
245
- process.exit(0);
246
245
  }
247
246
  },
248
247
  );
@@ -12,6 +12,7 @@ import type {
12
12
  ScriptEventName,
13
13
  } from "../../../../runScript";
14
14
  import type { Workspace } from "../../../../workspaces";
15
+ import type { WriteOutputOptions } from "../../../createCli";
15
16
  type ScriptEvent = TypedEvent<
16
17
  ScriptEventName,
17
18
  {
@@ -67,5 +68,6 @@ export declare const renderGroupedOutput: (
67
68
  summary: Promise<RunScriptsSummary<RunWorkspaceScriptMetadata>>,
68
69
  scriptEventTarget: ScriptEventTarget,
69
70
  activeScriptLines: number | "all",
71
+ outputWriters: Required<WriteOutputOptions>,
70
72
  ) => Promise<void>;
71
73
  export {};
@@ -70,6 +70,7 @@ const renderGroupedOutput = async (
70
70
  summary,
71
71
  scriptEventTarget,
72
72
  activeScriptLines,
73
+ outputWriters,
73
74
  ) => {
74
75
  const workspaceState = workspaces.reduce((acc, workspace) => {
75
76
  acc[workspace.name] = {
@@ -87,7 +88,7 @@ const renderGroupedOutput = async (
87
88
  }
88
89
  isInitialized = true;
89
90
  logger.debug("Initializing TUI state");
90
- process.stdout.write(cursorOps.hide());
91
+ outputWriters.stdout(cursorOps.hide());
91
92
  process.stdin.setRawMode?.(true);
92
93
  };
93
94
  let isReset = false;
@@ -97,7 +98,7 @@ const renderGroupedOutput = async (
97
98
  }
98
99
  isReset = true;
99
100
  logger.debug("Resetting TUI state");
100
- process.stdout.write(cursorOps.show());
101
+ outputWriters.stdout(cursorOps.show());
101
102
  process.stdin.unref();
102
103
  process.stdin.setRawMode?.(false);
103
104
  };
@@ -192,24 +193,24 @@ const renderGroupedOutput = async (
192
193
  });
193
194
  if (previousHeight > 0) {
194
195
  // clear previous frame
195
- process.stdout.write(cursorOps.up(previousHeight));
196
+ outputWriters.stdout(cursorOps.up(previousHeight));
196
197
  for (let i = 0; i < previousHeight; i++) {
197
- process.stdout.write(cursorOps.toColumn(1));
198
- process.stdout.write(lineOps.clearFull());
199
- process.stdout.write("\n");
198
+ outputWriters.stdout(cursorOps.toColumn(1));
199
+ outputWriters.stdout(lineOps.clearFull());
200
+ outputWriters.stdout("\n");
200
201
  }
201
- process.stdout.write(cursorOps.up(previousHeight));
202
+ outputWriters.stdout(cursorOps.up(previousHeight));
202
203
  }
203
204
  for (const line of linesToWrite) {
204
205
  if (isFinal && line.type === "scriptOutput") {
205
- process.stdout.write(line.text.replace(/\n?$/, "\n"));
206
+ outputWriters.stdout(line.text.replace(/\n?$/, "\n"));
206
207
  } else {
207
208
  const visibleLength = calculateVisibleLength(line.text);
208
209
  const truncated =
209
210
  visibleLength > width
210
211
  ? truncateTerminalString(line.text, width - 2) + "\x1b[0m…"
211
212
  : line.text;
212
- process.stdout.write(truncated.replace(/\n?$/, "\n"));
213
+ outputWriters.stdout(truncated.replace(/\n?$/, "\n"));
213
214
  }
214
215
  }
215
216
  previousHeight = linesToWrite.length;
@@ -261,7 +262,7 @@ const renderGroupedOutput = async (
261
262
  runOnExit((reason) => {
262
263
  try {
263
264
  if (typeof reason === "string" && reason.startsWith("SIG")) {
264
- process.stdout.write("\r" + lineOps.clearFull());
265
+ outputWriters.stdout("\r" + lineOps.clearFull());
265
266
  }
266
267
  Object.keys(workspaceState).forEach((workspaceName) => {
267
268
  handleExitResult({
@@ -1,4 +1,5 @@
1
1
  import type { RunScriptAcrossWorkspacesOutput } from "../../../../project";
2
+ import type { WriteOutputOptions } from "../../../createCli";
2
3
  export type RenderPlainOutputOptions = {
3
4
  stripDisruptiveControls?: boolean;
4
5
  prefix?: boolean;
@@ -18,5 +19,6 @@ export declare function generatePlainOutputLines(
18
19
  >;
19
20
  export declare const renderPlainOutput: (
20
21
  output: RunScriptAcrossWorkspacesOutput,
22
+ outputWriters: Required<WriteOutputOptions>,
21
23
  { stripDisruptiveControls, prefix }: RenderPlainOutputOptions,
22
24
  ) => Promise<void>;
@@ -30,13 +30,14 @@ async function* generatePlainOutputLines(
30
30
  }
31
31
  const renderPlainOutput = async (
32
32
  output,
33
+ outputWriters,
33
34
  { stripDisruptiveControls = true, prefix = false },
34
35
  ) => {
35
36
  for await (const { line, metadata } of generatePlainOutputLines(output, {
36
37
  stripDisruptiveControls,
37
38
  prefix,
38
39
  })) {
39
- process[metadata.streamName].write(line + "\n");
40
+ outputWriters[metadata.streamName](line + "\n");
40
41
  }
41
42
  };
42
43
 
@@ -1,19 +1,25 @@
1
- import { type Command } from "commander";
1
+ import { type CliMiddlewareOptions } from "./middleware";
2
+ export interface WriteOutputOptions {
3
+ stdout?: (...args: Parameters<typeof process.stdout.write>) => void;
4
+ stderr?: (...args: Parameters<typeof process.stderr.write>) => void;
5
+ }
2
6
  export interface RunCliOptions {
3
- argv?: string | string[];
7
+ argv?: string[];
4
8
  /** Should be `true` if args do not include the binary name (e.g. `bunx bun-workspaces`) */
5
9
  programmatic?: true;
10
+ middleware?: CliMiddlewareOptions;
11
+ writeOutput?: WriteOutputOptions;
12
+ terminalWidth?: number;
6
13
  }
7
14
  export interface CLI {
8
15
  run: (options?: RunCliOptions) => Promise<void>;
9
16
  }
10
17
  export interface CreateCliOptions {
11
- handleError?: (error: Error) => void;
12
- postInit?: (program: Command) => unknown;
13
18
  defaultCwd?: string;
19
+ /** Always handled when the result `.run()` is called */
20
+ defaultMiddleware?: CliMiddlewareOptions;
14
21
  }
15
22
  export declare const createCli: ({
16
- handleError,
17
- postInit,
18
23
  defaultCwd,
24
+ defaultMiddleware,
19
25
  }?: CreateCliOptions) => CLI;
@@ -7,49 +7,84 @@ import {
7
7
  defineGlobalCommands,
8
8
  defineProjectCommands,
9
9
  } from "./commands/index.mjs";
10
+ import { commandOutputLogger } from "./commands/commandHandlerUtils.mjs";
10
11
  import { fatalErrorLogger } from "./fatalErrorLogger.mjs";
11
- import { initializeWithGlobalOptions } from "./globalOptions/index.mjs"; // CONCATENATED MODULE: external "commander"
12
+ import { initializeWithGlobalOptions } from "./globalOptions/index.mjs";
13
+ import { resolveMiddleware } from "./middleware.mjs"; // CONCATENATED MODULE: external "commander"
12
14
  // CONCATENATED MODULE: external "../../package.json"
13
15
  // CONCATENATED MODULE: external "../internal/bun/index.mjs"
14
16
  // CONCATENATED MODULE: external "../internal/core/index.mjs"
15
17
  // CONCATENATED MODULE: external "../internal/logger/index.mjs"
16
18
  // CONCATENATED MODULE: external "./commands/index.mjs"
19
+ // CONCATENATED MODULE: external "./commands/commandHandlerUtils.mjs"
17
20
  // CONCATENATED MODULE: external "./fatalErrorLogger.mjs"
18
21
  // CONCATENATED MODULE: external "./globalOptions/index.mjs"
22
+ // CONCATENATED MODULE: external "./middleware.mjs"
19
23
  // CONCATENATED MODULE: ./src/cli/createCli.ts
20
24
 
21
- const createCli = ({
22
- handleError,
23
- postInit,
24
- defaultCwd = process.cwd(),
25
- } = {}) => {
25
+ const createCli = ({ defaultCwd = process.cwd(), defaultMiddleware } = {}) => {
26
26
  logger.debug(`Creating CLI with default cwd: ${defaultCwd}`);
27
- const run = async ({ argv = process.argv, programmatic } = {}) => {
28
- const errorListener =
29
- handleError ??
30
- ((error) => {
31
- fatalErrorLogger.error(error);
32
- process.exit(1);
33
- });
27
+ const run = async ({
28
+ argv = process.argv,
29
+ programmatic,
30
+ middleware: _runMiddleware,
31
+ writeOutput,
32
+ terminalWidth,
33
+ } = {}) => {
34
+ const middleware = resolveMiddleware(
35
+ defaultMiddleware ?? {},
36
+ _runMiddleware ?? {},
37
+ );
38
+ const outputWriters = {
39
+ stdout: (...args) => process.stdout.write(...args),
40
+ stderr: (...args) => process.stderr.write(...args),
41
+ ...writeOutput,
42
+ };
43
+ logger.setPrintStdout(outputWriters.stdout);
44
+ logger.setPrintStderr(outputWriters.stderr);
45
+ commandOutputLogger.setPrintStdout(outputWriters.stdout);
46
+ commandOutputLogger.setPrintStderr(outputWriters.stderr);
47
+ const errorListener = (error) => {
48
+ middleware.catchError(error);
49
+ fatalErrorLogger.error(error);
50
+ process.exit(1);
51
+ };
34
52
  process.on("unhandledRejection", errorListener);
35
53
  try {
36
54
  const program = createCommand("bun-workspaces")
37
55
  .description("A CLI on top of native Bun workspaces")
38
56
  .version(package_0.version)
39
- .showHelpAfterError(true);
40
- postInit?.(program);
41
- const rawArgs = typeof argv === "string" ? argv.split(/s+/) : argv;
57
+ .showHelpAfterError(true)
58
+ .configureOutput({
59
+ writeOut: outputWriters.stdout,
60
+ writeErr: outputWriters.stderr,
61
+ ...(terminalWidth
62
+ ? {
63
+ getOutHelpWidth: () => terminalWidth,
64
+ getErrHelpWidth: () => terminalWidth,
65
+ }
66
+ : {}),
67
+ });
68
+ const defaultContext = {
69
+ commanderProgram: program,
70
+ };
71
+ middleware.initProgram({
72
+ ...defaultContext,
73
+ argv,
74
+ });
42
75
  const { args, postTerminatorArgs } = (() => {
43
- const terminatorIndex = rawArgs.findIndex((arg) => arg === "--");
76
+ const terminatorIndex = argv.findIndex((arg) => arg === "--");
44
77
  return {
45
- args:
46
- terminatorIndex !== -1
47
- ? rawArgs.slice(0, terminatorIndex)
48
- : rawArgs,
78
+ args: terminatorIndex !== -1 ? argv.slice(0, terminatorIndex) : argv,
49
79
  postTerminatorArgs:
50
- terminatorIndex !== -1 ? rawArgs.slice(terminatorIndex + 1) : [],
80
+ terminatorIndex !== -1 ? argv.slice(terminatorIndex + 1) : [],
51
81
  };
52
82
  })();
83
+ middleware.processArgv({
84
+ ...defaultContext,
85
+ args,
86
+ postTerminatorArgs,
87
+ });
53
88
  const bunVersionError = validateCurrentBunVersion();
54
89
  if (bunVersionError) {
55
90
  fatalErrorLogger.error(bunVersionError.message);
@@ -60,6 +95,11 @@ const createCli = ({
60
95
  args,
61
96
  defaultCwd,
62
97
  );
98
+ middleware.findProject({
99
+ ...defaultContext,
100
+ project,
101
+ projectError,
102
+ });
63
103
  if (postTerminatorArgs.length) {
64
104
  logger.debug("Has post-terminator args");
65
105
  }
@@ -69,15 +109,31 @@ const createCli = ({
69
109
  project,
70
110
  projectError,
71
111
  postTerminatorArgs,
112
+ middleware,
113
+ outputWriters,
72
114
  });
73
115
  defineGlobalCommands({
74
116
  program,
75
117
  postTerminatorArgs,
118
+ middleware,
119
+ outputWriters,
76
120
  });
77
121
  logger.debug(`Commands initialized. Parsing args...`);
122
+ middleware.preParse({
123
+ ...defaultContext,
124
+ args,
125
+ project,
126
+ projectError,
127
+ });
78
128
  await program.parseAsync(args, {
79
129
  from: programmatic ? "user" : "node",
80
130
  });
131
+ middleware.postParse({
132
+ ...defaultContext,
133
+ args,
134
+ project,
135
+ projectError,
136
+ });
81
137
  } catch (error) {
82
138
  if (error instanceof BunWorkspacesError) {
83
139
  logger.debug(error);
@@ -0,0 +1,72 @@
1
+ import type { Command as CommanderProgram } from "commander";
2
+ import type { FileSystemProject } from "../project";
3
+ import type { CliCommandName, CliGlobalCommandName } from "./commands";
4
+ import type {
5
+ GlobalCommandContext,
6
+ ProjectCommandContext,
7
+ } from "./commands/commandHandlerUtils";
8
+ export type InitProgramContext = {
9
+ commanderProgram: CommanderProgram;
10
+ argv: string[];
11
+ };
12
+ export type ProcessArgvContext = {
13
+ args: string[];
14
+ postTerminatorArgs: string[];
15
+ };
16
+ export type FindProjectContext = {
17
+ commanderProgram: CommanderProgram;
18
+ project: FileSystemProject;
19
+ projectError: Error | null;
20
+ };
21
+ export type PreParseContext = {
22
+ commanderProgram: CommanderProgram;
23
+ args: string[];
24
+ project: FileSystemProject;
25
+ projectError: Error | null;
26
+ };
27
+ export type PostParseContext = {
28
+ commanderProgram: CommanderProgram;
29
+ args: string[];
30
+ project: FileSystemProject;
31
+ projectError: Error | null;
32
+ };
33
+ export type CommandMiddlewareContext<C extends CliCommandName> = {
34
+ commanderProgram: CommanderProgram;
35
+ commandName: C;
36
+ commandContext: C extends CliGlobalCommandName
37
+ ? GlobalCommandContext
38
+ : ProjectCommandContext;
39
+ commanderActionArgs: unknown[];
40
+ };
41
+ export type CommandMiddleware = <C extends CliCommandName>(
42
+ context: CommandMiddlewareContext<C>,
43
+ ) => CommanderProgram;
44
+ export type PostCleanupContext = {
45
+ commanderProgram: CommanderProgram;
46
+ args: string[];
47
+ project: FileSystemProject;
48
+ projectError: Error | null;
49
+ };
50
+ export type CliMiddleware = {
51
+ /** The first callback when the Commander program is created */
52
+ initProgram: (context: InitProgramContext) => CommanderProgram;
53
+ /** Before the true parsing, just splitting the argv into args and post-terminator args */
54
+ processArgv: (context: ProcessArgvContext) => CommanderProgram;
55
+ /** After the project has been initialized from global options */
56
+ findProject: (context: FindProjectContext) => CommanderProgram;
57
+ /** Before the Commander program parses the args */
58
+ preParse: (context: PreParseContext) => CommanderProgram;
59
+ /** After the Commander program has parsed the args (runs in finally block) */
60
+ postParse: (context: PostParseContext) => CommanderProgram;
61
+ /** Before a command is handled */
62
+ preHandleCommand: CommandMiddleware;
63
+ /** After a command is handled */
64
+ postHandleCommand: CommandMiddleware;
65
+ /** After the program has been parsed */
66
+ catchError: (error: Error) => unknown;
67
+ };
68
+ export type CliMiddlewareOptions = Partial<CliMiddleware>;
69
+ export declare const resolveMiddleware: (
70
+ defaultMiddleware: CliMiddlewareOptions,
71
+ runMiddleware: CliMiddlewareOptions,
72
+ ) => CliMiddleware;
@@ -0,0 +1,38 @@
1
+ import { defineErrors } from "../internal/core/index.mjs";
2
+ import { logger } from "../internal/logger/index.mjs"; // CONCATENATED MODULE: external "../internal/core/index.mjs"
3
+ // CONCATENATED MODULE: external "../internal/logger/index.mjs"
4
+ // CONCATENATED MODULE: ./src/cli/middleware.ts
5
+
6
+ const MIDDLEWARE_ERRORS = defineErrors("MiddlewareHandlerFailed");
7
+ const resolveMiddleware = (defaultMiddleware, runMiddleware) =>
8
+ Object.keys({
9
+ catchError: null,
10
+ initProgram: null,
11
+ processArgv: null,
12
+ findProject: null,
13
+ preParse: null,
14
+ postParse: null,
15
+ preHandleCommand: null,
16
+ postHandleCommand: null,
17
+ }).reduce((acc, _key) => {
18
+ const key = _key;
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ acc[key] = (ctx) => {
21
+ try {
22
+ let result = defaultMiddleware?.[key]?.(ctx);
23
+ result = runMiddleware?.[key]?.(ctx);
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ return result;
26
+ } catch (error) {
27
+ logger.error(
28
+ new MIDDLEWARE_ERRORS.MiddlewareHandlerFailed(
29
+ `Error in middleware handler "${key}"`,
30
+ ),
31
+ );
32
+ throw error;
33
+ }
34
+ };
35
+ return acc;
36
+ }, {});
37
+
38
+ export { resolveMiddleware };
@@ -24,6 +24,12 @@ export type Logger = {
24
24
  metadata?: Metadata,
25
25
  ): Log<Message, Metadata>;
26
26
  printLevel: LogLevelSetting;
27
+ setPrintStdout: (
28
+ stdout: (...args: Parameters<typeof process.stdout.write>) => void,
29
+ ) => void;
30
+ setPrintStderr: (
31
+ stderr: (...args: Parameters<typeof process.stderr.write>) => void,
32
+ ) => void;
27
33
  } & {
28
34
  [Level in LogLevel]: <
29
35
  Message extends string | Error = string,
@@ -17,6 +17,13 @@ const validateLogLevel = (level) => {
17
17
  const createLogger = (name) => new _Logger(name);
18
18
  const YELLOW = "\x1b[0;33m";
19
19
  const NC = "\x1b[0m";
20
+ const RED = "\x1b[0;31m";
21
+ const LEVEL_OUTPUT_TARGETS = {
22
+ debug: "stderr",
23
+ info: "stdout",
24
+ warn: "stderr",
25
+ error: "stderr",
26
+ };
20
27
  class _Logger {
21
28
  name;
22
29
  constructor(name) {
@@ -42,27 +49,25 @@ class _Logger {
42
49
  },
43
50
  ]
44
51
  : [];
45
- if (level === "debug") {
46
- // debug goes to stderr (console.debug goes to stdout which messes with machine-readable output etc.)
47
- process.stderr.write(
48
- (typeof mainMessage === "string"
49
- ? mainMessage
50
- : Bun.inspect(mainMessage, {
52
+ this[
53
+ LEVEL_OUTPUT_TARGETS[level] === "stderr"
54
+ ? "_printStderr"
55
+ : "_printStdout"
56
+ ](
57
+ (typeof mainMessage === "string"
58
+ ? mainMessage
59
+ : Bun.inspect(mainMessage, {
60
+ colors: true,
61
+ })) +
62
+ metadataMessages
63
+ .map((m) =>
64
+ Bun.inspect(m, {
51
65
  colors: true,
52
- })) +
53
- metadataMessages
54
- .map((m) =>
55
- Bun.inspect(m, {
56
- colors: true,
57
- }),
58
- )
59
- .join("\n") +
60
- "\n",
61
- );
62
- } else {
63
- // eslint-disable-next-line no-console
64
- console[level](mainMessage, ...metadataMessages);
65
- }
66
+ }),
67
+ )
68
+ .join("\n") +
69
+ "\n",
70
+ );
66
71
  }
67
72
  return log;
68
73
  }
@@ -92,13 +97,25 @@ class _Logger {
92
97
  level === "debug" || level === "warn"
93
98
  ? `[${this.name} ${level.toUpperCase()}]: ${content}`
94
99
  : content;
95
- return level === "warn" ? `${YELLOW}${prefixed}${NC}` : prefixed;
100
+ return level === "warn"
101
+ ? `${YELLOW}${prefixed}${NC}`
102
+ : level === "error"
103
+ ? `${RED}${prefixed}${NC}`
104
+ : prefixed;
96
105
  }
97
106
  _printLevel = IS_TEST ? "error" : "info";
98
107
  shouldPrint(level) {
99
108
  if (this.printLevel === "silent") return false;
100
109
  return getLevelNumber(level) >= getLevelNumber(this.printLevel);
101
110
  }
111
+ _printStdout = (...args) => process.stdout.write(...args);
112
+ _printStderr = (...args) => process.stderr.write(...args);
113
+ setPrintStdout(stdout) {
114
+ this._printStdout = stdout;
115
+ }
116
+ setPrintStderr(stderr) {
117
+ this._printStderr = stderr;
118
+ }
102
119
  }
103
120
  const logger = createLogger("bun-workspaces");
104
121
  /** Set the global logging level. Defaults to "info" or "error" when `NODE_ENV` is "test" */ const setLogLevel =