concurrently
Version:
Run commands concurrently
62 lines (61 loc) • 2.53 kB
JavaScript
import { spawnSync as baseSpawnSync } from 'node:child_process';
import nodeProcess from 'node:process';
const UTF8_CODEPAGE = 65001;
/**
* On Windows, concurrently pipes child-process output through a freshly-allocated console whose
* codepage may not be UTF-8, garbling non-ASCII output from commands that rely on it (see
* open-cli-tools/concurrently#302). This sets the console's codepage to UTF-8 once, which spawned
* children then inherit, and returns a function that restores the original codepage.
*
* No-ops (and never throws) if not on Windows, if the codepage is already UTF-8, or if reading/setting
* it fails for any reason -- this is a best-effort convenience, never something that should block
* concurrently from running.
*/
export function ensureUtf8Codepage(spawnSync = baseSpawnSync, process = nodeProcess) {
const noop = () => { };
if (process.platform !== 'win32') {
return noop;
}
try {
const original = readCodepage(spawnSync);
if (original == null || original === UTF8_CODEPAGE) {
return noop;
}
if (!setCodepage(spawnSync, UTF8_CODEPAGE)) {
return noop;
}
let restored = false;
return () => {
if (restored) {
return;
}
restored = true;
try {
setCodepage(spawnSync, original);
}
catch {
// Best-effort restore; nothing more we can do.
}
};
}
catch {
return noop;
}
}
function readCodepage(spawnSync) {
const result = spawnSync('cmd.exe', ['/s', '/c', 'chcp'], { encoding: 'utf8' });
if (result.error || result.status !== 0 || !result.stdout) {
return null;
}
// Locale-dependent text (e.g. "Active code page: 65001." or "Aktive Codepage: 65001."),
// so match on the number rather than the surrounding words.
const match = /(\d+)/.exec(result.stdout);
return match ? Number(match[1]) : null;
}
function setCodepage(spawnSync, codepage) {
// `chcp` changes the codepage of the console this process is attached to regardless of where its
// own stdio points, so output can be safely discarded here rather than inherited -- otherwise its
// confirmation text (e.g. "Active code page: 65001.") would leak into concurrently's own output.
const result = spawnSync('cmd.exe', ['/s', '/c', `chcp ${codepage}`], { stdio: 'ignore' });
return !result.error && result.status === 0;
}