Skip to content

Commit

Permalink
fix: update npx cache if possible when spec is a range
Browse files Browse the repository at this point in the history
Closes: npm#7838
  • Loading branch information
wraithgar committed Feb 26, 2025
1 parent 247ee1d commit 8461186
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 2 deletions.
2 changes: 1 addition & 1 deletion lib/commands/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const jsonParse = require('json-parse-even-better-errors')
const localeCompare = require('@isaacs/string-locale-compare')('en')
const { log, output } = require('proc-log')
const PkgJson = require('@npmcli/package-json')
const BaseCommand = require('../base-cmd.js')
const abbrev = require('abbrev')
const BaseCommand = require('../base-cmd.js')

const searchCachePackage = async (path, parsed, cacheKeys) => {
const searchMFH = new RegExp(`^make-fetch-happen:request-cache:.*(?<!/[@a-zA-Z]+)/${parsed.name}/-/(${parsed.name}[^/]+.tgz)$`)
Expand Down
4 changes: 3 additions & 1 deletion workspaces/libnpmexec/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const missingFromTree = async ({ spec, tree, flatOptions, isNpxTree, shallow })
// - 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
const npxByNameOnly = isNpxTree && spec.name === spec.raw
// If they gave a range and not a tag we still need to check if it's outdated.
if (spec.registry && spec.type !== 'tag' && !npxByNameOnly) {
// registry spec that is not a specific tag.
const nodesBySpec = tree.inventory.query('packageName', spec.name)
Expand All @@ -54,7 +55,8 @@ const missingFromTree = async ({ spec, tree, flatOptions, isNpxTree, shallow })
return { node }
}
// package requested by version range, only remaining registry type
if (semver.satisfies(node.package.version, spec.rawSpec)) {
// the npx tree shouldn't be ok w/ an outdated version
if (!isNpxTree && semver.satisfies(node.package.version, spec.rawSpec)) {
return { node }
}
}
Expand Down
52 changes: 52 additions & 0 deletions workspaces/libnpmexec/test/registry.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { resolve } = require('node:path')
const t = require('tap')
const { setup, createPkg, merge } = require('./fixtures/setup.js')
const crypto = require('node:crypto')

t.test('run from registry - no local packages', async t => {
const { fixtures, package } = createPkg({ versions: ['2.0.0'] })
Expand Down Expand Up @@ -245,3 +246,54 @@ t.test('run from registry - non existant global path', async t => {
value: 'packages-2.0.0',
})
})

t.test('npx tree triggers manifest fetch when local version does satisfy range using real npx cache inventory', async t => {
// The local installation is version 1.0.0, which does NOT satisfy the spec ^2.0.0.
const pkgData = createPkg({
localVersion: '1.0.0',
versions: ['1.0.0', '2.0.0', '2.0.1'],
name: '@npmcli/create-index',
})
const { fixtures, package: pkg } = pkgData

const hash = crypto.createHash('sha512')
.update('@npmcli/create-index@^2.0.0')
.digest('hex')
.slice(0, 16)

const npxCacheFixture = {
[hash]: {
'package.json': {
name: '@npmcli/create-index',
version: '2.0.0',
},
},
}

const { exec: execFn, path, registry, readOutput, binLinks } = setup(t, {
pkg: [pkg],
testdir: {
...fixtures,
npxCache: npxCacheFixture,
},
})

// Set up the registry package so that a manifest fetch returns version 2.0.1.
await pkg({
registry,
path,
tarballs: ['2.0.1'],
})
await binLinks()

// Execute in NPX mode with the spec ^2.0.0.
// The local tree (version 1.0.0) does not satisfy ^2.0.0, so the system will find the cached package (version 2.0.0) in npxCache and then update from the registry to 2.0.1.
await execFn({
args: ['create-index'],
packages: ['@npmcli/create-index@^2.0.0'],
})

t.match(await readOutput('@npmcli-create-index'), {
value: 'packages-2.0.1',
})
})

0 comments on commit 8461186

Please sign in to comment.