You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
5.2 KiB

4 years ago
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const globToRegExp = require("./globToRegExp").globToRegExp;
  7. function parseType(type) {
  8. const items = type.split("+");
  9. const t = items.shift();
  10. return {
  11. type: t === "*" ? null : t,
  12. features: items
  13. };
  14. }
  15. function isTypeMatched(baseType, testedType) {
  16. if (typeof baseType === "string") baseType = parseType(baseType);
  17. if (typeof testedType === "string") testedType = parseType(testedType);
  18. if (testedType.type && testedType.type !== baseType.type) return false;
  19. return testedType.features.every(requiredFeature => {
  20. return baseType.features.indexOf(requiredFeature) >= 0;
  21. });
  22. }
  23. function isResourceTypeMatched(baseType, testedType) {
  24. baseType = baseType.split("/");
  25. testedType = testedType.split("/");
  26. if (baseType.length !== testedType.length) return false;
  27. for (let i = 0; i < baseType.length; i++) {
  28. if (!isTypeMatched(baseType[i], testedType[i])) return false;
  29. }
  30. return true;
  31. }
  32. function isResourceTypeSupported(context, type) {
  33. return (
  34. context.supportedResourceTypes &&
  35. context.supportedResourceTypes.some(supportedType => {
  36. return isResourceTypeMatched(supportedType, type);
  37. })
  38. );
  39. }
  40. function isEnvironment(context, env) {
  41. return (
  42. context.environments &&
  43. context.environments.every(environment => {
  44. return isTypeMatched(environment, env);
  45. })
  46. );
  47. }
  48. const globCache = {};
  49. function getGlobRegExp(glob) {
  50. const regExp = globCache[glob] || (globCache[glob] = globToRegExp(glob));
  51. return regExp;
  52. }
  53. function matchGlob(glob, relativePath) {
  54. const regExp = getGlobRegExp(glob);
  55. return regExp.exec(relativePath);
  56. }
  57. function isGlobMatched(glob, relativePath) {
  58. return !!matchGlob(glob, relativePath);
  59. }
  60. function isConditionMatched(context, condition) {
  61. const items = condition.split("|");
  62. return items.some(function testFn(item) {
  63. item = item.trim();
  64. const inverted = /^!/.test(item);
  65. if (inverted) return !testFn(item.substr(1));
  66. if (/^[a-z]+:/.test(item)) {
  67. // match named condition
  68. const match = /^([a-z]+):\s*/.exec(item);
  69. const value = item.substr(match[0].length);
  70. const name = match[1];
  71. switch (name) {
  72. case "referrer":
  73. return isGlobMatched(value, context.referrer);
  74. default:
  75. return false;
  76. }
  77. } else if (item.indexOf("/") >= 0) {
  78. // match supported type
  79. return isResourceTypeSupported(context, item);
  80. } else {
  81. // match environment
  82. return isEnvironment(context, item);
  83. }
  84. });
  85. }
  86. function isKeyMatched(context, key) {
  87. for (;;) {
  88. const match = /^\[([^\]]+)\]\s*/.exec(key);
  89. if (!match) return key;
  90. key = key.substr(match[0].length);
  91. const condition = match[1];
  92. if (!isConditionMatched(context, condition)) {
  93. return false;
  94. }
  95. }
  96. }
  97. function getField(context, configuration, field) {
  98. let value;
  99. Object.keys(configuration).forEach(key => {
  100. const pureKey = isKeyMatched(context, key);
  101. if (pureKey === field) {
  102. value = configuration[key];
  103. }
  104. });
  105. return value;
  106. }
  107. function getMain(context, configuration) {
  108. return getField(context, configuration, "main");
  109. }
  110. function getExtensions(context, configuration) {
  111. return getField(context, configuration, "extensions");
  112. }
  113. function matchModule(context, configuration, request) {
  114. const modulesField = getField(context, configuration, "modules");
  115. if (!modulesField) return request;
  116. let newRequest = request;
  117. const keys = Object.keys(modulesField);
  118. let iteration = 0;
  119. let match;
  120. let index;
  121. for (let i = 0; i < keys.length; i++) {
  122. const key = keys[i];
  123. const pureKey = isKeyMatched(context, key);
  124. match = matchGlob(pureKey, newRequest);
  125. if (match) {
  126. const value = modulesField[key];
  127. if (typeof value !== "string") {
  128. return value;
  129. } else if (/^\(.+\)$/.test(pureKey)) {
  130. newRequest = newRequest.replace(getGlobRegExp(pureKey), value);
  131. } else {
  132. index = 1;
  133. newRequest = value.replace(/(\/?\*)?\*/g, replaceMatcher);
  134. }
  135. i = -1;
  136. if (iteration++ > keys.length) {
  137. throw new Error("Request '" + request + "' matches recursively");
  138. }
  139. }
  140. }
  141. return newRequest;
  142. function replaceMatcher(find) {
  143. switch (find) {
  144. case "/**": {
  145. const m = match[index++];
  146. return m ? "/" + m : "";
  147. }
  148. case "**":
  149. case "*":
  150. return match[index++];
  151. }
  152. }
  153. }
  154. function matchType(context, configuration, relativePath) {
  155. const typesField = getField(context, configuration, "types");
  156. if (!typesField) return undefined;
  157. let type;
  158. Object.keys(typesField).forEach(key => {
  159. const pureKey = isKeyMatched(context, key);
  160. if (isGlobMatched(pureKey, relativePath)) {
  161. const value = typesField[key];
  162. if (!type && /\/\*$/.test(value))
  163. throw new Error(
  164. "value ('" +
  165. value +
  166. "') of key '" +
  167. key +
  168. "' contains '*', but there is no previous value defined"
  169. );
  170. type = value.replace(/\/\*$/, "/" + type);
  171. }
  172. });
  173. return type;
  174. }
  175. exports.parseType = parseType;
  176. exports.isTypeMatched = isTypeMatched;
  177. exports.isResourceTypeSupported = isResourceTypeSupported;
  178. exports.isEnvironment = isEnvironment;
  179. exports.isGlobMatched = isGlobMatched;
  180. exports.isConditionMatched = isConditionMatched;
  181. exports.isKeyMatched = isKeyMatched;
  182. exports.getField = getField;
  183. exports.getMain = getMain;
  184. exports.getExtensions = getExtensions;
  185. exports.matchModule = matchModule;
  186. exports.matchType = matchType;