-
Notifications
You must be signed in to change notification settings - Fork 0
/
reload.js
137 lines (113 loc) · 3.45 KB
/
reload.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
var EventEmitter = require('events').EventEmitter,
debounce = require('debounce'),
first = require('ee-first'),
util = require('util'),
fs = require('fs');
// Load a JSON from the specified path. The loader can be used as a
// EventEmitter to track the progress is returned. This is used to coordinate
// multiple simultaneous requests for the same file into a single `readFile`
function JSONLoader(path) {
var self = this;
fs.readFile(path, function (err, data) {
var json;
if (err) {
return self.emit('error', err);
}
try {
json = JSON.parse(data);
} catch (e) {
return self.emit('error', e);
}
self.emit('load', json);
});
}
util.inherits(JSONLoader, EventEmitter);
// OPTIONS
// - persistent reload in watch
function ReloadJSON(options) {
options = options || {};
this.files = {};
this.watching = {};
this.persistent = options.persistent;
this.delay = options.delay || 10;
}
util.inherits(ReloadJSON, EventEmitter);
// Start reading a json file
ReloadJSON.prototype.read = function (path) {
var self = this,
files = this.files,
file = files[path] = new JSONLoader(path);
first([
[file, 'error', 'load'],
], function (err, ee, ev, args) {
var data;
if (err) {
// If an error occurs the loader needs to be invalidated.
files[path] = null;
return self.emit('error', err);
}
// Once loaded store data in cache and configure a watch on the
// file so that it can be reloaded when it is changed.
data = args[0];
files[path] = data
self.configureWatch(path);
self.emit('load', path, data);
});
return file;
}
// Configure a `fs.watch` on the given path that will trigger a reload if the
// file is changed.
ReloadJSON.prototype.configureWatch = function (path) {
var self = this,
watching = this.watching,
delay = this.delay,
watch;
if (watching[path]) {
return;
}
watch = watching[path] = fs.watch(path, {
persistent: this.persistent
});
// Make sure old data is not used again
watch.on('change', function (event, filename) {
self.files[path] = null;
self.emit('change', event, filename);
});
// Trigger a new read of the file after a debounce timeout, it's
// possible a read was started in the meantime in that case no read is
// triggered from the watch.
watch.on('change', debounce(function (ev) {
if (ev === 'change' && !self.files[path]) {
self.read(path);
}
}, delay));
watch.on('error', function (err) {
self.emit('error', err);
});
}
// Load json from the specified path. The result is cached and multiple request
// is consolidated into a single file read.
ReloadJSON.prototype.load = function (path, callback) {
var self = this,
files = this.files,
file = files[path];
if (file) {
if (!(file instanceof EventEmitter)) {
return setImmediate(function () {
callback(null, file);
});
}
} else {
file = this.read(path);
}
first([
[file, 'load', 'error']
], function (err, ee, ev, args) {
callback(err, err ? null : args[0]);
});
// Return a thunk
return function (fun) {
callback = fun;
}
}
module.exports = ReloadJSON;