poll_watcher.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. 'use strict';
  2. var fs = require('fs');
  3. var path = require('path');
  4. var watch = require('watch');
  5. var common = require('./common');
  6. var EventEmitter = require('events').EventEmitter;
  7. /**
  8. * Constants
  9. */
  10. var DEFAULT_DELAY = common.DEFAULT_DELAY;
  11. var CHANGE_EVENT = common.CHANGE_EVENT;
  12. var DELETE_EVENT = common.DELETE_EVENT;
  13. var ADD_EVENT = common.ADD_EVENT;
  14. var ALL_EVENT = common.ALL_EVENT;
  15. /**
  16. * Export `PollWatcher` class.
  17. */
  18. module.exports = PollWatcher;
  19. /**
  20. * Watches `dir`.
  21. *
  22. * @class PollWatcher
  23. * @param String dir
  24. * @param {Object} opts
  25. * @public
  26. */
  27. function PollWatcher(dir, opts) {
  28. opts = common.assignOptions(this, opts);
  29. this.watched = Object.create(null);
  30. this.root = path.resolve(dir);
  31. watch.createMonitor(
  32. this.root,
  33. {
  34. interval: opts.interval || DEFAULT_DELAY,
  35. filter: this.filter.bind(this),
  36. },
  37. this.init.bind(this)
  38. );
  39. }
  40. PollWatcher.prototype.__proto__ = EventEmitter.prototype;
  41. /**
  42. * Given a fullpath of a file or directory check if we need to watch it.
  43. *
  44. * @param {string} filepath
  45. * @param {object} stat
  46. * @private
  47. */
  48. PollWatcher.prototype.filter = function(filepath, stat) {
  49. return (
  50. stat.isDirectory() ||
  51. common.isFileIncluded(
  52. this.globs,
  53. this.dot,
  54. this.doIgnore,
  55. path.relative(this.root, filepath)
  56. )
  57. );
  58. };
  59. /**
  60. * Initiate the polling file watcher with the event emitter passed from
  61. * `watch.watchTree`.
  62. *
  63. * @param {EventEmitter} monitor
  64. * @public
  65. */
  66. PollWatcher.prototype.init = function(monitor) {
  67. this.watched = monitor.files;
  68. monitor.on('changed', this.emitEvent.bind(this, CHANGE_EVENT));
  69. monitor.on('removed', this.emitEvent.bind(this, DELETE_EVENT));
  70. monitor.on('created', this.emitEvent.bind(this, ADD_EVENT));
  71. // 1 second wait because mtime is second-based.
  72. setTimeout(this.emit.bind(this, 'ready'), 1000);
  73. };
  74. /**
  75. * Transform and emit an event comming from the poller.
  76. *
  77. * @param {EventEmitter} monitor
  78. * @public
  79. */
  80. PollWatcher.prototype.emitEvent = function(type, file, stat) {
  81. file = path.relative(this.root, file);
  82. if (type === DELETE_EVENT) {
  83. // Matching the non-polling API
  84. stat = null;
  85. }
  86. this.emit(type, file, this.root, stat);
  87. this.emit(ALL_EVENT, type, file, this.root, stat);
  88. };
  89. /**
  90. * End watching.
  91. *
  92. * @public
  93. */
  94. PollWatcher.prototype.close = function(callback) {
  95. Object.keys(this.watched).forEach(function(filepath) {
  96. fs.unwatchFile(filepath);
  97. });
  98. this.removeAllListeners();
  99. if (typeof callback === 'function') {
  100. setImmediate(callback.bind(null, null, true));
  101. }
  102. };