diff --git a/lib/make-spawn-args.js b/lib/make-spawn-args.js index 1c9f02c..73ffbe4 100644 --- a/lib/make-spawn-args.js +++ b/lib/make-spawn-args.js @@ -37,7 +37,7 @@ const makeSpawnArgs = options => { npm_lifecycle_event: event, npm_lifecycle_script: cmd, npm_config_node_gyp, - }) + }, event) const spawnOpts = { env: spawnEnv, diff --git a/lib/set-path.js b/lib/set-path.js index c59c270..2730183 100644 --- a/lib/set-path.js +++ b/lib/set-path.js @@ -1,3 +1,4 @@ +const { log } = require('proc-log') const { resolve, dirname, delimiter } = require('path') // the path here is relative, even though it does not need to be // in order to make the posix tests pass in windows @@ -6,7 +7,7 @@ const nodeGypPath = resolve(__dirname, '../lib/node-gyp-bin') // Windows typically calls its PATH environ 'Path', but this is not // guaranteed, nor is it guaranteed to be the only one. Merge them // all together in the order they appear in the object. -const setPATH = (projectPath, binPaths, env) => { +const setPATH = (projectPath, binPaths, env, event) => { const PATH = Object.keys(env).filter(p => /^path$/i.test(p) && env[p]) .map(p => env[p].split(delimiter)) .reduce((set, p) => set.concat(p.filter(concatted => !set.includes(concatted))), []) @@ -14,6 +15,12 @@ const setPATH = (projectPath, binPaths, env) => { const pathArr = [] if (binPaths) { + for (const bin of binPaths) { + if (bin.includes(delimiter)) { + const context = event ? `"${event}" script` : 'script execution' + log.warn('run-script', `Project path contains delimiter ("${delimiter}"), ${context} may not behave as expected.`) + } + } pathArr.push(...binPaths) } // unshift the ./node_modules/.bin from every folder diff --git a/test/make-spawn-args.js b/test/make-spawn-args.js index 110f28f..17504af 100644 --- a/test/make-spawn-args.js +++ b/test/make-spawn-args.js @@ -141,4 +141,52 @@ t.test('spawn args', async t => { })) t.ok(spawk.done()) }) + + await t.test('binPaths containing delimiter logs warning', async t => { + const { delimiter } = require('path') + const { log } = require('proc-log') + const originalWarn = log.warn + let warningLogged = false + + log.warn = (category, message) => { + if (category === 'run-script' && message.includes('delimiter') && message.includes('"test" script')) { + warningLogged = true + } + } + spawk.spawn( + /.*/, + false, + e => (e.env.PATH || e.env.Path).includes(`/path/with${delimiter}delimiter`) + ) + await t.resolves(() => runScript({ + pkg, + binPaths: [`/path/with${delimiter}delimiter`], + path: testdir, + event: 'test', + })) + + t.ok(warningLogged, 'warning should be logged when binPath contains delimiter') + t.ok(spawk.done()) + log.warn = originalWarn + }) + + await t.test('binPaths containing delimiter logs generic warning without event', async t => { + const { delimiter } = require('path') + const { log } = require('proc-log') + const setPATH = require('../lib/set-path.js') + const originalWarn = log.warn + let warningLogged = false + + log.warn = (category, message) => { + if (category === 'run-script' && message.includes('delimiter') && message.includes('script execution')) { + warningLogged = true + } + } + + // Test setPATH directly with no event (simulates npx/exec scenario) + setPATH(testdir, [`/path/with${delimiter}delimiter`], {}, '') + + t.ok(warningLogged, 'warning should be logged with generic message when no event') + log.warn = originalWarn + }) })