UNPKG

3.25 kBJavaScriptView Raw
1var chokidar = require('chokidar')
2var mm = require('minimatch')
3var expandBraces = require('expand-braces')
4
5var helper = require('./helper')
6var log = require('./logger').create('watcher')
7
8var DIR_SEP = require('path').sep
9
10// Get parent folder, that be watched (does not contain any special globbing character)
11var baseDirFromPattern = function (pattern) {
12 return pattern
13 .replace(/[/\\][^/\\]*\*.*$/, '') // remove parts with *
14 .replace(/[/\\][^/\\]*[!+]\(.*$/, '') // remove parts with !(...) and +(...)
15 .replace(/[/\\][^/\\]*\)\?.*$/, '') || DIR_SEP // remove parts with (...)?
16}
17
18var watchPatterns = function (patterns, watcher) {
19 // filter only unique non url patterns paths
20 var pathsToWatch = []
21 var uniqueMap = {}
22 var path
23
24 // expand ['a/{b,c}'] to ['a/b', 'a/c']
25 patterns = expandBraces(patterns)
26
27 patterns.forEach(function (pattern) {
28 path = baseDirFromPattern(pattern)
29 if (!uniqueMap[path]) {
30 uniqueMap[path] = true
31 pathsToWatch.push(path)
32 }
33 })
34
35 // watch only common parents, no sub paths
36 pathsToWatch.forEach(function (path) {
37 if (!pathsToWatch.some(function (p) {
38 return p !== path && path.substr(0, p.length + 1) === p + DIR_SEP
39 })) {
40 watcher.add(path)
41 log.debug('Watching "%s"', path)
42 }
43 })
44}
45
46// Function to test if a path should be ignored by chokidar.
47var createIgnore = function (patterns, excludes) {
48 return function (path, stat) {
49 if (!stat || stat.isDirectory()) {
50 return false
51 }
52
53 // Check if the path matches any of the watched patterns.
54 if (!patterns.some(function (pattern) {
55 return mm(path, pattern, {dot: true})
56 })) {
57 return true
58 }
59
60 // Check if the path matches any of the exclude patterns.
61 if (excludes.some(function (pattern) {
62 return mm(path, pattern, {dot: true})
63 })) {
64 return true
65 }
66
67 return false
68 }
69}
70
71var onlyWatchedTrue = function (pattern) {
72 return pattern.watched
73}
74
75var getWatchedPatterns = function (patternObjects) {
76 return patternObjects.filter(onlyWatchedTrue).map(function (patternObject) {
77 return patternObject.pattern
78 })
79}
80
81exports.watch = function (patterns, excludes, fileList, usePolling, emitter) {
82 var watchedPatterns = getWatchedPatterns(patterns)
83 var options = {
84 usePolling: usePolling,
85 ignorePermissionErrors: true,
86 ignoreInitial: true,
87 ignored: createIgnore(watchedPatterns, excludes)
88 }
89 var chokidarWatcher = new chokidar.FSWatcher(options)
90
91 watchPatterns(watchedPatterns, chokidarWatcher)
92
93 var bind = function (fn) {
94 return function (path) {
95 return fn.call(fileList, helper.normalizeWinPath(path))
96 }
97 }
98
99 // register events
100 chokidarWatcher.on('add', bind(fileList.addFile))
101 .on('change', bind(fileList.changeFile))
102 .on('unlink', bind(fileList.removeFile))
103 // If we don't subscribe; unhandled errors from Chokidar will bring Karma down
104 // (see GH Issue #959)
105 .on('error', function (e) {
106 log.debug(e)
107 })
108
109 emitter.on('exit', function (done) {
110 chokidarWatcher.close()
111 done()
112 })
113
114 return chokidarWatcher
115}
116
117exports.watch.$inject = ['config.files', 'config.exclude', 'fileList', 'config.usePolling',
118 'emitter']