| import {Buffer} from 'node:buffer';
|
| import path from 'node:path';
|
| import childProcess from 'node:child_process';
|
| import process from 'node:process';
|
| import crossSpawn from 'cross-spawn';
|
| import stripFinalNewline from 'strip-final-newline';
|
| import {npmRunPathEnv} from 'npm-run-path';
|
| import onetime from 'onetime';
|
| import {makeError} from './lib/error.js';
|
| import {normalizeStdio, normalizeStdioNode} from './lib/stdio.js';
|
| import {spawnedKill, spawnedCancel, setupTimeout, validateTimeout, setExitHandler} from './lib/kill.js';
|
| import {addPipeMethods} from './lib/pipe.js';
|
| import {handleInput, getSpawnedResult, makeAllStream, handleInputSync} from './lib/stream.js';
|
| import {mergePromise, getSpawnedPromise} from './lib/promise.js';
|
| import {joinCommand, parseCommand, parseTemplates, getEscapedCommand} from './lib/command.js';
|
| import {logCommand, verboseDefault} from './lib/verbose.js';
|
|
|
| const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100;
|
|
|
| const getEnv = ({env: envOption, extendEnv, preferLocal, localDir, execPath}) => {
|
| const env = extendEnv ? {...process.env, ...envOption} : envOption;
|
|
|
| if (preferLocal) {
|
| return npmRunPathEnv({env, cwd: localDir, execPath});
|
| }
|
|
|
| return env;
|
| };
|
|
|
| const handleArguments = (file, args, options = {}) => {
|
| const parsed = crossSpawn._parse(file, args, options);
|
| file = parsed.command;
|
| args = parsed.args;
|
| options = parsed.options;
|
|
|
| options = {
|
| maxBuffer: DEFAULT_MAX_BUFFER,
|
| buffer: true,
|
| stripFinalNewline: true,
|
| extendEnv: true,
|
| preferLocal: false,
|
| localDir: options.cwd || process.cwd(),
|
| execPath: process.execPath,
|
| encoding: 'utf8',
|
| reject: true,
|
| cleanup: true,
|
| all: false,
|
| windowsHide: true,
|
| verbose: verboseDefault,
|
| ...options,
|
| };
|
|
|
| options.env = getEnv(options);
|
|
|
| options.stdio = normalizeStdio(options);
|
|
|
| if (process.platform === 'win32' && path.basename(file, '.exe') === 'cmd') {
|
|
|
| args.unshift('/q');
|
| }
|
|
|
| return {file, args, options, parsed};
|
| };
|
|
|
| const handleOutput = (options, value, error) => {
|
| if (typeof value !== 'string' && !Buffer.isBuffer(value)) {
|
|
|
| return error === undefined ? undefined : '';
|
| }
|
|
|
| if (options.stripFinalNewline) {
|
| return stripFinalNewline(value);
|
| }
|
|
|
| return value;
|
| };
|
|
|
| export function execa(file, args, options) {
|
| const parsed = handleArguments(file, args, options);
|
| const command = joinCommand(file, args);
|
| const escapedCommand = getEscapedCommand(file, args);
|
| logCommand(escapedCommand, parsed.options);
|
|
|
| validateTimeout(parsed.options);
|
|
|
| let spawned;
|
| try {
|
| spawned = childProcess.spawn(parsed.file, parsed.args, parsed.options);
|
| } catch (error) {
|
|
|
| const dummySpawned = new childProcess.ChildProcess();
|
| const errorPromise = Promise.reject(makeError({
|
| error,
|
| stdout: '',
|
| stderr: '',
|
| all: '',
|
| command,
|
| escapedCommand,
|
| parsed,
|
| timedOut: false,
|
| isCanceled: false,
|
| killed: false,
|
| }));
|
| mergePromise(dummySpawned, errorPromise);
|
| return dummySpawned;
|
| }
|
|
|
| const spawnedPromise = getSpawnedPromise(spawned);
|
| const timedPromise = setupTimeout(spawned, parsed.options, spawnedPromise);
|
| const processDone = setExitHandler(spawned, parsed.options, timedPromise);
|
|
|
| const context = {isCanceled: false};
|
|
|
| spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned));
|
| spawned.cancel = spawnedCancel.bind(null, spawned, context);
|
|
|
| const handlePromise = async () => {
|
| const [{error, exitCode, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone);
|
| const stdout = handleOutput(parsed.options, stdoutResult);
|
| const stderr = handleOutput(parsed.options, stderrResult);
|
| const all = handleOutput(parsed.options, allResult);
|
|
|
| if (error || exitCode !== 0 || signal !== null) {
|
| const returnedError = makeError({
|
| error,
|
| exitCode,
|
| signal,
|
| stdout,
|
| stderr,
|
| all,
|
| command,
|
| escapedCommand,
|
| parsed,
|
| timedOut,
|
| isCanceled: context.isCanceled || (parsed.options.signal ? parsed.options.signal.aborted : false),
|
| killed: spawned.killed,
|
| });
|
|
|
| if (!parsed.options.reject) {
|
| return returnedError;
|
| }
|
|
|
| throw returnedError;
|
| }
|
|
|
| return {
|
| command,
|
| escapedCommand,
|
| exitCode: 0,
|
| stdout,
|
| stderr,
|
| all,
|
| failed: false,
|
| timedOut: false,
|
| isCanceled: false,
|
| killed: false,
|
| };
|
| };
|
|
|
| const handlePromiseOnce = onetime(handlePromise);
|
|
|
| handleInput(spawned, parsed.options);
|
|
|
| spawned.all = makeAllStream(spawned, parsed.options);
|
|
|
| addPipeMethods(spawned);
|
| mergePromise(spawned, handlePromiseOnce);
|
| return spawned;
|
| }
|
|
|
| export function execaSync(file, args, options) {
|
| const parsed = handleArguments(file, args, options);
|
| const command = joinCommand(file, args);
|
| const escapedCommand = getEscapedCommand(file, args);
|
| logCommand(escapedCommand, parsed.options);
|
|
|
| const input = handleInputSync(parsed.options);
|
|
|
| let result;
|
| try {
|
| result = childProcess.spawnSync(parsed.file, parsed.args, {...parsed.options, input});
|
| } catch (error) {
|
| throw makeError({
|
| error,
|
| stdout: '',
|
| stderr: '',
|
| all: '',
|
| command,
|
| escapedCommand,
|
| parsed,
|
| timedOut: false,
|
| isCanceled: false,
|
| killed: false,
|
| });
|
| }
|
|
|
| const stdout = handleOutput(parsed.options, result.stdout, result.error);
|
| const stderr = handleOutput(parsed.options, result.stderr, result.error);
|
|
|
| if (result.error || result.status !== 0 || result.signal !== null) {
|
| const error = makeError({
|
| stdout,
|
| stderr,
|
| error: result.error,
|
| signal: result.signal,
|
| exitCode: result.status,
|
| command,
|
| escapedCommand,
|
| parsed,
|
| timedOut: result.error && result.error.code === 'ETIMEDOUT',
|
| isCanceled: false,
|
| killed: result.signal !== null,
|
| });
|
|
|
| if (!parsed.options.reject) {
|
| return error;
|
| }
|
|
|
| throw error;
|
| }
|
|
|
| return {
|
| command,
|
| escapedCommand,
|
| exitCode: 0,
|
| stdout,
|
| stderr,
|
| failed: false,
|
| timedOut: false,
|
| isCanceled: false,
|
| killed: false,
|
| };
|
| }
|
|
|
| const normalizeScriptStdin = ({input, inputFile, stdio}) => input === undefined && inputFile === undefined && stdio === undefined
|
| ? {stdin: 'inherit'}
|
| : {};
|
|
|
| const normalizeScriptOptions = (options = {}) => ({
|
| preferLocal: true,
|
| ...normalizeScriptStdin(options),
|
| ...options,
|
| });
|
|
|
| function create$(options) {
|
| function $(templatesOrOptions, ...expressions) {
|
| if (!Array.isArray(templatesOrOptions)) {
|
| return create$({...options, ...templatesOrOptions});
|
| }
|
|
|
| const [file, ...args] = parseTemplates(templatesOrOptions, expressions);
|
| return execa(file, args, normalizeScriptOptions(options));
|
| }
|
|
|
| $.sync = (templates, ...expressions) => {
|
| if (!Array.isArray(templates)) {
|
| throw new TypeError('Please use $(options).sync`command` instead of $.sync(options)`command`.');
|
| }
|
|
|
| const [file, ...args] = parseTemplates(templates, expressions);
|
| return execaSync(file, args, normalizeScriptOptions(options));
|
| };
|
|
|
| return $;
|
| }
|
|
|
| export const $ = create$();
|
|
|
| export function execaCommand(command, options) {
|
| const [file, ...args] = parseCommand(command);
|
| return execa(file, args, options);
|
| }
|
|
|
| export function execaCommandSync(command, options) {
|
| const [file, ...args] = parseCommand(command);
|
| return execaSync(file, args, options);
|
| }
|
|
|
| export function execaNode(scriptPath, args, options = {}) {
|
| if (args && !Array.isArray(args) && typeof args === 'object') {
|
| options = args;
|
| args = [];
|
| }
|
|
|
| const stdio = normalizeStdioNode(options);
|
| const defaultExecArgv = process.execArgv.filter(arg => !arg.startsWith('--inspect'));
|
|
|
| const {
|
| nodePath = process.execPath,
|
| nodeOptions = defaultExecArgv,
|
| } = options;
|
|
|
| return execa(
|
| nodePath,
|
| [
|
| ...nodeOptions,
|
| scriptPath,
|
| ...(Array.isArray(args) ? args : []),
|
| ],
|
| {
|
| ...options,
|
| stdin: undefined,
|
| stdout: undefined,
|
| stderr: undefined,
|
| stdio,
|
| shell: false,
|
| },
|
| );
|
| }
|
|
|