1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | var fs = require('graceful-fs');
|
17 | var path = require('path');
|
18 | var globalConfig = require('./config/global-config');
|
19 | var ui = require('./ui');
|
20 | var dextend = require('./common').dextend;
|
21 | var HOME = require('./common').HOME;
|
22 | var Promise = require('bluebird');
|
23 | var Module = require('module');
|
24 |
|
25 | var base = process.env.jspmConfigPath && path.dirname(process.env.jspmConfigPath) || path.resolve(process.cwd());
|
26 | var registryRequireContext = new Module(base);
|
27 |
|
28 | registryRequireContext.paths = Module._nodeModulePaths(path.resolve(path.dirname(module.id), '..')).concat(Module._nodeModulePaths(base));
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | var registryClasses = {
|
35 | local: {
|
36 | constructor: {
|
37 | packageNameFormats: ['*']
|
38 | },
|
39 | lookup: function(pkg) {
|
40 | return Promise.reject('Package `local:' + pkg + '` can only be linked and not installed.');
|
41 | }
|
42 | }
|
43 | };
|
44 |
|
45 | process.on('exit', function() {
|
46 |
|
47 |
|
48 | for (var e in registryClasses) {
|
49 | if (registryClasses[e].dispose)
|
50 | registryClasses[e].dispose();
|
51 | }
|
52 | });
|
53 |
|
54 | var registryHooks = ['locate', 'lookup', 'download', 'getPackageConfig', 'processPackageConfig', 'processPackage', 'getOverride'];
|
55 |
|
56 | exports.load = function(registry) {
|
57 | if (registryClasses[registry])
|
58 | return registryClasses[registry];
|
59 |
|
60 | try {
|
61 |
|
62 | var tmpDir = path.resolve(HOME, '.jspm', registry + '-cache');
|
63 | if (!fs.existsSync(tmpDir))
|
64 | fs.mkdirSync(tmpDir);
|
65 |
|
66 | var options = dextend({
|
67 | timeouts: {
|
68 | lookup: 60,
|
69 | download: 300,
|
70 | process: 120
|
71 | },
|
72 | tmpDir: tmpDir,
|
73 | apiVersion: '2.0'
|
74 | }, globalConfig.config.registries[registry] || {});
|
75 |
|
76 | options.name = registry;
|
77 | if (globalConfig.config.strictSSL === false || globalConfig.config.strictSSL == 'false')
|
78 | options.strictSSL = false;
|
79 |
|
80 | if (!options.handler)
|
81 | throw 'Registry %' + registry + '% not found.';
|
82 |
|
83 | var RegistryClass = registryRequireContext.require(options.handler);
|
84 | var registryPackageJSON = registryRequireContext.require(options.handler + '/package.json');
|
85 | var versionString = registryPackageJSON.name + '@' + registryPackageJSON.version.split('.').splice(0, 2).join('.');
|
86 | options.versionString = versionString;
|
87 |
|
88 | var registryInstance = registryClasses[registry] = new RegistryClass(options, ui);
|
89 | registryInstance.constructor = RegistryClass;
|
90 |
|
91 | var timeoutLookup = options.timeouts.lookup * 1000;
|
92 | var timeoutDownload = options.timeouts.download * 1000;
|
93 | var timeoutProcess = options.timeouts.process * 1000;
|
94 |
|
95 | registryInstance.versionString = registryInstance.versionString || versionString;
|
96 |
|
97 | var maxRetries = globalConfig.config.maxRetries || 3;
|
98 |
|
99 | if (registryInstance.build) {
|
100 | ui.log('warn', 'Registry handler %' + options.handler + '% provides a `build` hook, which has been deprecated for `processPackage`.');
|
101 | registryInstance.processPackage = function(packageConfig, packageName, packageDir) {
|
102 | return this.build(packageConfig, packageDir);
|
103 | };
|
104 | }
|
105 |
|
106 |
|
107 | registryHooks.forEach(function(hook) {
|
108 | if (!registryInstance[hook])
|
109 | return;
|
110 |
|
111 | var runHook = registryInstance[hook];
|
112 | registryInstance[hook] = function() {
|
113 | var self = this;
|
114 | var args = arguments;
|
115 | var retries = 0;
|
116 | var timeout;
|
117 | if (hook == 'download')
|
118 | timeout = timeoutDownload;
|
119 | else if (hook == 'processPackage')
|
120 | timeout = timeoutProcess;
|
121 | else
|
122 | timeout = timeoutLookup;
|
123 |
|
124 | return new Promise(function(resolve, reject) {
|
125 |
|
126 | function tryHook() {
|
127 | var active = true;
|
128 |
|
129 | var timer = setTimeout(function() {
|
130 | active = false;
|
131 | checkRetry();
|
132 | }, timeout);
|
133 |
|
134 |
|
135 |
|
136 | (self.reconfigPromise_ || Promise.resolve(self))
|
137 | .then(function(endpoint) {
|
138 | self = endpoint;
|
139 | return runHook.apply(self, args);
|
140 | })
|
141 | .then(function(result) {
|
142 | clearTimeout(timer);
|
143 | if (active)
|
144 | resolve(result);
|
145 | }, function(err) {
|
146 | clearTimeout(timer);
|
147 | if (!active)
|
148 | return;
|
149 | active = false;
|
150 | return checkConfigure(err) || checkRetry(err);
|
151 | });
|
152 | }
|
153 |
|
154 | |
155 |
|
156 | function checkConfigure(err) {
|
157 | if (err && err.config && !self.triedConfig) {
|
158 |
|
159 |
|
160 | if (!self.reconfigPromise_) {
|
161 | ui.log('warn', err.message);
|
162 |
|
163 | self.reconfigPromise_ = exports.configure(registry)
|
164 | .then(function() {
|
165 |
|
166 | delete registryClasses[registry];
|
167 | var instance = exports.load(registry);
|
168 | instance.triedConfig = true;
|
169 | return instance;
|
170 | });
|
171 | }
|
172 |
|
173 | tryHook();
|
174 | return true;
|
175 | }
|
176 | }
|
177 |
|
178 | function checkRetry(err) {
|
179 |
|
180 | if (hook === 'processPackage' || hook === 'processPackageConfig')
|
181 | retries = maxRetries;
|
182 |
|
183 | retries++;
|
184 | var retriable = !err || err.retriable;
|
185 | var retry = retriable && retries <= maxRetries;
|
186 |
|
187 | var msg = (err ? 'Error' : 'Timed out') + ' on ' + hook +
|
188 | (typeof args[0] === 'string' ? ' for `' + registry + ':' + args[0] + '`' : '') +
|
189 | (retry ? ', retrying (' + retries + ').' : '') +
|
190 |
|
191 | (!err ? '\nTo increase the timeout run %jspm config registries.' + registry + '.timeouts.' + (hook == 'download' || hook == 'build' ? hook : 'lookup') + ' ' + timeout / 1000 * 2 + '%' : '') +
|
192 | (err ? '\n' + (!err.hideStack && err.stack || err) : '');
|
193 | if (retry) {
|
194 | ui.log('warn', msg);
|
195 | return tryHook();
|
196 | }
|
197 | else {
|
198 | return reject(msg);
|
199 | }
|
200 | }
|
201 | tryHook();
|
202 | });
|
203 | };
|
204 | });
|
205 |
|
206 | return registryInstance;
|
207 | }
|
208 | catch(e) {
|
209 | ui.log('err', !e.hideStack && e.stack || e);
|
210 | throw 'Unable to load registry %' + registry + '%';
|
211 | }
|
212 | };
|
213 |
|
214 | exports.configure = function(registry) {
|
215 | var registryConfig = globalConfig.config.registries[registry] || {},
|
216 | RegistryClass;
|
217 |
|
218 | if (!registryConfig.handler)
|
219 | throw 'Registry %' + registry + '% not found.';
|
220 |
|
221 | var handler = registryConfig.handler;
|
222 | delete registryConfig.handler;
|
223 |
|
224 | try {
|
225 | RegistryClass = registryRequireContext.require(handler);
|
226 | }
|
227 | catch(e) {
|
228 | throw 'Registry handler `' + handler + '` not installed.';
|
229 | }
|
230 |
|
231 | registryConfig.name = registry;
|
232 | registryConfig.strictSSL = globalConfig.config.strictSSL;
|
233 |
|
234 | return Promise.resolve(RegistryClass.configure && RegistryClass.configure(registryConfig, ui) || registryConfig)
|
235 | .then(function(_config) {
|
236 | delete _config.name;
|
237 | delete _config.strictSSL;
|
238 | _config.handler = handler;
|
239 | globalConfig.config.registries[registry] = _config;
|
240 | })
|
241 | .then(function() {
|
242 | globalConfig.save();
|
243 | });
|
244 | };
|
245 |
|
246 |
|
247 |
|
248 | exports.create = function(name, handler, override) {
|
249 |
|
250 |
|
251 | if (!override && globalConfig.config.registries[name]) {
|
252 | if (globalConfig.config.registries[name].handler === handler)
|
253 | return ui.confirm('Registry %' + name + '% already exists. Do you want to reconfigure it now?')
|
254 | .then(function(configure) {
|
255 | if (configure)
|
256 | return Promise.resolve(exports.configure(name))
|
257 | .then(function() {
|
258 | ui.log('ok', 'Registry %' + name + '% configured successfully.');
|
259 | return false;
|
260 | });
|
261 | else
|
262 | return false;
|
263 | });
|
264 | else
|
265 | return ui.confirm('Registry %' + name + '% already exists, but based on `' + globalConfig.config.registries[name].handler + '`. Are you sure you want to override it?')
|
266 | .then(function(override) {
|
267 | if (override)
|
268 | return Promise.resolve(exports.create(name, handler, true));
|
269 | return false;
|
270 | });
|
271 | }
|
272 |
|
273 | var registryConfig = globalConfig.config.registries[name] = globalConfig.config.registries[name] || {};
|
274 | registryConfig.handler = handler;
|
275 |
|
276 |
|
277 | return exports.configure(name);
|
278 | };
|