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,
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 |