249 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| function _fs() {
 | |
|   const data = _interopRequireDefault(require('fs'));
 | |
| 
 | |
|   _fs = function _fs() {
 | |
|     return data;
 | |
|   };
 | |
| 
 | |
|   return data;
 | |
| }
 | |
| 
 | |
| function _path() {
 | |
|   const data = _interopRequireDefault(require('path'));
 | |
| 
 | |
|   _path = function _path() {
 | |
|     return data;
 | |
|   };
 | |
| 
 | |
|   return data;
 | |
| }
 | |
| 
 | |
| function _events() {
 | |
|   const data = require('events');
 | |
| 
 | |
|   _events = function _events() {
 | |
|     return data;
 | |
|   };
 | |
| 
 | |
|   return data;
 | |
| }
 | |
| 
 | |
| function _anymatch() {
 | |
|   const data = _interopRequireDefault(require('anymatch'));
 | |
| 
 | |
|   _anymatch = function _anymatch() {
 | |
|     return data;
 | |
|   };
 | |
| 
 | |
|   return data;
 | |
| }
 | |
| 
 | |
| function _micromatch() {
 | |
|   const data = _interopRequireDefault(require('micromatch'));
 | |
| 
 | |
|   _micromatch = function _micromatch() {
 | |
|     return data;
 | |
|   };
 | |
| 
 | |
|   return data;
 | |
| }
 | |
| 
 | |
| function _walker() {
 | |
|   const data = _interopRequireDefault(require('walker'));
 | |
| 
 | |
|   _walker = function _walker() {
 | |
|     return data;
 | |
|   };
 | |
| 
 | |
|   return data;
 | |
| }
 | |
| 
 | |
| function _interopRequireDefault(obj) {
 | |
|   return obj && obj.__esModule ? obj : {default: obj};
 | |
| }
 | |
| 
 | |
| function _defineProperty(obj, key, value) {
 | |
|   if (key in obj) {
 | |
|     Object.defineProperty(obj, key, {
 | |
|       value: value,
 | |
|       enumerable: true,
 | |
|       configurable: true,
 | |
|       writable: true
 | |
|     });
 | |
|   } else {
 | |
|     obj[key] = value;
 | |
|   }
 | |
|   return obj;
 | |
| }
 | |
| 
 | |
| let fsevents;
 | |
| 
 | |
| try {
 | |
|   fsevents = require('fsevents');
 | |
| } catch (e) {
 | |
|   // Optional dependency, only supported on Darwin.
 | |
| }
 | |
| 
 | |
| const CHANGE_EVENT = 'change';
 | |
| const DELETE_EVENT = 'delete';
 | |
| const ADD_EVENT = 'add';
 | |
| const ALL_EVENT = 'all';
 | |
| 
 | |
| /**
 | |
|  * Export `FSEventsWatcher` class.
 | |
|  * Watches `dir`.
 | |
|  */
 | |
| class FSEventsWatcher extends _events().EventEmitter {
 | |
|   static isSupported() {
 | |
|     return fsevents !== undefined;
 | |
|   }
 | |
| 
 | |
|   static normalizeProxy(callback) {
 | |
|     return (filepath, stats) =>
 | |
|       callback(_path().default.normalize(filepath), stats);
 | |
|   }
 | |
| 
 | |
|   static recReaddir(
 | |
|     dir,
 | |
|     dirCallback,
 | |
|     fileCallback,
 | |
|     endCallback,
 | |
|     errorCallback,
 | |
|     ignored
 | |
|   ) {
 | |
|     (0, _walker().default)(dir)
 | |
|       .filterDir(
 | |
|         currentDir => !ignored || !(0, _anymatch().default)(ignored, currentDir)
 | |
|       )
 | |
|       .on('dir', FSEventsWatcher.normalizeProxy(dirCallback))
 | |
|       .on('file', FSEventsWatcher.normalizeProxy(fileCallback))
 | |
|       .on('error', errorCallback)
 | |
|       .on('end', () => {
 | |
|         endCallback();
 | |
|       });
 | |
|   }
 | |
| 
 | |
|   constructor(dir, opts) {
 | |
|     if (!fsevents) {
 | |
|       throw new Error(
 | |
|         '`fsevents` unavailable (this watcher can only be used on Darwin)'
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     super();
 | |
| 
 | |
|     _defineProperty(this, 'root', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'ignored', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'glob', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'dot', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'hasIgnore', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'doIgnore', void 0);
 | |
| 
 | |
|     _defineProperty(this, 'watcher', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_tracked', void 0);
 | |
| 
 | |
|     this.dot = opts.dot || false;
 | |
|     this.ignored = opts.ignored;
 | |
|     this.glob = Array.isArray(opts.glob) ? opts.glob : [opts.glob];
 | |
|     this.hasIgnore =
 | |
|       Boolean(opts.ignored) && !(Array.isArray(opts) && opts.length > 0);
 | |
|     this.doIgnore = opts.ignored
 | |
|       ? (0, _anymatch().default)(opts.ignored)
 | |
|       : () => false;
 | |
|     this.root = _path().default.resolve(dir);
 | |
|     this.watcher = fsevents(this.root);
 | |
|     this.watcher.start().on('change', this.handleEvent.bind(this));
 | |
|     this._tracked = new Set();
 | |
|     FSEventsWatcher.recReaddir(
 | |
|       this.root,
 | |
|       filepath => {
 | |
|         this._tracked.add(filepath);
 | |
|       },
 | |
|       filepath => {
 | |
|         this._tracked.add(filepath);
 | |
|       },
 | |
|       this.emit.bind(this, 'ready'),
 | |
|       this.emit.bind(this, 'error'),
 | |
|       this.ignored
 | |
|     );
 | |
|   }
 | |
|   /**
 | |
|    * End watching.
 | |
|    */
 | |
| 
 | |
|   close(callback) {
 | |
|     this.watcher.stop();
 | |
|     this.removeAllListeners();
 | |
| 
 | |
|     if (typeof callback === 'function') {
 | |
|       process.nextTick(callback.bind(null, null, true));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   isFileIncluded(relativePath) {
 | |
|     if (this.doIgnore(relativePath)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return this.glob.length
 | |
|       ? _micromatch().default.some(relativePath, this.glob, {
 | |
|           dot: this.dot
 | |
|         })
 | |
|       : this.dot || _micromatch().default.some(relativePath, '**/*');
 | |
|   }
 | |
| 
 | |
|   handleEvent(filepath) {
 | |
|     const relativePath = _path().default.relative(this.root, filepath);
 | |
| 
 | |
|     if (!this.isFileIncluded(relativePath)) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     _fs().default.lstat(filepath, (error, stat) => {
 | |
|       if (error && error.code !== 'ENOENT') {
 | |
|         this.emit('error', error);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (error) {
 | |
|         // Ignore files that aren't tracked and don't exist.
 | |
|         if (!this._tracked.has(filepath)) {
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         this._emit(DELETE_EVENT, relativePath);
 | |
| 
 | |
|         this._tracked.delete(filepath);
 | |
| 
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (this._tracked.has(filepath)) {
 | |
|         this._emit(CHANGE_EVENT, relativePath, stat);
 | |
|       } else {
 | |
|         this._tracked.add(filepath);
 | |
| 
 | |
|         this._emit(ADD_EVENT, relativePath, stat);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
|   /**
 | |
|    * Emit events.
 | |
|    */
 | |
| 
 | |
|   _emit(type, file, stat) {
 | |
|     this.emit(type, file, this.root, stat);
 | |
|     this.emit(ALL_EVENT, type, file, this.root, stat);
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = FSEventsWatcher;
 |