75 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			75 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 
								 | 
							
								import * as XRegExp from 'xregexp';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const dot = /\./g;
							 | 
						||
| 
								 | 
							
								const star = /\*/g;
							 | 
						||
| 
								 | 
							
								const doubleStar = /(^|\/)\*\*($|\/)/g;           // e.g. a/**/b or **/b or a/** but not a**b
							 | 
						||
| 
								 | 
							
								const modifiedPatterns = /(.)\(([^)]+)\)/g;       // e.g. `@(a|b)
							 | 
						||
| 
								 | 
							
								const restParam = /\/:([A-Za-z]+)\*/g;            // e.g. `:rest*`
							 | 
						||
| 
								 | 
							
								const namedParam = /\/:([A-Za-z]+)/g;             // e.g. `:api`
							 | 
						||
| 
								 | 
							
								const possiblyEmptyInitialSegments = /^\.🐷\//g;  // e.g. `**/a` can also match `a`
							 | 
						||
| 
								 | 
							
								const possiblyEmptySegments = /\/\.🐷\//g;        // e.g. `a/**/b` can also match `a/b`
							 | 
						||
| 
								 | 
							
								const willBeStar = /🐷/g;                         // e.g. `a**b` not matched by previous rule
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export class FirebaseGlob {
							 | 
						||
| 
								 | 
							
								  pattern: string;
							 | 
						||
| 
								 | 
							
								  regex: XRegExp;
							 | 
						||
| 
								 | 
							
								  namedParams: { [key: string]: boolean } = {};
							 | 
						||
| 
								 | 
							
								  restParams: { [key: string]: boolean } = {};
							 | 
						||
| 
								 | 
							
								  constructor(glob: string) {
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      const pattern = glob
							 | 
						||
| 
								 | 
							
								          .replace(dot, '\\.')
							 | 
						||
| 
								 | 
							
								          .replace(modifiedPatterns, replaceModifiedPattern)
							 | 
						||
| 
								 | 
							
								          .replace(restParam, (_, param) => {
							 | 
						||
| 
								 | 
							
								            // capture the rest of the string
							 | 
						||
| 
								 | 
							
								            this.restParams[param] = true;
							 | 
						||
| 
								 | 
							
								            return `(?:/(?<${param}>.🐷))?`;
							 | 
						||
| 
								 | 
							
								          })
							 | 
						||
| 
								 | 
							
								          .replace(namedParam, (_, param) => {
							 | 
						||
| 
								 | 
							
								            // capture the named parameter
							 | 
						||
| 
								 | 
							
								            this.namedParams[param] = true;
							 | 
						||
| 
								 | 
							
								            return `/(?<${param}>[^/]+)`;
							 | 
						||
| 
								 | 
							
								          })
							 | 
						||
| 
								 | 
							
								          .replace(doubleStar, '$1.🐷$2')                 // use the pig to avoid replacing ** in next rule
							 | 
						||
| 
								 | 
							
								          .replace(star, '[^/]*')                         // match a single segment
							 | 
						||
| 
								 | 
							
								          .replace(possiblyEmptyInitialSegments, '(?:.*)')// deal with **/ special cases
							 | 
						||
| 
								 | 
							
								          .replace(possiblyEmptySegments, '(?:/|/.*/)')   // deal with /**/ special cases
							 | 
						||
| 
								 | 
							
								          .replace(willBeStar, '*');                      // other ** matches
							 | 
						||
| 
								 | 
							
								      this.pattern = `^${pattern}$`;
							 | 
						||
| 
								 | 
							
								      this.regex = XRegExp(this.pattern);
							 | 
						||
| 
								 | 
							
								    } catch (e) {
							 | 
						||
| 
								 | 
							
								      throw new Error(`Error in FirebaseGlob: "${glob}" - ${e.message}`);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  test(url: string) {
							 | 
						||
| 
								 | 
							
								    return XRegExp.test(url, this.regex);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  match(url: string) {
							 | 
						||
| 
								 | 
							
								    const match = XRegExp.exec(url, this.regex);
							 | 
						||
| 
								 | 
							
								    if (match) {
							 | 
						||
| 
								 | 
							
								      const result = {};
							 | 
						||
| 
								 | 
							
								      const names = (this.regex as any).xregexp.captureNames || [];
							 | 
						||
| 
								 | 
							
								      names.forEach(name => result[name] = match[name]);
							 | 
						||
| 
								 | 
							
								      return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function replaceModifiedPattern(_, modifier, pattern) {
							 | 
						||
| 
								 | 
							
								  switch (modifier) {
							 | 
						||
| 
								 | 
							
								    case '!':
							 | 
						||
| 
								 | 
							
								      throw new Error(`"not" expansions are not supported: "${_}"`);
							 | 
						||
| 
								 | 
							
								    case '?':
							 | 
						||
| 
								 | 
							
								    case '+':
							 | 
						||
| 
								 | 
							
								      return `(${pattern})${modifier}`;
							 | 
						||
| 
								 | 
							
								    case '*':
							 | 
						||
| 
								 | 
							
								      return `(${pattern})🐷`;  // it will become a star
							 | 
						||
| 
								 | 
							
								    case '@':
							 | 
						||
| 
								 | 
							
								      return `(${pattern})`;
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      throw new Error(`unknown expansion type: "${modifier}" in "${_}"`);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |