@@ -1500,44 +1500,75 @@ function fileURLToPath(path, options = kEmptyObject) {
1500
1500
return ( windows ?? isWindows ) ? getPathFromURLWin32 ( path ) : getPathFromURLPosix ( path ) ;
1501
1501
}
1502
1502
1503
- // The following characters are percent-encoded when converting from file path
1504
- // to URL:
1505
- // - %: The percent character is the only character not encoded by the
1506
- // `pathname` setter.
1507
- // - \: Backslash is encoded on non-windows platforms since it's a valid
1508
- // character but the `pathname` setters replaces it by a forward slash.
1509
- // - LF: The newline character is stripped out by the `pathname` setter.
1510
- // (See whatwg/url#419)
1511
- // - CR: The carriage return character is also stripped out by the `pathname`
1512
- // setter.
1513
- // - TAB: The tab character is also stripped out by the `pathname` setter.
1503
+ // RFC1738 defines the following chars as "unsafe" for URLs
1504
+ // @see https://www.ietf.org/rfc/rfc1738.txt 2.2. URL Character Encoding Issues
1514
1505
const percentRegEx = / % / g;
1515
- const backslashRegEx = / \\ / g;
1516
1506
const newlineRegEx = / \n / g;
1517
1507
const carriageReturnRegEx = / \r / g;
1518
1508
const tabRegEx = / \t / g;
1519
- const questionRegex = / \? / g;
1509
+ const quoteRegEx = / " / g;
1520
1510
const hashRegex = / # / g;
1511
+ const spaceRegEx = / / g;
1512
+ const questionMarkRegex = / \? / g;
1513
+ const openSquareBracketRegEx = / \[ / g;
1514
+ const backslashRegEx = / \\ / g;
1515
+ const closeSquareBracketRegEx = / ] / g;
1516
+ const caretRegEx = / \^ / g;
1517
+ const verticalBarRegEx = / \| / g;
1518
+ const tildeRegEx = / ~ / g;
1521
1519
1522
1520
function encodePathChars ( filepath , options = kEmptyObject ) {
1523
- const windows = options ?. windows ;
1524
- if ( StringPrototypeIndexOf ( filepath , '%' ) !== - 1 )
1521
+ if ( StringPrototypeIncludes ( filepath , '%' ) ) {
1525
1522
filepath = RegExpPrototypeSymbolReplace ( percentRegEx , filepath , '%25' ) ;
1526
- // In posix, backslash is a valid character in paths:
1527
- if ( ! ( windows ?? isWindows ) && StringPrototypeIndexOf ( filepath , '\\' ) !== - 1 )
1528
- filepath = RegExpPrototypeSymbolReplace ( backslashRegEx , filepath , '%5C' ) ;
1529
- if ( StringPrototypeIndexOf ( filepath , '\n' ) !== - 1 )
1523
+ }
1524
+
1525
+ if ( StringPrototypeIncludes ( filepath , '\t' ) ) {
1526
+ filepath = RegExpPrototypeSymbolReplace ( tabRegEx , filepath , '%09' ) ;
1527
+ }
1528
+ if ( StringPrototypeIncludes ( filepath , '\n' ) ) {
1530
1529
filepath = RegExpPrototypeSymbolReplace ( newlineRegEx , filepath , '%0A' ) ;
1531
- if ( StringPrototypeIndexOf ( filepath , '\r' ) !== - 1 )
1530
+ }
1531
+ if ( StringPrototypeIncludes ( filepath , '\r' ) ) {
1532
1532
filepath = RegExpPrototypeSymbolReplace ( carriageReturnRegEx , filepath , '%0D' ) ;
1533
- if ( StringPrototypeIndexOf ( filepath , '\t' ) !== - 1 )
1534
- filepath = RegExpPrototypeSymbolReplace ( tabRegEx , filepath , '%09' ) ;
1533
+ }
1534
+ if ( StringPrototypeIncludes ( filepath , ' ' ) ) {
1535
+ filepath = RegExpPrototypeSymbolReplace ( spaceRegEx , filepath , '%20' ) ;
1536
+ }
1537
+ if ( StringPrototypeIncludes ( filepath , '"' ) ) {
1538
+ filepath = RegExpPrototypeSymbolReplace ( quoteRegEx , filepath , '%22' ) ;
1539
+ }
1540
+ if ( StringPrototypeIncludes ( filepath , '#' ) ) {
1541
+ filepath = RegExpPrototypeSymbolReplace ( hashRegex , filepath , '%23' ) ;
1542
+ }
1543
+ if ( StringPrototypeIncludes ( filepath , '?' ) ) {
1544
+ filepath = RegExpPrototypeSymbolReplace ( questionMarkRegex , filepath , '%3F' ) ;
1545
+ }
1546
+ if ( StringPrototypeIncludes ( filepath , '[' ) ) {
1547
+ filepath = RegExpPrototypeSymbolReplace ( openSquareBracketRegEx , filepath , '%5B' ) ;
1548
+ }
1549
+ // Back-slashes must be special-cased on Windows, where they are treated as path separator.
1550
+ if ( ! options . windows && StringPrototypeIncludes ( filepath , '\\' ) ) {
1551
+ filepath = RegExpPrototypeSymbolReplace ( backslashRegEx , filepath , '%5C' ) ;
1552
+ }
1553
+ if ( StringPrototypeIncludes ( filepath , ']' ) ) {
1554
+ filepath = RegExpPrototypeSymbolReplace ( closeSquareBracketRegEx , filepath , '%5D' ) ;
1555
+ }
1556
+ if ( StringPrototypeIncludes ( filepath , '^' ) ) {
1557
+ filepath = RegExpPrototypeSymbolReplace ( caretRegEx , filepath , '%5E' ) ;
1558
+ }
1559
+ if ( StringPrototypeIncludes ( filepath , '|' ) ) {
1560
+ filepath = RegExpPrototypeSymbolReplace ( verticalBarRegEx , filepath , '%7C' ) ;
1561
+ }
1562
+ if ( StringPrototypeIncludes ( filepath , '~' ) ) {
1563
+ filepath = RegExpPrototypeSymbolReplace ( tildeRegEx , filepath , '%7E' ) ;
1564
+ }
1565
+
1535
1566
return filepath ;
1536
1567
}
1537
1568
1538
1569
function pathToFileURL ( filepath , options = kEmptyObject ) {
1539
- const windows = options ?. windows ;
1540
- if ( ( windows ?? isWindows ) && StringPrototypeStartsWith ( filepath , '\\\\' ) ) {
1570
+ const windows = options ?. windows ?? isWindows ;
1571
+ if ( windows && StringPrototypeStartsWith ( filepath , '\\\\' ) ) {
1541
1572
const outURL = new URL ( 'file://' ) ;
1542
1573
// UNC path format: \\server\share\resource
1543
1574
// Handle extended UNC path and standard UNC path
@@ -1568,7 +1599,7 @@ function pathToFileURL(filepath, options = kEmptyObject) {
1568
1599
) ;
1569
1600
return outURL ;
1570
1601
}
1571
- let resolved = ( windows ?? isWindows ) ? path . win32 . resolve ( filepath ) : path . posix . resolve ( filepath ) ;
1602
+ let resolved = windows ? path . win32 . resolve ( filepath ) : path . posix . resolve ( filepath ) ;
1572
1603
// path.resolve strips trailing slashes so we must add them back
1573
1604
const filePathLast = StringPrototypeCharCodeAt ( filepath ,
1574
1605
filepath . length - 1 ) ;
@@ -1577,18 +1608,7 @@ function pathToFileURL(filepath, options = kEmptyObject) {
1577
1608
resolved [ resolved . length - 1 ] !== path . sep )
1578
1609
resolved += '/' ;
1579
1610
1580
- // Call encodePathChars first to avoid encoding % again for ? and #.
1581
- resolved = encodePathChars ( resolved , { windows } ) ;
1582
-
1583
- // Question and hash character should be included in pathname.
1584
- // Therefore, encoding is required to eliminate parsing them in different states.
1585
- // This is done as an optimization to not creating a URL instance and
1586
- // later triggering pathname setter, which impacts performance
1587
- if ( StringPrototypeIndexOf ( resolved , '?' ) !== - 1 )
1588
- resolved = RegExpPrototypeSymbolReplace ( questionRegex , resolved , '%3F' ) ;
1589
- if ( StringPrototypeIndexOf ( resolved , '#' ) !== - 1 )
1590
- resolved = RegExpPrototypeSymbolReplace ( hashRegex , resolved , '%23' ) ;
1591
- return new URL ( `file://${ resolved } ` ) ;
1611
+ return new URL ( `file://${ encodePathChars ( resolved , { windows } ) } ` ) ;
1592
1612
}
1593
1613
1594
1614
function toPathIfFileURL ( fileURLOrPath ) {
0 commit comments