Blame view

node_modules/commoner/lib/cache.js 3.13 KB
ce4c83ff   wxy   初始提交
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;