@vuepress-plume/plugin-search 1.0.0-rc.209 → 1.0.0-rc.210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/composables/index.d.ts +12 -11
- package/dist/client/composables/index.js +40 -62
- package/dist/client/config.js +19 -15
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.js +4 -17
- package/dist/client/utils/index.d.ts +7 -9
- package/dist/client/utils/index.js +34 -104
- package/dist/node/index.d.ts +16 -12
- package/dist/node/index.js +323 -429
- package/dist/shared/index.d.ts +22 -24
- package/dist/shared/index.js +1 -1
- package/package.json +2 -1
package/dist/node/index.js
CHANGED
|
@@ -1,256 +1,173 @@
|
|
|
1
|
-
import MiniSearch from
|
|
2
|
-
import pMap from
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const SEARCH_INDEX_DIR =
|
|
9
|
-
|
|
10
|
-
const indexByLocales =
|
|
11
|
-
|
|
12
|
-
const indexCache =
|
|
13
|
-
|
|
14
|
-
* Get or create a MiniSearch index for a specific locale.
|
|
15
|
-
*
|
|
16
|
-
* 获取或创建特定语言的 MiniSearch 索引。
|
|
17
|
-
*
|
|
18
|
-
* @param locale - Locale path (e.g., '/', '/zh/') / 语言路径
|
|
19
|
-
* @param lang - Language code for tokenization / 用于分词的语言代码
|
|
20
|
-
* @param options - Search index options / 搜索索引选项
|
|
21
|
-
* @returns MiniSearch instance for the locale / 该语言的 MiniSearch 实例
|
|
22
|
-
*/
|
|
1
|
+
import MiniSearch from 'minisearch'
|
|
2
|
+
import pMap from 'p-map'
|
|
3
|
+
import sanitizeHTML from 'sanitize-html'
|
|
4
|
+
import {colors, getDirname, logger, path} from 'vuepress/utils'
|
|
5
|
+
import {addViteOptimizeDepsInclude, getFullLocaleConfig} from '@vuepress/helper'
|
|
6
|
+
export * from '../shared/index.js'
|
|
7
|
+
|
|
8
|
+
const SEARCH_INDEX_DIR = 'internal/minisearchIndex/'
|
|
9
|
+
|
|
10
|
+
const indexByLocales = new Map()
|
|
11
|
+
|
|
12
|
+
const indexCache = new Map()
|
|
13
|
+
|
|
23
14
|
function getIndexByLocale(locale, lang, options) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
39
|
-
indexByLocales.set(locale, index);
|
|
40
|
-
}
|
|
41
|
-
return index;
|
|
15
|
+
let index = indexByLocales.get(locale)
|
|
16
|
+
if (!index) {
|
|
17
|
+
const segmenter = new Intl.Segmenter(lang, {granularity: 'word'})
|
|
18
|
+
index = new MiniSearch({
|
|
19
|
+
fields: ['title', 'titles', 'text'],
|
|
20
|
+
storeFields: ['title', 'titles'],
|
|
21
|
+
tokenize(text) {
|
|
22
|
+
return Array.from(segmenter.segment(text)).map((s) => s.segment)
|
|
23
|
+
},
|
|
24
|
+
...options.miniSearch?.options,
|
|
25
|
+
})
|
|
26
|
+
indexByLocales.set(locale, index)
|
|
27
|
+
}
|
|
28
|
+
return index
|
|
42
29
|
}
|
|
43
|
-
|
|
44
|
-
* Get or create an index cache for a specific file path.
|
|
45
|
-
*
|
|
46
|
-
* 获取或创建特定文件路径的索引缓存。
|
|
47
|
-
*
|
|
48
|
-
* @param filepath - File path to get cache for / 要获取缓存的文件路径
|
|
49
|
-
* @returns Array of index objects for the file / 该文件的索引对象数组
|
|
50
|
-
*/
|
|
30
|
+
|
|
51
31
|
function getIndexCache(filepath) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
32
|
+
let index = indexCache.get(filepath)
|
|
33
|
+
if (!index) {
|
|
34
|
+
index = []
|
|
35
|
+
indexCache.set(filepath, index)
|
|
36
|
+
}
|
|
37
|
+
return index
|
|
58
38
|
}
|
|
59
|
-
|
|
60
|
-
* Write a placeholder search index file for development mode.
|
|
61
|
-
*
|
|
62
|
-
* 为开发模式写入占位符搜索索引文件。
|
|
63
|
-
*
|
|
64
|
-
* This is used to provide an empty index before the actual index is prepared,
|
|
65
|
-
* preventing errors when the search component loads before indexing completes.
|
|
66
|
-
*
|
|
67
|
-
* 用于在实际索引准备完成之前提供空索引,
|
|
68
|
-
* 防止搜索组件在索引完成之前加载时出错。
|
|
69
|
-
*
|
|
70
|
-
* @param app - VuePress application instance / VuePress 应用实例
|
|
71
|
-
*/
|
|
39
|
+
|
|
72
40
|
async function prepareSearchIndexPlaceholder(app) {
|
|
73
|
-
|
|
41
|
+
await app.writeTemp(`${SEARCH_INDEX_DIR}index.js`, 'export const searchIndex = {}')
|
|
74
42
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*/
|
|
91
|
-
async function prepareSearchIndex({ app, isSearchable, searchOptions }) {
|
|
92
|
-
const start = performance.now();
|
|
93
|
-
indexByLocales.clear();
|
|
94
|
-
indexCache.clear();
|
|
95
|
-
await pMap(app.pages, (p) => indexFile(p, searchOptions, isSearchable), { concurrency: 64 });
|
|
96
|
-
await writeTemp(app);
|
|
97
|
-
if (app.env.isBuild) {
|
|
98
|
-
indexByLocales.clear();
|
|
99
|
-
indexCache.clear();
|
|
100
|
-
}
|
|
101
|
-
if (app.env.isDebug) logger.info(`\n[${colors.green("@vuepress-plume/plugin-search")}] prepare search time spent: ${(performance.now() - start).toFixed(2)}ms`);
|
|
43
|
+
|
|
44
|
+
async function prepareSearchIndex({app, isSearchable, searchOptions}) {
|
|
45
|
+
const start = performance.now()
|
|
46
|
+
indexByLocales.clear()
|
|
47
|
+
indexCache.clear()
|
|
48
|
+
await pMap(app.pages, (p) => indexFile(p, searchOptions, isSearchable), {concurrency: 64})
|
|
49
|
+
await writeTemp(app)
|
|
50
|
+
if (app.env.isBuild) {
|
|
51
|
+
indexByLocales.clear()
|
|
52
|
+
indexCache.clear()
|
|
53
|
+
}
|
|
54
|
+
if (app.env.isDebug)
|
|
55
|
+
logger.info(
|
|
56
|
+
`\n[${colors.green('@vuepress-plume/plugin-search')}] prepare search time spent: ${(performance.now() - start).toFixed(2)}ms`,
|
|
57
|
+
)
|
|
102
58
|
}
|
|
103
|
-
Promise.resolve()
|
|
104
|
-
|
|
105
|
-
* Write all search indexes to temporary files.
|
|
106
|
-
*
|
|
107
|
-
* 将所有搜索索引写入临时文件。
|
|
108
|
-
*
|
|
109
|
-
* Creates separate JavaScript files for each locale's search index,
|
|
110
|
-
* enabling lazy loading of indexes in the client.
|
|
111
|
-
*
|
|
112
|
-
* 为每个语言的搜索索引创建独立的 JavaScript 文件,
|
|
113
|
-
* 支持客户端延迟加载索引。
|
|
114
|
-
*
|
|
115
|
-
* @param app - VuePress application instance / VuePress 应用实例
|
|
116
|
-
*/
|
|
59
|
+
Promise.resolve()
|
|
60
|
+
|
|
117
61
|
async function writeTemp(app) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
62
|
+
const records = []
|
|
63
|
+
const promises = []
|
|
64
|
+
for (const [locale] of indexByLocales) {
|
|
65
|
+
const index = indexByLocales.get(locale)
|
|
66
|
+
const filename = `searchBox-${locale.replace(/^\/|\/$/g, '').replace(/\//g, '_') || 'default'}.js`
|
|
67
|
+
records.push(`${JSON.stringify(locale)}: () => import('@${SEARCH_INDEX_DIR}${filename}')`)
|
|
68
|
+
promises.push(
|
|
69
|
+
app.writeTemp(
|
|
70
|
+
`${SEARCH_INDEX_DIR}${filename}`,
|
|
71
|
+
`export default ${JSON.stringify(JSON.stringify(index) ?? {})}`,
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
promises.push(
|
|
76
|
+
app.writeTemp(
|
|
77
|
+
`${SEARCH_INDEX_DIR}index.js`,
|
|
78
|
+
`export const searchIndex = {${records.join(',')}}${app.env.isDev ? `\n${genHmrCode('searchIndex')}` : ''}`,
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
await Promise.all(promises)
|
|
128
82
|
}
|
|
129
|
-
|
|
130
|
-
* Index a single page for search.
|
|
131
|
-
*
|
|
132
|
-
* 为单个页面建立搜索索引。
|
|
133
|
-
*
|
|
134
|
-
* Extracts content from the page, splits it into sections based on headings,
|
|
135
|
-
* and adds each section to the appropriate locale's MiniSearch index.
|
|
136
|
-
*
|
|
137
|
-
* 从页面提取内容,根据标题将其分割为章节,
|
|
138
|
-
* 并将每个章节添加到相应语言的 MiniSearch 索引中。
|
|
139
|
-
*
|
|
140
|
-
* @param page - VuePress page object / VuePress 页面对象
|
|
141
|
-
* @param options - MiniSearch options / MiniSearch 选项
|
|
142
|
-
* @param isSearchable - Function to check if page should be indexed / 检查页面是否应被索引的函数
|
|
143
|
-
*/
|
|
83
|
+
|
|
144
84
|
async function indexFile(page, options, isSearchable) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
85
|
+
if (!page.filePath || page.frontmatter?.search === false) return
|
|
86
|
+
if (isSearchable && !isSearchable(page)) return
|
|
87
|
+
const fileId = page.path
|
|
88
|
+
const locale = page.pathLocale
|
|
89
|
+
const lang = page.lang
|
|
90
|
+
const index = getIndexByLocale(locale, lang, options)
|
|
91
|
+
const cache = getIndexCache(fileId)
|
|
92
|
+
const sections =
|
|
93
|
+
splitPageIntoSections(`<h1><a href="#"><span>${page.frontmatter.title || page.title}</span></a></h1>
|
|
94
|
+
${page.contentRendered}`)
|
|
95
|
+
try {
|
|
96
|
+
if (cache && cache.length) index.removeAll(cache)
|
|
97
|
+
} catch {}
|
|
98
|
+
for await (const section of sections) {
|
|
99
|
+
if (!section || !(section.text || section.titles)) break
|
|
100
|
+
const {anchor, text, titles} = section
|
|
101
|
+
const id = anchor ? [fileId, anchor].join('#') : fileId
|
|
102
|
+
if (index.has(id)) {
|
|
103
|
+
if (anchor)
|
|
104
|
+
logger.error(
|
|
105
|
+
`${colors.green('[@vuepress-plume/plugin-search]')} duplicate heading anchor : ${colors.cyan(titles.join(' >> '))} \n at ${colors.cyan(fileId)}`,
|
|
106
|
+
)
|
|
107
|
+
else
|
|
108
|
+
logger.error(
|
|
109
|
+
`${colors.green('[@vuepress-plume/plugin-search]')} duplicate page permalink : ${colors.cyan(fileId)}`,
|
|
110
|
+
)
|
|
111
|
+
} else {
|
|
112
|
+
const item = {
|
|
113
|
+
id,
|
|
114
|
+
text,
|
|
115
|
+
title: titles.at(-1),
|
|
116
|
+
titles: titles.slice(0, -1),
|
|
117
|
+
}
|
|
118
|
+
index.add(item)
|
|
119
|
+
cache.push(item)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
175
122
|
}
|
|
176
|
-
|
|
177
|
-
const headingRegex = /<h(\d*).*?>(<a.*? href="#.*?".*?>[\s\S]*?<\/a>)<\/h\1>/gi
|
|
178
|
-
|
|
179
|
-
const headingContentRegex = /<a.*? href="#(.*?)".*?><span>([\s\S]*?)<\/span><\/a>/i
|
|
180
|
-
|
|
181
|
-
const ignoreHeadingRegex = /<template[^>]*>[\s\S]*<\/template>/gi
|
|
182
|
-
|
|
183
|
-
* Split HTML content into sections based on heading elements.
|
|
184
|
-
*
|
|
185
|
-
* 根据标题元素将 HTML 内容分割为章节。
|
|
186
|
-
*
|
|
187
|
-
* This generator function parses HTML content and yields section objects
|
|
188
|
-
* containing anchor, titles hierarchy, and searchable text.
|
|
189
|
-
*
|
|
190
|
-
* 此生成器函数解析 HTML 内容并生成包含锚点、标题层级和可搜索文本的章节对象。
|
|
191
|
-
*
|
|
192
|
-
* @param html - HTML content to split / 要分割的 HTML 内容
|
|
193
|
-
* @yields Section objects with anchor, titles, and text / 包含锚点、标题和文本的章节对象
|
|
194
|
-
*/
|
|
123
|
+
|
|
124
|
+
const headingRegex = /<h(\d*).*?>(<a.*? href="#.*?".*?>[\s\S]*?<\/a>)<\/h\1>/gi
|
|
125
|
+
|
|
126
|
+
const headingContentRegex = /<a.*? href="#(.*?)".*?><span>([\s\S]*?)<\/span><\/a>/i
|
|
127
|
+
|
|
128
|
+
const ignoreHeadingRegex = /<template[^>]*>[\s\S]*<\/template>/gi
|
|
129
|
+
|
|
195
130
|
function* splitPageIntoSections(html) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
131
|
+
const result = html.split(headingRegex)
|
|
132
|
+
result.shift()
|
|
133
|
+
let parentTitles = []
|
|
134
|
+
for (let i = 0; i < result.length; i += 3) {
|
|
135
|
+
const level = Number.parseInt(result[i]) - 1
|
|
136
|
+
const heading = result[i + 1]
|
|
137
|
+
const headingResult = headingContentRegex.exec(heading)
|
|
138
|
+
const title = clearHtmlTags(headingResult?.[2] ?? '').trim()
|
|
139
|
+
const anchor = headingResult?.[1] ?? ''
|
|
140
|
+
const content = result[i + 2]
|
|
141
|
+
if (!title || !content) continue
|
|
142
|
+
if (level === 0) parentTitles = [title]
|
|
143
|
+
else parentTitles[level] = title
|
|
144
|
+
let titles = parentTitles.slice(0, level)
|
|
145
|
+
titles[level] = title
|
|
146
|
+
titles = titles.filter(Boolean)
|
|
147
|
+
yield {
|
|
148
|
+
anchor,
|
|
149
|
+
titles,
|
|
150
|
+
text: getSearchableText(content),
|
|
151
|
+
}
|
|
152
|
+
}
|
|
218
153
|
}
|
|
219
|
-
|
|
220
|
-
* Extract searchable text from HTML content by removing tags.
|
|
221
|
-
*
|
|
222
|
-
* 通过移除标签从 HTML 内容中提取可搜索文本。
|
|
223
|
-
*
|
|
224
|
-
* @param content - HTML content / HTML 内容
|
|
225
|
-
* @returns Plain text content / 纯文本内容
|
|
226
|
-
*/
|
|
154
|
+
|
|
227
155
|
function getSearchableText(content) {
|
|
228
|
-
|
|
229
|
-
|
|
156
|
+
content = clearHtmlTags(content)
|
|
157
|
+
return content
|
|
230
158
|
}
|
|
231
|
-
|
|
232
|
-
* Remove all HTML tags from a string.
|
|
233
|
-
*
|
|
234
|
-
* 移除字符串中的所有 HTML 标签。
|
|
235
|
-
*
|
|
236
|
-
* @param str - String containing HTML / 包含 HTML 的字符串
|
|
237
|
-
* @returns String with HTML tags removed / 移除 HTML 标签后的字符串
|
|
238
|
-
*/
|
|
159
|
+
|
|
239
160
|
function clearHtmlTags(str) {
|
|
240
|
-
|
|
241
|
-
|
|
161
|
+
str = str.replace(ignoreHeadingRegex, '')
|
|
162
|
+
return sanitizeHTML(str, {
|
|
163
|
+
allowedTags: [],
|
|
164
|
+
allowedAttributes: {},
|
|
165
|
+
})
|
|
242
166
|
}
|
|
243
|
-
|
|
244
|
-
* Generate HMR (Hot Module Replacement) code for the search index.
|
|
245
|
-
*
|
|
246
|
-
* 为搜索索引生成 HMR(热模块替换)代码。
|
|
247
|
-
*
|
|
248
|
-
* @param m - Module name / 模块名称
|
|
249
|
-
* @returns HMR code string / HMR 代码字符串
|
|
250
|
-
*/
|
|
167
|
+
|
|
251
168
|
function genHmrCode(m) {
|
|
252
|
-
|
|
253
|
-
|
|
169
|
+
const func = `update${m[0].toUpperCase()}${m.slice(1)}`
|
|
170
|
+
return `
|
|
254
171
|
if (import.meta.webpackHot) {
|
|
255
172
|
import.meta.webpackHot.accept()
|
|
256
173
|
if (__VUE_HMR_RUNTIME__.${m}) {
|
|
@@ -263,200 +180,177 @@ if (import.meta.hot) {
|
|
|
263
180
|
__VUE_HMR_RUNTIME__.${func}(${m})
|
|
264
181
|
})
|
|
265
182
|
}
|
|
266
|
-
|
|
183
|
+
`
|
|
267
184
|
}
|
|
268
|
-
|
|
269
|
-
//#region src/node/locales/index.ts
|
|
270
|
-
/**
|
|
271
|
-
* Default locale configurations for search plugin.
|
|
272
|
-
*
|
|
273
|
-
* 搜索插件的默认语言配置。
|
|
274
|
-
*
|
|
275
|
-
* Maps language codes to their respective locale strings.
|
|
276
|
-
*
|
|
277
|
-
* 将语言代码映射到相应的本地化字符串。
|
|
278
|
-
*/
|
|
185
|
+
|
|
279
186
|
const SEARCH_LOCALES = [
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
isSearchable,
|
|
448
|
-
searchOptions
|
|
449
|
-
});
|
|
450
|
-
else {
|
|
451
|
-
await prepareSearchIndexPlaceholder(app);
|
|
452
|
-
prepareSearchIndex({
|
|
453
|
-
app,
|
|
454
|
-
isSearchable,
|
|
455
|
-
searchOptions
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
});
|
|
187
|
+
[
|
|
188
|
+
['en', 'en-US'],
|
|
189
|
+
{
|
|
190
|
+
placeholder: 'Search',
|
|
191
|
+
resetButtonTitle: 'Reset search',
|
|
192
|
+
backButtonTitle: 'Close search',
|
|
193
|
+
noResultsText: 'No results for',
|
|
194
|
+
footer: {
|
|
195
|
+
selectText: 'to select',
|
|
196
|
+
selectKeyAriaLabel: 'enter',
|
|
197
|
+
navigateText: 'to navigate',
|
|
198
|
+
navigateUpKeyAriaLabel: 'up arrow',
|
|
199
|
+
navigateDownKeyAriaLabel: 'down arrow',
|
|
200
|
+
closeText: 'to close',
|
|
201
|
+
closeKeyAriaLabel: 'escape',
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
[
|
|
206
|
+
['zh', 'zh-CN', 'zh-Hans', 'zh-Hant'],
|
|
207
|
+
{
|
|
208
|
+
placeholder: '搜索文档',
|
|
209
|
+
resetButtonTitle: '重置搜索',
|
|
210
|
+
backButtonTitle: '关闭',
|
|
211
|
+
noResultsText: '无搜索结果:',
|
|
212
|
+
footer: {
|
|
213
|
+
selectText: '选择',
|
|
214
|
+
selectKeyAriaLabel: '输入',
|
|
215
|
+
navigateText: '切换',
|
|
216
|
+
navigateUpKeyAriaLabel: '向上',
|
|
217
|
+
navigateDownKeyAriaLabel: '向下',
|
|
218
|
+
closeText: '关闭',
|
|
219
|
+
closeKeyAriaLabel: '退出',
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
[
|
|
224
|
+
['zh-TW'],
|
|
225
|
+
{
|
|
226
|
+
placeholder: '搜尋文件',
|
|
227
|
+
resetButtonTitle: '重設搜尋',
|
|
228
|
+
backButtonTitle: '關閉',
|
|
229
|
+
noResultsText: '無搜尋結果:',
|
|
230
|
+
footer: {
|
|
231
|
+
selectText: '選擇',
|
|
232
|
+
selectKeyAriaLabel: '輸入',
|
|
233
|
+
navigateText: '切換',
|
|
234
|
+
navigateUpKeyAriaLabel: '向上',
|
|
235
|
+
navigateDownKeyAriaLabel: '向下',
|
|
236
|
+
closeText: '關閉',
|
|
237
|
+
closeKeyAriaLabel: '退出',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
[
|
|
242
|
+
['de', 'de-DE'],
|
|
243
|
+
{
|
|
244
|
+
placeholder: 'Dokumente durchsuchen',
|
|
245
|
+
resetButtonTitle: 'Suche zurücksetzen',
|
|
246
|
+
backButtonTitle: 'Schließen',
|
|
247
|
+
noResultsText: 'Keine Suchergebnisse:',
|
|
248
|
+
footer: {
|
|
249
|
+
selectText: 'Auswählen',
|
|
250
|
+
selectKeyAriaLabel: 'Eingabe',
|
|
251
|
+
navigateText: 'Wechseln',
|
|
252
|
+
navigateUpKeyAriaLabel: 'Nach oben',
|
|
253
|
+
navigateDownKeyAriaLabel: 'Nach unten',
|
|
254
|
+
closeText: 'Schließen',
|
|
255
|
+
closeKeyAriaLabel: 'Beenden',
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
[
|
|
260
|
+
['fr', 'fr-FR'],
|
|
261
|
+
{
|
|
262
|
+
placeholder: 'Rechercher dans la documentation',
|
|
263
|
+
resetButtonTitle: 'Réinitialiser la recherche',
|
|
264
|
+
backButtonTitle: 'Fermer',
|
|
265
|
+
noResultsText: 'Aucun résultat trouvé :',
|
|
266
|
+
footer: {
|
|
267
|
+
selectText: 'sélectionner',
|
|
268
|
+
selectKeyAriaLabel: 'Entrée',
|
|
269
|
+
navigateText: 'naviguer',
|
|
270
|
+
navigateUpKeyAriaLabel: 'haut',
|
|
271
|
+
navigateDownKeyAriaLabel: 'bas',
|
|
272
|
+
closeText: 'fermer',
|
|
273
|
+
closeKeyAriaLabel: 'sortie',
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
[
|
|
278
|
+
['ru', 'ru-RU'],
|
|
279
|
+
{
|
|
280
|
+
placeholder: 'Поиск по документации',
|
|
281
|
+
resetButtonTitle: 'Сбросить поиск',
|
|
282
|
+
backButtonTitle: 'Закрыть',
|
|
283
|
+
noResultsText: 'Нет результатов поиска:',
|
|
284
|
+
footer: {
|
|
285
|
+
selectText: 'Выбрать',
|
|
286
|
+
selectKeyAriaLabel: 'Ввод',
|
|
287
|
+
navigateText: 'Переключить',
|
|
288
|
+
navigateUpKeyAriaLabel: 'Вверх',
|
|
289
|
+
navigateDownKeyAriaLabel: 'Вниз',
|
|
290
|
+
closeText: 'Закрыть',
|
|
291
|
+
closeKeyAriaLabel: 'Выход',
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
[
|
|
296
|
+
['ja', 'ja-JP'],
|
|
297
|
+
{
|
|
298
|
+
placeholder: 'ドキュメントを検索',
|
|
299
|
+
resetButtonTitle: '検索をリセット',
|
|
300
|
+
backButtonTitle: '閉じる',
|
|
301
|
+
noResultsText: '検索結果がありません:',
|
|
302
|
+
footer: {
|
|
303
|
+
selectText: '選択',
|
|
304
|
+
selectKeyAriaLabel: '入力',
|
|
305
|
+
navigateText: '切り替え',
|
|
306
|
+
navigateUpKeyAriaLabel: '上へ',
|
|
307
|
+
navigateDownKeyAriaLabel: '下へ',
|
|
308
|
+
closeText: '閉じる',
|
|
309
|
+
closeKeyAriaLabel: '終了',
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
const __dirname = getDirname(import.meta.url)
|
|
316
|
+
|
|
317
|
+
function searchPlugin({locales = {}, isSearchable, ...searchOptions} = {}) {
|
|
318
|
+
return (app) => ({
|
|
319
|
+
name: '@vuepress-plume/plugin-search',
|
|
320
|
+
clientConfigFile: path.resolve(__dirname, '../client/config.js'),
|
|
321
|
+
define: {
|
|
322
|
+
__SEARCH_LOCALES__: getFullLocaleConfig({
|
|
323
|
+
app,
|
|
324
|
+
name: '@vuepress-plume/plugin-search',
|
|
325
|
+
default: SEARCH_LOCALES,
|
|
326
|
+
config: locales,
|
|
327
|
+
}),
|
|
328
|
+
__SEARCH_OPTIONS__: searchOptions,
|
|
329
|
+
},
|
|
330
|
+
extendsBundlerOptions(bundlerOptions) {
|
|
331
|
+
addViteOptimizeDepsInclude(bundlerOptions, app, [
|
|
332
|
+
'mark.js/src/vanilla.js',
|
|
333
|
+
'@vueuse/integrations/useFocusTrap',
|
|
334
|
+
'minisearch',
|
|
335
|
+
])
|
|
336
|
+
},
|
|
337
|
+
onPrepared: async (app) => {
|
|
338
|
+
if (app.env.isBuild)
|
|
339
|
+
await prepareSearchIndex({
|
|
340
|
+
app,
|
|
341
|
+
isSearchable,
|
|
342
|
+
searchOptions,
|
|
343
|
+
})
|
|
344
|
+
else {
|
|
345
|
+
await prepareSearchIndexPlaceholder(app)
|
|
346
|
+
prepareSearchIndex({
|
|
347
|
+
app,
|
|
348
|
+
isSearchable,
|
|
349
|
+
searchOptions,
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
})
|
|
460
354
|
}
|
|
461
|
-
|
|
462
|
-
export {
|
|
355
|
+
|
|
356
|
+
export {prepareSearchIndex, searchPlugin}
|