watchpack.js 4.2 KB

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