From 3870f1fcd4375d03863021646621a358209d012d Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sat, 15 Aug 2026 21:18:23 +0000 Subject: [PATCH 1/3] shell(mkdir): accept --verbose instead of the misspelling --vebose --- src/runtime/shell/builtin/mkdir.rs | 3 +- test/js/bun/shell/commands/mkdir.test.ts | 82 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 test/js/bun/shell/commands/mkdir.test.ts diff --git a/src/runtime/shell/builtin/mkdir.rs b/src/runtime/shell/builtin/mkdir.rs index a4695d61c6b2..d218e34ef9df 100644 --- a/src/runtime/shell/builtin/mkdir.rs +++ b/src/runtime/shell/builtin/mkdir.rs @@ -435,8 +435,7 @@ impl FlagParser for Opts { self.parents = true; return Some(ParseFlagResult::ContinueParsing); } - // Note: the `--vebose` typo is intentional (kept for compatibility). - if flag == b"--vebose" { + if flag == b"--verbose" { self.verbose = true; return Some(ParseFlagResult::ContinueParsing); } diff --git a/test/js/bun/shell/commands/mkdir.test.ts b/test/js/bun/shell/commands/mkdir.test.ts new file mode 100644 index 000000000000..5818c35413cf --- /dev/null +++ b/test/js/bun/shell/commands/mkdir.test.ts @@ -0,0 +1,82 @@ +import { $ } from "bun"; +import { describe, expect, test } from "bun:test"; +import { tempDir } from "harness"; +import { readdirSync, statSync } from "node:fs"; +import { join } from "node:path"; + +$.nothrow(); + +async function mkdir(cwd: string, flags: string, operand: string) { + const words = flags.split(" ").filter(Boolean); + const { stdout, stderr, exitCode } = await $`mkdir ${words} ${operand}`.cwd(cwd).quiet(); + return { stdout: stdout.toString(), stderr: stderr.toString(), exitCode }; +} + +describe.concurrent("bunshell mkdir", () => { + describe("verbose", () => { + // A verbose mkdir prints the path of every directory it creates, one per + // line. `--verbose` is the long spelling of `-v`; the parser used to + // accept only the misspelling `--vebose`. + test.each([ + ["-v", "dir", ["dir"]], + ["--verbose", "dir", ["dir"]], + ["-pv", "a/b", ["a", "a/b"]], + ["-p --verbose", "a/b", ["a", "a/b"]], + ["--parents --verbose", "a/b", ["a", "a/b"]], + ["--verbose --parents", "a/b", ["a", "a/b"]], + ["--verbose -p", "a/b", ["a", "a/b"]], + ])("mkdir %s %s prints %j", async (flags, operand, created) => { + using dir = tempDir("mkdir-verbose", {}); + const cwd = String(dir); + + expect(await mkdir(cwd, flags, operand)).toEqual({ + stdout: created.map(name => `${join(cwd, name)}\n`).join(""), + stderr: "", + exitCode: 0, + }); + expect(statSync(join(cwd, operand)).isDirectory()).toBeTrue(); + }); + + test.each(["--parents", "-p", ""])("mkdir %s a/b prints nothing without a verbose flag", async flags => { + using dir = tempDir("mkdir-quiet", { a: {} }); + const cwd = String(dir); + + expect(await mkdir(cwd, flags, "a/b")).toEqual({ stdout: "", stderr: "", exitCode: 0 }); + expect(statSync(join(cwd, "a", "b")).isDirectory()).toBeTrue(); + }); + + test.each(["--verbose --parents", "-pv"])( + "mkdir %s on an existing directory creates and prints nothing", + async flags => { + using dir = tempDir("mkdir-verbose-existing", { dir: {} }); + const cwd = String(dir); + + expect(await mkdir(cwd, flags, "dir")).toEqual({ stdout: "", stderr: "", exitCode: 0 }); + expect(readdirSync(cwd)).toEqual(["dir"]); + }, + ); + + test.each(["--verbose", "-v"])("mkdir %s on an existing directory fails and prints nothing", async flags => { + using dir = tempDir("mkdir-verbose-eexist", { dir: {} }); + const cwd = String(dir); + + expect(await mkdir(cwd, flags, "dir")).toEqual({ + stdout: "", + stderr: `mkdir: ${join(cwd, "dir")}: File exists\n`, + exitCode: 1, + }); + }); + + test("the misspelling --vebose is rejected like any other unknown option", async () => { + using dir = tempDir("mkdir-vebose", {}); + const cwd = String(dir); + + expect(await mkdir(cwd, "--vebose", "dir")).toEqual({ + stdout: "", + stderr: expect.stringMatching(/^mkdir: illegal option -- /), + exitCode: 1, + }); + expect(readdirSync(cwd)).toEqual([]); + }); + }); +}); From 110ffaef0c983b78d58a6cf997690cbcce419da8 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sat, 15 Aug 2026 21:32:03 +0000 Subject: [PATCH 2/3] test(shell/mkdir): expect macOS's EEXIST text in the existing-directory case --- test/js/bun/shell/commands/mkdir.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/js/bun/shell/commands/mkdir.test.ts b/test/js/bun/shell/commands/mkdir.test.ts index 5818c35413cf..15ede685f136 100644 --- a/test/js/bun/shell/commands/mkdir.test.ts +++ b/test/js/bun/shell/commands/mkdir.test.ts @@ -1,6 +1,6 @@ import { $ } from "bun"; import { describe, expect, test } from "bun:test"; -import { tempDir } from "harness"; +import { isMacOS, tempDir } from "harness"; import { readdirSync, statSync } from "node:fs"; import { join } from "node:path"; @@ -60,9 +60,11 @@ describe.concurrent("bunshell mkdir", () => { using dir = tempDir("mkdir-verbose-eexist", { dir: {} }); const cwd = String(dir); + // The builtins use the host's strerror text for EEXIST. + const eexist = isMacOS ? "File or folder exists" : "File exists"; expect(await mkdir(cwd, flags, "dir")).toEqual({ stdout: "", - stderr: `mkdir: ${join(cwd, "dir")}: File exists\n`, + stderr: `mkdir: ${join(cwd, "dir")}: ${eexist}\n`, exitCode: 1, }); }); From 02fcc4488c8a7d58c626b706ac3834a3f3ee174a Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sun, 16 Aug 2026 01:00:25 +0000 Subject: [PATCH 3/3] test(shell/mkdir): pin only what the verbose fix owns in the EEXIST case --- test/js/bun/shell/commands/mkdir.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/js/bun/shell/commands/mkdir.test.ts b/test/js/bun/shell/commands/mkdir.test.ts index 15ede685f136..d4a885e85306 100644 --- a/test/js/bun/shell/commands/mkdir.test.ts +++ b/test/js/bun/shell/commands/mkdir.test.ts @@ -1,6 +1,6 @@ import { $ } from "bun"; import { describe, expect, test } from "bun:test"; -import { isMacOS, tempDir } from "harness"; +import { tempDir } from "harness"; import { readdirSync, statSync } from "node:fs"; import { join } from "node:path"; @@ -60,11 +60,12 @@ describe.concurrent("bunshell mkdir", () => { using dir = tempDir("mkdir-verbose-eexist", { dir: {} }); const cwd = String(dir); - // The builtins use the host's strerror text for EEXIST. - const eexist = isMacOS ? "File or folder exists" : "File exists"; + // Whether the message names the operand or the resolved path, and the + // macOS wording of EEXIST (bun_core's coreutils_error_map), are decided + // elsewhere; this only checks that verbose output stays off on failure. expect(await mkdir(cwd, flags, "dir")).toEqual({ stdout: "", - stderr: `mkdir: ${join(cwd, "dir")}: ${eexist}\n`, + stderr: expect.stringMatching(/^mkdir: (?:.*[\\/])?dir: File (?:or folder )?exists\n$/), exitCode: 1, }); });