Skip to content

Commit

Permalink
Lets avoid spawn.sync
Browse files Browse the repository at this point in the history
  • Loading branch information
bcomnes committed Oct 4, 2023
1 parent 3df3708 commit a3ee6cd
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 102 deletions.
141 changes: 73 additions & 68 deletions lib/run-task.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,83 +139,86 @@ function cleanTaskArg (arg) {
*/
module.exports = function runTask (task, options) {
let cp = null
const promise = new Promise((resolve, reject) => {
ansiStylesPromise.then(({ default: ansiStyles }) => {
const stdin = options.stdin
const stdout = wrapLabeling(task, options.stdout, options.labelState, ansiStyles)
const stderr = wrapLabeling(task, options.stderr, options.labelState, ansiStyles)
const stdinKind = detectStreamKind(stdin, process.stdin)
const stdoutKind = detectStreamKind(stdout, process.stdout)
const stderrKind = detectStreamKind(stderr, process.stderr)
const spawnOptions = { stdio: [stdinKind, stdoutKind, stderrKind] }

// Print task name.
if (options.printName && stdout != null) {
stdout.write(createHeader(
task,
options.packageInfo,
options.stdout.isTTY,
ansiStyles
))
}

// Execute.
let npmPath = options.npmPath
if (!npmPath && process.env.npm_execpath) {
const basename = path.basename(process.env.npm_execpath)
let newBasename = basename
if (basename.startsWith('npx')) {
newBasename = basename.replace('npx', 'npm') // eslint-disable-line no-process-env
} else if (basename.startsWith('pnpx')) {
newBasename = basename.replace('pnpx', 'pnpm') // eslint-disable-line no-process-env
}

npmPath = newBasename === basename
? path.join(path.dirname(process.env.npm_execpath), newBasename)
: process.env.npm_execpath // eslint-disable-line no-process-env
async function asyncRunTask () {
const { default: ansiStyles } = await ansiStylesPromise

const stdin = options.stdin
const stdout = wrapLabeling(task, options.stdout, options.labelState, ansiStyles)
const stderr = wrapLabeling(task, options.stderr, options.labelState, ansiStyles)
const stdinKind = detectStreamKind(stdin, process.stdin)
const stdoutKind = detectStreamKind(stdout, process.stdout)
const stderrKind = detectStreamKind(stderr, process.stderr)
const spawnOptions = { stdio: [stdinKind, stdoutKind, stderrKind] }

// Print task name.
if (options.printName && stdout != null) {
stdout.write(createHeader(
task,
options.packageInfo,
options.stdout.isTTY,
ansiStyles
))
}

// Execute.
let npmPath = options.npmPath
if (!npmPath && process.env.npm_execpath) {
const basename = path.basename(process.env.npm_execpath)
let newBasename = basename
if (basename.startsWith('npx')) {
newBasename = basename.replace('npx', 'npm') // eslint-disable-line no-process-env
} else if (basename.startsWith('pnpx')) {
newBasename = basename.replace('pnpx', 'pnpm') // eslint-disable-line no-process-env
}

const npmPathIsJs = typeof npmPath === 'string' && /\.(c|m)?js/.test(path.extname(npmPath))
let execPath = (npmPathIsJs ? process.execPath : npmPath || 'npm')

if (!npmPath && !process.env.npm_execpath) {
// When a script is being run via pnpm, npmPath and npm_execpath will be null or undefined
// Attempt to figure out whether we're running via pnpm
const projectRoot = path.dirname(options.packageInfo.path)
const hasPnpmLockfile = fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))
const { status: pnpmFound, output } = spawn.sync('which', 'pnpm', { silent: true })
if (hasPnpmLockfile && __dirname.split(path.delimiter).includes('.pnpm') && pnpmFound) {
execPath = output
}
npmPath = newBasename === basename
? path.join(path.dirname(process.env.npm_execpath), newBasename)
: process.env.npm_execpath // eslint-disable-line no-process-env
}

const npmPathIsJs = typeof npmPath === 'string' && /\.(c|m)?js/.test(path.extname(npmPath))
let execPath = (npmPathIsJs ? process.execPath : npmPath || 'npm')

if (!npmPath && !process.env.npm_execpath) {
// When a script is being run via pnpm, npmPath and npm_execpath will be null or undefined
// Attempt to figure out whether we're running via pnpm
const projectRoot = path.dirname(options.packageInfo.path)
const hasPnpmLockfile = fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))
const { status: pnpmFound, output: pnpmWhichOutput } = await spawn('which', 'pnpm', { silent: true })
if (hasPnpmLockfile && __dirname.split(path.delimiter).includes('.pnpm') && pnpmFound) {
execPath = pnpmWhichOutput
}
}

const isYarn = process.env.npm_config_user_agent && process.env.npm_config_user_agent.startsWith('yarn') // eslint-disable-line no-process-env
const isYarn = process.env.npm_config_user_agent && process.env.npm_config_user_agent.startsWith('yarn') // eslint-disable-line no-process-env

const spawnArgs = ['run']
const spawnArgs = ['run']

if (npmPathIsJs) {
spawnArgs.unshift(npmPath)
}
if (!isYarn) {
Array.prototype.push.apply(spawnArgs, options.prefixOptions)
} else if (options.prefixOptions.indexOf('--silent') !== -1) {
spawnArgs.push('--silent')
}
Array.prototype.push.apply(spawnArgs, parseArgs(task).map(cleanTaskArg))
if (npmPathIsJs) {
spawnArgs.unshift(npmPath)
}
if (!isYarn) {
Array.prototype.push.apply(spawnArgs, options.prefixOptions)
} else if (options.prefixOptions.indexOf('--silent') !== -1) {
spawnArgs.push('--silent')
}
Array.prototype.push.apply(spawnArgs, parseArgs(task).map(cleanTaskArg))

cp = spawn(execPath, spawnArgs, spawnOptions)
cp = spawn(execPath, spawnArgs, spawnOptions)

// Piping stdio.
if (stdinKind === 'pipe') {
stdin.pipe(cp.stdin)
}
if (stdoutKind === 'pipe') {
cp.stdout.pipe(stdout, { end: false })
}
if (stderrKind === 'pipe') {
cp.stderr.pipe(stderr, { end: false })
}
// Piping stdio.
if (stdinKind === 'pipe') {
stdin.pipe(cp.stdin)
}
if (stdoutKind === 'pipe') {
cp.stdout.pipe(stdout, { end: false })
}
if (stderrKind === 'pipe') {
cp.stderr.pipe(stderr, { end: false })
}

return new Promise((resolve, reject) => {
// Register
cp.on('error', (err) => {
cp = null
Expand All @@ -226,7 +229,9 @@ module.exports = function runTask (task, options) {
resolve({ task, code, signal })
})
})
})
}

const promise = asyncRunTask()

promise.abort = function abort () {
if (cp != null) {
Expand Down
18 changes: 1 addition & 17 deletions lib/spawn-posix.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,9 @@ function kill () {
* @returns {ChildProcess} A ChildProcess instance of new process.
* @private
*/
function spawn (command, args, options) {
module.exports = function spawn (command, args, options) {
const child = crossSpawn(command, args, options)
child.kill = kill

return child
}

/**
* Launches a new process synchronously with the given command.
* This is almost same as `child_process.spawnSync`.
*
* This returns a `SpawnSyncReturns` object.
*
* @param {string} command - The command to run.
* @param {string[]} args - List of string arguments.
* @param {object} options - Options.
* @returns {SpawnSyncReturns} A ChildProcess instance of new process.
* @private
*/
spawn.sync = crossSpawn.sync

module.exports = spawn
18 changes: 1 addition & 17 deletions lib/spawn-win32.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,9 @@ function kill () {
* @returns {ChildProcess} A ChildProcess instance of new process.
* @private
*/
function spawn (command, args, options) {
module.exports = function spawn (command, args, options) {
const child = crossSpawn(command, args, options)
child.kill = kill

return child
}

/**
* Launches a new process synchronously with the given command.
* This is almost same as `child_process.spawnSync`.
*
* This returns a `SpawnSyncReturns` object.
*
* @param {string} command - The command to run.
* @param {string[]} args - List of string arguments.
* @param {object} options - Options.
* @returns {SpawnSyncReturns} A ChildProcess instance of new process.
* @private
*/
spawn.sync = crossSpawn.sync

module.exports = spawn

0 comments on commit a3ee6cd

Please sign in to comment.