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