/ cloudformation-templates / node_modules / sane / src / watchexec_watcher.js
watchexec_watcher.js
  1  'use strict';
  2  
  3  const execa = require('execa');
  4  
  5  const { statSync } = require('fs');
  6  const path = require('path');
  7  const common = require('./common');
  8  const EventEmitter = require('events').EventEmitter;
  9  
 10  const { EOL } = require('os');
 11  
 12  /**
 13   * Constants
 14   */
 15  
 16  const CHANGE_EVENT = common.CHANGE_EVENT;
 17  const DELETE_EVENT = common.DELETE_EVENT;
 18  const ADD_EVENT = common.ADD_EVENT;
 19  const ALL_EVENT = common.ALL_EVENT;
 20  
 21  const typeMap = {
 22    rename: CHANGE_EVENT,
 23    write: CHANGE_EVENT,
 24    remove: DELETE_EVENT,
 25    create: ADD_EVENT,
 26  };
 27  
 28  const messageRegexp = /(rename|write|remove|create)\s(.+)/;
 29  
 30  /**
 31   * Manages streams from subprocess and turns into sane events
 32   *
 33   * @param {Stream} data
 34   * @private
 35   */
 36  function _messageHandler(data) {
 37    data
 38      .toString()
 39      .split(EOL)
 40      .filter(str => str.trim().length)
 41      .filter(str => messageRegexp.test(str))
 42      .map(line => {
 43        const [, command, path] = [...line.match(messageRegexp)];
 44        return [command, path];
 45      })
 46      .forEach(([command, file]) => {
 47        let stat;
 48        const type = typeMap[command];
 49        if (type === DELETE_EVENT) {
 50          stat = null;
 51        } else {
 52          try {
 53            stat = statSync(file);
 54          } catch (e) {
 55            // There is likely a delete coming down the pipe.
 56            if (e.code === 'ENOENT') {
 57              return;
 58            }
 59            throw e;
 60          }
 61        }
 62        this.emitEvent(type, path.relative(this.root, file), stat);
 63      });
 64  }
 65  
 66  /**
 67   * Export `WatchexecWatcher` class.
 68   * Watches `dir`.
 69   *
 70   * @class WatchexecWatcher
 71   * @param String dir
 72   * @param {Object} opts
 73   * @public
 74   */
 75  class WatchexecWatcher extends EventEmitter {
 76    constructor(dir, opts) {
 77      super();
 78  
 79      common.assignOptions(this, opts);
 80  
 81      this.root = path.resolve(dir);
 82  
 83      this._process = execa(
 84        'watchexec',
 85        ['-n', '--', 'node', __dirname + '/watchexec_client.js'],
 86        { cwd: dir }
 87      );
 88  
 89      this._process.stdout.on('data', _messageHandler.bind(this));
 90      this._readyTimer = setTimeout(this.emit.bind(this, 'ready'), 1000);
 91    }
 92  
 93    close(callback) {
 94      clearTimeout(this._readyTimer);
 95      this.removeAllListeners();
 96      this._process && !this._process.killed && this._process.kill();
 97      if (typeof callback === 'function') {
 98        setImmediate(callback.bind(null, null, true));
 99      }
100    }
101  
102    /**
103     * Transform and emit an event comming from the poller.
104     *
105     * @param {EventEmitter} monitor
106     * @public
107     */
108    emitEvent(type, file, stat) {
109      this.emit(type, file, this.root, stat);
110      this.emit(ALL_EVENT, type, file, this.root, stat);
111    }
112  }
113  
114  WatchexecWatcher._messageHandler = _messageHandler;
115  
116  module.exports = WatchexecWatcher;