-
-
Notifications
You must be signed in to change notification settings - Fork 270
/
index.ts
124 lines (106 loc) 路 3.61 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import type { AnyOrama, Orama, SearchParams } from '@orama/orama'
import { create as createOramaDB, insert as insertIntoOramaDB, save as saveOramaDB } from '@orama/orama'
import type { AstroConfig, AstroIntegration, RouteData } from 'astro'
import { compile } from 'html-to-text'
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import path from 'node:path'
interface AstroPage {
pathname: string
}
const isWindows = process.platform === 'win32'
const joinPath = (isWindows ? path.win32 : path).join
export const defaultSchema = {
path: 'string',
title: 'string',
h1: 'string',
content: 'string'
} as const
export type PageIndexSchema = typeof defaultSchema
export interface OramaOptions {
language: string
/**
* Controls whether generatedFilePath is filter
* using case sensitive or case insensitive comparison
* @default false
*
*/
caseSensitive?: boolean
pathMatcher: RegExp
contentSelectors?: string[]
searchOptions?: Omit<SearchParams<AnyOrama, any>, 'term'> | undefined
}
const PKG_NAME = '@orama/plugin-astro'
const titleConverter = compile({
baseElements: { selectors: ['title'] }
})
const h1Converter = compile({
baseElements: { selectors: ['h1'] }
})
async function prepareOramaDb(
dbConfig: OramaOptions,
pages: AstroPage[],
routes: RouteData[],
dir: URL
): Promise<Orama<PageIndexSchema, any, any, any>> {
const contentConverter = compile({
baseElements: {
selectors: dbConfig.contentSelectors?.length ? dbConfig.contentSelectors : ['body']
}
})
// All routes are in the same folder, we can use the first one to get the basePath
const basePath = dir.pathname.slice(isWindows ? 1 : 0)
const pathsToBeIndexed = pages
.filter(({ pathname }) => dbConfig.pathMatcher.test(pathname))
.map(({ pathname }) => {
// Some pages like 404 are generated as 404.html while others are usually pageName/index.html
const matchingPathname = routes
.find((r) => r.distURL?.pathname.endsWith(pathname.replace(/\/$/, '') + '.html'))
?.distURL?.pathname?.slice(isWindows ? 1 : 0)
return {
pathname,
generatedFilePath: matchingPathname ?? `${basePath}${pathname.replace(/\/+$/, '')}/index.html`
}
})
.filter(({ generatedFilePath }) => !!generatedFilePath)
const oramaDB = await createOramaDB({
schema: defaultSchema,
...(dbConfig.language ? { language: dbConfig.language } : undefined)
})
for (const { pathname, generatedFilePath } of pathsToBeIndexed) {
const htmlContent = readFileSync(generatedFilePath, { encoding: 'utf8' })
const title = titleConverter(htmlContent) ?? ''
const h1 = h1Converter(htmlContent) ?? ''
const content = contentConverter(htmlContent)
await insertIntoOramaDB(
oramaDB,
{
path: `/${pathname}`,
title,
h1,
content
},
dbConfig.language
)
}
return oramaDB
}
export function createPlugin(options: Record<string, OramaOptions>): AstroIntegration {
return {
name: PKG_NAME,
hooks: {
'astro:build:done': async function ({ pages, routes, dir }): Promise<void> {
const assetsDir = joinPath(dir.pathname, 'assets').slice(isWindows ? 1 : 0)
if (!existsSync(assetsDir)) {
mkdirSync(assetsDir)
}
for (const [dbName, dbConfig] of Object.entries(options)) {
const namedDb = await prepareOramaDb(dbConfig, pages, routes, dir)
writeFileSync(joinPath(assetsDir, `oramaDB_${dbName}.json`), JSON.stringify(await saveOramaDB(namedDb)), {
encoding: 'utf8'
})
}
}
}
}
}
export default createPlugin