You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					277 lines
				
				6.6 KiB
			
		
		
			
		
	
	
					277 lines
				
				6.6 KiB
			| 
											3 years ago
										 | const minimatch = require('minimatch'); | ||
|  | const path = require('path'); | ||
|  | const fs = require('fs'); | ||
|  | const debug = require('debug')('nodemon:match'); | ||
|  | const utils = require('../utils'); | ||
|  | 
 | ||
|  | module.exports = match; | ||
|  | module.exports.rulesToMonitor = rulesToMonitor; | ||
|  | 
 | ||
|  | function rulesToMonitor(watch, ignore, config) { | ||
|  |   var monitor = []; | ||
|  | 
 | ||
|  |   if (!Array.isArray(ignore)) { | ||
|  |     if (ignore) { | ||
|  |       ignore = [ignore]; | ||
|  |     } else { | ||
|  |       ignore = []; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (!Array.isArray(watch)) { | ||
|  |     if (watch) { | ||
|  |       watch = [watch]; | ||
|  |     } else { | ||
|  |       watch = []; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (watch && watch.length) { | ||
|  |     monitor = utils.clone(watch); | ||
|  |   } | ||
|  | 
 | ||
|  |   if (ignore) { | ||
|  |     [].push.apply(monitor, (ignore || []).map(function (rule) { | ||
|  |       return '!' + rule; | ||
|  |     })); | ||
|  |   } | ||
|  | 
 | ||
|  |   var cwd = process.cwd(); | ||
|  | 
 | ||
|  |   // next check if the monitored paths are actual directories
 | ||
|  |   // or just patterns - and expand the rule to include *.*
 | ||
|  |   monitor = monitor.map(function (rule) { | ||
|  |     var not = rule.slice(0, 1) === '!'; | ||
|  | 
 | ||
|  |     if (not) { | ||
|  |       rule = rule.slice(1); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (rule === '.' || rule === '.*') { | ||
|  |       rule = '*.*'; | ||
|  |     } | ||
|  | 
 | ||
|  |     var dir = path.resolve(cwd, rule); | ||
|  | 
 | ||
|  |     try { | ||
|  |       var stat = fs.statSync(dir); | ||
|  |       if (stat.isDirectory()) { | ||
|  |         rule = dir; | ||
|  |         if (rule.slice(-1) !== '/') { | ||
|  |           rule += '/'; | ||
|  |         } | ||
|  |         rule += '**/*'; | ||
|  | 
 | ||
|  |         // `!not` ... sorry.
 | ||
|  |         if (!not) { | ||
|  |           config.dirs.push(dir); | ||
|  |         } | ||
|  |       } else { | ||
|  |         // ensures we end up in the check that tries to get a base directory
 | ||
|  |         // and then adds it to the watch list
 | ||
|  |         throw new Error(); | ||
|  |       } | ||
|  |     } catch (e) { | ||
|  |       var base = tryBaseDir(dir); | ||
|  |       if (!not && base) { | ||
|  |         if (config.dirs.indexOf(base) === -1) { | ||
|  |           config.dirs.push(base); | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     if (rule.slice(-1) === '/') { | ||
|  |       // just slap on a * anyway
 | ||
|  |       rule += '*'; | ||
|  |     } | ||
|  | 
 | ||
|  |     // if the url ends with * but not **/* and not *.*
 | ||
|  |     // then convert to **/* - somehow it was missed :-\
 | ||
|  |     if (rule.slice(-4) !== '**/*' && | ||
|  |       rule.slice(-1) === '*' && | ||
|  |       rule.indexOf('*.') === -1) { | ||
|  | 
 | ||
|  |       if (rule.slice(-2) !== '**') { | ||
|  |         rule += '*/*'; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  | 
 | ||
|  |     return (not ? '!' : '') + rule; | ||
|  |   }); | ||
|  | 
 | ||
|  |   return monitor; | ||
|  | } | ||
|  | 
 | ||
|  | function tryBaseDir(dir) { | ||
|  |   var stat; | ||
|  |   if (/[?*\{\[]+/.test(dir)) { // if this is pattern, then try to find the base
 | ||
|  |     try { | ||
|  |       var base = path.dirname(dir.replace(/([?*\{\[]+.*$)/, 'foo')); | ||
|  |       stat = fs.statSync(base); | ||
|  |       if (stat.isDirectory()) { | ||
|  |         return base; | ||
|  |       } | ||
|  |     } catch (error) { | ||
|  |       // console.log(error);
 | ||
|  |     } | ||
|  |   } else { | ||
|  |     try { | ||
|  |       stat = fs.statSync(dir); | ||
|  |       // if this path is actually a single file that exists, then just monitor
 | ||
|  |       // that, *specifically*.
 | ||
|  |       if (stat.isFile() || stat.isDirectory()) { | ||
|  |         return dir; | ||
|  |       } | ||
|  |     } catch (e) { } | ||
|  |   } | ||
|  | 
 | ||
|  |   return false; | ||
|  | } | ||
|  | 
 | ||
|  | function match(files, monitor, ext) { | ||
|  |   // sort the rules by highest specificity (based on number of slashes)
 | ||
|  |   // ignore rules (!) get sorted highest as they take precedent
 | ||
|  |   const cwd = process.cwd(); | ||
|  |   var rules = monitor.sort(function (a, b) { | ||
|  |     var r = b.split(path.sep).length - a.split(path.sep).length; | ||
|  |     var aIsIgnore = a.slice(0, 1) === '!'; | ||
|  |     var bIsIgnore = b.slice(0, 1) === '!'; | ||
|  | 
 | ||
|  |     if (aIsIgnore || bIsIgnore) { | ||
|  |       if (aIsIgnore) { | ||
|  |         return -1; | ||
|  |       } | ||
|  | 
 | ||
|  |       return 1; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (r === 0) { | ||
|  |       return b.length - a.length; | ||
|  |     } | ||
|  |     return r; | ||
|  |   }).map(function (s) { | ||
|  |     var prefix = s.slice(0, 1); | ||
|  | 
 | ||
|  |     if (prefix === '!') { | ||
|  |       if (s.indexOf('!' + cwd) === 0) { | ||
|  |         return s; | ||
|  |       } | ||
|  | 
 | ||
|  |       // if it starts with a period, then let's get the relative path
 | ||
|  |       if (s.indexOf('!.') === 0) { | ||
|  |         return '!' + path.resolve(cwd, s.substring(1)); | ||
|  |       } | ||
|  | 
 | ||
|  |       return '!**' + (prefix !== path.sep ? path.sep : '') + s.slice(1); | ||
|  |     } | ||
|  | 
 | ||
|  |     // if it starts with a period, then let's get the relative path
 | ||
|  |     if (s.indexOf('.') === 0) { | ||
|  |       return path.resolve(cwd, s); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (s.indexOf(cwd) === 0) { | ||
|  |       return s; | ||
|  |     } | ||
|  | 
 | ||
|  |     return '**' + (prefix !== path.sep ? path.sep : '') + s; | ||
|  |   }); | ||
|  | 
 | ||
|  |   debug('rules', rules); | ||
|  | 
 | ||
|  |   var good = []; | ||
|  |   var whitelist = []; // files that we won't check against the extension
 | ||
|  |   var ignored = 0; | ||
|  |   var watched = 0; | ||
|  |   var usedRules = []; | ||
|  |   var minimatchOpts = { | ||
|  |     dot: true, | ||
|  |   }; | ||
|  | 
 | ||
|  |   // enable case-insensitivity on Windows
 | ||
|  |   if (utils.isWindows) { | ||
|  |     minimatchOpts.nocase = true; | ||
|  |   } | ||
|  | 
 | ||
|  |   files.forEach(function (file) { | ||
|  |     file = path.resolve(cwd, file); | ||
|  | 
 | ||
|  |     var matched = false; | ||
|  |     for (var i = 0; i < rules.length; i++) { | ||
|  |       if (rules[i].slice(0, 1) === '!') { | ||
|  |         if (!minimatch(file, rules[i], minimatchOpts)) { | ||
|  |           debug('ignored', file, 'rule:', rules[i]); | ||
|  |           ignored++; | ||
|  |           matched = true; | ||
|  |           break; | ||
|  |         } | ||
|  |       } else { | ||
|  |         debug('matched', file, 'rule:', rules[i]); | ||
|  |         if (minimatch(file, rules[i], minimatchOpts)) { | ||
|  |           watched++; | ||
|  | 
 | ||
|  |           // don't repeat the output if a rule is matched
 | ||
|  |           if (usedRules.indexOf(rules[i]) === -1) { | ||
|  |             usedRules.push(rules[i]); | ||
|  |             utils.log.detail('matched rule: ' + rules[i]); | ||
|  |           } | ||
|  | 
 | ||
|  |           // if the rule doesn't match the WATCH EVERYTHING
 | ||
|  |           // but *does* match a rule that ends with *.*, then
 | ||
|  |           // white list it - in that we don't run it through
 | ||
|  |           // the extension check too.
 | ||
|  |           if (rules[i] !== '**' + path.sep + '*.*' && | ||
|  |             rules[i].slice(-3) === '*.*') { | ||
|  |             whitelist.push(file); | ||
|  |           } else if (path.basename(file) === path.basename(rules[i])) { | ||
|  |             // if the file matches the actual rule, then it's put on whitelist
 | ||
|  |             whitelist.push(file); | ||
|  |           } else { | ||
|  |             good.push(file); | ||
|  |           } | ||
|  |           matched = true; | ||
|  |           break; | ||
|  |         } else { | ||
|  |           // utils.log.detail('no match: ' + rules[i], file);
 | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |     if (!matched) { | ||
|  |       ignored++; | ||
|  |     } | ||
|  |   }); | ||
|  | 
 | ||
|  |   debug('good', good) | ||
|  | 
 | ||
|  |   // finally check the good files against the extensions that we're monitoring
 | ||
|  |   if (ext) { | ||
|  |     if (ext.indexOf(',') === -1) { | ||
|  |       ext = '**/*.' + ext; | ||
|  |     } else { | ||
|  |       ext = '**/*.{' + ext + '}'; | ||
|  |     } | ||
|  | 
 | ||
|  |     good = good.filter(function (file) { | ||
|  |       // only compare the filename to the extension test
 | ||
|  |       return minimatch(path.basename(file), ext, minimatchOpts); | ||
|  |     }); | ||
|  |   } // else assume *.*
 | ||
|  | 
 | ||
|  |   var result = good.concat(whitelist); | ||
|  | 
 | ||
|  |   if (utils.isWindows) { | ||
|  |     // fix for windows testing - I *think* this is okay to do
 | ||
|  |     result = result.map(function (file) { | ||
|  |       return file.slice(0, 1).toLowerCase() + file.slice(1); | ||
|  |     }); | ||
|  |   } | ||
|  | 
 | ||
|  |   return { | ||
|  |     result: result, | ||
|  |     ignored: ignored, | ||
|  |     watched: watched, | ||
|  |     total: files.length, | ||
|  |   }; | ||
|  | } |