diff --git a/workspaces/libnpmexec/lib/index.js b/workspaces/libnpmexec/lib/index.js index 7a224ab7f0a62..575f8e6ea4a5e 100644 --- a/workspaces/libnpmexec/lib/index.js +++ b/workspaces/libnpmexec/lib/index.js @@ -79,7 +79,6 @@ const exec = async (opts) => { const { args = [], call = '', - color = false, localBin = resolve('./node_modules/.bin'), locationMsg = undefined, globalBin = '', @@ -97,7 +96,6 @@ const exec = async (opts) => { const run = () => runScript({ args, call, - color, flatOptions, locationMsg, output, @@ -114,20 +112,36 @@ const exec = async (opts) => { const needPackageCommandSwap = (args.length > 0) && (packages.length === 0) // If they asked for a command w/o specifying a package, see if there is a - // bin that directly matches that name either globally or in the local tree. + // bin that directly matches that name: + // - in the local package itself + // - in the local tree + // - globally if (needPackageCommandSwap) { - const dir = dirname(dirname(localBin)) - const localBinPath = await localFileExists(dir, args[0], '/') - if (localBinPath) { - binPaths.push(localBinPath) - return await run() - } else if (globalPath && await fileExists(`${globalBin}/${args[0]}`)) { - binPaths.push(globalBin) - return await run() + let localManifest + try { + localManifest = await pacote.manifest(path, flatOptions) + } catch { + // no local package.json? no problem, move one. + } + if (localManifest?.bin?.[args[0]]) { + // we have to install the local package into the npx cache so that its + // bin links get set up + packages.push(path) + yes = true + flatOptions.installLinks = false + } else { + const dir = dirname(dirname(localBin)) + const localBinPath = await localFileExists(dir, args[0], '/') + if (localBinPath) { + binPaths.push(localBinPath) + return await run() + } else if (globalPath && await fileExists(`${globalBin}/${args[0]}`)) { + binPaths.push(globalBin) + return await run() + } + // We swap out args[0] with the bin from the manifest later + packages.push(args[0]) } - - // We swap out args[0] with the bin from the manifest later - packages.push(args[0]) } // Resolve any directory specs so that the npx directory is unique to the diff --git a/workspaces/libnpmexec/lib/run-script.js b/workspaces/libnpmexec/lib/run-script.js index b34895c824e6d..2f7b258345faa 100644 --- a/workspaces/libnpmexec/lib/run-script.js +++ b/workspaces/libnpmexec/lib/run-script.js @@ -15,7 +15,6 @@ const nocolor = { const run = async ({ args, call, - color, flatOptions, locationMsg, output = () => {}, @@ -26,6 +25,7 @@ const run = async ({ }) => { // turn list of args into command string const script = call || args.shift() || scriptShell + const color = !!flatOptions.color const colorize = color ? chalk : nocolor // do the fakey runScript dance diff --git a/workspaces/libnpmexec/test/index.js b/workspaces/libnpmexec/test/index.js index 8d34118296200..739a686aba8c3 100644 --- a/workspaces/libnpmexec/test/index.js +++ b/workspaces/libnpmexec/test/index.js @@ -24,39 +24,31 @@ const baseOpts = { yes: true, } -t.test('local pkg', async t => { +t.test('bin in local pkg', async t => { const pkg = { - name: 'pkg', + name: '@npmcli/local-pkg-bin-test', bin: { - a: 'index.js', + a: 'local-bin-test.js', }, } const path = t.testdir({ cache: {}, npxCache: {}, - node_modules: { - '.bin': {}, - a: { - 'index.js': `#!/usr/bin/env node + 'local-bin-test.js': `#!/usr/bin/env node require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`, - }, - }, 'package.json': JSON.stringify(pkg), }) const localBin = resolve(path, 'node_modules/.bin') const runPath = path + const npxCache = resolve(path, 'npxCache') - const executable = resolve(path, 'node_modules/a') + const executable = resolve(path, 'local-bin-test.js') fs.chmodSync(executable, 0o775) - await binLinks({ - path: resolve(path, 'node_modules/a'), - pkg, - }) - await libexec({ ...baseOpts, args: ['a', 'resfile'], + npxCache, localBin, path, runPath, diff --git a/workspaces/libnpmexec/test/run-script.js b/workspaces/libnpmexec/test/run-script.js index 40b31ebdf2b19..4b1cafa0dec73 100644 --- a/workspaces/libnpmexec/test/run-script.js +++ b/workspaces/libnpmexec/test/run-script.js @@ -4,6 +4,7 @@ const baseOpts = { args: [], call: '', color: false, + flatOptions: {}, path: '', runPath: '', shell: process.platform === 'win32' @@ -73,7 +74,7 @@ t.test('colorized interactive mode msg', async t => { OUTPUT.push(msg) }, runPath: '/foo/', - color: true, + flatOptions: { color: true }, }) t.matchSnapshot(OUTPUT.join('\n'), 'should print colorized output') })