Spaces:
Sleeping
Sleeping
| 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, | |
| }; | |
| } | |