-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathbuild-search-index.js
More file actions
70 lines (58 loc) · 2.3 KB
/
Copy pathbuild-search-index.js
File metadata and controls
70 lines (58 loc) · 2.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"use strict";
// Generates search-index.json from webroot HTML files.
// Usage: node build-search-index.js [directory]
const fs = require("fs");
const path = require("path");
const webroot = process.argv[2] || path.join(__dirname, "articles", "webroot");
const outFile = path.join(webroot, "search-index.json");
const htmlFiles = fs.readdirSync(webroot).filter(f => f.endsWith(".html") && f !== "index.html" && f !== "titlepage.html").sort();
const index = [];
htmlFiles.forEach(file => {
const html = fs.readFileSync(path.join(webroot, file), "utf8");
// Extract page title from <title>
const titleMatch = html.match(/<title>([^|]*)/);
const pageTitle = titleMatch ? titleMatch[1].trim() : file;
// Extract sections: split by h1/h2 headings
const sectionRegex = /<h[12][^>]*id="([^"]*)"[^>]*>([\s\S]*?)<\/h[12]>/g;
const sections = [];
let match;
const headings = [];
while ((match = sectionRegex.exec(html)) !== null) {
headings.push({ id: match[1], pos: match.index, title: stripTags(match[2]) });
}
if (headings.length === 0) {
// No headings — index the whole page
const bodyMatch = html.match(/<div class="book-page">([\s\S]*?)<\/div>\s*<nav/);
const text = bodyMatch ? stripTags(bodyMatch[1]).slice(0, 500) : "";
index.push({ file, title: pageTitle, id: "", text });
} else {
// Index each section — use positions from full html since headings were matched against it
for (let i = 0; i < headings.length; i++) {
const start = headings[i].pos;
const naviPos = html.indexOf('<nav class="book-navi');
const end = i + 1 < headings.length ? headings[i + 1].pos : (naviPos !== -1 ? naviPos : html.length);
const sectionHtml = html.slice(start, end);
const text = stripTags(sectionHtml).slice(0, 500);
sections.push({
file,
title: headings[i].title,
id: headings[i].id,
text
});
}
index.push(...sections);
}
});
function stripTags(html) {
return html
.replace(/<[^>]+>/g, "")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/&/g, "&")
.replace(/"/g, '"')
.replace(/&#\d+;/g, "")
.replace(/\s+/g, " ")
.trim();
}
fs.writeFileSync(outFile, JSON.stringify(index, null, 0), "utf8");
console.log(`Search index: ${index.length} entries -> ${outFile}`);