Skip to content

Commit a4d785c

Browse files
committed
refactor: revisions data file to be smaller
1 parent b486c7d commit a4d785c

9 files changed

Lines changed: 426 additions & 672 deletions

11ty/revisions.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@ import { execFileSync } from 'node:child_process'
22
import { createHash } from 'node:crypto'
33
import { readFile, writeFile } from 'node:fs/promises'
44

5-
export const HASH_ALGORITHM = 'shake128'
6-
export const HASH_OUTPUT_BYTES = 6
75
export const REVISIONS_PATH = 'www/_data/revisions.json'
86

9-
export interface Revision {
10-
hash: string
7+
const HASH_ALGORITHM = 'shake128'
8+
const HASH_OUTPUT_BYTES = 6
9+
10+
export interface RevisionVersion {
1111
algorithm: string
1212
outputBytes: number
1313
fields: string[]
1414
separator: string
15+
}
16+
17+
export interface Revision {
18+
v: number
19+
hash: string
1520
date: string
1621
commit: string
1722
message?: string
@@ -23,7 +28,10 @@ export interface RevisionEntry {
2328
generatedTeaserHash?: string
2429
}
2530

26-
export type RevisionsFile = Record<string, RevisionEntry>
31+
export interface RevisionsFile {
32+
versions: Record<string, RevisionVersion>
33+
entries: Record<string, RevisionEntry>
34+
}
2735

2836
export function computeHash(
2937
title: string,
@@ -39,10 +47,6 @@ export function computeHash(
3947
return hash.digest('hex')
4048
}
4149

42-
export function hashFields(subhead: string | undefined): string[] {
43-
return subhead != null ? ['title', 'subhead', 'content'] : ['title', 'content']
44-
}
45-
4650
export function computeTeaserHash(teaser: string): string {
4751
const hash = createHash(HASH_ALGORITHM, {
4852
outputLength: HASH_OUTPUT_BYTES,
@@ -79,17 +83,21 @@ export async function loadRevisions(path: string = REVISIONS_PATH): Promise<Revi
7983
const content = await readFile(path, 'utf-8')
8084
return JSON.parse(content) as RevisionsFile
8185
} catch {
82-
return {}
86+
return { versions: {}, entries: {} }
8387
}
8488
}
8589

8690
export async function saveRevisions(
8791
revisions: RevisionsFile,
8892
path: string = REVISIONS_PATH
8993
): Promise<void> {
90-
const sorted: RevisionsFile = {}
91-
for (const key of Object.keys(revisions).sort()) {
92-
sorted[key] = revisions[key]
94+
const sortedEntries: Record<string, RevisionEntry> = {}
95+
for (const key of Object.keys(revisions.entries).sort()) {
96+
sortedEntries[key] = revisions.entries[key]
97+
}
98+
const output: RevisionsFile = {
99+
versions: revisions.versions,
100+
entries: sortedEntries,
93101
}
94-
await writeFile(path, JSON.stringify(sorted, null, 2) + '\n')
102+
await writeFile(path, JSON.stringify(output, null, 2) + '\n')
95103
}

11ty/update-revisions.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ import matter from 'gray-matter'
33
import { readFile } from 'node:fs/promises'
44

55
import {
6-
HASH_ALGORITHM,
7-
HASH_OUTPUT_BYTES,
86
computeHash,
97
getCommitMessage,
108
getCurrentCommit,
11-
hashFields,
129
loadRevisions,
1310
saveRevisions,
1411
} from './revisions.ts'
@@ -32,24 +29,20 @@ async function main() {
3229
const content = parsed.content
3330

3431
const hash = computeHash(title, subhead, content)
35-
const fields = hashFields(subhead)
3632

37-
const entry = revisions[filePath] ?? { revisions: [] }
33+
const entry = revisions.entries[filePath] ?? { revisions: [] }
3834
const latestRevision = entry.revisions[entry.revisions.length - 1]
3935

4036
if (!latestRevision || latestRevision.hash !== hash) {
4137
const message = getCommitMessage(commit)
4238
entry.revisions.push({
39+
v: 1,
4340
hash,
44-
algorithm: HASH_ALGORITHM,
45-
outputBytes: HASH_OUTPUT_BYTES,
46-
fields,
47-
separator: '\\n',
4841
date: new Date().toISOString(),
4942
commit,
5043
...(message && { message }),
5144
})
52-
revisions[filePath] = entry
45+
revisions.entries[filePath] = entry
5346
revisionCount++
5447
}
5548
}

bin/backfill-revision-messages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ async function main() {
99
const revisions = await loadRevisions()
1010
let updated = 0
1111

12-
for (const entry of Object.values(revisions)) {
12+
for (const entry of Object.values(revisions.entries)) {
1313
for (const revision of entry.revisions) {
1414
if (!revision.message) {
1515
const message = getCommitMessage(revision.commit)

bin/backfill-revisions.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ import { readFile } from 'node:fs/promises'
55

66
import {
77
type RevisionsFile,
8-
HASH_ALGORITHM,
9-
HASH_OUTPUT_BYTES,
108
REVISIONS_PATH,
119
computeHash,
12-
hashFields,
1310
saveRevisions,
1411
} from '../11ty/revisions.ts'
1512

@@ -50,7 +47,17 @@ function getFileAtCommit(sha: string, filePath: string): string | null {
5047

5148
async function main() {
5249
const files = await globby('www/**/*.md')
53-
const revisions: RevisionsFile = {}
50+
const revisions: RevisionsFile = {
51+
versions: {
52+
'1': {
53+
algorithm: 'shake128',
54+
outputBytes: 6,
55+
fields: ['title', 'subhead', 'content'],
56+
separator: '\n',
57+
},
58+
},
59+
entries: {},
60+
}
5461

5562
let totalFiles = 0
5663
let totalRevisions = 0
@@ -70,7 +77,7 @@ async function main() {
7077
continue
7178
}
7279

73-
const entry: RevisionsFile[string] = { revisions: [] }
80+
const entry: RevisionsFile['entries'][string] = { revisions: [] }
7481
let previousHash: string | null = null
7582

7683
for (const logEntry of logEntries) {
@@ -93,11 +100,8 @@ async function main() {
93100
if (hash === previousHash) continue
94101

95102
entry.revisions.push({
103+
v: 1,
96104
hash,
97-
algorithm: HASH_ALGORITHM,
98-
outputBytes: HASH_OUTPUT_BYTES,
99-
fields: hashFields(subhead),
100-
separator: '\\n',
101105
date: logEntry.date,
102106
commit: logEntry.shortSha,
103107
})
@@ -106,7 +110,7 @@ async function main() {
106110
}
107111

108112
if (entry.revisions.length > 0) {
109-
revisions[filePath] = entry
113+
revisions.entries[filePath] = entry
110114
}
111115
}
112116

bin/generate-teasers.spec.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
insertTeaserIntoFrontmatter,
77
wrapText,
88
} from './generate-teasers.ts'
9-
import { computeHash, hashFields } from '../11ty/revisions.ts'
9+
import { computeHash } from '../11ty/revisions.ts'
1010

1111
describe('computeHash', () => {
1212
it('produces a 12-character hex string', () => {
@@ -33,16 +33,6 @@ describe('computeHash', () => {
3333
})
3434
})
3535

36-
describe('hashFields', () => {
37-
it('returns title and content when no subhead', () => {
38-
expect(hashFields(undefined)).toEqual(['title', 'content'])
39-
})
40-
41-
it('includes subhead when present', () => {
42-
expect(hashFields('A subhead')).toEqual(['title', 'subhead', 'content'])
43-
})
44-
})
45-
4636
describe('wrapText', () => {
4737
it('returns short text unchanged', () => {
4838
expect(wrapText('Hello world', 90)).toBe('Hello world')

bin/generate-teasers.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,13 @@ async function main() {
169169

170170
const hash = computeHash(title, subhead, content)
171171

172-
const inRevisions = filePath in revisions && revisions[filePath].teaserHash != null
173-
const hashMatch = inRevisions && revisions[filePath].teaserHash === hash
172+
const inRevisions =
173+
filePath in revisions.entries && revisions.entries[filePath].teaserHash != null
174+
const hashMatch = inRevisions && revisions.entries[filePath].teaserHash === hash
174175

175176
// Check if teaser was manually edited by comparing its hash to the stored generated hash
176177
const currentTeaserHash = teaser != null ? computeTeaserHash(teaser) : null
177-
const storedTeaserHash = revisions[filePath]?.generatedTeaserHash ?? null
178+
const storedTeaserHash = revisions.entries[filePath]?.generatedTeaserHash ?? null
178179
const teaserManuallyEdited =
179180
currentTeaserHash != null &&
180181
storedTeaserHash != null &&
@@ -206,9 +207,9 @@ async function main() {
206207
const updatedContent = insertTeaserIntoFrontmatter(fileContent, newTeaser)
207208
await writeFile(filePath, updatedContent)
208209

209-
revisions[filePath] ??= { revisions: [] }
210-
revisions[filePath].teaserHash = hash
211-
revisions[filePath].generatedTeaserHash = computeTeaserHash(newTeaser)
210+
revisions.entries[filePath] ??= { revisions: [] }
211+
revisions.entries[filePath].teaserHash = hash
212+
revisions.entries[filePath].generatedTeaserHash = computeTeaserHash(newTeaser)
212213
teaserHashChanged = true
213214
generatedCount++
214215
}

www/_data/eleventyComputed.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default {
7272
// ISO date of the most recent revision, if modified after creation
7373
lastModified: (data) => {
7474
const filePath = data.page?.inputPath?.replace(/^\.\//, '')
75-
const revs = filePath && data.revisions?.[filePath]?.revisions
75+
const revs = filePath && data.revisions?.entries?.[filePath]?.revisions
7676
if (revs?.length > 1) {
7777
return revs.at(-1).date
7878
}

0 commit comments

Comments
 (0)