From 3e7a53f69f9077796a9faf86978c42068a875362 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 19:04:11 +0000 Subject: [PATCH 1/2] feat: Add published date to non-post pages This change adds a "Published [Date]" footer to all pages that are not posts (e.g., home page, 404 page), excluding the site index. The date displayed is the latest of either the frontmatter date or the file's last modified date, formatted as "DD Mon YYYY". - Added `latestDate` async filter in `.eleventy.js` to compare and return the latest date. - Updated `_includes/layouts/base.njk` to include the date display logic for non-post pages. --- .eleventy.js | 34 ++++++++++++++++++++++++++++++++++ _includes/layouts/base.njk | 2 ++ package-lock.json | 3 +++ 3 files changed, 39 insertions(+) diff --git a/.eleventy.js b/.eleventy.js index 5597bec..554ce28 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -142,6 +142,40 @@ module.exports = function (eleventyConfig) { } ); + eleventyConfig.addNunjucksAsyncFilter( + "latestDate", + function (dateObj, filename, callback) { + const call = (result) => { + result.then((modifiedDate) => { + let latest = dateObj; + if (modifiedDate instanceof Date && !isNaN(modifiedDate)) { + // Compare timestamps + if (latest instanceof Date && !isNaN(latest)) { + if (modifiedDate.getTime() > latest.getTime()) { + latest = modifiedDate; + } + } else { + latest = modifiedDate; + } + } + callback(null, latest); + }); + result.catch((error) => { + // If retrieving last modified date fails, fall back to the provided date + callback(null, dateObj); + }); + }; + + const cached = lastModifiedDateCache.get(filename); + if (cached) { + return call(cached); + } + const promise = lastModifiedDate(filename); + lastModifiedDateCache.set(filename, promise); + call(promise); + } + ); + eleventyConfig.addFilter("encodeURIComponent", function (str) { return encodeURIComponent(str); }); diff --git a/_includes/layouts/base.njk b/_includes/layouts/base.njk index 80eddb0..1e3bdb4 100644 --- a/_includes/layouts/base.njk +++ b/_includes/layouts/base.njk @@ -106,6 +106,8 @@ {% if page.url and page.url.indexOf("/posts/") > -1 %}
Published
+ {% elif page.url and page.url.indexOf("/site-index/") == -1 %} +Published
{% endif %} diff --git a/package-lock.json b/package-lock.json index 5b4c1da..ef148fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,6 +66,7 @@ "integrity": "sha512-03ER4zukR6BgwppI5DHRE11lc+8B0fWsBrqacVWo3o49QkdEFXnEWjhyI9qd9LrPlgQHK2/MYyxuOvNwecyCLQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@11ty/dependency-tree": "^2.0.1", "@11ty/eleventy-utils": "^1.0.1", @@ -5468,6 +5469,7 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz", "integrity": "sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==", "license": "MIT", + "peer": true, "dependencies": { "colorette": "^1.2.2", "nanoid": "^3.1.22", @@ -6554,6 +6556,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, From 12f5329f6ff88e8d2b22c4a0df2563573ddfb445 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 19:39:53 +0000 Subject: [PATCH 2/2] feat: Add 'Originally published on' footer to non-post pages This change adds a footer to all non-post pages (excluding the site index) that displays the text "Originally published on [Date]". The date displayed is the earliest of the frontmatter date, the file's last modified date, or the file's original creation date (retrieved via git log). The text is styled to be centered, italicized, and have 50% opacity. - Added `getOriginalDate` helper and `earliestDate` async filter in `.eleventy.js` to determine the earliest valid date. - Updated `_includes/layouts/base.njk` to include the footer logic and styling for non-post pages. --- .eleventy.js | 89 ++++++++++++++++++++++++++------------ _includes/layouts/base.njk | 2 +- 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/.eleventy.js b/.eleventy.js index 554ce28..e1c27cc 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -142,37 +142,72 @@ module.exports = function (eleventyConfig) { } ); + async function getOriginalDate(filename) { + try { + const { stdout } = await execFile("git", [ + "log", + "--diff-filter=A", + "--follow", + "-1", + "--format=%cd", + filename, + ]); + return new Date(stdout); + } catch (e) { + console.error(e.message); + return null; + } + } + const originalDateCache = new Map(); + eleventyConfig.addNunjucksAsyncFilter( - "latestDate", + "earliestDate", function (dateObj, filename, callback) { - const call = (result) => { - result.then((modifiedDate) => { - let latest = dateObj; - if (modifiedDate instanceof Date && !isNaN(modifiedDate)) { - // Compare timestamps - if (latest instanceof Date && !isNaN(latest)) { - if (modifiedDate.getTime() > latest.getTime()) { - latest = modifiedDate; - } - } else { - latest = modifiedDate; - } - } - callback(null, latest); - }); - result.catch((error) => { - // If retrieving last modified date fails, fall back to the provided date - callback(null, dateObj); - }); + const getDates = async () => { + let dates = []; + + // 1. Frontmatter Date + if (dateObj instanceof Date && !isNaN(dateObj)) { + dates.push(dateObj); + } + + // 2. Last Modified Date (cached) + let modDatePromise = lastModifiedDateCache.get(filename); + if (!modDatePromise) { + modDatePromise = lastModifiedDate(filename); + lastModifiedDateCache.set(filename, modDatePromise); + } + try { + const modDate = await modDatePromise; + if (modDate instanceof Date && !isNaN(modDate)) { + dates.push(modDate); + } + } catch(e) {} + + // 3. Original Creation Date (cached) + let origDatePromise = originalDateCache.get(filename); + if (!origDatePromise) { + origDatePromise = getOriginalDate(filename); + originalDateCache.set(filename, origDatePromise); + } + try { + const origDate = await origDatePromise; + if (origDate instanceof Date && !isNaN(origDate)) { + dates.push(origDate); + } + } catch(e) {} + + if (dates.length === 0) { + // Fallback if no valid dates found + return new Date(); + } + + // Find earliest + dates.sort((a, b) => a.getTime() - b.getTime()); + return dates[0]; }; - const cached = lastModifiedDateCache.get(filename); - if (cached) { - return call(cached); - } - const promise = lastModifiedDate(filename); - lastModifiedDateCache.set(filename, promise); - call(promise); + getDates().then(date => callback(null, date)).catch(err => callback(null, dateObj)); } ); diff --git a/_includes/layouts/base.njk b/_includes/layouts/base.njk index 1e3bdb4..5a894a1 100644 --- a/_includes/layouts/base.njk +++ b/_includes/layouts/base.njk @@ -107,7 +107,7 @@ {% if page.url and page.url.indexOf("/posts/") > -1 %}Published
{% elif page.url and page.url.indexOf("/site-index/") == -1 %} -Published
+Originally published on
{% endif %}