Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions workspaces/libnpmexec/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@ const withLock = require('./with-lock.js')
const manifests = new Map()

const getManifest = async (spec, flatOptions) => {
const loadManifest = () => pacote.manifest(spec, {
...flatOptions,
preferOnline: true,
Arborist,
_isRoot: true,
})

if (spec.type === 'directory') {
return loadManifest()
}

if (!manifests.has(spec.raw)) {
const manifest = await pacote.manifest(spec, {
...flatOptions,
preferOnline: true,
Arborist,
_isRoot: true,
})
manifests.set(spec.raw, manifest)
manifests.set(spec.raw, await loadManifest())
}
return manifests.get(spec.raw)
}
Expand Down Expand Up @@ -247,16 +252,19 @@ const exec = async (opts) => {
if (!npxCache) {
throw new Error('Must provide a valid npxCache path')
}
const npxCacheKeyPackages = await Promise.all(packages.map(async p => {
// Keeps the npx directory unique to the resolved directory, not the
// potentially relative one (i.e. "npx ."). Directory specs also include
// their bin map so newly added local bins do not reuse stale shims.
const spec = npa(p)
if (spec.type === 'directory') {
const manifest = await getManifest(spec, flatOptions)
return `${spec.fetchSpec}\n${JSON.stringify(manifest.bin)}`
}
return p
}))
const hash = crypto.createHash('sha512')
.update(packages.map(p => {
// Keeps the npx directory unique to the resolved directory, not the
// potentially relative one (i.e. "npx .")
const spec = npa(p)
if (spec.type === 'directory') {
return spec.fetchSpec
}
return p
}).sort((a, b) => a.localeCompare(b, 'en')).join('\n'))
.update(npxCacheKeyPackages.sort((a, b) => a.localeCompare(b, 'en')).join('\n'))
.digest('hex')
.slice(0, 16)
const installDir = resolve(npxCache, hash)
Expand Down
36 changes: 36 additions & 0 deletions workspaces/libnpmexec/test/local.js
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,42 @@ for (const allowDirectory of ['none', 'root']) {
})
}

t.test('local package bin changes reinstall the npx cache entry', async t => {
const { pkg, fixtures } = createPkg({
version: '1.0.0',
name: '@npmcli/local-pkg-bin-change-test',
bin: {
a: 'a.js',
},
files: {
'a.js': { key: 'local-bin-a', value: 'A' },
},
})

const { exec, chmod, readOutput, path } = setup(t, {
pkg,
testdir: fixtures.packages['@npmcli-local-pkg-bin-change-test-1.0.0'],
})

await chmod('a.js')
await exec({ args: ['a'] })
t.match(await readOutput('local-bin-a'), {
value: 'A',
})

await fs.writeFile(resolve(path, 'b.js'), '#!/usr/bin/env node\nrequire("fs").writeFileSync("output-local-bin-b", JSON.stringify({ value: "B", args: process.argv.slice(2) }))')
await chmod('b.js')
const packageJson = JSON.parse(await fs.readFile(resolve(path, 'package.json'), 'utf8'))
packageJson.bin.b = 'b.js'
await fs.writeFile(resolve(path, 'package.json'), JSON.stringify(packageJson, null, 2))

await exec({ args: ['b', 'argument-b'] })
t.match(await readOutput('local-bin-b'), {
value: 'B',
args: ['argument-b'],
})
})

t.test('npm exec sequential workspace runs with same-named local bins', async t => {
t.plan(2)
const { path, readOutput, rmOutput, registry } = setup(t, {
Expand Down
Loading