Skip to content

Commit d1cbc2d

Browse files
committed
refactor(xref/headings): pre-index headings by ID at scrape time
Per sidvishnoi's review: index headings by ID during scraping rather than at store load time. Removes the redundant indexHeadings() runtime function.
1 parent c01b238 commit d1cbc2d

2 files changed

Lines changed: 17 additions & 33 deletions

File tree

routes/xref/lib/scraper.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export interface HeadingEntry {
5151
}
5252

5353
export interface HeadingsBySpec {
54-
[shortname: string]: HeadingEntry[];
54+
[shortname: string]: { [id: string]: HeadingEntry };
5555
}
5656

5757
interface HeadingsJSON {
@@ -251,7 +251,7 @@ async function readJSON<T = unknown>(filePath: string): Promise<T> {
251251

252252
/**
253253
* Read all headings data from webref's ed/headings/ directory.
254-
* Returns { shortname: HeadingEntry[] }. Indexed by id in the Store.
254+
* Returns { shortname: { id: HeadingEntry } } — pre-indexed for O(1) lookup.
255255
*/
256256
async function readAllHeadings(
257257
headingsDir: string,
@@ -271,14 +271,17 @@ async function readAllHeadings(
271271
const data = await readJSON<HeadingsJSON>(path.join(headingsDir, file));
272272
// Shortname derived from filename (webref doesn't include it in the JSON)
273273
const shortname = file.replace(/\.json$/, "").toLowerCase();
274-
const headings: HeadingEntry[] = (data.headings || []).map(h => ({
275-
id: h.id,
276-
href: h.href,
277-
title: h.title,
278-
number: h.number,
279-
level: h.level,
280-
}));
281-
result[shortname] = headings;
274+
const byId: { [id: string]: HeadingEntry } = Object.create(null);
275+
for (const h of data.headings || []) {
276+
byId[h.id] = {
277+
id: h.id,
278+
href: h.href,
279+
title: h.title,
280+
number: h.number,
281+
level: h.level,
282+
};
283+
}
284+
result[shortname] = byId;
282285
} catch (error) {
283286
console.error(`Error reading headings from ${file}:`, error);
284287
}

routes/xref/lib/store.ts

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
import path from "path";
2-
import { readFileSync, existsSync } from "fs";
2+
import { readFileSync } from "fs";
33

44
import { env } from "../../../utils/misc.js";
55
import { DataEntry } from "./search.js";
66
import { HeadingEntry, HeadingsBySpec } from "./scraper.js";
77

8-
/** Headings indexed by id for O(1) lookup. */
9-
type HeadingsIndex = {
10-
[shortname: string]: { [id: string]: HeadingEntry };
11-
};
12-
138
export class Store {
149
version = -1;
1510
bySpec: { [shortname: string]: DataEntry[] } = {};
@@ -21,8 +16,8 @@ export class Store {
2116
title: string;
2217
};
2318
} = {};
24-
/** Headings indexed by spec shortname, then by fragment id. */
25-
headings: HeadingsIndex = {};
19+
/** Headings pre-indexed by spec shortname, then by fragment id. */
20+
headings: HeadingsBySpec = {};
2621
/** Reverse lookup: shortname → spec title. */
2722
private specTitleByShortname: Map<string, string> = new Map();
2823

@@ -35,8 +30,7 @@ export class Store {
3530
this.byTerm = readJson("xref.json");
3631
this.bySpec = readJson("specs.json");
3732
this.specmap = readJson("specmap.json");
38-
const rawHeadings: HeadingsBySpec = readJsonOptional("headings.json");
39-
this.headings = indexHeadings(rawHeadings);
33+
this.headings = readJsonOptional("headings.json");
4034
this.specTitleByShortname = buildSpecTitleMap(this.specmap);
4135
this.version = Date.now();
4236
}
@@ -81,19 +75,6 @@ function readJsonOptional(filename: string) {
8175
}
8276
}
8377

84-
/** Index headings arrays by id for O(1) lookup per spec. */
85-
function indexHeadings(raw: HeadingsBySpec): HeadingsIndex {
86-
const indexed: HeadingsIndex = Object.create(null);
87-
for (const [shortname, headings] of Object.entries(raw)) {
88-
const byId: { [id: string]: HeadingEntry } = Object.create(null);
89-
for (const h of headings) {
90-
byId[h.id] = h;
91-
}
92-
indexed[shortname] = byId;
93-
}
94-
return indexed;
95-
}
96-
9778
/** Build a shortname → title map from the specmap for O(1) title lookup. */
9879
function buildSpecTitleMap(specmap: Store["specmap"]): Map<string, string> {
9980
const result = new Map<string, string>();

0 commit comments

Comments
 (0)