Skip to content

Commit

Permalink
Version listing improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
jasongin committed Nov 15, 2016
1 parent 0cb137f commit 044a7cd
Show file tree
Hide file tree
Showing 27 changed files with 845 additions and 574 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@ Command | Description
`nvs add <version>` | Download and extract a node version
`nvs rm <version>` | Remove a node version
`nvs migrate <fromver> [tover]` | Migrate global modules
`nvs use <version>` | Use a node version in the current shell
`nvs use [version]` | Use a node version in the current shell
`nvs auto [on/off]` | Automatically switch based on cwd
`nvs run <ver> <js> [args...]` | Run a script using a node version
`nvs exec <ver> <exe> [args...]` | Run an executable using a node version
`nvs which [version]` | Show the path to a node version binary
`nvs ls` | List local node versions
`nvs ls-remote [remote]` | List node versions available to download
`nvs ls [filter]` | List local node versions
`nvs ls-remote [filter]` | List node versions available to download
`nvs link [version]` | Link a version as the default
`nvs unlink` | Remove links to a default version
`nvs unlink [version]` | Remove links to a default version
`nvs alias [name] [value]` | Set or recall aliases for versions
`nvs remote [name] [value]` | Set or recall download base URIs

A version string consists of a semantic version number or version label ("lts" or "latest"), optionally preceded by a remote name, optionally followed by an architecture, separated by slashes. Examples: "lts", "4.6.0", "6.3.1/x86", "node/6.7.0/x64".
A version or filter consists of a complete or partial semantic version number or version label ("lts", "latest", "Argon", etc.), optionally preceded by a remote name, optionally followed by an architecture, separated by slashes. Examples: "lts", "4.6.0", "6/x86", "node/6.7/x64".

[Refer to the docs](./doc) for more details about each command.

Expand Down
2 changes: 1 addition & 1 deletion doc/ADD.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ nvs add <version>
```
Downloads and extracts a requested node version. An added node version is then ready to activate with a USE command.

A version string consists of a semantic version number or version label ("lts" or "latest"), optionally preceded by a remote name, optionally followed by a processor architecture or bitness ("x86", "x64", "32", "64"), separated by slashes. Examples: "lts", "4.6.0", "6.3.1/x86", "node/6.7.0/x64". An alias may also be used in place of a version string.
A version string consists of a complete or partial semantic version number or version label ("lts", "latest", "Argon", etc.), optionally preceded by a remote name, optionally followed by a processor architecture or bitness ("x86", "x64", "32", "64"), separated by slashes. When a partial version matches multiple available versions, the latest version is automatically selected. Examples: "node/lts", "4.6.0", "6/x86", "node/6.7/x64". An alias may also be used in place of a version string.

A remote name is one of the keys from the `remotes` mapping in `$NVS_HOME/settings.json`.
2 changes: 1 addition & 1 deletion doc/AUTO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ When invoked with no parameters, `nvs auto` searches for the nearest `.node-vers

The `nvs auto on` command enables automatic switching as needed whenever the current shell's working directory changes; `nvs auto off` disables automatic switching in the current shell. (This feature is not supported in Windows Command Prompt.)

A `.node-version` file must contain a single line with a valid NVS version string, with UTF8 encoding. A version string consists of a semantic version number, optionally preceded by a remote name, optionally followed by a processor architecture or bitness ("x86", "x64", "32", "64"), separated by slashes. Version labels ("lts", "latest") are not supported for use with the USE command. Examples: "4.6.0", "6.3.1/x86", "node/6.7.0/x64"
A `.node-version` file must contain a single line with a valid NVS version string, with UTF8 encoding. A version string consists of a complete or partial semantic version number or version label ("lts", "latest", "Argon", etc.), optionally preceded by a remote name, optionally followed by a processor architecture or bitness ("x86", "x64", "32", "64"), separated by slashes. When a partial version matches multiple available versions, the latest version is automatically selected. Examples: "4.6.0", "6/x86", "node/6.7/x64"
9 changes: 5 additions & 4 deletions doc/LIST-REMOTE.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# LIST-REMOTE Command - Node Version Switcher
```
nvs lsr [remote] [version]
nvs ls-remote [remote] [version]
nvs list-remote [remote] [version]
nvs lsr [remote] [filter]
nvs ls-remote [remote] [filter]
nvs list-remote [remote] [filter]
```
Lists node versions available to download.

If a remote name is specified, then only versions from that remote are listed; otherwise all remotes are listed. A remote name is one of the keys from the `remotes` mapping in `$NVS_HOME/settings.json`.

If a version filter is specified, then only versions matching the filter are listed; otherwise all versions are listed. A version filter is a complete or partial semantic version, for example `4`, `4.3`, or `4.3.2`.
The optional filter parameter may be a configured remote name, or a partial semantic version such as `6.5`, or a remote name and partial version such as `node/6`. If the filter is omitted then all available versions are listed.

6 changes: 4 additions & 2 deletions doc/LIST.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# LIST Command - Node Version Switcher
```
nvs ls
nvs list
nvs ls [filter]
nvs list [filter]
```
Lists local node versions that are immediately available to use.

The optional filter parameter may be a configured remote name, or a partial semantic version such as `6.5`, or a remote name and partial version such as `node/6`. If the filter is omitted then all available versions are listed.

The version currently in the path (if any) is marked with a `>`.
The default (linked) version (if any) is marked with a `#`.
2 changes: 1 addition & 1 deletion doc/REMOVE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ nvs remove <version>
```
Removes a node version that was previously added via an ADD command. Any symlinks or PATH entries (in the calling shell environment only) pointing to that version are also removed.

A version string consists of a semantic version number, optionally preceded by a remote name, optionally followed by a processor architecture or bitness ("x86", "x64", "32", "64"), separated by slashes. Version labels ("lts", "latest") are not supported for use with the REMOVE command. Examples: "4.6.0", "6.3.1/x86", "node/6.7.0/x64"
A version string consists of a semantic version number, optionally preceded by a remote name, optionally followed by a processor architecture or bitness ("x86", "x64", "32", "64"), separated by slashes. Examples: "4.6.0", "6/x86", "node/6.7/x64"

A remote name is one of the keys from the `remotes` mapping in `$NVS_HOME/settings.json`.
2 changes: 1 addition & 1 deletion doc/USE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ nvs use [version]
```
Updates the `PATH` of the calling shell to include the specified node version (which must have been already added). If no version is specified, then the default (linked) version is used, if any.

A version string consists of a semantic version number, optionally preceded by a remote name, optionally followed by a processor architecture or bitness ("x86", "x64", "32", "64"), separated by slashes. Version labels ("lts", "latest") are not supported for use with the USE command. Examples: "4.6.0", "6.3.1/x86", "node/6.7.0/x64". An alias may also be used in place of a version string.
A version string consists of a complete or partial semantic version number or version label ("lts", "latest", "Argon", etc.), optionally preceded by a remote name, optionally followed by a processor architecture or bitness ("x86", "x64", "32", "64"), separated by slashes. When a partial version matches multiple available versions, the latest version is automatically selected. Examples: "node/lts", "4.6.0", "6/x86", "node/6.7/x64". An alias may also be used in place of a version string.

A remote name is one of the keys from the `remotes` mapping in `$NVS_HOME/settings.json`.
131 changes: 17 additions & 114 deletions lib/addRemove.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,129 +3,26 @@ let fs = require('fs'); // Non-const enables test mocking
const path = require('path');
const Error = require('./error');

const nvsAvailable = require('./available');
let nvsList = require('./list'); // Non-const enables test mocking
let nvsUse = require('./use'); // Non-const enables test mocking
let nvsLink = require('./link'); // Non-const enables test mocking
let nvsDownload = require('./download'); // Non-const enables test mocking
let nvsExtract = require('./extract'); // Non-const enables test mocking
const nvsVersion = require('./version');
const NodeVersion = require('./version');

/**
* Lists all versions of node added by NVS.
* @param remoteName Optional remote name filter for the listing.
* @param semanticVersion Optional semantic version filter for the listing.
* @param arch Optional processor architecture filter for the listing.
*/
function list(remoteName, semanticVersion, arch) {
let readdirIfExists = dir => {
try {
return fs.readdirSync(dir);
} catch (e) {
Error.throwIfNot(Error.ENOENT, e, 'Cannot access directory: ' + dir);
return [];
}
};

let result = [];
if (!remoteName) {
// Scan all directories under the home directory, which might be remote names.
let childNames = readdirIfExists(settings.home).sort();
childNames.forEach(childName => {
let childPath = path.join(settings.home, childName);
let stats = fs.lstatSync(childPath);
if (stats.isDirectory() && childName !== 'node_modules') {
result = result.concat(list(childName, semanticVersion, arch));
}
});
} else if (!semanticVersion) {
// Scan all directories under the remote directory, which might be semantic versions.
let childNames = readdirIfExists(path.join(settings.home, remoteName)).sort();
childNames.forEach(childName => {
let childPath = path.join(settings.home, remoteName, childName);
let stats = fs.lstatSync(childPath);
if (stats.isDirectory()) {
result = result.concat(list(remoteName, childName, arch));
}
});
} else if (!arch) {
// Scan all directories under the semantic version directory, which might be architectures.
let childNames = readdirIfExists(path.join(settings.home, remoteName, semanticVersion)).sort();
childNames.forEach(childName => {
let childPath = path.join(settings.home, remoteName, semanticVersion, childName);
let stats = fs.lstatSync(childPath);
if (stats.isDirectory()) {
result = result.concat(list(remoteName, semanticVersion, childName));
}
});
} else {
// Check if a valid full version directory was found.
let version = nvsVersion.tryParse(remoteName + '/' + semanticVersion + '/' + arch, true);
if (version) {
let binPath = nvsUse.getVersionBinary(version);
if (binPath) {
let currentVersion = nvsUse.getCurrentVersion();
let linkedVersion = nvsLink.getLinkedVersion();
let isCurrent = currentVersion && nvsVersion.equal(currentVersion, version);
let isLinked = linkedVersion && nvsVersion.equal(linkedVersion, version);
result.push((isCurrent && isLinked ? '>#'
: isCurrent ? ' >' : isLinked ? ' #' : ' ') + version);
}
}
}
return result;
}

/**
* Resolves a labeled version (lts or latest) to a semantic version,
* according to the indications in the downloaded remote index.
* Downloads and extracts a version of node.
*/
function resolveSemanticVersionAsync(version) {
if (version.semanticVersion) {
// A semantic version is already present, so there's nothing to resolve.
return Promise.resolve(version);
}

// Download the index and use that to resolve a semantic version.
return nvsAvailable.downloadIndexAsync(version.remoteName).then(remoteIndex => {
let selectedBuild = null;
if (Array.isArray(remoteIndex)) {
let latest = null;
let lts = null;
if (remoteIndex.some(item => item.lts)) {
latest = remoteIndex[0];
lts = remoteIndex.find(item => item.lts);
}

if (!version.semanticVersion && version.label === 'latest' && latest) {
selectedBuild = latest;
} else if (!version.semanticVersion && version.label === 'lts' && lts) {
selectedBuild = lts;
} else if (!version.semanticVersion && version.label.startsWith('lts-')) {
let ltsName = version.label.substr(4).toLowerCase();
selectedBuild = remoteIndex.find(
item => item.lts && item.lts.toLowerCase() === ltsName);
} else if (version.semanticVersion) {
selectedBuild = remoteIndex.find(item =>
item.version === 'v' + version.semanticVersion);
}
}

if (!selectedBuild) {
function addAsync(version) {
return nvsList.getRemoteVersionsAsync(version.remoteName).then(versions => {
let resolvedVersion = nvsList.find(version, versions);
if (!resolvedVersion) {
throw new Error('Version ' +
(version.semanticVersion || version.label) +
' not found in remote: ' + version.remoteName);
} else {
version.semanticVersion = selectedBuild.version.substr(1);
return version;
' not found in remote: ' + version.remoteName, Error.ENOENT);
}
});
}

/**
* Downloads and extracts a version of node.
*/
function addAsync(version) {
return resolveSemanticVersionAsync(version).then(version => {
version = resolvedVersion;
let binPath = nvsUse.getVersionBinary(version);
if (binPath) {
return ['Already added at: ' + nvsUse.homePath(binPath),
Expand All @@ -137,6 +34,11 @@ function addAsync(version) {

let remoteUri = settings.remotes[version.remoteName];
return downloadAndExtractAsync(version, remoteUri).then(() => {
if (version.label) {
fs.writeFileSync(path.join(nvsUse.getVersionDir(version), '.nvs'),
JSON.stringify({ label: version.label }, null, 2));
}

binPath = nvsUse.getVersionBinary(version);
if (binPath) {
return ['Added at: ' + nvsUse.homePath(binPath),
Expand Down Expand Up @@ -316,11 +218,13 @@ function getArchiveFileNameAndUri(version, remoteUri) {
function remove(version) {
let result = [];

version.arch = version.arch || NodeVersion.defaultArch;

// Unlink this version if it is linked.
result = result.concat(nvsLink.unlink(version));

let currentVersion = nvsUse.getCurrentVersion();
if (currentVersion && nvsVersion.equal(currentVersion, version)) {
if (currentVersion && NodeVersion.equal(currentVersion, version)) {
// The specified version is currently in use. Remove it from the PATH.
result = result.concat(nvsUse.use('default'));
}
Expand Down Expand Up @@ -397,7 +301,6 @@ function removeDirectoryIfEmpty(dir) {
}

module.exports = {
list,
addAsync,
remove,
};
12 changes: 8 additions & 4 deletions lib/auto.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ const os = require('os');
const path = require('path');
const Error = require('./error');

const nvsVersion = require('./version');
const NodeVersion = require('./version');
let nvsUse = require('./use'); // Non-const enables test mocking
let nvsAddRemove = require('./addRemove'); // Non-const enables test mocking
let nvsList = require('./list'); // Non-const enables test mocking

/**
* Searches for the nearest `.node-version` file in the current directory or parent directories.
Expand All @@ -26,7 +27,8 @@ function autoSwitchAsync(cwd) {
}
if (versionString) {
try {
version = nvsVersion.parse(versionString);
version = NodeVersion.parse(versionString);
version.arch = version.arch || NodeVersion.defaultArch;
break;
} catch (e) {
throw new Error('Failed to parse version in file: ' + versionFile, e);
Expand All @@ -38,8 +40,10 @@ function autoSwitchAsync(cwd) {
}

if (version) {
let binPath = nvsUse.getVersionBinary(version);
if (!binPath) {
let resolvedVersion = nvsList.find(version);
if (resolvedVersion) {
version = resolvedVersion;
} else {
if (!settings.quiet) {
console.log('Adding: ' + version);
}
Expand Down
Loading

0 comments on commit 044a7cd

Please sign in to comment.