mirror of
https://github.com/luxfi/liquid.git
synced 2026-10-30 16:21:07 +00:00
79 lines
2.5 KiB
JavaScript
79 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
// Builds a chat-ready content index over docs/**/*.md(x).
|
|
// Output: build/search-index.json — chunked, tokenized, served as a static asset.
|
|
// Runtime chat widget queries Hanzo Zen with the matching chunks as context.
|
|
|
|
import { readFile, writeFile, mkdir, readdir } from "node:fs/promises";
|
|
import { existsSync } from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const ROOT = path.resolve(process.cwd(), "docs");
|
|
const OUT_DIR = path.resolve(process.cwd(), "build");
|
|
const OUT_FILE = path.join(OUT_DIR, "search-index.json");
|
|
const MAX_CHUNK_CHARS = 1200;
|
|
|
|
function chunk(text) {
|
|
const out = [];
|
|
const paras = text.split(/\n{2,}/).map((p) => p.trim()).filter(Boolean);
|
|
let buf = "";
|
|
for (const p of paras) {
|
|
if ((buf + "\n\n" + p).length > MAX_CHUNK_CHARS && buf) {
|
|
out.push(buf);
|
|
buf = p;
|
|
} else {
|
|
buf = buf ? buf + "\n\n" + p : p;
|
|
}
|
|
}
|
|
if (buf) out.push(buf);
|
|
return out;
|
|
}
|
|
|
|
function stripFrontmatter(src) {
|
|
if (!src.startsWith("---")) return { meta: {}, body: src };
|
|
const end = src.indexOf("\n---", 3);
|
|
if (end < 0) return { meta: {}, body: src };
|
|
const fm = src.slice(3, end).trim();
|
|
const meta = Object.fromEntries(
|
|
fm.split("\n").map((l) => {
|
|
const i = l.indexOf(":");
|
|
return i < 0 ? [l, ""] : [l.slice(0, i).trim(), l.slice(i + 1).trim().replace(/^["']|["']$/g, "")];
|
|
}),
|
|
);
|
|
return { meta, body: src.slice(end + 4) };
|
|
}
|
|
|
|
async function* walk(dir) {
|
|
const ents = await readdir(dir, { withFileTypes: true });
|
|
for (const e of ents) {
|
|
const p = path.join(dir, e.name);
|
|
if (e.isDirectory()) yield* walk(p);
|
|
else if (/\.mdx?$/.test(e.name)) yield p;
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
if (!existsSync(ROOT)) {
|
|
console.warn(`[index] no docs/ dir at ${ROOT}; skipping`);
|
|
return;
|
|
}
|
|
const docs = [];
|
|
for await (const file of walk(ROOT)) {
|
|
const src = await readFile(file, "utf8");
|
|
const { meta, body } = stripFrontmatter(src);
|
|
const rel = path.relative(ROOT, file).replace(/\\/g, "/");
|
|
const url = "/" + rel.replace(/\.mdx?$/, "").replace(/\/index$/, "");
|
|
const title = meta.title || meta.sidebar_label || path.basename(file, path.extname(file));
|
|
for (const [i, c] of chunk(body).entries()) {
|
|
docs.push({ id: `${rel}#${i}`, url, title, text: c });
|
|
}
|
|
}
|
|
await mkdir(OUT_DIR, { recursive: true });
|
|
await writeFile(OUT_FILE, JSON.stringify({ v: 1, count: docs.length, docs }));
|
|
console.log(`[index] wrote ${docs.length} chunks → ${OUT_FILE}`);
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|