| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.prependNodeModulesBinToPath = exports.Shell = void 0;
- const tslib_1 = require("tslib");
- const cli_framework_output_1 = require("@ionic/cli-framework-output");
- const utils_process_1 = require("@ionic/utils-process");
- const utils_subprocess_1 = require("@ionic/utils-subprocess");
- const utils_terminal_1 = require("@ionic/utils-terminal");
- const chalk_1 = tslib_1.__importDefault(require("chalk"));
- const debug_1 = require("debug");
- const path = tslib_1.__importStar(require("path"));
- const split2_1 = tslib_1.__importDefault(require("split2"));
- const stream_combiner2_1 = tslib_1.__importDefault(require("stream-combiner2"));
- const guards_1 = require("../guards");
- const color_1 = require("./color");
- const errors_1 = require("./errors");
- const debug = (0, debug_1.debug)('ionic:lib:shell');
- class Shell {
- constructor(e, options) {
- this.e = e;
- this.alterPath = options && options.alterPath ? options.alterPath : (p) => p;
- }
- async run(command, args, { stream, killOnExit = true, showCommand = true, showError = true, fatalOnNotFound = true, fatalOnError = true, truncateErrorOutput, ...crossSpawnOptions }) {
- const proc = await this.createSubprocess(command, args, crossSpawnOptions);
- const fullCmd = proc.bashify();
- const truncatedCmd = fullCmd.length > 80 ? fullCmd.substring(0, 80) + '...' : fullCmd;
- if (showCommand && this.e.log.level >= cli_framework_output_1.LOGGER_LEVELS.INFO) {
- this.e.log.rawmsg(`> ${(0, color_1.input)(fullCmd)}`);
- }
- const ws = stream ? stream : this.e.log.createWriteStream(cli_framework_output_1.LOGGER_LEVELS.INFO, false);
- try {
- const promise = proc.run();
- if (promise.p.stdout) {
- const s = (0, stream_combiner2_1.default)((0, split2_1.default)(), ws);
- // TODO: https://github.com/angular/angular-cli/issues/10922
- s.on('error', (err) => {
- debug('Error in subprocess stdout pipe: %o', err);
- });
- promise.p.stdout?.pipe(s);
- }
- if (promise.p.stderr) {
- const s = (0, stream_combiner2_1.default)((0, split2_1.default)(), ws);
- // TODO: https://github.com/angular/angular-cli/issues/10922
- s.on('error', (err) => {
- debug('Error in subprocess stderr pipe: %o', err);
- });
- promise.p.stderr?.pipe(s);
- }
- if (killOnExit) {
- (0, utils_process_1.onBeforeExit)(async () => {
- if (promise.p.pid) {
- await (0, utils_process_1.killProcessTree)(promise.p.pid);
- }
- });
- }
- await promise;
- }
- catch (e) {
- if (e instanceof utils_subprocess_1.SubprocessError && e.code === utils_subprocess_1.ERROR_COMMAND_NOT_FOUND) {
- if (fatalOnNotFound) {
- throw new errors_1.FatalException(`Command not found: ${(0, color_1.input)(command)}`, 127);
- }
- else {
- throw e;
- }
- }
- if (!(0, guards_1.isExitCodeException)(e)) {
- throw e;
- }
- let err = e.message || '';
- if (truncateErrorOutput && err.length > truncateErrorOutput) {
- err = `${(0, color_1.strong)('(truncated)')} ... ` + err.substring(err.length - truncateErrorOutput);
- }
- const publicErrorMsg = (`An error occurred while running subprocess ${(0, color_1.input)(command)}.\n` +
- `${(0, color_1.input)(truncatedCmd)} exited with exit code ${e.exitCode}.\n\n` +
- `Re-running this command with the ${(0, color_1.input)('--verbose')} flag may provide more information.`);
- const privateErrorMsg = `Subprocess (${(0, color_1.input)(command)}) encountered an error (exit code ${e.exitCode}).`;
- if (fatalOnError) {
- if (showError) {
- throw new errors_1.FatalException(publicErrorMsg, e.exitCode);
- }
- else {
- throw new errors_1.FatalException(privateErrorMsg, e.exitCode);
- }
- }
- else {
- if (showError) {
- this.e.log.error(publicErrorMsg);
- }
- }
- throw e;
- }
- }
- async output(command, args, { fatalOnNotFound = true, fatalOnError = true, showError = true, showCommand = false, ...crossSpawnOptions }) {
- const proc = await this.createSubprocess(command, args, crossSpawnOptions);
- const fullCmd = proc.bashify();
- const truncatedCmd = fullCmd.length > 80 ? fullCmd.substring(0, 80) + '...' : fullCmd;
- if (showCommand && this.e.log.level >= cli_framework_output_1.LOGGER_LEVELS.INFO) {
- this.e.log.rawmsg(`> ${(0, color_1.input)(fullCmd)}`);
- }
- try {
- return await proc.output();
- }
- catch (e) {
- if (e instanceof utils_subprocess_1.SubprocessError && e.code === utils_subprocess_1.ERROR_COMMAND_NOT_FOUND) {
- if (fatalOnNotFound) {
- throw new errors_1.FatalException(`Command not found: ${(0, color_1.input)(command)}`, 127);
- }
- else {
- throw e;
- }
- }
- if (!(0, guards_1.isExitCodeException)(e)) {
- throw e;
- }
- const errorMsg = `An error occurred while running ${(0, color_1.input)(truncatedCmd)} (exit code ${e.exitCode})\n`;
- if (fatalOnError) {
- throw new errors_1.FatalException(errorMsg, e.exitCode);
- }
- else {
- if (showError) {
- this.e.log.error(errorMsg);
- }
- }
- return '';
- }
- }
- /**
- * When `child_process.spawn` isn't provided a full path to the command
- * binary, it behaves differently on Windows than other platforms. For
- * Windows, discover the full path to the binary, otherwise fallback to the
- * command provided.
- *
- * @see https://github.com/ionic-team/ionic-cli/issues/3563#issuecomment-425232005
- */
- async resolveCommandPath(command, options) {
- if (utils_terminal_1.TERMINAL_INFO.windows) {
- try {
- return await this.which(command, { PATH: options.env && options.env.PATH ? options.env.PATH : process.env.PATH });
- }
- catch (e) {
- // ignore
- }
- }
- return command;
- }
- async which(command, { PATH = process.env.PATH } = {}) {
- return (0, utils_subprocess_1.which)(command, { PATH: this.alterPath(PATH || '') });
- }
- async spawn(command, args, { showCommand = true, ...crossSpawnOptions }) {
- const proc = await this.createSubprocess(command, args, crossSpawnOptions);
- const p = proc.spawn();
- if (showCommand && this.e.log.level >= cli_framework_output_1.LOGGER_LEVELS.INFO) {
- this.e.log.rawmsg(`> ${(0, color_1.input)(proc.bashify())}`);
- }
- return p;
- }
- async cmdinfo(command, args = []) {
- const opts = {};
- const proc = await this.createSubprocess(command, args, opts);
- try {
- const out = await proc.output();
- return out.split('\n').join(' ').trim();
- }
- catch (e) {
- // no command info at this point
- }
- }
- async createSubprocess(command, args = [], options = {}) {
- this.prepareSpawnOptions(options);
- const cmdpath = await this.resolveCommandPath(command, options);
- const proc = new utils_subprocess_1.Subprocess(cmdpath, args, options);
- return proc;
- }
- prepareSpawnOptions(options) {
- // Create a `process.env`-type object from all key/values of `process.env`,
- // then `options.env`, then add several key/values. PATH is supplemented
- // with the `node_modules\.bin` folder in the project directory so that we
- // can run binaries inside a project.
- options.env = (0, utils_process_1.createProcessEnv)(process.env, options.env ?? {}, {
- PATH: this.alterPath(process.env.PATH || ''),
- FORCE_COLOR: chalk_1.default.level > 0 ? '1' : '0',
- });
- }
- }
- exports.Shell = Shell;
- function prependNodeModulesBinToPath(projectDir, p) {
- return path.resolve(projectDir, 'node_modules', '.bin') + path.delimiter + p;
- }
- exports.prependNodeModulesBinToPath = prependNodeModulesBinToPath;
|