@@ -73,6 +73,50 @@ module.exports = (opts) => {
7373 res . setHeader ( 'etag' , etag ( stat , weakEtags ) ) ;
7474 res . setHeader ( 'last-modified' , ( new Date ( stat . mtime ) ) . toUTCString ( ) ) ;
7575 res . setHeader ( 'cache-control' , cache ) ;
76+
77+ // A step before render() is called to gives items additional
78+ // information so that render() can deliver the best user experience
79+ // possible.
80+ function process ( dirs , renderFiles , lolwuts ) {
81+ const filenamesThatExist = new Set ( ) ;
82+
83+ // Putting filenames in a set first keeps us in O(n) time complexity
84+ for ( let i = 0 ; i < renderFiles . length ; i ++ ) {
85+ const [ name , stat ] = renderFiles [ i ] ;
86+ filenamesThatExist . add ( name ) ;
87+ const renderOptions = { } ;
88+ renderFiles [ i ] = [ name , stat , renderOptions ] ;
89+ }
90+
91+ // Set render options for compressed files
92+ for ( const [ name , _stat , renderOptions ] of renderFiles ) {
93+ if (
94+ opts . brotli &&
95+ ! opts . forceContentEncoding &&
96+ name . endsWith ( '.br' )
97+ ) {
98+ const uncompressedName = name . slice ( 0 , - '.br' . length ) ;
99+ if ( filenamesThatExist . has ( uncompressedName ) ) {
100+ continue ;
101+ }
102+ renderOptions . uncompressedName = uncompressedName ;
103+ }
104+ }
105+ for ( const [ name , _stat , renderOptions ] of renderFiles ) {
106+ if (
107+ opts . gzip &&
108+ ! opts . forceContentEncoding &&
109+ name . endsWith ( '.gz' )
110+ ) {
111+ const uncompressedName = name . slice ( 0 , - '.gz' . length ) ;
112+ if ( filenamesThatExist . has ( uncompressedName ) ) {
113+ continue ;
114+ }
115+ renderOptions . uncompressedName = uncompressedName ;
116+ }
117+ }
118+ render ( dirs , renderFiles , lolwuts ) ;
119+ }
76120
77121 function render ( dirs , renderFiles , lolwuts ) {
78122 // each entry in the array is a [name, stat] tuple
@@ -94,7 +138,7 @@ module.exports = (opts) => {
94138
95139 const failed = false ;
96140 const writeRow = ( file ) => {
97- // render a row given a [name, stat] tuple
141+ // render a row given a [name, stat, renderOptions ] tuple
98142 const isDir = file [ 1 ] . isDirectory && file [ 1 ] . isDirectory ( ) ;
99143 let href = `./${ encodeURIComponent ( file [ 0 ] ) } ` ;
100144
@@ -103,7 +147,24 @@ module.exports = (opts) => {
103147 href += `/${ he . encode ( ( parsed . search ) ? parsed . search : '' ) } ` ;
104148 }
105149
106- const displayName = he . encode ( file [ 0 ] ) + ( ( isDir ) ? '/' : '' ) ;
150+ // Handle compressed files with uncompressed names
151+ let displayNameHTML ;
152+ let fileSize = sizeToString ( file [ 1 ] , humanReadable , si ) ;
153+
154+ if ( file [ 2 ] && file [ 2 ] . uncompressedName ) {
155+ // This is a compressed file, show both names with separate links
156+ const uncompressedName = he . encode ( file [ 2 ] . uncompressedName ) ;
157+ const compressedName = he . encode ( file [ 0 ] ) ;
158+ const uncompressedHref = `./${ encodeURIComponent ( file [ 2 ] . uncompressedName ) } ` ;
159+ const asterisk = `<span title="served from compressed file">*</span>` ;
160+ displayNameHTML = `<a href="${ uncompressedHref } ">${ uncompressedName } </a>` +
161+ `${ asterisk } (<a href="${ href } ">${ compressedName } </a>)` ;
162+ fileSize += '*' ;
163+ } else {
164+ // Regular file or directory
165+ displayNameHTML = `<a href="${ href } ">${ he . encode ( file [ 0 ] ) + ( ( isDir ) ? '/' : '' ) } </a>` ;
166+ }
167+
107168 const ext = file [ 0 ] . split ( '.' ) . pop ( ) ;
108169 const classForNonDir = supportedIcons [ ext ] ? ext : '_page' ;
109170 const iconClass = `icon-${ isDir ? '_blank' : classForNonDir } ` ;
@@ -116,8 +177,8 @@ module.exports = (opts) => {
116177 }
117178 html +=
118179 `<td class="last-modified">${ lastModifiedToString ( file [ 1 ] ) } </td>` +
119- `<td class="file-size"><code>${ sizeToString ( file [ 1 ] , humanReadable , si ) } </code></td>` +
120- `<td class="display-name"><a href=" ${ href } "> ${ displayName } </a> </td>` +
180+ `<td class="file-size"><code>${ fileSize } </code></td>` +
181+ `<td class="display-name">${ displayNameHTML } </td>` +
121182 '</tr>\n' ;
122183 } ;
123184
@@ -161,10 +222,10 @@ module.exports = (opts) => {
161222 return ;
162223 }
163224 dirs . unshift ( [ '..' , s ] ) ;
164- render ( dirs , sortedFiles , lolwuts ) ;
225+ process ( dirs , sortedFiles , lolwuts ) ;
165226 } ) ;
166227 } else {
167- render ( dirs , sortedFiles , lolwuts ) ;
228+ process ( dirs , sortedFiles , lolwuts ) ;
168229 }
169230 } ) ;
170231 } ) ;
0 commit comments