diff --git a/.gitignore b/.gitignore
index 05b4834..1a09921 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,17 +12,14 @@
# Main code, tests, and docs
!/lib/
+!/deps/
!/test/
!/doc/
!/tools/
!/setup/
-# Exclude most packages under node_modules because they are dev dependencies
-!/node_modules/
-/node_modules/*/
-
-# This one is actually a runtime dependency
-!/node_modules/progress/
+# Exclude packages under node_modules because they are dev dependencies
+/node_modules/
# Default version link
/default
diff --git a/node_modules/progress/.npmignore b/deps/progress/.npmignore
similarity index 100%
rename from node_modules/progress/.npmignore
rename to deps/progress/.npmignore
diff --git a/node_modules/progress/History.md b/deps/progress/History.md
similarity index 100%
rename from node_modules/progress/History.md
rename to deps/progress/History.md
diff --git a/node_modules/progress/LICENSE b/deps/progress/LICENSE
similarity index 100%
rename from node_modules/progress/LICENSE
rename to deps/progress/LICENSE
diff --git a/node_modules/progress/Makefile b/deps/progress/Makefile
similarity index 100%
rename from node_modules/progress/Makefile
rename to deps/progress/Makefile
diff --git a/node_modules/progress/Readme.md b/deps/progress/Readme.md
similarity index 73%
rename from node_modules/progress/Readme.md
rename to deps/progress/Readme.md
index 202cef3..23614b8 100644
--- a/node_modules/progress/Readme.md
+++ b/deps/progress/Readme.md
@@ -35,6 +35,7 @@ These are keys in the options object you can pass to the progress bar along with
- `stream` the output stream defaulting to stderr
- `complete` completion character defaulting to "="
- `incomplete` incomplete character defaulting to "-"
+- `renderThrottle` minimum time between updates in milliseconds defaulting to 16
- `clear` option to clear the bar on completion defaulting to false
- `callback` optional function to call when the progress bar completes
@@ -49,6 +50,32 @@ These are tokens you can use in the format of your progress bar.
- `:percent` completion percentage
- `:eta` estimated completion time in seconds
+Tokens other than `:bar` may include a suffix to specify width, that is a minus
+(for left-padding) or plus (for right-padding) followed by an integer, for example
+`:percent-4`.
+
+### Custom Tokens
+
+You can define custom tokens by adding a `{'name': value}` object parameter to your method (`tick()`, `update()`, etc.) calls.
+
+```javascript
+var bar = new ProgressBar(':current: :token1 :token2', { total: 3 })
+bar.tick({
+ 'token1': "Hello",
+ 'token2': "World!\n"
+})
+bar.tick(2, {
+ 'token1': "Goodbye",
+ 'token2': "World!"
+})
+```
+The above example would result in the output below.
+
+```
+1: Hello World!
+3: Goodbye World!
+```
+
## Examples
### Download
@@ -71,7 +98,7 @@ req.on('response', function(res){
var len = parseInt(res.headers['content-length'], 10);
console.log();
- var bar = new ProgressBar(' downloading [:bar] :percent :etas', {
+ var bar = new ProgressBar(' downloading [:bar] :percent-4 :eta-3s', {
complete: '=',
incomplete: ' ',
width: 20,
@@ -93,7 +120,7 @@ req.end();
The above example result in a progress bar like the one below.
```
-downloading [===== ] 29% 3.7s
+downloading [===== ] 29% 3.7s
```
You can see more examples in the `examples` folder.
diff --git a/node_modules/progress/index.js b/deps/progress/index.js
similarity index 100%
rename from node_modules/progress/index.js
rename to deps/progress/index.js
diff --git a/node_modules/progress/lib/node-progress.js b/deps/progress/lib/node-progress.js
similarity index 62%
rename from node_modules/progress/lib/node-progress.js
rename to deps/progress/lib/node-progress.js
index 0f47a9f..95de4fe 100644
--- a/node_modules/progress/lib/node-progress.js
+++ b/deps/progress/lib/node-progress.js
@@ -21,6 +21,7 @@ exports = module.exports = ProgressBar;
* - `stream` the output stream defaulting to stderr
* - `complete` completion character defaulting to "="
* - `incomplete` incomplete character defaulting to "-"
+ * - `renderThrottle` minimum time between updates in milliseconds defaulting to 16
* - `callback` optional function to call when the progress bar completes
* - `clear` will clear the progress bar upon termination
*
@@ -33,6 +34,10 @@ exports = module.exports = ProgressBar;
* - `:percent` completion percentage
* - `:eta` eta in seconds
*
+ * Tokens other than `:bar` may include a suffix to specify width, that is a minus
+ * (for left-padding) or plus (for right-padding) followed by an integer, for example
+ * `:percent-4`.
+ *
* @param {string} fmt
* @param {object|number} options or total
* @api public
@@ -60,7 +65,9 @@ function ProgressBar(fmt, options) {
complete : options.complete || '=',
incomplete : options.incomplete || '-'
};
+ this.renderThrottle = options.renderThrottle !== 0 ? (options.renderThrottle || 16) : 0;
this.callback = options.callback || function () {};
+ this.tokens = {};
this.lastDraw = '';
}
@@ -78,15 +85,21 @@ ProgressBar.prototype.tick = function(len, tokens){
// swap tokens
if ('object' == typeof len) tokens = len, len = 1;
+ if (tokens) this.tokens = tokens;
// start time for eta
if (0 == this.curr) this.start = new Date;
this.curr += len
- this.render(tokens);
+
+ // schedule render
+ if (!this.renderThrottleTimeout) {
+ this.renderThrottleTimeout = setTimeout(this.render.bind(this), this.renderThrottle);
+ }
// progress complete
- if (this.curr >= this.total) {
+ if (!this.complete && this.curr >= this.total) {
+ if (this.renderThrottleTimeout) this.render();
this.complete = true;
this.terminate();
this.callback(this);
@@ -103,6 +116,11 @@ ProgressBar.prototype.tick = function(len, tokens){
*/
ProgressBar.prototype.render = function (tokens) {
+ clearTimeout(this.renderThrottleTimeout);
+ this.renderThrottleTimeout = null;
+
+ if (tokens) this.tokens = tokens;
+
if (!this.stream.isTTY) return;
var ratio = this.curr / this.total;
@@ -114,13 +132,14 @@ ProgressBar.prototype.render = function (tokens) {
var eta = (percent == 100) ? 0 : elapsed * (this.total / this.curr - 1);
/* populate the bar template with percentages and timestamps */
- var str = this.fmt
- .replace(':current', this.curr)
- .replace(':total', this.total)
- .replace(':elapsed', isNaN(elapsed) ? '0.0' : (elapsed / 1000).toFixed(1))
- .replace(':eta', (isNaN(eta) || !isFinite(eta)) ? '0.0' : (eta / 1000)
- .toFixed(1))
- .replace(':percent', percent.toFixed(0) + '%');
+ var str = this.fmt;
+ str = replaceToken(str, 'current', this.curr);
+ str = replaceToken(str, 'total', this.total);
+ str = replaceToken(str, 'elapsed', isNaN(elapsed) ? '0.0'
+ : elapsed >= 10000 ? Math.round(elapsed / 1000) : (elapsed / 1000).toFixed(1));
+ str = replaceToken(str, 'eta', (isNaN(eta) || !isFinite(eta)) ? '0.0'
+ : eta >= 10000 ? Math.round(eta / 1000) : (eta / 1000).toFixed(1));
+ str = replaceToken(str, 'percent', percent.toFixed(0) + '%');
/* compute the available space (non-zero) for the bar */
var availableSpace = Math.max(0, this.stream.columns - str.replace(':bar', '').length);
@@ -135,16 +154,54 @@ ProgressBar.prototype.render = function (tokens) {
str = str.replace(':bar', complete + incomplete);
/* replace the extra tokens */
- if (tokens) for (var key in tokens) str = str.replace(':' + key, tokens[key]);
+ if (this.tokens) for (var key in this.tokens) str = replaceToken(str, key, this.tokens[key]);
if (this.lastDraw !== str) {
- this.stream.clearLine();
this.stream.cursorTo(0);
this.stream.write(str);
+ if (str.length < this.lastDraw.length) {
+ // Reduce flicker - don't clear unless the new line is shorter.
+ this.stream.clearLine(1);
+ }
this.lastDraw = str;
}
};
+/**
+ * Replace a token in a string, using optional width specifiers after the token.
+ * @param str {string} The string that may contain the token to be replaced.
+ * @param token {string} Token to replace, not including the ':' prefix or width suffix.
+ * @param value {string} The replacement value.
+ * @return The resulting string after replacement.
+ */
+function replaceToken(str, token, value) {
+ token = ':' + token;
+ var tokenIndex = str.indexOf(token);
+ if (tokenIndex < 0) {
+ return str;
+ }
+
+ value = (value ? value.toString() : '');
+
+ function repeat(s, n) { return n <= 0 ? '' : Array(n + 1).join(s); };
+
+ if (str[tokenIndex + token.length] === '-') {
+ width = parseInt(str.substr(tokenIndex + token.length + 1));
+ if (width) {
+ token = token + '-' + width;
+ value = repeat(' ', width - value.length) + value;
+ }
+ } else if (str[tokenIndex + token.length] === '+') {
+ width = parseInt(str.substr(tokenIndex + token.length + 1));
+ if (width) {
+ token = token + '+' + width;
+ value = value + repeat(' ', width - value.length);
+ }
+ }
+
+ return str.replace(token, value);
+}
+
/**
* "update" the progress bar to represent an exact percentage.
* The ratio (between 0 and 1) specified will be multiplied by `total` and
@@ -176,5 +233,5 @@ ProgressBar.prototype.terminate = function () {
if (this.clear) {
this.stream.clearLine();
this.stream.cursorTo(0);
- } else console.log();
+ } else this.stream.write('\n');
};
diff --git a/deps/progress/package.json b/deps/progress/package.json
new file mode 100644
index 0000000..4cf6409
--- /dev/null
+++ b/deps/progress/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "progress",
+ "version": "1.2.0",
+ "description": "Flexible ascii progress bar",
+ "keywords": [
+ "cli",
+ "progress"
+ ],
+ "author": {
+ "name": "TJ Holowaychuk",
+ "email": "tj@vision-media.ca"
+ },
+ "contributors": [
+ {
+ "name": "Christoffer Hallas",
+ "email": "christoffer.hallas@gmail.com"
+ },
+ {
+ "name": "Jordan Scales",
+ "email": "scalesjordan@gmail.com"
+ }
+ ],
+ "dependencies": {},
+ "main": "index",
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/visionmedia/node-progress.git"
+ },
+ "license": "MIT",
+ "gitHead": "4a6c2fdc782cff6c9f8933339e7aae0b38d682d4",
+ "readme": "Flexible ascii progress bar.\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install progress\r\n```\r\n\r\n## Usage\r\n\r\nFirst we create a `ProgressBar`, giving it a format string\r\nas well as the `total`, telling the progress bar when it will\r\nbe considered complete. After that all we need to do is `tick()` appropriately.\r\n\r\n```javascript\r\nvar ProgressBar = require('progress');\r\n\r\nvar bar = new ProgressBar(':bar', { total: 10 });\r\nvar timer = setInterval(function () {\r\n bar.tick();\r\n if (bar.complete) {\r\n console.log('\\ncomplete\\n');\r\n clearInterval(timer);\r\n }\r\n}, 100);\r\n```\r\n\r\n### Options\r\n\r\nThese are keys in the options object you can pass to the progress bar along with\r\n`total` as seen in the example above.\r\n\r\n- `total` total number of ticks to complete\r\n- `width` the displayed width of the progress bar defaulting to total\r\n- `stream` the output stream defaulting to stderr\r\n- `complete` completion character defaulting to \"=\"\r\n- `incomplete` incomplete character defaulting to \"-\"\r\n- `renderThrottle` minimum time between updates in milliseconds defaulting to 16\r\n- `clear` option to clear the bar on completion defaulting to false\r\n- `callback` optional function to call when the progress bar completes\r\n\r\n### Tokens\r\n\r\nThese are tokens you can use in the format of your progress bar.\r\n\r\n- `:bar` the progress bar itself\r\n- `:current` current tick number\r\n- `:total` total ticks\r\n- `:elapsed` time elapsed in seconds\r\n- `:percent` completion percentage\r\n- `:eta` estimated completion time in seconds\r\n\r\nTokens other than `:bar` may include a suffix to specify width, that is a minus\r\n(for left-padding) or plus (for right-padding) followed by an integer, for example\r\n`:percent-4`.\r\n\r\n### Custom Tokens\r\n\r\nYou can define custom tokens by adding a `{'name': value}` object parameter to your method (`tick()`, `update()`, etc.) calls.\r\n\r\n```javascript\r\nvar bar = new ProgressBar(':current: :token1 :token2', { total: 3 })\r\nbar.tick({\r\n 'token1': \"Hello\",\r\n 'token2': \"World!\\n\"\r\n})\r\nbar.tick(2, {\r\n 'token1': \"Goodbye\",\r\n 'token2': \"World!\"\r\n})\r\n```\r\nThe above example would result in the output below.\r\n\r\n```\r\n1: Hello World!\r\n3: Goodbye World!\r\n```\r\n\r\n## Examples\r\n\r\n### Download\r\n\r\nIn our download example each tick has a variable influence, so we pass the chunk\r\nlength which adjusts the progress bar appropriately relative to the total\r\nlength.\r\n\r\n```javascript\r\nvar ProgressBar = require('../');\r\nvar https = require('https');\r\n\r\nvar req = https.request({\r\n host: 'download.github.com',\r\n port: 443,\r\n path: '/visionmedia-node-jscoverage-0d4608a.zip'\r\n});\r\n\r\nreq.on('response', function(res){\r\n var len = parseInt(res.headers['content-length'], 10);\r\n\r\n console.log();\r\n var bar = new ProgressBar(' downloading [:bar] :percent-4 :eta-3s', {\r\n complete: '=',\r\n incomplete: ' ',\r\n width: 20,\r\n total: len\r\n });\r\n\r\n res.on('data', function (chunk) {\r\n bar.tick(chunk.length);\r\n });\r\n\r\n res.on('end', function () {\r\n console.log('\\n');\r\n });\r\n});\r\n\r\nreq.end();\r\n```\r\n\r\nThe above example result in a progress bar like the one below.\r\n\r\n```\r\ndownloading [===== ] 29% 3.7s\r\n```\r\n\r\nYou can see more examples in the `examples` folder.\r\n\r\n## License\r\n\r\nMIT\r\n",
+ "readmeFilename": "Readme.md",
+ "bugs": {
+ "url": "https://github.com/visionmedia/node-progress/issues"
+ },
+ "homepage": "https://github.com/visionmedia/node-progress#readme",
+ "_id": "progress@1.2.0",
+ "_shasum": "e9185384798a6911b3c37945b4dcf3ad28c13e46",
+ "_from": "git://github.com/jasongin/node-progress.git",
+ "_resolved": "git://github.com/jasongin/node-progress.git#4a6c2fdc782cff6c9f8933339e7aae0b38d682d4"
+}
diff --git a/lib/addRemove.js b/lib/addRemove.js
index 16d5cb1..fb3165f 100644
--- a/lib/addRemove.js
+++ b/lib/addRemove.js
@@ -6,10 +6,11 @@ const Error = require('./error');
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 NodeVersion = require('./version');
+let nvsDownload = null; // Delay-load
+let nvsExtract = null; // Delay-load
+
/**
* Downloads and extracts a version of node.
*/
@@ -72,12 +73,14 @@ function downloadAndExtractAsync(version, remoteUri) {
version.semanticVersion,
version.arch);
+ nvsDownload = nvsDownload || require('./download');
return nvsDownload.ensureFileCachedAsync(
archiveFileName,
archiveFileUri,
shasumFileName,
shasumFileUri
).then(zipFilePath => {
+ nvsExtract = nvsExtract || require('./extract');
return nvsExtract.extractAsync(zipFilePath, targetDir);
}).then(() => {
// Guess the name of the top-level extracted directory.
diff --git a/lib/download.js b/lib/download.js
index 7e08254..ff14dab 100644
--- a/lib/download.js
+++ b/lib/download.js
@@ -2,7 +2,7 @@
const crypto = require('crypto');
const path = require('path');
const stream = require('stream');
-const ProgressBar = require('progress');
+const ProgressBar = require('../deps/progress');
let fs = require('fs'); // Non-const enables test mocking
let http = require('http'); // Non-const enables test mocking
@@ -14,7 +14,7 @@ function downloadFileAsync(filePath, fileUri) {
let stream = null;
return new Promise((resolve, reject) => {
try {
- const progressFormat = 'Downloading [:bar] :percent :etas ';
+ const progressFormat = 'Downloading [:bar] :percent-4 :eta-3s ';
stream = fs.createWriteStream(filePath);
if (path.isAbsolute(fileUri)) {
@@ -41,7 +41,7 @@ function downloadFileAsync(filePath, fileUri) {
client.get(fileUri, (res) => {
if (res.statusCode === 200) {
let totalBytes = parseInt(res.headers['content-length'], 10);
- let progressFormat = 'Downloading [:bar] :percent :etas ';
+ let progressFormat = 'Downloading [:bar] :percent-4 :eta-3s ';
if (!settings.quiet && totalBytes > 100000) {
res.pipe(streamProgress(progressFormat, {
complete: '#',
@@ -163,7 +163,7 @@ function streamProgress(progressFormat, options) {
let progressBar = new ProgressBar(progressFormat, options);
passThrough.on('data', chunk => {
if (progressBar.curr + chunk.length >= progressBar.total) {
- let finalFormat = progressFormat.replace(/:etas/, ' ');
+ let finalFormat = progressFormat.replace(/:eta-3s/, ' ');
progressBar.fmt = finalFormat;
}
progressBar.tick(chunk.length);
diff --git a/lib/extract.js b/lib/extract.js
index cc1676f..ef407b0 100644
--- a/lib/extract.js
+++ b/lib/extract.js
@@ -1,7 +1,7 @@
/* global settings */
let childProcess = require('child_process'); // Non-const enables test mocking
const path = require('path');
-const ProgressBar = require('progress');
+const ProgressBar = require('../deps/progress');
const Error = require('./error');
@@ -40,7 +40,7 @@ function extractZipArchiveAsync(archiveFile, targetDir) {
}
return new Promise((resolve, reject) => {
- let progressFormat = '\x1b[1GExtracting [:bar] :percent :etas ';
+ let progressFormat = '\x1b[1GExtracting [:bar] :percent-4 :eta-3s ';
let progressBar = null;
let nextData = '';
if (!settings.quiet && totalFiles > 10) {
@@ -77,7 +77,7 @@ function extractZipArchiveAsync(archiveFile, targetDir) {
}
if (progressBar.curr + fileCount >= totalFiles) {
- let finalFormat = progressFormat.replace(/:etas/, ' ');
+ let finalFormat = progressFormat.replace(/:eta-3s/, ' ');
progressBar.fmt = finalFormat;
}
progressBar.tick(fileCount);
@@ -131,7 +131,7 @@ function extractTarArchiveAsync(archiveFile, targetDir) {
}
return new Promise((resolve, reject) => {
- let progressFormat = 'Extracting [:bar] :percent :etas ';
+ let progressFormat = 'Extracting [:bar] :percent-4 :eta-3s ';
let progressBar = null;
if (!settings.quiet && totalFiles > 10) {
progressBar = new ProgressBar(progressFormat, {
@@ -158,7 +158,7 @@ function extractTarArchiveAsync(archiveFile, targetDir) {
if (progressBar) {
let fileCount = countChars(data.toString(), '\n');
if (progressBar.curr + fileCount >= totalFiles) {
- let finalFormat = progressFormat.replace(/:etas/, ' ');
+ let finalFormat = progressFormat.replace(/:eta-3s/, ' ');
progressBar.fmt = finalFormat;
}
progressBar.tick(fileCount);
diff --git a/node_modules/progress/package.json b/node_modules/progress/package.json
deleted file mode 100644
index 1815528..0000000
--- a/node_modules/progress/package.json
+++ /dev/null
@@ -1,103 +0,0 @@
-{
- "_args": [
- [
- {
- "raw": "progress",
- "scope": null,
- "escapedName": "progress",
- "name": "progress",
- "rawSpec": "",
- "spec": "latest",
- "type": "tag"
- },
- "/Users/Jason/Projects/nvs"
- ]
- ],
- "_from": "progress@latest",
- "_id": "progress@1.1.8",
- "_inCache": true,
- "_location": "/progress",
- "_npmUser": {
- "name": "prezjordan",
- "email": "scalesjordan@gmail.com"
- },
- "_npmVersion": "1.4.14",
- "_phantomChildren": {},
- "_requested": {
- "raw": "progress",
- "scope": null,
- "escapedName": "progress",
- "name": "progress",
- "rawSpec": "",
- "spec": "latest",
- "type": "tag"
- },
- "_requiredBy": [
- "#USER",
- "/",
- "/eslint"
- ],
- "_resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
- "_shasum": "e260c78f6161cdd9b0e56cc3e0a85de17c7a57be",
- "_shrinkwrap": null,
- "_spec": "progress",
- "_where": "/Users/Jason/Projects/nvs",
- "author": {
- "name": "TJ Holowaychuk",
- "email": "tj@vision-media.ca"
- },
- "bugs": {
- "url": "https://github.com/visionmedia/node-progress/issues"
- },
- "contributors": [
- {
- "name": "Christoffer Hallas",
- "email": "christoffer.hallas@gmail.com"
- },
- {
- "name": "Jordan Scales",
- "email": "scalesjordan@gmail.com"
- }
- ],
- "dependencies": {},
- "description": "Flexible ascii progress bar",
- "devDependencies": {},
- "directories": {},
- "dist": {
- "shasum": "e260c78f6161cdd9b0e56cc3e0a85de17c7a57be",
- "tarball": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz"
- },
- "engines": {
- "node": ">=0.4.0"
- },
- "gitHead": "6b9524c0d07df9555d20ae95c65918020c50e3e2",
- "homepage": "https://github.com/visionmedia/node-progress",
- "keywords": [
- "cli",
- "progress"
- ],
- "main": "index",
- "maintainers": [
- {
- "name": "tjholowaychuk",
- "email": "tj@vision-media.ca"
- },
- {
- "name": "hallas",
- "email": "christoffer.hallas@forsvikgroup.com"
- },
- {
- "name": "prezjordan",
- "email": "scalesjordan@gmail.com"
- }
- ],
- "name": "progress",
- "optionalDependencies": {},
- "readme": "ERROR: No README data found!",
- "repository": {
- "type": "git",
- "url": "git://github.com/visionmedia/node-progress.git"
- },
- "scripts": {},
- "version": "1.1.8"
-}
diff --git a/setup/NvsSetup.wixproj b/setup/NvsSetup.wixproj
index cfca797..46f146b 100644
--- a/setup/NvsSetup.wixproj
+++ b/setup/NvsSetup.wixproj
@@ -14,7 +14,7 @@
$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets
$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets
ICE105
- NvsLibDir=..\lib\;NvsDocDir=..\doc\;NMProgressDir=..\node_modules\progress\
+ NvsLibDir=..\lib\;NvsDocDir=..\doc\;NvsDepsDir=..\deps\
$(HeatDirPaths)
$(Configuration)\
$(OutputPath)
@@ -34,7 +34,7 @@
-
+
@@ -68,7 +68,7 @@
-
+
diff --git a/setup/nvs.wxs b/setup/nvs.wxs
index 83fcab5..a6d218f 100644
--- a/setup/nvs.wxs
+++ b/setup/nvs.wxs
@@ -52,7 +52,7 @@
-
+