.
This commit is contained in:
		
							
								
								
									
										369
									
								
								qwen/nodejs/node_modules/jest-haste-map/build/watchers/NodeWatcher.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								qwen/nodejs/node_modules/jest-haste-map/build/watchers/NodeWatcher.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,369 @@
 | 
			
		||||
// vendored from https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/node_watcher.js
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const EventEmitter = require('events').EventEmitter;
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const platform = require('os').platform();
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const common = require('./common');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Constants
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const DEFAULT_DELAY = common.DEFAULT_DELAY;
 | 
			
		||||
const CHANGE_EVENT = common.CHANGE_EVENT;
 | 
			
		||||
const DELETE_EVENT = common.DELETE_EVENT;
 | 
			
		||||
const ADD_EVENT = common.ADD_EVENT;
 | 
			
		||||
const ALL_EVENT = common.ALL_EVENT;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Export `NodeWatcher` class.
 | 
			
		||||
 * Watches `dir`.
 | 
			
		||||
 *
 | 
			
		||||
 * @class NodeWatcher
 | 
			
		||||
 * @param {String} dir
 | 
			
		||||
 * @param {Object} opts
 | 
			
		||||
 * @public
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
module.exports = class NodeWatcher extends EventEmitter {
 | 
			
		||||
  constructor(dir, opts) {
 | 
			
		||||
    super();
 | 
			
		||||
    common.assignOptions(this, opts);
 | 
			
		||||
    this.watched = Object.create(null);
 | 
			
		||||
    this.changeTimers = Object.create(null);
 | 
			
		||||
    this.dirRegistery = Object.create(null);
 | 
			
		||||
    this.root = path.resolve(dir);
 | 
			
		||||
    this.watchdir = this.watchdir.bind(this);
 | 
			
		||||
    this.register = this.register.bind(this);
 | 
			
		||||
    this.checkedEmitError = this.checkedEmitError.bind(this);
 | 
			
		||||
    this.watchdir(this.root);
 | 
			
		||||
    common.recReaddir(
 | 
			
		||||
      this.root,
 | 
			
		||||
      this.watchdir,
 | 
			
		||||
      this.register,
 | 
			
		||||
      this.emit.bind(this, 'ready'),
 | 
			
		||||
      this.checkedEmitError,
 | 
			
		||||
      this.ignored
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Register files that matches our globs to know what to type of event to
 | 
			
		||||
   * emit in the future.
 | 
			
		||||
   *
 | 
			
		||||
   * Registery looks like the following:
 | 
			
		||||
   *
 | 
			
		||||
   *  dirRegister => Map {
 | 
			
		||||
   *    dirpath => Map {
 | 
			
		||||
   *       filename => true
 | 
			
		||||
   *    }
 | 
			
		||||
   *  }
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} filepath
 | 
			
		||||
   * @return {boolean} whether or not we have registered the file.
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  register(filepath) {
 | 
			
		||||
    const relativePath = path.relative(this.root, filepath);
 | 
			
		||||
    if (
 | 
			
		||||
      !common.isFileIncluded(this.globs, this.dot, this.doIgnore, relativePath)
 | 
			
		||||
    ) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    const dir = path.dirname(filepath);
 | 
			
		||||
    if (!this.dirRegistery[dir]) {
 | 
			
		||||
      this.dirRegistery[dir] = Object.create(null);
 | 
			
		||||
    }
 | 
			
		||||
    const filename = path.basename(filepath);
 | 
			
		||||
    this.dirRegistery[dir][filename] = true;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Removes a file from the registery.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} filepath
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  unregister(filepath) {
 | 
			
		||||
    const dir = path.dirname(filepath);
 | 
			
		||||
    if (this.dirRegistery[dir]) {
 | 
			
		||||
      const filename = path.basename(filepath);
 | 
			
		||||
      delete this.dirRegistery[dir][filename];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Removes a dir from the registery.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} dirpath
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  unregisterDir(dirpath) {
 | 
			
		||||
    if (this.dirRegistery[dirpath]) {
 | 
			
		||||
      delete this.dirRegistery[dirpath];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Checks if a file or directory exists in the registery.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} fullpath
 | 
			
		||||
   * @return {boolean}
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  registered(fullpath) {
 | 
			
		||||
    const dir = path.dirname(fullpath);
 | 
			
		||||
    return (
 | 
			
		||||
      this.dirRegistery[fullpath] ||
 | 
			
		||||
      (this.dirRegistery[dir] &&
 | 
			
		||||
        this.dirRegistery[dir][path.basename(fullpath)])
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Emit "error" event if it's not an ignorable event
 | 
			
		||||
   *
 | 
			
		||||
   * @param error
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
  checkedEmitError(error) {
 | 
			
		||||
    if (!isIgnorableFileError(error)) {
 | 
			
		||||
      this.emit('error', error);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Watch a directory.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} dir
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  watchdir(dir) {
 | 
			
		||||
    if (this.watched[dir]) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const watcher = fs.watch(
 | 
			
		||||
      dir,
 | 
			
		||||
      {
 | 
			
		||||
        persistent: true
 | 
			
		||||
      },
 | 
			
		||||
      this.normalizeChange.bind(this, dir)
 | 
			
		||||
    );
 | 
			
		||||
    this.watched[dir] = watcher;
 | 
			
		||||
    watcher.on('error', this.checkedEmitError);
 | 
			
		||||
    if (this.root !== dir) {
 | 
			
		||||
      this.register(dir);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Stop watching a directory.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} dir
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  stopWatching(dir) {
 | 
			
		||||
    if (this.watched[dir]) {
 | 
			
		||||
      this.watched[dir].close();
 | 
			
		||||
      delete this.watched[dir];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * End watching.
 | 
			
		||||
   *
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  close() {
 | 
			
		||||
    Object.keys(this.watched).forEach(this.stopWatching, this);
 | 
			
		||||
    this.removeAllListeners();
 | 
			
		||||
    return Promise.resolve();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * On some platforms, as pointed out on the fs docs (most likely just win32)
 | 
			
		||||
   * the file argument might be missing from the fs event. Try to detect what
 | 
			
		||||
   * change by detecting if something was deleted or the most recent file change.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} dir
 | 
			
		||||
   * @param {string} event
 | 
			
		||||
   * @param {string} file
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  detectChangedFile(dir, event, callback) {
 | 
			
		||||
    if (!this.dirRegistery[dir]) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    let found = false;
 | 
			
		||||
    let closest = {
 | 
			
		||||
      mtime: 0
 | 
			
		||||
    };
 | 
			
		||||
    let c = 0;
 | 
			
		||||
    Object.keys(this.dirRegistery[dir]).forEach(function (file, i, arr) {
 | 
			
		||||
      fs.lstat(path.join(dir, file), (error, stat) => {
 | 
			
		||||
        if (found) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        if (error) {
 | 
			
		||||
          if (isIgnorableFileError(error)) {
 | 
			
		||||
            found = true;
 | 
			
		||||
            callback(file);
 | 
			
		||||
          } else {
 | 
			
		||||
            this.emit('error', error);
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          if (stat.mtime > closest.mtime) {
 | 
			
		||||
            stat.file = file;
 | 
			
		||||
            closest = stat;
 | 
			
		||||
          }
 | 
			
		||||
          if (arr.length === ++c) {
 | 
			
		||||
            callback(closest.file);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }, this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Normalize fs events and pass it on to be processed.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} dir
 | 
			
		||||
   * @param {string} event
 | 
			
		||||
   * @param {string} file
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  normalizeChange(dir, event, file) {
 | 
			
		||||
    if (!file) {
 | 
			
		||||
      this.detectChangedFile(dir, event, actualFile => {
 | 
			
		||||
        if (actualFile) {
 | 
			
		||||
          this.processChange(dir, event, actualFile);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      this.processChange(dir, event, path.normalize(file));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Process changes.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} dir
 | 
			
		||||
   * @param {string} event
 | 
			
		||||
   * @param {string} file
 | 
			
		||||
   * @public
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  processChange(dir, event, file) {
 | 
			
		||||
    const fullPath = path.join(dir, file);
 | 
			
		||||
    const relativePath = path.join(path.relative(this.root, dir), file);
 | 
			
		||||
    fs.lstat(fullPath, (error, stat) => {
 | 
			
		||||
      if (error && error.code !== 'ENOENT') {
 | 
			
		||||
        this.emit('error', error);
 | 
			
		||||
      } else if (!error && stat.isDirectory()) {
 | 
			
		||||
        // win32 emits usless change events on dirs.
 | 
			
		||||
        if (event !== 'change') {
 | 
			
		||||
          this.watchdir(fullPath);
 | 
			
		||||
          if (
 | 
			
		||||
            common.isFileIncluded(
 | 
			
		||||
              this.globs,
 | 
			
		||||
              this.dot,
 | 
			
		||||
              this.doIgnore,
 | 
			
		||||
              relativePath
 | 
			
		||||
            )
 | 
			
		||||
          ) {
 | 
			
		||||
            this.emitEvent(ADD_EVENT, relativePath, stat);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        const registered = this.registered(fullPath);
 | 
			
		||||
        if (error && error.code === 'ENOENT') {
 | 
			
		||||
          this.unregister(fullPath);
 | 
			
		||||
          this.stopWatching(fullPath);
 | 
			
		||||
          this.unregisterDir(fullPath);
 | 
			
		||||
          if (registered) {
 | 
			
		||||
            this.emitEvent(DELETE_EVENT, relativePath);
 | 
			
		||||
          }
 | 
			
		||||
        } else if (registered) {
 | 
			
		||||
          this.emitEvent(CHANGE_EVENT, relativePath, stat);
 | 
			
		||||
        } else {
 | 
			
		||||
          if (this.register(fullPath)) {
 | 
			
		||||
            this.emitEvent(ADD_EVENT, relativePath, stat);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Triggers a 'change' event after debounding it to take care of duplicate
 | 
			
		||||
   * events on os x.
 | 
			
		||||
   *
 | 
			
		||||
   * @private
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  emitEvent(type, file, stat) {
 | 
			
		||||
    const key = `${type}-${file}`;
 | 
			
		||||
    const addKey = `${ADD_EVENT}-${file}`;
 | 
			
		||||
    if (type === CHANGE_EVENT && this.changeTimers[addKey]) {
 | 
			
		||||
      // Ignore the change event that is immediately fired after an add event.
 | 
			
		||||
      // (This happens on Linux).
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    clearTimeout(this.changeTimers[key]);
 | 
			
		||||
    this.changeTimers[key] = setTimeout(() => {
 | 
			
		||||
      delete this.changeTimers[key];
 | 
			
		||||
      if (type === ADD_EVENT && stat.isDirectory()) {
 | 
			
		||||
        // Recursively emit add events and watch for sub-files/folders
 | 
			
		||||
        common.recReaddir(
 | 
			
		||||
          path.resolve(this.root, file),
 | 
			
		||||
          function emitAddDir(dir, stats) {
 | 
			
		||||
            this.watchdir(dir);
 | 
			
		||||
            this.rawEmitEvent(ADD_EVENT, path.relative(this.root, dir), stats);
 | 
			
		||||
          }.bind(this),
 | 
			
		||||
          function emitAddFile(file, stats) {
 | 
			
		||||
            this.register(file);
 | 
			
		||||
            this.rawEmitEvent(ADD_EVENT, path.relative(this.root, file), stats);
 | 
			
		||||
          }.bind(this),
 | 
			
		||||
          function endCallback() {},
 | 
			
		||||
          this.checkedEmitError,
 | 
			
		||||
          this.ignored
 | 
			
		||||
        );
 | 
			
		||||
      } else {
 | 
			
		||||
        this.rawEmitEvent(type, file, stat);
 | 
			
		||||
      }
 | 
			
		||||
    }, DEFAULT_DELAY);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Actually emit the events
 | 
			
		||||
   */
 | 
			
		||||
  rawEmitEvent(type, file, stat) {
 | 
			
		||||
    this.emit(type, file, this.root, stat);
 | 
			
		||||
    this.emit(ALL_EVENT, type, file, this.root, stat);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
 * Determine if a given FS error can be ignored
 | 
			
		||||
 *
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
function isIgnorableFileError(error) {
 | 
			
		||||
  return (
 | 
			
		||||
    error.code === 'ENOENT' ||
 | 
			
		||||
    // Workaround Windows node issue #4337.
 | 
			
		||||
    (error.code === 'EPERM' && platform === 'win32')
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user