watcher.js 2.3 KB

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