You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
4.1 KiB
JavaScript
194 lines
4.1 KiB
JavaScript
6 years ago
|
'use strict';
|
||
|
|
||
|
var chars = require('./chars');
|
||
|
var utils = require('./utils');
|
||
|
|
||
|
/**
|
||
|
* Expose `Glob`
|
||
|
*/
|
||
|
|
||
|
var Glob = module.exports = function Glob(pattern, options) {
|
||
|
if (!(this instanceof Glob)) {
|
||
|
return new Glob(pattern, options);
|
||
|
}
|
||
|
this.options = options || {};
|
||
|
this.pattern = pattern;
|
||
|
this.history = [];
|
||
|
this.tokens = {};
|
||
|
this.init(pattern);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Initialize defaults
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.init = function(pattern) {
|
||
|
this.orig = pattern;
|
||
|
this.negated = this.isNegated();
|
||
|
this.options.track = this.options.track || false;
|
||
|
this.options.makeRe = true;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Push a change into `glob.history`. Useful
|
||
|
* for debugging.
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.track = function(msg) {
|
||
|
if (this.options.track) {
|
||
|
this.history.push({msg: msg, pattern: this.pattern});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return true if `glob.pattern` was negated
|
||
|
* with `!`, also remove the `!` from the pattern.
|
||
|
*
|
||
|
* @return {Boolean}
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.isNegated = function() {
|
||
|
if (this.pattern.charCodeAt(0) === 33 /* '!' */) {
|
||
|
this.pattern = this.pattern.slice(1);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Expand braces in the given glob pattern.
|
||
|
*
|
||
|
* We only need to use the [braces] lib when
|
||
|
* patterns are nested.
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.braces = function() {
|
||
|
if (this.options.nobraces !== true && this.options.nobrace !== true) {
|
||
|
// naive/fast check for imbalanced characters
|
||
|
var a = this.pattern.match(/[\{\(\[]/g);
|
||
|
var b = this.pattern.match(/[\}\)\]]/g);
|
||
|
|
||
|
// if imbalanced, don't optimize the pattern
|
||
|
if (a && b && (a.length !== b.length)) {
|
||
|
this.options.makeRe = false;
|
||
|
}
|
||
|
|
||
|
// expand brace patterns and join the resulting array
|
||
|
var expanded = utils.braces(this.pattern, this.options);
|
||
|
this.pattern = expanded.join('|');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Expand bracket expressions in `glob.pattern`
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.brackets = function() {
|
||
|
if (this.options.nobrackets !== true) {
|
||
|
this.pattern = utils.brackets(this.pattern);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Expand bracket expressions in `glob.pattern`
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.extglob = function() {
|
||
|
if (this.options.noextglob === true) return;
|
||
|
|
||
|
if (utils.isExtglob(this.pattern)) {
|
||
|
this.pattern = utils.extglob(this.pattern, {escape: true});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Parse the given pattern
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.parse = function(pattern) {
|
||
|
this.tokens = utils.parseGlob(pattern || this.pattern, true);
|
||
|
return this.tokens;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Replace `a` with `b`. Also tracks the change before and
|
||
|
* after each replacement. This is disabled by default, but
|
||
|
* can be enabled by setting `options.track` to true.
|
||
|
*
|
||
|
* Also, when the pattern is a string, `.split()` is used,
|
||
|
* because it's much faster than replace.
|
||
|
*
|
||
|
* @param {RegExp|String} `a`
|
||
|
* @param {String} `b`
|
||
|
* @param {Boolean} `escape` When `true`, escapes `*` and `?` in the replacement.
|
||
|
* @return {String}
|
||
|
*/
|
||
|
|
||
|
Glob.prototype._replace = function(a, b, escape) {
|
||
|
this.track('before (find): "' + a + '" (replace with): "' + b + '"');
|
||
|
if (escape) b = esc(b);
|
||
|
if (a && b && typeof a === 'string') {
|
||
|
this.pattern = this.pattern.split(a).join(b);
|
||
|
} else {
|
||
|
this.pattern = this.pattern.replace(a, b);
|
||
|
}
|
||
|
this.track('after');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Escape special characters in the given string.
|
||
|
*
|
||
|
* @param {String} `str` Glob pattern
|
||
|
* @return {String}
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.escape = function(str) {
|
||
|
this.track('before escape: ');
|
||
|
var re = /["\\](['"]?[^"'\\]['"]?)/g;
|
||
|
|
||
|
this.pattern = str.replace(re, function($0, $1) {
|
||
|
var o = chars.ESC;
|
||
|
var ch = o && o[$1];
|
||
|
if (ch) {
|
||
|
return ch;
|
||
|
}
|
||
|
if (/[a-z]/i.test($0)) {
|
||
|
return $0.split('\\').join('');
|
||
|
}
|
||
|
return $0;
|
||
|
});
|
||
|
|
||
|
this.track('after escape: ');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Unescape special characters in the given string.
|
||
|
*
|
||
|
* @param {String} `str`
|
||
|
* @return {String}
|
||
|
*/
|
||
|
|
||
|
Glob.prototype.unescape = function(str) {
|
||
|
var re = /__([A-Z]+)_([A-Z]+)__/g;
|
||
|
this.pattern = str.replace(re, function($0, $1) {
|
||
|
return chars[$1][$0];
|
||
|
});
|
||
|
this.pattern = unesc(this.pattern);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Escape/unescape utils
|
||
|
*/
|
||
|
|
||
|
function esc(str) {
|
||
|
str = str.split('?').join('%~');
|
||
|
str = str.split('*').join('%%');
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
function unesc(str) {
|
||
|
str = str.split('%~').join('?');
|
||
|
str = str.split('%%').join('*');
|
||
|
return str;
|
||
|
}
|