@@ -321,6 +321,7 @@ const absolutify = makeCacheableWithContext(_absolutify);
321321const PATH_QUERY_FRAGMENT_REGEXP =
322322 / ^ ( (?: \0 .| [ ^ ? # \0 ] ) * ) ( \? (?: \0 .| [ ^ # \0 ] ) * ) ? ( # .* ) ? $ / ;
323323const PATH_QUERY_REGEXP = / ^ ( (?: \0 .| [ ^ ? \0 ] ) * ) ( \? .* ) ? $ / ;
324+ const ZERO_ESCAPE_REGEXP = / \0 ( .) / g;
324325
325326/** @typedef {{ resource: string, path: string, query: string, fragment: string } } ParsedResource */
326327/** @typedef {{ resource: string, path: string, query: string } } ParsedResourceWithoutFragment */
@@ -330,15 +331,56 @@ const PATH_QUERY_REGEXP = /^((?:\0.|[^?\0])*)(\?.*)?$/;
330331 * @returns {ParsedResource } parsed parts
331332 */
332333const _parseResource = ( str ) => {
333- const match =
334- /** @type {[string, string, string | undefined, string | undefined] } */
335- ( /** @type {unknown } */ ( PATH_QUERY_FRAGMENT_REGEXP . exec ( str ) ) ) ;
336- return {
337- resource : str ,
338- path : match [ 1 ] . replace ( / \0 ( .) / g, "$1" ) ,
339- query : match [ 2 ] ? match [ 2 ] . replace ( / \0 ( .) / g, "$1" ) : "" ,
340- fragment : match [ 3 ] || ""
341- } ;
334+ const firstEscape = str . indexOf ( "\0" ) ;
335+
336+ // Handle `\0`
337+ if ( firstEscape !== - 1 ) {
338+ const match =
339+ /** @type {[string, string, string | undefined, string | undefined] } */
340+ ( /** @type {unknown } */ ( PATH_QUERY_FRAGMENT_REGEXP . exec ( str ) ) ) ;
341+
342+ return {
343+ resource : str ,
344+ path : match [ 1 ] . replace ( ZERO_ESCAPE_REGEXP , "$1" ) ,
345+ query : match [ 2 ] ? match [ 2 ] . replace ( ZERO_ESCAPE_REGEXP , "$1" ) : "" ,
346+ fragment : match [ 3 ] || ""
347+ } ;
348+ }
349+
350+ /** @type {ParsedResource } */
351+ const result = { resource : str , path : "" , query : "" , fragment : "" } ;
352+ const queryStart = str . indexOf ( "?" ) ;
353+ const fragmentStart = str . indexOf ( "#" ) ;
354+
355+ if ( fragmentStart < 0 ) {
356+ if ( queryStart < 0 ) {
357+ result . path = result . resource ;
358+
359+ // No fragment, no query
360+ return result ;
361+ }
362+
363+ result . path = str . slice ( 0 , queryStart ) ;
364+ result . query = str . slice ( queryStart ) ;
365+
366+ // Query, no fragment
367+ return result ;
368+ }
369+
370+ if ( queryStart < 0 || fragmentStart < queryStart ) {
371+ result . path = str . slice ( 0 , fragmentStart ) ;
372+ result . fragment = str . slice ( fragmentStart ) ;
373+
374+ // Fragment, no query
375+ return result ;
376+ }
377+
378+ result . path = str . slice ( 0 , queryStart ) ;
379+ result . query = str . slice ( queryStart , fragmentStart ) ;
380+ result . fragment = str . slice ( fragmentStart ) ;
381+
382+ // Query and fragment
383+ return result ;
342384} ;
343385
344386/**
@@ -347,14 +389,37 @@ const _parseResource = (str) => {
347389 * @returns {ParsedResourceWithoutFragment } parsed parts
348390 */
349391const _parseResourceWithoutFragment = ( str ) => {
350- const match =
351- /** @type {[string, string, string | undefined] } */
352- ( /** @type {unknown } */ ( PATH_QUERY_REGEXP . exec ( str ) ) ) ;
353- return {
354- resource : str ,
355- path : match [ 1 ] . replace ( / \0 ( .) / g, "$1" ) ,
356- query : match [ 2 ] ? match [ 2 ] . replace ( / \0 ( .) / g, "$1" ) : ""
357- } ;
392+ const firstEscape = str . indexOf ( "\0" ) ;
393+
394+ // Handle `\0`
395+ if ( firstEscape !== - 1 ) {
396+ const match =
397+ /** @type {[string, string, string | undefined] } */
398+ ( /** @type {unknown } */ ( PATH_QUERY_REGEXP . exec ( str ) ) ) ;
399+
400+ return {
401+ resource : str ,
402+ path : match [ 1 ] . replace ( ZERO_ESCAPE_REGEXP , "$1" ) ,
403+ query : match [ 2 ] ? match [ 2 ] . replace ( ZERO_ESCAPE_REGEXP , "$1" ) : ""
404+ } ;
405+ }
406+
407+ /** @type {ParsedResourceWithoutFragment } */
408+ const result = { resource : str , path : "" , query : "" } ;
409+ const queryStart = str . indexOf ( "?" ) ;
410+
411+ if ( queryStart < 0 ) {
412+ result . path = result . resource ;
413+
414+ // No query
415+ return result ;
416+ }
417+
418+ result . path = str . slice ( 0 , queryStart ) ;
419+ result . query = str . slice ( queryStart ) ;
420+
421+ // Query
422+ return result ;
358423} ;
359424
360425/**
0 commit comments