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