Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/libnpmexec/lib/index.js')
-rw-r--r--node_modules/libnpmexec/lib/index.js185
1 files changed, 185 insertions, 0 deletions
diff --git a/node_modules/libnpmexec/lib/index.js b/node_modules/libnpmexec/lib/index.js
new file mode 100644
index 000000000..906a0b540
--- /dev/null
+++ b/node_modules/libnpmexec/lib/index.js
@@ -0,0 +1,185 @@
+const { delimiter, resolve } = require('path')
+const { promisify } = require('util')
+const read = promisify(require('read'))
+const stat = promisify(require('fs').stat)
+
+const Arborist = require('@npmcli/arborist')
+const ciDetect = require('@npmcli/ci-detect')
+const logger = require('proc-log')
+const mkdirp = require('mkdirp-infer-owner')
+const npa = require('npm-package-arg')
+const pacote = require('pacote')
+const readPackageJson = require('read-package-json-fast')
+
+const cacheInstallDir = require('./cache-install-dir.js')
+const getBinFromManifest = require('./get-bin-from-manifest.js')
+const manifestMissing = require('./manifest-missing.js')
+const noTTY = require('./no-tty.js')
+const runScript = require('./run-script.js')
+
+const fileExists = (file) => stat(file)
+ .then((stat) => stat.isFile())
+ .catch(() => false)
+
+/* istanbul ignore next */
+const PATH = (
+ process.env.PATH || process.env.Path || process.env.path
+).split(delimiter)
+
+const exec = async (opts) => {
+ const {
+ args = [],
+ call = '',
+ color = false,
+ localBin = '',
+ locationMsg = undefined,
+ globalBin = '',
+ output,
+ packages: _packages = [],
+ path = '.',
+ runPath = '.',
+ scriptShell = undefined,
+ yes = undefined,
+ ...flatOptions
+ } = opts
+ const log = flatOptions.log || logger
+
+ // dereferences values because we manipulate it later
+ const packages = [..._packages]
+ const pathArr = [...PATH]
+ const _run = () => runScript({
+ args,
+ call,
+ color,
+ flatOptions,
+ locationMsg,
+ log,
+ output,
+ path,
+ pathArr,
+ runPath,
+ scriptShell,
+ })
+
+ // nothing to maybe install, skip the arborist dance
+ if (!call && !args.length && !packages.length)
+ return await _run()
+
+ const needPackageCommandSwap = args.length && !packages.length
+ // if there's an argument and no package has been explicitly asked for
+ // check the local and global bin paths for a binary named the same as
+ // the argument and run it if it exists, otherwise fall through to
+ // the behavior of treating the single argument as a package name
+ if (needPackageCommandSwap) {
+ let binExists = false
+ if (await fileExists(`${localBin}/${args[0]}`)) {
+ pathArr.unshift(localBin)
+ binExists = true
+ } else if (await fileExists(`${globalBin}/${args[0]}`)) {
+ pathArr.unshift(globalBin)
+ binExists = true
+ }
+
+ if (binExists)
+ return await _run()
+
+ packages.push(args[0])
+ }
+
+ // If we do `npm exec foo`, and have a `foo` locally, then we'll
+ // always use that, so we don't really need to fetch the manifest.
+ // So: run npa on each packages entry, and if it is a name with a
+ // rawSpec==='', then try to readPackageJson at
+ // node_modules/${name}/package.json, and only pacote fetch if
+ // that fails.
+ const manis = await Promise.all(packages.map(async p => {
+ const spec = npa(p, path)
+ if (spec.type === 'tag' && spec.rawSpec === '') {
+ // fall through to the pacote.manifest() approach
+ try {
+ const pj = resolve(path, 'node_modules', spec.name, 'package.json')
+ return await readPackageJson(pj)
+ } catch (er) {}
+ }
+ // Force preferOnline to true so we are making sure to pull in the latest
+ // This is especially useful if the user didn't give us a version, and
+ // they expect to be running @latest
+ return await pacote.manifest(p, {
+ ...flatOptions,
+ preferOnline: true,
+ })
+ }))
+
+ if (needPackageCommandSwap)
+ args[0] = getBinFromManifest(manis[0])
+
+ // figure out whether we need to install stuff, or if local is fine
+ const localArb = new Arborist({
+ ...flatOptions,
+ path,
+ })
+ const tree = await localArb.loadActual()
+
+ // do we have all the packages in manifest list?
+ const needInstall =
+ manis.some(manifest => manifestMissing({ tree, manifest }))
+
+ if (needInstall) {
+ const { cache } = flatOptions
+ const installDir = cacheInstallDir({ cache, packages })
+ await mkdirp(installDir)
+ const arb = new Arborist({
+ ...flatOptions,
+ path: installDir,
+ })
+ const tree = await arb.loadActual()
+
+ // at this point, we have to ensure that we get the exact same
+ // version, because it's something that has only ever been installed
+ // by npm exec in the cache install directory
+ const add = manis.filter(mani => manifestMissing({
+ tree,
+ manifest: {
+ ...mani,
+ _from: `${mani.name}@${mani.version}`,
+ },
+ }))
+ .map(mani => mani._from)
+ .sort((a, b) => a.localeCompare(b))
+
+ // no need to install if already present
+ if (add.length) {
+ if (!yes) {
+ // set -n to always say no
+ if (yes === false)
+ throw new Error('canceled')
+
+ if (noTTY() || ciDetect()) {
+ log.warn('exec', `The following package${
+ add.length === 1 ? ' was' : 's were'
+ } not found and will be installed: ${
+ add.map((pkg) => pkg.replace(/@$/, '')).join(', ')
+ }`)
+ } else {
+ const addList = add.map(a => ` ${a.replace(/@$/, '')}`)
+ .join('\n') + '\n'
+ const prompt = `Need to install the following packages:\n${
+ addList
+ }Ok to proceed? `
+ const confirm = await read({ prompt, default: 'y' })
+ if (confirm.trim().toLowerCase().charAt(0) !== 'y')
+ throw new Error('canceled')
+ }
+ }
+ await arb.reify({
+ ...flatOptions,
+ add,
+ })
+ }
+ pathArr.unshift(resolve(installDir, 'node_modules/.bin'))
+ }
+
+ return await _run()
+}
+
+module.exports = exec