UNPKG

12.4 kBJavaScriptView Raw
1var Core = function() { // jshint ignore:line
2
3 var registry = {}
4 var seen = {}
5 var helpers = {}
6 var customRequireDepsHandler = {
7 requireDeps: function (name, submodule, referrer) {
8 return require(name, submodule, referrer)
9 },
10 catchErrors: function (err, name, referrer) {
11 helpers.throwError('Could not find module ' + name + ' required by: ' + referrer)
12 }
13 }
14
15 helpers.isArray = function(myArray) {
16 return Object.prototype.toString.call(myArray).indexOf('Array') > -1;
17 }
18 helpers.isPlainObject = function(value) {
19 return value && (value.constructor === Object || value.constructor === undefined);
20 }
21 helpers.unreferencePlainObjectsAndArrays = function(source) {
22
23 if(helpers.isArray(source)) {
24
25 var length = source.length;
26 var unreferencedArray = [];
27
28 for (var i = 0; i < length; i++) {
29 unreferencedArray.push(helpers.unreferencePlainObjectsAndArrays(source[i]))
30 }
31
32 return unreferencedArray;
33
34 } else if(helpers.isPlainObject(source)) {
35
36 var name, s, empty = {}, dest = {};
37
38 for(name in source) {
39 /*jshint -W089 */
40 // the (!(name in empty) || empty[name] !== s) condition avoids copying properties in "source"
41 // inherited from Object.prototype. For example, if dest has a custom toString() method,
42 // don't overwrite it with the toString() method that source inherited from Object.prototype
43 s = source[name];
44 if(!(name in dest) || (dest[name] !== s && (!(name in empty) || empty[name] !== s))) {
45 dest[name] = helpers.unreferencePlainObjectsAndArrays(s);
46 }
47 }
48
49 // ugly, but I don't have other option: http://stackoverflow.com/questions/18563716/how-to-loop-through-object-propertys-created-by-object-defineproperty
50 if(source.__esModule){
51 dest.__esModule = source.__esModule;
52 }
53
54 return dest;
55 }
56
57 return source;
58 }
59 helpers.internalRequire = function(name) {
60
61 if (seen[name]) {
62 return seen[name];
63 }
64
65 if(!registry[name]) {
66 helpers.throwError('Could not find module: ' + name)
67 }
68
69 var exports = {};
70 var reified = helpers.getDependenciesValues(exports, name);
71
72 registry[name].callback.apply(this, reified);
73 seen[name] = exports
74
75 return exports;
76 }
77 helpers.getDependenciesValues = function(exports, name) {
78 var deps = registry[name].deps;
79 var reified = [];
80 var length = deps.length;
81 var dep;
82
83 for (var i = 0; i < length; i++) {
84
85 if (deps[i] === 'exports') {
86
87 reified.push(exports);
88
89 } else {
90 try {
91
92 dep = customRequireDepsHandler.requireDeps(deps[i], null, name)
93
94 reified.push(dep)
95
96 } catch(err) {
97 customRequireDepsHandler.catchErrors(err, deps[i], name)
98 }
99 }
100
101 }
102
103 return reified;
104 }
105 helpers.throwError = function(msg) {
106 throw new Error(msg);
107 }
108
109 var define = function(name, deps, callback) {
110
111 var value = {};
112
113 if (!callback) {
114
115 value.deps = [];
116 value.callback = deps;
117
118 } else {
119
120 value.deps = deps;
121 value.callback = callback;
122
123 }
124
125 if(registry[name]) {
126 helpers.throwError('duplicatedModule ' + name);
127 }
128
129 registry[name] = value;
130 }
131
132 var require = function(name, submodule) {
133 var module = helpers.internalRequire(name);
134 var amd_package = submodule ? module[submodule] : module;
135 var result = helpers.unreferencePlainObjectsAndArrays(amd_package);
136
137 return result;
138 }
139
140 var list_entries = function(options) {
141
142 var names = [];
143 var moduleName;
144 var regex = options && options.regex ? options.regex : '';
145 var shouldAddModule;
146
147 for (moduleName in registry) {
148
149 shouldAddModule = (regex && moduleName.match(regex)) || !regex;
150
151 if(shouldAddModule) {
152
153 names.push(moduleName);
154
155 }
156
157 }
158
159 return names;
160
161 }
162
163 var setCustomRequireDeps = function(handler){
164 customRequireDepsHandler = handler
165 }
166
167 return {
168 define: define,
169 require: require,
170 list_entries: list_entries,
171 setCustomRequireDeps: setCustomRequireDeps
172 }
173}
174
175var Errar = function() {
176
177 this.errors = [];
178 this.delimiter = '{{DELIMITER}}';
179 this.stackDelimiter = '{{>>>}}';
180 this.template = [
181 '{{title}}',
182 'PLUGINS:',
183 '{{plugins}}',
184 'STACKTRACE:',
185 '{{stacktrace}}'
186 ].join('\n');
187
188};
189
190Errar.prototype.hasErrors = function() {
191 return !!this.errors.length;
192};
193
194Errar.prototype.add = function(obj) {
195 this.errors.push(obj);
196};
197
198Errar.prototype.stringify = function() {
199
200 var summary = this.summary();
201 var template = this.template;
202 var isArray = function(myArray) {return Object.prototype.toString.call(myArray).indexOf('Array') > -1;};
203
204 for(var key in summary) {
205 template = template.replace('{{' + key + '}}', isArray(summary[key]) ? summary[key].join('\n') : summary[key]);
206 }
207
208 return template;
209};
210
211Errar.prototype.submodulesStringify = function() {
212
213 var hasDelimiter;
214 var nestedErrors = '';
215
216 for (var i = 0; i < this.errors.length; i++) {
217
218 if(this.errors[i].content.indexOf(this.delimiter) > -1) {
219 hasDelimiter = true;
220 nestedErrors = this.errors[i].content;
221 }
222
223 }
224
225 var moduleName = this.errors[0].name;
226 var result = hasDelimiter ? moduleName + this.stackDelimiter + nestedErrors : this.delimiter + JSON.stringify(this.errors);
227
228 return result;
229};
230
231Errar.prototype.summary = function() {
232
233 if(!this.errors.length) {
234
235 var defaultMessage = 'No errors available';
236
237 return {
238 title: defaultMessage,
239 plugins: defaultMessage,
240 stacktrace: defaultMessage
241 };
242 }
243
244 var errors = this.getRelevantErrors();
245
246 var result = {
247 title: this.formatTitle(errors),
248 plugins: this.formatPlugins(errors),
249 stacktrace: this.parseStacktrace(errors)
250 };
251
252 return result;
253};
254
255Errar.prototype.getRelevantErrors = function() {
256
257 var errors;
258
259 for (var i = 0; i < this.errors.length; i++) {
260
261 if(this.errors[i].content.indexOf(this.delimiter) > -1) {
262 errors = JSON.parse(this.errors[i].content.split(this.delimiter)[1]);
263 break;
264 }
265
266 }
267
268 if(!errors) {
269 errors = this.errors;
270 }
271
272 return errors;
273};
274
275Errar.prototype.formatTitle = function(errors) {
276
277 var last_error = errors[errors.length - 1];
278 var requiredBy = last_error.referrer ? ' required by ' + last_error.referrer : '';
279 var result = 'Error at the module: ' + last_error.name + requiredBy;
280
281 return result;
282};
283
284Errar.prototype.formatPlugins = function(errors) {
285
286 var result = [];
287
288 for (var i = 0; i < errors.length; i++) {
289 result.push('(' + errors[i].plugin + ') ' + errors[i].content);
290 }
291
292 return result;
293};
294
295Errar.prototype.parseStacktrace = function(errors) {
296
297 var first_module_from_trace = this.errors[0].name;
298 var middle_stack = this.getMiddleStacktrace();
299 var last_module_from_trace = errors[errors.length - 1].name;
300
301 var result = [];
302
303 for (var i = 0; i < middle_stack.length; i++) {
304
305 if(middle_stack[i]) {
306 result.push(middle_stack[i]);
307 }
308
309 }
310
311 if(first_module_from_trace && first_module_from_trace !== result[0]) {
312 result.splice(0, 0, first_module_from_trace);
313 }
314
315 if(last_module_from_trace && last_module_from_trace !== result[result.length - 1]) {
316 result.push(last_module_from_trace);
317 }
318
319 return result.reverse();
320};
321
322Errar.prototype.getMiddleStacktrace = function() {
323
324 var stacktrace = '';
325
326 for (var i = 0; i < this.errors.length; i++) {
327
328 if(this.errors[i].content.indexOf(this.delimiter) > -1) {
329 stacktrace = this.errors[i].content.split(this.delimiter)[0];
330 break;
331 }
332
333 }
334
335 var result = stacktrace.length ? stacktrace.split(this.stackDelimiter) : [];
336
337 return result;
338};
339
340/* global Errar */
341var NMD = function() { // jshint ignore:line
342
343 var amd = new Core();
344 var pipeline = ['default'];
345 var PLUGINS_configs = {};
346
347 var define = function(name, deps, callback) {
348 amd.define(name, deps, callback);
349 }
350
351 var require = function(name, submodule, referrer) {
352 /*
353 iterate over plugins
354 if the current plugin throws an error, move to the next plugin
355 if not, return the module
356 */
357 var result;
358 var hasResult;
359 var errorManager = new Errar();
360
361 for (var i = 0; i < pipeline.length; i++) {
362
363 try {
364
365 if(!NMD_PLUGINS[pipeline[i]]) {
366 throw new Error('Missing plugin definition for: ' + pipeline[i]);
367 }
368
369 result = NMD_PLUGINS[pipeline[i]].call(this, amd.require, PLUGINS_configs[pipeline[i]], name, submodule, referrer);
370 hasResult = true;
371
372 } catch(err) {
373
374 errorManager.add({
375 name: name,
376 plugin: pipeline[i],
377 submodule: submodule,
378 referrer: referrer,
379 content: err.message,
380 stack: err.stack
381 });
382
383 continue;
384 }
385
386 break;
387
388 }
389
390 if(!hasResult) {
391
392 throw new Error(referrer ? errorManager.submodulesStringify() : errorManager.stringify());
393
394 }
395
396 return result;
397 }
398
399 var catchErrors = function(err/*, name, referrer*/) {
400 throw new Error(err.message);
401 }
402
403 amd.setCustomRequireDeps({requireDeps: require, catchErrors: catchErrors})
404
405 return {
406 define: define,
407 require: require,
408 list_entries: amd.list_entries,
409 plugins: function(obj) {
410
411 for (var name in obj) {
412 PLUGINS_configs[name] = obj[name]
413 }
414
415 },
416 pipeline: function(list) {
417
418 if(list) {
419 pipeline = list
420 }
421
422 return pipeline
423
424 },
425 version: '0.4.3'
426 };
427
428};
429
430var NMD_PLUGINS = {}
431
432NMD_PLUGINS['default'] = function(amd_require, config, name, submodule, referrer) {
433 return amd_require(name, submodule, referrer)
434}
435
436NMD_PLUGINS.shortcuts = function(amd_require, config, name, submodule) {
437
438 var isShortcut = name.indexOf('@') === 0
439
440 if(!isShortcut) {
441 throw new Error(name + ' is not a shortcut')
442 }
443
444 var split = name.substring(1).split('/')
445 var shortcut = {
446 name: split[0],
447 value: split.slice(1).join('/')
448 }
449 var result = amd_require(config[shortcut.name] + shortcut.value, submodule)
450
451 return result
452
453}
454
455NMD_PLUGINS.defaultFile = function(amd_require, config, name, submodule) {
456
457 var hasDelimiter = name.lastIndexOf('/') === (name.length - 1)
458 var newModulePath = name + (hasDelimiter ? '' : '/') + config
459
460 return amd_require(newModulePath, submodule)
461
462}
463
464NMD_PLUGINS.relativePaths = function(amd_require, config, name, submodule, referrer) {
465
466 if (name.charAt(0) !== '.') {
467 throw new Error(name + ' is not a relative path')
468 }
469
470 if(!config) {
471 throw new Error('Relative paths are not allowed: ' + name)
472 }
473
474 var parts = name.split('/')
475 var parentBase = referrer.split('/').slice(0, -1)
476
477 for (var i = 0; i < parts.length; i++) {
478
479 var part = parts[i];
480
481 if (part === '..') {
482
483 parentBase.pop();
484
485 } else if (part === '.') {
486
487 continue;
488
489 } else {
490
491 parentBase.push(part);
492
493 }
494
495 }
496
497 var result = parentBase.join('/')
498
499 return amd_require(result, submodule, referrer)
500
501}
502
503 module.exports = NMD;
\No newline at end of file