You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					392 lines
				
				9.0 KiB
			
		
		
			
		
	
	
					392 lines
				
				9.0 KiB
			| 
											3 years ago
										 | 'use strict'; | ||
|  | 
 | ||
|  | const utils = require('./utils'); | ||
|  | const { | ||
|  |   CHAR_ASTERISK,             /* * */ | ||
|  |   CHAR_AT,                   /* @ */ | ||
|  |   CHAR_BACKWARD_SLASH,       /* \ */ | ||
|  |   CHAR_COMMA,                /* , */ | ||
|  |   CHAR_DOT,                  /* . */ | ||
|  |   CHAR_EXCLAMATION_MARK,     /* ! */ | ||
|  |   CHAR_FORWARD_SLASH,        /* / */ | ||
|  |   CHAR_LEFT_CURLY_BRACE,     /* { */ | ||
|  |   CHAR_LEFT_PARENTHESES,     /* ( */ | ||
|  |   CHAR_LEFT_SQUARE_BRACKET,  /* [ */ | ||
|  |   CHAR_PLUS,                 /* + */ | ||
|  |   CHAR_QUESTION_MARK,        /* ? */ | ||
|  |   CHAR_RIGHT_CURLY_BRACE,    /* } */ | ||
|  |   CHAR_RIGHT_PARENTHESES,    /* ) */ | ||
|  |   CHAR_RIGHT_SQUARE_BRACKET  /* ] */ | ||
|  | } = require('./constants'); | ||
|  | 
 | ||
|  | const isPathSeparator = code => { | ||
|  |   return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; | ||
|  | }; | ||
|  | 
 | ||
|  | const depth = token => { | ||
|  |   if (token.isPrefix !== true) { | ||
|  |     token.depth = token.isGlobstar ? Infinity : 1; | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Quickly scans a glob pattern and returns an object with a handful of | ||
|  |  * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), | ||
|  |  * `glob` (the actual pattern), `negated` (true if the path starts with `!` but not | ||
|  |  * with `!(`) and `negatedExtglob` (true if the path starts with `!(`). | ||
|  |  * | ||
|  |  * ```js
 | ||
|  |  * const pm = require('picomatch'); | ||
|  |  * console.log(pm.scan('foo/bar/*.js')); | ||
|  |  * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } | ||
|  |  * ```
 | ||
|  |  * @param {String} `str` | ||
|  |  * @param {Object} `options` | ||
|  |  * @return {Object} Returns an object with tokens and regex source string. | ||
|  |  * @api public | ||
|  |  */ | ||
|  | 
 | ||
|  | const scan = (input, options) => { | ||
|  |   const opts = options || {}; | ||
|  | 
 | ||
|  |   const length = input.length - 1; | ||
|  |   const scanToEnd = opts.parts === true || opts.scanToEnd === true; | ||
|  |   const slashes = []; | ||
|  |   const tokens = []; | ||
|  |   const parts = []; | ||
|  | 
 | ||
|  |   let str = input; | ||
|  |   let index = -1; | ||
|  |   let start = 0; | ||
|  |   let lastIndex = 0; | ||
|  |   let isBrace = false; | ||
|  |   let isBracket = false; | ||
|  |   let isGlob = false; | ||
|  |   let isExtglob = false; | ||
|  |   let isGlobstar = false; | ||
|  |   let braceEscaped = false; | ||
|  |   let backslashes = false; | ||
|  |   let negated = false; | ||
|  |   let negatedExtglob = false; | ||
|  |   let finished = false; | ||
|  |   let braces = 0; | ||
|  |   let prev; | ||
|  |   let code; | ||
|  |   let token = { value: '', depth: 0, isGlob: false }; | ||
|  | 
 | ||
|  |   const eos = () => index >= length; | ||
|  |   const peek = () => str.charCodeAt(index + 1); | ||
|  |   const advance = () => { | ||
|  |     prev = code; | ||
|  |     return str.charCodeAt(++index); | ||
|  |   }; | ||
|  | 
 | ||
|  |   while (index < length) { | ||
|  |     code = advance(); | ||
|  |     let next; | ||
|  | 
 | ||
|  |     if (code === CHAR_BACKWARD_SLASH) { | ||
|  |       backslashes = token.backslashes = true; | ||
|  |       code = advance(); | ||
|  | 
 | ||
|  |       if (code === CHAR_LEFT_CURLY_BRACE) { | ||
|  |         braceEscaped = true; | ||
|  |       } | ||
|  |       continue; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { | ||
|  |       braces++; | ||
|  | 
 | ||
|  |       while (eos() !== true && (code = advance())) { | ||
|  |         if (code === CHAR_BACKWARD_SLASH) { | ||
|  |           backslashes = token.backslashes = true; | ||
|  |           advance(); | ||
|  |           continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (code === CHAR_LEFT_CURLY_BRACE) { | ||
|  |           braces++; | ||
|  |           continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { | ||
|  |           isBrace = token.isBrace = true; | ||
|  |           isGlob = token.isGlob = true; | ||
|  |           finished = true; | ||
|  | 
 | ||
|  |           if (scanToEnd === true) { | ||
|  |             continue; | ||
|  |           } | ||
|  | 
 | ||
|  |           break; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (braceEscaped !== true && code === CHAR_COMMA) { | ||
|  |           isBrace = token.isBrace = true; | ||
|  |           isGlob = token.isGlob = true; | ||
|  |           finished = true; | ||
|  | 
 | ||
|  |           if (scanToEnd === true) { | ||
|  |             continue; | ||
|  |           } | ||
|  | 
 | ||
|  |           break; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (code === CHAR_RIGHT_CURLY_BRACE) { | ||
|  |           braces--; | ||
|  | 
 | ||
|  |           if (braces === 0) { | ||
|  |             braceEscaped = false; | ||
|  |             isBrace = token.isBrace = true; | ||
|  |             finished = true; | ||
|  |             break; | ||
|  |           } | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       if (scanToEnd === true) { | ||
|  |         continue; | ||
|  |       } | ||
|  | 
 | ||
|  |       break; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (code === CHAR_FORWARD_SLASH) { | ||
|  |       slashes.push(index); | ||
|  |       tokens.push(token); | ||
|  |       token = { value: '', depth: 0, isGlob: false }; | ||
|  | 
 | ||
|  |       if (finished === true) continue; | ||
|  |       if (prev === CHAR_DOT && index === (start + 1)) { | ||
|  |         start += 2; | ||
|  |         continue; | ||
|  |       } | ||
|  | 
 | ||
|  |       lastIndex = index + 1; | ||
|  |       continue; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (opts.noext !== true) { | ||
|  |       const isExtglobChar = code === CHAR_PLUS | ||
|  |         || code === CHAR_AT | ||
|  |         || code === CHAR_ASTERISK | ||
|  |         || code === CHAR_QUESTION_MARK | ||
|  |         || code === CHAR_EXCLAMATION_MARK; | ||
|  | 
 | ||
|  |       if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { | ||
|  |         isGlob = token.isGlob = true; | ||
|  |         isExtglob = token.isExtglob = true; | ||
|  |         finished = true; | ||
|  |         if (code === CHAR_EXCLAMATION_MARK && index === start) { | ||
|  |           negatedExtglob = true; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (scanToEnd === true) { | ||
|  |           while (eos() !== true && (code = advance())) { | ||
|  |             if (code === CHAR_BACKWARD_SLASH) { | ||
|  |               backslashes = token.backslashes = true; | ||
|  |               code = advance(); | ||
|  |               continue; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (code === CHAR_RIGHT_PARENTHESES) { | ||
|  |               isGlob = token.isGlob = true; | ||
|  |               finished = true; | ||
|  |               break; | ||
|  |             } | ||
|  |           } | ||
|  |           continue; | ||
|  |         } | ||
|  |         break; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     if (code === CHAR_ASTERISK) { | ||
|  |       if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; | ||
|  |       isGlob = token.isGlob = true; | ||
|  |       finished = true; | ||
|  | 
 | ||
|  |       if (scanToEnd === true) { | ||
|  |         continue; | ||
|  |       } | ||
|  |       break; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (code === CHAR_QUESTION_MARK) { | ||
|  |       isGlob = token.isGlob = true; | ||
|  |       finished = true; | ||
|  | 
 | ||
|  |       if (scanToEnd === true) { | ||
|  |         continue; | ||
|  |       } | ||
|  |       break; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (code === CHAR_LEFT_SQUARE_BRACKET) { | ||
|  |       while (eos() !== true && (next = advance())) { | ||
|  |         if (next === CHAR_BACKWARD_SLASH) { | ||
|  |           backslashes = token.backslashes = true; | ||
|  |           advance(); | ||
|  |           continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (next === CHAR_RIGHT_SQUARE_BRACKET) { | ||
|  |           isBracket = token.isBracket = true; | ||
|  |           isGlob = token.isGlob = true; | ||
|  |           finished = true; | ||
|  |           break; | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       if (scanToEnd === true) { | ||
|  |         continue; | ||
|  |       } | ||
|  | 
 | ||
|  |       break; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { | ||
|  |       negated = token.negated = true; | ||
|  |       start++; | ||
|  |       continue; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { | ||
|  |       isGlob = token.isGlob = true; | ||
|  | 
 | ||
|  |       if (scanToEnd === true) { | ||
|  |         while (eos() !== true && (code = advance())) { | ||
|  |           if (code === CHAR_LEFT_PARENTHESES) { | ||
|  |             backslashes = token.backslashes = true; | ||
|  |             code = advance(); | ||
|  |             continue; | ||
|  |           } | ||
|  | 
 | ||
|  |           if (code === CHAR_RIGHT_PARENTHESES) { | ||
|  |             finished = true; | ||
|  |             break; | ||
|  |           } | ||
|  |         } | ||
|  |         continue; | ||
|  |       } | ||
|  |       break; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (isGlob === true) { | ||
|  |       finished = true; | ||
|  | 
 | ||
|  |       if (scanToEnd === true) { | ||
|  |         continue; | ||
|  |       } | ||
|  | 
 | ||
|  |       break; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (opts.noext === true) { | ||
|  |     isExtglob = false; | ||
|  |     isGlob = false; | ||
|  |   } | ||
|  | 
 | ||
|  |   let base = str; | ||
|  |   let prefix = ''; | ||
|  |   let glob = ''; | ||
|  | 
 | ||
|  |   if (start > 0) { | ||
|  |     prefix = str.slice(0, start); | ||
|  |     str = str.slice(start); | ||
|  |     lastIndex -= start; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (base && isGlob === true && lastIndex > 0) { | ||
|  |     base = str.slice(0, lastIndex); | ||
|  |     glob = str.slice(lastIndex); | ||
|  |   } else if (isGlob === true) { | ||
|  |     base = ''; | ||
|  |     glob = str; | ||
|  |   } else { | ||
|  |     base = str; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (base && base !== '' && base !== '/' && base !== str) { | ||
|  |     if (isPathSeparator(base.charCodeAt(base.length - 1))) { | ||
|  |       base = base.slice(0, -1); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (opts.unescape === true) { | ||
|  |     if (glob) glob = utils.removeBackslashes(glob); | ||
|  | 
 | ||
|  |     if (base && backslashes === true) { | ||
|  |       base = utils.removeBackslashes(base); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   const state = { | ||
|  |     prefix, | ||
|  |     input, | ||
|  |     start, | ||
|  |     base, | ||
|  |     glob, | ||
|  |     isBrace, | ||
|  |     isBracket, | ||
|  |     isGlob, | ||
|  |     isExtglob, | ||
|  |     isGlobstar, | ||
|  |     negated, | ||
|  |     negatedExtglob | ||
|  |   }; | ||
|  | 
 | ||
|  |   if (opts.tokens === true) { | ||
|  |     state.maxDepth = 0; | ||
|  |     if (!isPathSeparator(code)) { | ||
|  |       tokens.push(token); | ||
|  |     } | ||
|  |     state.tokens = tokens; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (opts.parts === true || opts.tokens === true) { | ||
|  |     let prevIndex; | ||
|  | 
 | ||
|  |     for (let idx = 0; idx < slashes.length; idx++) { | ||
|  |       const n = prevIndex ? prevIndex + 1 : start; | ||
|  |       const i = slashes[idx]; | ||
|  |       const value = input.slice(n, i); | ||
|  |       if (opts.tokens) { | ||
|  |         if (idx === 0 && start !== 0) { | ||
|  |           tokens[idx].isPrefix = true; | ||
|  |           tokens[idx].value = prefix; | ||
|  |         } else { | ||
|  |           tokens[idx].value = value; | ||
|  |         } | ||
|  |         depth(tokens[idx]); | ||
|  |         state.maxDepth += tokens[idx].depth; | ||
|  |       } | ||
|  |       if (idx !== 0 || value !== '') { | ||
|  |         parts.push(value); | ||
|  |       } | ||
|  |       prevIndex = i; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (prevIndex && prevIndex + 1 < input.length) { | ||
|  |       const value = input.slice(prevIndex + 1); | ||
|  |       parts.push(value); | ||
|  | 
 | ||
|  |       if (opts.tokens) { | ||
|  |         tokens[tokens.length - 1].value = value; | ||
|  |         depth(tokens[tokens.length - 1]); | ||
|  |         state.maxDepth += tokens[tokens.length - 1].depth; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     state.slashes = slashes; | ||
|  |     state.parts = parts; | ||
|  |   } | ||
|  | 
 | ||
|  |   return state; | ||
|  | }; | ||
|  | 
 | ||
|  | module.exports = scan; |