| /* | 
|     MIT License http://www.opensource.org/licenses/mit-license.php | 
|     Author Tobias Koppers @sokra | 
| */ | 
| "use strict"; | 
|   | 
| const globToRegExp = require("./globToRegExp").globToRegExp; | 
|   | 
| function parseType(type) { | 
|     const items = type.split("+"); | 
|     const t = items.shift(); | 
|     return { | 
|         type: t === "*" ? null : t, | 
|         features: items | 
|     }; | 
| } | 
|   | 
| function isTypeMatched(baseType, testedType) { | 
|     if (typeof baseType === "string") baseType = parseType(baseType); | 
|     if (typeof testedType === "string") testedType = parseType(testedType); | 
|     if (testedType.type && testedType.type !== baseType.type) return false; | 
|     return testedType.features.every(requiredFeature => { | 
|         return baseType.features.indexOf(requiredFeature) >= 0; | 
|     }); | 
| } | 
|   | 
| function isResourceTypeMatched(baseType, testedType) { | 
|     baseType = baseType.split("/"); | 
|     testedType = testedType.split("/"); | 
|     if (baseType.length !== testedType.length) return false; | 
|     for (let i = 0; i < baseType.length; i++) { | 
|         if (!isTypeMatched(baseType[i], testedType[i])) return false; | 
|     } | 
|     return true; | 
| } | 
|   | 
| function isResourceTypeSupported(context, type) { | 
|     return ( | 
|         context.supportedResourceTypes && | 
|         context.supportedResourceTypes.some(supportedType => { | 
|             return isResourceTypeMatched(supportedType, type); | 
|         }) | 
|     ); | 
| } | 
|   | 
| function isEnvironment(context, env) { | 
|     return ( | 
|         context.environments && | 
|         context.environments.every(environment => { | 
|             return isTypeMatched(environment, env); | 
|         }) | 
|     ); | 
| } | 
|   | 
| const globCache = {}; | 
|   | 
| function getGlobRegExp(glob) { | 
|     const regExp = globCache[glob] || (globCache[glob] = globToRegExp(glob)); | 
|     return regExp; | 
| } | 
|   | 
| function matchGlob(glob, relativePath) { | 
|     const regExp = getGlobRegExp(glob); | 
|     return regExp.exec(relativePath); | 
| } | 
|   | 
| function isGlobMatched(glob, relativePath) { | 
|     return !!matchGlob(glob, relativePath); | 
| } | 
|   | 
| function isConditionMatched(context, condition) { | 
|     const items = condition.split("|"); | 
|     return items.some(function testFn(item) { | 
|         item = item.trim(); | 
|         const inverted = /^!/.test(item); | 
|         if (inverted) return !testFn(item.substr(1)); | 
|         if (/^[a-z]+:/.test(item)) { | 
|             // match named condition | 
|             const match = /^([a-z]+):\s*/.exec(item); | 
|             const value = item.substr(match[0].length); | 
|             const name = match[1]; | 
|             switch (name) { | 
|                 case "referrer": | 
|                     return isGlobMatched(value, context.referrer); | 
|                 default: | 
|                     return false; | 
|             } | 
|         } else if (item.indexOf("/") >= 0) { | 
|             // match supported type | 
|             return isResourceTypeSupported(context, item); | 
|         } else { | 
|             // match environment | 
|             return isEnvironment(context, item); | 
|         } | 
|     }); | 
| } | 
|   | 
| function isKeyMatched(context, key) { | 
|     for (;;) { | 
|         const match = /^\[([^\]]+)\]\s*/.exec(key); | 
|         if (!match) return key; | 
|         key = key.substr(match[0].length); | 
|         const condition = match[1]; | 
|         if (!isConditionMatched(context, condition)) { | 
|             return false; | 
|         } | 
|     } | 
| } | 
|   | 
| function getField(context, configuration, field) { | 
|     let value; | 
|     Object.keys(configuration).forEach(key => { | 
|         const pureKey = isKeyMatched(context, key); | 
|         if (pureKey === field) { | 
|             value = configuration[key]; | 
|         } | 
|     }); | 
|     return value; | 
| } | 
|   | 
| function getMain(context, configuration) { | 
|     return getField(context, configuration, "main"); | 
| } | 
|   | 
| function getExtensions(context, configuration) { | 
|     return getField(context, configuration, "extensions"); | 
| } | 
|   | 
| function matchModule(context, configuration, request) { | 
|     const modulesField = getField(context, configuration, "modules"); | 
|     if (!modulesField) return request; | 
|     let newRequest = request; | 
|     const keys = Object.keys(modulesField); | 
|     let iteration = 0; | 
|     let match; | 
|     let index; | 
|     for (let i = 0; i < keys.length; i++) { | 
|         const key = keys[i]; | 
|         const pureKey = isKeyMatched(context, key); | 
|         match = matchGlob(pureKey, newRequest); | 
|         if (match) { | 
|             const value = modulesField[key]; | 
|             if (typeof value !== "string") { | 
|                 return value; | 
|             } else if (/^\(.+\)$/.test(pureKey)) { | 
|                 newRequest = newRequest.replace(getGlobRegExp(pureKey), value); | 
|             } else { | 
|                 index = 1; | 
|                 newRequest = value.replace(/(\/?\*)?\*/g, replaceMatcher); | 
|             } | 
|             i = -1; | 
|             if (iteration++ > keys.length) { | 
|                 throw new Error("Request '" + request + "' matches recursively"); | 
|             } | 
|         } | 
|     } | 
|     return newRequest; | 
|   | 
|     function replaceMatcher(find) { | 
|         switch (find) { | 
|             case "/**": { | 
|                 const m = match[index++]; | 
|                 return m ? "/" + m : ""; | 
|             } | 
|             case "**": | 
|             case "*": | 
|                 return match[index++]; | 
|         } | 
|     } | 
| } | 
|   | 
| function matchType(context, configuration, relativePath) { | 
|     const typesField = getField(context, configuration, "types"); | 
|     if (!typesField) return undefined; | 
|     let type; | 
|     Object.keys(typesField).forEach(key => { | 
|         const pureKey = isKeyMatched(context, key); | 
|         if (isGlobMatched(pureKey, relativePath)) { | 
|             const value = typesField[key]; | 
|             if (!type && /\/\*$/.test(value)) | 
|                 throw new Error( | 
|                     "value ('" + | 
|                         value + | 
|                         "') of key '" + | 
|                         key + | 
|                         "' contains '*', but there is no previous value defined" | 
|                 ); | 
|             type = value.replace(/\/\*$/, "/" + type); | 
|         } | 
|     }); | 
|     return type; | 
| } | 
|   | 
| exports.parseType = parseType; | 
| exports.isTypeMatched = isTypeMatched; | 
| exports.isResourceTypeSupported = isResourceTypeSupported; | 
| exports.isEnvironment = isEnvironment; | 
| exports.isGlobMatched = isGlobMatched; | 
| exports.isConditionMatched = isConditionMatched; | 
| exports.isKeyMatched = isKeyMatched; | 
| exports.getField = getField; | 
| exports.getMain = getMain; | 
| exports.getExtensions = getExtensions; | 
| exports.matchModule = matchModule; | 
| exports.matchType = matchType; |