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:
authorGar <gar+gh@danger.computer>2022-08-03 18:36:47 +0300
committerGitHub <noreply@github.com>2022-08-03 18:36:47 +0300
commit19a834610d154f36748536b27aed13bfdb5ee748 (patch)
treed8d40f8f8061d6b7813f20801028c3312cb74220 /workspaces
parent8233fca44321186c485964d26aa3c7c43eafff3d (diff)
fix: properly find and run global scoped packages (#5250)
Diffstat (limited to 'workspaces')
-rw-r--r--workspaces/libnpmexec/lib/index.js48
-rw-r--r--workspaces/libnpmexec/test/index.js45
2 files changed, 75 insertions, 18 deletions
diff --git a/workspaces/libnpmexec/lib/index.js b/workspaces/libnpmexec/lib/index.js
index 0c66a2836..fcc596d31 100644
--- a/workspaces/libnpmexec/lib/index.js
+++ b/workspaces/libnpmexec/lib/index.js
@@ -27,8 +27,16 @@ const binPaths = []
// spec.raw so we don't have to fetch again when we check npxCache
const manifests = new Map()
+const getManifest = async (spec, flatOptions) => {
+ if (!manifests.get(spec.raw)) {
+ const manifest = await pacote.manifest(spec, { ...flatOptions, preferOnline: true })
+ manifests.set(spec.raw, manifest)
+ }
+ return manifests.get(spec.raw)
+}
+
// Returns the required manifest if the spec is missing from the tree
-const missingFromTree = async ({ spec, tree, pacoteOpts }) => {
+const missingFromTree = async ({ spec, tree, flatOptions }) => {
if (spec.registry && (spec.rawSpec === '' || spec.type !== 'tag')) {
// registry spec that is not a specific tag.
const nodesBySpec = tree.inventory.query('packageName', spec.name)
@@ -48,17 +56,11 @@ const missingFromTree = async ({ spec, tree, pacoteOpts }) => {
}
}
}
- if (!manifests.get(spec.raw)) {
- manifests.set(spec.raw, await pacote.manifest(spec, pacoteOpts))
- }
- return manifests.get(spec.raw)
+ return await getManifest(spec, flatOptions)
} else {
// non-registry spec, or a specific tag. Look up manifest and check
// resolved to see if it's in the tree.
- if (!manifests.get(spec.raw)) {
- manifests.set(spec.raw, await pacote.manifest(spec, pacoteOpts))
- }
- const manifest = manifests.get(spec.raw)
+ const manifest = await getManifest(spec, flatOptions)
const nodesByManifest = tree.inventory.query('packageName', manifest.name)
for (const node of nodesByManifest) {
if (node.package.resolved === manifest._resolved) {
@@ -78,6 +80,7 @@ const exec = async (opts) => {
localBin = resolve('./node_modules/.bin'),
locationMsg = undefined,
globalBin = '',
+ globalPath = '',
output,
// dereference values because we manipulate it later
packages: [...packages] = [],
@@ -106,9 +109,9 @@ const exec = async (opts) => {
return run()
}
- const pacoteOpts = { ...flatOptions, perferOnline: true }
-
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.
if (needPackageCommandSwap) {
const dir = dirname(dirname(localBin))
const localBinPath = await localFileExists(dir, args[0], '/')
@@ -131,25 +134,34 @@ const exec = async (opts) => {
const needInstall = []
await Promise.all(packages.map(async pkg => {
const spec = npa(pkg, path)
- const manifest = await missingFromTree({ spec, tree: localTree, pacoteOpts })
+ const manifest = await missingFromTree({ spec, tree: localTree, flatOptions })
if (manifest) {
+ // Package does not exist in the local tree
needInstall.push({ spec, manifest })
}
}))
if (needPackageCommandSwap) {
// Either we have a scoped package or the bin of our package we inferred
- // from arg[0] is not identical to the package name
+ // from arg[0] might not be identical to the package name
+ const spec = npa(args[0])
let commandManifest
if (needInstall.length === 0) {
- commandManifest = await pacote.manifest(args[0], {
- ...flatOptions,
- preferOnline: true,
- })
+ commandManifest = await getManifest(spec, flatOptions)
} else {
commandManifest = needInstall[0].manifest
}
+
args[0] = getBinFromManifest(commandManifest)
+
+ // See if the package is installed globally, and run the translated bin
+ const globalArb = new Arborist({ ...flatOptions, path: globalPath, global: true })
+ const globalTree = await globalArb.loadActual()
+ const globalManifest = await missingFromTree({ spec, tree: globalTree, flatOptions })
+ if (!globalManifest) {
+ binPaths.push(globalBin)
+ return await run()
+ }
}
const add = []
@@ -171,7 +183,7 @@ const exec = async (opts) => {
})
const npxTree = await npxArb.loadActual()
await Promise.all(needInstall.map(async ({ spec }) => {
- const manifest = await missingFromTree({ spec, tree: npxTree, pacoteOpts })
+ const manifest = await missingFromTree({ spec, tree: npxTree, flatOptions })
if (manifest) {
// Manifest is not in npxCache, we need to install it there
if (!spec.registry) {
diff --git a/workspaces/libnpmexec/test/index.js b/workspaces/libnpmexec/test/index.js
index 0eae95910..236e4f508 100644
--- a/workspaces/libnpmexec/test/index.js
+++ b/workspaces/libnpmexec/test/index.js
@@ -485,6 +485,51 @@ t.test('global space pkg', async t => {
t.equal(res, 'GLOBAL PKG', 'should run local pkg bin script')
})
+t.test('global scoped pkg', async t => {
+ const pkg = {
+ name: '@ruyadorno/create-test',
+ bin: {
+ 'create-test': 'index.js',
+ },
+ }
+ const path = t.testdir({
+ cache: {},
+ npxCache: {},
+ global: {
+ node_modules: {
+ '.bin': {},
+ '@ruyadorno': {
+ 'create-test': {
+ 'index.js': `#!/usr/bin/env node
+ require('fs').writeFileSync(process.argv.slice(2)[0], 'GLOBAL PKG')`,
+ 'package.json': JSON.stringify(pkg),
+ },
+ },
+ },
+ },
+ })
+ const globalBin = resolve(path, 'global/node_modules/.bin')
+ const globalPath = resolve(path, 'global')
+ const runPath = path
+
+ await binLinks({
+ path: resolve(path, 'global/node_modules/@ruyadorno/create-test'),
+ pkg,
+ })
+
+ await libexec({
+ ...baseOpts,
+ args: ['@ruyadorno/create-test', 'resfile'],
+ globalBin,
+ globalPath,
+ path,
+ runPath,
+ })
+
+ const res = fs.readFileSync(resolve(path, 'resfile')).toString()
+ t.equal(res, 'GLOBAL PKG', 'should run global pkg bin script')
+})
+
t.test('run from registry - no local packages', async t => {
const testdir = t.testdir({
cache: {},