fix(search): prevent pagefind filter hangs

Resolve the Pagefind browser loader in Vite dev and cap filter-only
result hydration so broad filters render promptly instead of stalling
behind thousands of fragment fetches.
This commit is contained in:
ThatGuySam 2026-03-15 19:25:07 -05:00
parent e1da6eb880
commit a1ae595717
2 changed files with 85 additions and 2 deletions

View file

@ -366,6 +366,9 @@ export default {
...this.filterQueryList
].filter( Boolean ).join(' ')
},
pagefindResultLimit () {
return Math.max( this.initialList.length, 25 )
},
pagefindFilters () {
const filters = new SearchFilters()
filters.setFromStringArray( this.filterQueryList )
@ -518,10 +521,20 @@ export default {
return null
}
const resultData = await Promise.all( ( pagefindQuery.results || [] ).map( async result => {
const limitedResults = ( pagefindQuery.results || [] ).slice( 0, this.pagefindResultLimit )
const settledResultData = await Promise.allSettled( limitedResults.map( async result => {
return await result.data()
} ) )
const resultData = settledResultData.flatMap( settledResult => {
if ( settledResult.status === 'fulfilled' ) {
return [ settledResult.value ]
}
console.warn('Failed to load Pagefind result data', settledResult.reason)
return []
} )
return resultData.map( data => {
return mapPagefindDataToListing( data, {
highlightTerms: this.inputTerms

View file

@ -6,6 +6,10 @@ import {
pagefindScriptURL
} from '~/helpers/pagefind/config.js'
const pagefindGlobalKey = '__doesItArmPagefind'
const pagefindLoaderPromiseKey = '__doesItArmPagefindLoaderPromise'
const pagefindLoaderTimeoutMs = 10 * 1000
function escapeHtml ( text = '' ) {
return text
.replaceAll('&', '&')
@ -70,6 +74,72 @@ export function mapPagefindDataToListing ( resultData, {
}
}
function getPagefindModuleSource () {
return [
`import * as pagefindModule from ${ JSON.stringify( pagefindScriptURL ) }`,
`globalThis.${ pagefindGlobalKey } = pagefindModule.default || pagefindModule`
].join('\n')
}
function waitForPagefindGlobal () {
return new Promise( ( resolve, reject ) => {
if ( globalThis[ pagefindGlobalKey ] ) {
resolve( globalThis[ pagefindGlobalKey ] )
return
}
const startedAt = Date.now()
const timer = setInterval( () => {
if ( globalThis[ pagefindGlobalKey ] ) {
clearInterval( timer )
resolve( globalThis[ pagefindGlobalKey ] )
return
}
if ( Date.now() - startedAt >= pagefindLoaderTimeoutMs ) {
clearInterval( timer )
reject( new Error(`Timed out waiting for Pagefind browser module at ${ pagefindScriptURL }`) )
}
}, 20 )
} )
}
async function loadPagefindBrowserModule () {
if ( typeof document === 'undefined' ) {
throw new Error('PagefindClient can only load in a browser document')
}
if ( globalThis[ pagefindGlobalKey ] ) {
return globalThis[ pagefindGlobalKey ]
}
if ( !globalThis[ pagefindLoaderPromiseKey ] ) {
globalThis[ pagefindLoaderPromiseKey ] = new Promise( async ( resolve, reject ) => {
const script = document.createElement('script')
script.async = true
script.type = 'module'
script.textContent = getPagefindModuleSource()
script.onerror = () => {
delete globalThis[ pagefindLoaderPromiseKey ]
reject( new Error(`Failed to load Pagefind browser module from ${ pagefindScriptURL }`) )
}
document.head.append( script )
try {
resolve( await waitForPagefindGlobal() )
} catch ( err ) {
delete globalThis[ pagefindLoaderPromiseKey ]
reject( err )
}
} )
}
return await globalThis[ pagefindLoaderPromiseKey ]
}
export class PagefindClient {
constructor ( options = {} ) {
this.bundlePath = options.bundlePath || pagefindBundleRelativeURL
@ -100,7 +170,7 @@ export class PagefindClient {
async loadPagefindScript () {
if ( this.pagefind ) return
const pagefindModule = await import(/* @vite-ignore */ pagefindScriptURL)
const pagefindModule = await loadPagefindBrowserModule()
this.pagefind = pagefindModule.default || pagefindModule
this.pagefind.options({