Skip to content

Commit

Permalink
fix(exec): npx to run specified version if multiple version exist glo…
Browse files Browse the repository at this point in the history
…bally (#7587)

When multiple version of the same package is exist globally either at
top level or at any level as a sub dependency, even though the version
specified does not exist at top level it was running top level bin since
it matches the bin name.
This fixes checks for depth of the found node along with already
existing specs checks.

Fixes: #7486
  • Loading branch information
milaninfy authored Jun 14, 2024
1 parent 93883bb commit 71c6848
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
8 changes: 6 additions & 2 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, shallow }) => {
// 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 a top level node
if (shallow && node.depth) {
continue
}
if (spec.rawSpec === '*') {
return { node }
}
Expand Down Expand Up @@ -202,7 +206,7 @@ const exec = async (opts) => {
const globalArb = new Arborist({ ...flatOptions, path: globalPath, global: true })
const globalTree = await globalArb.loadActual()
const { manifest: globalManifest } =
await missingFromTree({ spec, tree: globalTree, flatOptions })
await missingFromTree({ spec, tree: globalTree, flatOptions, shallow: true })
if (!globalManifest && 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/A@2.0.0'],
})

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/A@1.0.0'],
})

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 71c6848

Please sign in to comment.