diff --git a/src/bun.js/api/bun/process.zig b/src/bun.js/api/bun/process.zig index 699734afcd8c58..885496d73ee80b 100644 --- a/src/bun.js/api/bun/process.zig +++ b/src/bun.js/api/bun/process.zig @@ -1509,13 +1509,26 @@ pub fn spawnProcessWindows( bun.Analytics.Features.spawn += 1; var uv_process_options = std.mem.zeroes(uv.uv_process_options_t); - - uv_process_options.args = argv; uv_process_options.env = envp; - uv_process_options.file = options.argv0 orelse argv[0].?; - uv_process_options.exit_cb = &Process.onExitUV; var stack_allocator = std.heap.stackFallback(8192, bun.default_allocator); const allocator = stack_allocator.get(); + + var file = options.argv0 orelse argv[0].?; + var args = argv; + var force_verbatim_arguments = false; + if (!bun.strings.eqlComptime(std.fs.path.extension(std.mem.sliceTo(file, 0)), ".exe")) { + force_verbatim_arguments = true; + file = "cmd.exe"; + const args_slice = std.mem.span(args); + args = try allocator.allocSentinel(?[*:0]const u8, args_slice.len + 2, null); + args[0..2].* = .{ file, "/c" }; + args[2] = std.fmt.allocPrintZ(allocator, "\"{?s}\"", .{args_slice[0]}) catch unreachable; + @memcpy(args[3..], args_slice[1..]); + } + uv_process_options.file = file; + uv_process_options.args = args; + + uv_process_options.exit_cb = &Process.onExitUV; const loop = options.windows.loop.platformEventLoop().uv_loop; const cwd = try allocator.dupeZ(u8, options.cwd); @@ -1540,7 +1553,7 @@ pub fn spawnProcessWindows( uv_process_options.flags |= uv.UV_PROCESS_WINDOWS_HIDE; } - if (options.windows.verbatim_arguments) { + if (options.windows.verbatim_arguments or force_verbatim_arguments) { uv_process_options.flags |= uv.UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; } diff --git a/test/js/bun/shell/bunshell.test.ts b/test/js/bun/shell/bunshell.test.ts index 6e4588fcedb408..9d44be36bfd812 100644 --- a/test/js/bun/shell/bunshell.test.ts +++ b/test/js/bun/shell/bunshell.test.ts @@ -833,6 +833,11 @@ ${temp_dir}` .stdout("complex > command; $(execute)\n") .runAsTest("complex_mixed_special_chars"); }); + + test("spaces in path and args", async () => { + const fields = "name scripts"; + expect(async () => { await $`npm pkg get ${fields}` }).not.toThrow(); + }); }); describe("deno_task", () => {