202 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| "use strict";
 | |
| 
 | |
| function globToRegExp(glob) {
 | |
| 	// * [^\\\/]*
 | |
| 	// /**/ /.+/
 | |
| 	// ^* \./.+ (concord special)
 | |
| 	// ? [^\\\/]
 | |
| 	// [!...] [^...]
 | |
| 	// [^...] [^...]
 | |
| 	// / [\\\/]
 | |
| 	// {...,...} (...|...)
 | |
| 	// ?(...|...) (...|...)?
 | |
| 	// +(...|...) (...|...)+
 | |
| 	// *(...|...) (...|...)*
 | |
| 	// @(...|...) (...|...)
 | |
| 	if (/^\(.+\)$/.test(glob)) {
 | |
| 		// allow to pass an RegExp in brackets
 | |
| 		return new RegExp(glob.substr(1, glob.length - 2));
 | |
| 	}
 | |
| 	const tokens = tokenize(glob);
 | |
| 	const process = createRoot();
 | |
| 	const regExpStr = tokens.map(process).join("");
 | |
| 	return new RegExp("^" + regExpStr + "$");
 | |
| }
 | |
| 
 | |
| const SIMPLE_TOKENS = {
 | |
| 	"@(": "one",
 | |
| 	"?(": "zero-one",
 | |
| 	"+(": "one-many",
 | |
| 	"*(": "zero-many",
 | |
| 	"|": "segment-sep",
 | |
| 	"/**/": "any-path-segments",
 | |
| 	"**": "any-path",
 | |
| 	"*": "any-path-segment",
 | |
| 	"?": "any-char",
 | |
| 	"{": "or",
 | |
| 	"/": "path-sep",
 | |
| 	",": "comma",
 | |
| 	")": "closing-segment",
 | |
| 	"}": "closing-or"
 | |
| };
 | |
| 
 | |
| function tokenize(glob) {
 | |
| 	return glob
 | |
| 		.split(
 | |
| 			/([@?+*]\(|\/\*\*\/|\*\*|[?*]|\[[!^]?(?:[^\]\\]|\\.)+\]|\{|,|\/|[|)}])/g
 | |
| 		)
 | |
| 		.map(item => {
 | |
| 			if (!item) return null;
 | |
| 			const t = SIMPLE_TOKENS[item];
 | |
| 			if (t) {
 | |
| 				return {
 | |
| 					type: t
 | |
| 				};
 | |
| 			}
 | |
| 			if (item[0] === "[") {
 | |
| 				if (item[1] === "^" || item[1] === "!") {
 | |
| 					return {
 | |
| 						type: "inverted-char-set",
 | |
| 						value: item.substr(2, item.length - 3)
 | |
| 					};
 | |
| 				} else {
 | |
| 					return {
 | |
| 						type: "char-set",
 | |
| 						value: item.substr(1, item.length - 2)
 | |
| 					};
 | |
| 				}
 | |
| 			}
 | |
| 			return {
 | |
| 				type: "string",
 | |
| 				value: item
 | |
| 			};
 | |
| 		})
 | |
| 		.filter(Boolean)
 | |
| 		.concat({
 | |
| 			type: "end"
 | |
| 		});
 | |
| }
 | |
| 
 | |
| function createRoot() {
 | |
| 	const inOr = [];
 | |
| 	const process = createSeqment();
 | |
| 	let initial = true;
 | |
| 	return function(token) {
 | |
| 		switch (token.type) {
 | |
| 			case "or":
 | |
| 				inOr.push(initial);
 | |
| 				return "(";
 | |
| 			case "comma":
 | |
| 				if (inOr.length) {
 | |
| 					initial = inOr[inOr.length - 1];
 | |
| 					return "|";
 | |
| 				} else {
 | |
| 					return process(
 | |
| 						{
 | |
| 							type: "string",
 | |
| 							value: ","
 | |
| 						},
 | |
| 						initial
 | |
| 					);
 | |
| 				}
 | |
| 			case "closing-or":
 | |
| 				if (inOr.length === 0) throw new Error("Unmatched '}'");
 | |
| 				inOr.pop();
 | |
| 				return ")";
 | |
| 			case "end":
 | |
| 				if (inOr.length) throw new Error("Unmatched '{'");
 | |
| 				return process(token, initial);
 | |
| 			default: {
 | |
| 				const result = process(token, initial);
 | |
| 				initial = false;
 | |
| 				return result;
 | |
| 			}
 | |
| 		}
 | |
| 	};
 | |
| }
 | |
| 
 | |
| function createSeqment() {
 | |
| 	const inSeqment = [];
 | |
| 	const process = createSimple();
 | |
| 	return function(token, initial) {
 | |
| 		switch (token.type) {
 | |
| 			case "one":
 | |
| 			case "one-many":
 | |
| 			case "zero-many":
 | |
| 			case "zero-one":
 | |
| 				inSeqment.push(token.type);
 | |
| 				return "(";
 | |
| 			case "segment-sep":
 | |
| 				if (inSeqment.length) {
 | |
| 					return "|";
 | |
| 				} else {
 | |
| 					return process(
 | |
| 						{
 | |
| 							type: "string",
 | |
| 							value: "|"
 | |
| 						},
 | |
| 						initial
 | |
| 					);
 | |
| 				}
 | |
| 			case "closing-segment": {
 | |
| 				const segment = inSeqment.pop();
 | |
| 				switch (segment) {
 | |
| 					case "one":
 | |
| 						return ")";
 | |
| 					case "one-many":
 | |
| 						return ")+";
 | |
| 					case "zero-many":
 | |
| 						return ")*";
 | |
| 					case "zero-one":
 | |
| 						return ")?";
 | |
| 				}
 | |
| 				throw new Error("Unexcepted segment " + segment);
 | |
| 			}
 | |
| 			case "end":
 | |
| 				if (inSeqment.length > 0) {
 | |
| 					throw new Error("Unmatched segment, missing ')'");
 | |
| 				}
 | |
| 				return process(token, initial);
 | |
| 			default:
 | |
| 				return process(token, initial);
 | |
| 		}
 | |
| 	};
 | |
| }
 | |
| 
 | |
| function createSimple() {
 | |
| 	return function(token, initial) {
 | |
| 		switch (token.type) {
 | |
| 			case "path-sep":
 | |
| 				return "[\\\\/]+";
 | |
| 			case "any-path-segments":
 | |
| 				return "[\\\\/]+(?:(.+)[\\\\/]+)?";
 | |
| 			case "any-path":
 | |
| 				return "(.*)";
 | |
| 			case "any-path-segment":
 | |
| 				if (initial) {
 | |
| 					return "\\.[\\\\/]+(?:.*[\\\\/]+)?([^\\\\/]+)";
 | |
| 				} else {
 | |
| 					return "([^\\\\/]*)";
 | |
| 				}
 | |
| 			case "any-char":
 | |
| 				return "[^\\\\/]";
 | |
| 			case "inverted-char-set":
 | |
| 				return "[^" + token.value + "]";
 | |
| 			case "char-set":
 | |
| 				return "[" + token.value + "]";
 | |
| 			case "string":
 | |
| 				return token.value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
 | |
| 			case "end":
 | |
| 				return "";
 | |
| 			default:
 | |
| 				throw new Error("Unsupported token '" + token.type + "'");
 | |
| 		}
 | |
| 	};
 | |
| }
 | |
| 
 | |
| exports.globToRegExp = globToRegExp;
 | 
