Skip to content

Commit b9eebc6

Browse files
authored
Replace Lunr with MiniSearch (shoelace-style#2204)
* make keys horizontal * replace lunr with minisearch
1 parent 4640055 commit b9eebc6

File tree

8 files changed

+61
-57
lines changed

8 files changed

+61
-57
lines changed

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
"menuitemradio",
131131
"metaframeworks",
132132
"middlewares",
133+
"minisearch",
133134
"minlength",
134135
"minmax",
135136
"monospace",

package-lock.json

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@
5656
"globby": "^13.2.2",
5757
"husky": "^8.0.3",
5858
"lint-staged": "^14.0.1",
59-
"lunr": "^2.3.9",
6059
"markdown-it": "^14.1.0",
6160
"markdown-it-attrs": "^4.3.0",
6261
"markdown-it-container": "^3.0.0",
6362
"markdown-it-ins": "^3.0.1",
6463
"markdown-it-kbd": "^2.2.2",
6564
"markdown-it-mark": "^3.0.1",
6665
"marked": "^11.1.0",
66+
"minisearch": "^7.2.0",
6767
"node-html-parser": "^6.1.13",
6868
"npm-check-updates": "^17.1.11",
6969
"ora": "^8.1.1",
@@ -90,4 +90,4 @@
9090
"prettier --write"
9191
]
9292
}
93-
}
93+
}

packages/webawesome/docs/_includes/search.njk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
</div>
4444

4545
{# Footer #}
46-
<footer class="wa-cluster wa-justify-content-center wa-gap-xl">
46+
<footer class="wa-gap-xl">
4747
<small>
4848
<kbd aria-label="Up"><wa-icon name="arrow-up" family="micro"></wa-icon></kbd>
4949
<kbd aria-label="Down"><wa-icon name="arrow-down" family="micro"></wa-icon></kbd>

packages/webawesome/docs/_plugins/search.js

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-invalid-this */
22
import { readFileSync } from 'fs';
33
import { mkdir, writeFile } from 'fs/promises';
4-
import lunr from 'lunr';
4+
import MiniSearch from 'minisearch';
55
import { parse } from 'node-html-parser';
66
import * as path from 'path';
77
import { dirname, join } from 'path';
@@ -21,7 +21,7 @@ function normalizeDisplayUrl(url) {
2121
}
2222

2323
/**
24-
* Eleventy plugin to build a Lunr search index.
24+
* Eleventy plugin to build a MiniSearch search index.
2525
*/
2626
export function searchPlugin(options = {}) {
2727
options = {
@@ -105,23 +105,21 @@ export function searchPlugin(options = {}) {
105105
const map = [];
106106

107107
getCachedPages();
108-
const searchIndex = lunr(function () {
109-
let index = 0;
110-
111-
this.ref('id');
112-
this.field('t', { boost: 20 });
113-
this.field('h', { boost: 10 });
114-
this.field('c');
115-
116-
for (const [_inputPath, page] of pagesToIndex) {
117-
this.add({ id: index, t: page.title, h: page.headings, c: page.content });
118-
map[index] = { title: page.title, description: page.description, url: page.url };
119-
index++;
120-
}
108+
109+
const searchIndex = new MiniSearch({
110+
fields: ['t', 'h', 'c'],
111+
storeFields: [],
121112
});
122113

114+
let index = 0;
115+
for (const [_inputPath, page] of pagesToIndex) {
116+
searchIndex.add({ id: index, t: page.title, h: page.headings, c: page.content });
117+
map[index] = { title: page.title, description: page.description, url: page.url };
118+
index++;
119+
}
120+
123121
await mkdir(dirname(outputFilename), { recursive: true });
124-
await writeFile(outputFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
122+
await writeFile(outputFilename, JSON.stringify({ searchIndex: searchIndex.toJSON(), map }), 'utf-8');
125123
await writeFile(cachedPages, JSON.stringify({ pages: [...pagesToIndex.entries()] }, null, 2));
126124
});
127125
};

packages/webawesome/docs/assets/scripts/search.js

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// Search data
22
const version = document.documentElement.getAttribute('data-version') || '';
33
const res = await Promise.all([
4-
import('https://cdn.jsdelivr.net/npm/lunr/+esm'),
4+
import('https://cdn.jsdelivr.net/npm/minisearch@7/+esm'),
55
fetch(`/search.json?v=${version}`),
66
import('/assets/scripts/track.js').catch(() => null),
77
]);
8-
const lunr = res[0].default;
8+
const MiniSearch = res[0].default;
99
const searchData = await res[1].json();
10-
const searchIndex = lunr.Index.load(searchData.searchIndex);
10+
const searchIndex = MiniSearch.loadJSON(JSON.stringify(searchData.searchIndex), {
11+
fields: ['t', 'h', 'c'],
12+
});
1113
const map = searchData.map;
1214
const searchDebounce = 200;
1315
const queryTrackDelay = 1000;
@@ -318,33 +320,33 @@ async function updateResults(query = '') {
318320
let matches = [];
319321

320322
if (hasQuery) {
321-
// Track seen refs to avoid duplicates
322-
const seenRefs = new Set();
323-
324-
// Start with a standard search to get the best "exact match" result
325-
searchIndex.search(`${trimmedQuery}`).forEach(match => {
326-
matches.push(match);
327-
seenRefs.add(match.ref);
328-
});
329-
330-
// Add wildcard matches if not already included
331-
searchIndex.search(`${trimmedQuery}*`).forEach(match => {
332-
if (!seenRefs.has(match.ref)) {
333-
matches.push(match);
334-
seenRefs.add(match.ref);
335-
}
323+
matches = searchIndex.search(trimmedQuery, {
324+
prefix: true,
325+
fuzzy: 0.2,
326+
boost: { t: 20, h: 10 },
336327
});
337328

338-
// Add fuzzy search matches last
339-
const fuzzyTokens = trimmedQuery
340-
.split(' ')
341-
.map(term => `${term}~1`)
342-
.join(' ');
343-
searchIndex.search(fuzzyTokens).forEach(match => {
344-
if (!seenRefs.has(match.ref)) {
345-
matches.push(match);
346-
seenRefs.add(match.ref);
347-
}
329+
// Re-rank results to prioritize title matches. Searches don't account for where in a title a match occurs, so
330+
// "change" can rank "pagination" above "changelog". This applies a bonus to results whose title contains the
331+
// query as a word boundary match.
332+
const queryLower = trimmedQuery.toLowerCase();
333+
matches.sort((a, b) => {
334+
const titleA = (map[a.id]?.title ?? '').toLowerCase();
335+
const titleB = (map[b.id]?.title ?? '').toLowerCase();
336+
337+
const rankTitle = title => {
338+
if (title === queryLower) return 3;
339+
if (title.startsWith(queryLower)) return 2;
340+
// Match query at a word boundary (e.g. "change" matches "price change")
341+
if (new RegExp(`\\b${queryLower.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`).test(title)) return 1;
342+
return 0;
343+
};
344+
345+
const rankDiff = rankTitle(titleB) - rankTitle(titleA);
346+
if (rankDiff !== 0) return rankDiff;
347+
348+
// Preserve MiniSearch's original score ordering within the same rank
349+
return b.score - a.score;
348350
});
349351
}
350352

@@ -355,7 +357,7 @@ async function updateResults(query = '') {
355357
input.setAttribute('aria-activedescendant', '');
356358
results.innerHTML = '';
357359
matches.forEach((match, index) => {
358-
const page = map[match.ref];
360+
const page = map[match.id];
359361
if (!page || !page.url) return;
360362

361363
const li = document.createElement('li');
@@ -367,7 +369,7 @@ async function updateResults(query = '') {
367369

368370
li.classList.add('site-search-result');
369371
li.setAttribute('role', 'option');
370-
li.setAttribute('id', `search-result-item-${match.ref}`);
372+
li.setAttribute('id', `search-result-item-${match.id}`);
371373
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
372374
if (page.url === '/') icon = 'home';
373375
else if (page.url === '/docs') icon = 'rocket-launch';

packages/webawesome/docs/assets/styles/search.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@
224224

225225
/* Footer */
226226
#site-search footer {
227+
flex-direction: row;
228+
justify-content: center;
227229
color: var(--wa-color-text-quiet);
228230
border-top: var(--wa-panel-border-style) var(--wa-panel-border-width) var(--wa-color-neutral-border-quiet);
229231
border-bottom-left-radius: var(--wa-border-radius-l);

packages/webawesome/docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ layout: page
408408
<wa-button appearance="filled" pill href="https://github.com/open-wc/custom-elements-manifest">Custom Elements Manifest</wa-button>
409409
<wa-button appearance="filled" pill href="https://floating-ui.com/">Floating UI</wa-button>
410410
<wa-button appearance="filled" pill href="https://animate.style/">animate.css</wa-button>
411-
<wa-button appearance="filled" pill href="https://lunrjs.com/">Lunr</wa-button>
411+
<wa-button appearance="filled" pill href="https://lucaong.github.io/minisearch/">MiniSearch</wa-button>
412412
</div>
413413
</div>
414414
</div>

0 commit comments

Comments
 (0)