Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 67 additions & 6 deletions lib/core/show-dir/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,50 @@ module.exports = (opts) => {
res.setHeader('etag', etag(stat, weakEtags));
res.setHeader('last-modified', (new Date(stat.mtime)).toUTCString());
res.setHeader('cache-control', cache);

// A step before render() is called to gives items additional
// information so that render() can deliver the best user experience
// possible.
function process(dirs, renderFiles, lolwuts) {
const filenamesThatExist = new Set();

// Putting filenames in a set first keeps us in O(n) time complexity
for (let i=0; i < renderFiles.length; i++) {
const [name, stat] = renderFiles[i];
filenamesThatExist.add(name);
const renderOptions = {};
renderFiles[i] = [name, stat, renderOptions];
}

// Set render options for compressed files
for (const [name, _stat, renderOptions] of renderFiles) {
if (
opts.brotli &&
! opts.forceContentEncoding &&
name.endsWith('.br')
) {
const uncompressedName = name.slice(0, -'.br'.length);
if (filenamesThatExist.has(uncompressedName)) {
continue;
}
renderOptions.uncompressedName = uncompressedName;
}
}
for (const [name, _stat, renderOptions] of renderFiles) {
if (
opts.gzip &&
! opts.forceContentEncoding &&
name.endsWith('.gz')
) {
const uncompressedName = name.slice(0, -'.gz'.length);
if (filenamesThatExist.has(uncompressedName)) {
continue;
}
renderOptions.uncompressedName = uncompressedName;
}
}
render(dirs, renderFiles, lolwuts);
}

function render(dirs, renderFiles, lolwuts) {
// each entry in the array is a [name, stat] tuple
Expand All @@ -94,7 +138,7 @@ module.exports = (opts) => {

const failed = false;
const writeRow = (file) => {
// render a row given a [name, stat] tuple
// render a row given a [name, stat, renderOptions] tuple
const isDir = file[1].isDirectory && file[1].isDirectory();
let href = `./${encodeURIComponent(file[0])}`;

Expand All @@ -103,7 +147,24 @@ module.exports = (opts) => {
href += `/${he.encode((parsed.search) ? parsed.search : '')}`;
}

const displayName = he.encode(file[0]) + ((isDir) ? '/' : '');
// Handle compressed files with uncompressed names
let displayNameHTML;
let fileSize = sizeToString(file[1], humanReadable, si);

if (file[2] && file[2].uncompressedName) {
// This is a compressed file, show both names with separate links
const uncompressedName = he.encode(file[2].uncompressedName);
const compressedName = he.encode(file[0]);
const uncompressedHref = `./${encodeURIComponent(file[2].uncompressedName)}`;
const asterisk = `<span title="served from compressed file">*</span>`;
displayNameHTML = `<a href="${uncompressedHref}">${uncompressedName}</a>` +
`${asterisk} (<a href="${href}">${compressedName}</a>)`;
fileSize += '*';
} else {
// Regular file or directory
displayNameHTML = `<a href="${href}">${he.encode(file[0]) + ((isDir) ? '/' : '')}</a>`;
}

const ext = file[0].split('.').pop();
const classForNonDir = supportedIcons[ext] ? ext : '_page';
const iconClass = `icon-${isDir ? '_blank' : classForNonDir}`;
Expand All @@ -116,8 +177,8 @@ module.exports = (opts) => {
}
html +=
`<td class="last-modified">${lastModifiedToString(file[1])}</td>` +
`<td class="file-size"><code>${sizeToString(file[1], humanReadable, si)}</code></td>` +
`<td class="display-name"><a href="${href}">${displayName}</a></td>` +
`<td class="file-size"><code>${fileSize}</code></td>` +
`<td class="display-name">${displayNameHTML}</td>` +
'</tr>\n';
};

Expand Down Expand Up @@ -161,10 +222,10 @@ module.exports = (opts) => {
return;
}
dirs.unshift(['..', s]);
render(dirs, sortedFiles, lolwuts);
process(dirs, sortedFiles, lolwuts);
});
} else {
render(dirs, sortedFiles, lolwuts);
process(dirs, sortedFiles, lolwuts);
}
});
});
Expand Down
Loading