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