Apply module bundling
[platform/framework/web/wrtjs.git] / node_modules / webpack / lib / util / identifier.js
1 /*
2         MIT License http://www.opensource.org/licenses/mit-license.php
3 */
4
5 "use strict";
6
7 const path = require("path");
8
9 const WINDOWS_ABS_PATH_REGEXP = /^[a-zA-Z]:[\\/]/;
10 const SEGMENTS_SPLIT_REGEXP = /([|!])/;
11 const WINDOWS_PATH_SEPARATOR_REGEXP = /\\/g;
12
13 /**
14  * @typedef {Object} MakeRelativePathsCache
15  * @property {Map<string, Map<string, string>>=} relativePaths
16  */
17
18 const relativePathToRequest = relativePath => {
19         if (relativePath === "") return "./.";
20         if (relativePath === "..") return "../.";
21         if (relativePath.startsWith("../")) return relativePath;
22         return `./${relativePath}`;
23 };
24
25 /**
26  * @param {string} context context for relative path
27  * @param {string} maybeAbsolutePath path to make relative
28  * @returns {string} relative path in request style
29  */
30 const absoluteToRequest = (context, maybeAbsolutePath) => {
31         if (maybeAbsolutePath[0] === "/") {
32                 if (
33                         maybeAbsolutePath.length > 1 &&
34                         maybeAbsolutePath[maybeAbsolutePath.length - 1] === "/"
35                 ) {
36                         // this 'path' is actually a regexp generated by dynamic requires.
37                         // Don't treat it as an absolute path.
38                         return maybeAbsolutePath;
39                 }
40
41                 const querySplitPos = maybeAbsolutePath.indexOf("?");
42                 let resource =
43                         querySplitPos === -1
44                                 ? maybeAbsolutePath
45                                 : maybeAbsolutePath.slice(0, querySplitPos);
46                 resource = relativePathToRequest(path.posix.relative(context, resource));
47                 return querySplitPos === -1
48                         ? resource
49                         : resource + maybeAbsolutePath.slice(querySplitPos);
50         }
51
52         if (WINDOWS_ABS_PATH_REGEXP.test(maybeAbsolutePath)) {
53                 const querySplitPos = maybeAbsolutePath.indexOf("?");
54                 let resource =
55                         querySplitPos === -1
56                                 ? maybeAbsolutePath
57                                 : maybeAbsolutePath.slice(0, querySplitPos);
58                 resource = path.win32.relative(context, resource);
59                 if (!WINDOWS_ABS_PATH_REGEXP.test(resource)) {
60                         resource = relativePathToRequest(
61                                 resource.replace(WINDOWS_PATH_SEPARATOR_REGEXP, "/")
62                         );
63                 }
64                 return querySplitPos === -1
65                         ? resource
66                         : resource + maybeAbsolutePath.slice(querySplitPos);
67         }
68
69         // not an absolute path
70         return maybeAbsolutePath;
71 };
72
73 /**
74  * @param {string} context context for relative path
75  * @param {string} relativePath path
76  * @returns {string} absolute path
77  */
78 const requestToAbsolute = (context, relativePath) => {
79         if (relativePath.startsWith("./") || relativePath.startsWith("../"))
80                 return path.join(context, relativePath);
81         return relativePath;
82 };
83
84 const makeCacheable = realFn => {
85         /** @type {WeakMap<object, Map<string, ParsedResource>>} */
86         const cache = new WeakMap();
87
88         const getCache = associatedObjectForCache => {
89                 const entry = cache.get(associatedObjectForCache);
90                 if (entry !== undefined) return entry;
91                 /** @type {Map<string, ParsedResource>} */
92                 const map = new Map();
93                 cache.set(associatedObjectForCache, map);
94                 return map;
95         };
96
97         /**
98          * @param {string} str the path with query and fragment
99          * @param {Object=} associatedObjectForCache an object to which the cache will be attached
100          * @returns {ParsedResource} parsed parts
101          */
102         const fn = (str, associatedObjectForCache) => {
103                 if (!associatedObjectForCache) return realFn(str);
104                 const cache = getCache(associatedObjectForCache);
105                 const entry = cache.get(str);
106                 if (entry !== undefined) return entry;
107                 const result = realFn(str);
108                 cache.set(str, result);
109                 return result;
110         };
111
112         fn.bindCache = associatedObjectForCache => {
113                 const cache = getCache(associatedObjectForCache);
114                 return str => {
115                         const entry = cache.get(str);
116                         if (entry !== undefined) return entry;
117                         const result = realFn(str);
118                         cache.set(str, result);
119                         return result;
120                 };
121         };
122
123         return fn;
124 };
125
126 const makeCacheableWithContext = fn => {
127         /** @type {WeakMap<object, Map<string, Map<string, string>>>} */
128         const cache = new WeakMap();
129
130         /**
131          * @param {string} context context used to create relative path
132          * @param {string} identifier identifier used to create relative path
133          * @param {Object=} associatedObjectForCache an object to which the cache will be attached
134          * @returns {string} the returned relative path
135          */
136         const cachedFn = (context, identifier, associatedObjectForCache) => {
137                 if (!associatedObjectForCache) return fn(context, identifier);
138
139                 let innerCache = cache.get(associatedObjectForCache);
140                 if (innerCache === undefined) {
141                         innerCache = new Map();
142                         cache.set(associatedObjectForCache, innerCache);
143                 }
144
145                 let cachedResult;
146                 let innerSubCache = innerCache.get(context);
147                 if (innerSubCache === undefined) {
148                         innerCache.set(context, (innerSubCache = new Map()));
149                 } else {
150                         cachedResult = innerSubCache.get(identifier);
151                 }
152
153                 if (cachedResult !== undefined) {
154                         return cachedResult;
155                 } else {
156                         const result = fn(context, identifier);
157                         innerSubCache.set(identifier, result);
158                         return result;
159                 }
160         };
161
162         /**
163          * @param {Object=} associatedObjectForCache an object to which the cache will be attached
164          * @returns {function(string, string): string} cached function
165          */
166         cachedFn.bindCache = associatedObjectForCache => {
167                 let innerCache;
168                 if (associatedObjectForCache) {
169                         innerCache = cache.get(associatedObjectForCache);
170                         if (innerCache === undefined) {
171                                 innerCache = new Map();
172                                 cache.set(associatedObjectForCache, innerCache);
173                         }
174                 } else {
175                         innerCache = new Map();
176                 }
177
178                 /**
179                  * @param {string} context context used to create relative path
180                  * @param {string} identifier identifier used to create relative path
181                  * @returns {string} the returned relative path
182                  */
183                 const boundFn = (context, identifier) => {
184                         let cachedResult;
185                         let innerSubCache = innerCache.get(context);
186                         if (innerSubCache === undefined) {
187                                 innerCache.set(context, (innerSubCache = new Map()));
188                         } else {
189                                 cachedResult = innerSubCache.get(identifier);
190                         }
191
192                         if (cachedResult !== undefined) {
193                                 return cachedResult;
194                         } else {
195                                 const result = fn(context, identifier);
196                                 innerSubCache.set(identifier, result);
197                                 return result;
198                         }
199                 };
200
201                 return boundFn;
202         };
203
204         /**
205          * @param {string} context context used to create relative path
206          * @param {Object=} associatedObjectForCache an object to which the cache will be attached
207          * @returns {function(string): string} cached function
208          */
209         cachedFn.bindContextCache = (context, associatedObjectForCache) => {
210                 let innerSubCache;
211                 if (associatedObjectForCache) {
212                         let innerCache = cache.get(associatedObjectForCache);
213                         if (innerCache === undefined) {
214                                 innerCache = new Map();
215                                 cache.set(associatedObjectForCache, innerCache);
216                         }
217
218                         innerSubCache = innerCache.get(context);
219                         if (innerSubCache === undefined) {
220                                 innerCache.set(context, (innerSubCache = new Map()));
221                         }
222                 } else {
223                         innerSubCache = new Map();
224                 }
225
226                 /**
227                  * @param {string} identifier identifier used to create relative path
228                  * @returns {string} the returned relative path
229                  */
230                 const boundFn = identifier => {
231                         const cachedResult = innerSubCache.get(identifier);
232                         if (cachedResult !== undefined) {
233                                 return cachedResult;
234                         } else {
235                                 const result = fn(context, identifier);
236                                 innerSubCache.set(identifier, result);
237                                 return result;
238                         }
239                 };
240
241                 return boundFn;
242         };
243
244         return cachedFn;
245 };
246
247 /**
248  *
249  * @param {string} context context for relative path
250  * @param {string} identifier identifier for path
251  * @returns {string} a converted relative path
252  */
253 const _makePathsRelative = (context, identifier) => {
254         return identifier
255                 .split(SEGMENTS_SPLIT_REGEXP)
256                 .map(str => absoluteToRequest(context, str))
257                 .join("");
258 };
259
260 exports.makePathsRelative = makeCacheableWithContext(_makePathsRelative);
261
262 /**
263  *
264  * @param {string} context context for relative path
265  * @param {string} identifier identifier for path
266  * @returns {string} a converted relative path
267  */
268 const _makePathsAbsolute = (context, identifier) => {
269         return identifier
270                 .split(SEGMENTS_SPLIT_REGEXP)
271                 .map(str => requestToAbsolute(context, str))
272                 .join("");
273 };
274
275 exports.makePathsAbsolute = makeCacheableWithContext(_makePathsAbsolute);
276
277 /**
278  * @param {string} context absolute context path
279  * @param {string} request any request string may containing absolute paths, query string, etc.
280  * @returns {string} a new request string avoiding absolute paths when possible
281  */
282 const _contextify = (context, request) => {
283         return request
284                 .split("!")
285                 .map(r => absoluteToRequest(context, r))
286                 .join("!");
287 };
288
289 const contextify = makeCacheableWithContext(_contextify);
290 exports.contextify = contextify;
291
292 /**
293  * @param {string} context absolute context path
294  * @param {string} request any request string
295  * @returns {string} a new request string using absolute paths when possible
296  */
297 const _absolutify = (context, request) => {
298         return request
299                 .split("!")
300                 .map(r => requestToAbsolute(context, r))
301                 .join("!");
302 };
303
304 const absolutify = makeCacheableWithContext(_absolutify);
305 exports.absolutify = absolutify;
306
307 const PATH_QUERY_FRAGMENT_REGEXP =
308         /^((?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
309 const PATH_QUERY_REGEXP = /^((?:\0.|[^?\0])*)(\?.*)?$/;
310
311 /** @typedef {{ resource: string, path: string, query: string, fragment: string }} ParsedResource */
312 /** @typedef {{ resource: string, path: string, query: string }} ParsedResourceWithoutFragment */
313
314 /**
315  * @param {string} str the path with query and fragment
316  * @returns {ParsedResource} parsed parts
317  */
318 const _parseResource = str => {
319         const match = PATH_QUERY_FRAGMENT_REGEXP.exec(str);
320         return {
321                 resource: str,
322                 path: match[1].replace(/\0(.)/g, "$1"),
323                 query: match[2] ? match[2].replace(/\0(.)/g, "$1") : "",
324                 fragment: match[3] || ""
325         };
326 };
327 exports.parseResource = makeCacheable(_parseResource);
328
329 /**
330  * Parse resource, skips fragment part
331  * @param {string} str the path with query and fragment
332  * @returns {ParsedResourceWithoutFragment} parsed parts
333  */
334 const _parseResourceWithoutFragment = str => {
335         const match = PATH_QUERY_REGEXP.exec(str);
336         return {
337                 resource: str,
338                 path: match[1].replace(/\0(.)/g, "$1"),
339                 query: match[2] ? match[2].replace(/\0(.)/g, "$1") : ""
340         };
341 };
342 exports.parseResourceWithoutFragment = makeCacheable(
343         _parseResourceWithoutFragment
344 );
345
346 /**
347  * @param {string} filename the filename which should be undone
348  * @param {string} outputPath the output path that is restored (only relevant when filename contains "..")
349  * @param {boolean} enforceRelative true returns ./ for empty paths
350  * @returns {string} repeated ../ to leave the directory of the provided filename to be back on output dir
351  */
352 exports.getUndoPath = (filename, outputPath, enforceRelative) => {
353         let depth = -1;
354         let append = "";
355         outputPath = outputPath.replace(/[\\/]$/, "");
356         for (const part of filename.split(/[/\\]+/)) {
357                 if (part === "..") {
358                         if (depth > -1) {
359                                 depth--;
360                         } else {
361                                 const i = outputPath.lastIndexOf("/");
362                                 const j = outputPath.lastIndexOf("\\");
363                                 const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j);
364                                 if (pos < 0) return outputPath + "/";
365                                 append = outputPath.slice(pos + 1) + "/" + append;
366                                 outputPath = outputPath.slice(0, pos);
367                         }
368                 } else if (part !== ".") {
369                         depth++;
370                 }
371         }
372         return depth > 0
373                 ? `${"../".repeat(depth)}${append}`
374                 : enforceRelative
375                 ? `./${append}`
376                 : append;
377 };