@@ -1414,6 +1414,8 @@ const backslashRegEx = /\\/g;
14141414const newlineRegEx = / \n / g;
14151415const carriageReturnRegEx = / \r / g;
14161416const tabRegEx = / \t / g;
1417+ const questionRegex = / \? / ;
1418+ const hashRegex = / # / ;
14171419
14181420function encodePathChars ( filepath ) {
14191421 if ( StringPrototypeIncludes ( filepath , '%' ) )
@@ -1431,8 +1433,8 @@ function encodePathChars(filepath) {
14311433}
14321434
14331435function pathToFileURL ( filepath ) {
1434- const outURL = new URL ( 'file://' ) ;
14351436 if ( isWindows && StringPrototypeStartsWith ( filepath , '\\\\' ) ) {
1437+ const outURL = new URL ( 'file://' ) ;
14361438 // UNC path format: \\server\share\resource
14371439 const hostnameEndIndex = StringPrototypeIndexOf ( filepath , '\\' , 2 ) ;
14381440 if ( hostnameEndIndex === - 1 ) {
@@ -1453,18 +1455,29 @@ function pathToFileURL(filepath) {
14531455 outURL . hostname = domainToASCII ( hostname ) ;
14541456 outURL . pathname = encodePathChars (
14551457 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 ( StringPrototypeIncludes ( resolved , '?' ) )
1477+ resolved = RegExpPrototypeSymbolReplace ( questionRegex , resolved , '%3F' ) ;
1478+ if ( StringPrototypeIncludes ( resolved , '#' ) )
1479+ resolved = RegExpPrototypeSymbolReplace ( hashRegex , resolved , '%23' ) ;
1480+ return new URL ( `file://${ resolved } ` ) ;
14681481}
14691482
14701483function toPathIfFileURL ( fileURLOrPath ) {
0 commit comments