| 'use strict' |
| |
| const mm = require('minimatch') |
| const braces = require('braces') |
| const PatternUtils = require('./utils/pattern-utils') |
| |
| const helper = require('./helper') |
| const log = require('./logger').create('watcher') |
| |
| const DIR_SEP = require('path').sep |
| |
| function watchPatterns (patterns, watcher) { |
| let expandedPatterns = [] |
| patterns.map((pattern) => { |
| // expand ['a/{b,c}'] to ['a/b', 'a/c'] |
| expandedPatterns = expandedPatterns.concat(braces.expand(pattern, { keepEscaping: true })) |
| }) |
| expandedPatterns |
| .map(PatternUtils.getBaseDir) |
| .filter((path, index, paths) => paths.indexOf(path) === index) // filter unique values |
| .forEach((path, index, paths) => { |
| if (!paths.some((p) => path.startsWith(p + DIR_SEP))) { |
| watcher.add(path) |
| log.debug(`Watching "${path}"`) |
| } |
| }) |
| } |
| |
| function checkAnyPathMatch (patterns, path) { |
| return patterns.some((pattern) => mm(path, pattern, { dot: true })) |
| } |
| |
| function createIgnore (patterns, excludes) { |
| return function (path, stat) { |
| if (stat && !stat.isDirectory()) { |
| return !checkAnyPathMatch(patterns, path) || checkAnyPathMatch(excludes, path) |
| } else { |
| return false |
| } |
| } |
| } |
| |
| function getWatchedPatterns (patterns) { |
| return patterns |
| .filter((pattern) => pattern.watched) |
| .map((pattern) => pattern.pattern) |
| } |
| |
| function watch (patterns, excludes, fileList, usePolling, emitter) { |
| const watchedPatterns = getWatchedPatterns(patterns) |
| // Lazy-load 'chokidar' to make the dependency optional. This is desired when |
| // third-party watchers are in use. |
| const chokidar = require('chokidar') |
| const watcher = new chokidar.FSWatcher({ |
| usePolling: usePolling, |
| ignorePermissionErrors: true, |
| ignoreInitial: true, |
| ignored: createIgnore(watchedPatterns, excludes) |
| }) |
| |
| watchPatterns(watchedPatterns, watcher) |
| |
| watcher |
| .on('add', (path) => fileList.addFile(helper.normalizeWinPath(path))) |
| .on('change', (path) => fileList.changeFile(helper.normalizeWinPath(path))) |
| .on('unlink', (path) => fileList.removeFile(helper.normalizeWinPath(path))) |
| .on('error', log.debug.bind(log)) |
| |
| emitter.on('exit', (done) => { |
| watcher.close() |
| done() |
| }) |
| |
| return watcher |
| } |
| |
| watch.$inject = [ |
| 'config.files', |
| 'config.exclude', |
| 'fileList', |
| 'config.usePolling', |
| 'emitter' |
| ] |
| |
| module.exports = watch |