@vuepress-plume/plugin-search 1.0.0-rc.209 → 1.0.0-rc.211

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.
@@ -1,256 +1,173 @@
1
- import MiniSearch from "minisearch";
2
- import pMap from "p-map";
3
- import { colors, getDirname, logger, path } from "vuepress/utils";
4
- import { addViteOptimizeDepsInclude, getFullLocaleConfig } from "@vuepress/helper";
5
- export * from "../shared/index.js";
6
- //#region src/node/prepareSearchIndex.ts
7
- /** Directory path for storing search index files / 存储搜索索引文件的目录路径 */
8
- const SEARCH_INDEX_DIR = "internal/minisearchIndex/";
9
- /** Map of locale paths to their MiniSearch instances / 语言路径到 MiniSearch 实例的映射 */
10
- const indexByLocales = /* @__PURE__ */ new Map();
11
- /** Cache for index objects by file path / 按文件路径缓存索引对象 */
12
- const indexCache = /* @__PURE__ */ new Map();
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
- let index = indexByLocales.get(locale);
25
- if (!index) {
26
- const segmenter = new Intl.Segmenter(lang, { granularity: "word" });
27
- index = new MiniSearch({
28
- fields: [
29
- "title",
30
- "titles",
31
- "text"
32
- ],
33
- storeFields: ["title", "titles"],
34
- tokenize(text) {
35
- return Array.from(segmenter.segment(text)).map((s) => s.segment);
36
- },
37
- ...options.miniSearch?.options
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
- let index = indexCache.get(filepath);
53
- if (!index) {
54
- index = [];
55
- indexCache.set(filepath, index);
56
- }
57
- return index;
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
- await app.writeTemp(`${SEARCH_INDEX_DIR}index.js`, "export const searchIndex = {}");
41
+ await app.writeTemp(`${SEARCH_INDEX_DIR}index.js`, 'export const searchIndex = {}')
74
42
  }
75
- /**
76
- * Prepare search indexes for all pages in the VuePress application.
77
- *
78
- * 为 VuePress 应用中的所有页面准备搜索索引。
79
- *
80
- * This function indexes all pages concurrently, creating separate MiniSearch
81
- * instances for each locale, and writes the indexes to temporary files.
82
- *
83
- * 此函数并发索引所有页面,为每个语言创建独立的 MiniSearch 实例,
84
- * 并将索引写入临时文件。
85
- *
86
- * @param options - Search index preparation options / 搜索索引准备选项
87
- * @param options.app - VuePress application instance / VuePress 应用实例
88
- * @param options.isSearchable - Function to filter searchable pages / 过滤可搜索页面的函数
89
- * @param options.searchOptions - MiniSearch configuration / MiniSearch 配置
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
- const records = [];
119
- const promises = [];
120
- for (const [locale] of indexByLocales) {
121
- const index = indexByLocales.get(locale);
122
- const filename = `searchBox-${locale.replace(/^\/|\/$/g, "").replace(/\//g, "_") || "default"}.js`;
123
- records.push(`${JSON.stringify(locale)}: () => import('@${SEARCH_INDEX_DIR}${filename}')`);
124
- promises.push(app.writeTemp(`${SEARCH_INDEX_DIR}${filename}`, `export default ${JSON.stringify(JSON.stringify(index) ?? {})}`));
125
- }
126
- promises.push(app.writeTemp(`${SEARCH_INDEX_DIR}index.js`, `export const searchIndex = {${records.join(",")}}${app.env.isDev ? `\n${genHmrCode("searchIndex")}` : ""}`));
127
- await Promise.all(promises);
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
- if (!page.filePath || page.frontmatter?.search === false) return;
146
- if (isSearchable && !isSearchable(page)) return;
147
- const fileId = page.path;
148
- const locale = page.pathLocale;
149
- const lang = page.lang;
150
- const index = getIndexByLocale(locale, lang, options);
151
- const cache = getIndexCache(fileId);
152
- const sections = splitPageIntoSections(`<h1><a href="#"><span>${page.frontmatter.title || page.title}</span></a></h1>
153
- ${page.contentRendered}`);
154
- try {
155
- if (cache && cache.length) index.removeAll(cache);
156
- } catch {}
157
- for await (const section of sections) {
158
- if (!section || !(section.text || section.titles)) break;
159
- const { anchor, text, titles } = section;
160
- const id = anchor ? [fileId, anchor].join("#") : fileId;
161
- if (index.has(id)) {
162
- if (anchor) logger.error(`${colors.green("[@vuepress-plume/plugin-search]")} duplicate heading anchor : ${colors.cyan(titles.join(" >> "))} \n at ${colors.cyan(fileId)}`);
163
- else logger.error(`${colors.green("[@vuepress-plume/plugin-search]")} duplicate page permalink : ${colors.cyan(fileId)}`);
164
- } else {
165
- const item = {
166
- id,
167
- text,
168
- title: titles.at(-1),
169
- titles: titles.slice(0, -1)
170
- };
171
- index.add(item);
172
- cache.push(item);
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
- /** Regex pattern for matching heading tags / 匹配标题标签的正则表达式 */
177
- const headingRegex = /<h(\d*).*?>(<a.*? href="#.*?".*?>[\s\S]*?<\/a>)<\/h\1>/gi;
178
- /** Regex pattern for extracting heading content / 提取标题内容的正则表达式 */
179
- const headingContentRegex = /<a.*? href="#(.*?)".*?><span>([\s\S]*?)<\/span><\/a>/i;
180
- /** Regex pattern for ignoring template content / 忽略模板内容的正则表达式 */
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
- const result = html.split(headingRegex);
197
- result.shift();
198
- let parentTitles = [];
199
- for (let i = 0; i < result.length; i += 3) {
200
- const level = Number.parseInt(result[i]) - 1;
201
- const heading = result[i + 1];
202
- const headingResult = headingContentRegex.exec(heading);
203
- const title = clearHtmlTags(headingResult?.[2] ?? "").trim();
204
- const anchor = headingResult?.[1] ?? "";
205
- const content = result[i + 2];
206
- if (!title || !content) continue;
207
- if (level === 0) parentTitles = [title];
208
- else parentTitles[level] = title;
209
- let titles = parentTitles.slice(0, level);
210
- titles[level] = title;
211
- titles = titles.filter(Boolean);
212
- yield {
213
- anchor,
214
- titles,
215
- text: getSearchableText(content)
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
- content = clearHtmlTags(content);
229
- return content;
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
- str = str.replace(ignoreHeadingRegex, "");
241
- return str.replace(/<[^>]*>/g, "");
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
- const func = `update${m[0].toUpperCase()}${m.slice(1)}`;
253
- return `
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
- //#endregion
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
- [["en", "en-US"], {
281
- placeholder: "Search",
282
- resetButtonTitle: "Reset search",
283
- backButtonTitle: "Close search",
284
- noResultsText: "No results for",
285
- footer: {
286
- selectText: "to select",
287
- selectKeyAriaLabel: "enter",
288
- navigateText: "to navigate",
289
- navigateUpKeyAriaLabel: "up arrow",
290
- navigateDownKeyAriaLabel: "down arrow",
291
- closeText: "to close",
292
- closeKeyAriaLabel: "escape"
293
- }
294
- }],
295
- [[
296
- "zh",
297
- "zh-CN",
298
- "zh-Hans",
299
- "zh-Hant"
300
- ], {
301
- placeholder: "搜索文档",
302
- resetButtonTitle: "重置搜索",
303
- backButtonTitle: "关闭",
304
- noResultsText: "无搜索结果:",
305
- footer: {
306
- selectText: "选择",
307
- selectKeyAriaLabel: "输入",
308
- navigateText: "切换",
309
- navigateUpKeyAriaLabel: "向上",
310
- navigateDownKeyAriaLabel: "向下",
311
- closeText: "关闭",
312
- closeKeyAriaLabel: "退出"
313
- }
314
- }],
315
- [["zh-TW"], {
316
- placeholder: "搜尋文件",
317
- resetButtonTitle: "重設搜尋",
318
- backButtonTitle: "關閉",
319
- noResultsText: "無搜尋結果:",
320
- footer: {
321
- selectText: "選擇",
322
- selectKeyAriaLabel: "輸入",
323
- navigateText: "切換",
324
- navigateUpKeyAriaLabel: "向上",
325
- navigateDownKeyAriaLabel: "向下",
326
- closeText: "關閉",
327
- closeKeyAriaLabel: "退出"
328
- }
329
- }],
330
- [["de", "de-DE"], {
331
- placeholder: "Dokumente durchsuchen",
332
- resetButtonTitle: "Suche zurücksetzen",
333
- backButtonTitle: "Schließen",
334
- noResultsText: "Keine Suchergebnisse:",
335
- footer: {
336
- selectText: "Auswählen",
337
- selectKeyAriaLabel: "Eingabe",
338
- navigateText: "Wechseln",
339
- navigateUpKeyAriaLabel: "Nach oben",
340
- navigateDownKeyAriaLabel: "Nach unten",
341
- closeText: "Schließen",
342
- closeKeyAriaLabel: "Beenden"
343
- }
344
- }],
345
- [["fr", "fr-FR"], {
346
- placeholder: "Rechercher dans la documentation",
347
- resetButtonTitle: "Réinitialiser la recherche",
348
- backButtonTitle: "Fermer",
349
- noResultsText: "Aucun résultat trouvé :",
350
- footer: {
351
- selectText: "sélectionner",
352
- selectKeyAriaLabel: "Entrée",
353
- navigateText: "naviguer",
354
- navigateUpKeyAriaLabel: "haut",
355
- navigateDownKeyAriaLabel: "bas",
356
- closeText: "fermer",
357
- closeKeyAriaLabel: "sortie"
358
- }
359
- }],
360
- [["ru", "ru-RU"], {
361
- placeholder: "Поиск по документации",
362
- resetButtonTitle: "Сбросить поиск",
363
- backButtonTitle: "Закрыть",
364
- noResultsText: "Нет результатов поиска:",
365
- footer: {
366
- selectText: "Выбрать",
367
- selectKeyAriaLabel: "Ввод",
368
- navigateText: "Переключить",
369
- navigateUpKeyAriaLabel: "Вверх",
370
- navigateDownKeyAriaLabel: "Вниз",
371
- closeText: "Закрыть",
372
- closeKeyAriaLabel: "Выход"
373
- }
374
- }],
375
- [["ja", "ja-JP"], {
376
- placeholder: "ドキュメントを検索",
377
- resetButtonTitle: "検索をリセット",
378
- backButtonTitle: "閉じる",
379
- noResultsText: "検索結果がありません:",
380
- footer: {
381
- selectText: "選択",
382
- selectKeyAriaLabel: "入力",
383
- navigateText: "切り替え",
384
- navigateUpKeyAriaLabel: "上へ",
385
- navigateDownKeyAriaLabel: "下へ",
386
- closeText: "閉じる",
387
- closeKeyAriaLabel: "終了"
388
- }
389
- }]
390
- ];
391
- //#endregion
392
- //#region src/node/searchPlugin.ts
393
- const __dirname = getDirname(import.meta.url);
394
- /**
395
- * Create a VuePress search plugin instance.
396
- *
397
- * 创建 VuePress 搜索插件实例。
398
- *
399
- * @param options - Plugin configuration options / 插件配置选项
400
- * @param options.locales - Locale-specific search configurations / 特定语言的搜索配置
401
- * @param options.isSearchable - Function to determine if a page should be indexed / 判断页面是否应被索引的函数
402
- * @param options.searchOptions - MiniSearch options / MiniSearch 配置选项
403
- * @returns VuePress plugin object / VuePress 插件对象
404
- * @example
405
- * // Basic usage
406
- * export default {
407
- * plugins: [
408
- * searchPlugin()
409
- * ]
410
- * }
411
- *
412
- * // With custom options
413
- * export default {
414
- * plugins: [
415
- * searchPlugin({
416
- * locales: {
417
- * '/zh/': { placeholder: '搜索文档' }
418
- * },
419
- * isSearchable: (page) => page.path !== '/secret/'
420
- * })
421
- * ]
422
- * }
423
- */
424
- function searchPlugin({ locales = {}, isSearchable, ...searchOptions } = {}) {
425
- return (app) => ({
426
- name: "@vuepress-plume/plugin-search",
427
- clientConfigFile: path.resolve(__dirname, "../client/config.js"),
428
- define: {
429
- __SEARCH_LOCALES__: getFullLocaleConfig({
430
- app,
431
- name: "@vuepress-plume/plugin-search",
432
- default: SEARCH_LOCALES,
433
- config: locales
434
- }),
435
- __SEARCH_OPTIONS__: searchOptions
436
- },
437
- extendsBundlerOptions(bundlerOptions) {
438
- addViteOptimizeDepsInclude(bundlerOptions, app, [
439
- "mark.js/src/vanilla.js",
440
- "@vueuse/integrations/useFocusTrap",
441
- "minisearch"
442
- ]);
443
- },
444
- onPrepared: async (app) => {
445
- if (app.env.isBuild) await prepareSearchIndex({
446
- app,
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
- //#endregion
462
- export { prepareSearchIndex, searchPlugin };
355
+
356
+ export {prepareSearchIndex, searchPlugin}