cscchokidar-next 0.0.1-security → 4.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cscchokidar-next might be problematic. Click here for more details.

@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ const {sep} = require('path');
4
+ const {platform} = process;
5
+
6
+ exports.EV_ALL = 'all';
7
+ exports.EV_READY = 'ready';
8
+ exports.EV_ADD = 'add';
9
+ exports.EV_CHANGE = 'change';
10
+ exports.EV_ADD_DIR = 'addDir';
11
+ exports.EV_UNLINK = 'unlink';
12
+ exports.EV_UNLINK_DIR = 'unlinkDir';
13
+ exports.EV_RAW = 'raw';
14
+ exports.EV_ERROR = 'error';
15
+
16
+ exports.STR_DATA = 'data';
17
+ exports.STR_END = 'end';
18
+ exports.STR_CLOSE = 'close';
19
+
20
+ exports.FSEVENT_CREATED = 'created';
21
+ exports.FSEVENT_MODIFIED = 'modified';
22
+ exports.FSEVENT_DELETED = 'deleted';
23
+ exports.FSEVENT_MOVED = 'moved';
24
+ exports.FSEVENT_CLONED = 'cloned';
25
+ exports.FSEVENT_UNKNOWN = 'unknown';
26
+ exports.FSEVENT_TYPE_FILE = 'file';
27
+ exports.FSEVENT_TYPE_DIRECTORY = 'directory';
28
+ exports.FSEVENT_TYPE_SYMLINK = 'symlink';
29
+
30
+ exports.KEY_LISTENERS = 'listeners';
31
+ exports.KEY_ERR = 'errHandlers';
32
+ exports.KEY_RAW = 'rawEmitters';
33
+ exports.HANDLER_KEYS = [exports.KEY_LISTENERS, exports.KEY_ERR, exports.KEY_RAW];
34
+
35
+ exports.DOT_SLASH = `.${sep}`;
36
+
37
+ exports.BACK_SLASH_RE = /\\/g;
38
+ exports.DOUBLE_SLASH_RE = /\/\//;
39
+ exports.SLASH_OR_BACK_SLASH_RE = /[/\\]/;
40
+ exports.DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
41
+ exports.REPLACER_RE = /^\.[/\\]/;
42
+
43
+ exports.SLASH = '/';
44
+ exports.SLASH_SLASH = '//';
45
+ exports.BRACE_START = '{';
46
+ exports.BANG = '!';
47
+ exports.ONE_DOT = '.';
48
+ exports.TWO_DOTS = '..';
49
+ exports.STAR = '*';
50
+ exports.GLOBSTAR = '**';
51
+ exports.ROOT_GLOBSTAR = '/**/*';
52
+ exports.SLASH_GLOBSTAR = '/**';
53
+ exports.DIR_SUFFIX = 'Dir';
54
+ exports.ANYMATCH_OPTS = {dot: true};
55
+ exports.STRING_TYPE = 'string';
56
+ exports.FUNCTION_TYPE = 'function';
57
+ exports.EMPTY_STR = '';
58
+ exports.EMPTY_FN = () => {};
59
+ exports.IDENTITY_FN = val => val;
60
+
61
+ exports.isWindows = platform === 'win32';
62
+ exports.isMacos = platform === 'darwin';
63
+ exports.isLinux = platform === 'linux';
@@ -0,0 +1,523 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const sysPath = require('path');
5
+ const { promisify } = require('util');
6
+
7
+ let fsevents;
8
+ try {
9
+ fsevents = require('fsevents');
10
+ } catch (error) {
11
+ if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) console.error(error);
12
+ }
13
+
14
+ if (fsevents) {
15
+ // TODO: real check
16
+ const mtch = process.version.match(/v(\d+)\.(\d+)/);
17
+ if (mtch && mtch[1] && mtch[2]) {
18
+ const maj = Number.parseInt(mtch[1], 10);
19
+ const min = Number.parseInt(mtch[2], 10);
20
+ if (maj === 8 && min < 16) {
21
+ fsevents = undefined;
22
+ }
23
+ }
24
+ }
25
+
26
+ const {
27
+ EV_ADD,
28
+ EV_CHANGE,
29
+ EV_ADD_DIR,
30
+ EV_UNLINK,
31
+ EV_ERROR,
32
+ STR_DATA,
33
+ STR_END,
34
+ FSEVENT_CREATED,
35
+ FSEVENT_MODIFIED,
36
+ FSEVENT_DELETED,
37
+ FSEVENT_MOVED,
38
+ // FSEVENT_CLONED,
39
+ FSEVENT_UNKNOWN,
40
+ FSEVENT_TYPE_FILE,
41
+ FSEVENT_TYPE_DIRECTORY,
42
+ FSEVENT_TYPE_SYMLINK,
43
+
44
+ ROOT_GLOBSTAR,
45
+ DIR_SUFFIX,
46
+ DOT_SLASH,
47
+ FUNCTION_TYPE,
48
+ EMPTY_FN,
49
+ IDENTITY_FN
50
+ } = require('./constants');
51
+
52
+ const Depth = (value) => isNaN(value) ? {} : {depth: value};
53
+
54
+ const stat = promisify(fs.stat);
55
+ const lstat = promisify(fs.lstat);
56
+ const realpath = promisify(fs.realpath);
57
+
58
+ const statMethods = { stat, lstat };
59
+
60
+ /**
61
+ * @typedef {String} Path
62
+ */
63
+
64
+ /**
65
+ * @typedef {Object} FsEventsWatchContainer
66
+ * @property {Set<Function>} listeners
67
+ * @property {Function} rawEmitter
68
+ * @property {{stop: Function}} watcher
69
+ */
70
+
71
+ // fsevents instance helper functions
72
+ /**
73
+ * Object to hold per-process fsevents instances (may be shared across chokidar FSWatcher instances)
74
+ * @type {Map<Path,FsEventsWatchContainer>}
75
+ */
76
+ const FSEventsWatchers = new Map();
77
+
78
+ // Threshold of duplicate path prefixes at which to start
79
+ // consolidating going forward
80
+ const consolidateThreshhold = 10;
81
+
82
+ const wrongEventFlags = new Set([
83
+ 69888, 70400, 71424, 72704, 73472, 131328, 131840, 262912
84
+ ]);
85
+
86
+ /**
87
+ * Instantiates the fsevents interface
88
+ * @param {Path} path path to be watched
89
+ * @param {Function} callback called when fsevents is bound and ready
90
+ * @returns {{stop: Function}} new fsevents instance
91
+ */
92
+ const createFSEventsInstance = (path, callback) => {
93
+ const stop = fsevents.watch(path, callback);
94
+ return {stop};
95
+ };
96
+
97
+ /**
98
+ * Instantiates the fsevents interface or binds listeners to an existing one covering
99
+ * the same file tree.
100
+ * @param {Path} path - to be watched
101
+ * @param {Path} realPath - real path for symlinks
102
+ * @param {Function} listener - called when fsevents emits events
103
+ * @param {Function} rawEmitter - passes data to listeners of the 'raw' event
104
+ * @returns {Function} closer
105
+ */
106
+ function setFSEventsListener(path, realPath, listener, rawEmitter) {
107
+ let watchPath = sysPath.extname(path) ? sysPath.dirname(path) : path;
108
+ const parentPath = sysPath.dirname(watchPath);
109
+ let cont = FSEventsWatchers.get(watchPath);
110
+
111
+ // If we've accumulated a substantial number of paths that
112
+ // could have been consolidated by watching one directory
113
+ // above the current one, create a watcher on the parent
114
+ // path instead, so that we do consolidate going forward.
115
+ if (couldConsolidate(parentPath)) {
116
+ watchPath = parentPath;
117
+ }
118
+
119
+ const resolvedPath = sysPath.resolve(path);
120
+ const hasSymlink = resolvedPath !== realPath;
121
+
122
+ const filteredListener = (fullPath, flags, info) => {
123
+ if (hasSymlink) fullPath = fullPath.replace(realPath, resolvedPath);
124
+ if (
125
+ fullPath === resolvedPath ||
126
+ !fullPath.indexOf(resolvedPath + sysPath.sep)
127
+ ) listener(fullPath, flags, info);
128
+ };
129
+
130
+ // check if there is already a watcher on a parent path
131
+ // modifies `watchPath` to the parent path when it finds a match
132
+ let watchedParent = false;
133
+ for (const watchedPath of FSEventsWatchers.keys()) {
134
+ if (realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep) === 0) {
135
+ watchPath = watchedPath;
136
+ cont = FSEventsWatchers.get(watchPath);
137
+ watchedParent = true;
138
+ break;
139
+ }
140
+ }
141
+
142
+ if (cont || watchedParent) {
143
+ cont.listeners.add(filteredListener);
144
+ } else {
145
+ cont = {
146
+ listeners: new Set([filteredListener]),
147
+ rawEmitter,
148
+ watcher: createFSEventsInstance(watchPath, (fullPath, flags) => {
149
+ if (!cont.listeners.size) return;
150
+ const info = fsevents.getInfo(fullPath, flags);
151
+ cont.listeners.forEach(list => {
152
+ list(fullPath, flags, info);
153
+ });
154
+
155
+ cont.rawEmitter(info.event, fullPath, info);
156
+ })
157
+ };
158
+ FSEventsWatchers.set(watchPath, cont);
159
+ }
160
+
161
+ // removes this instance's listeners and closes the underlying fsevents
162
+ // instance if there are no more listeners left
163
+ return () => {
164
+ const lst = cont.listeners;
165
+
166
+ lst.delete(filteredListener);
167
+ if (!lst.size) {
168
+ FSEventsWatchers.delete(watchPath);
169
+ if (cont.watcher) return cont.watcher.stop().then(() => {
170
+ cont.rawEmitter = cont.watcher = undefined;
171
+ Object.freeze(cont);
172
+ });
173
+ }
174
+ };
175
+ }
176
+
177
+ // Decide whether or not we should start a new higher-level
178
+ // parent watcher
179
+ const couldConsolidate = (path) => {
180
+ let count = 0;
181
+ for (const watchPath of FSEventsWatchers.keys()) {
182
+ if (watchPath.indexOf(path) === 0) {
183
+ count++;
184
+ if (count >= consolidateThreshhold) {
185
+ return true;
186
+ }
187
+ }
188
+ }
189
+
190
+ return false;
191
+ };
192
+
193
+ // returns boolean indicating whether fsevents can be used
194
+ const canUse = () => fsevents && FSEventsWatchers.size < 128;
195
+
196
+ // determines subdirectory traversal levels from root to path
197
+ const calcDepth = (path, root) => {
198
+ let i = 0;
199
+ while (!path.indexOf(root) && (path = sysPath.dirname(path)) !== root) i++;
200
+ return i;
201
+ };
202
+
203
+ // returns boolean indicating whether the fsevents' event info has the same type
204
+ // as the one returned by fs.stat
205
+ const sameTypes = (info, stats) => (
206
+ info.type === FSEVENT_TYPE_DIRECTORY && stats.isDirectory() ||
207
+ info.type === FSEVENT_TYPE_SYMLINK && stats.isSymbolicLink() ||
208
+ info.type === FSEVENT_TYPE_FILE && stats.isFile()
209
+ )
210
+
211
+ /**
212
+ * @mixin
213
+ */
214
+ class FsEventsHandler {
215
+
216
+ /**
217
+ * @param {import('../index').FSWatcher} fsw
218
+ */
219
+ constructor(fsw) {
220
+ this.fsw = fsw;
221
+ }
222
+ checkIgnored(path, stats) {
223
+ const ipaths = this.fsw._ignoredPaths;
224
+ if (this.fsw._isIgnored(path, stats)) {
225
+ ipaths.add(path);
226
+ if (stats && stats.isDirectory()) {
227
+ ipaths.add(path + ROOT_GLOBSTAR);
228
+ }
229
+ return true;
230
+ }
231
+
232
+ ipaths.delete(path);
233
+ ipaths.delete(path + ROOT_GLOBSTAR);
234
+ }
235
+
236
+ addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
237
+ const event = watchedDir.has(item) ? EV_CHANGE : EV_ADD;
238
+ this.handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts);
239
+ }
240
+
241
+ async checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts) {
242
+ try {
243
+ const stats = await stat(path)
244
+ if (this.fsw.closed) return;
245
+ if (sameTypes(info, stats)) {
246
+ this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
247
+ } else {
248
+ this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
249
+ }
250
+ } catch (error) {
251
+ if (error.code === 'EACCES') {
252
+ this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
253
+ } else {
254
+ this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
255
+ }
256
+ }
257
+ }
258
+
259
+ handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts) {
260
+ if (this.fsw.closed || this.checkIgnored(path)) return;
261
+
262
+ if (event === EV_UNLINK) {
263
+ const isDirectory = info.type === FSEVENT_TYPE_DIRECTORY
264
+ // suppress unlink events on never before seen files
265
+ if (isDirectory || watchedDir.has(item)) {
266
+ this.fsw._remove(parent, item, isDirectory);
267
+ }
268
+ } else {
269
+ if (event === EV_ADD) {
270
+ // track new directories
271
+ if (info.type === FSEVENT_TYPE_DIRECTORY) this.fsw._getWatchedDir(path);
272
+
273
+ if (info.type === FSEVENT_TYPE_SYMLINK && opts.followSymlinks) {
274
+ // push symlinks back to the top of the stack to get handled
275
+ const curDepth = opts.depth === undefined ?
276
+ undefined : calcDepth(fullPath, realPath) + 1;
277
+ return this._addToFsEvents(path, false, true, curDepth);
278
+ }
279
+
280
+ // track new paths
281
+ // (other than symlinks being followed, which will be tracked soon)
282
+ this.fsw._getWatchedDir(parent).add(item);
283
+ }
284
+ /**
285
+ * @type {'add'|'addDir'|'unlink'|'unlinkDir'}
286
+ */
287
+ const eventName = info.type === FSEVENT_TYPE_DIRECTORY ? event + DIR_SUFFIX : event;
288
+ this.fsw._emit(eventName, path);
289
+ if (eventName === EV_ADD_DIR) this._addToFsEvents(path, false, true);
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Handle symlinks encountered during directory scan
295
+ * @param {String} watchPath - file/dir path to be watched with fsevents
296
+ * @param {String} realPath - real path (in case of symlinks)
297
+ * @param {Function} transform - path transformer
298
+ * @param {Function} globFilter - path filter in case a glob pattern was provided
299
+ * @returns {Function} closer for the watcher instance
300
+ */
301
+ _watchWithFsEvents(watchPath, realPath, transform, globFilter) {
302
+ if (this.fsw.closed || this.fsw._isIgnored(watchPath)) return;
303
+ const opts = this.fsw.options;
304
+ const watchCallback = async (fullPath, flags, info) => {
305
+ if (this.fsw.closed) return;
306
+ if (
307
+ opts.depth !== undefined &&
308
+ calcDepth(fullPath, realPath) > opts.depth
309
+ ) return;
310
+ const path = transform(sysPath.join(
311
+ watchPath, sysPath.relative(watchPath, fullPath)
312
+ ));
313
+ if (globFilter && !globFilter(path)) return;
314
+ // ensure directories are tracked
315
+ const parent = sysPath.dirname(path);
316
+ const item = sysPath.basename(path);
317
+ const watchedDir = this.fsw._getWatchedDir(
318
+ info.type === FSEVENT_TYPE_DIRECTORY ? path : parent
319
+ );
320
+
321
+ // correct for wrong events emitted
322
+ if (wrongEventFlags.has(flags) || info.event === FSEVENT_UNKNOWN) {
323
+ if (typeof opts.ignored === FUNCTION_TYPE) {
324
+ let stats;
325
+ try {
326
+ stats = await stat(path);
327
+ } catch (error) {}
328
+ if (this.fsw.closed) return;
329
+ if (this.checkIgnored(path, stats)) return;
330
+ if (sameTypes(info, stats)) {
331
+ this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
332
+ } else {
333
+ this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts);
334
+ }
335
+ } else {
336
+ this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts);
337
+ }
338
+ } else {
339
+ switch (info.event) {
340
+ case FSEVENT_CREATED:
341
+ case FSEVENT_MODIFIED:
342
+ return this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts);
343
+ case FSEVENT_DELETED:
344
+ case FSEVENT_MOVED:
345
+ return this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts);
346
+ }
347
+ }
348
+ };
349
+
350
+ const closer = setFSEventsListener(
351
+ watchPath,
352
+ realPath,
353
+ watchCallback,
354
+ this.fsw._emitRaw
355
+ );
356
+
357
+ this.fsw._emitReady();
358
+ return closer;
359
+ }
360
+
361
+ /**
362
+ * Handle symlinks encountered during directory scan
363
+ * @param {String} linkPath path to symlink
364
+ * @param {String} fullPath absolute path to the symlink
365
+ * @param {Function} transform pre-existing path transformer
366
+ * @param {Number} curDepth level of subdirectories traversed to where symlink is
367
+ * @returns {Promise<void>}
368
+ */
369
+ async _handleFsEventsSymlink(linkPath, fullPath, transform, curDepth) {
370
+ // don't follow the same symlink more than once
371
+ if (this.fsw.closed || this.fsw._symlinkPaths.has(fullPath)) return;
372
+
373
+ this.fsw._symlinkPaths.set(fullPath, true);
374
+ this.fsw._incrReadyCount();
375
+
376
+ try {
377
+ const linkTarget = await realpath(linkPath);
378
+ if (this.fsw.closed) return;
379
+ if (this.fsw._isIgnored(linkTarget)) {
380
+ return this.fsw._emitReady();
381
+ }
382
+
383
+ this.fsw._incrReadyCount();
384
+
385
+ // add the linkTarget for watching with a wrapper for transform
386
+ // that causes emitted paths to incorporate the link's path
387
+ this._addToFsEvents(linkTarget || linkPath, (path) => {
388
+ let aliasedPath = linkPath;
389
+ if (linkTarget && linkTarget !== DOT_SLASH) {
390
+ aliasedPath = path.replace(linkTarget, linkPath);
391
+ } else if (path !== DOT_SLASH) {
392
+ aliasedPath = sysPath.join(linkPath, path);
393
+ }
394
+ return transform(aliasedPath);
395
+ }, false, curDepth);
396
+ } catch(error) {
397
+ if (this.fsw._handleError(error)) {
398
+ return this.fsw._emitReady();
399
+ }
400
+ }
401
+ }
402
+
403
+ /**
404
+ *
405
+ * @param {Path} newPath
406
+ * @param {fs.Stats} stats
407
+ */
408
+ emitAdd(newPath, stats, processPath, opts, forceAdd) {
409
+ const pp = processPath(newPath);
410
+ const isDir = stats.isDirectory();
411
+ const dirObj = this.fsw._getWatchedDir(sysPath.dirname(pp));
412
+ const base = sysPath.basename(pp);
413
+
414
+ // ensure empty dirs get tracked
415
+ if (isDir) this.fsw._getWatchedDir(pp);
416
+ if (dirObj.has(base)) return;
417
+ dirObj.add(base);
418
+
419
+ if (!opts.ignoreInitial || forceAdd === true) {
420
+ this.fsw._emit(isDir ? EV_ADD_DIR : EV_ADD, pp, stats);
421
+ }
422
+ }
423
+
424
+ initWatch(realPath, path, wh, processPath) {
425
+ if (this.fsw.closed) return;
426
+ const closer = this._watchWithFsEvents(
427
+ wh.watchPath,
428
+ sysPath.resolve(realPath || wh.watchPath),
429
+ processPath,
430
+ wh.globFilter
431
+ );
432
+ this.fsw._addPathCloser(path, closer);
433
+ }
434
+
435
+ /**
436
+ * Handle added path with fsevents
437
+ * @param {String} path file/dir path or glob pattern
438
+ * @param {Function|Boolean=} transform converts working path to what the user expects
439
+ * @param {Boolean=} forceAdd ensure add is emitted
440
+ * @param {Number=} priorDepth Level of subdirectories already traversed.
441
+ * @returns {Promise<void>}
442
+ */
443
+ async _addToFsEvents(path, transform, forceAdd, priorDepth) {
444
+ if (this.fsw.closed) {
445
+ return;
446
+ }
447
+ const opts = this.fsw.options;
448
+ const processPath = typeof transform === FUNCTION_TYPE ? transform : IDENTITY_FN;
449
+
450
+ const wh = this.fsw._getWatchHelpers(path);
451
+
452
+ // evaluate what is at the path we're being asked to watch
453
+ try {
454
+ const stats = await statMethods[wh.statMethod](wh.watchPath);
455
+ if (this.fsw.closed) return;
456
+ if (this.fsw._isIgnored(wh.watchPath, stats)) {
457
+ throw null;
458
+ }
459
+ if (stats.isDirectory()) {
460
+ // emit addDir unless this is a glob parent
461
+ if (!wh.globFilter) this.emitAdd(processPath(path), stats, processPath, opts, forceAdd);
462
+
463
+ // don't recurse further if it would exceed depth setting
464
+ if (priorDepth && priorDepth > opts.depth) return;
465
+
466
+ // scan the contents of the dir
467
+ this.fsw._readdirp(wh.watchPath, {
468
+ fileFilter: entry => wh.filterPath(entry),
469
+ directoryFilter: entry => wh.filterDir(entry),
470
+ ...Depth(opts.depth - (priorDepth || 0))
471
+ }).on(STR_DATA, (entry) => {
472
+ // need to check filterPath on dirs b/c filterDir is less restrictive
473
+ if (this.fsw.closed) {
474
+ return;
475
+ }
476
+ if (entry.stats.isDirectory() && !wh.filterPath(entry)) return;
477
+
478
+ const joinedPath = sysPath.join(wh.watchPath, entry.path);
479
+ const {fullPath} = entry;
480
+
481
+ if (wh.followSymlinks && entry.stats.isSymbolicLink()) {
482
+ // preserve the current depth here since it can't be derived from
483
+ // real paths past the symlink
484
+ const curDepth = opts.depth === undefined ?
485
+ undefined : calcDepth(joinedPath, sysPath.resolve(wh.watchPath)) + 1;
486
+
487
+ this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth);
488
+ } else {
489
+ this.emitAdd(joinedPath, entry.stats, processPath, opts, forceAdd);
490
+ }
491
+ }).on(EV_ERROR, EMPTY_FN).on(STR_END, () => {
492
+ this.fsw._emitReady();
493
+ });
494
+ } else {
495
+ this.emitAdd(wh.watchPath, stats, processPath, opts, forceAdd);
496
+ this.fsw._emitReady();
497
+ }
498
+ } catch (error) {
499
+ if (!error || this.fsw._handleError(error)) {
500
+ // TODO: Strange thing: "should not choke on an ignored watch path" will be failed without 2 ready calls -__-
501
+ this.fsw._emitReady();
502
+ this.fsw._emitReady();
503
+ }
504
+ }
505
+
506
+ if (opts.persistent && forceAdd !== true) {
507
+ if (typeof transform === FUNCTION_TYPE) {
508
+ // realpath has already been resolved
509
+ this.initWatch(undefined, path, wh, processPath);
510
+ } else {
511
+ let realPath;
512
+ try {
513
+ realPath = await realpath(wh.watchPath);
514
+ } catch (e) {}
515
+ this.initWatch(realPath, path, wh, processPath);
516
+ }
517
+ }
518
+ }
519
+
520
+ }
521
+
522
+ module.exports = FsEventsHandler;
523
+ module.exports.canUse = canUse;