Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: don't prompt on npm exec [directory] #5298

Merged
merged 1 commit into from
Aug 17, 2022
Merged
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
fix: don't prompt on npm exec [directory]
Local directories have to be "installed" so that their bins are linked
and set up and callable, the user shouldn't need to be prompted to do
that.  Note that this does NOT affect anything passed via the
`--package` param, because that may also contain non-directory specs so
the existing behavior needs to be preserved.  This is a small QOL
improvement for the isolated use case of "npm exec [directory]"

This also updates the hashing method used to come up with the `.npx`
directory to resolve the paths to packages first, so that `npm exec .`
in different directories don't share the same `.npx` directory.
wraithgar committed Aug 11, 2022

Verified

This commit was signed with the committer’s verified signature.
aymanbagabas Ayman Bagabas
commit b32be094b0048324a070717f2a819b7eee702a83
29 changes: 27 additions & 2 deletions workspaces/libnpmexec/lib/index.js
Original file line number Diff line number Diff line change
@@ -63,6 +63,9 @@ const missingFromTree = async ({ spec, tree, flatOptions }) => {
// non-registry spec, or a specific tag. Look up manifest and check
// resolved to see if it's in the tree.
const manifest = await getManifest(spec, flatOptions)
if (spec.type === 'directory') {
return { manifest }
}
const nodesByManifest = tree.inventory.query('packageName', manifest.name)
for (const node of nodesByManifest) {
if (node.package.resolved === manifest._resolved) {
@@ -89,10 +92,10 @@ const exec = async (opts) => {
path = '.',
runPath = '.',
scriptShell = isWindows ? process.env.ComSpec || 'cmd' : 'sh',
yes = undefined,
...flatOptions
} = opts

let yes = opts.yes
const run = () => runScript({
args,
call,
@@ -129,6 +132,16 @@ const exec = async (opts) => {
packages.push(args[0])
}

// Resolve any directory specs so that the npx directory is unique to the
// resolved directory, not the potentially relative one (i.e. "npx .")
for (const i in packages) {
const pkg = packages[i]
const spec = npa(pkg)
if (spec.type === 'directory') {
packages[i] = spec.fetchSpec
}
}

const localArb = new Arborist({ ...flatOptions, path })
const localTree = await localArb.loadActual()

@@ -153,6 +166,10 @@ const exec = async (opts) => {
if (needPackageCommandSwap) {
const spec = npa(args[0])

if (spec.type === 'directory') {
yes = true
}

args[0] = getBinFromManifest(commandManifest)

if (needInstall.length > 0 && globalPath) {
@@ -176,7 +193,15 @@ const exec = async (opts) => {
throw new Error('Must provide a valid npxCache path')
}
const hash = crypto.createHash('sha512')
.update(packages.sort((a, b) => a.localeCompare(b, 'en')).join('\n'))
.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'))
.digest('hex')
.slice(0, 16)
const installDir = resolve(npxCache, hash)
11 changes: 10 additions & 1 deletion workspaces/libnpmexec/test/index.js
Original file line number Diff line number Diff line change
@@ -482,7 +482,16 @@ require('fs').writeFileSync(process.argv.slice(2)[0], 'LOCAL PKG')`,
const executable = resolve(path, 'a/index.js')
fs.chmodSync(executable, 0o775)

await libexec({
const mockexec = t.mock('../lib/index.js', {
'@npmcli/ci-detect': () => true,
'proc-log': {
warn (title, msg) {
t.fail('should not warn about local file package install')
},
},
})

await mockexec({
...baseOpts,
args: [`file:${resolve(path, 'a')}`, 'resfile'],
cache,