repo_description: execute npm package binaries (moved)
repo_topics: cli npm package-manager
code: 'use strict'
  
  const Buffer = require('safe-buffer').Buffer
  const promisify = require('./util.js').promisify
  
  const child = require('./child')
  const fs = require('fs')
  const parseArgs = require('./parse-args.js')
  const path = require('path')
  const which = promisify(require('which'))
  
  module.exports = npx
  module.exports.parseArgs = parseArgs
  function npx (argv) {
    const shell = argv['shell-auto-fallback']
    if (shell || shell === '') {
      const fallback = require('./auto-fallback.js')(
        shell, process.env.SHELL, argv
      )
      if (fallback) {
        return console.log(fallback)
      } else {
        process.exitCode = 1
        return
      }
    }
  
    if (!argv.call && (!argv.command || !argv.package)) {
      !argv.q && console.error(Y()`\nERROR: You must supply a command.\n`)
      !argv.q && parseArgs.showHelp()
      process.exitCode = 1
      return
    }
  
    const startTime = Date.now()
  
    // First, we look to see if we're inside an npm project, and grab its
    // bin path. This is exactly the same as running `$ npm bin`.
    return localBinPath(process.cwd()).then(local => {
      if (local) {
        // Local project paths take priority. Go ahead and prepend it.
        process.env.PATH = `${local}${path.delimiter}${process.env.PATH}`
      }
      return Promise.all([
        // Figuring out if a command exists, early on, lets us maybe
        // short-circuit a few things later. This bit here primarily benefits
        // calls like `$ npx foo`, where we might just be trying to invoke
        // a single command and use whatever is already in the path.
        argv.command && getExistingPath(argv.command, argv),
        // The `-c` flag involves special behavior when used: in this case,
        // we take a bit of extra time to pick up npm's full lifecycle script
        // environment (so you can use `$npm_package_xxxxx` and company).
        // Without that flag, we just use the current env.
        argv.call && local && getEnv(argv)
      ]).then(args => {
        const existing = args[0]
        const newEnv = args[1]
        if (newEnv) {
          // NOTE - we don't need to manipulate PATH further here, because
          //        npm has already done so. And even added the node-gyp path!
          Object.assign(process.env, newEnv)
        }
        if ((!existing && !argv.call) || argv.packageRequested) {
          // We only fire off the updateNotifier if we're installing things
          if (argv.npxPkg) {
            try {
              require('update-notifier')({
                pkg: require(argv.npxPkg)
              }).notify()
            } catch (e) {}
          }
          // Some npm packages need to be installed. Let's install them!
          return ensurePackages(argv.package, argv).then(results => {
            if (results && results.added && results.updated && !argv.q) {
              console.error(Y()`npx: installed ${
                results.added.length + results.updated.length
              } in ${(Date.now() - startTime) / 1000}s`)
            }
            if (
              argv.command &&
              !existing &&
              !argv.packageRequested &&
              argv.package.length === 1
            ) {
              return promisify(fs.readdir)(results.bin).then(bins => {
                if (process.platform === 'win32') {
                  bins = bins.filter(b => b !== 'etc' && b !== 'node_modules')
                }
                if (bins.length < 1) {
                  throw new Error(Y()`command not found: ${argv.command}`)
                }
                const cmd = new RegExp(`^${argv.command}(?:\\.cmd)?$`, 'i')
                const matching = bins.find(b => b.match(cmd))
                return path.resolve(results.bin, bins[matching] || bins[0])
              }, err => {
                if (err.code === 'ENOENT') {
                  throw new Error(Y()`command not found: ${argv.command}`)
                } else {
                  throw err
                }
              })
            } else {
              return existing
            }
          })
        } else {
          // We can skip any extra installation, 'cause everything exists.
          return existing
        }
      }).then(existing => {
        return execCommand(existing, argv)
      }).catch(err => {
        !argv.q && console.error(err.message)
        process.exitCode = err.exitCode || 1
      })
    })
  }
  
  module.exports._localBinPath = localBinPath
  function localBinPath (cwd) {
    return require('./get-prefix.js')(cwd).then(prefix => {
      return prefix && path.join(prefix, 'node_modules', '.bin')
    })
  }
  
  module.exports._getEnv = getEnv
  function getEnv (opts) {
    const args = ['run', 'env', '--parseable']
    return findNodeScript(opts.npm, {isLocal: true}).then(npmPath => {
      if (npmPath) {
        args.unshift(child.escapeArg(opts.npm))
        return process.argv[0]
      } else {
        return opts.npm
      }
    }).then(npmPath => {
      return child.exec(npmPath, args)
    }).then(require('dotenv').parse)
  }
  
  module.exports._ensurePackages = ensurePackages
  function ensurePackages (specs, opts) {
    return (
      opts.cache ? Promise.resolve(opts.cache) : getNpmCache(opts)
    ).then(cache => {
      const prefix = path.join(cache, '_npx', process.pid.toString())
      const bins = process.platform === 'win32'
        ? prefix
        : path.join(prefix, 'bin')
      const rimraf = require('rimraf')
      process.on('exit', () => rimraf.sync(prefix))
      return promisify(rimraf)(bins).then(() => {
        return installPackages(specs, prefix, opts)
      }).then(info => {
        // This will make temp bins _higher priority_ than even local bins.
        // This is intentional, since npx assumes that if you went through
        // the trouble of doing `-p`, you're rather have that one. Right? ;)
        process.env.PATH = `${bins}${path.delimiter}${process.env.PATH}`
        if (!info) { info = {} }
        info.prefix = prefix
        info.bin = bins
        return info
      })
    })
  }
  
  module.exports._getExistingPath = getExistingPath
  function getExistingPath (command, opts) {
    if (opts.isLocal) {
      return Promise.resolve(command)
    } else if (
      opts.cmdHadVersion || opts.packageRequested || opts.ignoreExisting
    ) {
      return Promise.resolve(false)
    } else {
      return which(command).catch(err => {
        if (err.code === 'ENOENT') {
          if (opts.install === false) {
            err.exitCode = 127
            throw err
          }
        } else {
          throw err
        }
      })
    }
  }
  
  module.exports._getNpmCache = getNpmCache
  function getNpmCache (opts) {
    const args = ['config', 'get', 'cache', '--parseable']
    if (opts.userconfig) {
      args.push('--userconfig', child.escapeArg(opts.userconfig, true))
    }
    return findNodeScript(opts.npm, {isLocal: true}).then(npmPath => {
      if (npmPath) {
        // This one is NOT escaped as a path because it's handed to Node.
        args.unshift(child.escapeArg(opts.npm))
        return process.argv[0]
      } else {
        return opts.npm
      }
    }).then(npmPath => {
      return child.exec(npmPath, args)
    }).then(cache => cache.trim())
  }
  
  module.exports._buildArgs = buildArgs
  function buildArgs (specs, prefix, opts) {
    const args = ['install'].concat(specs)
    args.push('--global', '--prefix', prefix)
    if (opts.cache) args.push('--cache', opts.cache)
    if (opts.userconfig) args.push('--userconfig', opts.userconfig)
    args.push('--loglevel', 'error', '--json')
  
    return args
  }
  
  module.exports._installPackages = installPackages
  function installPackages (specs, prefix, opts) {
    const args = buildArgs(specs, prefix, opts)
    return findNodeScript(opts.npm, {isLocal: true}).then(npmPath => {
      if (npmPath) {
        args.unshift(
          process.platform === 'win32'
            ? child.escapeArg(opts.npm)
            : opts.npm
        )
        return process.argv[0]
      } else {
        return opts.npm
      }
    }).then(npmPath => {
      return process.platform === 'win32' ? child.escapeArg(npmPath, true) : npmPath
    }).then(npmPath => {
      return child.spawn(npmPath, args, {
        stdio: opts.installerStdio
          ? opts.installerStdio
          : [0, 'pipe', opts.q ? 'ignore' : 2]
      }).then(deets => {
        try {
          return deets.stdout ? JSON.parse(deets.stdout) : null
        } catch (e) { }
      }, err => {
        if (err.exitCode) {
          err.message = Y()`Install for ${specs} failed with code ${err.exitCode}`
        }
        throw err
      })
    })
  }
  
  module.exports._execCommand = execCommand
  function execCommand (_existing, argv) {
    return findNodeScript(_existing, argv).then(existing => {
      const argvCmdOpts = argv.cmdOpts || []
      if (existing && !argv.alwaysSpawn && !argv.nodeArg && !argv.shell && existing !== process.argv[1]) {
        const Module = require('module')
        // let it take over the process. This means we can skip node startup!
        if (!argv.noYargs) {
          // blow away built-up yargs crud
          require('yargs').reset()
        }
        process.argv = [
          process.argv[0], // Current node binary
          existing // node script path. `runMain()` will set this as the new main
        ].concat(argvCmdOpts) // options for the cmd itself
        Module.runMain() // ✨MAGIC✨. Sorry-not-sorry
      } else if (!existing && argv.nodeArg && argv.nodeArg.length) {
        throw new Error(Y()`ERROR: --node-arg/-n can only be used on packages with node scripts.`)
      } else {
        let cmd = existing
        let cmdOpts = argvCmdOpts
        if (existing) {
          cmd = process.argv[0]
          if (process.platform === 'win32') {
            cmd = child.escapeArg(cmd, true)
          }
          // If we know we're running a run script and we got a --node-arg,
          // we need to fudge things a bit to get them working right.
          cmdOpts = argv.nodeArg
          if (cmdOpts) {
            cmdOpts = Array.isArray(cmdOpts) ? cmdOpts : [cmdOpts]
          } else {
            cmdOpts = []
          }
          // It's valid for a single arg to be a string of multiple
          // space-separated node args.
          // Example: `$ npx -n '--inspect --harmony --debug' ...`
          cmdOpts = cmdOpts.reduce((acc, arg) => {
            return acc.concat(arg.split(/\s+/))
          }, [])
          cmdOpts = cmdOpts.concat(existing, argvCmdOpts)
        }
        const opts = Object.assign({}, argv, { cmdOpts })
        return child.runCommand(cmd, opts).catch(err => {
          if (err.isOperational && err.exitCode) {
            // At this point, we want to treat errors from the child as if
            // we were just running the command. That means no extra msg logging
            process.exitCode = err.exitCode
          } else {
            // But if it's not just a regular child-level error, blow up normally
            throw err
          }
        })
      }
    })
  }
  
  module.exports._findNodeScript = findNodeScript
  function findNodeScript (existing, opts) {
    if (!existing) {
      return Promise.resolve(false)
    } else {
      return promisify(fs.stat)(existing).then(stat => {
        if (opts && opts.isLocal && path.extname(existing) === '.js') {
          return existing
        } else if (opts && opts.isLocal && stat.isDirectory()) {
          // npx will execute the directory itself
          try {
            const pkg = require(path.resolve(existing, 'package.json'))
            const target = path.resolve(existing, pkg.bin || pkg.main || 'index.js')
            return findNodeScript(target, opts).then(script => {
              if (script) {
                return script
              } else {
                throw new Error(Y()`command not found: ${target}`)
              }
            })
          } catch (e) {
            throw new Error(Y()`command not found: ${existing}`)
          }
        } else if (process.platform !== 'win32') {
          const bytecount = 400
          const buf = Buffer.alloc(bytecount)
          return promisify(fs.open)(existing, 'r').then(fd => {
            return promisify(fs.read)(fd, buf, 0, bytecount, 0).then(() => {
              return promisify(fs.close)(fd)
            }, err => {
              return promisify(fs.close)(fd).then(() => { throw err })
            })
          }).then(() => {
            const re = /#!\s*(?:\/usr\/bin\/env\s*node|\/usr\/local\/bin\/node|\/usr\/bin\/node)\s*\r?\n/i
            return buf.toString('utf8').match(re) && existing
          })
        } else if (process.platform === 'win32') {
          const buf = Buffer.alloc(1000)
          return promisify(fs.open)(existing, 'r').then(fd => {
            return promisify(fs.read)(fd, buf, 0, 1000, 0).then(() => {
              return promisify(fs.close)(fd)
            }, err => {
              return promisify(fs.close)(fd).then(() => { throw err })
            })
          }).then(() => {
            return buf.toString('utf8').trim()
          }).then(str => {
            const cmd = /"%~dp0\\node\.exe"\s+"%~dp0\\(.*)"\s+%\*/
            const mingw = /"\$basedir\/node"\s+"\$basedir\/(.*)"\s+"\$@"/i
            return str.match(cmd) || str.match(mingw)
          }).then(match => {
            return match && path.join(path.dirname(existing), match[1])
          })
        }
      })
    }
  }
  
  function Y () {
    return require('./y.js')
  }
  