From a6ad3b9be45ed6dfaef3cdb0678770c57ce51e39 Mon Sep 17 00:00:00 2001 From: Martin Amps Date: Sun, 22 Dec 2024 15:14:46 +0700 Subject: [PATCH] add --elide-lines override flag for workspace filtering (#15837) --- src/bunfig.zig | 8 +++ src/cli.zig | 22 +++++- src/cli/filter_run.zig | 14 +++- test/cli/run/filter-workspace.test.ts | 96 ++++++++++++++++++++++++++- 4 files changed, 134 insertions(+), 6 deletions(-) diff --git a/src/bunfig.zig b/src/bunfig.zig index ac2ff21f204c66..8ca56ec0ee8c7f 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -597,6 +597,14 @@ pub const Bunfig = struct { } } + if (run_expr.get("elide-lines")) |elide_lines| { + if (elide_lines.data == .e_number) { + this.ctx.bundler_options.elide_lines = @intFromFloat(elide_lines.data.e_number.value); + } else { + try this.addError(elide_lines.loc, "Expected number"); + } + } + if (run_expr.get("shell")) |shell| { if (shell.asString(allocator)) |value| { if (strings.eqlComptime(value, "bun")) { diff --git a/src/cli.zig b/src/cli.zig index d49b37200c4496..95c5a10a047b93 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -243,6 +243,7 @@ pub const Arguments = struct { const auto_only_params = [_]ParamType{ // clap.parseParam("--all") catch unreachable, clap.parseParam("--silent Don't print the script command") catch unreachable, + clap.parseParam("--elide-lines Number of lines of script output shown when using --filter (default: 10). Set to 0 to show all lines.") catch unreachable, clap.parseParam("-v, --version Print version and exit") catch unreachable, clap.parseParam("--revision Print version with revision and exit") catch unreachable, } ++ auto_or_run_params; @@ -250,6 +251,7 @@ pub const Arguments = struct { const run_only_params = [_]ParamType{ clap.parseParam("--silent Don't print the script command") catch unreachable, + clap.parseParam("--elide-lines Number of lines of script output shown when using --filter (default: 10). Set to 0 to show all lines.") catch unreachable, } ++ auto_or_run_params; pub const run_params = run_only_params ++ runtime_params_ ++ transpiler_params_ ++ base_params_; @@ -501,6 +503,15 @@ pub const Arguments = struct { if (cmd == .RunCommand or cmd == .AutoCommand) { ctx.filters = args.options("--filter"); + + if (args.option("--elide-lines")) |elide_lines| { + if (elide_lines.len > 0) { + ctx.bundler_options.elide_lines = std.fmt.parseInt(usize, elide_lines, 10) catch { + Output.prettyErrorln("error: Invalid elide-lines: \"{s}\"", .{elide_lines}); + Global.exit(1); + }; + } + } } if (cmd == .TestCommand) { @@ -1106,6 +1117,15 @@ pub const Arguments = struct { ctx.debug.silent = true; } + if (args.option("--elide-lines")) |elide_lines| { + if (elide_lines.len > 0) { + ctx.bundler_options.elide_lines = std.fmt.parseInt(usize, elide_lines, 10) catch { + Output.prettyErrorln("error: Invalid elide-lines: \"{s}\"", .{elide_lines}); + Global.exit(1); + }; + } + } + if (opts.define) |define| { if (define.keys.len > 0) bun.JSC.RuntimeTranspilerCache.is_disabled = true; @@ -1513,7 +1533,7 @@ pub const Command = struct { env_behavior: Api.DotEnvBehavior = .disable, env_prefix: []const u8 = "", - + elide_lines: ?usize = null, // Compile options compile: bool = false, compile_target: Cli.CompileTarget = .{}, diff --git a/src/cli/filter_run.zig b/src/cli/filter_run.zig index cede921e3fd178..9093ee3fa234c4 100644 --- a/src/cli/filter_run.zig +++ b/src/cli/filter_run.zig @@ -29,6 +29,7 @@ const ScriptConfig = struct { // ../../node_modules/.bin // and so forth, in addition to the user's $PATH. PATH: []const u8, + elide_count: ?usize, fn cmp(_: void, a: @This(), b: @This()) bool { return bun.strings.cmpStringsAsc({}, a.package_name, b.package_name); @@ -247,7 +248,7 @@ const State = struct { if (data[data.len - 1] == '\n') { data = data[0 .. data.len - 1]; } - if (max_lines == null) return .{ .content = data, .elided_count = 0 }; + if (max_lines == null or max_lines.? == 0) return .{ .content = data, .elided_count = 0 }; var i: usize = data.len; var lines: usize = 0; while (i > 0) : (i -= 1) { @@ -282,7 +283,9 @@ const State = struct { } for (this.handles) |*handle| { // normally we truncate the output to 10 lines, but on abort we print everything to aid debugging - const e = elide(handle.buffer.items, if (is_abort) null else 10); + const elide_lines = if (is_abort) null else handle.config.elide_count orelse 10; + const e = elide(handle.buffer.items, elide_lines); + try this.draw_buf.writer().print(fmt("{s} {s} $ {s}\n"), .{ handle.config.package_name, handle.config.script_name, handle.config.script_content }); if (e.elided_count > 0) { try this.draw_buf.writer().print( @@ -513,6 +516,7 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { .combined = copy_script.items[0 .. copy_script.items.len - 1 :0], .deps = pkgjson.dependencies, .PATH = PATH, + .elide_count = ctx.bundler_options.elide_lines, }); } } @@ -536,6 +540,12 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { .env = this_transpiler.env, }; + // Check if elide-lines is used in a non-terminal environment + if (ctx.bundler_options.elide_lines != null and !state.pretty_output) { + Output.prettyErrorln("error: --elide-lines is only supported in terminal environments", .{}); + Global.exit(1); + } + // initialize the handles var map = bun.StringHashMap(std.ArrayList(*ProcessHandle)).init(ctx.allocator); for (scripts.items, 0..) |*script, i| { diff --git a/test/cli/run/filter-workspace.test.ts b/test/cli/run/filter-workspace.test.ts index a6c402c8a69a43..2d5b4f4fe9b097 100644 --- a/test/cli/run/filter-workspace.test.ts +++ b/test/cli/run/filter-workspace.test.ts @@ -86,6 +86,8 @@ function runInCwdSuccess({ antipattern, command = ["present"], auto = false, + env = {}, + elideCount, }: { cwd: string; pattern: string | string[]; @@ -93,8 +95,16 @@ function runInCwdSuccess({ antipattern?: RegExp | RegExp[]; command?: string[]; auto?: boolean; + env?: Record; + elideCount?: number; }) { const cmd = auto ? [bunExe()] : [bunExe(), "run"]; + + // Add elide-lines first if specified + if (elideCount !== undefined) { + cmd.push("--elide-lines", elideCount.toString()); + } + if (Array.isArray(pattern)) { for (const p of pattern) { cmd.push("--filter", p); @@ -102,13 +112,15 @@ function runInCwdSuccess({ } else { cmd.push("--filter", pattern); } + for (const c of command) { cmd.push(c); } + const { exitCode, stdout, stderr } = spawnSync({ - cwd: cwd, - cmd: cmd, - env: bunEnv, + cwd, + cmd, + env: { ...bunEnv, ...env }, stdout: "pipe", stderr: "pipe", }); @@ -416,4 +428,82 @@ describe("bun", () => { expect(stdoutval).toMatch(/code 23/); expect(exitCode).toBe(23); }); + + function runElideLinesTest({ + elideLines, + target_pattern, + antipattern, + win32ExpectedError, + }: { + elideLines: number; + target_pattern: RegExp[]; + antipattern?: RegExp[]; + win32ExpectedError: RegExp; + }) { + const dir = tempDirWithFiles("testworkspace", { + packages: { + dep0: { + "index.js": Array(20).fill("console.log('log_line');").join("\n"), + "package.json": JSON.stringify({ + name: "dep0", + scripts: { + script: `${bunExe()} run index.js`, + }, + }), + }, + }, + "package.json": JSON.stringify({ + name: "ws", + workspaces: ["packages/*"], + }), + }); + + if (process.platform === "win32") { + const { exitCode, stderr } = spawnSync({ + cwd: dir, + cmd: [bunExe(), "run", "--filter", "./packages/dep0", "--elide-lines", String(elideLines), "script"], + env: { ...bunEnv, FORCE_COLOR: "1", NO_COLOR: "0" }, + stdout: "pipe", + stderr: "pipe", + }); + expect(stderr.toString()).toMatch(win32ExpectedError); + expect(exitCode).not.toBe(0); + return; + } + + runInCwdSuccess({ + cwd: dir, + pattern: "./packages/dep0", + env: { FORCE_COLOR: "1", NO_COLOR: "0" }, + target_pattern, + antipattern, + command: ["script"], + elideCount: elideLines, + }); + } + + test("elides output by default when using --filter", () => { + runElideLinesTest({ + elideLines: 10, + target_pattern: [/\[10 lines elided\]/, /(?:log_line[\s\S]*?){20}/], + win32ExpectedError: /--elide-lines is only supported in terminal environments/, + }); + }); + + test("respects --elide-lines argument", () => { + runElideLinesTest({ + elideLines: 15, + target_pattern: [/\[5 lines elided\]/, /(?:log_line[\s\S]*?){20}/], + win32ExpectedError: /--elide-lines is only supported in terminal environments/, + }); + }); + + test("--elide-lines=0 shows all output", () => { + runElideLinesTest({ + elideLines: 0, + target_pattern: [/(?:log_line[\s\S]*?){20}/], + antipattern: [/lines elided/], + win32ExpectedError: /--elide-lines is only supported in terminal environments/, + }); + }); });