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