diff --git a/package.json b/package.json
index 69abefe..0200b60 100644
--- a/package.json
+++ b/package.json
@@ -5,8 +5,8 @@
"type": "module",
"scripts": {
"start": "wrangler dev --remote",
- "format": "prettier --check --write \"**/*.{ts,js,json,md,hbs}\"",
- "prettier": "prettier --check \"**/*.{ts,js,json,md,hbs}\"",
+ "format": "prettier --check --write \"**/*.{ts,js,json,md}\"",
+ "prettier": "prettier --check \"**/*.{ts,js,json,md}\"",
"lint": "eslint ./src",
"test:unit": "node --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout --loader=tsx ./tests/unit/index.test.ts",
"test:e2e": "wrangler deploy --dry-run --outdir=dist && node --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout --loader=tsx ./tests/e2e/index.test.ts",
diff --git a/scripts/compile-handlebars.js b/scripts/compile-handlebars.js
index 076ccd0..7758efc 100644
--- a/scripts/compile-handlebars.js
+++ b/scripts/compile-handlebars.js
@@ -36,9 +36,7 @@ templatesToParse.then(files => {
// The Handlebars.precompile returns a JavaScript object
// and then we make a default export of the object
- const javascriptTemplate = `export default ${removeStringIndents(
- compiledTemplate
- )}`;
+ const javascriptTemplate = `export default ${compiledTemplate}`;
const outputFilename = filePath.replace('.hbs', '.out.js');
diff --git a/src/handlers/strategies/directoryListing.ts b/src/handlers/strategies/directoryListing.ts
index 34a7933..3eaa002 100644
--- a/src/handlers/strategies/directoryListing.ts
+++ b/src/handlers/strategies/directoryListing.ts
@@ -17,6 +17,20 @@ import { S3_MAX_KEYS, S3_RETRY_LIMIT } from '../../constants/limits';
// Applies the Template into a Handlebars Template Function
const handleBarsTemplate = Handlebars.template(htmlTemplate);
+const months = [
+ 'Jan',
+ 'Feb',
+ 'Mar',
+ 'Apr',
+ 'Jun',
+ 'Jul',
+ 'Aug',
+ 'Sep',
+ 'Oct',
+ 'Nov',
+ 'Dec',
+];
+
/**
* @TODO: Simplify the iteration logic or make it more readable
*
@@ -35,17 +49,7 @@ export function renderDirectoryListing(
env: Env
): Response {
// Holds the contents of the listing (directories and files)
- const tableElements = [];
-
- // There shouldn't really be a case where we're listing the root
- // directory (/) when this is deployed, so always add the option
- // to go up a directory
- tableElements.push({
- href: '../',
- name: '../',
- lastModified: '-',
- size: '-',
- });
+ const tableElements: object[] = [];
const urlPathname = `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}`;
@@ -53,16 +57,26 @@ export function renderDirectoryListing(
delimitedPrefixes.forEach(name => {
const extra = encodeURIComponent(name.substring(0, name.length - 1));
+ let displayName: string;
+ let displayNamePaddingRight: string = ''; // hate this
+ if (name.length > 50) {
+ displayName = name.substring(0, 49) + '>';
+ } else {
+ displayName = name;
+ displayNamePaddingRight = ' '.repeat(50 - name.length);
+ }
+
tableElements.push({
- href: `${urlPathname}${extra}/`,
- name,
- lastModified: '-',
- size: '-',
+ href: `${extra}/`,
+ displayNamePaddingRight,
+ name: displayName,
+ lastModified: ' -',
+ size: ' -',
});
});
// Last time any of the files within the directory got modified
- let lastModified: Date | undefined = undefined;
+ let directoryLastModified: Date | undefined = undefined;
// Renders all the Files within the Directory
objects.forEach(object => {
@@ -71,20 +85,35 @@ export function renderDirectoryListing(
// Find the most recent date a file in this
// directory was modified, we'll use it
// in the `Last-Modified` header
- if (lastModified === undefined || object.LastModified! > lastModified) {
- lastModified = object.LastModified!;
+ if (
+ directoryLastModified === undefined ||
+ object.LastModified! > directoryLastModified
+ ) {
+ directoryLastModified = object.LastModified!;
}
- let dateStr = object.LastModified!.toISOString();
+ const lastModified = object.LastModified!;
+ const dateStr = `${lastModified.getUTCDay()}-${months.at(
+ lastModified.getUTCMonth()
+ )}-${lastModified.getUTCFullYear()} ${lastModified.getUTCHours()}:${lastModified.getUTCMinutes()}`;
+
+ let displayName: string = '';
+ let displayNamePaddingRight: string = ''; // hate this
+ if (name!.length > 50) {
+ displayName = name!.substring(0, 47) + '..>';
+ } else {
+ displayName = name!;
+ displayNamePaddingRight = ' '.repeat(50 - name!.length);
+ }
- dateStr = dateStr.split('.')[0].replace('T', ' ');
- dateStr = dateStr.slice(0, dateStr.lastIndexOf(':')) + 'Z';
+ const bytes = niceBytes(object.Size!);
tableElements.push({
href: `${urlPathname}${encodeURIComponent(name ?? '')}`,
- name,
+ name: displayName,
+ displayNamePaddingRight,
lastModified: dateStr,
- size: niceBytes(object.Size!),
+ size: ' '.repeat(20 - bytes.length) + bytes,
});
});
@@ -95,11 +124,13 @@ export function renderDirectoryListing(
});
// Gets an UTC-string on the ISO-8901 format of last modified date
- const lastModifiedUTC = (lastModified ?? new Date()).toUTCString();
+ const directoryLastModifiedUtc = (
+ directoryLastModified ?? new Date()
+ ).toUTCString();
return new Response(request.method === 'GET' ? renderedListing : null, {
headers: {
- 'last-modified': lastModifiedUTC,
+ 'last-modified': directoryLastModifiedUtc,
'content-type': 'text/html',
'cache-control': env.DIRECTORY_CACHE_CONTROL || 'no-store',
},
diff --git a/src/templates/directoryListing.hbs b/src/templates/directoryListing.hbs
index b9a7e4c..60c8888 100644
--- a/src/templates/directoryListing.hbs
+++ b/src/templates/directoryListing.hbs
@@ -1,31 +1,11 @@
-
- Index of {{pathname}}
-
-
-
-
-
- Index of {{pathname}}
-
-
- Filename |
- Modified |
- Size |
-
- {{#each entries}}
-
- {{name}} |
- {{lastModified}} |
- {{size}} |
-
- {{/each}}
-
-
+Index of {{pathname}}
+
+Index of {{pathname}}
../
+{{#each entries}}
+{{name}}{{displayNamePaddingRight}}
+{{lastModified}}
+{{size}}
+{{/each}}
+
\ No newline at end of file
diff --git a/src/templates/directoryListing.out.js b/src/templates/directoryListing.out.js
index ec461bf..9772bed 100644
--- a/src/templates/directoryListing.out.js
+++ b/src/templates/directoryListing.out.js
@@ -1 +1 @@
-export default{1:function(t,e,n,l,a){var o,i=t.strict,r=t.lambda;return""+(null!=(o=r(i(e,"name",{start:{line:24,column:35},end:{line:24,column:39}}),e))?o:"")+" | "+(null!=(o=r(i(e,"lastModified",{start:{line:25,column:16},end:{line:25,column:28}}),e))?o:"")+" | "+(null!=(o=r(i(e,"size",{start:{line:26,column:16},end:{line:26,column:20}}),e))?o:"")+" |
"},compiler:[8,">= 4.3.0"],main:function(t,e,n,l,a){var o,i=t.strict,r=t.lambda,c=t.lookupProperty||function(t,e){if(Object.prototype.hasOwnProperty.call(t,e))return t[e]};return"Index of "+(null!=(o=r(i(e,"pathname",{start:{line:3,column:22},end:{line:3,column:30}}),e))?o:"")+"Index of "+(null!=(o=r(i(e,"pathname",{start:{line:15,column:19},end:{line:15,column:27}}),e))?o:"")+"
Filename | Modified | Size |
"+(null!=(o=c(n,"each").call(null!=e?e:t.nullContext||{},c(e,"entries"),{name:"each",hash:{},fn:t.program(1,a,0),inverse:t.noop,data:a,loc:{start:{line:22,column:6},end:{line:28,column:15}}}))?o:"")+"
"},useData:!0};
+export default{1:function(n,l,e,t,a){var r,o=n.strict,u=n.lambda;return""+(null!=(r=u(o(l,"name",{start:{line:6,column:21},end:{line:6,column:25}}),l))?r:"")+""+(null!=(r=u(o(l,"displayNamePaddingRight",{start:{line:6,column:33},end:{line:6,column:56}}),l))?r:"")+" "+(null!=(r=u(o(l,"lastModified",{start:{line:6,column:61},end:{line:6,column:73}}),l))?r:"")+" "+(null!=(r=u(o(l,"size",{start:{line:6,column:78},end:{line:6,column:82}}),l))?r:"")+"\r\n"},compiler:[8,">= 4.3.0"],main:function(n,l,e,t,a){var r,o=n.strict,u=n.lambda,i=n.lookupProperty||function(n,l){if(Object.prototype.hasOwnProperty.call(n,l))return n[l]};return"\r\nIndex of "+(null!=(r=u(o(l,"pathname",{start:{line:2,column:24},end:{line:2,column:32}}),l))?r:"")+"\r\n\r\nIndex of "+(null!=(r=u(o(l,"pathname",{start:{line:4,column:15},end:{line:4,column:23}}),l))?r:"")+"
../\r\n"+(null!=(r=i(e,"each").call(null!=l?l:n.nullContext||{},i(l,"entries"),{name:"each",hash:{},fn:n.program(1,a,0),inverse:n.noop,data:a,loc:{start:{line:5,column:0},end:{line:7,column:9}}}))?r:"")+"
\r\n"},useData:!0};
diff --git a/tests/e2e/directory.test.ts b/tests/e2e/directory.test.ts
index 71889aa..61f852c 100644
--- a/tests/e2e/directory.test.ts
+++ b/tests/e2e/directory.test.ts
@@ -1,6 +1,6 @@
import { after, before, describe, it } from 'node:test';
import assert from 'node:assert';
-import { readFileSync } from 'node:fs';
+import { readFileSync, writeFileSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import http from 'http';
import { Miniflare } from 'miniflare';
@@ -93,7 +93,10 @@ describe('Directory Tests (Restricted Directory Listing)', () => {
// it'll pass for the other listings and therefore
// don't need to test it over and over again
const body = await res.text();
- assert.strictEqual(body, expectedHtml.replaceAll('\n', ''));
+ assert.strictEqual(
+ body.replaceAll('\r', ''),
+ expectedHtml.replaceAll('\r', '')
+ );
});
it('allows `/dist/`', async () => {
diff --git a/tests/e2e/test-data/expected-html/dist.txt b/tests/e2e/test-data/expected-html/dist.txt
index 0909a6b..815770c 100644
--- a/tests/e2e/test-data/expected-html/dist.txt
+++ b/tests/e2e/test-data/expected-html/dist.txt
@@ -1 +1,8 @@
-Index of /dist/Index of /dist/
\ No newline at end of file
+
+Index of /dist/
+
+Index of /dist/
../
+latest/ - -
+index.json 2-Oct-2023 5:43 18 B
+
+
\ No newline at end of file