nw-builder 3.6.0 → 3.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/.editorconfig +17 -0
  2. package/.github/CHANGELOG.md +77 -35
  3. package/.github/CODE_OF_CONDUCT.md +55 -0
  4. package/.github/{ISSUE_REQUEST_TEMPLATE.md → ISSUE_TEMPLATE.md} +1 -0
  5. package/.github/workflows/cd.yml +3 -3
  6. package/.github/workflows/ci.yml +9 -5
  7. package/README.md +81 -23
  8. package/bin/nwbuild.cjs +102 -0
  9. package/dist/index.cjs +1 -0
  10. package/lib/Version.cjs +81 -0
  11. package/lib/downloader.cjs +177 -0
  12. package/lib/index.cjs +1078 -0
  13. package/lib/{platformOverrides.js → platformOverrides.cjs} +61 -36
  14. package/lib/utils.cjs +293 -0
  15. package/lib/versions.cjs +206 -0
  16. package/package.json +57 -29
  17. package/src/constants/Platform.js +16 -0
  18. package/src/constants/Platforms.js +143 -0
  19. package/src/constants/index.js +7 -0
  20. package/src/index.js +2 -0
  21. package/src/utilities/checkCache.js +30 -0
  22. package/src/utilities/detectCurrentPlatform.js +24 -0
  23. package/src/utilities/index.js +4 -0
  24. package/{example/icons → test/demo}/icon.icns +0 -0
  25. package/{example/icons → test/demo}/icon.ico +0 -0
  26. package/test/demo/index.cjs +14 -0
  27. package/test/demo/index.html +10 -0
  28. package/{example/nwapp → test/demo}/package.json +7 -6
  29. package/test/downloader.cjs +131 -0
  30. package/test/expected/README.md +1 -1
  31. package/test/expected/merged +1 -1
  32. package/test/expected/oneOveriddenRestNot/README.md +1 -1
  33. package/test/expected/oneOveriddenRestNot/osx32.json +7 -7
  34. package/test/expected/oneOveriddenRestNot/osx64.json +7 -7
  35. package/test/expected/osx-plist/README.md +1 -1
  36. package/test/expected/platformOverrides/README.md +1 -1
  37. package/test/expected/platformOverrides/linux32.json +12 -12
  38. package/test/expected/platformOverrides/linux64.json +8 -8
  39. package/test/expected/platformOverrides/osx32.json +10 -10
  40. package/test/expected/platformOverrides/osx64.json +10 -10
  41. package/test/expected/platformOverrides/win32.json +10 -10
  42. package/test/expected/platformOverrides/win64.json +10 -10
  43. package/test/fixtures/README.md +1 -1
  44. package/test/fixtures/invalid.json +1 -1
  45. package/test/fixtures/manifest/README.md +1 -1
  46. package/test/fixtures/manifest/versions.json +9 -3
  47. package/test/fixtures/nwapp/README.md +1 -1
  48. package/test/fixtures/nwapp/images/imagefile.img +1 -1
  49. package/test/fixtures/nwapp/index.html +5 -2
  50. package/test/fixtures/nwapp/javascript/bower_packages/simple/package.json +1 -1
  51. package/test/fixtures/nwapp/javascript/jsfile.js +1 -1
  52. package/test/fixtures/nwapp/package.json +1 -1
  53. package/test/fixtures/oneOveriddenRestNot/README.md +1 -1
  54. package/test/fixtures/oneOveriddenRestNot/package.json +13 -13
  55. package/test/fixtures/osx-plist/README.md +1 -1
  56. package/test/fixtures/platformOverrides/README.md +1 -1
  57. package/test/fixtures/platformOverrides/package.json +68 -48
  58. package/test/fixtures/testVersions.html +73 -16
  59. package/test/nwBuilder.cjs +339 -0
  60. package/test/unit/checkCache.test.js +19 -0
  61. package/test/unit/checkCacheDir/v0.64.1/linux64/nw1.app +0 -0
  62. package/test/unit/checkCacheDir/v0.64.1/linux64/nw2.app +0 -0
  63. package/test/unit/detectCurrentPlatform.test.js +58 -0
  64. package/test/utils.cjs +310 -0
  65. package/test/versions.cjs +485 -0
  66. package/bin/nwbuild +0 -98
  67. package/demo.js +0 -22
  68. package/example/icons/README.md +0 -7
  69. package/example/nwapp/Credits.html +0 -9
  70. package/example/nwapp/README.md +0 -7
  71. package/example/nwapp/images/README.md +0 -7
  72. package/example/nwapp/images/kitten.jpg +0 -0
  73. package/example/nwapp/index.html +0 -22
  74. package/example/package.json +0 -7
  75. package/index.js +0 -1
  76. package/lib/Version.js +0 -60
  77. package/lib/detectCurrentPlatform.js +0 -17
  78. package/lib/downloader.js +0 -192
  79. package/lib/index.js +0 -873
  80. package/lib/platforms.js +0 -76
  81. package/lib/utils.js +0 -249
  82. package/lib/versions.js +0 -198
  83. package/test/downloader.js +0 -87
  84. package/test/nwBuilder.js +0 -237
  85. package/test/utils.js +0 -232
  86. package/test/versions.js +0 -330
@@ -6,45 +6,58 @@ Apply platform-specific manifest values. Works with JSON or plain objects. The p
6
6
 
7
7
  var applyOverrides, detectPlatform, getOverridesToApply, os, _;
8
8
 
9
- os = require('os');
10
- _ = require('lodash');
9
+ os = require("os");
10
+ _ = require("lodash");
11
11
 
12
- detectPlatform = function() {
12
+ detectPlatform = function () {
13
13
  var platform;
14
14
  platform = os.platform();
15
- if (platform === 'darwin') {
16
- platform = 'osx';
15
+ if (platform === "darwin") {
16
+ platform = "osx";
17
17
  } else if (platform.match(/^win/)) {
18
- platform = 'win';
18
+ platform = "win";
19
19
  }
20
20
  return {
21
21
  agnostic: platform,
22
- specific: platform + (process.arch === 'x64' || process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') ? 64 : 32)
22
+ specific:
23
+ platform +
24
+ (process.arch === "x64" ||
25
+ Object.prototype.hasOwnProperty.call(
26
+ process.env,
27
+ "PROCESSOR_ARCHITEW6432",
28
+ )
29
+ ? 64
30
+ : 32),
23
31
  };
24
32
  };
25
33
 
26
- getOverridesToApply = function(platform, cb) {
34
+ getOverridesToApply = function (platform, cb) {
27
35
  var agnosticPlatform, architectureAgnostic, architectureSpecific, _i, _len;
28
- architectureAgnostic = ['linux', 'osx', 'win'];
36
+ architectureAgnostic = ["linux", "osx", "win"];
29
37
  architectureSpecific = [];
30
38
  for (_i = 0, _len = architectureAgnostic.length; _i < _len; _i++) {
31
39
  agnosticPlatform = architectureAgnostic[_i];
32
- architectureSpecific.push(agnosticPlatform + '32');
33
- architectureSpecific.push(agnosticPlatform + '64');
40
+ architectureSpecific.push(agnosticPlatform + "32");
41
+ architectureSpecific.push(agnosticPlatform + "64");
34
42
  }
35
43
  if (platform) {
36
- if (!(architectureAgnostic.indexOf(platform) > -1 || architectureSpecific.indexOf(platform) > -1)) {
37
- return cb(new Error('Invalid platform passed', null));
44
+ if (
45
+ !(
46
+ architectureAgnostic.indexOf(platform) > -1 ||
47
+ architectureSpecific.indexOf(platform) > -1
48
+ )
49
+ ) {
50
+ return cb(new Error("Invalid platform passed", null));
38
51
  }
39
52
  if (architectureAgnostic.indexOf(platform) > -1) {
40
53
  return cb(null, {
41
54
  agnostic: platform,
42
- specific: null
55
+ specific: null,
43
56
  });
44
57
  } else {
45
58
  return cb(null, {
46
59
  agnostic: platform.substr(0, platform.length - 2),
47
- specific: platform
60
+ specific: platform,
48
61
  });
49
62
  }
50
63
  } else {
@@ -52,30 +65,39 @@ getOverridesToApply = function(platform, cb) {
52
65
  }
53
66
  };
54
67
 
55
- applyOverrides = function(_arg) {
68
+ applyOverrides = function (_arg) {
56
69
  var cb, options, platform, result;
57
- platform = _arg.platform, options = _arg.options, cb = _arg.cb;
70
+ (platform = _arg.platform), (options = _arg.options), (cb = _arg.cb);
58
71
  result = _.cloneDeep(options);
59
- if ((options.platformOverrides[platform] != null) && Object.keys(options.platformOverrides[platform]).length) {
60
- result = _.mergeWith(result, options.platformOverrides[platform], function(optionValue, overrideValue) {
61
-
62
- /*
72
+ if (
73
+ options.platformOverrides[platform] != null &&
74
+ Object.keys(options.platformOverrides[platform]).length
75
+ ) {
76
+ result = _.mergeWith(
77
+ result,
78
+ options.platformOverrides[platform],
79
+ function (optionValue, overrideValue) {
80
+ /*
63
81
  If overrides.x is {} but source.x is a non-empty object {prop:0, another:2},
64
82
  take the {}}
65
83
  */
66
- if ((_.isPlainObject(overrideValue) || _.isArray(overrideValue)) && _.isEmpty(overrideValue)) {
67
- return overrideValue;
68
- }
69
- });
84
+ if (
85
+ (_.isPlainObject(overrideValue) || _.isArray(overrideValue)) &&
86
+ _.isEmpty(overrideValue)
87
+ ) {
88
+ return overrideValue;
89
+ }
90
+ },
91
+ );
70
92
  }
71
93
  return cb(result);
72
94
  };
73
95
 
74
- module.exports = function(_arg, cb) {
96
+ module.exports = function (_arg, cb) {
75
97
  var callback, options, platform;
76
- options = _arg.options, platform = _arg.platform;
77
- callback = cb != null ? cb : (function() {});
78
- return getOverridesToApply(platform, function(err, overridesToApply) {
98
+ (options = _arg.options), (platform = _arg.platform);
99
+ callback = cb != null ? cb : function () {};
100
+ return getOverridesToApply(platform, function (err, overridesToApply) {
79
101
  var applySpecificOverrides, objectMode, originalOptions;
80
102
  if (err != null) {
81
103
  return callback(err, null);
@@ -89,28 +111,31 @@ module.exports = function(_arg, cb) {
89
111
  return callback(err, null);
90
112
  }
91
113
  if (options.platformOverrides != null) {
92
- applySpecificOverrides = function(optionsToOverride) {
114
+ applySpecificOverrides = function (optionsToOverride) {
93
115
  return applyOverrides({
94
116
  platform: overridesToApply.specific,
95
117
  options: optionsToOverride,
96
- cb: function(result) {
118
+ cb: function (result) {
97
119
  delete result.platformOverrides;
98
120
  return callback(null, objectMode ? result : JSON.stringify(result));
99
- }
121
+ },
100
122
  });
101
123
  };
102
124
  if (overridesToApply.agnostic != null) {
103
125
  return applyOverrides({
104
126
  platform: overridesToApply.agnostic,
105
127
  options: options,
106
- cb: function(result) {
128
+ cb: function (result) {
107
129
  if (overridesToApply.specific != null) {
108
130
  return applySpecificOverrides(result);
109
131
  } else {
110
132
  delete result.platformOverrides;
111
- return callback(null, objectMode ? result : JSON.stringify(result));
133
+ return callback(
134
+ null,
135
+ objectMode ? result : JSON.stringify(result),
136
+ );
112
137
  }
113
- }
138
+ },
114
139
  });
115
140
  } else {
116
141
  return applySpecificOverrides(options);
@@ -119,4 +144,4 @@ module.exports = function(_arg, cb) {
119
144
  return callback(null, originalOptions);
120
145
  }
121
146
  });
122
- };
147
+ };
package/lib/utils.cjs ADDED
@@ -0,0 +1,293 @@
1
+ var fs = require("graceful-fs-extra");
2
+ var path = require("path");
3
+ var _ = require("lodash");
4
+ var plist = require("plist");
5
+ var Glob = require("simple-glob");
6
+ var temp = require("temp");
7
+ var archiver = require("archiver");
8
+ var thenify = require("thenify");
9
+
10
+ var readFile = thenify(fs.readFile);
11
+ var writeFile = thenify(fs.writeFile);
12
+
13
+ // Automatically track and cleanup files at exit
14
+ temp.track();
15
+
16
+ module.exports = {
17
+ getPackageInfo: function (path) {
18
+ return new Promise(function (resolve, reject) {
19
+ fs.readFile(path, function (err, data) {
20
+ if (err) return reject(err);
21
+ try {
22
+ var appPkg = JSON.parse(data);
23
+ } catch (e) {
24
+ reject(
25
+ "Invalid package.json: " +
26
+ e +
27
+ "\nMake sure the file is encoded as utf-8",
28
+ );
29
+ return;
30
+ }
31
+ if (!appPkg.name || !appPkg.version) {
32
+ reject(
33
+ "Please make sure that your project's package.json includes a version and a name value",
34
+ );
35
+ } else {
36
+ resolve(appPkg);
37
+ }
38
+ });
39
+ });
40
+ },
41
+ getFileList: function (fileglob) {
42
+ var self = this,
43
+ jsonfile,
44
+ destFiles = [],
45
+ srcFiles = [],
46
+ package_path,
47
+ matches = Glob(fileglob);
48
+
49
+ return new Promise(function (resolve, reject) {
50
+ if (!matches.length) return reject("No files matching");
51
+
52
+ matches.forEach(function (file) {
53
+ var internalFileName = path.normalize(file);
54
+ if (internalFileName.match("package.json")) {
55
+ jsonfile = self.closerPathDepth(internalFileName, jsonfile);
56
+ package_path = path.normalize(
57
+ jsonfile.split("package.json")[0] || "./",
58
+ );
59
+ }
60
+ if (!fs.lstatSync(internalFileName).isDirectory()) {
61
+ srcFiles.push(internalFileName);
62
+ }
63
+ });
64
+
65
+ if (!jsonfile) {
66
+ return reject("Could not find a package.json in your src folder");
67
+ }
68
+
69
+ srcFiles.forEach(function (file) {
70
+ destFiles.push({
71
+ src: file,
72
+ dest: file.replace(package_path, ""),
73
+ });
74
+ });
75
+
76
+ resolve({
77
+ files: destFiles,
78
+ json: jsonfile,
79
+ });
80
+ });
81
+ },
82
+ closerPathDepth: function (path1, path2) {
83
+ if (!path2) {
84
+ return path1;
85
+ }
86
+
87
+ var d1 = this.pathDepth(path1),
88
+ d2 = this.pathDepth(path2);
89
+
90
+ return d1 < d2 ? path1 : path2;
91
+ },
92
+ pathDepth: function (absolutePath) {
93
+ return absolutePath.split(path.sep).length;
94
+ },
95
+ copyFile: function (src, dest, _event, options) {
96
+ return new Promise(function (resolve, reject) {
97
+ options = options || {};
98
+ var stats = fs.lstatSync(src);
99
+ fs.copy(src, dest, options, function (err) {
100
+ if (err) return reject(err);
101
+
102
+ var retryCount = 0;
103
+ var existsCallback = function (exists) {
104
+ if (exists) {
105
+ fs.chmod(dest, stats.mode, function (err) {
106
+ // ignore error
107
+ if (err) {
108
+ _event.emit(
109
+ "log",
110
+ "chmod " +
111
+ stats.mode +
112
+ " on " +
113
+ dest +
114
+ " failed after copying, ignoring",
115
+ );
116
+ }
117
+
118
+ resolve();
119
+ });
120
+ } else if (retryCount++ < 2) {
121
+ // This is antipattern!!!
122
+ // Callback should be called when the copy is finished!!!!
123
+ setTimeout(function () {
124
+ fs.exists(dest, existsCallback);
125
+ }, 1000);
126
+ } else {
127
+ reject(
128
+ new Error(
129
+ "Copied file (" +
130
+ dest +
131
+ ") doesn't exist in destination after copying",
132
+ ),
133
+ );
134
+ }
135
+ };
136
+
137
+ fs.exists(dest, existsCallback);
138
+ });
139
+ });
140
+ },
141
+ mergeFiles: function (app, zipfile, chmod) {
142
+ // we need to pipe the app into the zipfile and chmod it
143
+ return new Promise(function (resolve, reject) {
144
+ var zipStream = fs.createReadStream(zipfile),
145
+ writeStream = fs.createWriteStream(app, { flags: "a" });
146
+
147
+ zipStream.on("error", reject);
148
+ writeStream.on("error", reject);
149
+
150
+ writeStream.on("finish", function () {
151
+ if (chmod) {
152
+ fs.chmodSync(app, chmod);
153
+ }
154
+ resolve();
155
+ });
156
+
157
+ zipStream.pipe(writeStream);
158
+ });
159
+ },
160
+ generateZipFile: function (
161
+ files,
162
+ _event,
163
+ platformSpecificManifest,
164
+ zipOptions,
165
+ ) {
166
+ var destStream = temp.createWriteStream(),
167
+ archive = archiver("zip", zipOptions || {});
168
+
169
+ return new Promise(function (resolve, reject) {
170
+ // Resolve on close
171
+ destStream.on("close", function () {
172
+ resolve(destStream.path);
173
+ });
174
+
175
+ // Reject on Error
176
+ archive.on("error", reject);
177
+
178
+ // Add the files
179
+ files.forEach(function (file) {
180
+ if (file.dest === "package.json" && platformSpecificManifest) {
181
+ archive.append(platformSpecificManifest, { name: "package.json" });
182
+ } else {
183
+ archive.file(file.src, { name: file.dest });
184
+ }
185
+ });
186
+
187
+ // Some logs
188
+ archive.on("entry", function (file) {
189
+ _event.emit("log", "Zipping " + file.name);
190
+ });
191
+
192
+ // Pipe the stream
193
+ archive.pipe(destStream);
194
+ archive.finalize();
195
+ });
196
+ },
197
+ getPlistOptions: function (parsedParams, custom) {
198
+ var obj = {};
199
+ if (parsedParams.name) {
200
+ obj.CFBundleName = parsedParams.name;
201
+ obj.CFBundleDisplayName = parsedParams.name;
202
+ }
203
+ if (parsedParams.version) {
204
+ obj.CFBundleVersion = parsedParams.version;
205
+ obj.CFBundleShortVersionString = "Version " + parsedParams.version;
206
+ }
207
+ if (parsedParams.copyright) {
208
+ obj.NSHumanReadableCopyright = parsedParams.copyright;
209
+ }
210
+
211
+ return _.merge(obj, custom);
212
+ },
213
+ editPlist: function (plistInput, plistOutput, options) {
214
+ options = options || {};
215
+
216
+ // Make sure all required properties are set
217
+ [
218
+ "CFBundleName",
219
+ "CFBundleDisplayName",
220
+ "CFBundleVersion",
221
+ "CFBundleShortVersionString",
222
+ ].forEach(function (prop) {
223
+ if (!Object.prototype.hasOwnProperty.call(options, prop)) {
224
+ throw new Error("Missing macPlist property '" + prop + "'");
225
+ }
226
+ });
227
+
228
+ // Bundle identifier based on package name
229
+ if (options.CFBundleIdentifier === undefined) {
230
+ options.CFBundleIdentifier =
231
+ "com.nw-builder." +
232
+ options.CFBundleName.toLowerCase().replace(/[^a-z-]/g, "");
233
+ }
234
+
235
+ // Read the input file
236
+ return (
237
+ readFile(plistInput, "utf8")
238
+ // Parse it
239
+ .then(plist.parse)
240
+ // Then overwrite the properties with custom values
241
+ .then(function (info) {
242
+ // Keep backwards compatibility and handle aliases
243
+ Object.keys(options).forEach(function (key) {
244
+ var value = options[key];
245
+ switch (key) {
246
+ case "mac_bundle_id":
247
+ info.CFBundleIdentifier = value;
248
+ break;
249
+ case "mac_document_types":
250
+ info.CFBundleDocumentTypes = value.map(function (type) {
251
+ return {
252
+ CFBundleTypeName: type.name,
253
+ CFBundleTypeExtensions: type.extensions,
254
+ CFBundleTypeRole: type.role,
255
+ LSIsAppleDefaultForType: type.isDefault,
256
+ };
257
+ });
258
+ break;
259
+ default:
260
+ info[key] = value;
261
+ }
262
+ });
263
+
264
+ // Remove some unwanted properties
265
+ if (
266
+ !(
267
+ Object.prototype.hasOwnProperty.call(
268
+ options,
269
+ "mac_document_types",
270
+ ) ||
271
+ Object.prototype.hasOwnProperty.call(
272
+ options,
273
+ "CFBundleDocumentTypes",
274
+ )
275
+ )
276
+ ) {
277
+ info.CFBundleDocumentTypes = [];
278
+ }
279
+
280
+ if (
281
+ !Object.prototype.hasOwnProperty.call(
282
+ options,
283
+ "UTExportedTypeDeclarations",
284
+ )
285
+ )
286
+ info.UTExportedTypeDeclarations = [];
287
+
288
+ // Write output file
289
+ return writeFile(plistOutput, plist.build(info));
290
+ })
291
+ );
292
+ },
293
+ };
@@ -0,0 +1,206 @@
1
+ const { Platforms } = require("../dist/index.cjs");
2
+ var semver = require("semver");
3
+ var request = require("request");
4
+ var _ = require("lodash");
5
+ var Version = require("./Version.cjs");
6
+
7
+ /**
8
+ * @param {string} url
9
+ * @param {object} options - Passed to request
10
+ * @returns {promise} which resolves to response body
11
+ */
12
+ function get(url, options) {
13
+ return new Promise(function (resolve, reject) {
14
+ request(url, options, function (err, res, body) {
15
+ if (err) {
16
+ reject(err);
17
+ } else if (res.statusCode !== 200) {
18
+ reject("Received status code " + res.statusCode + ": " + url);
19
+ } else {
20
+ resolve(body);
21
+ }
22
+ });
23
+ });
24
+ }
25
+
26
+ /**
27
+ * @param {string} downloadUrl
28
+ * @returns {promise} which resolves to an array of {Version}s
29
+ */
30
+ function getLegacyVersions(downloadUrl) {
31
+ var scrapePtrn = /href="v?([0-9]+\.[0-9]+\.[0-9]+[^"]*)\/"/gi,
32
+ searchRes,
33
+ versions = [];
34
+
35
+ return get(downloadUrl).then(function (body) {
36
+ // scrapes valid semver versions from apache directory listing
37
+ while ((searchRes = scrapePtrn.exec(body)) !== null) {
38
+ searchRes = searchRes[1];
39
+ if (semver.valid(searchRes) && !_.includes(versions, searchRes)) {
40
+ versions.push(searchRes);
41
+ }
42
+ }
43
+ // order with newest version at front of array
44
+ versions = versions.sort(function (a, b) {
45
+ return semver.compare(b, a);
46
+ });
47
+
48
+ // filter out invalid / alpha versions
49
+ var validationPromises = [];
50
+ versions.forEach(function (version) {
51
+ if (!isLegacyVersion(version)) {
52
+ return;
53
+ }
54
+ validationPromises.push(
55
+ new Promise(function (resolve) {
56
+ // check if windows 64-bit ZIP exists
57
+ var win32Url = new Version({
58
+ version: version,
59
+ flavors: ["sdk"],
60
+ supportedPlatforms: ["win32"],
61
+ downloadUrl: downloadUrl,
62
+ }).platforms["win32-sdk"];
63
+ request.head(win32Url, function (err, res) {
64
+ // note: this takes a version string and replaces it with an object (will be converted back later)
65
+ resolve({
66
+ version: version,
67
+ flavors: ["sdk"],
68
+ valid: !err && res.statusCode === 200,
69
+ });
70
+ });
71
+ }),
72
+ );
73
+ });
74
+
75
+ var allPlatforms = Object.keys(Platforms);
76
+
77
+ return Promise.all(validationPromises).then(function (versions) {
78
+ // convert back to array of version strings (filtered)
79
+ return versions
80
+ .filter(function (versionObj) {
81
+ return versionObj.valid;
82
+ })
83
+ .map(function (versionObj) {
84
+ return new Version({
85
+ version: versionObj.version,
86
+ flavors: versionObj.flavors,
87
+ supportedPlatforms: allPlatforms,
88
+ downloadUrl: downloadUrl,
89
+ });
90
+ });
91
+ });
92
+ });
93
+ }
94
+
95
+ /**
96
+ * @param {string?} manifestUrl
97
+ * @returns {promise} which resolves to response body
98
+ */
99
+ function getManifest(manifestUrl) {
100
+ if (!manifestUrl) {
101
+ manifestUrl = "https://nwjs.io/versions.json";
102
+ }
103
+ return get(manifestUrl, { json: true }).then(function (body) {
104
+ return body;
105
+ });
106
+ }
107
+
108
+ /**
109
+ * @param {string} downloadUrl
110
+ * @param {string} manifestUrl
111
+ * @returns {promise} which resolves to an array of {Version}s
112
+ */
113
+ function getVersionsFromManifest(downloadUrl, manifestUrl) {
114
+ var mapFilesToPlatforms = function (files) {
115
+ return files.map(function (file) {
116
+ // convert win-x64 to win64, linux-ia32 to linux 32, etc.
117
+ return file.replace(/-(x|ia)/, "");
118
+ });
119
+ };
120
+
121
+ return getManifest(manifestUrl).then(function (manifest) {
122
+ return manifest.versions
123
+ .filter(function (versionFromManifest) {
124
+ // 0.12.3 is an exception that is in the manifest but is kind of a legacy version
125
+ return (
126
+ versionFromManifest.flavors !== undefined &&
127
+ (versionFromManifest.flavors.indexOf("sdk") !== -1 ||
128
+ versionFromManifest.version === "v0.12.3")
129
+ );
130
+ })
131
+ .map(function (versionFromManifest) {
132
+ return new Version({
133
+ version: versionFromManifest.version.replace("v", ""),
134
+ flavors: versionFromManifest.flavors,
135
+ supportedPlatforms: mapFilesToPlatforms(versionFromManifest.files),
136
+ downloadUrl: downloadUrl,
137
+ });
138
+ });
139
+ });
140
+ }
141
+
142
+ /**
143
+ * @param {string} version
144
+ * @returns {boolean}
145
+ */
146
+ function isLegacyVersion(version) {
147
+ return semver.lte(version, "0.12.3");
148
+ }
149
+
150
+ module.exports = {
151
+ /**
152
+ * Gets the latest stable version
153
+ * @param {string} downloadUrl
154
+ * @param {string} manifestUrl
155
+ * @param {string} flavor
156
+ * @returns {promise} which resolves to a {Version}
157
+ */
158
+ getLatestVersion: function (downloadUrl, manifestUrl, flavor) {
159
+ return getManifest(manifestUrl)
160
+ .then(function (manifest) {
161
+ return {
162
+ desiredVersion: manifest.stable.replace("v", ""),
163
+ downloadUrl: downloadUrl,
164
+ manifestUrl: manifestUrl,
165
+ flavor: flavor,
166
+ };
167
+ })
168
+ .then(this.getVersion);
169
+ },
170
+ /**
171
+ * @param {string} args.desiredVersion
172
+ * @param {string} args.downloadUrl
173
+ * @param {string} args.manifestUrl
174
+ * @param {string} args.flavor
175
+ * @returns {promise} which resolves to a {Version}
176
+ */
177
+ getVersion: function (args) {
178
+ return (
179
+ isLegacyVersion(args.desiredVersion)
180
+ ? getLegacyVersions
181
+ : getVersionsFromManifest
182
+ )(args.downloadUrl, args.manifestUrl).then(function (versions) {
183
+ var version = versions.findIndex(function (version) {
184
+ return version.version === args.desiredVersion;
185
+ });
186
+
187
+ return version >= 0
188
+ ? Promise.resolve(versions[version])
189
+ : Promise.reject("Version " + args.desiredVersion + " not found.");
190
+ });
191
+ },
192
+ /**
193
+ * @param {string} downloadUrl
194
+ * @param {string} manifestUrl
195
+ * @param {string} flavor
196
+ * @returns {promise} which resolves to an array of {Version}s
197
+ */
198
+ getVersions: function (downloadUrl, manifestUrl, flavor) {
199
+ return Promise.all([
200
+ getVersionsFromManifest(downloadUrl, manifestUrl, flavor),
201
+ getLegacyVersions(downloadUrl, flavor),
202
+ ]).then(function (versionLists) {
203
+ return versionLists[0].concat(versionLists[1]);
204
+ });
205
+ },
206
+ };