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