watcher.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. 'use strict'
  2. const mm = require('minimatch')
  3. const braces = require('braces')
  4. const PatternUtils = require('./utils/pattern-utils')
  5. const helper = require('./helper')
  6. const log = require('./logger').create('watcher')
  7. const DIR_SEP = require('path').sep
  8. function watchPatterns (patterns, watcher) {
  9. let expandedPatterns = []
  10. patterns.map((pattern) => {
  11. expandedPatterns = expandedPatterns.concat(braces.expand(pattern)) // expand ['a/{b,c}'] to ['a/b', 'a/c']
  12. })
  13. expandedPatterns
  14. .map(PatternUtils.getBaseDir)
  15. .filter((path, index, paths) => paths.indexOf(path) === index) // filter unique values
  16. .forEach((path, index, paths) => {
  17. if (!paths.some((p) => path.startsWith(p + DIR_SEP))) {
  18. watcher.add(path)
  19. log.debug(`Watching "${path}"`)
  20. }
  21. })
  22. }
  23. function checkAnyPathMatch (patterns, path) {
  24. return patterns.some((pattern) => mm(path, pattern, {dot: true}))
  25. }
  26. function createIgnore (patterns, excludes) {
  27. return function (path, stat) {
  28. if (stat && !stat.isDirectory()) {
  29. return !checkAnyPathMatch(patterns, path) || checkAnyPathMatch(excludes, path)
  30. } else {
  31. return false
  32. }
  33. }
  34. }
  35. function getWatchedPatterns (patterns) {
  36. return patterns
  37. .filter((pattern) => pattern.watched)
  38. .map((pattern) => pattern.pattern)
  39. }
  40. function watch (patterns, excludes, fileList, usePolling, emitter) {
  41. const watchedPatterns = getWatchedPatterns(patterns)
  42. // Lazy-load 'chokidar' to make the dependency optional. This is desired when
  43. // third-party watchers are in use.
  44. const chokidar = require('chokidar')
  45. const watcher = new chokidar.FSWatcher({
  46. usePolling: usePolling,
  47. ignorePermissionErrors: true,
  48. ignoreInitial: true,
  49. ignored: createIgnore(watchedPatterns, excludes)
  50. })
  51. watchPatterns(watchedPatterns, watcher)
  52. watcher
  53. .on('add', (path) => fileList.addFile(helper.normalizeWinPath(path)))
  54. .on('change', (path) => fileList.changeFile(helper.normalizeWinPath(path)))
  55. .on('unlink', (path) => fileList.removeFile(helper.normalizeWinPath(path)))
  56. .on('error', log.debug.bind(log))
  57. emitter.on('exit', (done) => {
  58. watcher.close()
  59. done()
  60. })
  61. return watcher
  62. }
  63. watch.$inject = [
  64. 'config.files',
  65. 'config.exclude',
  66. 'fileList',
  67. 'config.usePolling',
  68. 'emitter'
  69. ]
  70. module.exports = watch