learning-website-nextjs1-9 Exercise 1: A Search That Runs in the Browser, and the Plain Text It Searches ========================================================================================================== There is no search server and no database: the site is made of files. So search is a FILE too: when the site is built, an index of every page is written out as one JSON file, and the browser fetches it the first time somebody searches, then looks words up in it. Three jobs, in a package of plain functions (@lw/search) so they can be tested without a browser: 1. htmlToText: the words a person would read on a page. 2. buildSearchIndex: for every word, the pages it is on (and how often). 3. search: a query against the index. The package has two doors, because a browser must never load code that reads files: the main one (for the build) and "@lw/search/client" (prepare and search only, which the page imports). Save as packages/search/package.json: { "name": "@lw/search", "version": "0.1.0", "private": true, "type": "module", "exports": { ".": "./src/index.ts", "./client": "./src/client.ts" }, "dependencies": { "@lw/content": "*" }, "scripts": { "test": "node --test \"src/**/*.test.ts\"", "typecheck": "tsc --noEmit" } } Save as packages/search/src/text.ts: import { decodeEntities } from "@lw/content"; /** Japanese: hiragana, katakana (with the long mark), and the common kanji blocks. The same ranges as the Django project. */ const CJK_CHAR = /[-ヿ㐀-䶿一-鿿豈-]/; export const hasCjk = (text: string): boolean => CJK_CHAR.test(text); // Code and styling are not content. An opening "), "word"); assert.equal(htmlToText("
x y
\n\nz
"), "x y z"); assert.equal(htmlToText("React: <> and > done
"), "React: <> and done"); // the empty closing tag > is ignored assert.equal(htmlToText("1 ½ Δδ É
"), "1 ½ Δδ É"); }); test("an unclosed