| 
									
										
										
										
											2018-01-25 10:13:30 +00:00
										 |  |  | import * as XRegExp from 'xregexp'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 15:27:24 +03:00
										 |  |  | // The `XRegExp` typings are not accurate.
 | 
					
						
							|  |  |  | interface XRegExp extends RegExp { | 
					
						
							|  |  |  |   xregexp: { captureNames?: string[] }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 10:13:30 +00:00
										 |  |  | 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}$`; | 
					
						
							| 
									
										
										
										
											2019-04-24 15:27:24 +03:00
										 |  |  |       this.regex = XRegExp(this.pattern) as XRegExp; | 
					
						
							| 
									
										
										
										
											2018-01-25 10:13:30 +00:00
										 |  |  |     } catch (e) { | 
					
						
							|  |  |  |       throw new Error(`Error in FirebaseGlob: "${glob}" - ${e.message}`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   test(url: string) { | 
					
						
							|  |  |  |     return XRegExp.test(url, this.regex); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   match(url: string) { | 
					
						
							| 
									
										
										
										
											2019-04-24 15:27:24 +03:00
										 |  |  |     const match = XRegExp.exec(url, this.regex) as ReturnType<typeof XRegExp.exec> & { [captured: string]: string }; | 
					
						
							| 
									
										
										
										
											2018-01-25 10:13:30 +00:00
										 |  |  |     if (match) { | 
					
						
							| 
									
										
										
										
											2019-04-24 15:27:24 +03:00
										 |  |  |       const result: { [key: string]: string } = {}; | 
					
						
							|  |  |  |       const names = this.regex.xregexp.captureNames || []; | 
					
						
							|  |  |  |       names.forEach(name => result[name] = (match[name])); | 
					
						
							| 
									
										
										
										
											2018-01-25 10:13:30 +00:00
										 |  |  |       return result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 15:27:24 +03:00
										 |  |  | function replaceModifiedPattern(_: string, modifier: string, pattern: string) { | 
					
						
							| 
									
										
										
										
											2018-01-25 10:13:30 +00:00
										 |  |  |   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 "${_}"`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |