mirror of
https://github.com/ThatGuySam/doesitarm.git
synced 2026-05-15 06:35:20 -07:00
Add Pagefind indexing and browser search adapters behind a provider switch. This lets prebuild generate either Stork or Pagefind search artifacts and lets the existing search UI run against Pagefind while preserving scoped filters, excerpts, and result metadata.
181 lines
5 KiB
JavaScript
181 lines
5 KiB
JavaScript
import fs from 'fs-extra'
|
|
import * as pagefind from 'pagefind'
|
|
|
|
import {
|
|
isNonEmptyArray,
|
|
isNonEmptyString
|
|
} from '~/helpers/check-types.js'
|
|
import {
|
|
getRouteType
|
|
} from '~/helpers/app-derived.js'
|
|
import {
|
|
getAppCategory
|
|
} from '~/helpers/categories.js'
|
|
import {
|
|
pagefindLanguage,
|
|
pagefindOutputPath
|
|
} from '~/helpers/pagefind/config.js'
|
|
|
|
function getSearchListing ( sitemapEntry ) {
|
|
return sitemapEntry.payload.app || sitemapEntry.payload.listing || sitemapEntry.payload.video || null
|
|
}
|
|
|
|
function pushContentPart ( parts, value ) {
|
|
if ( !isNonEmptyString( value ) ) return
|
|
|
|
parts.push( value.trim() )
|
|
}
|
|
|
|
function pushListContentPart ( parts, values ) {
|
|
if ( !isNonEmptyArray( values ) ) return
|
|
|
|
pushContentPart( parts, values.join(', ') )
|
|
}
|
|
|
|
function normalizeFilterValue ( value ) {
|
|
if ( !isNonEmptyString( value ) ) return null
|
|
|
|
return value.replaceAll('-', '_').trim()
|
|
}
|
|
|
|
export function shouldIndexSitemapEntry ( sitemapEntry ) {
|
|
return getSearchListing( sitemapEntry ) !== null
|
|
}
|
|
|
|
export function makePagefindTitle ( sitemapEntry ) {
|
|
const listing = getSearchListing( sitemapEntry )
|
|
const routeType = getRouteType( sitemapEntry.route )
|
|
|
|
let title = listing?.name || sitemapEntry.route
|
|
|
|
if ( routeType === 'benchmarks' ) {
|
|
title = `${ title } Benchmarks`
|
|
}
|
|
|
|
return title
|
|
}
|
|
|
|
export function makePagefindContent ( sitemapEntry ) {
|
|
const listing = getSearchListing( sitemapEntry )
|
|
const routeType = getRouteType( sitemapEntry.route )
|
|
const parts = []
|
|
|
|
pushContentPart( parts, makePagefindTitle( sitemapEntry ) )
|
|
pushContentPart( parts, listing?.text )
|
|
pushContentPart( parts, listing?.content )
|
|
pushContentPart( parts, listing?.description )
|
|
pushListContentPart( parts, listing?.aliases )
|
|
pushListContentPart( parts, listing?.tags )
|
|
pushListContentPart( parts, listing?.timestamps?.map( timestamp => timestamp.fullText ) )
|
|
pushListContentPart( parts, listing?.appLinks?.map( appLink => appLink.name ) )
|
|
pushContentPart( parts, listing?.category?.label )
|
|
pushContentPart( parts, listing?.status )
|
|
|
|
if ( routeType === 'benchmarks' ) {
|
|
pushContentPart( parts, 'Benchmarks')
|
|
pushContentPart( parts, 'Apple Silicon App Tested')
|
|
}
|
|
|
|
return parts.join('\n\n')
|
|
}
|
|
|
|
export function makePagefindFilters ( sitemapEntry ) {
|
|
const listing = getSearchListing( sitemapEntry )
|
|
const routeType = getRouteType( sitemapEntry.route )
|
|
const filters = {
|
|
type: [ normalizeFilterValue( routeType ) ]
|
|
}
|
|
|
|
const status = normalizeFilterValue( listing?.status )
|
|
|
|
if ( status !== null ) {
|
|
filters.status = [ status ]
|
|
}
|
|
|
|
if ( listing?.category?.slug ) {
|
|
filters.category = [ getAppCategory( listing ).snakeSlug ]
|
|
}
|
|
|
|
return filters
|
|
}
|
|
|
|
export function mapSitemapEntryToPagefindRecord ( sitemapEntry ) {
|
|
if ( !shouldIndexSitemapEntry( sitemapEntry ) ) return null
|
|
|
|
const listing = getSearchListing( sitemapEntry )
|
|
const routeType = getRouteType( sitemapEntry.route )
|
|
const lastUpdatedTimestamp = String( listing?.lastUpdated?.timestamp || 0 )
|
|
|
|
return {
|
|
url: sitemapEntry.route,
|
|
content: makePagefindContent( sitemapEntry ),
|
|
language: pagefindLanguage,
|
|
meta: {
|
|
title: makePagefindTitle( sitemapEntry ),
|
|
text: listing?.text || '',
|
|
slug: listing?.slug || sitemapEntry.route,
|
|
categorySlug: listing?.category?.slug || 'uncategorized',
|
|
routeType,
|
|
lastUpdatedTimestamp
|
|
},
|
|
filters: makePagefindFilters( sitemapEntry ),
|
|
sort: {
|
|
updated: lastUpdatedTimestamp
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function writePagefindIndex ( sitemapEndpoints, {
|
|
outputPath = pagefindOutputPath
|
|
} = {} ) {
|
|
await fs.remove( outputPath )
|
|
|
|
const {
|
|
errors,
|
|
index
|
|
} = await pagefind.createIndex({
|
|
forceLanguage: pagefindLanguage
|
|
})
|
|
|
|
if ( errors.length > 0 ) {
|
|
throw new Error(`Pagefind createIndex errors: ${ errors.join(', ') }`)
|
|
}
|
|
|
|
if ( !index ) {
|
|
throw new Error('Pagefind index was not created')
|
|
}
|
|
|
|
let recordCount = 0
|
|
|
|
try {
|
|
for ( const sitemapEntry of sitemapEndpoints ) {
|
|
const record = mapSitemapEntryToPagefindRecord( sitemapEntry )
|
|
|
|
if ( record === null ) continue
|
|
|
|
const response = await index.addCustomRecord( record )
|
|
|
|
if ( response.errors.length > 0 ) {
|
|
throw new Error(`Pagefind addCustomRecord errors for ${ sitemapEntry.route }: ${ response.errors.join(', ') }`)
|
|
}
|
|
|
|
recordCount += 1
|
|
}
|
|
|
|
const writeResponse = await index.writeFiles({
|
|
outputPath
|
|
})
|
|
|
|
if ( writeResponse.errors.length > 0 ) {
|
|
throw new Error(`Pagefind writeFiles errors: ${ writeResponse.errors.join(', ') }`)
|
|
}
|
|
|
|
return {
|
|
outputPath,
|
|
recordCount
|
|
}
|
|
} finally {
|
|
await index.deleteIndex().catch( () => null )
|
|
await pagefind.close().catch( () => null )
|
|
}
|
|
}
|