Skip to content
This repository has been archived by the owner on Dec 6, 2023. It is now read-only.

[WIP] Alias support #8

Merged
merged 17 commits into from
Jul 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 75 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@ var name = require('./package.json').name;

var VERSION_REGEX = /(\w+)-(.+)/;

/**
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment appears inaccurate; if not present, it seems to be a rejected promise.

* Get the system version of Node, if present.
* Otherwise, return a rejected Promise.
*/
var getSystemNode = function() {
return new Promise(function(resolve, reject) {
var stdout, stderr;
var cmd = child.spawn(process.env.SHELL,
['-c', 'source $NVM_DIR/nvm.sh; nvm run --silent system --version;']);

cmd.stdout.pipe(concat(function(data) {
stdout = data;
}));

cmd.stderr.pipe(concat(function(data) {
stderr = data;
}));

cmd.on('close', function(code) {
if (code === 0) { resolve({ stdout: stdout, stderr: stderr }); }
else {
reject(new Error('Could not find system version of node.'));
}
});
});
};

/**
* Run an nvm command.
*
Expand All @@ -35,7 +62,7 @@ var nvmCommand = function(command) {
if (code === 0) { resolve({ stdout: stdout, stderr: stderr }); }
else {
reject(new Error(util.format('nvm exited with status: %d\n%s',
code, stdout.toString().trim() + stderr.toString().trim())));
code, String(stdout).trim() + String(stderr).trim())));
}
});
});
Expand All @@ -50,7 +77,7 @@ var nvmCommand = function(command) {
* @return {Array.<String>}
*/
var parseVersions = function(output) {
var string = output.stdout.toString()
var string = String(output.stdout)
.replace(/\x1b[^m]*m/g, '')
.replace(/^->/gm, '');
return string.split('\n')
Expand Down Expand Up @@ -105,6 +132,10 @@ var versionNumber = function(version) {
* @return {String}
*/
var findVersion = function(versions, matching) {
if (matching === 'system') {
return matching;
}

var highestMatch = null;

var mName = versionName(matching);
Expand All @@ -124,6 +155,26 @@ var findVersion = function(versions, matching) {
return highestMatch;
};

/**
* Parse the results of the nvm version call.
*
* @param {Promise} matching
*/
var parseMatching = function(matching) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String(x) is always preferable to x.toString()

return String(matching.stdout).trim();
};
/**
* Use nvm to resolve a version string (which could be a version number or an
alias) to an installed version number (or N/A).
*
* @param {String} matching
*/
var resolveVersion = function(matching) {
return Promise.resolve()
.then(function() { return nvmCommand('version "' + matching + '"'); })
.then(parseMatching);
};

/**
* Get installed version matching a given version.
*
Expand All @@ -132,9 +183,15 @@ var findVersion = function(versions, matching) {
*/
var installedVersion = function(matching) {
return Promise.resolve()
.then(function() { return listVersions(); })
.then(function(versions) {
return findVersion(versions, matching);
.then(function() {
return Promise.all([
resolveVersion(matching),
listVersions(),
]);
})
.spread(function(parsedVersion, versions) {
var parsedMatching = parsedVersion !== 'N/A' ? parsedVersion : matching;
return findVersion(versions, parsedMatching);
});
};

Expand All @@ -147,9 +204,20 @@ var installedVersion = function(matching) {
var match = function(version) {
return Promise.resolve()
.then(function() { return installedVersion(version); })
.then(function(use) {
.then(function(useVersion) {
return useVersion === 'system' ?
getSystemNode()
.then(parseMatching)
.then(function(use) {
return 'system: ' + use;
}) :
useVersion;
})
.then(function(useVersion) {
var use = (useVersion && useVersion.indexOf('system') === 0) ?
'system' : useVersion;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has the ability to conflict with an alias defined as something like system-x, doesn’t it?

Perhaps the then handler above that decides to get the system node should also update a variable define in the scope of the match function body to store that choice & use it here.

Copy link
Contributor Author

@duckontheweb duckontheweb Nov 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbyoung Actually, in this case useVersion will only ever start with system if the .nvmrc string is simply system, and not if it is something like system-x. The reason is that resolveVersion will resolve to an actual version number for aliases like system-x, which will then be propagated throughout the rest of the match method. I've added a test to check that this is the case in 42a4dbb.

var command = util.format('nvm use %s > /dev/null;', use);
var result = { version: use, command: command };
var result = { version: useVersion, command: command };
return use ? result :
Promise.reject(new Error('no version matching ' + version));
});
Expand Down
92 changes: 90 additions & 2 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,37 @@ var child = require('child_process');

describe('plugin', function() {

var getLTSVersion = 'source $NVM_DIR/nvm.sh; nvm version "lts/boron"';
var getSystemLikeAlias = 'source $NVM_DIR/nvm.sh; nvm version "system-alias"';
var getSystemVersion = 'source $NVM_DIR/nvm.sh; nvm run --silent system --version;';
var listNvmVersions = 'source $NVM_DIR/nvm.sh; nvm list';

beforeEach(function() {
var spawn = child.spawn;
sinon.stub(child, 'spawn', function(/*cmd, args*/) {
return spawn('echo', ['v0.7.12\n0.10.26\nv0.10.28\nv0.10.29\nv0.10.101\nv0.11.13']);

sinon.stub(child, 'spawn', function(cmd, args) {
var versionMatch = args[1].match(/source \$NVM_DIR\/nvm\.sh; nvm version "(v*\d+[\.\d]*)"/);

if (args[1] === getLTSVersion) {
// Mock return for an aliased version
return spawn('echo', ['v6.12.0']);
} else if (args[1] === getSystemLikeAlias) {
return spawn('echo', ['v6.9.5']);
} else if (args[1] === getSystemVersion) {
return spawn('echo', ['v0.12.7']);
} else if (versionMatch) {
// Mock return for a normal version numver
var version = versionMatch[1];
version = 'v' + version.replace('v', '');
return spawn('echo', [version]);
} else if (args[1] === listNvmVersions) {
// Mock the version list command
return spawn('echo', ['v0.7.12\n0.10.26\nv0.10.28\nv0.10.29\nv0.10.101\nv0.11.13\nv6.9.5\nv6.12.0']);
} else {
// Assume all other commands are nvm version "<uninstalled_version>"
return spawn('echo', ['N/A']);
}

});
});
afterEach(function() { child.spawn.restore(); });
Expand Down Expand Up @@ -117,4 +144,65 @@ describe('plugin', function() {
expect(plugin._findVersion(['iojs-v1.1.0', 'v0.12.0'], 'iojs-v1.1'))
.to.eql('iojs-v1.1.0');
});

it('finds aliased versions', function(done) {
plugin.match('lts/boron').then(function(result) {
expect(result).to.eql({
version: 'v6.12.0',
command: 'nvm use v6.12.0 > /dev/null;'
});
}).done(done);
});

it('finds system version', function(done) {
plugin.match('system').then(function(result) {
expect(result).to.eql({
version: 'system: v0.12.7',
command: 'nvm use system > /dev/null;'
});
}).done(done);
});

it('differentiates between aliases containing "system" and the system node', function (done) {
plugin.match('system-alias').then(function(result) {
expect(result).to.eql({
version: 'v6.9.5',
command: 'nvm use v6.9.5 > /dev/null;'
});
}).done(done);
});

it('returns rejected promise if system node is not present', function(done) {
child.spawn.restore();

var spawn = child.spawn;

sinon.stub(child, 'spawn', function(cmd, args) {
var versionMatch = args[1].match(/source \$NVM_DIR\/nvm\.sh; nvm version "(v*\d+[\.\d]*)"/);

if (args[1] === getLTSVersion) {
// Mock return for an aliased version
return spawn('echo', ['v6.12.0']);
} else if (args[1] === getSystemVersion) {
return spawn(process.env.SHELL, ['1>&2', 'echo', '/Users/my_user/.nvm/nvm-exec: line 15: exec: system: not found']);
} else if (versionMatch) {
// Mock return for a normal version numver
var version = versionMatch[1];
version = 'v' + version.replace('v', '');
return spawn('echo', [version]);
} else if (args[1] === listNvmVersions) {
// Mock the version list command
return spawn('echo', ['v0.7.12\n0.10.26\nv0.10.28\nv0.10.29\nv0.10.101\nv0.11.13\nv6.12.0']);
} else {
// Assume all other commands are nvm version "<uninstalled_version>"
return spawn('echo', ['N/A']);
}
});
plugin.match('system').then(
function() {
throw new Error('Plugin should have rejected bad command.');
},
function(e) {expect(e).to.match(/.*Error: Could not find system version of node.*/);}
).done(done);
});
});