diff --git a/tasks/aws-s3-builds-page/fileList.hbs b/tasks/aws-s3-builds-page/fileList.hbs
new file mode 100644
index 00000000..265f3248
--- /dev/null
+++ b/tasks/aws-s3-builds-page/fileList.hbs
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+ Handlebars.js Builds
+
+
+ Handlebars.js builds
+ See https://handlebarsjs.com for documentation.
+ Machine-readable version: {{jsonListUrl}}
+
+
+
+ | Name |
+ Size |
+ Last-Modified |
+
+
+
+ {{#each fileList as | file |}}
+
+ | {{key}} |
+ {{file.size}} |
+ {{file.lastModified}} |
+
+ {{/each}}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tasks/aws-s3-builds-page/generateFileList-test.js b/tasks/aws-s3-builds-page/generateFileList-test.js
index e3cf3d70..9d2cd794 100644
--- a/tasks/aws-s3-builds-page/generateFileList-test.js
+++ b/tasks/aws-s3-builds-page/generateFileList-test.js
@@ -13,12 +13,18 @@ const s3Client = createS3Client();
runTest(async ({ log }) => {
log('Generate file list');
- const filename = `test-file-list${crypto.randomUUID()}`;
+ const filename = `test-file-list-${crypto.randomUUID()}`;
await generateFileList(filename);
log(`Checking JSON at ${s3Client.fileUrl(`${filename}.json`)}`);
const jsonList = JSON.parse(await s3Client.fetchFile(`${filename}.json`));
- assert(jsonList.includes('handlebars-v4.7.7.js'));
+ assert(jsonList.find(s3obj => s3obj.key === 'handlebars-v4.7.7.js'));
+
+ log(`Checking HTML at ${s3Client.fileUrl(`${filename}.html`)}`);
+ const htmlList = await s3Client.fetchFile(`${filename}.html`);
+ assert(htmlList.includes('handlebars-v4.7.7.js'));
+ assert(htmlList.includes('handlebarsjs.com'));
+ assert(!htmlList.includes('index.html'));
log(`Deleting file ${filename}.json`);
await s3Client.deleteFile(`${filename}.json`);
diff --git a/tasks/aws-s3-builds-page/generateFileList.js b/tasks/aws-s3-builds-page/generateFileList.js
index 49cceb50..efe38302 100644
--- a/tasks/aws-s3-builds-page/generateFileList.js
+++ b/tasks/aws-s3-builds-page/generateFileList.js
@@ -1,13 +1,39 @@
/* eslint-disable no-console */
const { createS3Client } = require('./s3client');
+const Handlebars = require('../..');
+const fs = require('node:fs/promises');
+const path = require('path');
async function generateFileList(nameWithoutExtension) {
const s3Client = createS3Client();
const fileList = await s3Client.listFiles();
+ const relevantFiles = fileList.filter(s3obj => s3obj.key.endsWith('.js'));
+
+ await uploadJson(s3Client, relevantFiles, nameWithoutExtension);
+ await uploadHtml(s3Client, relevantFiles, nameWithoutExtension);
+}
+
+async function uploadJson(s3Client, fileList, nameWithoutExtension) {
const fileListJson = JSON.stringify(fileList, null, 2);
await s3Client.uploadData(fileListJson, nameWithoutExtension + '.json', {
contentType: 'application/json'
});
}
+async function uploadHtml(s3Client, fileList, nameWithoutExtension) {
+ const templateStr = await fs.readFile(
+ path.join(__dirname, 'fileList.hbs'),
+ 'utf-8'
+ );
+ const template = Handlebars.compile(templateStr);
+ Handlebars.registerHelper('json', obj => JSON.stringify(obj));
+ const fileListHtml = template({
+ fileList,
+ jsonListUrl: nameWithoutExtension + '.json'
+ });
+ await s3Client.uploadData(fileListHtml, nameWithoutExtension + '.html', {
+ contentType: 'text/html'
+ });
+}
+
module.exports = { generateFileList };
diff --git a/tasks/aws-s3-builds-page/s3client/listFiles.js b/tasks/aws-s3-builds-page/s3client/listFiles.js
index 8796e2ed..922332ae 100644
--- a/tasks/aws-s3-builds-page/s3client/listFiles.js
+++ b/tasks/aws-s3-builds-page/s3client/listFiles.js
@@ -14,11 +14,19 @@ async function listFiles(s3Client, bucket) {
IsTruncated,
NextContinuationToken
} = await s3Client.send(command);
- files.push(...Contents.map(s3obj => s3obj.Key));
+ files.push(...Contents.map(dataFromS3Object));
isTruncated = IsTruncated;
command.input.ContinuationToken = NextContinuationToken;
}
return files;
}
+function dataFromS3Object(s3obj) {
+ return {
+ key: s3obj.Key,
+ size: s3obj.Size,
+ lastModified: s3obj.LastModified.toISOString()
+ };
+}
+
module.exports = { listFiles };
diff --git a/tasks/aws-s3-builds-page/s3client/s3-test.js b/tasks/aws-s3-builds-page/s3client/s3-test.js
index 58e5448b..6a0a442b 100644
--- a/tasks/aws-s3-builds-page/s3client/s3-test.js
+++ b/tasks/aws-s3-builds-page/s3client/s3-test.js
@@ -2,6 +2,7 @@
const { createS3Client } = require('./index');
const crypto = require('crypto');
const { runTest } = require('../test-utils/runTest');
+const assert = require('node:assert');
// This is a test file. It is intended to be run manually
// with the proper environment variables set
@@ -12,6 +13,8 @@ const { runTest } = require('../test-utils/runTest');
const client = createS3Client();
+const ISO_DATE = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
+
runTest(async ({ log }) => {
const uuid = crypto.randomUUID();
const filename = `test-file-${uuid}`;
@@ -22,7 +25,7 @@ runTest(async ({ log }) => {
log(`Check if uploaded "${filename}"`);
const listing = await client.listFiles();
- if (!listing.includes(filename)) {
+ if (!listing.find(s3obj => s3obj.key === filename)) {
throw new Error(`File "${filename}" has not been uploaded`);
}
@@ -46,13 +49,24 @@ runTest(async ({ log }) => {
await expectContentType(filename, 'text/plain');
log(`Check contents of "${filename}"`);
expectStringContains('Hello world', await client.fetchFile(filename));
+ const helloWorldObj = (await client.listFiles()).find(
+ s3obj => s3obj.key === filename
+ );
+ assert.equal(helloWorldObj.size, 11, 'Checking file size of hello world');
+ assert.match(
+ helloWorldObj.lastModified,
+ ISO_DATE,
+ 'Last modified must be an iso-date'
+ );
log(`Delete "${filename}"`);
await client.deleteFile(filename);
log(`Check if deleted "${filename}"`);
- const listingAfterDelete = await client.listFiles();
- if (listingAfterDelete.includes(filename)) {
+ const foundFile = (await client.listFiles()).find(
+ s3obj => s3obj.key === filename
+ );
+ if (foundFile != null) {
throw new Error(`File "${filename}" has not been deleted`);
}
});
diff --git a/tasks/aws-s3-builds-page/test-utils/runTest.js b/tasks/aws-s3-builds-page/test-utils/runTest.js
index 1c94718a..21963039 100644
--- a/tasks/aws-s3-builds-page/test-utils/runTest.js
+++ b/tasks/aws-s3-builds-page/test-utils/runTest.js
@@ -18,8 +18,10 @@ function runTest(asyncFn) {
async function detectSurplusFiles() {
const listing = await s3Client.listFiles();
let surplusFileDetected = false;
- const testFilesInBucket = listing.filter(name => name.includes('test-file'));
- for (const filename of testFilesInBucket) {
+ const testFilesInBucket = listing.filter(name =>
+ name.key.includes('test-file')
+ );
+ for (const { key: filename } of testFilesInBucket) {
if (process.argv[2] === '--delete-surplus') {
await s3Client.deleteFile(filename);
} else {
diff --git a/tasks/publish-to-aws.js b/tasks/publish-to-aws.js
index 522ef0f8..ff7037c6 100644
--- a/tasks/publish-to-aws.js
+++ b/tasks/publish-to-aws.js
@@ -2,6 +2,7 @@ const git = require('./util/git');
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
const semver = require('semver');
const { publishWithSuffixes } = require('./aws-s3-builds-page/publish');
+const { generateFileList } = require('./aws-s3-builds-page/generateFileList');
module.exports = function(grunt) {
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);
@@ -28,6 +29,7 @@ module.exports = function(grunt) {
if (suffixes.length > 0) {
await publishWithSuffixes(suffixes);
+ await generateFileList('index');
}
});
};