nx
Version:
187 lines (186 loc) • 6.43 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PseudoTtyProcessWithSend = exports.PseudoTtyProcess = exports.PseudoTerminal = void 0;
exports.createPseudoTerminal = createPseudoTerminal;
const os = require("os");
const socket_utils_1 = require("../daemon/socket-utils");
const native_1 = require("../native");
const pseudo_ipc_1 = require("./pseudo-ipc");
const exit_codes_1 = require("../utils/exit-codes");
// Register single event listeners for all pseudo-terminal instances
const pseudoTerminalShutdownCallbacks = [];
process.on('exit', (code) => {
pseudoTerminalShutdownCallbacks.forEach((cb) => cb(code));
});
function createPseudoTerminal(skipSupportCheck = false) {
if (!skipSupportCheck && !PseudoTerminal.isSupported()) {
throw new Error('Pseudo terminal is not supported on this platform.');
}
const pseudoTerminal = new PseudoTerminal(new native_1.RustPseudoTerminal());
pseudoTerminalShutdownCallbacks.push(pseudoTerminal.shutdown.bind(pseudoTerminal));
return pseudoTerminal;
}
let id = 0;
class PseudoTerminal {
static isSupported() {
return process.stdout.isTTY && supportedPtyPlatform();
}
constructor(rustPseudoTerminal) {
this.rustPseudoTerminal = rustPseudoTerminal;
this.pseudoIPCPath = (0, socket_utils_1.getForkedProcessOsSocketPath)(process.pid.toString() + '-' + id++);
this.pseudoIPC = new pseudo_ipc_1.PseudoIPCServer(this.pseudoIPCPath);
this.initialized = false;
this.childProcesses = new Set();
}
async init() {
if (this.initialized) {
return;
}
await this.pseudoIPC.init();
this.initialized = true;
}
shutdown(code) {
for (const cp of this.childProcesses) {
try {
cp.kill((0, exit_codes_1.codeToSignal)(code));
}
catch { }
}
if (this.initialized) {
this.pseudoIPC.close();
}
}
runCommand(command, { cwd, execArgv, jsEnv, quiet, tty, } = {}) {
const cp = new PseudoTtyProcess(this.rustPseudoTerminal, this.rustPseudoTerminal.runCommand(command, cwd, jsEnv, execArgv, quiet, tty));
this.childProcesses.add(cp);
return cp;
}
async fork(id, script, { cwd, execArgv, jsEnv, quiet, commandLabel, }) {
if (!this.initialized) {
throw new Error('Call init() before forking processes');
}
const cp = new PseudoTtyProcessWithSend(this.rustPseudoTerminal, this.rustPseudoTerminal.fork(id, script, this.pseudoIPCPath, cwd, jsEnv, execArgv, quiet, commandLabel), id, this.pseudoIPC);
this.childProcesses.add(cp);
await this.pseudoIPC.waitForChildReady(id);
return cp;
}
sendMessageToChildren(message) {
this.pseudoIPC.sendMessageToChildren(message);
}
onMessageFromChildren(callback) {
this.pseudoIPC.onMessageFromChildren(callback);
}
}
exports.PseudoTerminal = PseudoTerminal;
class PseudoTtyProcess {
constructor(rustPseudoTerminal, childProcess) {
this.rustPseudoTerminal = rustPseudoTerminal;
this.childProcess = childProcess;
this.isAlive = true;
this.exitCallbacks = [];
this.outputCallbacks = [];
this.terminalOutput = '';
childProcess.onOutput((output) => {
this.terminalOutput += output;
this.outputCallbacks.forEach((cb) => cb(output));
});
childProcess.onExit((message) => {
this.isAlive = false;
const code = messageToCode(message);
childProcess.cleanup();
this.exitCallbacks.forEach((cb) => cb(code));
});
}
async getResults() {
return new Promise((res) => {
this.onExit((code) => {
res({ code, terminalOutput: this.terminalOutput });
});
});
}
onExit(callback) {
this.exitCallbacks.push(callback);
}
onOutput(callback) {
this.outputCallbacks.push(callback);
}
kill(s) {
if (this.isAlive) {
try {
this.childProcess.kill(s);
}
catch {
// when the child process completes before we explicitly call kill, this will throw
// do nothing
}
finally {
this.isAlive = false;
}
}
}
getParserAndWriter() {
return this.childProcess.getParserAndWriter();
}
}
exports.PseudoTtyProcess = PseudoTtyProcess;
class PseudoTtyProcessWithSend extends PseudoTtyProcess {
constructor(rustPseudoTerminal, _childProcess, id, pseudoIpc) {
super(rustPseudoTerminal, _childProcess);
this.rustPseudoTerminal = rustPseudoTerminal;
this.id = id;
this.pseudoIpc = pseudoIpc;
}
send(message) {
this.pseudoIpc.sendMessageToChild(this.id, message);
}
}
exports.PseudoTtyProcessWithSend = PseudoTtyProcessWithSend;
function messageToCode(message) {
if (message.startsWith('Terminated by ')) {
switch (message.replace('Terminated by ', '').trim()) {
case 'Termination':
return 143;
case 'Interrupt':
return 130;
default:
return 128;
}
}
else if (message.startsWith('Exited with code ')) {
return parseInt(message.replace('Exited with code ', '').trim());
}
else if (message === 'Success') {
return 0;
}
else {
return 1;
}
}
function supportedPtyPlatform() {
if (native_1.IS_WASM) {
return false;
}
if (process.platform !== 'win32') {
return true;
}
// TODO: Re-enable Windows support when it's stable
// Currently, there's an issue with control chars.
// See: https://github.com/nrwl/nx/issues/22358
if (process.env.NX_WINDOWS_PTY_SUPPORT !== 'true') {
return false;
}
let windowsVersion = os.release().split('.');
let windowsBuild = windowsVersion[2];
if (!windowsBuild) {
return false;
}
// Mininum supported Windows version:
// https://en.wikipedia.org/wiki/Windows_10,_version_1809
// https://learn.microsoft.com/en-us/windows/console/createpseudoconsole#requirements
if (+windowsBuild < 17763) {
return false;
}
else {
return true;
}
}