diff --git a/.changeset/wet-cougars-invent.md b/.changeset/wet-cougars-invent.md new file mode 100644 index 0000000000..5085b3765f --- /dev/null +++ b/.changeset/wet-cougars-invent.md @@ -0,0 +1,8 @@ +--- +"@pnpm/global-bin-dir": patch +--- + +When looking for suitable directories for global executables, ignore case. + +When comparing to the currently running Node.js executable directory, +ignore any trailing slash. `/foo/bar` is the same as `/foo/bar/`. diff --git a/packages/global-bin-dir/src/index.ts b/packages/global-bin-dir/src/index.ts index a198a427e5..2c2a8739a5 100644 --- a/packages/global-bin-dir/src/index.ts +++ b/packages/global-bin-dir/src/index.ts @@ -16,12 +16,13 @@ function pickBestGlobalBinDir (dirs: string[]) { const nodeBinDir = path.dirname(process.execPath) const noWriteAccessDirs = [] as string[] for (const dir of dirs) { + const lowCaseDir = dir.toLowerCase() if ( - isUnderDir('node', dir) || - isUnderDir('nodejs', dir) || - isUnderDir('npm', dir) || - isUnderDir('pnpm', dir) || - nodeBinDir === dir + isUnderDir('node', lowCaseDir) || + isUnderDir('nodejs', lowCaseDir) || + isUnderDir('npm', lowCaseDir) || + isUnderDir('pnpm', lowCaseDir) || + path.relative(nodeBinDir, dir) === '' ) { if (canWriteToDirAndExists(dir)) return dir noWriteAccessDirs.push(dir) diff --git a/packages/global-bin-dir/test/index.ts b/packages/global-bin-dir/test/index.ts index 90ac706e42..14f95ef0b1 100644 --- a/packages/global-bin-dir/test/index.ts +++ b/packages/global-bin-dir/test/index.ts @@ -26,6 +26,7 @@ const npmGlobalBin = makePath('home', 'z', '.npm') const pnpmGlobalBin = makePath('home', 'z', '.pnpm') const otherDir = makePath('some', 'dir') const currentExecDir = makePath('current', 'exec') +const dirWithTrailingSlash = `${makePath('current', 'slash')}${path.sep}` process.env[FAKE_PATH] = [ userGlobalBin, nodeGlobalBin, @@ -33,6 +34,7 @@ process.env[FAKE_PATH] = [ pnpmGlobalBin, otherDir, currentExecDir, + dirWithTrailingSlash, ].join(path.delimiter) test('prefer a directory that has "nodejs", "npm", or "pnpm" in the path', (t) => { @@ -66,6 +68,10 @@ test('prefer the directory of the currently executed nodejs command', (t) => { process.execPath = path.join(currentExecDir, 'n') canWriteToDir = (dir) => dir !== nodeGlobalBin && dir !== npmGlobalBin && dir !== pnpmGlobalBin t.equal(globalBinDir(), currentExecDir) + + process.execPath = path.join(dirWithTrailingSlash, 'n') + t.equal(globalBinDir(), dirWithTrailingSlash) + process.execPath = originalExecPath t.end() }) @@ -106,3 +112,15 @@ test('throw exception if PATH is not set', (t) => { process.env[FAKE_PATH] = pathEnv t.end() }) + +test('prefer a directory that has "Node" in the path', (t) => { + const capitalizedNodeGlobalBin = makePath('home', 'z', '.nvs', 'Node', '12.0.0', 'x64', 'bin') + const pathEnv = process.env[FAKE_PATH] + process.env[FAKE_PATH] = capitalizedNodeGlobalBin + + canWriteToDir = () => true + t.equal(globalBinDir(), capitalizedNodeGlobalBin) + + process.env[FAKE_PATH] = pathEnv + t.end() +})