@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
65 lines (62 loc) • 2.18 kB
JavaScript
import { spawn } from 'node:child_process';
const NEEDS_CMD_QUOTING = /[\s"%&<>^|]/u;
const BACKSLASH_RUN_BEFORE_QUOTE = /(\\*)"/gu;
const TRAILING_BACKSLASH_RUN = /(\\+)$/u;
const spawnShellCompat = (command, args, platform = process.platform) => {
if (platform !== "win32" || command === process.execPath) {
return { args: [...args], command, shell: false };
}
const quote = (value) => {
if (value === "") {
return `""`;
}
if (!NEEDS_CMD_QUOTING.test(value)) {
return value;
}
const escaped = value.replaceAll(BACKSLASH_RUN_BEFORE_QUOTE, String.raw`$1$1\"`).replace(TRAILING_BACKSLASH_RUN, "$1$1");
return `"${escaped}"`;
};
return { args: args.map((argument) => quote(argument)), command: quote(command), shell: true };
};
const defaultSpawner = (descriptor) => new Promise((resolve, reject) => {
const hasInput = typeof descriptor.input === "string";
const wantCapture = descriptor.captureStdout === true;
let stdout = "inherit";
if (wantCapture) {
stdout = "pipe";
} else if (descriptor.stdoutToStderr) {
stdout = 2;
}
const exec = spawnShellCompat(descriptor.command, descriptor.args);
const child = spawn(exec.command, exec.args, {
cwd: descriptor.cwd ?? process.cwd(),
env: descriptor.env ? { ...process.env, ...descriptor.env } : process.env,
shell: exec.shell,
stdio: [hasInput ? "pipe" : "inherit", stdout, "inherit"]
});
let captured = "";
if (wantCapture && child.stdout) {
child.stdout.on("data", (chunk) => {
captured += chunk.toString("utf8");
process.stdout.write(chunk);
});
}
child.on("error", (error) => {
reject(error);
});
child.on("exit", (code, signal) => {
resolve({ code: code ?? (signal ? 1 : 0), stdout: wantCapture ? captured : void 0 });
});
if (hasInput && child.stdin) {
child.stdin.end(descriptor.input);
}
});
const createRecordingSpawner = (exitCode = 0) => {
const calls = [];
const spawner = (descriptor) => {
calls.push({ descriptor });
return Promise.resolve({ code: exitCode });
};
return { calls, spawner };
};
export { createRecordingSpawner, defaultSpawner, spawnShellCompat };