clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

main
forge / test / search-phrases.ts
search-phrases.ts 2.9K
1// How the search reads a query and what it matches, run in Node with the2// browser's own code: site/assets/query.ts, which search.ts matches every3// section with, and codesearch.ts's words for file contents.4//5// node test/search-phrases.ts terms <query>... each query's terms, then code search's6// node test/search-phrases.ts match <search.json> <query> what the sections list7//8// `terms` prints a line per query: `<query> => [term] [term] | code [word]`,9// with - for none.10// `match` builds the records from a built search.json (or repos.json) the11// way search.ts's load() does, less the file list, and prints a line per12// hit in rank order within its kind: `<kind>: <title> | <second line>`,13// with each marked run in [brackets].14import fs from 'node:fs';15import { marks, score, snippet, terms } from '../site/assets/query.ts';16import { words } from '../site/assets/codesearch.ts';1718const [mode, ...args] = process.argv.slice(2);19const show = (ts: string[]): string => ts.map((t) => `[${t}]`).join(' ') || '-';2021interface Rec {22 kind: string;23 title: string;24 meta: string;25 text: string;26 key: string;27}2829if (mode === 'terms') {30 for (const q of args) console.log(`${q} => ${show(terms(q))} | code ${show(words(q))}`);31} else if (mode === 'match') {32 const [file, q] = args;33 type Row = (string | number)[];34 const ix = JSON.parse(fs.readFileSync(file, 'utf8')) as { commits?: Row[]; reviews?: Row[]; wiki?: Row[]; repos?: Row[] };35 const recs: Rec[] = [];36 const rec = (kind: string, title: string, meta = '', text = '', key = ''): number => recs.push({ kind, title, meta, text, key });37 for (const [slug, desc] of ix.repos ?? []) rec('Repositories', String(slug), '', String(desc));38 for (const [id, title, branch, author, status] of ix.reviews ?? []) rec('Reviews', `#${id} ${title}`, `${branch}, ${author ? author + ', ' : ''}${status}`);39 for (const [sha, subject, author, date, body] of ix.commits ?? []) rec('Commits', String(subject), `${String(sha).slice(0, 7)} ${author}, ${date}`, String(body), String(sha));40 for (const [, title, text] of ix.wiki ?? []) rec('Wiki', String(title), '', String(text));41 const ts = terms(q);42 const marked = (s: string): string => marks(s, ts).map(([t, hit]) => (hit ? `[${t}]` : t)).join('');43 for (const kind of ['Repositories', 'Reviews', 'Commits', 'Wiki']) {44 const hits = recs45 .filter((r) => r.kind === kind && ts.length)46 .map((r) => ({ r, m: score([r.title.toLowerCase(), r.meta.toLowerCase(), r.text.toLowerCase()], r.key, ts) }))47 .filter((h) => h.m)48 .sort((a, b) => b.m!.s - a.m!.s);49 for (const { r, m } of hits) {50 const extra = m!.inText ? snippet(r.text, ts) : r.meta || r.text.slice(0, 120);51 console.log(`${kind}: ${marked(r.title)}${extra ? ` | ${marked(extra)}` : ''}`);52 }53 }54} else {55 console.error('usage: node test/search-phrases.ts terms <query>... | match <search.json> <query>');56 process.exit(2);57}