| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | <rules>: <rule> | 
					
						
							|  |  |  | <rules>: [<rule>] | 
					
						
							|  |  |  | <rule>: { | 
					
						
							|  |  |  | 	resource: { | 
					
						
							|  |  |  | 		test: <condition>, | 
					
						
							|  |  |  | 		include: <condition>, | 
					
						
							|  |  |  | 		exclude: <condition>, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	resource: <condition>, -> resource.test | 
					
						
							|  |  |  | 	test: <condition>, -> resource.test | 
					
						
							|  |  |  | 	include: <condition>, -> resource.include | 
					
						
							|  |  |  | 	exclude: <condition>, -> resource.exclude | 
					
						
							| 
									
										
										
										
											2016-11-23 00:58:24 +08:00
										 |  |  | 	resourceQuery: <condition>, | 
					
						
							| 
									
										
										
										
											2017-01-18 05:26:38 +08:00
										 |  |  | 	compiler: <condition>, | 
					
						
							| 
									
										
										
										
											2016-11-23 00:58:24 +08:00
										 |  |  | 	issuer: <condition>, | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 	use: "loader", -> use[0].loader | 
					
						
							|  |  |  | 	loader: <>, -> use[0].loader | 
					
						
							|  |  |  | 	loaders: <>, -> use | 
					
						
							|  |  |  | 	options: {}, -> use[0].options, | 
					
						
							|  |  |  | 	query: {}, -> options | 
					
						
							|  |  |  | 	parser: {}, | 
					
						
							|  |  |  | 	use: [ | 
					
						
							|  |  |  | 		"loader" -> use[x].loader | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 	use: [ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			loader: "loader", | 
					
						
							|  |  |  | 			options: {} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 	rules: [ | 
					
						
							|  |  |  | 		<rule> | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 	oneOf: [ | 
					
						
							|  |  |  | 		<rule> | 
					
						
							|  |  |  | 	] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <condition>: /regExp/ | 
					
						
							|  |  |  | <condition>: function(arg) {} | 
					
						
							|  |  |  | <condition>: "starting" | 
					
						
							|  |  |  | <condition>: [<condition>] // or
 | 
					
						
							|  |  |  | <condition>: { and: [<condition>] } | 
					
						
							|  |  |  | <condition>: { or: [<condition>] } | 
					
						
							|  |  |  | <condition>: { not: [<condition>] } | 
					
						
							| 
									
										
										
										
											2016-10-23 02:20:22 +08:00
										 |  |  | <condition>: { test: <condition>, include: <condition>, exclude: <condition> } | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | normalized: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	resource: function(), | 
					
						
							| 
									
										
										
										
											2016-11-23 00:58:24 +08:00
										 |  |  | 	resourceQuery: function(), | 
					
						
							| 
									
										
										
										
											2017-01-18 05:26:38 +08:00
										 |  |  | 	compiler: function(), | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 	issuer: function(), | 
					
						
							|  |  |  | 	use: [ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			loader: string, | 
					
						
							|  |  |  | 			options: string, | 
					
						
							|  |  |  | 			<any>: <any> | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 	rules: [<rule>], | 
					
						
							|  |  |  | 	oneOf: [<rule>], | 
					
						
							|  |  |  | 	<any>: <any>, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | "use strict"; | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-08 18:32:05 +08:00
										 |  |  | const notMatcher = matcher => { | 
					
						
							|  |  |  | 	return function(str) { | 
					
						
							|  |  |  | 		return !matcher(str); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const orMatcher = items => { | 
					
						
							|  |  |  | 	return function(str) { | 
					
						
							|  |  |  | 		for(let i = 0; i < items.length; i++) { | 
					
						
							|  |  |  | 			if(items[i](str)) | 
					
						
							|  |  |  | 				return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const andMatcher = items => { | 
					
						
							|  |  |  | 	return function(str) { | 
					
						
							|  |  |  | 		for(let i = 0; i < items.length; i++) { | 
					
						
							|  |  |  | 			if(!items[i](str)) | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | module.exports = class RuleSet { | 
					
						
							|  |  |  | 	constructor(rules) { | 
					
						
							|  |  |  | 		this.references = Object.create(null); | 
					
						
							|  |  |  | 		this.rules = RuleSet.normalizeRules(rules, this.references, "ref-"); | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 	static normalizeRules(rules, refs, ident) { | 
					
						
							|  |  |  | 		if(Array.isArray(rules)) { | 
					
						
							|  |  |  | 			return rules.map((rule, idx) => { | 
					
						
							|  |  |  | 				return RuleSet.normalizeRule(rule, refs, `${ident}-${idx}`); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} else if(rules) { | 
					
						
							|  |  |  | 			return [RuleSet.normalizeRule(rules, refs, ident)]; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			return []; | 
					
						
							| 
									
										
										
										
											2016-10-23 02:20:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 	static normalizeRule(rule, refs, ident) { | 
					
						
							|  |  |  | 		if(typeof rule === "string") | 
					
						
							|  |  |  | 			return { | 
					
						
							|  |  |  | 				use: [{ | 
					
						
							|  |  |  | 					loader: rule | 
					
						
							|  |  |  | 				}] | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 		if(!rule) | 
					
						
							|  |  |  | 			throw new Error("Unexcepted null when object was expected as rule"); | 
					
						
							|  |  |  | 		if(typeof rule !== "object") | 
					
						
							|  |  |  | 			throw new Error("Unexcepted " + typeof rule + " when object was expected as rule (" + rule + ")"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-16 17:27:44 +08:00
										 |  |  | 		const newRule = {}; | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		let useSource; | 
					
						
							|  |  |  | 		let resourceSource; | 
					
						
							|  |  |  | 		let condition; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-08 18:32:05 +08:00
										 |  |  | 		const checkUseSource = newSource => { | 
					
						
							|  |  |  | 			if(useSource && useSource !== newSource) | 
					
						
							|  |  |  | 				throw new Error(RuleSet.buildErrorMessage(rule, new Error("Rule can only have one result source (provided " + newSource + " and " + useSource + ")"))); | 
					
						
							|  |  |  | 			useSource = newSource; | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const checkResourceSource = newSource => { | 
					
						
							|  |  |  | 			if(resourceSource && resourceSource !== newSource) | 
					
						
							|  |  |  | 				throw new Error(RuleSet.buildErrorMessage(rule, new Error("Rule can only have one resource source (provided " + newSource + " and " + resourceSource + ")"))); | 
					
						
							|  |  |  | 			resourceSource = newSource; | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.test || rule.include || rule.exclude) { | 
					
						
							|  |  |  | 			checkResourceSource("test + include + exclude"); | 
					
						
							|  |  |  | 			condition = { | 
					
						
							|  |  |  | 				test: rule.test, | 
					
						
							|  |  |  | 				include: rule.include, | 
					
						
							|  |  |  | 				exclude: rule.exclude | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				newRule.resource = RuleSet.normalizeCondition(condition); | 
					
						
							|  |  |  | 			} catch(error) { | 
					
						
							|  |  |  | 				throw new Error(RuleSet.buildErrorMessage(condition, error)); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-10-23 07:46:15 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.resource) { | 
					
						
							|  |  |  | 			checkResourceSource("resource"); | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				newRule.resource = RuleSet.normalizeCondition(rule.resource); | 
					
						
							|  |  |  | 			} catch(error) { | 
					
						
							|  |  |  | 				throw new Error(RuleSet.buildErrorMessage(rule.resource, error)); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-11-23 00:58:24 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.resourceQuery) { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				newRule.resourceQuery = RuleSet.normalizeCondition(rule.resourceQuery); | 
					
						
							|  |  |  | 			} catch(error) { | 
					
						
							|  |  |  | 				throw new Error(RuleSet.buildErrorMessage(rule.resourceQuery, error)); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-10-23 07:46:15 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 05:26:38 +08:00
										 |  |  | 		if(rule.compiler) { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				newRule.compiler = RuleSet.normalizeCondition(rule.compiler); | 
					
						
							|  |  |  | 			} catch(error) { | 
					
						
							|  |  |  | 				throw new Error(RuleSet.buildErrorMessage(rule.compiler, error)); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.issuer) { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				newRule.issuer = RuleSet.normalizeCondition(rule.issuer); | 
					
						
							|  |  |  | 			} catch(error) { | 
					
						
							|  |  |  | 				throw new Error(RuleSet.buildErrorMessage(rule.issuer, error)); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.loader && rule.loaders) | 
					
						
							|  |  |  | 			throw new Error(RuleSet.buildErrorMessage(rule, new Error("Provided loader and loaders for rule (use only one of them)"))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const loader = rule.loaders || rule.loader; | 
					
						
							|  |  |  | 		if(typeof loader === "string" && !rule.options && !rule.query) { | 
					
						
							|  |  |  | 			checkUseSource("loader"); | 
					
						
							|  |  |  | 			newRule.use = RuleSet.normalizeUse(loader.split("!"), ident); | 
					
						
							|  |  |  | 		} else if(typeof loader === "string" && (rule.options || rule.query)) { | 
					
						
							|  |  |  | 			checkUseSource("loader + options/query"); | 
					
						
							|  |  |  | 			newRule.use = RuleSet.normalizeUse({ | 
					
						
							|  |  |  | 				loader: loader, | 
					
						
							|  |  |  | 				options: rule.options, | 
					
						
							|  |  |  | 				query: rule.query | 
					
						
							|  |  |  | 			}, ident); | 
					
						
							|  |  |  | 		} else if(loader && (rule.options || rule.query)) { | 
					
						
							|  |  |  | 			throw new Error(RuleSet.buildErrorMessage(rule, new Error("options/query cannot be used with loaders (use options for each array item)"))); | 
					
						
							|  |  |  | 		} else if(loader) { | 
					
						
							|  |  |  | 			checkUseSource("loaders"); | 
					
						
							|  |  |  | 			newRule.use = RuleSet.normalizeUse(loader, ident); | 
					
						
							|  |  |  | 		} else if(rule.options || rule.query) { | 
					
						
							|  |  |  | 			throw new Error(RuleSet.buildErrorMessage(rule, new Error("options/query provided without loader (use loader + options)"))); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.use) { | 
					
						
							|  |  |  | 			checkUseSource("use"); | 
					
						
							|  |  |  | 			newRule.use = RuleSet.normalizeUse(rule.use, ident); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.rules) | 
					
						
							|  |  |  | 			newRule.rules = RuleSet.normalizeRules(rule.rules, refs, `${ident}-rules`); | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.oneOf) | 
					
						
							|  |  |  | 			newRule.oneOf = RuleSet.normalizeRules(rule.oneOf, refs, `${ident}-oneOf`); | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		const keys = Object.keys(rule).filter((key) => { | 
					
						
							| 
									
										
										
										
											2018-01-12 00:58:39 +08:00
										 |  |  | 			return !["resource", "resourceQuery", "compiler", "test", "include", "exclude", "issuer", "loader", "options", "query", "loaders", "use", "rules", "oneOf"].includes(key); | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-01-22 20:52:43 +08:00
										 |  |  | 		for(const key of keys) { | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 			newRule[key] = rule[key]; | 
					
						
							| 
									
										
										
										
											2018-01-22 20:52:43 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(Array.isArray(newRule.use)) { | 
					
						
							| 
									
										
										
										
											2018-01-22 20:52:43 +08:00
										 |  |  | 			for(const item of newRule.use) { | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 				if(item.ident) { | 
					
						
							|  |  |  | 					refs[item.ident] = item.options; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-01-22 20:52:43 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		return newRule; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-23 07:46:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 	static buildErrorMessage(condition, error) { | 
					
						
							|  |  |  | 		const conditionAsText = JSON.stringify(condition, (key, value) => { | 
					
						
							|  |  |  | 			return value === undefined ? "undefined" : value; | 
					
						
							|  |  |  | 		}, 2); | 
					
						
							|  |  |  | 		return error.message + " in " + conditionAsText; | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 	static normalizeUse(use, ident) { | 
					
						
							| 
									
										
										
										
											2017-11-09 02:34:05 +08:00
										 |  |  | 		if(typeof use === "function") { | 
					
						
							|  |  |  | 			return data => RuleSet.normalizeUse(use(data), ident); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(Array.isArray(use)) { | 
					
						
							|  |  |  | 			return use | 
					
						
							|  |  |  | 				.map((item, idx) => RuleSet.normalizeUse(item, `${ident}-${idx}`)) | 
					
						
							|  |  |  | 				.reduce((arr, items) => arr.concat(items), []); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return [RuleSet.normalizeUseItem(use, ident)]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	static normalizeUseItemString(useItemString) { | 
					
						
							|  |  |  | 		const idx = useItemString.indexOf("?"); | 
					
						
							|  |  |  | 		if(idx >= 0) { | 
					
						
							|  |  |  | 			return { | 
					
						
							|  |  |  | 				loader: useItemString.substr(0, idx), | 
					
						
							|  |  |  | 				options: useItemString.substr(idx + 1) | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-23 02:14:54 +08:00
										 |  |  | 		return { | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 			loader: useItemString | 
					
						
							| 
									
										
										
										
											2016-12-23 02:14:54 +08:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 	static normalizeUseItem(item, ident) { | 
					
						
							|  |  |  | 		if(typeof item === "string") { | 
					
						
							|  |  |  | 			return RuleSet.normalizeUseItemString(item); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-16 17:27:44 +08:00
										 |  |  | 		const newItem = {}; | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(item.options && item.query) | 
					
						
							|  |  |  | 			throw new Error("Provided options and query in use"); | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(!item.loader) | 
					
						
							|  |  |  | 			throw new Error("No loader specified"); | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		newItem.options = item.options || item.query; | 
					
						
							| 
									
										
										
										
											2017-01-18 05:26:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(typeof newItem.options === "object" && newItem.options) { | 
					
						
							|  |  |  | 			if(newItem.options.ident) | 
					
						
							|  |  |  | 				newItem.ident = newItem.options.ident; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				newItem.ident = ident; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		const keys = Object.keys(item).filter(function(key) { | 
					
						
							| 
									
										
										
										
											2018-01-12 00:58:39 +08:00
										 |  |  | 			return !["options", "query"].includes(key); | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-22 20:52:43 +08:00
										 |  |  | 		for(const key of keys) { | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 			newItem[key] = item[key]; | 
					
						
							| 
									
										
										
										
											2018-01-22 20:52:43 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		return newItem; | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	static normalizeCondition(condition) { | 
					
						
							|  |  |  | 		if(!condition) | 
					
						
							|  |  |  | 			throw new Error("Expected condition but got falsy value"); | 
					
						
							|  |  |  | 		if(typeof condition === "string") { | 
					
						
							| 
									
										
										
										
											2017-02-16 18:37:26 +08:00
										 |  |  | 			return str => str.indexOf(condition) === 0; | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if(typeof condition === "function") { | 
					
						
							|  |  |  | 			return condition; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if(condition instanceof RegExp) { | 
					
						
							|  |  |  | 			return condition.test.bind(condition); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if(Array.isArray(condition)) { | 
					
						
							|  |  |  | 			const items = condition.map(c => RuleSet.normalizeCondition(c)); | 
					
						
							|  |  |  | 			return orMatcher(items); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if(typeof condition !== "object") | 
					
						
							|  |  |  | 			throw Error("Unexcepted " + typeof condition + " when condition was expected (" + condition + ")"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-16 17:27:44 +08:00
										 |  |  | 		const matchers = []; | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		Object.keys(condition).forEach(key => { | 
					
						
							|  |  |  | 			const value = condition[key]; | 
					
						
							|  |  |  | 			switch(key) { | 
					
						
							|  |  |  | 				case "or": | 
					
						
							|  |  |  | 				case "include": | 
					
						
							|  |  |  | 				case "test": | 
					
						
							|  |  |  | 					if(value) | 
					
						
							|  |  |  | 						matchers.push(RuleSet.normalizeCondition(value)); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case "and": | 
					
						
							|  |  |  | 					if(value) { | 
					
						
							|  |  |  | 						const items = value.map(c => RuleSet.normalizeCondition(c)); | 
					
						
							|  |  |  | 						matchers.push(andMatcher(items)); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case "not": | 
					
						
							|  |  |  | 				case "exclude": | 
					
						
							|  |  |  | 					if(value) { | 
					
						
							|  |  |  | 						const matcher = RuleSet.normalizeCondition(value); | 
					
						
							|  |  |  | 						matchers.push(notMatcher(matcher)); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					throw new Error("Unexcepted property " + key + " in condition"); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		if(matchers.length === 0) | 
					
						
							|  |  |  | 			throw new Error("Excepted condition but got " + condition); | 
					
						
							|  |  |  | 		if(matchers.length === 1) | 
					
						
							|  |  |  | 			return matchers[0]; | 
					
						
							|  |  |  | 		return andMatcher(matchers); | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	exec(data) { | 
					
						
							|  |  |  | 		const result = []; | 
					
						
							|  |  |  | 		this._run(data, { | 
					
						
							|  |  |  | 			rules: this.rules | 
					
						
							|  |  |  | 		}, result); | 
					
						
							|  |  |  | 		return result; | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_run(data, rule, result) { | 
					
						
							|  |  |  | 		// test conditions
 | 
					
						
							|  |  |  | 		if(rule.resource && !data.resource) | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		if(rule.resourceQuery && !data.resourceQuery) | 
					
						
							|  |  |  | 			return false; | 
					
						
							| 
									
										
										
										
											2017-01-18 05:26:38 +08:00
										 |  |  | 		if(rule.compiler && !data.compiler) | 
					
						
							|  |  |  | 			return false; | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		if(rule.issuer && !data.issuer) | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		if(rule.resource && !rule.resource(data.resource)) | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		if(data.issuer && rule.issuer && !rule.issuer(data.issuer)) | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		if(data.resourceQuery && rule.resourceQuery && !rule.resourceQuery(data.resourceQuery)) | 
					
						
							|  |  |  | 			return false; | 
					
						
							| 
									
										
										
										
											2017-01-18 05:26:38 +08:00
										 |  |  | 		if(data.compiler && rule.compiler && !rule.compiler(data.compiler)) | 
					
						
							|  |  |  | 			return false; | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// apply
 | 
					
						
							|  |  |  | 		const keys = Object.keys(rule).filter((key) => { | 
					
						
							| 
									
										
										
										
											2018-01-12 00:58:39 +08:00
										 |  |  | 			return !["resource", "resourceQuery", "compiler", "issuer", "rules", "oneOf", "use", "enforce"].includes(key); | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-01-22 20:52:43 +08:00
										 |  |  | 		for(const key of keys) { | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 			result.push({ | 
					
						
							|  |  |  | 				type: key, | 
					
						
							|  |  |  | 				value: rule[key] | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-01-22 20:52:43 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if(rule.use) { | 
					
						
							| 
									
										
										
										
											2017-11-09 02:34:05 +08:00
										 |  |  | 			const process = use => { | 
					
						
							|  |  |  | 				if(typeof use === "function") { | 
					
						
							|  |  |  | 					process(use(data)); | 
					
						
							|  |  |  | 				} else if(Array.isArray(use)) { | 
					
						
							|  |  |  | 					use.forEach(process); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					result.push({ | 
					
						
							|  |  |  | 						type: "use", | 
					
						
							|  |  |  | 						value: use, | 
					
						
							|  |  |  | 						enforce: rule.enforce | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			process(rule.use); | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(rule.rules) { | 
					
						
							| 
									
										
										
										
											2017-02-16 18:37:26 +08:00
										 |  |  | 			for(let i = 0; i < rule.rules.length; i++) { | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 				this._run(data, rule.rules[i], result); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(rule.oneOf) { | 
					
						
							| 
									
										
										
										
											2017-02-16 18:37:26 +08:00
										 |  |  | 			for(let i = 0; i < rule.oneOf.length; i++) { | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 				if(this._run(data, rule.oneOf[i], result)) | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-02-15 14:55:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	findOptionsByIdent(ident) { | 
					
						
							|  |  |  | 		const options = this.references[ident]; | 
					
						
							|  |  |  | 		if(!options) throw new Error("Can't find options with ident '" + ident + "'"); | 
					
						
							|  |  |  | 		return options; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-14 18:04:42 +08:00
										 |  |  | }; |