Allow merged indexes to be marked optional

A merged index that failed to load made mergeIndex throw, and the
failed instance stayed in the list, so search stopped working for
every index. Indexes passed with `optional: true` are now dropped
with a warning when they fail to load, and search runs over the
indexes that did load.

Fixes #887
This commit is contained in:
dualfroz committed 2026-10-03 01:37:54 +02:00
1 parent ebcea5d0cb
commit b572112ae7
5 files changed
+118 -4

No files matched your search

+24
View File
@@ -155,6 +155,30 @@ await pagefind.mergeIndex("https://docs.example.com/pagefind", {
``` ```
{{< /diffcode >}} {{< /diffcode >}}
## Skipping indexes that fail to load
By default, a merged index that can't be loaded stops search from working. If some of your indexes might be missing, for example while one of the sites hasn't been built yet, pass an `optional` option for those indexes. Pagefind will log a warning and search across the indexes that did load:
{{< diffcode >}}
```js
// Component UI:
const { configureInstance } = window.PagefindComponents;
configureInstance("default", {
mergeIndex: [{
bundlePath: "https://docs.example.com/pagefind",
+ optional: true
}]
});
// JS API:
const pagefind = await import("/pagefind/pagefind.js");
await pagefind.mergeIndex("https://docs.example.com/pagefind", {
+ optional: true
});
```
{{< /diffcode >}}
## Notes ## Notes
### Cross origin indexes ### Cross origin indexes
@@ -0,0 +1,33 @@
name: Multisite Search > Pagefind skips an optional index that fails to load
steps:
- ref: ./background.toolproof.yml
- macro: I run Pagefind with "--site root/website_a"
- step: stdout should contain "Running Pagefind"
- step: The file "root/website_a/pagefind/pagefind.js" should not be empty
- macro: I run Pagefind with "--site root/website_b"
- step: stdout should contain "Running Pagefind"
- step: The file "root/website_b/pagefind/pagefind.js" should not be empty
- step: I serve the directory "root"
- step: In my browser, I load "/"
- step: In my browser, I evaluate {js}
js: >-
let pagefind = await import("/website_a/pagefind/pagefind.js");
await pagefind.mergeIndex("/website_c/pagefind/", { optional: true });
await pagefind.mergeIndex("/website_b/pagefind/");
let search = await pagefind.search("web");
let pages = await Promise.all(search.results.map(r => r.data()));
document.querySelector('[data-result]').innerText = pages.map(p =>
p.url).join(", ");
- step: In my browser, I evaluate {js}
js: >-
let val = await toolproof.querySelector("[data-result]");
toolproof.assert_eq(val.innerHTML, `/website_a/hello/,
/website_b/lorem/`);
@@ -0,0 +1,42 @@
name: Multisite Search > Pagefind UI skips an optional index that fails to load
steps:
- ref: ./background.toolproof.yml
- step: I have a "root/index.html" file with the content {html}
html: |-
<!DOCTYPE html><html lang="en"><head></head><body><div id="search"></div>
<script src="/website_a/pagefind/pagefind-ui.js"></script></body></html>
- macro: I run Pagefind with "--site root/website_a"
- step: stdout should contain "Running Pagefind"
- step: The file "root/website_a/pagefind/pagefind.js" should not be empty
- macro: I run Pagefind with "--site root/website_b"
- step: stdout should contain "Running Pagefind"
- step: The file "root/website_b/pagefind/pagefind.js" should not be empty
- step: I serve the directory "root"
- step: In my browser, I load "/"
- step: In my browser, I evaluate {js}
js: |-
let pui = new PagefindUI({
element: "#search",
mergeIndex: [{
bundlePath: "/website_c/pagefind/",
optional: true
}, {
bundlePath: "/website_b/pagefind/"
}]
});
pui.triggerSearch("web");
- step: In my browser, I evaluate {js}
js: >-
let val = await
toolproof.querySelector(".pagefind-ui__result:nth-of-type(1)
.pagefind-ui__result-link");
toolproof.assert_eq(val.innerHTML, `web web world web
PAGEFIND_ROOT_SELECTOR`);
- step: In my browser, I evaluate {js}
js: >-
let val = await
toolproof.querySelector(".pagefind-ui__result:nth-of-type(2)
.pagefind-ui__result-link");
toolproof.assert_eq(val.innerHTML, `web ipsum`);
+13 -4
View File
@@ -909,11 +909,20 @@ export class Pagefind {
await asyncSleep(50); await asyncSleep(50);
} }
await newInstance.init(options.language || this.primaryLanguage, { try {
load_wasm: false, await newInstance.init(options.language || this.primaryLanguage, {
}); load_wasm: false,
});
} catch (e) {
if (!options.optional) throw e;
this.instances = this.instances.filter((i) => i !== newInstance);
console.warn(
`Skipping optional mergeIndex ${indexPath} that failed to load`,
);
return;
}
const { language, ...remainingOptions } = options; const { language, optional, ...remainingOptions } = options;
await newInstance.options(remainingOptions); await newInstance.options(remainingOptions);
} }
+6
View File
@@ -28,6 +28,12 @@ declare global {
*/ */
highlightParam?: string; highlightParam?: string;
language?: string; language?: string;
/**
* If set, a merged index that fails to load is skipped instead of failing the whole search.
*
* Only applies in multisite setups, to indexes passed to mergeIndex.
*/
optional?: boolean;
/** /**
* Whether an instance of Pagefind is the primary index or not (for multisite). * Whether an instance of Pagefind is the primary index or not (for multisite).
* *