diff --git a/scripts/dev.mjs b/scripts/dev.mjs index 895880f9..62ded4bf 100644 --- a/scripts/dev.mjs +++ b/scripts/dev.mjs @@ -185,13 +185,22 @@ export function developmentServiceEnvironment(prepared, base = process.env) { return environment; } +export function usesWindowsCmdShell(command, platform = process.platform) { + return platform === "win32" && /\.(cmd|bat)$/i.test(command); +} + export function run( command, args, { cwd = repoRoot, label = [command, ...args].join(" "), environment = process.env } = {}, ) { return new Promise((resolve, reject) => { - const child = spawn(command, args, { cwd, env: environment, stdio: "inherit" }); + const child = spawn(command, args, { + cwd, + env: environment, + stdio: "inherit", + shell: usesWindowsCmdShell(command), + }); child.once("error", (error) => { const hint = error.code === "ENOENT" diff --git a/scripts/dev.test.mjs b/scripts/dev.test.mjs index ce034cff..5227c40a 100644 --- a/scripts/dev.test.mjs +++ b/scripts/dev.test.mjs @@ -10,6 +10,7 @@ import { prepareDevEnv, setEnvIfBlank, usesLocalStorage, + usesWindowsCmdShell, } from "./dev.mjs"; const example = [ @@ -280,3 +281,11 @@ test("an exported development origin overrides the env file, and a blank one is }); assert.equal(blank.devOrigins, "from-file.example.dev"); }); + +test("Windows .cmd/.bat spawns use a shell so Node does not throw EINVAL", () => { + assert.equal(usesWindowsCmdShell("pnpm.cmd", "win32"), true); + assert.equal(usesWindowsCmdShell("pnpm.bat", "win32"), true); + assert.equal(usesWindowsCmdShell("pnpm", "win32"), false); + assert.equal(usesWindowsCmdShell("docker", "win32"), false); + assert.equal(usesWindowsCmdShell("pnpm.cmd", "linux"), false); +});