@@ -82,9 +82,7 @@ class ResourceMapper {
8282
8383 // Determine the URL by chopping off everything after the dollar sign
8484 const pathname = this . _removeDollarExtension ( path )
85- const url = `${ this . resolveUrl ( hostname ) } ${
86- pathname . split ( '/' ) . map ( ( component ) => encodeURIComponent ( component ) ) . join ( '/' )
87- } `
85+ const url = `${ this . resolveUrl ( hostname ) } ${ this . _encodePath ( pathname ) } `
8886 return { url, contentType : this . _getContentTypeFromExtension ( path ) }
8987 }
9088
@@ -95,7 +93,7 @@ class ResourceMapper {
9593 contentType = contentType ? contentType . replace ( / \s * ; .* / , '' ) : ''
9694 // Parse the URL and find the base file path
9795 const { pathname, hostname } = this . _parseUrl ( url )
98- const filePath = this . resolveFilePath ( hostname , decodeURIComponent ( pathname ) )
96+ const filePath = this . resolveFilePath ( hostname , this . _decodePath ( pathname ) )
9997 if ( filePath . indexOf ( '/..' ) >= 0 ) {
10098 throw new Error ( 'Disallowed /.. segment in URL' )
10199 }
@@ -149,6 +147,25 @@ class ResourceMapper {
149147 return { path, contentType : contentType || this . _defaultContentType }
150148 }
151149
150+ // encode/decode path except slash/%encodedSlash/double%encodedSlash
151+ _exceptSlash ( ) { return / ( \/ | % 2 f | % 2 F | % 2 5 2 F | % 2 5 2 f ) / g }
152+
153+ _encodePath ( pathname ) {
154+ const pathArray = pathname . split ( this . _exceptSlash ( ) )
155+ pathArray . forEach ( ( el , i ) => {
156+ if ( i % 2 === 0 ) pathArray [ i ] = encodeURIComponent ( el )
157+ } )
158+ return pathArray . join ( '' )
159+ }
160+
161+ _decodePath ( pathname ) {
162+ const pathArray = pathname . split ( this . _exceptSlash ( ) )
163+ pathArray . forEach ( ( el , i ) => {
164+ if ( i % 2 === 0 ) pathArray [ i ] = decodeURIComponent ( el )
165+ } )
166+ return pathArray . join ( '' )
167+ }
168+
152169 // Parses a URL into hostname and pathname
153170 _parseUrl ( url ) {
154171 // URL specified as string
0 commit comments