Skip to content

Commit 27cc108

Browse files
authored
feat: add binPaths param (#99)
1 parent 2d179d6 commit 27cc108

File tree

6 files changed

+75
-65
lines changed

6 files changed

+75
-65
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ runScript({
1717
// required, the folder where the package lives
1818
path: '/path/to/package/folder',
1919

20+
// optional, these paths will be put at the beginning of `$PATH`, even
21+
// after run-script adds the node_modules/.bin folder(s) from
22+
// `process.cwd()`. This is for commands like `npm init`, `npm exec`,
23+
// and `npx` to make sure manually installed packages come before
24+
// anything that happens to be in the tree in `process.cwd()`.
25+
binPaths: [
26+
'/path/to/npx/node_modules/.bin',
27+
'/path/to/npm/prefix/node_modules/.bin',
28+
]
29+
2030
// optional, defaults to /bin/sh on unix, or cmd.exe on windows
2131
scriptShell: '/bin/bash',
2232

lib/make-spawn-args.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ const makeSpawnArgs = options => {
2020
event,
2121
path,
2222
scriptShell = isWindows ? process.env.ComSpec || 'cmd' : 'sh',
23+
binPaths,
2324
env = {},
2425
stdio,
2526
cmd,
2627
args = [],
2728
stdioString = false,
2829
} = options
2930

30-
const spawnEnv = setPATH(path, {
31+
const spawnEnv = setPATH(path, binPaths, {
3132
// we need to at least save the PATH environment var
3233
...process.env,
3334
...env,
@@ -100,7 +101,9 @@ const makeSpawnArgs = options => {
100101
// delete the script, this is just a best effort
101102
try {
102103
unlink(scriptFile)
103-
} catch (err) {}
104+
} catch (err) {
105+
// ignore errors
106+
}
104107
}
105108

106109
return [scriptShell, spawnArgs, spawnOpts, cleanup]

lib/run-script-pkg.js

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const runScriptPkg = async options => {
1414
event,
1515
path,
1616
scriptShell,
17+
binPaths = false,
1718
env = {},
1819
stdio = 'pipe',
1920
pkg,
@@ -58,6 +59,7 @@ const runScriptPkg = async options => {
5859
event,
5960
path,
6061
scriptShell,
62+
binPaths,
6163
env: packageEnvs(env, pkg),
6264
stdio,
6365
cmd,

lib/set-path.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
const { resolve, dirname } = require('path')
2-
const isWindows = require('./is-windows.js')
1+
const { resolve, dirname, delimiter } = require('path')
32
// the path here is relative, even though it does not need to be
43
// in order to make the posix tests pass in windows
54
const nodeGypPath = resolve(__dirname, '../lib/node-gyp-bin')
65

76
// Windows typically calls its PATH environ 'Path', but this is not
87
// guaranteed, nor is it guaranteed to be the only one. Merge them
98
// all together in the order they appear in the object.
10-
const setPATH = (projectPath, env) => {
11-
// not require('path').delimiter, because we fake this for testing
12-
const delimiter = isWindows ? ';' : ':'
9+
const setPATH = (projectPath, binPaths, env) => {
1310
const PATH = Object.keys(env).filter(p => /^path$/i.test(p) && env[p])
1411
.map(p => env[p].split(delimiter))
1512
.reduce((set, p) => set.concat(p.filter(concatted => !set.includes(concatted))), [])
1613
.join(delimiter)
1714

1815
const pathArr = []
16+
if (binPaths) {
17+
pathArr.push(...binPaths)
18+
}
1919
// unshift the ./node_modules/.bin from every folder
2020
// walk up until dirname() does nothing, at the root
21-
// XXX should we specify a cwd that we don't go above?
21+
// XXX we should specify a cwd that we don't go above
2222
let p = projectPath
2323
let pp
2424
do {

test/run-script-pkg.js

+11
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ t.test('pkg has server.js, start not specified', async t => {
6060
path,
6161
scriptShell: 'sh',
6262
args: [],
63+
binPaths: false,
6364
env: {
6465
environ: 'value',
6566
},
@@ -83,6 +84,7 @@ t.test('pkg has server.js, start not specified, with args', async t => {
8384
environ: 'value',
8485
},
8586
args: ['a', 'b', 'c'],
87+
binPaths: false,
8688
stdio: 'pipe',
8789
pkg: {
8890
@@ -100,6 +102,7 @@ t.test('pkg has server.js, start not specified, with args', async t => {
100102
stdio: 'pipe',
101103
cmd: 'node server.js',
102104
args: ['a', 'b', 'c'],
105+
binPaths: false,
103106
}, {
104107
event: 'start',
105108
script: 'node server.js',
@@ -132,6 +135,7 @@ t.test('pkg has no foo script, but custom cmd provided', t => runScriptPkg({
132135
path: 'path',
133136
scriptShell: 'sh',
134137
args: [],
138+
binPaths: false,
135139
env: {
136140
environ: 'value',
137141
},
@@ -168,6 +172,7 @@ t.test('do the banner when stdio is inherited, handle line breaks', t => {
168172
path: 'path',
169173
scriptShell: 'sh',
170174
args: [],
175+
binPaths: false,
171176
env: {
172177
environ: 'value',
173178
},
@@ -206,6 +211,7 @@ t.test('do not show banner when stdio is inherited, if suppressed', t => {
206211
path: 'path',
207212
scriptShell: 'sh',
208213
args: [],
214+
binPaths: false,
209215
env: {
210216
environ: 'value',
211217
},
@@ -242,6 +248,7 @@ t.test('do the banner with no pkgid', t => {
242248
path: 'path',
243249
scriptShell: 'sh',
244250
args: [],
251+
binPaths: false,
245252
env: {
246253
environ: 'value',
247254
},
@@ -275,6 +282,7 @@ t.test('pkg has foo script', t => runScriptPkg({
275282
path: 'path',
276283
scriptShell: 'sh',
277284
args: [],
285+
binPaths: false,
278286
env: {
279287
environ: 'value',
280288
},
@@ -302,12 +310,14 @@ t.test('pkg has foo script, with args', t => runScriptPkg({
302310
},
303311
},
304312
args: ['a', 'b', 'c'],
313+
binPaths: false,
305314
}).then(res => t.strictSame(res, ['sh', ['-c', 'bar'], {
306315
stdioString: false,
307316
event: 'foo',
308317
path: 'path',
309318
scriptShell: 'sh',
310319
args: ['a', 'b', 'c'],
320+
binPaths: false,
311321
env: {
312322
environ: 'value',
313323
},
@@ -346,6 +356,7 @@ t.test('pkg has no install or preinstall script, but node-gyp files are present'
346356
path: 'path',
347357
scriptShell: 'sh',
348358
args: [],
359+
binPaths: false,
349360
env: { environ: 'value' },
350361
stdio: 'pipe',
351362
cmd: 'node-gyp rebuild',

test/set-path.js

+41-57
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,53 @@
11
const t = require('tap')
2-
const requireInject = require('require-inject')
3-
const isWindows = require('../lib/is-windows.js')
2+
const { resolve, delimiter } = require('path').posix
43

5-
if (!process.env.__FAKE_TESTING_PLATFORM__) {
6-
const fake = isWindows ? 'posix' : 'win32'
7-
t.spawn(process.execPath, [__filename, fake], { env: {
8-
...process.env,
9-
__FAKE_TESTING_PLATFORM__: fake,
10-
} })
11-
}
4+
const setPATH = t.mock('../lib/set-path.js', {
5+
// Always use posix path functions so tests are consistent
6+
path: require('path').posix,
7+
})
128

13-
if (isWindows) {
14-
const setPATH = requireInject('../lib/set-path.js', {
15-
path: require('path').win32,
16-
})
17-
const expect = [
18-
'c:\\x\\y\\z\\node_modules\\a\\node_modules\\b\\node_modules\\.bin',
19-
'c:\\x\\y\\z\\node_modules\\a\\node_modules\\node_modules\\.bin',
20-
'c:\\x\\y\\z\\node_modules\\a\\node_modules\\.bin',
21-
'c:\\x\\y\\z\\node_modules\\node_modules\\.bin',
22-
'c:\\x\\y\\z\\node_modules\\.bin',
23-
'c:\\x\\y\\node_modules\\.bin',
24-
'c:\\x\\node_modules\\.bin',
25-
'c:\\node_modules\\.bin',
26-
require('path').win32.resolve(__dirname, '../lib/node-gyp-bin'),
27-
'c:\\usr\\local\\bin',
28-
'c:\\usr\\local\\sbin',
29-
'c:\\usr\\bin',
30-
'c:\\usr\\sbin',
31-
'c:\\bin',
32-
'c:\\sbin',
33-
].join(';')
34-
t.strictSame(setPATH('c:\\x\\y\\z\\node_modules\\a\\node_modules\\b', {
9+
const paths = [
10+
'/x/y/z/node_modules/a/node_modules/b/node_modules/.bin',
11+
'/x/y/z/node_modules/a/node_modules/node_modules/.bin',
12+
'/x/y/z/node_modules/a/node_modules/.bin',
13+
'/x/y/z/node_modules/node_modules/.bin',
14+
'/x/y/z/node_modules/.bin',
15+
'/x/y/node_modules/.bin',
16+
'/x/node_modules/.bin',
17+
'/node_modules/.bin',
18+
resolve(__dirname, '../lib/node-gyp-bin'),
19+
'/usr/local/bin',
20+
'/usr/local/sbin',
21+
'/usr/bin',
22+
'/usr/sbin',
23+
'/bin',
24+
'/sbin',
25+
]
26+
t.test('no binPaths', async t => {
27+
const projectPath = '/x/y/z/node_modules/a/node_modules/b'
28+
t.strictSame(setPATH(projectPath, false, {
3529
foo: 'bar',
36-
PATH: 'c:\\usr\\local\\bin;c:\\usr\\local\\sbin',
37-
Path: 'c:\\usr\\local\\bin;c:\\usr\\bin;c:\\usr\\sbin;c:\\bin;c:\\sbin',
30+
PATH: '/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin',
3831
}), {
3932
foo: 'bar',
40-
PATH: expect,
41-
Path: expect,
33+
PATH: paths.join(delimiter),
4234
})
43-
} else {
44-
const setPATH = requireInject('../lib/set-path.js', {
45-
path: require('path').posix,
46-
})
47-
t.strictSame(setPATH('/x/y/z/node_modules/a/node_modules/b', {
35+
})
36+
37+
t.test('binPaths end up at beginning of PATH', async t => {
38+
const projectPath = '/x/y/z/node_modules/a/node_modules/b'
39+
const binPaths = [
40+
'/q/r/s/node_modules/.bin',
41+
'/t/u/v/node_modules/.bin',
42+
]
43+
t.strictSame(setPATH(projectPath, binPaths, {
4844
foo: 'bar',
4945
PATH: '/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin',
5046
}), {
5147
foo: 'bar',
52-
PATH:
53-
'/x/y/z/node_modules/a/node_modules/b/node_modules/.bin:' +
54-
'/x/y/z/node_modules/a/node_modules/node_modules/.bin:' +
55-
'/x/y/z/node_modules/a/node_modules/.bin:' +
56-
'/x/y/z/node_modules/node_modules/.bin:' +
57-
'/x/y/z/node_modules/.bin:' +
58-
'/x/y/node_modules/.bin:' +
59-
'/x/node_modules/.bin:' +
60-
'/node_modules/.bin:' +
61-
require('path').posix.resolve(__dirname, '../lib/node-gyp-bin') + ':' +
62-
'/usr/local/bin:' +
63-
'/usr/local/sbin:' +
64-
'/usr/bin:' +
65-
'/usr/sbin:' +
66-
'/bin:' +
67-
'/sbin',
48+
PATH: [
49+
...binPaths,
50+
...paths,
51+
].join(delimiter),
6852
})
69-
}
53+
})

0 commit comments

Comments
 (0)