123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- 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;
|