@@ -73,6 +73,50 @@ module.exports = (opts) => {
73
73
res . setHeader ( 'etag' , etag ( stat , weakEtags ) ) ;
74
74
res . setHeader ( 'last-modified' , ( new Date ( stat . mtime ) ) . toUTCString ( ) ) ;
75
75
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
+ }
76
120
77
121
function render ( dirs , renderFiles , lolwuts ) {
78
122
// each entry in the array is a [name, stat] tuple
@@ -94,7 +138,7 @@ module.exports = (opts) => {
94
138
95
139
const failed = false ;
96
140
const writeRow = ( file ) => {
97
- // render a row given a [name, stat] tuple
141
+ // render a row given a [name, stat, renderOptions ] tuple
98
142
const isDir = file [ 1 ] . isDirectory && file [ 1 ] . isDirectory ( ) ;
99
143
let href = `./${ encodeURIComponent ( file [ 0 ] ) } ` ;
100
144
@@ -103,7 +147,24 @@ module.exports = (opts) => {
103
147
href += `/${ he . encode ( ( parsed . search ) ? parsed . search : '' ) } ` ;
104
148
}
105
149
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
+
107
168
const ext = file [ 0 ] . split ( '.' ) . pop ( ) ;
108
169
const classForNonDir = supportedIcons [ ext ] ? ext : '_page' ;
109
170
const iconClass = `icon-${ isDir ? '_blank' : classForNonDir } ` ;
@@ -116,8 +177,8 @@ module.exports = (opts) => {
116
177
}
117
178
html +=
118
179
`<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>` +
121
182
'</tr>\n' ;
122
183
} ;
123
184
@@ -161,10 +222,10 @@ module.exports = (opts) => {
161
222
return ;
162
223
}
163
224
dirs . unshift ( [ '..' , s ] ) ;
164
- render ( dirs , sortedFiles , lolwuts ) ;
225
+ process ( dirs , sortedFiles , lolwuts ) ;
165
226
} ) ;
166
227
} else {
167
- render ( dirs , sortedFiles , lolwuts ) ;
228
+ process ( dirs , sortedFiles , lolwuts ) ;
168
229
}
169
230
} ) ;
170
231
} ) ;
0 commit comments