Skip to content

Commit 81d6cee

Browse files
committed
fix: npm exec on folders missing package.json
This fixes running `npm exec` in folders that does not have a `package.json` file. Fixes: #1975 PR-URL: #2081 Credit: @ruyadorno Close: #2081 Reviewed-by: @nlf
1 parent 3990b42 commit 81d6cee

File tree

2 files changed

+45
-47
lines changed

2 files changed

+45
-47
lines changed

lib/exec.js

+40-43
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,41 @@ const PATH = require('./utils/path.js')
6565

6666
const cmd = (args, cb) => exec(args).then(() => cb()).catch(cb)
6767

68+
const run = async ({ args, call, pathArr }) => {
69+
// turn list of args into command string
70+
const script = call || args.map(escapeArg).join(' ').trim()
71+
72+
// do the fakey runScript dance
73+
// still should work if no package.json in cwd
74+
const realPkg = await readPackageJson(`${npm.localPrefix}/package.json`)
75+
.catch(() => ({}))
76+
const pkg = {
77+
...realPkg,
78+
scripts: {
79+
...(realPkg.scripts || {}),
80+
npx: script,
81+
},
82+
}
83+
84+
npm.log.disableProgress()
85+
try {
86+
return await runScript({
87+
pkg,
88+
banner: false,
89+
// we always run in cwd, not --prefix
90+
path: process.cwd(),
91+
stdioString: true,
92+
event: 'npx',
93+
env: {
94+
PATH: pathArr.join(delimiter),
95+
},
96+
stdio: 'inherit',
97+
})
98+
} finally {
99+
npm.log.enableProgress()
100+
}
101+
}
102+
68103
const exec = async args => {
69104
const { package: packages, call } = npm.flatOptions
70105

@@ -89,17 +124,10 @@ const exec = async args => {
89124
}
90125

91126
if (binExists) {
92-
return await runScript({
93-
cmd: [args[0], ...args.slice(1).map(escapeArg)].join(' ').trim(),
94-
banner: false,
95-
// we always run in cwd, not --prefix
96-
path: process.cwd(),
97-
stdioString: true,
98-
event: 'npx',
99-
env: {
100-
PATH: pathArr.join(delimiter),
101-
},
102-
stdio: 'inherit',
127+
return await run({
128+
args,
129+
call: [args[0], ...args.slice(1).map(escapeArg)].join(' ').trim(),
130+
pathArr,
103131
})
104132
}
105133

@@ -129,9 +157,6 @@ const exec = async args => {
129157
if (needPackageCommandSwap)
130158
args[0] = getBinFromManifest(manis[0])
131159

132-
// turn list of args into command string
133-
const script = call || args.map(escapeArg).join(' ').trim()
134-
135160
// figure out whether we need to install stuff, or if local is fine
136161
const localArb = new Arborist({
137162
...npm.flatOptions,
@@ -180,35 +205,7 @@ const exec = async args => {
180205
pathArr.unshift(resolve(installDir, 'node_modules/.bin'))
181206
}
182207

183-
// do the fakey runScript dance
184-
// still should work if no package.json in cwd
185-
const realPkg = await readPackageJson(`${npm.localPrefix}/package.json`)
186-
.catch(() => ({}))
187-
const pkg = {
188-
...realPkg,
189-
scripts: {
190-
...(realPkg.scripts || {}),
191-
npx: script,
192-
},
193-
}
194-
195-
npm.log.disableProgress()
196-
try {
197-
return await runScript({
198-
pkg,
199-
banner: false,
200-
// we always run in cwd, not --prefix
201-
path: process.cwd(),
202-
stdioString: true,
203-
event: 'npx',
204-
env: {
205-
PATH: pathArr.join(delimiter),
206-
},
207-
stdio: 'inherit',
208-
})
209-
} finally {
210-
npm.log.enableProgress()
211-
}
208+
return await run({ args, call, pathArr })
212209
}
213210

214211
const manifestMissing = (tree, mani) => {

test/lib/exec.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,16 @@ const PATH = require('../../lib/utils/path.js')
8282

8383
let CI_NAME = 'travis-ci'
8484

85-
const exec = requireInject('../../lib/exec.js', {
85+
const mocks = {
8686
'@npmcli/arborist': Arborist,
8787
'@npmcli/run-script': runScript,
8888
'@npmcli/ci-detect': () => CI_NAME,
8989
'../../lib/npm.js': npm,
9090
pacote,
9191
read,
9292
'mkdirp-infer-owner': mkdirp
93-
})
93+
}
94+
const exec = requireInject('../../lib/exec.js', mocks)
9495

9596
t.afterEach(cb => {
9697
MKDIRPS.length = 0
@@ -122,7 +123,7 @@ t.test('npx foo, bin already exists locally', async t => {
122123
t.ifError(er, 'npm exec')
123124
})
124125
t.strictSame(RUN_SCRIPTS, [{
125-
cmd: 'foo',
126+
pkg: { scripts: { npx: 'foo' }},
126127
banner: false,
127128
path: process.cwd(),
128129
stdioString: true,
@@ -146,7 +147,7 @@ t.test('npx foo, bin already exists globally', async t => {
146147
t.ifError(er, 'npm exec')
147148
})
148149
t.strictSame(RUN_SCRIPTS, [{
149-
cmd: 'foo',
150+
pkg: { scripts: { npx: 'foo' }},
150151
banner: false,
151152
path: process.cwd(),
152153
stdioString: true,

0 commit comments

Comments
 (0)