cache.js
3.13 KB
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
var assert = require("assert");
var Q = require("q");
var fs = require("fs");
var path = require("path");
var util = require("./util");
var EventEmitter = require("events").EventEmitter;
var hasOwn = Object.prototype.hasOwnProperty;
/**
* ReadFileCache is an EventEmitter subclass that caches file contents in
* memory so that subsequent calls to readFileP return the same contents,
* regardless of any changes in the underlying file.
*/
function ReadFileCache(sourceDir, charset) {
assert.ok(this instanceof ReadFileCache);
assert.strictEqual(typeof sourceDir, "string");
this.charset = charset;
EventEmitter.call(this);
Object.defineProperties(this, {
sourceDir: { value: sourceDir },
sourceCache: { value: {} }
});
}
util.inherits(ReadFileCache, EventEmitter);
var RFCp = ReadFileCache.prototype;
/**
* Read a file from the cache if possible, else from disk.
*/
RFCp.readFileP = function(relativePath) {
var cache = this.sourceCache;
relativePath = path.normalize(relativePath);
return hasOwn.call(cache, relativePath)
? cache[relativePath]
: this.noCacheReadFileP(relativePath);
};
/**
* Read (or re-read) a file without using the cache.
*
* The new contents are stored in the cache for any future calls to
* readFileP.
*/
RFCp.noCacheReadFileP = function(relativePath) {
relativePath = path.normalize(relativePath);
var added = !hasOwn.call(this.sourceCache, relativePath);
var promise = this.sourceCache[relativePath] = util.readFileP(
path.join(this.sourceDir, relativePath), this.charset);
if (added) {
this.emit("added", relativePath);
}
return promise;
};
/**
* If you have reason to believe the contents of a file have changed, call
* this method to re-read the file and compare the new contents to the
* cached contents. If the new contents differ from the contents of the
* cache, the "changed" event will be emitted.
*/
RFCp.reportPossiblyChanged = function(relativePath) {
var self = this;
var cached = self.readFileP(relativePath);
var fresh = self.noCacheReadFileP(relativePath);
Q.spread([
cached.catch(orNull),
fresh.catch(orNull)
], function(oldData, newData) {
if (oldData !== newData) {
self.emit("changed", relativePath);
}
}).done();
};
/**
* Invoke the given callback for all files currently known to the
* ReadFileCache, and invoke it in the future when any new files become
* known to the cache.
*/
RFCp.subscribe = function(callback, context) {
for (var relativePath in this.sourceCache) {
if (hasOwn.call(this.sourceCache, relativePath)) {
callback.call(context || null, relativePath);
}
}
this.on("added", function(relativePath) {
callback.call(context || null, relativePath);
});
};
/**
* Avoid memory leaks by removing listeners and emptying the cache.
*/
RFCp.clear = function() {
this.removeAllListeners();
for (var relativePath in this.sourceCache) {
delete this.sourceCache[relativePath];
}
};
function orNull(err) {
return null;
}
exports.ReadFileCache = ReadFileCache;