Skip to content

Commit

Permalink
fix: npx global bin discovery with multiple version installed
Browse files Browse the repository at this point in the history
  • Loading branch information
milaninfy committed Jun 12, 2024
1 parent 93883bb commit afe4815
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 4 deletions.
14 changes: 10 additions & 4 deletions workspaces/libnpmexec/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const getManifest = async (spec, flatOptions) => {

// Returns the required manifest if the spec is missing from the tree
// Returns the found node if it is in the tree
const missingFromTree = async ({ spec, tree, flatOptions, isNpxTree }) => {
const missingFromTree = async ({ spec, tree, flatOptions, isNpxTree, depth }) => {
// If asking for a spec by name only (spec.raw === spec.name):
// - In local or global mode go with anything in the tree that matches
// - If looking in the npx cache check if a newer version is available
Expand All @@ -41,6 +41,10 @@ const missingFromTree = async ({ spec, tree, flatOptions, isNpxTree }) => {
// registry spec that is not a specific tag.
const nodesBySpec = tree.inventory.query('packageName', spec.name)
for (const node of nodesBySpec) {
// continue if node is not at specified depth, no need to check further
if (depth !== undefined && node.depth !== depth) {
continue
}
if (spec.rawSpec === '*') {
return { node }
}
Expand Down Expand Up @@ -201,9 +205,11 @@ const exec = async (opts) => {
// 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 { manifest: globalManifest } =
await missingFromTree({ spec, tree: globalTree, flatOptions })
if (!globalManifest && await fileExists(`${globalBin}/${args[0]}`)) {
const { node: globalNode } =
await missingFromTree({ spec, tree: globalTree, flatOptions, depth: 0 })

// if only found at top level
if (globalNode && await fileExists(`${globalBin}/${args[0]}`)) {
binPaths.push(globalBin)
return await run()
}
Expand Down
62 changes: 62 additions & 0 deletions workspaces/libnpmexec/test/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,65 @@ t.test('run multiple from registry', async t => {
value: 'packages-2.0.0',
})
})
t.test('packages with different versions in the global tree', async t => {
const pkgA1 = createPkg({
localVersion: '1.0.0',
versions: ['1.0.0', '2.0.0'],
name: '@npmcli/A',
})

const pkgA2 = createPkg({
localVersion: '2.0.0',
name: '@npmcli/A',
versions: ['1.0.0', '2.0.0'],
})

const pkgB = createPkg({
localVersion: '1.0.0',
name: '@npmcli/B',
})

const pkgBfix = merge(pkgB.fixtures, {
node_modules: {
'@npmcli': { B: {
node_modules: {
'@npmcli': {
A: pkgA2.fixtures.packages['@npmcli-A-2.0.0'],
} },
'package.json': { dependencies: { '@npmcli/A': '2.0.0' } },
},
},
} })

const { chmod, exec, readOutput, binLinks, registry, path } = setup(t, {
pkg: [pkgA2.pkg, pkgA1.pkg, pkgB.pkg],
global: true,
testdir: merge(pkgA1.fixtures, pkgBfix),
})

await chmod()
await binLinks()

await pkgA2.package({ registry, path, times: 2, tarballs: ['2.0.0'] })
await pkgA1.package({ registry, path, times: 1, tarballs: [] })

await exec({
args: ['@npmcli/[email protected]'],
})

t.match(await readOutput('@npmcli-A'), {
value: 'packages-2.0.0',
args: [],
created: 'packages/@npmcli-A-2.0.0/bin-file.js',
})

await exec({
args: ['@npmcli/[email protected]'],
})

t.match(await readOutput('@npmcli-A'), {
value: 'local-1.0.0',
args: [],
created: 'global/node_modules/@npmcli/A/bin-file.js',
})
})

0 comments on commit afe4815

Please sign in to comment.