src/vs/base/common/path.ts

1589 LOC · 1189 covered · 400 uncovered · 221 ranges · 20961 concepts · 73 introducers · 12741 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- map.ts ×97
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > // NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace
7 > // Copied from: https://github.com/nodejs/node/commits/v22.15.0/lib/path.js
8 > // Excluding: the change that adds primordials
9 > // (https://github.com/nodejs/node/commit/187a862d221dec42fa9a5c4214e7034d9092792f and others)
10 > // Excluding: the change that adds glob matching
11 > // (https://github.com/nodejs/node/commit/57b8b8e18e5e2007114c63b71bf0baedc01936a6)
12 >
13 > /**
14 > * Copyright Joyent, Inc. and other Node contributors.
15 > *
16 > * Permission is hereby granted, free of charge, to any person obtaining a
17 > * copy of this software and associated documentation files (the
18 > * "Software"), to deal in the Software without restriction, including
19 > * without limitation the rights to use, copy, modify, merge, publish,
20 > * distribute, sublicense, and/or sell copies of the Software, and to permit
21 > * persons to whom the Software is furnished to do so, subject to the
22 > * following conditions:
23 > *
24 > * The above copyright notice and this permission notice shall be included
25 > * in all copies or substantial portions of the Software.
26 > *
27 > * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28 > * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 > * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
30 > * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
31 > * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
32 > * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
33 > * USE OR OTHER DEALINGS IN THE SOFTWARE.
34 > */
35 >
36 > import * as process from './process.js';
37 >
38 > const CHAR_UPPERCASE_A = 65;/* A */
39 > const CHAR_LOWERCASE_A = 97; /* a */
40 > const CHAR_UPPERCASE_Z = 90; /* Z */
41 > const CHAR_LOWERCASE_Z = 122; /* z */
42 > const CHAR_DOT = 46; /* . */
43 > const CHAR_FORWARD_SLASH = 47; /* / */
44 > const CHAR_BACKWARD_SLASH = 92; /* \ */
45 > const CHAR_COLON = 58; /* : */
46 > const CHAR_QUESTION_MARK = 63; /* ? */
47 >
48 > class ErrorInvalidArgType extends Error {
49 > code: 'ERR_INVALID_ARG_TYPE';
50 > constructor(name: string, expected: string, actual: unknown) {
51 // determiner: 'must be' or 'must not be'
52 let determiner;
53 if (typeof expected === 'string' && expected.indexOf('not ') === 0) {
54 determiner = 'must not be';
55 expected = expected.replace(/^not /, '');
56 } else {
57 determiner = 'must be';
58 }
59
60 const type = name.indexOf('.') !== -1 ? 'property' : 'argument';
61 let msg = `The "${name}" ${type} ${determiner} of type ${expected}`;
62
63 msg += `. Received type ${typeof actual}`;
64 super(msg);
65
66 this.code = 'ERR_INVALID_ARG_TYPE';
67 }
68 > } map.ts ×97
69 >
70 function validateObject(pathObject: object, name: string) {
71 if (pathObject === null || typeof pathObject !== 'object') {
72 throw new ErrorInvalidArgType(name, 'Object', pathObject);
73 }
74 }
76 > function validateString(value: string, name: string) { path.ts ×2
77 > if (typeof value !== 'string') {
78 throw new ErrorInvalidArgType(name, 'string', value);
79 }
80 > } path.ts ×2
82 > const platformIsWin32 = (process.platform === 'win32');
83 >
84 > function isPathSeparator(code: number | undefined) { path.ts ×1
85 > return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
86 > }
88 > function isPosixPathSeparator(code: number | undefined) { path.ts ×6
89 > return code === CHAR_FORWARD_SLASH;
90 > }
92 > function isWindowsDeviceRoot(code: number) { path.ts ×2
93 > return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) ||
94 > (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z); path.ts ×1
95 > } path.ts ×2
97 > // Resolves . and .. elements in a path with directory names
98 > function normalizeString(path: string, allowAboveRoot: boolean, separator: string, isPathSeparator: (code?: number) => boolean) { path.ts ×6
99 > let res = '';
100 > let lastSegmentLength = 0;
101 > let lastSlash = -1;
102 > let dots = 0;
103 > let code = 0;
104 > for (let i = 0; i <= path.length; ++i) {
105 > if (i < path.length) {
106 > code = path.charCodeAt(i);
107 > }
108 > else if (isPathSeparator(code)) {
109 > break; path.ts ×1
110 > }
111 > else { path.ts ×3
112 > code = CHAR_FORWARD_SLASH;
113 > }
114 > path.ts ×6
115 > if (isPathSeparator(code)) {
116 > if (lastSlash === i - 1 || dots === 1) {
117 > // NOOP path.ts ×1
118 > } else if (dots === 2) { path.ts ×6
119 > if (res.length < 2 || lastSegmentLength !== 2 || path.ts ×5
120 > res.charCodeAt(res.length - 1) !== CHAR_DOT || path.ts ×1
121 > res.charCodeAt(res.length - 2) !== CHAR_DOT) { path.ts ×5
122 > if (res.length > 2) {
123 > const lastSlashIndex = res.lastIndexOf(separator);
124 > if (lastSlashIndex === -1) {
125 > res = ''; path.ts ×1
126 > lastSegmentLength = 0;
127 > } else { path.ts ×5
128 > res = res.slice(0, lastSlashIndex); path.ts ×1
129 > lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
130 > }
131 > lastSlash = i; path.ts ×5
132 > dots = 0;
133 > continue;
134 > } else if (res.length !== 0) {
135 > res = ''; path.ts ×1
136 > lastSegmentLength = 0;
137 > lastSlash = i;
138 > dots = 0;
139 > continue;
140 > }
141 > } path.ts ×5
142 > if (allowAboveRoot) { path.ts ×1
143 > res += res.length > 0 ? `${separator}..` : '..'; path.ts ×4
144 > lastSegmentLength = 2;
145 > }
146 > } else { path.ts ×3
147 > if (res.length > 0) {
148 > res += `${separator}${path.slice(lastSlash + 1, i)}`; path.ts ×1
149 > }
150 > else { path.ts ×3
151 > res = path.slice(lastSlash + 1, i);
152 > }
153 > lastSegmentLength = i - lastSlash - 1;
154 > }
155 > lastSlash = i; path.ts ×6
156 > dots = 0;
157 > } else if (code === CHAR_DOT && dots !== -1) {
158 > ++dots; path.ts ×1
159 > } else { path.ts ×3
160 > dots = -1;
161 > }
162 > } path.ts ×6
163 > return res;
164 > }
165 > map.ts ×97
166 function formatExt(ext: string): string {
167 return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : '';
168 }
169 > map.ts ×97
170 function _format(sep: string, pathObject: ParsedPath) {
171 validateObject(pathObject, 'pathObject');
172 const dir = pathObject.dir || pathObject.root;
173 const base = pathObject.base ||
174 `${pathObject.name || ''}${formatExt(pathObject.ext)}`;
175 if (!dir) {
176 return base;
177 }
178 return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`;
179 }
180 > map.ts ×97
181 > export interface ParsedPath {
182 > root: string;
183 > dir: string;
184 > base: string;
185 > ext: string;
186 > name: string;
187 > }
188 >
189 > export interface IPath {
190 > normalize(path: string): string;
191 > isAbsolute(path: string): boolean;
192 > join(...paths: string[]): string;
193 > resolve(...pathSegments: string[]): string;
194 > relative(from: string, to: string): string;
195 > dirname(path: string): string;
196 > basename(path: string, suffix?: string): string;
197 > extname(path: string): string;
198 > format(pathObject: ParsedPath): string;
199 > parse(path: string): ParsedPath;
200 > toNamespacedPath(path: string): string;
201 > sep: '\\' | '/';
202 > delimiter: string;
203 > win32: IPath | null;
204 > posix: IPath | null;
205 > }
206 >
207 > export const win32: IPath = {
208 > // path.resolve([from ...], to)
209 > resolve(...pathSegments: string[]): string {
210 > let resolvedDevice = ''; path.ts ×8
211 > let resolvedTail = '';
212 > let resolvedAbsolute = false;
213 >
214 > for (let i = pathSegments.length - 1; i >= -1; i--) {
215 > let path;
216 > if (i >= 0) {
217 > path = pathSegments[i];
218 > validateString(path, `paths[${i}]`);
219 >
220 > // Skip empty entries
221 > if (path.length === 0) {
222 continue;
223 }
224 > } else if (resolvedDevice.length === 0) { path.ts ×8
225 path = process.cwd();
226 } else {
227 // Windows has the concept of drive-specific current working
228 // directories. If we've resolved a drive letter but not yet an
229 // absolute path, get cwd for that drive, or the process cwd if
230 // the drive cwd is not available. We're sure the device is not
231 // a UNC path at this points, because UNC paths are always absolute.
232 path = process.env[`=${resolvedDevice}`] || process.cwd();
233
234 // Verify that a cwd was found and that it actually points
235 // to our drive. If not, default to the drive's root.
236 if (path === undefined ||
237 (path.slice(0, 2).toLowerCase() !== resolvedDevice.toLowerCase() &&
238 path.charCodeAt(2) === CHAR_BACKWARD_SLASH)) {
239 path = `${resolvedDevice}\\`;
240 }
241 }
242 > path.ts ×8
243 > const len = path.length;
244 > let rootEnd = 0;
245 > let device = '';
246 > let isAbsolute = false;
247 > const code = path.charCodeAt(0);
248 >
249 > // Try to match a root
250 > if (len === 1) {
251 if (isPathSeparator(code)) {
252 // `path` contains just a path separator
253 rootEnd = 1;
254 isAbsolute = true;
255 }
256 > } else if (isPathSeparator(code)) { path.ts ×8
257 > // Possible UNC root
258 >
259 > // If we started with a separator, we know we at least have an
260 > // absolute path of some kind (UNC or otherwise)
261 > isAbsolute = true;
262 >
263 > if (isPathSeparator(path.charCodeAt(1))) {
264 > // Matched double path separator at beginning
265 > let j = 2;
266 > let last = j;
267 > // Match 1 or more non-path separators
268 > while (j < len && !isPathSeparator(path.charCodeAt(j))) {
269 > j++;
270 > }
271 > if (j < len && j !== last) {
272 > const firstPart = path.slice(last, j);
273 > // Matched!
274 > last = j;
275 > // Match 1 or more path separators
276 > while (j < len && isPathSeparator(path.charCodeAt(j))) {
277 > j++;
278 > }
279 > if (j < len && j !== last) {
280 > // Matched!
281 > last = j;
282 > // Match 1 or more non-path separators
283 > while (j < len && !isPathSeparator(path.charCodeAt(j))) {
284 > j++;
285 > }
286 > if (j === len || j !== last) {
287 > // We matched a UNC root
288 > device = `\\\\${firstPart}\\${path.slice(last, j)}`;
289 > rootEnd = j;
290 > }
291 > }
292 > }
293 > } else {
294 > rootEnd = 1; path.ts ×3
295 > }
296 > } else if (isWindowsDeviceRoot(code) && path.ts ×8
297 > path.charCodeAt(1) === CHAR_COLON) {
298 > // Possible device root
299 > device = path.slice(0, 2);
300 > rootEnd = 2;
301 > if (len > 2 && isPathSeparator(path.charCodeAt(2))) {
302 > // Treat separator following drive name as an absolute path
303 > // indicator
304 > isAbsolute = true;
305 > rootEnd = 3;
306 > }
307 > }
308 >
309 > if (device.length > 0) {
310 > if (resolvedDevice.length > 0) {
311 > if (device.toLowerCase() !== resolvedDevice.toLowerCase()) { path.ts ×3
312 > // This path points to another device so it is not applicable
313 > continue;
314 > }
315 > } else { path.ts ×8
316 > resolvedDevice = device;
317 > }
318 > }
319 >
320 > if (resolvedAbsolute) {
321 > if (resolvedDevice.length > 0) { path.ts ×3
322 > break;
323 > }
324 > } else { path.ts ×8
325 > resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`;
326 > resolvedAbsolute = isAbsolute;
327 > if (isAbsolute && resolvedDevice.length > 0) {
328 > break;
329 > }
330 > }
331 > }
332 >
333 > // At this point the path should be resolved to a full absolute path,
334 > // but handle relative paths to be safe (might happen when process.cwd()
335 > // fails)
336 >
337 > // Normalize the tail path
338 > resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\',
339 > isPathSeparator);
340 >
341 > return resolvedAbsolute ?
342 > `${resolvedDevice}\\${resolvedTail}` :
343 `${resolvedDevice}${resolvedTail}` || '.';
344 > }, path.ts ×8
345 > map.ts ×97
346 > normalize(path: string): string {
347 > validateString(path, 'path'); path.ts ×10
348 > const len = path.length;
349 > if (len === 0) {
350 return '.';
351 }
352 > let rootEnd = 0; path.ts ×10
353 > let device;
354 > let isAbsolute = false;
355 > const code = path.charCodeAt(0);
356 >
357 > // Try to match a root
358 > if (len === 1) {
359 > // `path` contains just a single char, exit early to avoid path.ts ×9
360 > // unnecessary work
361 > return isPosixPathSeparator(code) ? '\\' : path;
362 > }
363 > if (isPathSeparator(code)) { path.ts ×10
364 > // Possible UNC root path.ts ×3
365 >
366 > // If we started with a separator, we know we at least have an absolute
367 > // path of some kind (UNC or otherwise)
368 > isAbsolute = true;
369 >
370 > if (isPathSeparator(path.charCodeAt(1))) {
371 > // Matched double path separator at beginning path.ts ×2
372 > let j = 2;
373 > let last = j;
374 > // Match 1 or more non-path separators
375 > while (j < len && !isPathSeparator(path.charCodeAt(j))) {
376 > j++;
377 > }
378 > if (j < len && j !== last) {
379 > const firstPart = path.slice(last, j);
380 > // Matched!
381 > last = j;
382 > // Match 1 or more path separators
383 > while (j < len && isPathSeparator(path.charCodeAt(j))) {
384 > j++;
385 > }
386 > if (j < len && j !== last) {
387 > // Matched!
388 > last = j;
389 > // Match 1 or more non-path separators
390 > while (j < len && !isPathSeparator(path.charCodeAt(j))) {
391 > j++;
392 > }
393 > if (j === len) {
394 > // We matched a UNC root only path.ts ×9
395 > // Return the normalized version of the UNC root since there
396 > // is nothing left to process
397 > return `\\\\${firstPart}\\${path.slice(last)}\\`;
398 > }
399 > if (j !== last) { path.ts ×1
400 > // We matched a UNC root with leftovers
401 > device = `\\\\${firstPart}\\${path.slice(last, j)}`;
402 > rootEnd = j;
403 > }
404 > } path.ts ×2
405 > }
406 > } else { path.ts ×3
407 > rootEnd = 1;
408 > }
409 > } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { path.ts ×10
410 > // Possible device root path.ts ×1
411 > device = path.slice(0, 2);
412 > rootEnd = 2;
413 > if (len > 2 && isPathSeparator(path.charCodeAt(2))) {
414 > // Treat separator following drive name as an absolute path
415 > // indicator
416 > isAbsolute = true;
417 > rootEnd = 3;
418 > }
419 > }
420 > path.ts ×10
421 > let tail = rootEnd < len ?
422 > normalizeString(path.slice(rootEnd), !isAbsolute, '\\', isPathSeparator) :
423 > ''; path.ts ×4
424 > if (tail.length === 0 && !isAbsolute) { path.ts ×10
425 > tail = '.'; path.ts ×4
426 > }
427 > if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) { path.ts ×10
428 > tail += '\\'; path.ts ×4
429 > }
430 > if (!isAbsolute && device === undefined && path.includes(':')) { path.ts ×10
431 > // If the original path was not absolute and if we have not been able to path.ts ×3
432 > // resolve it relative to a particular device, we need to ensure that the
433 > // `tail` has not become something that Windows might interpret as an
434 > // absolute path. See CVE-2024-36139.
435 > if (tail.length >= 2 &&
436 > isWindowsDeviceRoot(tail.charCodeAt(0)) &&
437 > tail.charCodeAt(1) === CHAR_COLON) {
438 return `.\\${tail}`;
439 }
440 > let index = path.indexOf(':'); path.ts ×3
441 > do {
442 > if (index === len - 1 || isPathSeparator(path.charCodeAt(index + 1))) {
443 return `.\\${tail}`;
444 }
445 > } while ((index = path.indexOf(':', index + 1)) !== -1); path.ts ×3
446 > }
447 > if (device === undefined) { path.ts ×10
448 > return isAbsolute ? `\\${tail}` : tail; path.ts ×3
449 > }
450 > return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`; path.ts ×10
451 > },
452 > map.ts ×97
453 > isAbsolute(path: string): boolean {
454 > validateString(path, 'path'); path.ts ×2
455 > const len = path.length;
456 > if (len === 0) {
457 > return false; path.ts ×1
458 > }
459 > path.ts ×2
460 > const code = path.charCodeAt(0);
461 > return isPathSeparator(code) ||
462 > // Possible device root
463 > (len > 2 &&
464 > isWindowsDeviceRoot(code) &&
465 > path.charCodeAt(1) === CHAR_COLON &&
466 > isPathSeparator(path.charCodeAt(2)));
467 > },
468 > map.ts ×97
469 > join(...paths: string[]): string {
470 > if (paths.length === 0) { path.ts ×6
471 > return '.'; path.ts ×9
472 > }
473 > path.ts ×6
474 > let joined;
475 > let firstPart: string | undefined;
476 > for (let i = 0; i < paths.length; ++i) {
477 > const arg = paths[i];
478 > validateString(arg, 'path');
479 > if (arg.length > 0) {
480 > if (joined === undefined) {
481 > joined = firstPart = arg;
482 > }
483 > else {
484 > joined += `\\${arg}`;
485 > }
486 > }
487 > }
488 >
489 > if (joined === undefined) {
490 > return '.'; path.ts ×9
491 > }
492 > path.ts ×6
493 > // Make sure that the joined path doesn't start with two slashes, because
494 > // normalize() will mistake it for a UNC path then.
495 > //
496 > // This step is skipped when it is very clear that the user actually
497 > // intended to point at a UNC path. This is assumed when the first
498 > // non-empty string arguments starts with exactly two slashes followed by
499 > // at least one more non-slash character.
500 > //
501 > // Note that for normalize() to treat a path as a UNC path it needs to
502 > // have at least 2 components, so we don't filter for that here.
503 > // This means that the user can use join to construct UNC paths from
504 > // a server name and a share name; for example:
505 > // path.join('//server', 'share') -> '\\\\server\\share\\')
506 > let needsReplace = true;
507 > let slashCount = 0;
508 > if (typeof firstPart === 'string' && isPathSeparator(firstPart.charCodeAt(0))) {
509 > ++slashCount; path.ts ×2
510 > const firstLen = firstPart.length;
511 > if (firstLen > 1 && isPathSeparator(firstPart.charCodeAt(1))) {
512 > ++slashCount; path.ts ×9
513 > if (firstLen > 2) {
514 > if (isPathSeparator(firstPart.charCodeAt(2))) {
515 > ++slashCount;
516 > } else {
517 > // We matched a UNC path in the first part
518 > needsReplace = false;
519 > }
520 > }
521 > }
522 > } path.ts ×2
523 > if (needsReplace) { path.ts ×6
524 > // Find any more consecutive slashes we need to replace
525 > while (slashCount < joined.length &&
526 > isPathSeparator(joined.charCodeAt(slashCount))) {
527 > slashCount++; path.ts ×9
528 > }
529 > path.ts ×6
530 > // Replace the slashes if needed
531 > if (slashCount >= 2) {
532 > joined = `\\${joined.slice(slashCount)}`; path.ts ×9
533 > }
534 > } path.ts ×6
535 >
536 > return win32.normalize(joined);
537 > },
538 > map.ts ×97
539 >
540 > // It will solve the relative path from `from` to `to`, for instance:
541 > // from = 'C:\\orandea\\test\\aaa'
542 > // to = 'C:\\orandea\\impl\\bbb'
543 > // The output of the function should be: '..\\..\\impl\\bbb'
544 > relative(from: string, to: string): string {
545 > validateString(from, 'from'); path.ts ×7
546 > validateString(to, 'to');
547 >
548 > if (from === to) {
549 > return '';
550 > }
551 >
552 > const fromOrig = win32.resolve(from);
553 > const toOrig = win32.resolve(to);
554 >
555 > if (fromOrig === toOrig) {
556 return '';
557 }
558 > path.ts ×7
559 > from = fromOrig.toLowerCase();
560 > to = toOrig.toLowerCase();
561 >
562 > if (from === to) {
563 > return '';
564 > }
565 >
566 > if (fromOrig.length !== from.length || toOrig.length !== to.length) {
567 const fromSplit = fromOrig.split('\\');
568 const toSplit = toOrig.split('\\');
569 if (fromSplit[fromSplit.length - 1] === '') {
570 fromSplit.pop();
571 }
572 if (toSplit[toSplit.length - 1] === '') {
573 toSplit.pop();
574 }
575
576 const fromLen = fromSplit.length;
577 const toLen = toSplit.length;
578 const length = fromLen < toLen ? fromLen : toLen;
579
580 let i;
581 for (i = 0; i < length; i++) {
582 if (fromSplit[i].toLowerCase() !== toSplit[i].toLowerCase()) {
583 break;
584 }
585 }
586
587 if (i === 0) {
588 return toOrig;
589 } else if (i === length) {
590 if (toLen > length) {
591 return toSplit.slice(i).join('\\');
592 }
593 if (fromLen > length) {
594 return '..\\'.repeat(fromLen - 1 - i) + '..';
595 }
596 return '';
597 }
598
599 return '..\\'.repeat(fromLen - i) + toSplit.slice(i).join('\\');
600 }
601 > path.ts ×7
602 > // Trim any leading backslashes
603 > let fromStart = 0;
604 > while (fromStart < from.length &&
605 > from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) {
606 > fromStart++;
607 > }
608 > // Trim trailing backslashes (applicable to UNC paths only)
609 > let fromEnd = from.length;
610 > while (fromEnd - 1 > fromStart &&
611 > from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) {
612 > fromEnd--;
613 > }
614 > const fromLen = fromEnd - fromStart;
615 >
616 > // Trim any leading backslashes
617 > let toStart = 0;
618 > while (toStart < to.length &&
619 > to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {
620 > toStart++;
621 > }
622 > // Trim trailing backslashes (applicable to UNC paths only)
623 > let toEnd = to.length;
624 > while (toEnd - 1 > toStart &&
625 > to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) {
626 > toEnd--;
627 > }
628 > const toLen = toEnd - toStart;
629 >
630 > // Compare paths to find the longest common path from root
631 > const length = fromLen < toLen ? fromLen : toLen;
632 > let lastCommonSep = -1;
633 > let i = 0;
634 > for (; i < length; i++) {
635 > const fromCode = from.charCodeAt(fromStart + i);
636 > if (fromCode !== to.charCodeAt(toStart + i)) {
637 > break;
638 > } else if (fromCode === CHAR_BACKWARD_SLASH) {
639 > lastCommonSep = i;
640 > }
641 > }
642 >
643 > // We found a mismatch before the first common path separator was seen, so
644 > // return the original `to`.
645 > if (i !== length) {
646 > if (lastCommonSep === -1) {
647 > return toOrig;
648 > }
649 > } else {
650 > if (toLen > length) {
651 > if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) {
652 > // We get here if `from` is the exact base path for `to`.
653 > // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz'
654 > return toOrig.slice(toStart + i + 1);
655 > }
656 > if (i === 2) {
657 // We get here if `from` is the device root.
658 // For example: from='C:\\'; to='C:\\foo'
659 return toOrig.slice(toStart + i);
660 }
661 > } path.ts ×7
662 > if (fromLen > length) {
663 > if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) {
664 > // We get here if `to` is the exact base path for `from`.
665 > // For example: from='C:\\foo\\bar'; to='C:\\foo'
666 > lastCommonSep = i;
667 > } else if (i === 2) {
668 // We get here if `to` is the device root.
669 // For example: from='C:\\foo\\bar'; to='C:\\'
670 lastCommonSep = 3;
671 }
672 > } path.ts ×7
673 > if (lastCommonSep === -1) {
674 lastCommonSep = 0;
675 }
676 > } path.ts ×7
677 >
678 > let out = '';
679 > // Generate the relative path based on the path difference between `to` and
680 > // `from`
681 > for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
682 > if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) {
683 > out += out.length === 0 ? '..' : '\\..';
684 > }
685 > }
686 >
687 > toStart += lastCommonSep;
688 >
689 > // Lastly, append the rest of the destination (`to`) path that comes after
690 > // the common path parts
691 > if (out.length > 0) {
692 > return `${out}${toOrig.slice(toStart, toEnd)}`;
693 > }
694
695 if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {
696 ++toStart;
697 }
698
699 return toOrig.slice(toStart, toEnd);
700 > }, path.ts ×7
701 > map.ts ×97
702 > toNamespacedPath(path: string): string {
703 // Note: this will *probably* throw somewhere.
704 if (typeof path !== 'string' || path.length === 0) {
705 return path;
706 }
707
708 const resolvedPath = win32.resolve(path);
709
710 if (resolvedPath.length <= 2) {
711 return path;
712 }
713
714 if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) {
715 // Possible UNC root
716 if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) {
717 const code = resolvedPath.charCodeAt(2);
718 if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) {
719 // Matched non-long UNC root, convert the path to a long UNC path
720 return `\\\\?\\UNC\\${resolvedPath.slice(2)}`;
721 }
722 }
723 } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) &&
724 resolvedPath.charCodeAt(1) === CHAR_COLON &&
725 resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH) {
726 // Matched device root, convert the path to a long UNC path
727 return `\\\\?\\${resolvedPath}`;
728 }
729
730 return resolvedPath;
731 },
732 > map.ts ×97
733 > dirname(path: string): string {
734 > validateString(path, 'path'); path.ts ×5
735 > const len = path.length;
736 > if (len === 0) {
737 > return '.'; path.ts ×6
738 > }
739 > let rootEnd = -1; path.ts ×5
740 > let offset = 0;
741 > const code = path.charCodeAt(0);
742 >
743 > if (len === 1) {
744 > // `path` contains just a path separator, exit early to avoid path.ts ×6
745 > // unnecessary work or a dot.
746 > return isPathSeparator(code) ? path : '.';
747 > }
748 > path.ts ×5
749 > // Try to match a root
750 > if (isPathSeparator(code)) {
751 > // Possible UNC root path.ts ×6
752 >
753 > rootEnd = offset = 1;
754 >
755 > if (isPathSeparator(path.charCodeAt(1))) {
756 > // Matched double path separator at beginning
757 > let j = 2;
758 > let last = j;
759 > // Match 1 or more non-path separators
760 > while (j < len && !isPathSeparator(path.charCodeAt(j))) {
761 > j++;
762 > }
763 > if (j < len && j !== last) {
764 > // Matched!
765 > last = j;
766 > // Match 1 or more path separators
767 > while (j < len && isPathSeparator(path.charCodeAt(j))) {
768 > j++;
769 > }
770 > if (j < len && j !== last) {
771 > // Matched!
772 > last = j;
773 > // Match 1 or more non-path separators
774 > while (j < len && !isPathSeparator(path.charCodeAt(j))) {
775 > j++;
776 > }
777 > if (j === len) {
778 > // We matched a UNC root only
779 > return path;
780 > }
781 > if (j !== last) {
782 > // We matched a UNC root with leftovers
783 >
784 > // Offset by 1 to include the separator after the UNC root to
785 > // treat it as a "normal root" on top of a (UNC) root
786 > rootEnd = offset = j + 1;
787 > }
788 > }
789 > }
790 > }
791 > // Possible device root
792 > } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { path.ts ×5
793 > rootEnd = len > 2 && isPathSeparator(path.charCodeAt(2)) ? 3 : 2;
794 > offset = rootEnd;
795 > }
796 >
797 > let end = -1;
798 > let matchedSlash = true;
799 > for (let i = len - 1; i >= offset; --i) {
800 > if (isPathSeparator(path.charCodeAt(i))) {
801 > if (!matchedSlash) {
802 > end = i;
803 > break;
804 > }
805 > } else {
806 > // We saw the first non-path separator
807 > matchedSlash = false;
808 > }
809 > }
810 >
811 > if (end === -1) {
812 > if (rootEnd === -1) { path.ts ×6
813 > return '.';
814 > }
815 >
816 > end = rootEnd;
817 > }
818 > return path.slice(0, end); path.ts ×5
819 > },
820 > map.ts ×97
821 > basename(path: string, suffix?: string): string {
822 > if (suffix !== undefined) { path.ts ×5
823 > validateString(suffix, 'suffix');
824 > }
825 > validateString(path, 'path');
826 > let start = 0;
827 > let end = -1;
828 > let matchedSlash = true;
829 > let i;
830 >
831 > // Check for a drive letter prefix so as not to mistake the following
832 > // path separator as an extra separator at the end of the path that can be
833 > // disregarded
834 > if (path.length >= 2 &&
835 > isWindowsDeviceRoot(path.charCodeAt(0)) &&
836 > path.charCodeAt(1) === CHAR_COLON) {
837 > start = 2;
838 > }
839 >
840 > if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {
841 > if (suffix === path) {
842 > return '';
843 > }
844 > let extIdx = suffix.length - 1;
845 > let firstNonSlashEnd = -1;
846 > for (i = path.length - 1; i >= start; --i) {
847 > const code = path.charCodeAt(i);
848 > if (isPathSeparator(code)) {
849 > // If we reached a path separator that was not part of a set of path
850 > // separators at the end of the string, stop now
851 > if (!matchedSlash) {
852 > start = i + 1;
853 > break;
854 > }
855 > } else {
856 > if (firstNonSlashEnd === -1) {
857 > // We saw the first non-path separator, remember this index in case
858 > // we need it if the extension ends up not matching
859 > matchedSlash = false;
860 > firstNonSlashEnd = i + 1;
861 > }
862 > if (extIdx >= 0) {
863 > // Try to match the explicit extension
864 > if (code === suffix.charCodeAt(extIdx)) {
865 > if (--extIdx === -1) {
866 > // We matched the extension, so mark this as the end of our path
867 > // component
868 > end = i;
869 > }
870 > } else {
871 // Extension does not match, so our result is the entire path
872 // component
873 extIdx = -1;
874 end = firstNonSlashEnd;
875 }
876 > } path.ts ×5
877 > }
878 > }
879 >
880 > if (start === end) {
881 > end = firstNonSlashEnd;
882 > } else if (end === -1) {
883 > end = path.length;
884 > }
885 > return path.slice(start, end);
886 > }
887 > for (i = path.length - 1; i >= start; --i) {
888 > if (isPathSeparator(path.charCodeAt(i))) {
889 > // If we reached a path separator that was not part of a set of path
890 > // separators at the end of the string, stop now
891 > if (!matchedSlash) {
892 > start = i + 1;
893 > break;
894 > }
895 > } else if (end === -1) {
896 > // We saw the first non-path separator, mark this as the end of our
897 > // path component
898 > matchedSlash = false;
899 > end = i + 1;
900 > }
901 > }
902 >
903 > if (end === -1) {
904 > return '';
905 > }
906 > return path.slice(start, end);
907 > },
908 > map.ts ×97
909 > extname(path: string): string {
910 > validateString(path, 'path'); path.ts ×3
911 > let start = 0;
912 > let startDot = -1;
913 > let startPart = 0;
914 > let end = -1;
915 > let matchedSlash = true;
916 > // Track the state of characters (if any) we see before our first dot and
917 > // after any path separator we find
918 > let preDotState = 0;
919 >
920 > // Check for a drive letter prefix so as not to mistake the following
921 > // path separator as an extra separator at the end of the path that can be
922 > // disregarded
923 >
924 > if (path.length >= 2 &&
925 > path.charCodeAt(1) === CHAR_COLON &&
926 > isWindowsDeviceRoot(path.charCodeAt(0))) {
927 > start = startPart = 2;
928 > }
929 >
930 > for (let i = path.length - 1; i >= start; --i) {
931 > const code = path.charCodeAt(i);
932 > if (isPathSeparator(code)) {
933 > // If we reached a path separator that was not part of a set of path
934 > // separators at the end of the string, stop now
935 > if (!matchedSlash) {
936 > startPart = i + 1;
937 > break;
938 > }
939 > continue;
940 > }
941 > if (end === -1) {
942 > // We saw the first non-path separator, mark this as the end of our
943 > // extension
944 > matchedSlash = false;
945 > end = i + 1;
946 > }
947 > if (code === CHAR_DOT) {
948 > // If this is our first dot, mark it as the start of our extension
949 > if (startDot === -1) {
950 > startDot = i;
951 > }
952 > else if (preDotState !== 1) {
953 > preDotState = 1;
954 > }
955 > } else if (startDot !== -1) {
956 > // We saw a non-dot and non-path separator before our dot, so we should
957 > // have a good chance at having a non-empty extension
958 > preDotState = -1;
959 > }
960 > }
961 >
962 > if (startDot === -1 ||
963 > end === -1 ||
964 > // We saw a non-dot character immediately before the dot
965 > preDotState === 0 ||
966 > // The (right-most) trimmed path component is exactly '..'
967 > (preDotState === 1 &&
968 > startDot === end - 1 &&
969 > startDot === startPart + 1)) {
970 > return '';
971 > }
972 > return path.slice(startDot, end);
973 > },
974 > map.ts ×97
975 > format: _format.bind(null, '\\'),
976 >
977 > parse(path) {
978 validateString(path, 'path');
979
980 const ret = { root: '', dir: '', base: '', ext: '', name: '' };
981 if (path.length === 0) {
982 return ret;
983 }
984
985 const len = path.length;
986 let rootEnd = 0;
987 let code = path.charCodeAt(0);
988
989 if (len === 1) {
990 if (isPathSeparator(code)) {
991 // `path` contains just a path separator, exit early to avoid
992 // unnecessary work
993 ret.root = ret.dir = path;
994 return ret;
995 }
996 ret.base = ret.name = path;
997 return ret;
998 }
999 // Try to match a root
1000 if (isPathSeparator(code)) {
1001 // Possible UNC root
1002
1003 rootEnd = 1;
1004 if (isPathSeparator(path.charCodeAt(1))) {
1005 // Matched double path separator at beginning
1006 let j = 2;
1007 let last = j;
1008 // Match 1 or more non-path separators
1009 while (j < len && !isPathSeparator(path.charCodeAt(j))) {
1010 j++;
1011 }
1012 if (j < len && j !== last) {
1013 // Matched!
1014 last = j;
1015 // Match 1 or more path separators
1016 while (j < len && isPathSeparator(path.charCodeAt(j))) {
1017 j++;
1018 }
1019 if (j < len && j !== last) {
1020 // Matched!
1021 last = j;
1022 // Match 1 or more non-path separators
1023 while (j < len && !isPathSeparator(path.charCodeAt(j))) {
1024 j++;
1025 }
1026 if (j === len) {
1027 // We matched a UNC root only
1028 rootEnd = j;
1029 } else if (j !== last) {
1030 // We matched a UNC root with leftovers
1031 rootEnd = j + 1;
1032 }
1033 }
1034 }
1035 }
1036 } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
1037 // Possible device root
1038 if (len <= 2) {
1039 // `path` contains just a drive root, exit early to avoid
1040 // unnecessary work
1041 ret.root = ret.dir = path;
1042 return ret;
1043 }
1044 rootEnd = 2;
1045 if (isPathSeparator(path.charCodeAt(2))) {
1046 if (len === 3) {
1047 // `path` contains just a drive root, exit early to avoid
1048 // unnecessary work
1049 ret.root = ret.dir = path;
1050 return ret;
1051 }
1052 rootEnd = 3;
1053 }
1054 }
1055 if (rootEnd > 0) {
1056 ret.root = path.slice(0, rootEnd);
1057 }
1058
1059 let startDot = -1;
1060 let startPart = rootEnd;
1061 let end = -1;
1062 let matchedSlash = true;
1063 let i = path.length - 1;
1064
1065 // Track the state of characters (if any) we see before our first dot and
1066 // after any path separator we find
1067 let preDotState = 0;
1068
1069 // Get non-dir info
1070 for (; i >= rootEnd; --i) {
1071 code = path.charCodeAt(i);
1072 if (isPathSeparator(code)) {
1073 // If we reached a path separator that was not part of a set of path
1074 // separators at the end of the string, stop now
1075 if (!matchedSlash) {
1076 startPart = i + 1;
1077 break;
1078 }
1079 continue;
1080 }
1081 if (end === -1) {
1082 // We saw the first non-path separator, mark this as the end of our
1083 // extension
1084 matchedSlash = false;
1085 end = i + 1;
1086 }
1087 if (code === CHAR_DOT) {
1088 // If this is our first dot, mark it as the start of our extension
1089 if (startDot === -1) {
1090 startDot = i;
1091 } else if (preDotState !== 1) {
1092 preDotState = 1;
1093 }
1094 } else if (startDot !== -1) {
1095 // We saw a non-dot and non-path separator before our dot, so we should
1096 // have a good chance at having a non-empty extension
1097 preDotState = -1;
1098 }
1099 }
1100
1101 if (end !== -1) {
1102 if (startDot === -1 ||
1103 // We saw a non-dot character immediately before the dot
1104 preDotState === 0 ||
1105 // The (right-most) trimmed path component is exactly '..'
1106 (preDotState === 1 &&
1107 startDot === end - 1 &&
1108 startDot === startPart + 1)) {
1109 ret.base = ret.name = path.slice(startPart, end);
1110 } else {
1111 ret.name = path.slice(startPart, startDot);
1112 ret.base = path.slice(startPart, end);
1113 ret.ext = path.slice(startDot, end);
1114 }
1115 }
1116
1117 // If the directory is the root, use the entire root as the `dir` including
1118 // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the
1119 // trailing slash (`C:\abc\def` -> `C:\abc`).
1120 if (startPart > 0 && startPart !== rootEnd) {
1121 ret.dir = path.slice(0, startPart - 1);
1122 } else {
1123 ret.dir = ret.root;
1124 }
1125
1126 return ret;
1127 },
1128 > map.ts ×97
1129 > sep: '\\',
1130 > delimiter: ';',
1131 > win32: null,
1132 > posix: null
1133 > };
1134 >
1135 > const posixCwd = (() => {
1136 > if (platformIsWin32) {
1137 // Converts Windows' backslash path separators to POSIX forward slashes
1138 // and truncates any drive indicator
1139 const regexp = /\\/g;
1140 return () => {
1141 const cwd = process.cwd().replace(regexp, '/');
1142 return cwd.slice(cwd.indexOf('/'));
1143 };
1144 }
1145 > map.ts ×97
1146 > // We're already on POSIX, no need for any transformations
1147 > return () => process.cwd();
1148 > })();
1149 >
1150 > export const posix: IPath = {
1151 > // path.resolve([from ...], to)
1152 > resolve(...pathSegments: string[]): string {
1153 > let resolvedPath = ''; path.ts ×3
1154 > let resolvedAbsolute = false;
1155 >
1156 > for (let i = pathSegments.length - 1; i >= 0 && !resolvedAbsolute; i--) {
1157 > const path = pathSegments[i];
1158 > validateString(path, `paths[${i}]`);
1159 >
1160 > // Skip empty entries
1161 > if (path.length === 0) {
1162 continue;
1163 }
1164 > path.ts ×3
1165 > resolvedPath = `${path}/${resolvedPath}`;
1166 > resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1167 > }
1168 >
1169 > if (!resolvedAbsolute) {
1170 > const cwd = posixCwd(); path.ts ×1
1171 > resolvedPath = `${cwd}/${resolvedPath}`;
1172 > resolvedAbsolute =
1173 > cwd.charCodeAt(0) === CHAR_FORWARD_SLASH;
1174 > }
1175 > path.ts ×3
1176 > // At this point the path should be resolved to a full absolute path, but
1177 > // handle relative paths to be safe (might happen when process.cwd() fails)
1178 >
1179 > // Normalize the path
1180 > resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/',
1181 > isPosixPathSeparator);
1182 >
1183 > if (resolvedAbsolute) {
1184 > return `/${resolvedPath}`;
1185 > }
1186 > return resolvedPath.length > 0 ? resolvedPath : '.';
1187 > },
1188 > map.ts ×97
1189 > normalize(path: string): string {
1190 > validateString(path, 'path'); path.ts ×3
1191 >
1192 > if (path.length === 0) {
1193 return '.';
1194 }
1195 > path.ts ×3
1196 > const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1197 > const trailingSeparator =
1198 > path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH;
1199 >
1200 > // Normalize the path
1201 > path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator);
1202 >
1203 > if (path.length === 0) {
1204 > if (isAbsolute) { path.ts ×1
1205 > return '/';
1206 > }
1207 > return trailingSeparator ? './' : '.';
1208 > }
1209 > if (trailingSeparator) { path.ts ×3
1210 > path += '/'; path.ts ×1
1211 > }
1212 > path.ts ×3
1213 > return isAbsolute ? `/${path}` : path; path.ts ×3
1214 > },
1215 > map.ts ×97
1216 > isAbsolute(path: string): boolean {
1217 > validateString(path, 'path'); path.ts ×1
1218 > return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1219 > },
1220 > map.ts ×97
1221 > join(...paths: string[]): string {
1222 > if (paths.length === 0) { path.ts ×3
1223 > return '.'; path.ts ×9
1224 > }
1225 > path.ts ×3
1226 > const path = [];
1227 > for (let i = 0; i < paths.length; ++i) {
1228 > const arg = paths[i];
1229 > validateString(arg, 'path');
1230 > if (arg.length > 0) {
1231 > path.push(arg);
1232 > }
1233 > }
1234 >
1235 > if (path.length === 0) {
1236 > return '.'; path.ts ×9
1237 > }
1238 > path.ts ×3
1239 > return posix.normalize(path.join('/'));
1240 > },
1241 > map.ts ×97
1242 > relative(from: string, to: string): string {
1243 > validateString(from, 'from'); path.ts ×3
1244 > validateString(to, 'to');
1245 >
1246 > if (from === to) {
1247 > return ''; path.ts ×1
1248 > }
1249 > path.ts ×3
1250 > // Trim leading forward slashes.
1251 > from = posix.resolve(from);
1252 > to = posix.resolve(to);
1253 >
1254 > if (from === to) {
1255 > return ''; path.ts ×2
1256 > }
1257 > path.ts ×3
1258 > const fromStart = 1;
1259 > const fromEnd = from.length;
1260 > const fromLen = fromEnd - fromStart;
1261 > const toStart = 1;
1262 > const toLen = to.length - toStart;
1263 >
1264 > // Compare paths to find the longest common path from root
1265 > const length = (fromLen < toLen ? fromLen : toLen); path.ts ×3
1266 > let lastCommonSep = -1;
1267 > let i = 0;
1268 > for (; i < length; i++) {
1269 > const fromCode = from.charCodeAt(fromStart + i); path.ts ×3
1270 > if (fromCode !== to.charCodeAt(toStart + i)) {
1271 > break; path.ts ×2
1272 > } else if (fromCode === CHAR_FORWARD_SLASH) { path.ts ×3
1273 > lastCommonSep = i; path.ts ×1
1274 > }
1275 > } path.ts ×3
1276 > if (i === length) { path.ts ×3
1277 > if (toLen > length) { path.ts ×3
1278 > if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) {
1279 > // We get here if `from` is the exact base path for `to`. path.ts ×1
1280 > // For example: from='/foo/bar'; to='/foo/bar/baz'
1281 > return to.slice(toStart + i + 1);
1282 > }
1283 > if (i === 0) { path.ts ×1
1284 > // We get here if `from` is the root
1285 > // For example: from='/'; to='/foo'
1286 > return to.slice(toStart + i);
1287 > }
1288 > } else if (fromLen > length) { path.ts ×3
1289 > if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { path.ts ×2
1290 > // We get here if `to` is the exact base path for `from`.
1291 > // For example: from='/foo/bar/baz'; to='/foo/bar'
1292 > lastCommonSep = i;
1293 > } else if (i === 0) {
1294 > // We get here if `to` is the root. path.ts ×2
1295 > // For example: from='/foo/bar'; to='/'
1296 > lastCommonSep = 0;
1297 > }
1298 > } path.ts ×2
1299 > } path.ts ×3
1300 > path.ts ×2
1301 > let out = '';
1302 > // Generate the relative path based on the path difference between `to`
1303 > // and `from`.
1304 > for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
1305 > if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) {
1306 > out += out.length === 0 ? '..' : '/..';
1307 > }
1308 > }
1309 >
1310 > // Lastly, append the rest of the destination (`to`) path that comes after
1311 > // the common path parts.
1312 > return `${out}${to.slice(toStart + lastCommonSep)}`;
1313 > }, path.ts ×3
1314 > map.ts ×97
1315 > toNamespacedPath(path: string): string {
1316 // Non-op on posix systems
1317 return path;
1318 },
1319 > map.ts ×97
1320 > dirname(path: string): string {
1321 > validateString(path, 'path'); path.ts ×5
1322 > if (path.length === 0) {
1323 > return '.'; path.ts ×6
1324 > }
1325 > const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; path.ts ×5
1326 > let end = -1;
1327 > let matchedSlash = true;
1328 > for (let i = path.length - 1; i >= 1; --i) {
1329 > if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
1330 > if (!matchedSlash) { path.ts ×2
1331 > end = i;
1332 > break;
1333 > }
1334 > } else { path.ts ×5
1335 > // We saw the first non-path separator
1336 > matchedSlash = false;
1337 > }
1338 > }
1339 >
1340 > if (end === -1) {
1341 > return hasRoot ? '/' : '.'; path.ts ×1
1342 > }
1343 > if (hasRoot && end === 1) { path.ts ×5
1344 > return '//'; path.ts ×6
1345 > }
1346 > return path.slice(0, end); path.ts ×2
1347 > }, path.ts ×5
1348 > map.ts ×97
1349 > basename(path: string, suffix?: string): string {
1350 > if (suffix !== undefined) { path.ts ×3
1351 > validateString(suffix, 'suffix'); path.ts ×1
1352 > }
1353 > validateString(path, 'path'); path.ts ×3
1354 >
1355 > let start = 0;
1356 > let end = -1;
1357 > let matchedSlash = true;
1358 > let i;
1359 >
1360 > if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {
1361 > if (suffix === path) { path.ts ×7
1362 > return ''; path.ts ×5
1363 > }
1364 > let extIdx = suffix.length - 1; path.ts ×7
1365 > let firstNonSlashEnd = -1;
1366 > for (i = path.length - 1; i >= 0; --i) {
1367 > const code = path.charCodeAt(i);
1368 > if (code === CHAR_FORWARD_SLASH) {
1369 > // If we reached a path separator that was not part of a set of path path.ts ×1
1370 > // separators at the end of the string, stop now
1371 > if (!matchedSlash) {
1372 > start = i + 1;
1373 > break;
1374 > }
1375 > } else { path.ts ×7
1376 > if (firstNonSlashEnd === -1) {
1377 > // We saw the first non-path separator, remember this index in case
1378 > // we need it if the extension ends up not matching
1379 > matchedSlash = false;
1380 > firstNonSlashEnd = i + 1;
1381 > }
1382 > if (extIdx >= 0) {
1383 > // Try to match the explicit extension
1384 > if (code === suffix.charCodeAt(extIdx)) {
1385 > if (--extIdx === -1) { path.ts ×1
1386 > // We matched the extension, so mark this as the end of our path
1387 > // component
1388 > end = i;
1389 > }
1390 > } else { path.ts ×7
1391 > // Extension does not match, so our result is the entire path path.ts ×1
1392 > // component
1393 > extIdx = -1;
1394 > end = firstNonSlashEnd;
1395 > }
1396 > } path.ts ×7
1397 > }
1398 > }
1399 >
1400 > if (start === end) {
1401 > end = firstNonSlashEnd; path.ts ×5
1402 > } else if (end === -1) { path.ts ×7
1403 > end = path.length; path.ts ×5
1404 > }
1405 > return path.slice(start, end); path.ts ×7
1406 > }
1407 > for (i = path.length - 1; i >= 0; --i) { path.ts ×2
1408 > if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { path.ts ×3
1409 > // If we reached a path separator that was not part of a set of path path.ts ×1
1410 > // separators at the end of the string, stop now
1411 > if (!matchedSlash) {
1412 > start = i + 1;
1413 > break;
1414 > }
1415 > } else if (end === -1) { path.ts ×3
1416 > // We saw the first non-path separator, mark this as the end of our
1417 > // path component
1418 > matchedSlash = false;
1419 > end = i + 1;
1420 > }
1421 > }
1422 > path.ts ×2
1423 > if (end === -1) {
1424 > return ''; path.ts ×1
1425 > }
1426 > return path.slice(start, end); path.ts ×3
1427 > }, path.ts ×3
1428 > map.ts ×97
1429 > extname(path: string): string {
1430 > validateString(path, 'path'); path.ts ×6
1431 > let startDot = -1;
1432 > let startPart = 0;
1433 > let end = -1;
1434 > let matchedSlash = true;
1435 > // Track the state of characters (if any) we see before our first dot and
1436 > // after any path separator we find
1437 > let preDotState = 0;
1438 > for (let i = path.length - 1; i >= 0; --i) {
1439 > const char = path[i];
1440 > if (char === '/') {
1441 > // If we reached a path separator that was not part of a set of path path.ts ×1
1442 > // separators at the end of the string, stop now
1443 > if (!matchedSlash) {
1444 > startPart = i + 1;
1445 > break;
1446 > }
1447 > continue; path.ts ×3
1448 > }
1449 > if (end === -1) { path.ts ×6
1450 > // We saw the first non-path separator, mark this as the end of our
1451 > // extension
1452 > matchedSlash = false;
1453 > end = i + 1;
1454 > }
1455 > if (char === '.') {
1456 > // If this is our first dot, mark it as the start of our extension path.ts ×4
1457 > if (startDot === -1) {
1458 > startDot = i;
1459 > }
1460 > else if (preDotState !== 1) { path.ts ×1
1461 > preDotState = 1;
1462 > }
1463 > } else if (startDot !== -1) { path.ts ×6
1464 > // We saw a non-dot and non-path separator before our dot, so we should path.ts ×4
1465 > // have a good chance at having a non-empty extension
1466 > preDotState = -1;
1467 > }
1468 > } path.ts ×6
1469 >
1470 > if (startDot === -1 ||
1471 > end === -1 || path.ts ×4
1472 > // We saw a non-dot character immediately before the dot
1473 > preDotState === 0 ||
1474 > // The (right-most) trimmed path component is exactly '..'
1475 > (preDotState === 1 &&
1476 > startDot === end - 1 && path.ts ×3
1477 > startDot === startPart + 1)) { path.ts ×6
1478 > return ''; path.ts ×1
1479 > }
1480 > return path.slice(startDot, end); path.ts ×4
1481 > }, path.ts ×6
1482 > map.ts ×97
1483 > format: _format.bind(null, '/'),
1484 >
1485 > parse(path: string): ParsedPath {
1486 validateString(path, 'path');
1487
1488 const ret = { root: '', dir: '', base: '', ext: '', name: '' };
1489 if (path.length === 0) {
1490 return ret;
1491 }
1492 const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1493 let start;
1494 if (isAbsolute) {
1495 ret.root = '/';
1496 start = 1;
1497 } else {
1498 start = 0;
1499 }
1500 let startDot = -1;
1501 let startPart = 0;
1502 let end = -1;
1503 let matchedSlash = true;
1504 let i = path.length - 1;
1505
1506 // Track the state of characters (if any) we see before our first dot and
1507 // after any path separator we find
1508 let preDotState = 0;
1509
1510 // Get non-dir info
1511 for (; i >= start; --i) {
1512 const code = path.charCodeAt(i);
1513 if (code === CHAR_FORWARD_SLASH) {
1514 // If we reached a path separator that was not part of a set of path
1515 // separators at the end of the string, stop now
1516 if (!matchedSlash) {
1517 startPart = i + 1;
1518 break;
1519 }
1520 continue;
1521 }
1522 if (end === -1) {
1523 // We saw the first non-path separator, mark this as the end of our
1524 // extension
1525 matchedSlash = false;
1526 end = i + 1;
1527 }
1528 if (code === CHAR_DOT) {
1529 // If this is our first dot, mark it as the start of our extension
1530 if (startDot === -1) {
1531 startDot = i;
1532 } else if (preDotState !== 1) {
1533 preDotState = 1;
1534 }
1535 } else if (startDot !== -1) {
1536 // We saw a non-dot and non-path separator before our dot, so we should
1537 // have a good chance at having a non-empty extension
1538 preDotState = -1;
1539 }
1540 }
1541
1542 if (end !== -1) {
1543 const start = startPart === 0 && isAbsolute ? 1 : startPart;
1544 if (startDot === -1 ||
1545 // We saw a non-dot character immediately before the dot
1546 preDotState === 0 ||
1547 // The (right-most) trimmed path component is exactly '..'
1548 (preDotState === 1 &&
1549 startDot === end - 1 &&
1550 startDot === startPart + 1)) {
1551 ret.base = ret.name = path.slice(start, end);
1552 } else {
1553 ret.name = path.slice(start, startDot);
1554 ret.base = path.slice(start, end);
1555 ret.ext = path.slice(startDot, end);
1556 }
1557 }
1558
1559 if (startPart > 0) {
1560 ret.dir = path.slice(0, startPart - 1);
1561 } else if (isAbsolute) {
1562 ret.dir = '/';
1563 }
1564
1565 return ret;
1566 },
1567 > map.ts ×97
1568 > sep: '/',
1569 > delimiter: ':',
1570 > win32: null,
1571 > posix: null
1572 > };
1573 >
1574 > posix.win32 = win32.win32 = win32;
1575 > posix.posix = win32.posix = posix;
1576 >
1577 > export const normalize = (platformIsWin32 ? win32.normalize : posix.normalize);
1578 > export const isAbsolute = (platformIsWin32 ? win32.isAbsolute : posix.isAbsolute);
1579 > export const join = (platformIsWin32 ? win32.join : posix.join);
1580 > export const resolve = (platformIsWin32 ? win32.resolve : posix.resolve);
1581 > export const relative = (platformIsWin32 ? win32.relative : posix.relative);
1582 > export const dirname = (platformIsWin32 ? win32.dirname : posix.dirname);
1583 > export const basename = (platformIsWin32 ? win32.basename : posix.basename);
1584 > export const extname = (platformIsWin32 ? win32.extname : posix.extname);
1585 > export const format = (platformIsWin32 ? win32.format : posix.format);
1586 > export const parse = (platformIsWin32 ? win32.parse : posix.parse);
1587 > export const toNamespacedPath = (platformIsWin32 ? win32.toNamespacedPath : posix.toNamespacedPath);
1588 > export const sep = (platformIsWin32 ? win32.sep : posix.sep);
1589 > export const delimiter = (platformIsWin32 ? win32.delimiter : posix.delimiter);