mirror of
https://github.com/actions/setup-python.git
synced 2025-06-28 21:53:47 +00:00
Initial pass
This commit is contained in:
commit
39c08a0eaa
7242 changed files with 1886006 additions and 0 deletions
398
node_modules/sane/src/node_watcher.js
generated
vendored
Normal file
398
node_modules/sane/src/node_watcher.js
generated
vendored
Normal file
|
@ -0,0 +1,398 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const common = require('./common');
|
||||
const platform = require('os').platform();
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
let relativePath = path.relative(this.root, filepath);
|
||||
if (
|
||||
!common.isFileIncluded(this.globs, this.dot, this.doIgnore, relativePath)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let dir = path.dirname(filepath);
|
||||
if (!this.dirRegistery[dir]) {
|
||||
this.dirRegistery[dir] = Object.create(null);
|
||||
}
|
||||
|
||||
let filename = path.basename(filepath);
|
||||
this.dirRegistery[dir][filename] = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from the registery.
|
||||
*
|
||||
* @param {string} filepath
|
||||
* @private
|
||||
*/
|
||||
|
||||
unregister(filepath) {
|
||||
let dir = path.dirname(filepath);
|
||||
if (this.dirRegistery[dir]) {
|
||||
let 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) {
|
||||
let 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;
|
||||
}
|
||||
|
||||
let 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(callback) {
|
||||
Object.keys(this.watched).forEach(this.stopWatching, this);
|
||||
this.removeAllListeners();
|
||||
if (typeof callback === 'function') {
|
||||
setImmediate(callback.bind(null, null, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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),
|
||||
function(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);
|
||||
}
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
}, 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,
|
||||
function(actualFile) {
|
||||
if (actualFile) {
|
||||
this.processChange(dir, event, actualFile);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
} else {
|
||||
this.processChange(dir, event, path.normalize(file));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process changes.
|
||||
*
|
||||
* @param {string} dir
|
||||
* @param {string} event
|
||||
* @param {string} file
|
||||
* @public
|
||||
*/
|
||||
|
||||
processChange(dir, event, file) {
|
||||
let fullPath = path.join(dir, file);
|
||||
let relativePath = path.join(path.relative(this.root, dir), file);
|
||||
|
||||
fs.lstat(
|
||||
fullPath,
|
||||
function(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 {
|
||||
let 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a 'change' event after debounding it to take care of duplicate
|
||||
* events on os x.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
emitEvent(type, file, stat) {
|
||||
let key = type + '-' + file;
|
||||
let 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(
|
||||
function() {
|
||||
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);
|
||||
}
|
||||
}.bind(this),
|
||||
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')
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue