Skip to content

Commit 8cfb372

Browse files
authored
Make it idempotent (#20)
1 parent 8e6e317 commit 8cfb372

File tree

2 files changed

+34
-9
lines changed

2 files changed

+34
-9
lines changed

index.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,34 @@ export const npmRunPath = ({
1212
} = {}) => {
1313
const cwdPath = path.resolve(toPath(cwd));
1414
const result = [];
15+
const pathParts = pathOption.split(path.delimiter);
1516

1617
if (preferLocal) {
17-
applyPreferLocal(result, cwdPath);
18+
applyPreferLocal(result, pathParts, cwdPath);
1819
}
1920

2021
if (addExecPath) {
21-
applyExecPath(result, execPath, cwdPath);
22+
applyExecPath(result, pathParts, execPath, cwdPath);
2223
}
2324

2425
return [...result, pathOption].join(path.delimiter);
2526
};
2627

27-
const applyPreferLocal = (result, cwdPath) => {
28+
const applyPreferLocal = (result, pathParts, cwdPath) => {
2829
for (const directory of traversePathUp(cwdPath)) {
29-
result.push(path.join(directory, 'node_modules/.bin'));
30+
const pathPart = path.join(directory, 'node_modules/.bin');
31+
if (!pathParts.includes(pathPart)) {
32+
result.push(pathPart);
33+
}
3034
}
3135
};
3236

3337
// Ensure the running `node` binary is used
34-
const applyExecPath = (result, execPath, cwdPath) => {
35-
result.push(path.resolve(cwdPath, toPath(execPath), '..'));
38+
const applyExecPath = (result, pathParts, execPath, cwdPath) => {
39+
const pathPart = path.resolve(cwdPath, toPath(execPath), '..');
40+
if (!pathParts.includes(pathPart)) {
41+
result.push(pathPart);
42+
}
3643
};
3744

3845
export const npmRunPathEnv = ({env = process.env, ...options} = {}) => {

test.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import {fileURLToPath, pathToFileURL} from 'node:url';
44
import test from 'ava';
55
import {npmRunPath, npmRunPathEnv} from './index.js';
66

7-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
7+
const localBinaryDirectory = fileURLToPath(new URL('node_modules/.bin', import.meta.url));
88

99
const testLocalDirectory = (t, addExecPath, preferLocal, expectedResult) => {
1010
t.is(
11-
npmRunPath({path: '', addExecPath, preferLocal}).split(path.delimiter)[0] === path.join(__dirname, 'node_modules/.bin'),
11+
npmRunPath({path: '', addExecPath, preferLocal}).split(path.delimiter)[0] === localBinaryDirectory,
1212
expectedResult,
1313
);
1414
};
@@ -20,7 +20,7 @@ test('"preferLocal: false", "addExecPath: false" does not add node_modules/.bin
2020

2121
const testLocalDirectoryEnv = (t, addExecPath, preferLocal, expectedResult) => {
2222
t.is(
23-
npmRunPathEnv({env: {PATH: 'foo'}, addExecPath, preferLocal}).PATH.split(path.delimiter)[0] === path.join(__dirname, 'node_modules/.bin'),
23+
npmRunPathEnv({env: {PATH: 'foo'}, addExecPath, preferLocal}).PATH.split(path.delimiter)[0] === localBinaryDirectory,
2424
expectedResult,
2525
);
2626
};
@@ -30,6 +30,15 @@ test('"addExecPath: false" still adds node_modules/.bin - npmRunPathEnv()', test
3030
test('"preferLocal: false" does not add node_modules/.bin - npmRunPathEnv()', testLocalDirectoryEnv, undefined, false, false);
3131
test('"preferLocal: false", "addExecPath: false" does not add node_modules/.bin - npmRunPathEnv()', testLocalDirectoryEnv, false, false, false);
3232

33+
test('node_modules/.bin is not added twice', t => {
34+
const firstPathEnv = npmRunPath({path: ''});
35+
const pathEnv = npmRunPath({path: firstPathEnv});
36+
const execPaths = pathEnv
37+
.split(path.delimiter)
38+
.filter(pathPart => pathPart === localBinaryDirectory);
39+
t.is(execPaths.length, 1);
40+
});
41+
3342
test('the `cwd` option changes the current directory', t => {
3443
t.is(
3544
npmRunPath({path: '', cwd: './dir'}).split(path.delimiter)[0],
@@ -49,6 +58,15 @@ test('push `execPath` later in the PATH', t => {
4958
t.is(pathEnv.at(-2), path.dirname(process.execPath));
5059
});
5160

61+
test('`execPath` is not added twice', t => {
62+
const firstPathEnv = npmRunPath({path: ''});
63+
const pathEnv = npmRunPath({path: firstPathEnv});
64+
const execPaths = pathEnv
65+
.split(path.delimiter)
66+
.filter(pathPart => pathPart === path.dirname(process.execPath));
67+
t.is(execPaths.length, 1);
68+
});
69+
5270
const testExecPath = (t, preferLocal, addExecPath, expectedResult) => {
5371
const pathEnv = npmRunPath({
5472
path: '',

0 commit comments

Comments
 (0)