Skip to content

Commit e520f0f

Browse files
authored
build date in default footer (#577)
* build date in default footer * TZ=America/Los_Angeles * short month name * ISO 8601 in title
1 parent f8e5f1c commit e520f0f

File tree

40 files changed

+97
-41
lines changed

40 files changed

+97
-41
lines changed

docs/config.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ Whether to show the previous & next footer links; defaults to true.
151151

152152
## footer
153153

154-
An HTML fragment to add to the footer. Defaults to `Built with Observable`.
154+
An HTML fragment to add to the footer. Defaults to Built with Observable on [today’s date].”
155155

156156
## toc
157157

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"deploy": "tsx ./bin/observable.ts deploy",
2828
"test": "yarn test:mocha && yarn test:tsc && yarn test:lint && yarn test:prettier",
2929
"test:coverage": "c8 yarn test:mocha",
30-
"test:mocha": "rm -rf test/.observablehq/cache test/input/build/*/.observablehq/cache && OBSERVABLE_TELEMETRY_DISABLE=1 tsx --no-warnings=ExperimentalWarning ./node_modules/.bin/mocha 'test/**/*-test.*'",
30+
"test:mocha": "rm -rf test/.observablehq/cache test/input/build/*/.observablehq/cache && OBSERVABLE_TELEMETRY_DISABLE=1 TZ=America/Los_Angeles tsx --no-warnings=ExperimentalWarning ./node_modules/.bin/mocha 'test/**/*-test.*'",
3131
"test:lint": "eslint src test",
3232
"test:prettier": "prettier --check src test",
3333
"test:tsc": "tsc --noEmit",

src/config.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {readFile} from "node:fs/promises";
22
import {basename, dirname, extname, join} from "node:path";
33
import {visitFiles} from "./files.js";
4+
import {formatIsoDate, formatLocaleDate} from "./format.js";
45
import {parseMarkdown} from "./markdown.js";
56
import {resolveTheme} from "./theme.js";
67
import {resolvePath} from "./url.js";
@@ -68,10 +69,23 @@ async function readPages(root: string): Promise<Page[]> {
6869
return pages;
6970
}
7071

71-
const DEFAULT_FOOTER = 'Built with <a href="https://observablehq.com/" target=_blank>Observable</a>';
72+
let currentDate = new Date();
73+
74+
export function setCurrentDate(date = new Date()): void {
75+
currentDate = date;
76+
}
7277

7378
export async function normalizeConfig(spec: any = {}, defaultRoot = "docs"): Promise<Config> {
74-
let {root = defaultRoot, output = "dist", style, theme = "default", deploy, footer = DEFAULT_FOOTER} = spec;
79+
let {
80+
root = defaultRoot,
81+
output = "dist",
82+
style,
83+
theme = "default",
84+
deploy,
85+
footer = `Built with <a href="https://observablehq.com/" target="_blank">Observable</a> on <a title="${formatIsoDate(
86+
currentDate
87+
)}">${formatLocaleDate(currentDate)}</a>.`
88+
} = spec;
7589
root = String(root);
7690
output = String(output);
7791
if (style === null) style = null;

src/format.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export function formatIsoDate(date: Date): string {
2+
return `${[pad(date.getFullYear(), 4), pad(date.getMonth() + 1, 2), pad(date.getDate(), 2)].join("-")}T${[
3+
pad(date.getHours(), 2),
4+
pad(date.getMinutes(), 2),
5+
pad(date.getSeconds(), 2)
6+
].join(":")}`;
7+
}
8+
9+
export function formatLocaleDate(date: Date, locale: Intl.LocalesArgument = "en-US"): string {
10+
return date.toLocaleDateString(locale, {month: "short", day: "numeric", year: "numeric"});
11+
}
12+
13+
function pad(number: number, length: number): string {
14+
return String(number).padStart(length, "0");
15+
}

test/build-test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {open, readFile, rm} from "node:fs/promises";
44
import {join, normalize, relative} from "node:path";
55
import {difference} from "d3-array";
66
import {FileBuildEffects, build} from "../src/build.js";
7-
import {readConfig} from "../src/config.js";
7+
import {readConfig, setCurrentDate} from "../src/config.js";
88
import {mockJsDelivr} from "./mocks/jsdelivr.js";
99

1010
const silentEffects = {
@@ -13,6 +13,7 @@ const silentEffects = {
1313
};
1414

1515
describe("build", async () => {
16+
before(() => setCurrentDate(new Date("2024-01-10T16:00:00")));
1617
mockJsDelivr();
1718

1819
// Each sub-directory of test/input/build is a test case.

test/config-test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import assert from "node:assert";
2-
import {normalizeConfig as config, mergeToc, readConfig} from "../src/config.js";
2+
import {normalizeConfig as config, mergeToc, readConfig, setCurrentDate} from "../src/config.js";
33

44
const root = "test/input/build/config";
55

66
describe("readConfig(undefined, root)", () => {
7+
before(() => setCurrentDate(new Date("2024-01-11T01:02:03")));
78
it("imports the config file at the specified root", async () => {
89
assert.deepStrictEqual(await readConfig(undefined, "test/input/build/config"), {
910
root: "test/input/build/config",
@@ -18,7 +19,8 @@ describe("readConfig(undefined, root)", () => {
1819
title: undefined,
1920
toc: {label: "On this page", show: true},
2021
pager: true,
21-
footer: 'Built with <a href="https://observablehq.com/" target=_blank>Observable</a>',
22+
footer:
23+
'Built with <a href="https://observablehq.com/" target="_blank">Observable</a> on <a title="2024-01-11T01:02:03">Jan 11, 2024</a>.',
2224
deploy: {
2325
workspace: "acme",
2426
project: "bi"
@@ -34,7 +36,8 @@ describe("readConfig(undefined, root)", () => {
3436
title: undefined,
3537
toc: {label: "Contents", show: true},
3638
pager: true,
37-
footer: 'Built with <a href="https://observablehq.com/" target=_blank>Observable</a>',
39+
footer:
40+
'Built with <a href="https://observablehq.com/" target="_blank">Observable</a> on <a title="2024-01-11T01:02:03">Jan 11, 2024</a>.',
3841
deploy: null
3942
});
4043
});

test/format-test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import assert from "node:assert";
2+
import {formatIsoDate, formatLocaleDate} from "../src/format.js";
3+
4+
describe("formatIsoDate", () => {
5+
it("returns an ISO 8601 string in local time", () => {
6+
assert.strictEqual(formatIsoDate(new Date("2013-01-02")), "2013-01-01T16:00:00");
7+
assert.strictEqual(formatIsoDate(new Date("2013-01-02T08:00:00")), "2013-01-02T08:00:00");
8+
assert.strictEqual(formatIsoDate(new Date("2013-01-02T08:00:00Z")), "2013-01-02T00:00:00");
9+
});
10+
});
11+
12+
describe("formatLocaleDate", () => {
13+
it("returns an string formatted for en-US", () => {
14+
assert.strictEqual(formatLocaleDate(new Date("2013-01-02")), "Jan 1, 2013");
15+
assert.strictEqual(formatLocaleDate(new Date("2013-01-02T08:00:00")), "Jan 2, 2013");
16+
assert.strictEqual(formatLocaleDate(new Date("2013-01-02T08:00:00Z")), "Jan 2, 2013");
17+
});
18+
it("respects the specified locale", () => {
19+
assert.strictEqual(formatLocaleDate(new Date("2013-01-02"), "fr-FR"), "1 janv. 2013");
20+
assert.strictEqual(formatLocaleDate(new Date("2013-01-02T08:00:00"), "fr-FR"), "2 janv. 2013");
21+
assert.strictEqual(formatLocaleDate(new Date("2013-01-02T08:00:00Z"), "fr-FR"), "2 janv. 2013");
22+
});
23+
});

test/output/build/404/404.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ <h1 id="page-not-found" tabindex="-1"><a class="observablehq-header-anchor" href
3434
<p>Sorry, but we can’t find the page you requested.</p>
3535
</main>
3636
<footer id="observablehq-footer">
37-
<div>Built with <a href="https://observablehq.com/" target=_blank>Observable</a></div>
37+
<div>Built with <a href="https://observablehq.com/" target="_blank">Observable</a> on <a title="2024-01-10T16:00:00">Jan 10, 2024</a>.</div>
3838
</footer>
3939
</div>

test/output/build/archives/tar.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,6 @@ <h1 id="tar" tabindex="-1"><a class="observablehq-header-anchor" href="#tar">Tar
8686
</main>
8787
<footer id="observablehq-footer">
8888
<nav><a rel="prev" href="./"><span>Home</span></a><a rel="next" href="./zip"><span>Zip</span></a></nav>
89-
<div>Built with <a href="https://observablehq.com/" target=_blank>Observable</a></div>
89+
<div>Built with <a href="https://observablehq.com/" target="_blank">Observable</a> on <a title="2024-01-10T16:00:00">Jan 10, 2024</a>.</div>
9090
</footer>
9191
</div>

test/output/build/archives/zip.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,6 @@ <h1 id="zip" tabindex="-1"><a class="observablehq-header-anchor" href="#zip">Zip
7070
</main>
7171
<footer id="observablehq-footer">
7272
<nav><a rel="prev" href="./tar"><span>Tar</span></a></nav>
73-
<div>Built with <a href="https://observablehq.com/" target=_blank>Observable</a></div>
73+
<div>Built with <a href="https://observablehq.com/" target="_blank">Observable</a> on <a title="2024-01-10T16:00:00">Jan 10, 2024</a>.</div>
7474
</footer>
7575
</div>

0 commit comments

Comments
 (0)