feat: live timer since rebuild

This commit is contained in:
venus
2026-10-20 17:01:05 -05:00
parent c8cd5fd0d9
commit 07de3b2782
6 changed files with 103 additions and 0 deletions
+6
View File
@@ -6,6 +6,12 @@ import { postComparator, isPostPublic } from "./src/_utils/sort-posts.js";
import { validateLinkedArticles } from "./src/_utils/linked-articles.js"; import { validateLinkedArticles } from "./src/_utils/linked-articles.js";
export default function (eleventyConfig) { export default function (eleventyConfig) {
let buildTimestamp = new Date().toISOString();
eleventyConfig.on("eleventy.before", () => {
buildTimestamp = new Date().toISOString();
});
eleventyConfig.addGlobalData("buildTimestamp", () => buildTimestamp);
// Build ignored local post fixtures without making them Git-tracked. // Build ignored local post fixtures without making them Git-tracked.
eleventyConfig.setUseGitIgnore(false); eleventyConfig.setUseGitIgnore(false);
+1
View File
@@ -29,6 +29,7 @@
{% include "partials/footer.njk" %} {% include "partials/footer.njk" %}
</div> </div>
<script type="module" src="/assets/js/build-timer.js"></script>
{% block scripts %}{% endblock %} {% block scripts %}{% endblock %}
</body> </body>
</html> </html>
+2
View File
@@ -4,6 +4,8 @@
<span>RUNTIME: ELEVENTY 3.x STATIC</span> <span>RUNTIME: ELEVENTY 3.x STATIC</span>
<span>&bull;</span> <span>&bull;</span>
<span>THEME: NATIVE TERMINAL DARK</span> <span>THEME: NATIVE TERMINAL DARK</span>
<span>&bull;</span>
<time id="build-age" data-build-timestamp="{{ buildTimestamp }}" aria-live="polite">REBUILT: INITIALIZING</time>
</div> </div>
<div class="footer-right"> <div class="footer-right">
+30
View File
@@ -0,0 +1,30 @@
export const formatBuildAge = (buildTimestamp, nowTimestamp = new Date().toISOString()) => {
const buildTime = Date.parse(buildTimestamp);
const now = Date.parse(nowTimestamp);
if (!Number.isFinite(buildTime) || !Number.isFinite(now)) return "unknown";
const elapsedSeconds = Math.max(0, Math.floor((now - buildTime) / 1000));
const days = Math.floor(elapsedSeconds / 86400);
const hours = Math.floor((elapsedSeconds % 86400) / 3600);
const minutes = Math.floor((elapsedSeconds % 3600) / 60);
const seconds = elapsedSeconds % 60;
if (days > 0) return `${days}d ${hours}h ago`;
if (hours > 0) return `${hours}h ${minutes}m ago`;
if (minutes > 0) return `${minutes}m ${seconds}s ago`;
return `${seconds}s ago`;
};
if (typeof document !== "undefined") {
const timer = document.getElementById("build-age");
if (timer) {
const updateBuildAge = () => {
timer.textContent = `REBUILT: ${formatBuildAge(timer.dataset.buildTimestamp)}`;
};
updateBuildAge();
window.setInterval(updateBuildAge, 1000);
}
}
+62
View File
@@ -0,0 +1,62 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import configureEleventy from "../eleventy.config.js";
import { formatBuildAge } from "../src/assets/js/build-timer.js";
test("Build age: formats elapsed time as a compact live footer value", () => {
assert.equal(
formatBuildAge("2026-10-20T12:00:00.000Z", "2026-10-20T12:00:00.000Z"),
"0s ago"
);
assert.equal(
formatBuildAge("2026-10-20T12:00:00.000Z", "2026-10-20T12:01:01.000Z"),
"1m 1s ago"
);
assert.equal(
formatBuildAge("2026-10-20T12:00:00.000Z", "2026-10-21T14:03:04.000Z"),
"1d 2h ago"
);
assert.equal(
formatBuildAge("2026-10-20T12:00:00.000Z", "2026-10-20T11:59:59.000Z"),
"0s ago"
);
});
test("Build age: Eleventy exposes one ISO timestamp for each rebuild", () => {
const globalData = new Map();
const events = new Map();
configureEleventy({
setUseGitIgnore() {},
addPassthroughCopy() {},
addPlugin() {},
setLibrary() {},
addFilter() {},
addCollection() {},
addGlobalData(name, value) {
globalData.set(name, value);
},
on(name, callback) {
events.set(name, callback);
},
setServerOptions() {}
});
events.get("eleventy.before")();
assert.match(
globalData.get("buildTimestamp")(),
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
);
});
test("Build age: footer provides an accessible live timer backed by the build timestamp", () => {
const footer = fs.readFileSync(path.resolve("src/_includes/partials/footer.njk"), "utf-8");
const baseLayout = fs.readFileSync(path.resolve("src/_includes/layouts/base.njk"), "utf-8");
assert.match(
footer,
/<time id="build-age" data-build-timestamp="{{ buildTimestamp }}" aria-live="polite">REBUILT: INITIALIZING<\/time>/
);
assert.match(baseLayout, /<script type="module" src="\/assets\/js\/build-timer\.js"><\/script>/);
});
+2
View File
@@ -95,6 +95,8 @@ test("Linked articles: the Eleventy posts collection rejects invalid frontmatter
addCollection(name, callback) { addCollection(name, callback) {
collections.set(name, callback); collections.set(name, callback);
}, },
addGlobalData() {},
on() {},
setServerOptions() {} setServerOptions() {}
}); });