| 'use strict'; | 
|   | 
| var utils = module.exports; | 
| var path = require('path'); | 
|   | 
| /** | 
|  * Module dependencies | 
|  */ | 
|   | 
| var isWindows = require('is-windows')(); | 
| var Snapdragon = require('snapdragon'); | 
| utils.define = require('define-property'); | 
| utils.diff = require('arr-diff'); | 
| utils.extend = require('extend-shallow'); | 
| utils.pick = require('object.pick'); | 
| utils.typeOf = require('kind-of'); | 
| utils.unique = require('array-unique'); | 
|   | 
| /** | 
|  * Returns true if the given value is effectively an empty string | 
|  */ | 
|   | 
| utils.isEmptyString = function(val) { | 
|   return String(val) === '' || String(val) === './'; | 
| }; | 
|   | 
| /** | 
|  * Returns true if the platform is windows, or `path.sep` is `\\`. | 
|  * This is defined as a function to allow `path.sep` to be set in unit tests, | 
|  * or by the user, if there is a reason to do so. | 
|  * @return {Boolean} | 
|  */ | 
|   | 
| utils.isWindows = function() { | 
|   return path.sep === '\\' || isWindows === true; | 
| }; | 
|   | 
| /** | 
|  * Return the last element from an array | 
|  */ | 
|   | 
| utils.last = function(arr, n) { | 
|   return arr[arr.length - (n || 1)]; | 
| }; | 
|   | 
| /** | 
|  * Get the `Snapdragon` instance to use | 
|  */ | 
|   | 
| utils.instantiate = function(ast, options) { | 
|   var snapdragon; | 
|   // if an instance was created by `.parse`, use that instance | 
|   if (utils.typeOf(ast) === 'object' && ast.snapdragon) { | 
|     snapdragon = ast.snapdragon; | 
|   // if the user supplies an instance on options, use that instance | 
|   } else if (utils.typeOf(options) === 'object' && options.snapdragon) { | 
|     snapdragon = options.snapdragon; | 
|   // create a new instance | 
|   } else { | 
|     snapdragon = new Snapdragon(options); | 
|   } | 
|   | 
|   utils.define(snapdragon, 'parse', function(str, options) { | 
|     var parsed = Snapdragon.prototype.parse.call(this, str, options); | 
|     parsed.input = str; | 
|   | 
|     // escape unmatched brace/bracket/parens | 
|     var last = this.parser.stack.pop(); | 
|     if (last && this.options.strictErrors !== true) { | 
|       var open = last.nodes[0]; | 
|       var inner = last.nodes[1]; | 
|       if (last.type === 'bracket') { | 
|         if (inner.val.charAt(0) === '[') { | 
|           inner.val = '\\' + inner.val; | 
|         } | 
|   | 
|       } else { | 
|         open.val = '\\' + open.val; | 
|         var sibling = open.parent.nodes[1]; | 
|         if (sibling.type === 'star') { | 
|           sibling.loose = true; | 
|         } | 
|       } | 
|     } | 
|   | 
|     // add non-enumerable parser reference | 
|     utils.define(parsed, 'parser', this.parser); | 
|     return parsed; | 
|   }); | 
|   | 
|   return snapdragon; | 
| }; | 
|   | 
| /** | 
|  * Create the key to use for memoization. The key is generated | 
|  * by iterating over the options and concatenating key-value pairs | 
|  * to the pattern string. | 
|  */ | 
|   | 
| utils.createKey = function(pattern, options) { | 
|   if (typeof options === 'undefined') { | 
|     return pattern; | 
|   } | 
|   var key = pattern; | 
|   for (var prop in options) { | 
|     if (options.hasOwnProperty(prop)) { | 
|       key += ';' + prop + '=' + String(options[prop]); | 
|     } | 
|   } | 
|   return key; | 
| }; | 
|   | 
| /** | 
|  * Cast `val` to an array | 
|  * @return {Array} | 
|  */ | 
|   | 
| utils.arrayify = function(val) { | 
|   if (typeof val === 'string') return [val]; | 
|   return val ? (Array.isArray(val) ? val : [val]) : []; | 
| }; | 
|   | 
| /** | 
|  * Return true if `val` is a non-empty string | 
|  */ | 
|   | 
| utils.isString = function(val) { | 
|   return typeof val === 'string'; | 
| }; | 
|   | 
| /** | 
|  * Return true if `val` is a non-empty string | 
|  */ | 
|   | 
| utils.isRegex = function(val) { | 
|   return utils.typeOf(val) === 'regexp'; | 
| }; | 
|   | 
| /** | 
|  * Return true if `val` is a non-empty string | 
|  */ | 
|   | 
| utils.isObject = function(val) { | 
|   return utils.typeOf(val) === 'object'; | 
| }; | 
|   | 
| /** | 
|  * Escape regex characters in the given string | 
|  */ | 
|   | 
| utils.escapeRegex = function(str) { | 
|   return str.replace(/[-[\]{}()^$|*+?.\\/\s]/g, '\\$&'); | 
| }; | 
|   | 
| /** | 
|  * Combines duplicate characters in the provided `input` string. | 
|  * @param {String} `input` | 
|  * @returns {String} | 
|  */ | 
|   | 
| utils.combineDupes = function(input, patterns) { | 
|   patterns = utils.arrayify(patterns).join('|').split('|'); | 
|   patterns = patterns.map(function(s) { | 
|     return s.replace(/\\?([+*\\/])/g, '\\$1'); | 
|   }); | 
|   var substr = patterns.join('|'); | 
|   var regex = new RegExp('(' + substr + ')(?=\\1)', 'g'); | 
|   return input.replace(regex, ''); | 
| }; | 
|   | 
| /** | 
|  * Returns true if the given `str` has special characters | 
|  */ | 
|   | 
| utils.hasSpecialChars = function(str) { | 
|   return /(?:(?:(^|\/)[!.])|[*?+()|[\]{}]|[+@]\()/.test(str); | 
| }; | 
|   | 
| /** | 
|  * Normalize slashes in the given filepath. | 
|  * | 
|  * @param {String} `filepath` | 
|  * @return {String} | 
|  */ | 
|   | 
| utils.toPosixPath = function(str) { | 
|   return str.replace(/\\+/g, '/'); | 
| }; | 
|   | 
| /** | 
|  * Strip backslashes before special characters in a string. | 
|  * | 
|  * @param {String} `str` | 
|  * @return {String} | 
|  */ | 
|   | 
| utils.unescape = function(str) { | 
|   return utils.toPosixPath(str.replace(/\\(?=[*+?!.])/g, '')); | 
| }; | 
|   | 
| /** | 
|  * Strip the drive letter from a windows filepath | 
|  * @param {String} `fp` | 
|  * @return {String} | 
|  */ | 
|   | 
| utils.stripDrive = function(fp) { | 
|   return utils.isWindows() ? fp.replace(/^[a-z]:[\\/]+?/i, '/') : fp; | 
| }; | 
|   | 
| /** | 
|  * Strip the prefix from a filepath | 
|  * @param {String} `fp` | 
|  * @return {String} | 
|  */ | 
|   | 
| utils.stripPrefix = function(str) { | 
|   if (str.charAt(0) === '.' && (str.charAt(1) === '/' || str.charAt(1) === '\\')) { | 
|     return str.slice(2); | 
|   } | 
|   return str; | 
| }; | 
|   | 
| /** | 
|  * Returns true if `str` is a common character that doesn't need | 
|  * to be processed to be used for matching. | 
|  * @param {String} `str` | 
|  * @return {Boolean} | 
|  */ | 
|   | 
| utils.isSimpleChar = function(str) { | 
|   return str.trim() === '' || str === '.'; | 
| }; | 
|   | 
| /** | 
|  * Returns true if the given str is an escaped or | 
|  * unescaped path character | 
|  */ | 
|   | 
| utils.isSlash = function(str) { | 
|   return str === '/' || str === '\\/' || str === '\\' || str === '\\\\'; | 
| }; | 
|   | 
| /** | 
|  * Returns a function that returns true if the given | 
|  * pattern matches or contains a `filepath` | 
|  * | 
|  * @param {String} `pattern` | 
|  * @return {Function} | 
|  */ | 
|   | 
| utils.matchPath = function(pattern, options) { | 
|   return (options && options.contains) | 
|     ? utils.containsPattern(pattern, options) | 
|     : utils.equalsPattern(pattern, options); | 
| }; | 
|   | 
| /** | 
|  * Returns true if the given (original) filepath or unixified path are equal | 
|  * to the given pattern. | 
|  */ | 
|   | 
| utils._equals = function(filepath, unixPath, pattern) { | 
|   return pattern === filepath || pattern === unixPath; | 
| }; | 
|   | 
| /** | 
|  * Returns true if the given (original) filepath or unixified path contain | 
|  * the given pattern. | 
|  */ | 
|   | 
| utils._contains = function(filepath, unixPath, pattern) { | 
|   return filepath.indexOf(pattern) !== -1 || unixPath.indexOf(pattern) !== -1; | 
| }; | 
|   | 
| /** | 
|  * Returns a function that returns true if the given | 
|  * pattern is the same as a given `filepath` | 
|  * | 
|  * @param {String} `pattern` | 
|  * @return {Function} | 
|  */ | 
|   | 
| utils.equalsPattern = function(pattern, options) { | 
|   var unixify = utils.unixify(options); | 
|   options = options || {}; | 
|   | 
|   return function fn(filepath) { | 
|     var equal = utils._equals(filepath, unixify(filepath), pattern); | 
|     if (equal === true || options.nocase !== true) { | 
|       return equal; | 
|     } | 
|     var lower = filepath.toLowerCase(); | 
|     return utils._equals(lower, unixify(lower), pattern); | 
|   }; | 
| }; | 
|   | 
| /** | 
|  * Returns a function that returns true if the given | 
|  * pattern contains a `filepath` | 
|  * | 
|  * @param {String} `pattern` | 
|  * @return {Function} | 
|  */ | 
|   | 
| utils.containsPattern = function(pattern, options) { | 
|   var unixify = utils.unixify(options); | 
|   options = options || {}; | 
|   | 
|   return function(filepath) { | 
|     var contains = utils._contains(filepath, unixify(filepath), pattern); | 
|     if (contains === true || options.nocase !== true) { | 
|       return contains; | 
|     } | 
|     var lower = filepath.toLowerCase(); | 
|     return utils._contains(lower, unixify(lower), pattern); | 
|   }; | 
| }; | 
|   | 
| /** | 
|  * Returns a function that returns true if the given | 
|  * regex matches the `filename` of a file path. | 
|  * | 
|  * @param {RegExp} `re` Matching regex | 
|  * @return {Function} | 
|  */ | 
|   | 
| utils.matchBasename = function(re) { | 
|   return function(filepath) { | 
|     return re.test(filepath) || re.test(path.basename(filepath)); | 
|   }; | 
| }; | 
|   | 
| /** | 
|  * Returns the given value unchanced. | 
|  * @return {any} | 
|  */ | 
|   | 
| utils.identity = function(val) { | 
|   return val; | 
| }; | 
|   | 
| /** | 
|  * Determines the filepath to return based on the provided options. | 
|  * @return {any} | 
|  */ | 
|   | 
| utils.value = function(str, unixify, options) { | 
|   if (options && options.unixify === false) { | 
|     return str; | 
|   } | 
|   if (options && typeof options.unixify === 'function') { | 
|     return options.unixify(str); | 
|   } | 
|   return unixify(str); | 
| }; | 
|   | 
| /** | 
|  * Returns a function that normalizes slashes in a string to forward | 
|  * slashes, strips `./` from beginning of paths, and optionally unescapes | 
|  * special characters. | 
|  * @return {Function} | 
|  */ | 
|   | 
| utils.unixify = function(options) { | 
|   var opts = options || {}; | 
|   return function(filepath) { | 
|     if (opts.stripPrefix !== false) { | 
|       filepath = utils.stripPrefix(filepath); | 
|     } | 
|     if (opts.unescape === true) { | 
|       filepath = utils.unescape(filepath); | 
|     } | 
|     if (opts.unixify === true || utils.isWindows()) { | 
|       filepath = utils.toPosixPath(filepath); | 
|     } | 
|     return filepath; | 
|   }; | 
| }; |