Skip to content

Commit a1ae595

Browse files
committed
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.
1 parent e1da6eb commit a1ae595

2 files changed

Lines changed: 85 additions & 2 deletions

File tree

components/search-stork.vue

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,9 @@ export default {
366366
...this.filterQueryList
367367
].filter( Boolean ).join(' ')
368368
},
369+
pagefindResultLimit () {
370+
return Math.max( this.initialList.length, 25 )
371+
},
369372
pagefindFilters () {
370373
const filters = new SearchFilters()
371374
filters.setFromStringArray( this.filterQueryList )
@@ -518,10 +521,20 @@ export default {
518521
return null
519522
}
520523
521-
const resultData = await Promise.all( ( pagefindQuery.results || [] ).map( async result => {
524+
const limitedResults = ( pagefindQuery.results || [] ).slice( 0, this.pagefindResultLimit )
525+
const settledResultData = await Promise.allSettled( limitedResults.map( async result => {
522526
return await result.data()
523527
} ) )
524528
529+
const resultData = settledResultData.flatMap( settledResult => {
530+
if ( settledResult.status === 'fulfilled' ) {
531+
return [ settledResult.value ]
532+
}
533+
534+
console.warn('Failed to load Pagefind result data', settledResult.reason)
535+
return []
536+
} )
537+
525538
return resultData.map( data => {
526539
return mapPagefindDataToListing( data, {
527540
highlightTerms: this.inputTerms

helpers/pagefind/browser.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import {
66
pagefindScriptURL
77
} from '~/helpers/pagefind/config.js'
88

9+
const pagefindGlobalKey = '__doesItArmPagefind'
10+
const pagefindLoaderPromiseKey = '__doesItArmPagefindLoaderPromise'
11+
const pagefindLoaderTimeoutMs = 10 * 1000
12+
913
function escapeHtml ( text = '' ) {
1014
return text
1115
.replaceAll('&', '&')
@@ -70,6 +74,72 @@ export function mapPagefindDataToListing ( resultData, {
7074
}
7175
}
7276

77+
function getPagefindModuleSource () {
78+
return [
79+
`import * as pagefindModule from ${ JSON.stringify( pagefindScriptURL ) }`,
80+
`globalThis.${ pagefindGlobalKey } = pagefindModule.default || pagefindModule`
81+
].join('\n')
82+
}
83+
84+
function waitForPagefindGlobal () {
85+
return new Promise( ( resolve, reject ) => {
86+
if ( globalThis[ pagefindGlobalKey ] ) {
87+
resolve( globalThis[ pagefindGlobalKey ] )
88+
return
89+
}
90+
91+
const startedAt = Date.now()
92+
const timer = setInterval( () => {
93+
if ( globalThis[ pagefindGlobalKey ] ) {
94+
clearInterval( timer )
95+
resolve( globalThis[ pagefindGlobalKey ] )
96+
return
97+
}
98+
99+
if ( Date.now() - startedAt >= pagefindLoaderTimeoutMs ) {
100+
clearInterval( timer )
101+
reject( new Error(`Timed out waiting for Pagefind browser module at ${ pagefindScriptURL }`) )
102+
}
103+
}, 20 )
104+
} )
105+
}
106+
107+
async function loadPagefindBrowserModule () {
108+
if ( typeof document === 'undefined' ) {
109+
throw new Error('PagefindClient can only load in a browser document')
110+
}
111+
112+
if ( globalThis[ pagefindGlobalKey ] ) {
113+
return globalThis[ pagefindGlobalKey ]
114+
}
115+
116+
if ( !globalThis[ pagefindLoaderPromiseKey ] ) {
117+
globalThis[ pagefindLoaderPromiseKey ] = new Promise( async ( resolve, reject ) => {
118+
const script = document.createElement('script')
119+
120+
script.async = true
121+
script.type = 'module'
122+
script.textContent = getPagefindModuleSource()
123+
124+
script.onerror = () => {
125+
delete globalThis[ pagefindLoaderPromiseKey ]
126+
reject( new Error(`Failed to load Pagefind browser module from ${ pagefindScriptURL }`) )
127+
}
128+
129+
document.head.append( script )
130+
131+
try {
132+
resolve( await waitForPagefindGlobal() )
133+
} catch ( err ) {
134+
delete globalThis[ pagefindLoaderPromiseKey ]
135+
reject( err )
136+
}
137+
} )
138+
}
139+
140+
return await globalThis[ pagefindLoaderPromiseKey ]
141+
}
142+
73143
export class PagefindClient {
74144
constructor ( options = {} ) {
75145
this.bundlePath = options.bundlePath || pagefindBundleRelativeURL
@@ -100,7 +170,7 @@ export class PagefindClient {
100170
async loadPagefindScript () {
101171
if ( this.pagefind ) return
102172

103-
const pagefindModule = await import(/* @vite-ignore */ pagefindScriptURL)
173+
const pagefindModule = await loadPagefindBrowserModule()
104174
this.pagefind = pagefindModule.default || pagefindModule
105175

106176
this.pagefind.options({

0 commit comments

Comments
 (0)