Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / chokidar / index.js
1 'use strict';
2 var EventEmitter = require('events').EventEmitter;
3 var fs = require('fs');
4 var sysPath = require('path');
5 var asyncEach = require('async-each');
6 var anymatch = require('anymatch');
7 var globParent = require('glob-parent');
8 var isGlob = require('is-glob');
9 var isAbsolute = require('path-is-absolute');
10 var inherits = require('inherits');
11
12 var NodeFsHandler = require('./lib/nodefs-handler');
13 var FsEventsHandler = require('./lib/fsevents-handler');
14
15 var arrify = function(value) {
16   if (value == null) return [];
17   return Array.isArray(value) ? value : [value];
18 };
19
20 var flatten = function(list, result) {
21   if (result == null) result = [];
22   list.forEach(function(item) {
23     if (Array.isArray(item)) {
24       flatten(item, result);
25     } else {
26       result.push(item);
27     }
28   });
29   return result;
30 };
31
32 // Little isString util for use in Array#every.
33 var isString = function(thing) {
34   return typeof thing === 'string';
35 };
36
37 // Public: Main class.
38 // Watches files & directories for changes.
39 //
40 // * _opts - object, chokidar options hash
41 //
42 // Emitted events:
43 // `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
44 //
45 // Examples
46 //
47 //  var watcher = new FSWatcher()
48 //    .add(directories)
49 //    .on('add', path => console.log('File', path, 'was added'))
50 //    .on('change', path => console.log('File', path, 'was changed'))
51 //    .on('unlink', path => console.log('File', path, 'was removed'))
52 //    .on('all', (event, path) => console.log(path, ' emitted ', event))
53 //
54 function FSWatcher(_opts) {
55   EventEmitter.call(this);
56   var opts = {};
57   // in case _opts that is passed in is a frozen object
58   if (_opts) for (var opt in _opts) opts[opt] = _opts[opt];
59   this._watched = Object.create(null);
60   this._closers = Object.create(null);
61   this._ignoredPaths = Object.create(null);
62   Object.defineProperty(this, '_globIgnored', {
63     get: function() { return Object.keys(this._ignoredPaths); }
64   });
65   this.closed = false;
66   this._throttled = Object.create(null);
67   this._symlinkPaths = Object.create(null);
68
69   function undef(key) {
70     return opts[key] === undefined;
71   }
72
73   // Set up default options.
74   if (undef('persistent')) opts.persistent = true;
75   if (undef('ignoreInitial')) opts.ignoreInitial = false;
76   if (undef('ignorePermissionErrors')) opts.ignorePermissionErrors = false;
77   if (undef('interval')) opts.interval = 100;
78   if (undef('binaryInterval')) opts.binaryInterval = 300;
79   this.enableBinaryInterval = opts.binaryInterval !== opts.interval;
80
81   // Enable fsevents on OS X when polling isn't explicitly enabled.
82   if (undef('useFsEvents')) opts.useFsEvents = !opts.usePolling;
83
84   // If we can't use fsevents, ensure the options reflect it's disabled.
85   if (!FsEventsHandler.canUse()) opts.useFsEvents = false;
86
87   // Use polling on Mac if not using fsevents.
88   // Other platforms use non-polling fs.watch.
89   if (undef('usePolling') && !opts.useFsEvents) {
90     opts.usePolling = process.platform === 'darwin';
91   }
92
93   // Global override (useful for end-developers that need to force polling for all
94   // instances of chokidar, regardless of usage/dependency depth)
95   var envPoll = process.env.CHOKIDAR_USEPOLLING;
96   if (envPoll !== undefined) {
97     var envLower = envPoll.toLowerCase();
98
99     if (envLower === 'false' || envLower === '0') {
100       opts.usePolling = false;
101     } else if (envLower === 'true' || envLower === '1') {
102       opts.usePolling = true;
103     } else {
104       opts.usePolling = !!envLower
105     }
106   }
107
108   // Editor atomic write normalization enabled by default with fs.watch
109   if (undef('atomic')) opts.atomic = !opts.usePolling && !opts.useFsEvents;
110   if (opts.atomic) this._pendingUnlinks = Object.create(null);
111
112   if (undef('followSymlinks')) opts.followSymlinks = true;
113
114   if (undef('awaitWriteFinish')) opts.awaitWriteFinish = false;
115   if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {};
116   var awf = opts.awaitWriteFinish;
117   if (awf) {
118     if (!awf.stabilityThreshold) awf.stabilityThreshold = 2000;
119     if (!awf.pollInterval) awf.pollInterval = 100;
120
121     this._pendingWrites = Object.create(null);
122   }
123   if (opts.ignored) opts.ignored = arrify(opts.ignored);
124
125   this._isntIgnored = function(path, stat) {
126     return !this._isIgnored(path, stat);
127   }.bind(this);
128
129   var readyCalls = 0;
130   this._emitReady = function() {
131     if (++readyCalls >= this._readyCount) {
132       this._emitReady = Function.prototype;
133       this._readyEmitted = true;
134       // use process.nextTick to allow time for listener to be bound
135       process.nextTick(this.emit.bind(this, 'ready'));
136     }
137   }.bind(this);
138
139   this.options = opts;
140
141   // You’re frozen when your heart’s not open.
142   Object.freeze(opts);
143 }
144
145 inherits(FSWatcher, EventEmitter);
146
147 // Common helpers
148 // --------------
149
150 // Private method: Normalize and emit events
151 //
152 // * event     - string, type of event
153 // * path      - string, file or directory path
154 // * val[1..3] - arguments to be passed with event
155 //
156 // Returns the error if defined, otherwise the value of the
157 // FSWatcher instance's `closed` flag
158 FSWatcher.prototype._emit = function(event, path, val1, val2, val3) {
159   if (this.options.cwd) path = sysPath.relative(this.options.cwd, path);
160   var args = [event, path];
161   if (val3 !== undefined) args.push(val1, val2, val3);
162   else if (val2 !== undefined) args.push(val1, val2);
163   else if (val1 !== undefined) args.push(val1);
164
165   var awf = this.options.awaitWriteFinish;
166   if (awf && this._pendingWrites[path]) {
167     this._pendingWrites[path].lastChange = new Date();
168     return this;
169   }
170
171   if (this.options.atomic) {
172     if (event === 'unlink') {
173       this._pendingUnlinks[path] = args;
174       setTimeout(function() {
175         Object.keys(this._pendingUnlinks).forEach(function(path) {
176           this.emit.apply(this, this._pendingUnlinks[path]);
177           this.emit.apply(this, ['all'].concat(this._pendingUnlinks[path]));
178           delete this._pendingUnlinks[path];
179         }.bind(this));
180       }.bind(this), typeof this.options.atomic === "number"
181         ? this.options.atomic
182         : 100);
183       return this;
184     } else if (event === 'add' && this._pendingUnlinks[path]) {
185       event = args[0] = 'change';
186       delete this._pendingUnlinks[path];
187     }
188   }
189
190   var emitEvent = function() {
191     this.emit.apply(this, args);
192     if (event !== 'error') this.emit.apply(this, ['all'].concat(args));
193   }.bind(this);
194
195   if (awf && (event === 'add' || event === 'change') && this._readyEmitted) {
196     var awfEmit = function(err, stats) {
197       if (err) {
198         event = args[0] = 'error';
199         args[1] = err;
200         emitEvent();
201       } else if (stats) {
202         // if stats doesn't exist the file must have been deleted
203         if (args.length > 2) {
204           args[2] = stats;
205         } else {
206           args.push(stats);
207         }
208         emitEvent();
209       }
210     };
211
212     this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
213     return this;
214   }
215
216   if (event === 'change') {
217     if (!this._throttle('change', path, 50)) return this;
218   }
219
220   if (
221     this.options.alwaysStat && val1 === undefined &&
222     (event === 'add' || event === 'addDir' || event === 'change')
223   ) {
224     var fullPath = this.options.cwd ? sysPath.join(this.options.cwd, path) : path;
225     fs.stat(fullPath, function(error, stats) {
226       // Suppress event when fs.stat fails, to avoid sending undefined 'stat'
227       if (error || !stats) return;
228
229       args.push(stats);
230       emitEvent();
231     });
232   } else {
233     emitEvent();
234   }
235
236   return this;
237 };
238
239 // Private method: Common handler for errors
240 //
241 // * error  - object, Error instance
242 //
243 // Returns the error if defined, otherwise the value of the
244 // FSWatcher instance's `closed` flag
245 FSWatcher.prototype._handleError = function(error) {
246   var code = error && error.code;
247   var ipe = this.options.ignorePermissionErrors;
248   if (error &&
249     code !== 'ENOENT' &&
250     code !== 'ENOTDIR' &&
251     (!ipe || (code !== 'EPERM' && code !== 'EACCES'))
252   ) this.emit('error', error);
253   return error || this.closed;
254 };
255
256 // Private method: Helper utility for throttling
257 //
258 // * action  - string, type of action being throttled
259 // * path    - string, path being acted upon
260 // * timeout - int, duration of time to suppress duplicate actions
261 //
262 // Returns throttle tracking object or false if action should be suppressed
263 FSWatcher.prototype._throttle = function(action, path, timeout) {
264   if (!(action in this._throttled)) {
265     this._throttled[action] = Object.create(null);
266   }
267   var throttled = this._throttled[action];
268   if (path in throttled) return false;
269   function clear() {
270     delete throttled[path];
271     clearTimeout(timeoutObject);
272   }
273   var timeoutObject = setTimeout(clear, timeout);
274   throttled[path] = {timeoutObject: timeoutObject, clear: clear};
275   return throttled[path];
276 };
277
278 // Private method: Awaits write operation to finish
279 //
280 // * path    - string, path being acted upon
281 // * threshold - int, time in milliseconds a file size must be fixed before
282 //                    acknowledgeing write operation is finished
283 // * awfEmit - function, to be called when ready for event to be emitted
284 // Polls a newly created file for size variations. When files size does not
285 // change for 'threshold' milliseconds calls callback.
286 FSWatcher.prototype._awaitWriteFinish = function(path, threshold, event, awfEmit) {
287   var timeoutHandler;
288
289   var fullPath = path;
290   if (this.options.cwd && !isAbsolute(path)) {
291     fullPath = sysPath.join(this.options.cwd, path);
292   }
293
294   var now = new Date();
295
296   var awaitWriteFinish = (function (prevStat) {
297     fs.stat(fullPath, function(err, curStat) {
298       if (err) {
299         if (err.code !== 'ENOENT') awfEmit(err);
300         return;
301       }
302
303       var now = new Date();
304
305       if (prevStat && curStat.size != prevStat.size) {
306         this._pendingWrites[path].lastChange = now;
307       }
308
309       if (now - this._pendingWrites[path].lastChange >= threshold) {
310         delete this._pendingWrites[path];
311         awfEmit(null, curStat);
312       } else {
313         timeoutHandler = setTimeout(
314           awaitWriteFinish.bind(this, curStat),
315           this.options.awaitWriteFinish.pollInterval
316         );
317       }
318     }.bind(this));
319   }.bind(this));
320
321   if (!(path in this._pendingWrites)) {
322     this._pendingWrites[path] = {
323       lastChange: now,
324       cancelWait: function() {
325         delete this._pendingWrites[path];
326         clearTimeout(timeoutHandler);
327         return event;
328       }.bind(this)
329     };
330     timeoutHandler = setTimeout(
331       awaitWriteFinish.bind(this),
332       this.options.awaitWriteFinish.pollInterval
333     );
334   }
335 };
336
337 // Private method: Determines whether user has asked to ignore this path
338 //
339 // * path  - string, path to file or directory
340 // * stats - object, result of fs.stat
341 //
342 // Returns boolean
343 var dotRe = /\..*\.(sw[px])$|\~$|\.subl.*\.tmp/;
344 FSWatcher.prototype._isIgnored = function(path, stats) {
345   if (this.options.atomic && dotRe.test(path)) return true;
346
347   if (!this._userIgnored) {
348     var cwd = this.options.cwd;
349     var ignored = this.options.ignored;
350     if (cwd && ignored) {
351       ignored = ignored.map(function (path) {
352         if (typeof path !== 'string') return path;
353         return isAbsolute(path) ? path : sysPath.join(cwd, path);
354       });
355     }
356     var paths = arrify(ignored)
357       .filter(function(path) {
358         return typeof path === 'string' && !isGlob(path);
359       }).map(function(path) {
360         return path + '/**';
361       });
362     this._userIgnored = anymatch(
363       this._globIgnored.concat(ignored).concat(paths)
364     );
365   }
366
367   return this._userIgnored([path, stats]);
368 };
369
370 // Private method: Provides a set of common helpers and properties relating to
371 // symlink and glob handling
372 //
373 // * path - string, file, directory, or glob pattern being watched
374 // * depth - int, at any depth > 0, this isn't a glob
375 //
376 // Returns object containing helpers for this path
377 var replacerRe = /^\.[\/\\]/;
378 FSWatcher.prototype._getWatchHelpers = function(path, depth) {
379   path = path.replace(replacerRe, '');
380   var watchPath = depth || !isGlob(path) ? path : globParent(path);
381   var fullWatchPath = sysPath.resolve(watchPath);
382   var hasGlob = watchPath !== path;
383   var globFilter = hasGlob ? anymatch(path) : false;
384   var follow = this.options.followSymlinks;
385   var globSymlink = hasGlob && follow ? null : false;
386
387   var checkGlobSymlink = function(entry) {
388     // only need to resolve once
389     // first entry should always have entry.parentDir === ''
390     if (globSymlink == null) {
391       globSymlink = entry.fullParentDir === fullWatchPath ? false : {
392         realPath: entry.fullParentDir,
393         linkPath: fullWatchPath
394       };
395     }
396
397     if (globSymlink) {
398       return entry.fullPath.replace(globSymlink.realPath, globSymlink.linkPath);
399     }
400
401     return entry.fullPath;
402   };
403
404   var entryPath = function(entry) {
405     return sysPath.join(watchPath,
406       sysPath.relative(watchPath, checkGlobSymlink(entry))
407     );
408   };
409
410   var filterPath = function(entry) {
411     if (entry.stat && entry.stat.isSymbolicLink()) return filterDir(entry);
412     var resolvedPath = entryPath(entry);
413     return (!hasGlob || globFilter(resolvedPath)) &&
414       this._isntIgnored(resolvedPath, entry.stat) &&
415       (this.options.ignorePermissionErrors ||
416         this._hasReadPermissions(entry.stat));
417   }.bind(this);
418
419   var getDirParts = function(path) {
420     if (!hasGlob) return false;
421     var parts = sysPath.relative(watchPath, path).split(/[\/\\]/);
422     return parts;
423   };
424
425   var dirParts = getDirParts(path);
426   if (dirParts && dirParts.length > 1) dirParts.pop();
427   var unmatchedGlob;
428
429   var filterDir = function(entry) {
430     if (hasGlob) {
431       var entryParts = getDirParts(checkGlobSymlink(entry));
432       var globstar = false;
433       unmatchedGlob = !dirParts.every(function(part, i) {
434         if (part === '**') globstar = true;
435         return globstar || !entryParts[i] || anymatch(part, entryParts[i]);
436       });
437     }
438     return !unmatchedGlob && this._isntIgnored(entryPath(entry), entry.stat);
439   }.bind(this);
440
441   return {
442     followSymlinks: follow,
443     statMethod: follow ? 'stat' : 'lstat',
444     path: path,
445     watchPath: watchPath,
446     entryPath: entryPath,
447     hasGlob: hasGlob,
448     globFilter: globFilter,
449     filterPath: filterPath,
450     filterDir: filterDir
451   };
452 };
453
454 // Directory helpers
455 // -----------------
456
457 // Private method: Provides directory tracking objects
458 //
459 // * directory - string, path of the directory
460 //
461 // Returns the directory's tracking object
462 FSWatcher.prototype._getWatchedDir = function(directory) {
463   var dir = sysPath.resolve(directory);
464   var watcherRemove = this._remove.bind(this);
465   if (!(dir in this._watched)) this._watched[dir] = {
466     _items: Object.create(null),
467     add: function(item) {
468       if (item !== '.') this._items[item] = true;
469     },
470     remove: function(item) {
471       delete this._items[item];
472       if (!this.children().length) {
473         fs.readdir(dir, function(err) {
474           if (err) watcherRemove(sysPath.dirname(dir), sysPath.basename(dir));
475         });
476       }
477     },
478     has: function(item) {return item in this._items;},
479     children: function() {return Object.keys(this._items);}
480   };
481   return this._watched[dir];
482 };
483
484 // File helpers
485 // ------------
486
487 // Private method: Check for read permissions
488 // Based on this answer on SO: http://stackoverflow.com/a/11781404/1358405
489 //
490 // * stats - object, result of fs.stat
491 //
492 // Returns boolean
493 FSWatcher.prototype._hasReadPermissions = function(stats) {
494   return Boolean(4 & parseInt(((stats && stats.mode) & 0x1ff).toString(8)[0], 10));
495 };
496
497 // Private method: Handles emitting unlink events for
498 // files and directories, and via recursion, for
499 // files and directories within directories that are unlinked
500 //
501 // * directory - string, directory within which the following item is located
502 // * item      - string, base path of item/directory
503 //
504 // Returns nothing
505 FSWatcher.prototype._remove = function(directory, item) {
506   // if what is being deleted is a directory, get that directory's paths
507   // for recursive deleting and cleaning of watched object
508   // if it is not a directory, nestedDirectoryChildren will be empty array
509   var path = sysPath.join(directory, item);
510   var fullPath = sysPath.resolve(path);
511   var isDirectory = this._watched[path] || this._watched[fullPath];
512
513   // prevent duplicate handling in case of arriving here nearly simultaneously
514   // via multiple paths (such as _handleFile and _handleDir)
515   if (!this._throttle('remove', path, 100)) return;
516
517   // if the only watched file is removed, watch for its return
518   var watchedDirs = Object.keys(this._watched);
519   if (!isDirectory && !this.options.useFsEvents && watchedDirs.length === 1) {
520     this.add(directory, item, true);
521   }
522
523   // This will create a new entry in the watched object in either case
524   // so we got to do the directory check beforehand
525   var nestedDirectoryChildren = this._getWatchedDir(path).children();
526
527   // Recursively remove children directories / files.
528   nestedDirectoryChildren.forEach(function(nestedItem) {
529     this._remove(path, nestedItem);
530   }, this);
531
532   // Check if item was on the watched list and remove it
533   var parent = this._getWatchedDir(directory);
534   var wasTracked = parent.has(item);
535   parent.remove(item);
536
537   // If we wait for this file to be fully written, cancel the wait.
538   var relPath = path;
539   if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path);
540   if (this.options.awaitWriteFinish && this._pendingWrites[relPath]) {
541     var event = this._pendingWrites[relPath].cancelWait();
542     if (event === 'add') return;
543   }
544
545   // The Entry will either be a directory that just got removed
546   // or a bogus entry to a file, in either case we have to remove it
547   delete this._watched[path];
548   delete this._watched[fullPath];
549   var eventName = isDirectory ? 'unlinkDir' : 'unlink';
550   if (wasTracked && !this._isIgnored(path)) this._emit(eventName, path);
551
552   // Avoid conflicts if we later create another file with the same name
553   if (!this.options.useFsEvents) {
554     this._closePath(path);
555   }
556 };
557
558 FSWatcher.prototype._closePath = function(path) {
559   if (!this._closers[path]) return;
560   this._closers[path]();
561   delete this._closers[path];
562   this._getWatchedDir(sysPath.dirname(path)).remove(sysPath.basename(path));
563 }
564
565 // Public method: Adds paths to be watched on an existing FSWatcher instance
566
567 // * paths     - string or array of strings, file/directory paths and/or globs
568 // * _origAdd  - private boolean, for handling non-existent paths to be watched
569 // * _internal - private boolean, indicates a non-user add
570
571 // Returns an instance of FSWatcher for chaining.
572 FSWatcher.prototype.add = function(paths, _origAdd, _internal) {
573   var cwd = this.options.cwd;
574   this.closed = false;
575   paths = flatten(arrify(paths));
576
577   if (!paths.every(isString)) {
578     throw new TypeError('Non-string provided as watch path: ' + paths);
579   }
580
581   if (cwd) paths = paths.map(function(path) {
582     if (isAbsolute(path)) {
583       return path;
584     } else if (path[0] === '!') {
585       return '!' + sysPath.join(cwd, path.substring(1));
586     } else {
587       return sysPath.join(cwd, path);
588     }
589   });
590
591   // set aside negated glob strings
592   paths = paths.filter(function(path) {
593     if (path[0] === '!') {
594       this._ignoredPaths[path.substring(1)] = true;
595     } else {
596       // if a path is being added that was previously ignored, stop ignoring it
597       delete this._ignoredPaths[path];
598       delete this._ignoredPaths[path + '/**'];
599
600       // reset the cached userIgnored anymatch fn
601       // to make ignoredPaths changes effective
602       this._userIgnored = null;
603
604       return true;
605     }
606   }, this);
607
608   if (this.options.useFsEvents && FsEventsHandler.canUse()) {
609     if (!this._readyCount) this._readyCount = paths.length;
610     if (this.options.persistent) this._readyCount *= 2;
611     paths.forEach(this._addToFsEvents, this);
612   } else {
613     if (!this._readyCount) this._readyCount = 0;
614     this._readyCount += paths.length;
615     asyncEach(paths, function(path, next) {
616       this._addToNodeFs(path, !_internal, 0, 0, _origAdd, function(err, res) {
617         if (res) this._emitReady();
618         next(err, res);
619       }.bind(this));
620     }.bind(this), function(error, results) {
621       results.forEach(function(item) {
622         if (!item) return;
623         this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
624       }, this);
625     }.bind(this));
626   }
627
628   return this;
629 };
630
631 // Public method: Close watchers or start ignoring events from specified paths.
632
633 // * paths     - string or array of strings, file/directory paths and/or globs
634
635 // Returns instance of FSWatcher for chaining.
636 FSWatcher.prototype.unwatch = function(paths) {
637   if (this.closed) return this;
638   paths = flatten(arrify(paths));
639
640   paths.forEach(function(path) {
641     // convert to absolute path unless relative path already matches
642     if (!isAbsolute(path) && !this._closers[path]) {
643       if (this.options.cwd) path = sysPath.join(this.options.cwd, path);
644       path = sysPath.resolve(path);
645     }
646
647     this._closePath(path);
648
649     this._ignoredPaths[path] = true;
650     if (path in this._watched) {
651       this._ignoredPaths[path + '/**'] = true;
652     }
653
654     // reset the cached userIgnored anymatch fn
655     // to make ignoredPaths changes effective
656     this._userIgnored = null;
657   }, this);
658
659   return this;
660 };
661
662 // Public method: Close watchers and remove all listeners from watched paths.
663
664 // Returns instance of FSWatcher for chaining.
665 FSWatcher.prototype.close = function() {
666   if (this.closed) return this;
667
668   this.closed = true;
669   Object.keys(this._closers).forEach(function(watchPath) {
670     this._closers[watchPath]();
671     delete this._closers[watchPath];
672   }, this);
673   this._watched = Object.create(null);
674
675   this.removeAllListeners();
676   return this;
677 };
678
679 // Public method: Expose list of watched paths
680
681 // Returns object w/ dir paths as keys and arrays of contained paths as values.
682 FSWatcher.prototype.getWatched = function() {
683   var watchList = {};
684   Object.keys(this._watched).forEach(function(dir) {
685     var key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;
686     watchList[key || '.'] = Object.keys(this._watched[dir]._items).sort();
687   }.bind(this));
688   return watchList;
689 };
690
691 // Attach watch handler prototype methods
692 function importHandler(handler) {
693   Object.keys(handler.prototype).forEach(function(method) {
694     FSWatcher.prototype[method] = handler.prototype[method];
695   });
696 }
697 importHandler(NodeFsHandler);
698 if (FsEventsHandler.canUse()) importHandler(FsEventsHandler);
699
700 // Export FSWatcher class
701 exports.FSWatcher = FSWatcher;
702
703 // Public function: Instantiates watcher with paths to be tracked.
704
705 // * paths     - string or array of strings, file/directory paths and/or globs
706 // * options   - object, chokidar options
707
708 // Returns an instance of FSWatcher for chaining.
709 exports.watch = function(paths, options) {
710   return new FSWatcher(options).add(paths);
711 };