@@ -1414,25 +1414,27 @@ const backslashRegEx = /\\/g;
1414
1414
const newlineRegEx = / \n / g;
1415
1415
const carriageReturnRegEx = / \r / g;
1416
1416
const tabRegEx = / \t / g;
1417
+ const questionRegex = / \? / g;
1418
+ const hashRegex = / # / g;
1417
1419
1418
1420
function encodePathChars ( filepath ) {
1419
- if ( StringPrototypeIncludes ( filepath , '%' ) )
1421
+ if ( StringPrototypeIndexOf ( filepath , '%' ) !== - 1 )
1420
1422
filepath = RegExpPrototypeSymbolReplace ( percentRegEx , filepath , '%25' ) ;
1421
1423
// In posix, backslash is a valid character in paths:
1422
- if ( ! isWindows && StringPrototypeIncludes ( filepath , '\\' ) )
1424
+ if ( ! isWindows && StringPrototypeIndexOf ( filepath , '\\' ) !== - 1 )
1423
1425
filepath = RegExpPrototypeSymbolReplace ( backslashRegEx , filepath , '%5C' ) ;
1424
- if ( StringPrototypeIncludes ( filepath , '\n' ) )
1426
+ if ( StringPrototypeIndexOf ( filepath , '\n' ) !== - 1 )
1425
1427
filepath = RegExpPrototypeSymbolReplace ( newlineRegEx , filepath , '%0A' ) ;
1426
- if ( StringPrototypeIncludes ( filepath , '\r' ) )
1428
+ if ( StringPrototypeIndexOf ( filepath , '\r' ) !== - 1 )
1427
1429
filepath = RegExpPrototypeSymbolReplace ( carriageReturnRegEx , filepath , '%0D' ) ;
1428
- if ( StringPrototypeIncludes ( filepath , '\t' ) )
1430
+ if ( StringPrototypeIndexOf ( filepath , '\t' ) !== - 1 )
1429
1431
filepath = RegExpPrototypeSymbolReplace ( tabRegEx , filepath , '%09' ) ;
1430
1432
return filepath ;
1431
1433
}
1432
1434
1433
1435
function pathToFileURL ( filepath ) {
1434
- const outURL = new URL ( 'file://' ) ;
1435
1436
if ( isWindows && StringPrototypeStartsWith ( filepath , '\\\\' ) ) {
1437
+ const outURL = new URL ( 'file://' ) ;
1436
1438
// UNC path format: \\server\share\resource
1437
1439
const hostnameEndIndex = StringPrototypeIndexOf ( filepath , '\\' , 2 ) ;
1438
1440
if ( hostnameEndIndex === - 1 ) {
@@ -1453,18 +1455,29 @@ function pathToFileURL(filepath) {
1453
1455
outURL . hostname = domainToASCII ( hostname ) ;
1454
1456
outURL . pathname = encodePathChars (
1455
1457
RegExpPrototypeSymbolReplace ( backslashRegEx , StringPrototypeSlice ( filepath , hostnameEndIndex ) , '/' ) ) ;
1456
- } else {
1457
- let resolved = path . resolve ( filepath ) ;
1458
- // path.resolve strips trailing slashes so we must add them back
1459
- const filePathLast = StringPrototypeCharCodeAt ( filepath ,
1460
- filepath . length - 1 ) ;
1461
- if ( ( filePathLast === CHAR_FORWARD_SLASH ||
1462
- ( isWindows && filePathLast === CHAR_BACKWARD_SLASH ) ) &&
1463
- resolved [ resolved . length - 1 ] !== path . sep )
1464
- resolved += '/' ;
1465
- outURL . pathname = encodePathChars ( resolved ) ;
1466
- }
1467
- return outURL ;
1458
+ return outURL ;
1459
+ }
1460
+ let resolved = path . resolve ( filepath ) ;
1461
+ // path.resolve strips trailing slashes so we must add them back
1462
+ const filePathLast = StringPrototypeCharCodeAt ( filepath ,
1463
+ filepath . length - 1 ) ;
1464
+ if ( ( filePathLast === CHAR_FORWARD_SLASH ||
1465
+ ( isWindows && filePathLast === CHAR_BACKWARD_SLASH ) ) &&
1466
+ resolved [ resolved . length - 1 ] !== path . sep )
1467
+ resolved += '/' ;
1468
+
1469
+ // Call encodePathChars first to avoid encoding % again for ? and #.
1470
+ resolved = encodePathChars ( resolved ) ;
1471
+
1472
+ // Question and hash character should be included in pathname.
1473
+ // Therefore, encoding is required to eliminate parsing them in different states.
1474
+ // This is done as an optimization to not creating a URL instance and
1475
+ // later triggering pathname setter, which impacts performance
1476
+ if ( StringPrototypeIndexOf ( resolved , '?' ) !== - 1 )
1477
+ resolved = RegExpPrototypeSymbolReplace ( questionRegex , resolved , '%3F' ) ;
1478
+ if ( StringPrototypeIndexOf ( resolved , '#' ) !== - 1 )
1479
+ resolved = RegExpPrototypeSymbolReplace ( hashRegex , resolved , '%23' ) ;
1480
+ return new URL ( `file://${ resolved } ` ) ;
1468
1481
}
1469
1482
1470
1483
function toPathIfFileURL ( fileURLOrPath ) {
0 commit comments