source: frontend/node_modules/enhanced-resolve/lib/util/path.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const path = require("path");
9
10const CHAR_HASH = "#".charCodeAt(0);
11const CHAR_SLASH = "/".charCodeAt(0);
12const CHAR_BACKSLASH = "\\".charCodeAt(0);
13const CHAR_A = "A".charCodeAt(0);
14const CHAR_Z = "Z".charCodeAt(0);
15const CHAR_LOWER_A = "a".charCodeAt(0);
16const CHAR_LOWER_Z = "z".charCodeAt(0);
17const CHAR_DOT = ".".charCodeAt(0);
18const CHAR_COLON = ":".charCodeAt(0);
19const CHAR_QUESTION = "?".charCodeAt(0);
20
21const posixNormalize = path.posix.normalize;
22const winNormalize = path.win32.normalize;
23
24/**
25 * @enum {number}
26 */
27const PathType = Object.freeze({
28 Empty: 0,
29 Normal: 1,
30 Relative: 2,
31 AbsoluteWin: 3,
32 AbsolutePosix: 4,
33 Internal: 5,
34});
35
36const deprecatedInvalidSegmentRegEx =
37 /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))(\\|\/|$)/i;
38
39const invalidSegmentRegEx =
40 /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))?(\\|\/|$)/i;
41
42/**
43 * @param {string} maybePath a path known to start with `\\`
44 * @returns {PathType} AbsoluteWin for `\\?\…` / `\\.\…`, otherwise Normal
45 */
46const getDosDeviceType = (maybePath) => {
47 if (maybePath.length >= 4 && maybePath.charCodeAt(3) === CHAR_BACKSLASH) {
48 const c2 = maybePath.charCodeAt(2);
49 if (c2 === CHAR_QUESTION || c2 === CHAR_DOT) {
50 return PathType.AbsoluteWin;
51 }
52 }
53 return PathType.Normal;
54};
55
56/**
57 * @param {string} maybePath a path
58 * @returns {PathType} type of path
59 */
60const getType = (maybePath) => {
61 switch (maybePath.length) {
62 case 0:
63 return PathType.Empty;
64 case 1: {
65 const c0 = maybePath.charCodeAt(0);
66 switch (c0) {
67 case CHAR_DOT:
68 return PathType.Relative;
69 case CHAR_SLASH:
70 return PathType.AbsolutePosix;
71 case CHAR_HASH:
72 return PathType.Internal;
73 }
74 return PathType.Normal;
75 }
76 case 2: {
77 const c0 = maybePath.charCodeAt(0);
78 switch (c0) {
79 case CHAR_DOT: {
80 const c1 = maybePath.charCodeAt(1);
81 switch (c1) {
82 case CHAR_DOT:
83 case CHAR_SLASH:
84 return PathType.Relative;
85 }
86 return PathType.Normal;
87 }
88 case CHAR_SLASH:
89 return PathType.AbsolutePosix;
90 case CHAR_HASH:
91 return PathType.Internal;
92 }
93 const c1 = maybePath.charCodeAt(1);
94 if (
95 c1 === CHAR_COLON &&
96 ((c0 >= CHAR_A && c0 <= CHAR_Z) ||
97 (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
98 ) {
99 return PathType.AbsoluteWin;
100 }
101 return PathType.Normal;
102 }
103 }
104 const c0 = maybePath.charCodeAt(0);
105 switch (c0) {
106 case CHAR_DOT: {
107 const c1 = maybePath.charCodeAt(1);
108 switch (c1) {
109 case CHAR_SLASH:
110 return PathType.Relative;
111 case CHAR_DOT: {
112 const c2 = maybePath.charCodeAt(2);
113 if (c2 === CHAR_SLASH) return PathType.Relative;
114 return PathType.Normal;
115 }
116 }
117 return PathType.Normal;
118 }
119 case CHAR_SLASH:
120 return PathType.AbsolutePosix;
121 case CHAR_HASH:
122 return PathType.Internal;
123 }
124 const c1 = maybePath.charCodeAt(1);
125 if (c1 === CHAR_COLON) {
126 const c2 = maybePath.charCodeAt(2);
127 if (
128 (c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) &&
129 ((c0 >= CHAR_A && c0 <= CHAR_Z) ||
130 (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
131 ) {
132 return PathType.AbsoluteWin;
133 }
134 }
135 // DOS device paths (`\\?\…`, `\\.\…`) are handled in a cold helper so
136 // this function stays small — inlining the full check here regressed
137 // `description-files-multi` under `--no-opt` interpretation. Here we
138 // only pay the two-byte gate for non-DOS inputs.
139 if (c0 === CHAR_BACKSLASH && c1 === CHAR_BACKSLASH) {
140 return getDosDeviceType(maybePath);
141 }
142 return PathType.Normal;
143};
144
145/**
146 * @param {string} maybePath a path
147 * @returns {string} the normalized path
148 */
149const normalize = (maybePath) => {
150 switch (getType(maybePath)) {
151 case PathType.Empty:
152 return maybePath;
153 case PathType.AbsoluteWin:
154 return winNormalize(maybePath);
155 case PathType.Relative: {
156 const r = posixNormalize(maybePath);
157 return getType(r) === PathType.Relative ? r : `./${r}`;
158 }
159 }
160 return posixNormalize(maybePath);
161};
162
163/**
164 * @param {string} rootPath the root path
165 * @param {string | undefined} request the request path
166 * @returns {string} the joined path
167 */
168const join = (rootPath, request) => {
169 if (!request) return normalize(rootPath);
170 const requestType = getType(request);
171 switch (requestType) {
172 case PathType.AbsolutePosix:
173 return posixNormalize(request);
174 case PathType.AbsoluteWin:
175 return winNormalize(request);
176 }
177 switch (getType(rootPath)) {
178 case PathType.Normal:
179 case PathType.Relative:
180 case PathType.AbsolutePosix:
181 return posixNormalize(`${rootPath}/${request}`);
182 case PathType.AbsoluteWin:
183 return winNormalize(`${rootPath}\\${request}`);
184 }
185 switch (requestType) {
186 case PathType.Empty:
187 return rootPath;
188 case PathType.Relative: {
189 const r = posixNormalize(rootPath);
190 return getType(r) === PathType.Relative ? r : `./${r}`;
191 }
192 }
193 return posixNormalize(rootPath);
194};
195
196/**
197 * @param {string} maybePath a path
198 * @returns {string} the directory name
199 */
200const dirname = (maybePath) => {
201 switch (getType(maybePath)) {
202 case PathType.AbsoluteWin:
203 return path.win32.dirname(maybePath);
204 }
205 return path.posix.dirname(maybePath);
206};
207
208/** @typedef {{ fn: (rootPath: string, request: string) => string, cache: Map<string, Map<string, string | undefined>> }} CachedJoin */
209
210/**
211 * @returns {CachedJoin} cached join
212 */
213const createCachedJoin = () => {
214 /** @type {CachedJoin["cache"]} */
215 const cache = new Map();
216 /** @type {CachedJoin["fn"]} */
217 const fn = (rootPath, request) => {
218 /** @type {string | undefined} */
219 let cacheEntry;
220 let inner = cache.get(rootPath);
221 if (inner === undefined) {
222 cache.set(rootPath, (inner = new Map()));
223 } else {
224 cacheEntry = inner.get(request);
225 if (cacheEntry !== undefined) return cacheEntry;
226 }
227 cacheEntry = join(rootPath, request);
228 inner.set(request, cacheEntry);
229 return cacheEntry;
230 };
231 return { fn, cache };
232};
233
234/** @typedef {{ fn: (maybePath: string) => string, cache: Map<string, string> }} CachedDirname */
235
236/**
237 * @returns {CachedDirname} cached dirname
238 */
239const createCachedDirname = () => {
240 /** @type {CachedDirname["cache"]} */
241 const cache = new Map();
242 /** @type {CachedDirname["fn"]} */
243 const fn = (maybePath) => {
244 const cacheEntry = cache.get(maybePath);
245 if (cacheEntry !== undefined) return cacheEntry;
246 const result = dirname(maybePath);
247 cache.set(maybePath, result);
248 return result;
249 };
250 return { fn, cache };
251};
252
253/** @typedef {{ fn: (maybePath: string, suffix?: string) => string, cache: Map<string, Map<string | undefined, string | undefined>> }} CachedBasename */
254
255/**
256 * @returns {CachedBasename} cached basename
257 */
258const createCachedBasename = () => {
259 /** @type {CachedBasename["cache"]} */
260 const cache = new Map();
261 /** @type {CachedBasename["fn"]} */
262 const fn = (maybePath, suffix) => {
263 /** @type {string | undefined} */
264 let cacheEntry;
265 let inner = cache.get(maybePath);
266 if (inner === undefined) {
267 cache.set(maybePath, (inner = new Map()));
268 } else {
269 cacheEntry = inner.get(suffix);
270 if (cacheEntry !== undefined) return cacheEntry;
271 }
272 cacheEntry = path.basename(maybePath, suffix);
273 inner.set(suffix, cacheEntry);
274 return cacheEntry;
275 };
276 return { fn, cache };
277};
278
279/**
280 * Whether `request` is a relative request — i.e. matches `^\.\.?(?:\/|$)`.
281 *
282 * This is called on every `doResolve` via `UnsafeCachePlugin` and
283 * `getInnerRequest`, so the char-code form is meaningfully faster than the
284 * equivalent regex test: no regex state machine, no string object churn.
285 * @param {string} request request string
286 * @returns {boolean} true if request is relative
287 */
288const isRelativeRequest = (request) => {
289 const len = request.length;
290 if (len === 0 || request.charCodeAt(0) !== CHAR_DOT) return false;
291 if (len === 1) return true; // "."
292 const c1 = request.charCodeAt(1);
293 if (c1 === CHAR_SLASH) return true; // "./..."
294 if (c1 !== CHAR_DOT) return false; // ".x..."
295 if (len === 2) return true; // ".."
296 return request.charCodeAt(2) === CHAR_SLASH; // "../..."
297};
298
299/**
300 * Check if childPath is a subdirectory of parentPath.
301 *
302 * Called from `TsconfigPathsPlugin._selectPathsDataForContext` inside a loop
303 * over every tsconfig-paths context on every resolve, so it's worth keeping
304 * cheap. Compared to the previous `startsWith(normalize(parent + "/"))`
305 * version, this: checks the last char with `charCodeAt` instead of two
306 * `endsWith` calls; and skips `normalize()` entirely in the common case
307 * (parent has no trailing separator), since all we really need is the same
308 * anchoring effect — a cheap `startsWith` plus a separator char check on the
309 * byte immediately after `parentPath.length`.
310 * @param {string} parentPath parent directory path
311 * @param {string} childPath child path to check
312 * @returns {boolean} true if childPath is under parentPath
313 */
314const isSubPath = (parentPath, childPath) => {
315 const parentLen = parentPath.length;
316 if (parentLen === 0) {
317 // Match the old `normalize("" + "/") === "/"` fallback: an empty
318 // parent only "contains" a child that starts with a forward slash.
319 return childPath.length > 0 && childPath.charCodeAt(0) === CHAR_SLASH;
320 }
321 const lastChar = parentPath.charCodeAt(parentLen - 1);
322 if (lastChar === CHAR_SLASH || lastChar === CHAR_BACKSLASH) {
323 // Parent already ends with a separator — a plain prefix test is enough.
324 return childPath.startsWith(parentPath);
325 }
326 if (childPath.length <= parentLen) return false;
327 if (!childPath.startsWith(parentPath)) return false;
328 // Must be followed by a separator so "/app" doesn't match "/app-other".
329 const nextChar = childPath.charCodeAt(parentLen);
330 return nextChar === CHAR_SLASH || nextChar === CHAR_BACKSLASH;
331};
332
333module.exports.PathType = PathType;
334module.exports.createCachedBasename = createCachedBasename;
335module.exports.createCachedDirname = createCachedDirname;
336module.exports.createCachedJoin = createCachedJoin;
337module.exports.deprecatedInvalidSegmentRegEx = deprecatedInvalidSegmentRegEx;
338module.exports.dirname = dirname;
339module.exports.getType = getType;
340module.exports.invalidSegmentRegEx = invalidSegmentRegEx;
341module.exports.isRelativeRequest = isRelativeRequest;
342module.exports.isSubPath = isSubPath;
343module.exports.join = join;
344module.exports.normalize = normalize;
Note: See TracBrowser for help on using the repository browser.