-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
148 lines (131 loc) · 3.92 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
'use strict';
/**
* Create a new lazy initialisation hook.
*
* @param {Object} structure The hook where all initialisation methods are on.
* @returns {Function} initialisation.
* @api public
*/
exports.on = function on(structure) {
/**
* Lazy load our exports so they are only included when needed.
*
* @param {String} name The name or key of the `export`
* @param {Function} fn The function that needs to be executed once to load
* @api private
*/
function initialise(name, fn, options) {
Object.defineProperty(structure, name, {
configurable: true,
//
// The lazy initialiser, it will set the value of the returned function for
// the given name.
//
get: function get() {
/**
* Simple clean up method.
*
* @param {String} cleanup
* @param {String} method
* @api private
*/
function register(cleanup, method) {
cleanup = cleanup || name;
//
// Register that this module needs a clean up.
//
initialise.registered[cleanup] = {
method: method || true,
async: this === 'async'
};
}
//
// Allow an more awesome clean up interface by telling that shit is
// async:
//
// initialise('foo', function (cleanup) {
// cleanup('method');
// cleanup.async('method');
// });
//
register.async = register.bind('async');
return Object.defineProperty(this, name, {
value: fn.call(structure, register, options || {})
})[name];
},
//
// Simple helper function that will set the value and remove our
// initialisation structure. This is required to make the `get` working.
//
set: function set(val) {
return Object.defineProperty(this, name, {
value: val
})[name];
}
});
}
//
// Simple registery that contains the exports that need to be cleaned up.
//
initialise.registered = Object.create(null);
/**
* This will destroy all references and clean up all the initialised code so we
* can exit cleanly.
*
* @param {Function} fn Callback function for when everything is cleared.
* @api public
*/
initialise.end = function end(fn) {
var todo = 0
, done = 0;
/**
* A poor man's async helper. It stores the last error and calls the
* callback once all async cleanup has finished.
*
* @param {Error} err Optional error
* @api private
*/
function next(err) {
if (err) next.err = err;
if (++done === todo && fn) fn(next.err);
}
Object.keys(initialise.registered).forEach(function each(name) {
var unregister = initialise.registered[name]
, method = unregister.method
, instance = structure[name]
, async = unregister.async
, type = typeof method;
//
// Allow custom clean-up functions to be used. This is useful if you want to
// gracefully exit first and if that fails, forcefully exit. Or mabye it
// requires specific arguments etc.
//
if ('function' === type) {
if (!async) return method();
todo++;
return method(next);
}
//
// If no clean-up method is provided try to guess the method instead. There
// are couple of common `api`'s that can be used here.
//
if ('string' !== typeof method) {
if ('end' in instance) method = 'end';
else if ('close' in instance) method = 'close';
else if ('destroy' in instance) method = 'destroy';
}
// Savely clean it up.
if (method in instance) {
if (!async) return instance[method]();
todo++;
return instance[method](next);
}
});
// Optional callback.
if (fn && todo === done) fn();
};
//
// Expose the initialise function.
//
return initialise;
};