Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

chokidar

Package Overview
Dependencies
Maintainers
2
Versions
106
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chokidar - npm Package Compare versions

Comparing version 3.1.1 to 3.2.0

134

index.js
'use strict';
const EventEmitter = require('events').EventEmitter;
const { EventEmitter } = require('events');
const fs = require('fs');

@@ -39,20 +39,4 @@ const sysPath = require('path');

/**
* @param {String|Array<String>} value
*/
const arrify = (value = []) => Array.isArray(value) ? value : [value];
const flatten = (list, result = []) => {
list.forEach(item => {
if (Array.isArray(item)) {
flatten(item, result);
} else {
result.push(item);
}
});
return result;
};
// Optimize RAM usage.
const BACK_SLASH = /\\/g;
const BACK_SLASH_RE = /\\/g;
const SLASH = '/';

@@ -71,6 +55,41 @@ const DOUBLE_SLASH = /\/\//;

const STRING_TYPE = 'string';
const isWindows = process.platform === 'win32';
const EMPTY_FN = () => {};
const EV_ALL = 'all';
const EV_READY = 'ready';
const EV_ADD = 'add';
const EV_CHANGE = 'change';
const EV_UNLINK = 'unlink';
const EV_ADD_DIR = 'addDir';
const EV_UNLINK_DIR = 'unlinkDir';
const EV_RAW = 'raw';
const EV_ERROR = 'error';
const arrify = (value = []) => Array.isArray(value) ? value : [value];
const flatten = (list, result = []) => {
list.forEach(item => {
if (Array.isArray(item)) {
flatten(item, result);
} else {
result.push(item);
}
});
return result;
};
const unifyPaths = (paths_) => {
/**
* @type {Array<String>}
*/
let paths = flatten(arrify(paths_));
if (!paths.every(p => typeof p === STRING_TYPE)) {
throw new TypeError('Non-string provided as watch path: ' + paths);
}
paths = paths.map(normalizePathToUnix);
return paths;
};
const toUnix = (string) => {
let str = string.replace(BACK_SLASH, SLASH);
let str = string.replace(BACK_SLASH_RE, SLASH);
while (str.match(DOUBLE_SLASH)) {

@@ -157,3 +176,4 @@ str = str.replace(DOUBLE_SLASH, SLASH);

/** @type {object|boolean} */
this.globSymlink = this.hasGlob && follow ? null : false;
if (path === '') this.hasGlob = false;
this.globSymlink = this.hasGlob && follow ? undefined : false;
this.globFilter = this.hasGlob ? anymatch(path, undefined, ANYMATCH_OPTS) : false;

@@ -171,3 +191,3 @@ this.dirParts = this.getDirParts(path);

// first entry should always have entry.parentDir === ''
if (this.globSymlink == null) {
if (this.globSymlink === undefined) {
this.globSymlink = entry.fullParentDir === this.fullWatchPath ?

@@ -325,6 +345,6 @@ false : {realPath: entry.fullParentDir, linkPath: this.fullWatchPath};

// use process.nextTick to allow time for listener to be bound
process.nextTick(() => this.emit('ready'));
process.nextTick(() => this.emit(EV_READY));
}
};
this._emitRaw = (...args) => this.emit('raw', ...args);
this._emitRaw = (...args) => this.emit(EV_RAW, ...args);
this._readyEmitted = false;

@@ -356,12 +376,3 @@ this.options = opts;

this.closed = false;
/**
* @type {Array<String>}
*/
let paths = flatten(arrify(paths_));
if (!paths.every(p => typeof p === STRING_TYPE)) {
throw new TypeError('Non-string provided as watch path: ' + paths);
}
paths = paths.map(path => sysPath.normalize(path));
let paths = unifyPaths(paths_);
if (cwd) {

@@ -392,3 +403,3 @@ paths = paths.map((path) => {

// to make ignoredPaths changes effective
this._userIgnored = null;
this._userIgnored = undefined;

@@ -425,8 +436,8 @@ return true;

* Close watchers or start ignoring events from specified paths.
* @param {Path|Array<Path>} paths - string or array of strings, file/directory paths and/or globs
* @param {Path|Array<Path>} paths_ - string or array of strings, file/directory paths and/or globs
* @returns {FSWatcher} for chaining
*/
unwatch(paths) {
unwatch(paths_) {
if (this.closed) return this;
paths = flatten(arrify(paths));
let paths = unifyPaths(paths_);
const cwd = this.options.cwd;

@@ -450,3 +461,3 @@

// to make ignoredPaths changes effective
this._userIgnored = null;
this._userIgnored = undefined;
});

@@ -466,11 +477,11 @@

// Memory management.
this.removeAllListeners();
this._closers.forEach(closerList => closerList.forEach(closer => closer()));
this._closers.clear();
this._watched.clear();
this._streams.forEach(stream => stream.destroy());
this._streams.clear();
this._symlinkPaths.clear();
this._throttled.clear();
this.removeAllListeners();
this._userIgnored = undefined;
this._readyCount = 0;
this._readyEmitted = false;
['closers', 'watched', 'streams', 'symlinkPaths', 'throttled'].forEach(key => {
this['_' + key].clear();
});
return this;

@@ -494,3 +505,3 @@ }

this.emit(...args);
if (event !== 'error') this.emit(...['all'].concat(args));
if (event !== EV_ERROR) this.emit(EV_ALL, ...args);
}

@@ -515,2 +526,3 @@

const opts = this.options;
if (isWindows) path = sysPath.normalize(path);
if (opts.cwd) path = sysPath.relative(opts.cwd, path);

@@ -531,3 +543,3 @@ /** @type Array<any> */

if (opts.atomic) {
if (event === 'unlink') {
if (event === EV_UNLINK) {
this._pendingUnlinks.set(path, args);

@@ -537,3 +549,3 @@ setTimeout(() => {

this.emit(...entry);
this.emit(...['all', ...entry]);
this.emit(EV_ALL, ...entry);
this._pendingUnlinks.delete(path);

@@ -543,4 +555,4 @@ });

return this;
} else if (event === 'add' && this._pendingUnlinks.has(path)) {
event = args[0] = 'change';
} else if (event === EV_ADD && this._pendingUnlinks.has(path)) {
event = args[0] = EV_CHANGE;
this._pendingUnlinks.delete(path);

@@ -550,6 +562,6 @@ }

if (awf && (event === 'add' || event === 'change') && this._readyEmitted) {
if (awf && (event === EV_ADD || event === EV_CHANGE) && this._readyEmitted) {
const awfEmit = (err, stats) => {
if (err) {
event = args[0] = 'error';
event = args[0] = EV_ERROR;
args[1] = err;

@@ -572,4 +584,4 @@ this.emitWithAll(event, args);

if (event === 'change') {
const isThrottled = !this._throttle('change', path, 50);
if (event === EV_CHANGE) {
const isThrottled = !this._throttle(EV_CHANGE, path, 50);
if (isThrottled) return this;

@@ -579,3 +591,3 @@ }

if (opts.alwaysStat && val1 === undefined &&
(event === 'add' || event === 'addDir' || event === 'change')
(event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE)
) {

@@ -607,3 +619,3 @@ const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path;

) {
this.emit('error', error);
this.emit(EV_ERROR, error);
}

@@ -842,3 +854,3 @@ return error || this.closed;

const event = this._pendingWrites.get(relPath).cancelWait();
if (event === 'add') return;
if (event === EV_ADD) return;
}

@@ -850,3 +862,3 @@

this._watched.delete(fullPath);
const eventName = isDirectory ? 'unlinkDir' : 'unlink';
const eventName = isDirectory ? EV_UNLINK_DIR : EV_UNLINK;
if (wasTracked && !this._isIgnored(path)) this._emit(eventName, path);

@@ -890,7 +902,7 @@

_readdirp(root, opts) {
const options = Object.assign({type: 'all', alwaysStat: true, lstat: true}, opts);
const options = Object.assign({type: EV_ALL, alwaysStat: true, lstat: true}, opts);
let stream = readdirp(root, options);
this._streams.add(stream);
stream.once('close', () => {
stream = null;
stream = undefined;
});

@@ -900,3 +912,3 @@ stream.once('end', () => {

this._streams.delete(stream);
stream = null;
stream = undefined;
}

@@ -903,0 +915,0 @@ });

@@ -19,3 +19,3 @@ 'use strict';

if (maj === 8 && min < 16) {
fsevents = null;
fsevents = undefined;
}

@@ -25,2 +25,5 @@ }

const EV_ADD = 'add';
const EV_CHANGE = 'change';
const EV_ADD_DIR = 'addDir';
const EMPTY_FN = () => {};

@@ -84,3 +87,3 @@

*/
function setFSEventsListener(path, realPath, listener, rawEmitter) {
function setFSEventsListener(path, realPath, listener, rawEmitter, fsw) {
let watchPath = sysPath.extname(path) ? sysPath.dirname(path) : path;

@@ -128,2 +131,3 @@ const parentPath = sysPath.dirname(watchPath);

watcher: createFSEventsInstance(watchPath, (fullPath, flags) => {
if (fsw.closed) return;
const info = fsevents.getInfo(fullPath, flags);

@@ -143,11 +147,10 @@ cont.listeners.forEach(list => {

return () => {
const wl = cont.listeners;
const lst = cont.listeners;
wl.delete(filteredListener);
if (!wl.size) {
lst.delete(filteredListener);
if (!lst.size) {
FSEventsWatchers.delete(watchPath);
cont.watcher.stop();
cont.rawEmitter = cont.watcher = null;
cont.rawEmitter = cont.watcher = undefined;
Object.freeze(cont);
Object.freeze(cont.listeners);
}

@@ -189,6 +192,6 @@ };

/**
* @param {import('../index').FSWatcher} fsW
* @param {import('../index').FSWatcher} fsw
*/
constructor(fsW) {
this.fsw = fsW;
constructor(fsw) {
this.fsw = fsw;
}

@@ -210,3 +213,3 @@ checkIgnored(path, stats) {

addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
const event = watchedDir.has(item) ? 'change' : 'add';
const event = watchedDir.has(item) ? EV_CHANGE : EV_ADD;
this.handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts);

@@ -218,3 +221,9 @@ }

const fd = await open(path, 'r');
if (this.fsw.closed) {
return;
}
await close(fd);
if (this.fsw.closed) {
return;
}
this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);

@@ -239,3 +248,3 @@ } catch (error) {

} else {
if (event === 'add') {
if (event === EV_ADD) {
// track new directories

@@ -260,3 +269,3 @@ if (info.type === 'directory') this.fsw._getWatchedDir(path);

this.fsw._emit(eventName, path);
if (eventName === 'addDir') this._addToFsEvents(path, false, true);
if (eventName === EV_ADD_DIR) this._addToFsEvents(path, false, true);
}

@@ -274,6 +283,7 @@ }

_watchWithFsEvents(watchPath, realPath, transform, globFilter) {
if (this.fsw.closed) return;
if (this.fsw._isIgnored(watchPath)) return;
const opts = this.fsw.options;
const watchCallback = async (fullPath, flags, info) => {
if (this.fsw.closed) return;
if (

@@ -301,2 +311,5 @@ opts.depth !== undefined &&

} catch (error) {}
if (this.fsw.closed) {
return;
}
if (this.checkIgnored(path, stats)) return;

@@ -327,3 +340,4 @@ if (stats) {

watchCallback,
this.fsw._emitRaw
this.fsw._emitRaw,
this.fsw
);

@@ -352,2 +366,5 @@

const linkTarget = await realpath(linkPath);
if (this.fsw.closed) {
return;
}
if (this.fsw._isIgnored(linkTarget)) {

@@ -395,3 +412,3 @@ return this.fsw._emitReady();

if (!opts.ignoreInitial || forceAdd === true) {
this.fsw._emit(isDir ? 'addDir' : 'add', pp, stats);
this.fsw._emit(isDir ? EV_ADD_DIR : EV_ADD, pp, stats);
}

@@ -398,0 +415,0 @@ }

@@ -15,3 +15,50 @@ 'use strict';

const statMethods = { lstat, stat };
const isWindows = process.platform === 'win32';
const emptyFn = () => {};
const KEY_LISTENERS = 'listeners';
const KEY_ERR = 'errHandlers';
const KEY_RAW = 'rawEmitters';
const HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
const EV_CHANGE = 'change';
const EV_ADD = 'add';
const EV_ADD_DIR = 'addDir';
// TODO: emit errors properly. Example: EMFILE on Macos.
const foreach = (val, fn) => {
if (val instanceof Set) {
val.forEach(fn);
} else {
fn(val);
}
};
const addAndConvert = (main, prop, item) => {
let container = main[prop];
if (!(container instanceof Set)) {
main[prop] = container = new Set([container]);
}
container.add(item);
};
const clearItem = cont => key => {
const set = cont[key];
if (set instanceof Set) {
set.clear();
} else {
delete cont[key];
}
};
const delFromSet = (main, prop, item) => {
const container = main[prop];
if (!(container instanceof Set)) {
if (container === item) delete main[prop];
} else {
container.delete(item);
}
};
const isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
/**

@@ -39,4 +86,2 @@ * @typedef {String} Path

const FsWatchInstances = new Map();
const emptyFn = () => {};
const FUNCTIONS = ['listeners', 'errHandlers', 'rawEmitters'];

@@ -61,3 +106,3 @@ /**

fsWatchBroadcast(
sysPath.resolve(path, evPath), 'listeners', sysPath.join(path, evPath)
sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath)
);

@@ -84,3 +129,3 @@ }

if (!cont) return;
cont[type].forEach((listener) => {
foreach(cont[type], (listener) => {
listener(val1, val2, val3);

@@ -99,5 +144,3 @@ });

const setFsWatchListener = (path, fullPath, options, handlers) => {
const listener = handlers.listener;
const errHandler = handlers.errHandler;
const rawEmitter = handlers.rawEmitter;
const {listener, errHandler, rawEmitter} = handlers;
let cont = FsWatchInstances.get(fullPath);

@@ -114,5 +157,5 @@

if (cont) {
cont.listeners.add(listener);
cont.errHandlers.add(errHandler);
cont.rawEmitters.add(rawEmitter);
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_ERR, errHandler);
addAndConvert(cont, KEY_RAW, rawEmitter);
} else {

@@ -122,12 +165,12 @@ watcher = createFsWatchInstance(

options,
fsWatchBroadcast.bind(null, fullPath, 'listeners'),
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
errHandler, // no need to use broadcast here
fsWatchBroadcast.bind(null, fullPath, 'rawEmitters')
fsWatchBroadcast.bind(null, fullPath, KEY_RAW)
);
if (!watcher) return;
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, 'errHandlers');
watcher.on('error', async (error) => {
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
cont.watcherUnusable = true; // documented since Node 10.4.1
// Workaround for https://github.com/joyent/node/issues/4337
if (process.platform === 'win32' && error.code === 'EPERM') {
if (isWindows && error.code === 'EPERM') {
try {

@@ -143,5 +186,5 @@ const fd = await open(path, 'r');

cont = {
listeners: new Set([listener]),
errHandlers: new Set([errHandler]),
rawEmitters: new Set([rawEmitter]),
listeners: listener,
errHandlers: errHandler,
rawEmitters: rawEmitter,
watcher: watcher

@@ -156,6 +199,6 @@ };

return () => {
cont.listeners.delete(listener);
cont.errHandlers.delete(errHandler);
cont.rawEmitters.delete(rawEmitter);
if (!cont.listeners.size) {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_ERR, errHandler);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
// Check to protect against issue gh-730.

@@ -166,8 +209,4 @@ // if (cont.watcherUnusable) {

FsWatchInstances.delete(fullPath);
FUNCTIONS.forEach(key => {
const set = cont[key];
set.clear();
Object.freeze(set);
});
cont.watcher = null;
HANDLER_KEYS.forEach(clearItem(cont));
cont.watcher = undefined;
Object.freeze(cont);

@@ -209,22 +248,23 @@ }

fs.unwatchFile(fullPath);
cont = null;
cont = undefined;
}
if (cont) {
cont.listeners.add(listener);
cont.rawEmitters.add(rawEmitter);
addAndConvert(cont, KEY_LISTENERS, listener);
addAndConvert(cont, KEY_RAW, rawEmitter);
} else {
listeners.add(listener);
rawEmitters.add(rawEmitter);
// TODO
// listeners.add(listener);
// rawEmitters.add(rawEmitter);
cont = {
listeners: listeners,
rawEmitters: rawEmitters,
listeners: listener,
rawEmitters: rawEmitter,
options: options,
watcher: fs.watchFile(fullPath, options, (curr, prev) => {
cont.rawEmitters.forEach((rawEmitter) => {
rawEmitter('change', fullPath, {curr: curr, prev: prev});
foreach(cont.rawEmitters, (rawEmitter) => {
rawEmitter(EV_CHANGE, fullPath, {curr, prev});
});
const currmtime = curr.mtimeMs;
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
cont.listeners.forEach((listener) => listener(path, curr));
foreach(cont.listeners, (listener) => listener(path, curr));
}

@@ -239,11 +279,10 @@ })

// instance if there are no more listeners left.
return () => {
cont.listeners.delete(listener);
cont.rawEmitters.delete(rawEmitter);
if (!cont.listeners.size) {
return () => {
delFromSet(cont, KEY_LISTENERS, listener);
delFromSet(cont, KEY_RAW, rawEmitter);
if (isEmptySet(cont.listeners)) {
FsWatchFileInstances.delete(fullPath);
fs.unwatchFile(fullPath);
cont.options = cont.watcher = null;
cont.options = cont.watcher = undefined;
Object.freeze(cont);
Object.freeze(cont.listeners);
}

@@ -333,3 +372,3 @@ };

if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit('change', file, newStats);
this.fsw._emit(EV_CHANGE, file, newStats);
}

@@ -347,3 +386,3 @@ prevStats = newStats;

if (!at || at <= mt || mt !== prevStats.mtimeMs) {
this.fsw._emit('change', file, newStats);
this.fsw._emit(EV_CHANGE, file, newStats);
}

@@ -356,4 +395,4 @@ prevStats = newStats;

if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
if (!this.fsw._throttle('add', file, 0)) return;
this.fsw._emit('add', file, stats);
if (!this.fsw._throttle(EV_ADD, file, 0)) return;
this.fsw._emit(EV_ADD, file, stats);
}

@@ -383,6 +422,9 @@

const linkPath = await fsrealpath(path);
if (this.fsw.closed) {
return;
}
if (dir.has(item)) {
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit('change', path, entry.stats);
this.fsw._emit(EV_CHANGE, path, entry.stats);
}

@@ -392,3 +434,3 @@ } else {

this.fsw._symlinkPaths.set(full, linkPath);
this.fsw._emit('add', path, entry.stats);
this.fsw._emit(EV_ADD, path, entry.stats);
}

@@ -431,5 +473,10 @@ this.fsw._emitReady();

if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item))
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
return;
}
if (this.fsw.closed) {
return;
}
// Files that present in current directory snapshot

@@ -491,3 +538,3 @@ // but absent in previous are added to watch list and

if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
if (!wh.hasGlob || wh.globFilter(dir)) this.fsw._emit('addDir', dir, stats);
if (!wh.hasGlob || wh.globFilter(dir)) this.fsw._emit(EV_ADD_DIR, dir, stats);
}

@@ -507,2 +554,5 @@

await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
if (this.fsw.closed) {
return;
}
}

@@ -560,3 +610,9 @@

const targetPath = follow ? await fsrealpath(path) : path;
if (this.fsw.closed) {
return;
}
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
if (this.fsw.closed) {
return;
}
// preserve this symlink's target path

@@ -568,6 +624,12 @@ if (path !== targetPath && targetPath !== undefined) {

const targetPath = follow ? await fsrealpath(path) : path;
if (this.fsw.closed) {
return;
}
const parent = sysPath.dirname(wh.watchPath);
this.fsw._getWatchedDir(parent).add(wh.watchPath);
this.fsw._emit('add', wh.watchPath, stats);
this.fsw._emit(EV_ADD, wh.watchPath, stats);
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
if (this.fsw.closed) {
return;
}

@@ -574,0 +636,0 @@ // preserve this symlink's target path

{
"name": "chokidar",
"description": "A neat wrapper around node.js fs.watch / fs.watchFile / fsevents.",
"version": "3.1.1",
"version": "3.2.0",
"homepage": "https://github.com/paulmillr/chokidar",

@@ -12,15 +12,15 @@ "author": "Paul Miller (https://paulmillr.com)",

"engines": {
"node": ">= 8"
"node": ">= 8.16"
},
"dependencies": {
"anymatch": "^3.1.0",
"braces": "^3.0.2",
"glob-parent": "^5.0.0",
"is-binary-path": "^2.1.0",
"is-glob": "^4.0.1",
"normalize-path": "^3.0.0",
"readdirp": "^3.1.1"
"anymatch": "~3.1.1",
"braces": "~3.0.2",
"glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.1.3"
},
"optionalDependencies": {
"fsevents": "^2.0.6"
"fsevents": "~2.1.0"
},

@@ -27,0 +27,0 @@ "devDependencies": {

@@ -284,3 +284,4 @@ # Chokidar [![Weekly downloads](https://img.shields.io/npm/dw/chokidar.svg)](https://github.com/paulmillr/chokidar) [![Yearly downloads](https://img.shields.io/npm/dy/chokidar.svg)](https://github.com/paulmillr/chokidar)

- **v3.1 (Sep 16, 2019):** dotfiles are no longer filtered out by default. Use `ignored` option if needed. Improve Linux performance by 50%.
- **v3.2 (Oct 1, 2019):** Improve Linux RAM usage by 50%. Stability optimizations. Windows glob fixes.
- **v3.1 (Sep 16, 2019):** dotfiles are no longer filtered out by default. Use `ignored` option if needed. Improve initial Linux scan time by 50%.
- **v3 (Apr 30, 2019):** massive CPU & RAM consumption improvements; reduces deps / package size by a factor of 17x and bumps Node.js requirement to v8.16 and higher.

@@ -287,0 +288,0 @@ - **v2 (Dec 29, 2017):** Globs are now posix-style-only; without windows support. Tons of bugfixes.

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc