UNPKG

7.59 kBJavaScriptView Raw
1var request = require('request');
2var check = require('check-types');
3var verify = check.verify;
4var semver = require('semver');
5var q = require('q');
6var localVersion = require('./local-module-version');
7var isUrl = require('npm-utils').isUrl;
8var _ = require('lodash');
9
10var _registryUrl = require('npm-utils').registryUrl;
11check.verify.fn(_registryUrl, 'expected registry url function');
12var registryUrl = _.once(_registryUrl);
13
14function cleanVersion(version, name) {
15 var originalVersion = version;
16 verify.unemptyString(version, 'missing version string' + version);
17 verify.unemptyString(name, 'missing name string' + name);
18
19 if (isUrl(version)) {
20 // version = version.substr(version.indexOf('#') + 1);
21
22 // hmm, because we don't have a way to fetch git tags yet
23 // just skip these dependencies
24 console.log('Cannot handle git repos, skipping', name, 'at', version);
25 return;
26 }
27 if (version === 'original' ||
28 version === 'modified' ||
29 version === 'created') {
30 return;
31 }
32
33 version = version.replace('~', '').replace('^', '');
34 var twoDigitVersion = /^\d+\.\d+$/;
35 if (twoDigitVersion.test(version)) {
36 version += '.0';
37 }
38 if (version === 'latest' || version === '*') {
39 console.log('Module', name, 'uses version', version);
40 console.log('It is recommented to list a specific version number');
41 version = localVersion(name);
42 if (!version) {
43 version = '0.0.1';
44 }
45 console.log('module', name, 'local version', version);
46 }
47 try {
48 version = semver.clean(version);
49 } catch (err) {
50 console.error('exception when cleaning version', version);
51 return;
52 }
53 if (!version) {
54 console.error('could not clean version ' + originalVersion + ' for ' + name);
55 return;
56 }
57 console.assert(version, 'missing clean version ' + originalVersion + ' for ' + name);
58 return version;
59}
60
61function cleanVersionPair(nameVersion) {
62 check.verify.array(nameVersion, 'expected an array');
63 console.assert(nameVersion.length === 2,
64 'expected 2 items, name and version ' + nameVersion);
65 var name = nameVersion[0];
66 check.verify.string(name, 'could not get module name from ' + nameVersion);
67
68 var version = nameVersion[1];
69 check.verify.string(version, 'could not get module version from ' + nameVersion);
70 version = cleanVersion(version, name);
71 if (!version) {
72 return;
73 }
74
75 nameVersion[1] = version;
76 return nameVersion;
77}
78
79function cleanVersionObject(info) {
80 check.verify.object(info, 'expected info');
81 var name = info.name;
82 check.verify.string(name, 'could not get module name from ' + info);
83
84 var version = info.version;
85 check.verify.string(version, 'could not get module version from ' + info);
86 version = cleanVersion(version, name);
87
88 if (!version) {
89 return;
90 }
91
92 info.version = version;
93 return info;
94}
95
96function cleanVersions(nameVersionPairs) {
97 check.verify.array(nameVersionPairs, 'expected array');
98 var cleaned = nameVersionPairs
99 .map(cleanVersionObject)
100 .filter(check.object);
101 return cleaned;
102}
103
104// fetching versions inspired by
105// https://github.com/jprichardson/npm-latest
106// returns a promise
107function fetchVersions(nameVersion) {
108 // console.log(nameVersion);
109 // TODO use check.schema
110 check.verify.object(nameVersion, 'expected name, version object');
111 var name = nameVersion.name;
112 var version = nameVersion.version;
113 check.verify.string(name, 'missing name string');
114 check.verify.string(version, 'missing version string');
115
116 // console.log('fetching versions for', name, 'current version', version);
117 var MAX_WAIT_TIMEOUT = 5000;
118 var deferred = q.defer();
119
120 registryUrl().then(function (npmUrl) {
121 check.verify.webUrl(npmUrl, 'need npm registry url, got ' + npmUrl);
122
123 npmUrl = npmUrl.replace(/^https:/, 'http:').trim();
124 var url = npmUrl + name;
125
126 // TODO how to detect if the registry is not responding?
127
128 request.get(url, onNPMversions);
129 var timer = setTimeout(function () {
130 var msg = 'timed out waiting for NPM for package ' + name;
131 console.error(msg);
132 deferred.reject(msg);
133 }, MAX_WAIT_TIMEOUT);
134
135 function onNPMversions(err, response, body) {
136 clearTimeout(timer);
137
138 if (err) {
139 console.error('ERROR when fetching info for package', name);
140 deferred.reject(err.message);
141 return;
142 }
143
144 try {
145 var info = JSON.parse(body);
146 if (info.error) {
147 var str = 'ERROR in npm info for ' + name + ' reason ' + info.reason;
148 console.error(str);
149 deferred.reject(str);
150 return;
151 }
152 var versions;
153 if (info.time) {
154 versions = Object.keys(info.time);
155 } else if (info.versions) {
156 versions = Object.keys(info.versions);
157 }
158 if (!Array.isArray(versions)) {
159 throw new Error('Could not get versions for ' + name + ' from ' + info);
160 }
161
162 var validVersions = versions.filter(function (version) {
163 return cleanVersion(version, name);
164 });
165 var newerVersions = validVersions.filter(function (ver) {
166 var later = semver.gt(ver, version);
167 return later;
168 });
169
170 deferred.resolve({
171 name: name,
172 versions: newerVersions
173 });
174 return;
175 } catch (err) {
176 console.error(err);
177 deferred.reject('Could not fetch versions for ' + name);
178 return;
179 }
180 }
181 });
182
183 return deferred.promise;
184}
185
186// returns a promise with available new versions
187function nextVersions(options, nameVersionPairs, checkLatestOnly) {
188 check.verify.object(options, 'expected object with options');
189 check.verify.array(nameVersionPairs, 'expected array');
190 checkLatestOnly = !!checkLatestOnly;
191 nameVersionPairs = cleanVersions(nameVersionPairs);
192
193 var MAX_CHECK_TIMEOUT = 10000;
194 var deferred = q.defer();
195
196 if (!options.tldr) {
197 console.log('checking NPM registry');
198 }
199 var fetchPromises = nameVersionPairs.map(fetchVersions);
200 var fetchAllPromise = q.all(fetchPromises)
201 .timeout(MAX_CHECK_TIMEOUT, 'timed out waiting for NPM');
202
203 fetchAllPromise.then(function (results) {
204 var available = results.filter(function (nameNewVersions) {
205 return nameNewVersions.versions.length;
206 });
207 if (checkLatestOnly) {
208 available = available.map(function (nameVersions) {
209 if (nameVersions.versions.length > 1) {
210 nameVersions.versions = nameVersions.versions.slice(-1);
211 }
212 return nameVersions;
213 });
214 } else {
215 console.log('checking ALL versions');
216 }
217 deferred.resolve(available);
218 }, function (error) {
219 deferred.reject(error);
220 });
221
222 return deferred.promise;
223}
224
225module.exports = {
226 isUrl: isUrl,
227 cleanVersion: cleanVersion,
228 cleanVersions: cleanVersions,
229 fetchVersions: fetchVersions,
230 nextVersions: nextVersions
231};