@ts-common/azure-js-dev-tools
Version:
Developer dependencies for TypeScript related projects
566 lines • 22.6 kB
JavaScript
;
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = exports.getChildProcessStdio = exports.logResult = exports.logEnvironmentVariables = exports.logCommand = exports.getShowResultFunction = exports.getArgsArray = exports.FakeRunner = exports.RealRunner = exports.chunkToString = exports.ensureQuoted = exports.shouldQuote = exports.quoteIfNeeded = exports.parseCommands = exports.parseCommand = exports.createCommand = exports.parseCommandTokens = exports.parseCommandToken = exports.commandToString = void 0;
var tslib_1 = require("tslib");
var child_process_1 = require("child_process");
var os = tslib_1.__importStar(require("os"));
var arrays_1 = require("./arrays");
var common_1 = require("./common");
var path_1 = require("./path");
/**
* Get the string representation of the provided command.
*/
function commandToString(command) {
var e_1, _a;
var result = quoteIfNeeded(command.executable);
if (arrays_1.any(command.args)) {
try {
for (var _b = tslib_1.__values(command.args), _c = _b.next(); !_c.done; _c = _b.next()) {
var arg = _c.value;
if (arg) {
result += " " + quoteIfNeeded(arg);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
}
return result;
}
exports.commandToString = commandToString;
function parseCommandNonWhitespaceToken(commandString, startIndex) {
var commandStringLength = commandString.length;
var endIndex = startIndex;
var quote;
var escaped = false;
while (endIndex < commandStringLength) {
var currentCharacter = commandString[endIndex];
++endIndex;
if (!quote) {
if (currentCharacter === " ") {
--endIndex;
break;
}
else if (currentCharacter === "'" || currentCharacter === "\"") {
quote = currentCharacter;
}
}
else if (!escaped) {
if (currentCharacter === "\\") {
escaped = true;
}
else if (currentCharacter === quote) {
quote = undefined;
}
}
else {
escaped = false;
}
}
return {
text: commandString.substring(startIndex, endIndex),
startIndex: startIndex,
endIndex: endIndex,
};
}
function parseCommandWhitespaceToken(commandString, startIndex) {
var commandStringLength = commandString.length;
var endIndex = startIndex + 1;
while (endIndex < commandStringLength && commandString[endIndex] === " ") {
++endIndex;
}
return {
text: commandString.substring(startIndex, endIndex),
startIndex: startIndex,
endIndex: endIndex,
isWhitespace: true,
};
}
function parseCommandToken(commandString, startIndex) {
var result;
var commandStringLength = commandString.length;
if (0 <= startIndex && startIndex < commandStringLength) {
var firstCharacter = commandString[startIndex];
if (firstCharacter === " ") {
result = parseCommandWhitespaceToken(commandString, startIndex);
}
else {
result = parseCommandNonWhitespaceToken(commandString, startIndex);
}
}
return result;
}
exports.parseCommandToken = parseCommandToken;
function parseCommandTokens(commandString, startIndex) {
if (startIndex === void 0) { startIndex = 0; }
var result = [];
var argToken = parseCommandToken(commandString, startIndex);
while (argToken) {
if (!argToken.isWhitespace) {
result.push(argToken);
}
argToken = parseCommandToken(commandString, argToken.endIndex);
}
return result;
}
exports.parseCommandTokens = parseCommandTokens;
function createCommand(commandTokens) {
var result;
if (arrays_1.any(commandTokens)) {
result = {
executable: commandTokens.shift().text,
args: commandTokens.map(function (commandToken) { return commandToken.text; }),
};
}
return result;
}
exports.createCommand = createCommand;
/**
* Parse a command object from the provided command string.
*/
function parseCommand(commandString, startIndex) {
if (startIndex === void 0) { startIndex = 0; }
var commandTokens = parseCommandTokens(commandString, startIndex);
return createCommand(commandTokens);
}
exports.parseCommand = parseCommand;
function parseCommands(commandString, startIndex) {
if (startIndex === void 0) { startIndex = 0; }
var commandTokens = parseCommandTokens(commandString, startIndex);
var commandStartIndex = 0;
var result = [];
for (var commandIndex = 0; commandIndex < commandTokens.length; ++commandIndex) {
var command = commandTokens[commandIndex];
if (command.text === "&" || command.text === "&&") {
if (commandStartIndex === commandIndex) {
++commandStartIndex;
}
else {
result.push(createCommand(commandTokens.slice(commandStartIndex, commandIndex)));
commandStartIndex = commandIndex + 1;
}
}
}
if (commandStartIndex < commandTokens.length) {
result.push(createCommand(commandTokens.slice(commandStartIndex, commandTokens.length)));
}
return result;
}
exports.parseCommands = parseCommands;
/**
* Quote the provided value if it is needed.
*/
function quoteIfNeeded(value, quote) {
if (quote === void 0) { quote = "\""; }
return shouldQuote(value, quote) ? ensureQuoted(value, quote) : value;
}
exports.quoteIfNeeded = quoteIfNeeded;
/**
* Determine whether or not the provided value should be quoted.
*/
function shouldQuote(value, quote) {
if (quote === void 0) { quote = "\""; }
return !!value && (value.includes(" ") || value.includes(quote));
}
exports.shouldQuote = shouldQuote;
/**
* Ensure that the provided value is surrounded by the provided quote string.
*/
function ensureQuoted(value, quote) {
if (quote === void 0) { quote = "\""; }
if (quote && value.length < quote.length * 2 || !value.startsWith(quote) || !value.endsWith(quote)) {
if (quote === "\"" || quote === "'") {
value = common_1.replaceAll(value, quote, "\\" + quote);
}
value = "" + quote + value + quote;
}
return value;
}
exports.ensureQuoted = ensureQuoted;
function chunkToString(chunk) {
if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString("utf8");
}
return chunk;
}
exports.chunkToString = chunkToString;
var captureProcessOutput = function (source, captureFn, capturePrefix) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
var capturedOutput, captureOutputFn;
return tslib_1.__generator(this, function (_a) {
capturedOutput = "";
if (captureFn === false || source === null) {
return [2 /*return*/, undefined];
}
captureOutputFn = function (text) {
capturedOutput += text;
if (typeof captureFn === "function") {
if (capturePrefix) {
text = "[" + capturePrefix + "] " + text;
}
captureFn(text);
}
};
return [2 /*return*/, new Promise(function (resolve, reject) {
var currentOutputLine = "";
source.addListener("data", function (chunk) {
currentOutputLine += chunkToString(chunk);
while (true) {
var newLineIndex = currentOutputLine.indexOf("\n");
if (newLineIndex === -1) {
break;
}
var startOfNextLine = newLineIndex + 1;
captureOutputFn(currentOutputLine.substring(0, startOfNextLine));
currentOutputLine = currentOutputLine.substring(startOfNextLine);
}
});
source.addListener("error", function (error) {
if (currentOutputLine) {
captureOutputFn(currentOutputLine);
currentOutputLine = "";
}
reject(error);
});
source.addListener("end", function () {
if (currentOutputLine) {
captureOutputFn(currentOutputLine);
currentOutputLine = "";
}
resolve(capturedOutput);
});
})];
});
}); };
/**
* A command runner that runs commands using a spawned process.
*/
var RealRunner = /** @class */ (function () {
function RealRunner() {
}
RealRunner.prototype.run = function (command, options) {
if (options === void 0) { options = {}; }
var executablePath = path_1.normalizePath(command.executable, os.platform());
var childProcess = child_process_1.spawn(executablePath, command.args || [], {
cwd: options.executionFolderPath,
stdio: getChildProcessStdio(options),
env: options.environmentVariables || process.env
});
var stdoutCaptured = captureProcessOutput(childProcess.stdout, options.captureOutput, options.capturePrefix);
var stderrCaptured = captureProcessOutput(childProcess.stderr, options.captureError, options.capturePrefix);
var processExitCode;
var processDone = new Promise(function (resolve, reject) {
childProcess.addListener("exit", function (exitCode) {
processExitCode = exitCode;
resolve();
});
childProcess.addListener("error", reject);
});
return Promise.all([processDone, stdoutCaptured, stderrCaptured])
.then(function (_a) {
var _b = tslib_1.__read(_a, 3), _ = _b[0], stdout = _b[1], stderr = _b[2];
return {
exitCode: processExitCode,
stdout: stdout, stderr: stderr,
processId: childProcess.pid,
};
})
.catch(function (error) {
if (typeof options.captureError === "function") {
options.captureError(error.toString());
}
return {
error: error
};
});
};
return RealRunner;
}());
exports.RealRunner = RealRunner;
function getExecutionFolderPath(options) {
return (options && options.executionFolderPath) || process.cwd();
}
/**
* A fake command runner.
*/
var FakeRunner = /** @class */ (function () {
function FakeRunner(innerRunner) {
this.fakeCommands = [];
this.innerRunner = innerRunner || new RealRunner();
this.unrecognizedCommand = function (command, options) {
return Promise.reject(new Error("No FakeRunner result has been registered for the command \"" + commandToString(command) + "\" at \"" + getExecutionFolderPath(options) + "\"."));
};
}
/**
* Set the function to invoke when an unrecognized command is run.
* @param unrecognizedCommandHandler The function to call when an unrecognized command is run.
*/
FakeRunner.prototype.onUnrecognizedCommand = function (unrecognizedCommandHandler) {
this.unrecognizedCommand = unrecognizedCommandHandler;
};
/**
* Configure this FakeRunner so that all unrecognized commands will be passed through to its
* inner runner.
*/
FakeRunner.prototype.passthroughUnrecognized = function () {
var _this = this;
this.onUnrecognizedCommand(function (command, options) {
return _this.innerRunner.run(command, options);
});
};
/**
* Set the fake result to return when the provided command is run.
* @param command The command to fake.
*/
FakeRunner.prototype.set = function (command) {
this.fakeCommands.push(command);
};
/**
* Indicate that when this Runner attempts to run the provided commandString it should just pass
* the commandString through to the inner runner.
* @param commandString The commandString to pass through to the inner runner.
*/
FakeRunner.prototype.passthrough = function (command, executionFolderPath) {
var _this = this;
this.set({
executable: command.executable,
args: command.args,
executionFolderPath: executionFolderPath,
result: function () { return _this.innerRunner.run(command, { executionFolderPath: executionFolderPath }); }
});
};
FakeRunner.prototype.run = function (command, options) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var commandString, executionFolderPath, fakeCommand, result, runResult;
return tslib_1.__generator(this, function (_a) {
commandString = commandToString(command);
executionFolderPath = getExecutionFolderPath(options);
fakeCommand = arrays_1.last(this.fakeCommands, function (registeredFakeCommand) {
return commandString === commandToString(registeredFakeCommand) &&
(!registeredFakeCommand.executionFolderPath || executionFolderPath === registeredFakeCommand.executionFolderPath);
});
if (!fakeCommand) {
result = Promise.resolve(this.unrecognizedCommand(command, options));
}
else {
runResult = fakeCommand.result;
if (!runResult) {
runResult = { exitCode: 0 };
}
else if (typeof runResult === "function") {
runResult = runResult();
}
result = Promise.resolve(runResult);
}
return [2 /*return*/, result];
});
});
};
return FakeRunner;
}());
exports.FakeRunner = FakeRunner;
/**
* Get an array of string arguments from the provided args.
* @param args The args string or array of strings.
*/
function getArgsArray(args) {
var argsArray = [];
if (typeof args === "string") {
argsArray.push.apply(argsArray, tslib_1.__spread(args.split(" ")));
}
else if (args) {
argsArray.push.apply(argsArray, tslib_1.__spread(args));
}
return argsArray;
}
exports.getArgsArray = getArgsArray;
/**
* Get the showResult function that will determine whether the command's result will be logged.
* @param showResult The showResult property from the command's RunOptions.
*/
function getShowResultFunction(showResult) {
var result;
if (showResult === undefined) {
result = function (result) { return result.exitCode !== 0; };
}
else if (typeof showResult === "boolean") {
result = function () { return !!showResult; };
}
else {
result = showResult;
}
return result;
}
exports.getShowResultFunction = getShowResultFunction;
function logCommand(command, options) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var commandString;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!(options && options.log && (options.showCommand == undefined || options.showCommand))) return [3 /*break*/, 2];
commandString = commandToString(command);
if (options.executionFolderPath) {
commandString = options.executionFolderPath + ": " + commandString;
}
return [4 /*yield*/, Promise.resolve(options.log(commandString))];
case 1:
_a.sent();
_a.label = 2;
case 2: return [2 /*return*/];
}
});
});
}
exports.logCommand = logCommand;
function logEnvironmentVariables(options) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a, _b, _c, entryName, entryValue, e_2_1;
var e_2, _d;
return tslib_1.__generator(this, function (_e) {
switch (_e.label) {
case 0:
if (!(options && options.log && options.environmentVariables && options.showEnvironmentVariables)) return [3 /*break*/, 9];
return [4 /*yield*/, options.log("Environment Variables:")];
case 1:
_e.sent();
_e.label = 2;
case 2:
_e.trys.push([2, 7, 8, 9]);
_a = tslib_1.__values(Object.entries(options.environmentVariables)), _b = _a.next();
_e.label = 3;
case 3:
if (!!_b.done) return [3 /*break*/, 6];
_c = tslib_1.__read(_b.value, 2), entryName = _c[0], entryValue = _c[1];
return [4 /*yield*/, options.log(" \"" + entryName + "\": \"" + entryValue + "\"")];
case 4:
_e.sent();
_e.label = 5;
case 5:
_b = _a.next();
return [3 /*break*/, 3];
case 6: return [3 /*break*/, 9];
case 7:
e_2_1 = _e.sent();
e_2 = { error: e_2_1 };
return [3 /*break*/, 9];
case 8:
try {
if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
}
finally { if (e_2) throw e_2.error; }
return [7 /*endfinally*/];
case 9: return [2 /*return*/];
}
});
});
}
exports.logEnvironmentVariables = logEnvironmentVariables;
function logResult(result, options) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var showResult;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!(options && options.log)) return [3 /*break*/, 10];
showResult = getShowResultFunction(options.showResult);
if (!showResult(result)) return [3 /*break*/, 10];
return [4 /*yield*/, Promise.resolve(options.log("Exit Code: " + result.exitCode))];
case 1:
_a.sent();
if (!(result.stdout && (options.captureOutput === true || options.captureOutput === undefined))) return [3 /*break*/, 4];
return [4 /*yield*/, Promise.resolve(options.log("Output:"))];
case 2:
_a.sent();
return [4 /*yield*/, Promise.resolve(options.log(result.stdout))];
case 3:
_a.sent();
_a.label = 4;
case 4:
if (!(result.stderr && (options.captureError === true || options.captureError === undefined))) return [3 /*break*/, 7];
return [4 /*yield*/, Promise.resolve(options.log("Error:"))];
case 5:
_a.sent();
return [4 /*yield*/, Promise.resolve(options.log(result.stderr))];
case 6:
_a.sent();
_a.label = 7;
case 7:
if (!(result.error && (options.captureError === true || options.captureError === undefined))) return [3 /*break*/, 10];
return [4 /*yield*/, Promise.resolve(options.log("Error:"))];
case 8:
_a.sent();
return [4 /*yield*/, Promise.resolve(options.log(JSON.stringify(result.error, undefined, 2)))];
case 9:
_a.sent();
_a.label = 10;
case 10: return [2 /*return*/];
}
});
});
}
exports.logResult = logResult;
function getChildProcessStdio(options) {
return [
"inherit",
options.captureOutput === false ? "ignore" : "pipe",
options.captureError === false ? "ignore" : "pipe"
];
}
exports.getChildProcessStdio = getChildProcessStdio;
/**
* Run the provided command asynchronously.
* @param command The command to run.
* @param args The arguments to provide to the command.
*/
function run(command, args, options) {
if (options === void 0) { options = {}; }
return tslib_1.__awaiter(this, void 0, void 0, function () {
var runner, result;
var _a;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
if (typeof command === "string") {
command = {
executable: command,
};
}
if (!command.args) {
command.args = [];
}
if (args) {
(_a = command.args).push.apply(_a, tslib_1.__spread(args));
}
runner = options.runner || new RealRunner();
return [4 /*yield*/, logCommand(command, options)];
case 1:
_b.sent();
return [4 /*yield*/, logEnvironmentVariables(options)];
case 2:
_b.sent();
return [4 /*yield*/, runner.run(command, options)];
case 3:
result = _b.sent();
return [4 /*yield*/, logResult(result, options)];
case 4:
_b.sent();
if (options.throwOnError && result.exitCode) {
throw new Error(command.executable + " " + command.args.join(" ") + " " + (result.stderr || ""));
}
return [2 /*return*/, result];
}
});
});
}
exports.run = run;
//# sourceMappingURL=run.js.map