Skip to content

Commit 1ff4ac6

Browse files
authored
Add workaround for npm cmd shims (#109)
1 parent d4e96bd commit 1ff4ac6

File tree

9 files changed

+80
-12
lines changed

9 files changed

+80
-12
lines changed

.appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
version: "{build}"
22
environment:
3-
nodejs_version: 6
3+
nodejs_version: 10
44
nodejs_arch: x64
55
install:
66
- ps: Install-Product node $env:nodejs_version $env:nodejs_arch

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
node_modules/
12
deps/
23
test/temp/

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ os:
33
- osx
44
language: node_js
55
node_js:
6-
- "6"
6+
- "10"
77
addons:
88
apt:
99
packages:

lib/addRemove.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const NodeVersion = require('./version');
1515
let nvsDownload = null; // Delay-load
1616
let nvsExtract = null; // Delay-load
1717

18+
const isWindows = process.platform === 'win32';
19+
1820
/**
1921
* Downloads and extracts a version of node.
2022
*
@@ -163,6 +165,8 @@ function downloadAndExtractAsync(version) {
163165
}
164166
});
165167
}
168+
169+
return fixNpmCmdShimsAsync(targetDir);
166170
}).catch(e => {
167171
removeDirectoryIfEmpty(targetDir);
168172
removeDirectoryIfEmpty(path.dirname(targetDir));
@@ -272,6 +276,53 @@ function removeDirectoryIfEmpty(dir) {
272276
}
273277
}
274278

279+
/**
280+
* Fix Windows .cmd shims for npm executables, which are installed incorrectly
281+
* by Node.js and may block global npm package updates.
282+
*
283+
* @param {string} targetDir Directory where a node version was extracted.
284+
*/
285+
async function fixNpmCmdShimsAsync(targetDir) {
286+
if (!isWindows) return;
287+
288+
try {
289+
// This assumes the npm version carried with the node installation
290+
// includes a `cmd-shim` module. Currently true for at least npm >= 3.
291+
const cmdShimPath = path.join(
292+
targetDir, 'node_modules', 'npm', 'node_modules', 'cmd-shim');
293+
const cmdShim = require(cmdShimPath);
294+
295+
// Enumerate .cmd files in the target directory and fix if they are shims.
296+
for (let childName of fs.readdirSync(targetDir)) {
297+
if (path.extname(childName).toLowerCase() !== '.cmd') {
298+
continue;
299+
}
300+
301+
const cmdName = path.basename(childName, '.cmd');
302+
const shimPath = path.join(targetDir, cmdName);
303+
const jsCliPath = path.join(
304+
targetDir, 'node_modules', 'npm', 'bin', `${cmdName}-cli.js`);
305+
if (!fs.existsSync(jsCliPath)) {
306+
// A corresponding JS CLI file does not exist in the npm bin dir.
307+
// Probably this is some other .cmd file that isn't an npm shim.
308+
continue;
309+
}
310+
311+
// Found an npm shim. Call the cmd-shim module to fix it.
312+
await new Promise((resolve, reject) => {
313+
cmdShim(jsCliPath, shimPath, (e) => {
314+
if (e) reject(e);
315+
else resolve();
316+
});
317+
});
318+
}
319+
} catch (e) {
320+
// Not a fatal error. Most things still work if the shims are not fixed.
321+
// The only problem may be that the global npm package cannot be upgraded.
322+
console.warn('Warning: Failed to fix npm cmd shims: ' + e.message);
323+
}
324+
}
325+
275326
module.exports = {
276327
addAsync,
277328
remove,

test/cli/bashTests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const nvsRootDir = path.resolve(__dirname, '..', '..');
88
const testParentDir = path.resolve(__dirname, '..', 'temp');
99
const testDir = path.join(testParentDir, 'bash');
1010

11-
const testNodeVersion = '6.10.3';
11+
const testNodeVersion = '8.5.0';
1212

1313
test.before(t => {
1414
require('../fsUtil').createDirectoryIfNotFound(testParentDir);
@@ -26,7 +26,7 @@ test('Bash CLI', t => {
2626
const commands = [
2727
'echo $NVS_HOME',
2828
'. ./nvs.sh',
29-
'nvs lsr',
29+
'nvs lsr 8',
3030
'nvs add ' + testNodeVersion,
3131
'nvs link ' + testNodeVersion,
3232
'nvs use',

test/cli/cmdTests.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ const nvsRootDir = path.resolve(__dirname, '..', '..');
88
const testParentDir = path.resolve(__dirname, '..', 'temp');
99
const testDir = path.join(testParentDir, 'cmd');
1010

11-
const testNodeVersion = '6.10.3';
11+
const testNodeVersion = '8.5.0';
12+
const testNpmVersion = '6.4.1';
1213

1314
test.before(t => {
1415
require('../fsUtil').createDirectoryIfNotFound(testParentDir);
@@ -25,12 +26,14 @@ if (process.platform !== 'win32') {
2526
test('Command Prompt CLI', t => {
2627
const commands = [
2728
'echo %NVS_HOME%',
28-
'.\\nvs.cmd lsr',
29+
'.\\nvs.cmd lsr 8',
2930
'.\\nvs.cmd add ' + testNodeVersion,
3031
'.\\nvs.cmd link ' + testNodeVersion,
3132
'.\\nvs.cmd use',
3233
'echo !PATH!',
3334
'node -v',
35+
'npm install -g npm@' + testNpmVersion,
36+
'npm -v',
3437
'.\\nvs.cmd unlink',
3538
'rd /q /s %NVS_HOME%',
3639
];
@@ -56,4 +59,5 @@ test('Command Prompt CLI', t => {
5659
});
5760
const output = result.stdout.toString().trim().replace(/\r\n/g, '\n');
5861
t.regex(output, new RegExp('\n> node -v *\nv' + testNodeVersion + ' *\n', 'm'));
62+
t.regex(output, new RegExp('\n> npm -v *\n' + testNpmVersion + ' *\n', 'm'));
5963
});

test/cli/gitBashTests.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ const nvsRootDir = path.resolve(__dirname, '..', '..');
88
const testParentDir = path.resolve(__dirname, '..', 'temp');
99
const testDir = path.join(testParentDir, 'git-bash');
1010

11-
const testNodeVersion = '6.10.3';
11+
const testNodeVersion = '8.5.0';
12+
const testNpmVersion = '6.4.1';
1213

1314
test.before(t => {
1415
require('../fsUtil').createDirectoryIfNotFound(testParentDir);
@@ -26,12 +27,17 @@ test('Git Bash CLI', t => {
2627
const commands = [
2728
'echo $NVS_HOME',
2829
'. ./nvs.sh',
29-
'nvs lsr',
30+
'nvs lsr 8',
3031
'nvs add ' + testNodeVersion,
3132
'nvs link ' + testNodeVersion,
3233
'nvs use',
3334
'echo $PATH',
3435
'node -v',
36+
// `npm install -g npm` doesn't work in Git bash, because the `npm` script file
37+
// (being executed) is locked while the npm install process tries to update it.
38+
// The workaround is to run the command via cmd.exe outside of bash.
39+
`cmd "/C npm install -g npm@${testNpmVersion}"`,
40+
'npm -v',
3541
'nvs unlink',
3642
'rm -rf $NVS_HOME',
3743
];
@@ -55,4 +61,5 @@ test('Git Bash CLI', t => {
5561
});
5662
const output = result.stdout.toString().trim().replace(/\r\n/g, '\n');
5763
t.regex(output, new RegExp('\n> node -v *\nv' + testNodeVersion + ' *\n', 'm'));
64+
t.regex(output, new RegExp('\n> npm -v *\n' + testNpmVersion + ' *\n', 'm'));
5865
});

test/cli/psTests.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ const nvsRootDir = path.resolve(__dirname, '..', '..');
88
const testParentDir = path.resolve(__dirname, '..', 'temp');
99
const testDir = path.join(testParentDir, 'ps');
1010

11-
const testNodeVersion = '6.10.3';
11+
const testNodeVersion = '8.5.0';
12+
const testNpmVersion = '6.4.1';
1213

1314
test.before(t => {
1415
require('../fsUtil').createDirectoryIfNotFound(testParentDir);
@@ -25,12 +26,14 @@ if (process.platform !== 'win32') {
2526
test('PowerShell CLI', t => {
2627
const commands = [
2728
'echo $env:NVS_HOME',
28-
'.\\nvs.ps1 lsr',
29+
'.\\nvs.ps1 lsr 8',
2930
'.\\nvs.ps1 add ' + testNodeVersion,
3031
'.\\nvs.ps1 link ' + testNodeVersion,
3132
'.\\nvs.ps1 use',
3233
'echo $env:PATH',
3334
'node -v',
35+
'npm install -g npm@' + testNpmVersion,
36+
'npm -v',
3437
'.\\nvs.ps1 unlink',
3538
'Remove-Item -Recurse -Force $env:NVS_HOME',
3639
];
@@ -51,6 +54,8 @@ test('PowerShell CLI', t => {
5154
},
5255
cwd: nvsRootDir,
5356
});
57+
5458
const output = result.stdout.toString().trim().replace(/\r\n/g, '\n');
5559
t.regex(output, new RegExp('\n> node -v *\nv' + testNodeVersion + ' *\n', 'm'));
60+
t.regex(output, new RegExp('\n> npm -v *\n' + testNpmVersion + ' *\n', 'm'));
5661
});

test/cli/zshTests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const nvsRootDir = path.resolve(__dirname, '..', '..');
88
const testParentDir = path.resolve(__dirname, '..', 'temp');
99
const testDir = path.join(testParentDir, 'zsh');
1010

11-
const testNodeVersion = '6.10.3';
11+
const testNodeVersion = '8.5.0';
1212

1313
test.before(t => {
1414
require('../fsUtil').createDirectoryIfNotFound(testParentDir);
@@ -26,7 +26,7 @@ test('Zsh CLI', t => {
2626
const commands = [
2727
'echo $NVS_HOME',
2828
'. ./nvs.sh',
29-
'nvs lsr',
29+
'nvs lsr 8',
3030
'nvs add ' + testNodeVersion,
3131
'nvs link ' + testNodeVersion,
3232
'nvs use',

0 commit comments

Comments
 (0)