@@ -1496,25 +1496,27 @@ const backslashRegEx = /\\/g; |
1496 | 1496 | const newlineRegEx = /\n/g; |
1497 | 1497 | const carriageReturnRegEx = /\r/g; |
1498 | 1498 | const tabRegEx = /\t/g; |
| 1499 | +const questionRegex = /\?/g; |
| 1500 | +const hashRegex = /#/g; |
1499 | 1501 |
|
1500 | 1502 | function encodePathChars(filepath) { |
1501 | | - if (StringPrototypeIncludes(filepath, '%')) |
| 1503 | + if (StringPrototypeIndexOf(filepath, '%') !== -1) |
1502 | 1504 | filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); |
1503 | 1505 | // In posix, backslash is a valid character in paths: |
1504 | | - if (!isWindows && StringPrototypeIncludes(filepath, '\\')) |
| 1506 | + if (!isWindows && StringPrototypeIndexOf(filepath, '\\') !== -1) |
1505 | 1507 | filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); |
1506 | | - if (StringPrototypeIncludes(filepath, '\n')) |
| 1508 | + if (StringPrototypeIndexOf(filepath, '\n') !== -1) |
1507 | 1509 | filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); |
1508 | | - if (StringPrototypeIncludes(filepath, '\r')) |
| 1510 | + if (StringPrototypeIndexOf(filepath, '\r') !== -1) |
1509 | 1511 | filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); |
1510 | | - if (StringPrototypeIncludes(filepath, '\t')) |
| 1512 | + if (StringPrototypeIndexOf(filepath, '\t') !== -1) |
1511 | 1513 | filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); |
1512 | 1514 | return filepath; |
1513 | 1515 | } |
1514 | 1516 |
|
1515 | 1517 | function pathToFileURL(filepath) { |
1516 | | - const outURL = new URL('file://'); |
1517 | 1518 | if (isWindows && StringPrototypeStartsWith(filepath, '\\\\')) { |
| 1519 | + const outURL = new URL('file://'); |
1518 | 1520 | // UNC path format: \\server\share\resource |
1519 | 1521 | const hostnameEndIndex = StringPrototypeIndexOf(filepath, '\\', 2); |
1520 | 1522 | if (hostnameEndIndex === -1) { |
@@ -1535,18 +1537,29 @@ function pathToFileURL(filepath) { |
1535 | 1537 | outURL.hostname = domainToASCII(hostname); |
1536 | 1538 | outURL.pathname = encodePathChars( |
1537 | 1539 | RegExpPrototypeSymbolReplace(backslashRegEx, StringPrototypeSlice(filepath, hostnameEndIndex), '/')); |
1538 | | - } else { |
1539 | | - let resolved = path.resolve(filepath); |
1540 | | - // path.resolve strips trailing slashes so we must add them back |
1541 | | - const filePathLast = StringPrototypeCharCodeAt(filepath, |
1542 | | - filepath.length - 1); |
1543 | | - if ((filePathLast === CHAR_FORWARD_SLASH || |
1544 | | - (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) && |
1545 | | - resolved[resolved.length - 1] !== path.sep) |
1546 | | - resolved += '/'; |
1547 | | - outURL.pathname = encodePathChars(resolved); |
| 1540 | + return outURL; |
1548 | 1541 | } |
1549 | | - return outURL; |
| 1542 | + let resolved = path.resolve(filepath); |
| 1543 | + // path.resolve strips trailing slashes so we must add them back |
| 1544 | + const filePathLast = StringPrototypeCharCodeAt(filepath, |
| 1545 | + filepath.length - 1); |
| 1546 | + if ((filePathLast === CHAR_FORWARD_SLASH || |
| 1547 | + (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) && |
| 1548 | + resolved[resolved.length - 1] !== path.sep) |
| 1549 | + resolved += '/'; |
| 1550 | + |
| 1551 | + // Call encodePathChars first to avoid encoding % again for ? and #. |
| 1552 | + resolved = encodePathChars(resolved); |
| 1553 | + |
| 1554 | + // Question and hash character should be included in pathname. |
| 1555 | + // Therefore, encoding is required to eliminate parsing them in different states. |
| 1556 | + // This is done as an optimization to not creating a URL instance and |
| 1557 | + // later triggering pathname setter, which impacts performance |
| 1558 | + if (StringPrototypeIndexOf(resolved, '?') !== -1) |
| 1559 | + resolved = RegExpPrototypeSymbolReplace(questionRegex, resolved, '%3F'); |
| 1560 | + if (StringPrototypeIndexOf(resolved, '#') !== -1) |
| 1561 | + resolved = RegExpPrototypeSymbolReplace(hashRegex, resolved, '%23'); |
| 1562 | + return new URL(`file://${resolved}`); |
1550 | 1563 | } |
1551 | 1564 |
|
1552 | 1565 | function toPathIfFileURL(fileURLOrPath) { |
|