Skip to content

Commit a8cd44c

Browse files
authored
feat: link uncompressed in "index of" (#930)
Closes #844
1 parent 60ea0bb commit a8cd44c

File tree

1 file changed

+67
-6
lines changed

1 file changed

+67
-6
lines changed

lib/core/show-dir/index.js

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)