Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81159 views
1
var assert = require("assert");
2
var Q = require("q");
3
var fs = require("fs");
4
var path = require("path");
5
var util = require("./util");
6
var EventEmitter = require("events").EventEmitter;
7
var hasOwn = Object.prototype.hasOwnProperty;
8
9
/**
10
* ReadFileCache is an EventEmitter subclass that caches file contents in
11
* memory so that subsequent calls to readFileP return the same contents,
12
* regardless of any changes in the underlying file.
13
*/
14
function ReadFileCache(sourceDir, charset) {
15
assert.ok(this instanceof ReadFileCache);
16
assert.strictEqual(typeof sourceDir, "string");
17
18
this.charset = charset;
19
20
EventEmitter.call(this);
21
22
Object.defineProperties(this, {
23
sourceDir: { value: sourceDir },
24
sourceCache: { value: {} }
25
});
26
}
27
28
util.inherits(ReadFileCache, EventEmitter);
29
var RFCp = ReadFileCache.prototype;
30
31
/**
32
* Read a file from the cache if possible, else from disk.
33
*/
34
RFCp.readFileP = function(relativePath) {
35
var cache = this.sourceCache;
36
37
relativePath = path.normalize(relativePath);
38
39
return hasOwn.call(cache, relativePath)
40
? cache[relativePath]
41
: this.noCacheReadFileP(relativePath);
42
};
43
44
/**
45
* Read (or re-read) a file without using the cache.
46
*
47
* The new contents are stored in the cache for any future calls to
48
* readFileP.
49
*/
50
RFCp.noCacheReadFileP = function(relativePath) {
51
relativePath = path.normalize(relativePath);
52
53
var added = !hasOwn.call(this.sourceCache, relativePath);
54
var promise = this.sourceCache[relativePath] = util.readFileP(
55
path.join(this.sourceDir, relativePath), this.charset);
56
57
if (added) {
58
this.emit("added", relativePath);
59
}
60
61
return promise;
62
};
63
64
/**
65
* If you have reason to believe the contents of a file have changed, call
66
* this method to re-read the file and compare the new contents to the
67
* cached contents. If the new contents differ from the contents of the
68
* cache, the "changed" event will be emitted.
69
*/
70
RFCp.reportPossiblyChanged = function(relativePath) {
71
var self = this;
72
var cached = self.readFileP(relativePath);
73
var fresh = self.noCacheReadFileP(relativePath);
74
75
Q.spread([
76
cached.catch(orNull),
77
fresh.catch(orNull)
78
], function(oldData, newData) {
79
if (oldData !== newData) {
80
self.emit("changed", relativePath);
81
}
82
}).done();
83
};
84
85
/**
86
* Invoke the given callback for all files currently known to the
87
* ReadFileCache, and invoke it in the future when any new files become
88
* known to the cache.
89
*/
90
RFCp.subscribe = function(callback, context) {
91
for (var relativePath in this.sourceCache) {
92
if (hasOwn.call(this.sourceCache, relativePath)) {
93
callback.call(context || null, relativePath);
94
}
95
}
96
97
this.on("added", function(relativePath) {
98
callback.call(context || null, relativePath);
99
});
100
};
101
102
/**
103
* Avoid memory leaks by removing listeners and emptying the cache.
104
*/
105
RFCp.clear = function() {
106
this.removeAllListeners();
107
108
for (var relativePath in this.sourceCache) {
109
delete this.sourceCache[relativePath];
110
}
111
};
112
113
function orNull(err) {
114
return null;
115
}
116
117
exports.ReadFileCache = ReadFileCache;
118
119