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