Search the basketball league from the front page
The front page now opens with a search box. Type a team, a player, a stat or a word from a post, and the matches
appear as you type: teams, the players on the leader boards, the game of the season, the posts and the site's pages.
Filters count each kind of match, so a search for team shows how many teams, players and games it found,
and a link to the search keeps its words in the address, like /?q=rebounds.
No server, just a small index
The site is built ahead of time and served as plain files, so the search is too. When the site is built, it also writes a list of everything a visitor might look for: one entry per team, leader, post, game and page, each with a title, the words it is found by and a link. For the current season that is 65 entries in about 9 KB. Your browser loads it once and matches your words against it:
export function find(index, query, kind) {
const words = plain(query).split(/\s+/).filter(Boolean);
const counts = { all: 0, post: 0, team: 0, player: 0, game: 0, page: 0 };
if (!words.length) return { hits: [], counts };
const titled = [], worded = [];
for (const e of index) {
const title = plain(e.title), all = `${title} ${plain(e.text)}`;
if (!words.every((w) => all.includes(w))) continue;
counts.all += 1;
if (e.kind in counts) counts[e.kind] += 1;
if (kind !== "all" && e.kind !== kind) continue;
(words.some((w) => title.includes(w)) ? titled : worded).push(e);
}
return { hits: [...titled, ...worded], counts };
}
Every word must match, in any order. Case and accents do not matter. An entry whose title matches comes before one matched only by its other words. What it finds is written into the page as text, never as markup, so typing a tag into the box shows the tag rather than running it.
An index that cannot disagree with its pages
An index is a second copy of what the pages say, and a second copy can drift. So a test holds every entry against the page it links to: the link and its anchor must exist, and the title and every number the entry mentions must appear on that page. The leader boards are computed once and read by both the season page and the index, so their numbers cannot differ. Before this search went live, all 65 of its links were walked against the built site, and none failed to resolve.
The index never holds the game of the season's final score, so searching for the game does not spoil it.
A check that counted a comment as code
The site has a check that fails when a script defines a function nothing calls. Widening it to the search's
scripts turned up a flaw in the check itself. The search function passed as used, though nothing called it yet,
because a comment at the top of its file said "what a visitor's words find". The check counted the word
find in a comment as a call. It now reads only the code, with a planted case that proves it. Reading
only the code, it then called a second function unused that is in fact used: the build calls it from a file the
check had never read. The check reads that file too now.