watchpack.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. var watcherManager = require("./watcherManager");
  6. var EventEmitter = require("events").EventEmitter;
  7. function Watchpack(options) {
  8. EventEmitter.call(this);
  9. if(!options) options = {};
  10. if(!options.aggregateTimeout) options.aggregateTimeout = 200;
  11. this.options = options;
  12. this.watcherOptions = {
  13. ignored: options.ignored,
  14. poll: options.poll
  15. };
  16. this.fileWatchers = [];
  17. this.dirWatchers = [];
  18. this.mtimes = Object.create(null);
  19. this.paused = false;
  20. this.aggregatedChanges = [];
  21. this.aggregatedRemovals = [];
  22. this.aggregateTimeout = 0;
  23. this._onTimeout = this._onTimeout.bind(this);
  24. }
  25. module.exports = Watchpack;
  26. Watchpack.prototype = Object.create(EventEmitter.prototype);
  27. Watchpack.prototype.watch = function watch(files, directories, startTime) {
  28. this.paused = false;
  29. var oldFileWatchers = this.fileWatchers;
  30. var oldDirWatchers = this.dirWatchers;
  31. this.fileWatchers = files.map(function(file) {
  32. return this._fileWatcher(file, watcherManager.watchFile(file, this.watcherOptions, startTime));
  33. }, this);
  34. this.dirWatchers = directories.map(function(dir) {
  35. return this._dirWatcher(dir, watcherManager.watchDirectory(dir, this.watcherOptions, startTime));
  36. }, this);
  37. oldFileWatchers.forEach(function(w) {
  38. w.close();
  39. }, this);
  40. oldDirWatchers.forEach(function(w) {
  41. w.close();
  42. }, this);
  43. };
  44. Watchpack.prototype.close = function resume() {
  45. this.paused = true;
  46. if(this.aggregateTimeout)
  47. clearTimeout(this.aggregateTimeout);
  48. this.fileWatchers.forEach(function(w) {
  49. w.close();
  50. }, this);
  51. this.dirWatchers.forEach(function(w) {
  52. w.close();
  53. }, this);
  54. this.fileWatchers.length = 0;
  55. this.dirWatchers.length = 0;
  56. };
  57. Watchpack.prototype.pause = function pause() {
  58. this.paused = true;
  59. if(this.aggregateTimeout)
  60. clearTimeout(this.aggregateTimeout);
  61. };
  62. function addWatchersToArray(watchers, array) {
  63. watchers.forEach(function(w) {
  64. if(array.indexOf(w.directoryWatcher) < 0) {
  65. array.push(w.directoryWatcher);
  66. addWatchersToArray(Object.keys(w.directoryWatcher.directories).reduce(function(a, dir) {
  67. if(w.directoryWatcher.directories[dir] !== true)
  68. a.push(w.directoryWatcher.directories[dir]);
  69. return a;
  70. }, []), array);
  71. }
  72. });
  73. }
  74. Watchpack.prototype.getTimes = function() {
  75. var directoryWatchers = [];
  76. addWatchersToArray(this.fileWatchers.concat(this.dirWatchers), directoryWatchers);
  77. var obj = Object.create(null);
  78. directoryWatchers.forEach(function(w) {
  79. var times = w.getTimes();
  80. Object.keys(times).forEach(function(file) {
  81. obj[file] = times[file];
  82. });
  83. });
  84. return obj;
  85. };
  86. Watchpack.prototype._fileWatcher = function _fileWatcher(file, watcher) {
  87. watcher.on("change", function(mtime, type) {
  88. this._onChange(file, mtime, file, type);
  89. }.bind(this));
  90. watcher.on("remove", function(type) {
  91. this._onRemove(file, file, type);
  92. }.bind(this));
  93. return watcher;
  94. };
  95. Watchpack.prototype._dirWatcher = function _dirWatcher(item, watcher) {
  96. watcher.on("change", function(file, mtime, type) {
  97. this._onChange(item, mtime, file, type);
  98. }.bind(this));
  99. return watcher;
  100. };
  101. Watchpack.prototype._onChange = function _onChange(item, mtime, file) {
  102. file = file || item;
  103. this.mtimes[file] = mtime;
  104. if(this.paused) return;
  105. this.emit("change", file, mtime);
  106. if(this.aggregateTimeout)
  107. clearTimeout(this.aggregateTimeout);
  108. if(this.aggregatedChanges.indexOf(item) < 0)
  109. this.aggregatedChanges.push(item);
  110. this.aggregateTimeout = setTimeout(this._onTimeout, this.options.aggregateTimeout);
  111. };
  112. Watchpack.prototype._onRemove = function _onRemove(item, file) {
  113. file = file || item;
  114. delete this.mtimes[item];
  115. if(this.paused) return;
  116. this.emit("remove", item);
  117. if(this.aggregateTimeout)
  118. clearTimeout(this.aggregateTimeout);
  119. if(this.aggregatedRemovals.indexOf(item) < 0)
  120. this.aggregatedRemovals.push(item);
  121. this.aggregateTimeout = setTimeout(this._onTimeout, this.options.aggregateTimeout);
  122. };
  123. Watchpack.prototype._onTimeout = function _onTimeout() {
  124. this.aggregateTimeout = 0;
  125. var changes = this.aggregatedChanges;
  126. var removals = this.aggregatedRemovals;
  127. this.aggregatedChanges = [];
  128. this.aggregatedRemovals = [];
  129. this.emit("aggregated", changes, removals);
  130. };