310 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			310 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 'use strict'; | ||
|  | 
 | ||
|  | var utils = module.exports; | ||
|  | var path = require('path'); | ||
|  | 
 | ||
|  | /** | ||
|  |  * Module dependencies | ||
|  |  */ | ||
|  | 
 | ||
|  | 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 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 === '\\' || process.platform === 'win32'; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * 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.apply(this, arguments); | ||
|  |     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 (utils.typeOf(options) !== 'object') { | ||
|  |     return pattern; | ||
|  |   } | ||
|  |   var val = pattern; | ||
|  |   var keys = Object.keys(options); | ||
|  |   for (var i = 0; i < keys.length; i++) { | ||
|  |     var key = keys[i]; | ||
|  |     val += ';' + key + '=' + String(options[key]); | ||
|  |   } | ||
|  |   return val; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * 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.isObject = function(val) { | ||
|  |   return utils.typeOf(val) === 'object'; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Returns true if the given `str` has special characters | ||
|  |  */ | ||
|  | 
 | ||
|  | utils.hasSpecialChars = function(str) { | ||
|  |   return /(?:(?:(^|\/)[!.])|[*?+()|\[\]{}]|[+@]\()/.test(str); | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Escape regex characters in the given string | ||
|  |  */ | ||
|  | 
 | ||
|  | utils.escapeRegex = function(str) { | ||
|  |   return str.replace(/[-[\]{}()^$|*+?.\\\/\s]/g, '\\$&'); | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * 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 prefix from a filepath | ||
|  |  * @param {String} `fp` | ||
|  |  * @return {String} | ||
|  |  */ | ||
|  | 
 | ||
|  | utils.stripPrefix = function(str) { | ||
|  |   if (str.charAt(0) !== '.') { | ||
|  |     return str; | ||
|  |   } | ||
|  |   var ch = str.charAt(1); | ||
|  |   if (utils.isSlash(ch)) { | ||
|  |     return str.slice(2); | ||
|  |   } | ||
|  |   return 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(path.basename(filepath)); | ||
|  |   }; | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * 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; | ||
|  |   } | ||
|  |   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) { | ||
|  |   options = options || {}; | ||
|  |   return function(filepath) { | ||
|  |     if (utils.isWindows() || options.unixify === true) { | ||
|  |       filepath = utils.toPosixPath(filepath); | ||
|  |     } | ||
|  |     if (options.stripPrefix !== false) { | ||
|  |       filepath = utils.stripPrefix(filepath); | ||
|  |     } | ||
|  |     if (options.unescape === true) { | ||
|  |       filepath = utils.unescape(filepath); | ||
|  |     } | ||
|  |     return filepath; | ||
|  |   }; | ||
|  | }; |