The default spawn behavior on Windows is spawn with cmd.exe, which is reasonable for compatibility, but makes it hard to kill due to cmd.exe prompting with "Terminate batch job (Y/N)?" for each command; among other issues. In most cases using pwsh.exe would be preferred.
Similarly, the shell on other platforms is hard-coded to /bin/sh which is quite a bit more limited than may be expected.
Worse, this behavior is essentially already implemented in Node's native spawn() function since v5.7.0 as the { shell: true } option - which additionally accepts a string for the shell (which uses essentially execFile(options.shell, ["-c", command])).
In either case, users may prefer to use a shell emulator like @yarnpkg/shell to get portable behavior.
There is an undocumented spawn option which you can use to override this implementation, but only for the unconfigured createConcurrently option, the default configured export seemingly accidentally doesn't forward it to the underlying createConcurrently() - requiring the caller to re-implement the default behavior. Adding a single spawn: options.spawn line works fine. Perhaps the configured concurrently should use something like return createConcurrently(commands, { outputStream: process.stdout, ...options, controllers: [<existing>, ...options.controllers] }) to forward all options by default.
Alternatively, the API could expose a new shell or spawnOptions which would override the default shell behavior, or even a shellEmulator: boolean option (to match e.g. the pnpm equivalent option) which uses @yarnpkg/shell.
The default spawn behavior on Windows is spawn with
cmd.exe, which is reasonable for compatibility, but makes it hard to kill due tocmd.exeprompting with "Terminate batch job (Y/N)?" for each command; among other issues. In most cases usingpwsh.exewould be preferred.Similarly, the shell on other platforms is hard-coded to
/bin/shwhich is quite a bit more limited than may be expected.Worse, this behavior is essentially already implemented in Node's native
spawn()function since v5.7.0 as the{ shell: true }option - which additionally accepts a string for the shell (which uses essentiallyexecFile(options.shell, ["-c", command])).In either case, users may prefer to use a shell emulator like
@yarnpkg/shellto get portable behavior.There is an undocumented
spawnoption which you can use to override this implementation, but only for the unconfiguredcreateConcurrentlyoption, the default configured export seemingly accidentally doesn't forward it to the underlyingcreateConcurrently()- requiring the caller to re-implement the default behavior. Adding a singlespawn: options.spawnline works fine. Perhaps the configuredconcurrentlyshould use something likereturn createConcurrently(commands, { outputStream: process.stdout, ...options, controllers: [<existing>, ...options.controllers] })to forward all options by default.Alternatively, the API could expose a new
shellorspawnOptionswhich would override the defaultshellbehavior, or even ashellEmulator: booleanoption (to match e.g. the pnpm equivalent option) which uses@yarnpkg/shell.