@vuepress-plume/plugin-search 1.0.0-rc.192 → 1.0.0-rc.193
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/lib/client/composables/index.d.ts +30 -0
- package/lib/client/composables/index.js +32 -4
- package/lib/client/config.js +1 -3
- package/lib/client/index.js +15 -2
- package/lib/client/utils/index.d.ts +75 -0
- package/lib/client/utils/index.js +76 -2
- package/lib/node/index.d.ts +54 -0
- package/lib/node/index.js +301 -176
- package/lib/shared/index.d.ts +58 -6
- package/lib/shared/index.js +1 -1
- package/package.json +6 -6
|
@@ -2,12 +2,42 @@ import { ComputedRef, MaybeRef, ShallowRef } from "vue";
|
|
|
2
2
|
import { SearchBoxLocales, SearchLocaleOptions } from "../../shared/index.js";
|
|
3
3
|
|
|
4
4
|
//#region src/client/composables/locale.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Get locale configuration for the current route.
|
|
7
|
+
*
|
|
8
|
+
* 获取当前路由的语言配置。
|
|
9
|
+
*
|
|
10
|
+
* @param locales - Locale configuration object / 语言配置对象
|
|
11
|
+
* @returns Computed ref to the current locale settings / 当前语言设置的计算引用
|
|
12
|
+
* @example
|
|
13
|
+
* const locale = useLocale(locales)
|
|
14
|
+
* console.log(locale.value.placeholder) // 'Search' or localized value
|
|
15
|
+
*/
|
|
5
16
|
declare function useLocale(locales: MaybeRef<SearchBoxLocales>): ComputedRef<Partial<SearchLocaleOptions>>;
|
|
6
17
|
//#endregion
|
|
7
18
|
//#region src/client/composables/searchIndex.d.ts
|
|
19
|
+
/**
|
|
20
|
+
* Type definition for search index data.
|
|
21
|
+
*
|
|
22
|
+
* 搜索索引数据的类型定义。
|
|
23
|
+
*
|
|
24
|
+
* Maps locale paths to functions that load the corresponding search index.
|
|
25
|
+
*
|
|
26
|
+
* 将语言路径映射到加载相应搜索索引的函数。
|
|
27
|
+
*/
|
|
8
28
|
type SearchIndexData = Record<string, () => Promise<{
|
|
9
29
|
default: string;
|
|
10
30
|
}>>;
|
|
31
|
+
/**
|
|
32
|
+
* Get the search index data for all locales.
|
|
33
|
+
*
|
|
34
|
+
* 获取所有语言的搜索索引数据。
|
|
35
|
+
*
|
|
36
|
+
* @returns Shallow ref to the search index data / 搜索索引数据的浅层引用
|
|
37
|
+
* @example
|
|
38
|
+
* const indexData = useSearchIndex()
|
|
39
|
+
* const localeIndex = await indexData.value['/zh/']()
|
|
40
|
+
*/
|
|
11
41
|
declare function useSearchIndex(): ShallowRef<SearchIndexData>;
|
|
12
42
|
//#endregion
|
|
13
43
|
export { useLocale, useSearchIndex };
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { computed, shallowRef, toRef } from "vue";
|
|
2
2
|
import { useRouteLocale } from "vuepress/client";
|
|
3
3
|
import { searchIndex } from "@internal/minisearchIndex";
|
|
4
|
-
|
|
5
4
|
//#region src/client/composables/locale.ts
|
|
5
|
+
/**
|
|
6
|
+
* Default locale configuration for search.
|
|
7
|
+
*
|
|
8
|
+
* 搜索的默认语言配置。
|
|
9
|
+
*/
|
|
6
10
|
const defaultLocales = { "/": {
|
|
7
11
|
placeholder: "Search",
|
|
8
12
|
resetButtonTitle: "Reset search",
|
|
@@ -18,21 +22,45 @@ const defaultLocales = { "/": {
|
|
|
18
22
|
closeKeyAriaLabel: "escape"
|
|
19
23
|
}
|
|
20
24
|
} };
|
|
25
|
+
/**
|
|
26
|
+
* Get locale configuration for the current route.
|
|
27
|
+
*
|
|
28
|
+
* 获取当前路由的语言配置。
|
|
29
|
+
*
|
|
30
|
+
* @param locales - Locale configuration object / 语言配置对象
|
|
31
|
+
* @returns Computed ref to the current locale settings / 当前语言设置的计算引用
|
|
32
|
+
* @example
|
|
33
|
+
* const locale = useLocale(locales)
|
|
34
|
+
* console.log(locale.value.placeholder) // 'Search' or localized value
|
|
35
|
+
*/
|
|
21
36
|
function useLocale(locales) {
|
|
22
37
|
const localesRef = toRef(locales);
|
|
23
38
|
const routeLocale = useRouteLocale();
|
|
24
39
|
return computed(() => localesRef.value[routeLocale.value] ?? defaultLocales[routeLocale.value] ?? defaultLocales["/"]);
|
|
25
40
|
}
|
|
26
|
-
|
|
27
41
|
//#endregion
|
|
28
42
|
//#region src/client/composables/searchIndex.ts
|
|
43
|
+
/**
|
|
44
|
+
* Reactive reference to the search index data.
|
|
45
|
+
*
|
|
46
|
+
* 搜索索引数据的响应式引用。
|
|
47
|
+
*/
|
|
29
48
|
const searchIndexData = shallowRef(searchIndex);
|
|
49
|
+
/**
|
|
50
|
+
* Get the search index data for all locales.
|
|
51
|
+
*
|
|
52
|
+
* 获取所有语言的搜索索引数据。
|
|
53
|
+
*
|
|
54
|
+
* @returns Shallow ref to the search index data / 搜索索引数据的浅层引用
|
|
55
|
+
* @example
|
|
56
|
+
* const indexData = useSearchIndex()
|
|
57
|
+
* const localeIndex = await indexData.value['/zh/']()
|
|
58
|
+
*/
|
|
30
59
|
function useSearchIndex() {
|
|
31
60
|
return searchIndexData;
|
|
32
61
|
}
|
|
33
62
|
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) __VUE_HMR_RUNTIME__.updateSearchIndex = (data) => {
|
|
34
63
|
searchIndexData.value = data;
|
|
35
64
|
};
|
|
36
|
-
|
|
37
65
|
//#endregion
|
|
38
|
-
export { useLocale, useSearchIndex };
|
|
66
|
+
export { useLocale, useSearchIndex };
|
package/lib/client/config.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { h } from "vue";
|
|
2
2
|
import { defineClientConfig } from "vuepress/client";
|
|
3
3
|
import Search from "./components/Search.vue";
|
|
4
|
-
|
|
5
4
|
//#region src/client/config.ts
|
|
6
5
|
const locales = __SEARCH_LOCALES__;
|
|
7
6
|
const searchOptions = __SEARCH_OPTIONS__;
|
|
@@ -12,6 +11,5 @@ var config_default = defineClientConfig({ enhance({ app }) {
|
|
|
12
11
|
...props
|
|
13
12
|
}));
|
|
14
13
|
} });
|
|
15
|
-
|
|
16
14
|
//#endregion
|
|
17
|
-
export { config_default as default };
|
|
15
|
+
export { config_default as default };
|
package/lib/client/index.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import SearchBox from "./components/Search.vue";
|
|
2
2
|
import { useSearchIndex } from "./composables/index.js";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
//#region src/client/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* VuePress Search Plugin - Client Side Entry
|
|
6
|
+
*
|
|
7
|
+
* VuePress 搜索插件 - 客户端入口
|
|
8
|
+
*
|
|
9
|
+
* Exports the SearchBox component and search index composable for use in
|
|
10
|
+
* VuePress theme components.
|
|
11
|
+
*
|
|
12
|
+
* 导出 SearchBox 组件和搜索索引组合式函数,供 VuePress 主题组件使用。
|
|
13
|
+
*
|
|
14
|
+
* @module plugin-search/client
|
|
15
|
+
*/
|
|
16
|
+
//#endregion
|
|
17
|
+
export { SearchBox, useSearchIndex };
|
|
@@ -1,11 +1,86 @@
|
|
|
1
1
|
//#region src/client/utils/lru.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* LRU (Least Recently Used) Cache Implementation
|
|
4
|
+
*
|
|
5
|
+
* LRU(最近最少使用)缓存实现
|
|
6
|
+
*
|
|
7
|
+
* Adapted from https://stackoverflow.com/a/46432113/11613622
|
|
8
|
+
*
|
|
9
|
+
* @module plugin-search/client/utils/lru
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Generic LRU Cache implementation using Map.
|
|
13
|
+
*
|
|
14
|
+
* 使用 Map 实现的通用 LRU 缓存。
|
|
15
|
+
*
|
|
16
|
+
* Automatically evicts the least recently used item when the cache reaches
|
|
17
|
+
* its maximum size.
|
|
18
|
+
*
|
|
19
|
+
* 当缓存达到最大容量时,自动淘汰最近最少使用的项。
|
|
20
|
+
*
|
|
21
|
+
* @template K - Key type / 键类型
|
|
22
|
+
* @template V - Value type / 值类型
|
|
23
|
+
* @example
|
|
24
|
+
* const cache = new LRUCache<string, number>(3)
|
|
25
|
+
* cache.set('a', 1)
|
|
26
|
+
* cache.set('b', 2)
|
|
27
|
+
* cache.set('c', 3)
|
|
28
|
+
* cache.set('d', 4) // 'a' is evicted
|
|
29
|
+
* cache.get('b') // returns 2, 'b' becomes most recent
|
|
30
|
+
*/
|
|
2
31
|
declare class LRUCache<K, V> {
|
|
32
|
+
/** Maximum number of items in the cache / 缓存中的最大项数 */
|
|
3
33
|
private max;
|
|
34
|
+
/** Internal Map storage / 内部 Map 存储 */
|
|
4
35
|
private cache;
|
|
36
|
+
/**
|
|
37
|
+
* Create a new LRU Cache instance.
|
|
38
|
+
*
|
|
39
|
+
* 创建新的 LRU 缓存实例。
|
|
40
|
+
*
|
|
41
|
+
* @param max - Maximum cache size (default: 10) / 最大缓存大小(默认:10)
|
|
42
|
+
*/
|
|
5
43
|
constructor(max?: number);
|
|
44
|
+
/**
|
|
45
|
+
* Get a value from the cache.
|
|
46
|
+
*
|
|
47
|
+
* 从缓存中获取值。
|
|
48
|
+
*
|
|
49
|
+
* Accessing an item moves it to the end (most recently used position).
|
|
50
|
+
*
|
|
51
|
+
* 访问项会将其移动到末尾(最近使用的位置)。
|
|
52
|
+
*
|
|
53
|
+
* @param key - Cache key / 缓存键
|
|
54
|
+
* @returns Cached value or undefined if not found / 缓存值,未找到则返回 undefined
|
|
55
|
+
*/
|
|
6
56
|
get(key: K): V | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Set a value in the cache.
|
|
59
|
+
*
|
|
60
|
+
* 在缓存中设置值。
|
|
61
|
+
*
|
|
62
|
+
* If the key already exists, it is moved to the end. If the cache is full,
|
|
63
|
+
* the oldest item is evicted.
|
|
64
|
+
*
|
|
65
|
+
* 如果键已存在,则将其移动到末尾。如果缓存已满,则淘汰最旧的项。
|
|
66
|
+
*
|
|
67
|
+
* @param key - Cache key / 缓存键
|
|
68
|
+
* @param val - Value to cache / 要缓存的值
|
|
69
|
+
*/
|
|
7
70
|
set(key: K, val: V): void;
|
|
71
|
+
/**
|
|
72
|
+
* Get the first (oldest) key in the cache.
|
|
73
|
+
*
|
|
74
|
+
* 获取缓存中的第一个(最旧的)键。
|
|
75
|
+
*
|
|
76
|
+
* @returns The oldest key or undefined if cache is empty / 最旧的键,缓存为空则返回 undefined
|
|
77
|
+
*/
|
|
8
78
|
first(): K | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Clear all items from the cache.
|
|
81
|
+
*
|
|
82
|
+
* 清空缓存中的所有项。
|
|
83
|
+
*/
|
|
9
84
|
clear(): void;
|
|
10
85
|
}
|
|
11
86
|
//#endregion
|
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
//#region src/client/utils/lru.ts
|
|
2
|
+
/**
|
|
3
|
+
* LRU (Least Recently Used) Cache Implementation
|
|
4
|
+
*
|
|
5
|
+
* LRU(最近最少使用)缓存实现
|
|
6
|
+
*
|
|
7
|
+
* Adapted from https://stackoverflow.com/a/46432113/11613622
|
|
8
|
+
*
|
|
9
|
+
* @module plugin-search/client/utils/lru
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Generic LRU Cache implementation using Map.
|
|
13
|
+
*
|
|
14
|
+
* 使用 Map 实现的通用 LRU 缓存。
|
|
15
|
+
*
|
|
16
|
+
* Automatically evicts the least recently used item when the cache reaches
|
|
17
|
+
* its maximum size.
|
|
18
|
+
*
|
|
19
|
+
* 当缓存达到最大容量时,自动淘汰最近最少使用的项。
|
|
20
|
+
*
|
|
21
|
+
* @template K - Key type / 键类型
|
|
22
|
+
* @template V - Value type / 值类型
|
|
23
|
+
* @example
|
|
24
|
+
* const cache = new LRUCache<string, number>(3)
|
|
25
|
+
* cache.set('a', 1)
|
|
26
|
+
* cache.set('b', 2)
|
|
27
|
+
* cache.set('c', 3)
|
|
28
|
+
* cache.set('d', 4) // 'a' is evicted
|
|
29
|
+
* cache.get('b') // returns 2, 'b' becomes most recent
|
|
30
|
+
*/
|
|
2
31
|
var LRUCache = class {
|
|
32
|
+
/** Maximum number of items in the cache / 缓存中的最大项数 */
|
|
3
33
|
max;
|
|
34
|
+
/** Internal Map storage / 内部 Map 存储 */
|
|
4
35
|
cache;
|
|
36
|
+
/**
|
|
37
|
+
* Create a new LRU Cache instance.
|
|
38
|
+
*
|
|
39
|
+
* 创建新的 LRU 缓存实例。
|
|
40
|
+
*
|
|
41
|
+
* @param max - Maximum cache size (default: 10) / 最大缓存大小(默认:10)
|
|
42
|
+
*/
|
|
5
43
|
constructor(max = 10) {
|
|
6
44
|
this.max = max;
|
|
7
45
|
this.cache = /* @__PURE__ */ new Map();
|
|
8
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Get a value from the cache.
|
|
49
|
+
*
|
|
50
|
+
* 从缓存中获取值。
|
|
51
|
+
*
|
|
52
|
+
* Accessing an item moves it to the end (most recently used position).
|
|
53
|
+
*
|
|
54
|
+
* 访问项会将其移动到末尾(最近使用的位置)。
|
|
55
|
+
*
|
|
56
|
+
* @param key - Cache key / 缓存键
|
|
57
|
+
* @returns Cached value or undefined if not found / 缓存值,未找到则返回 undefined
|
|
58
|
+
*/
|
|
9
59
|
get(key) {
|
|
10
60
|
const item = this.cache.get(key);
|
|
11
61
|
if (item !== void 0) {
|
|
@@ -14,18 +64,42 @@ var LRUCache = class {
|
|
|
14
64
|
}
|
|
15
65
|
return item;
|
|
16
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Set a value in the cache.
|
|
69
|
+
*
|
|
70
|
+
* 在缓存中设置值。
|
|
71
|
+
*
|
|
72
|
+
* If the key already exists, it is moved to the end. If the cache is full,
|
|
73
|
+
* the oldest item is evicted.
|
|
74
|
+
*
|
|
75
|
+
* 如果键已存在,则将其移动到末尾。如果缓存已满,则淘汰最旧的项。
|
|
76
|
+
*
|
|
77
|
+
* @param key - Cache key / 缓存键
|
|
78
|
+
* @param val - Value to cache / 要缓存的值
|
|
79
|
+
*/
|
|
17
80
|
set(key, val) {
|
|
18
81
|
if (this.cache.has(key)) this.cache.delete(key);
|
|
19
82
|
else if (this.cache.size === this.max) this.cache.delete(this.first());
|
|
20
83
|
this.cache.set(key, val);
|
|
21
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the first (oldest) key in the cache.
|
|
87
|
+
*
|
|
88
|
+
* 获取缓存中的第一个(最旧的)键。
|
|
89
|
+
*
|
|
90
|
+
* @returns The oldest key or undefined if cache is empty / 最旧的键,缓存为空则返回 undefined
|
|
91
|
+
*/
|
|
22
92
|
first() {
|
|
23
93
|
return this.cache.keys().next().value;
|
|
24
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Clear all items from the cache.
|
|
97
|
+
*
|
|
98
|
+
* 清空缓存中的所有项。
|
|
99
|
+
*/
|
|
25
100
|
clear() {
|
|
26
101
|
this.cache.clear();
|
|
27
102
|
}
|
|
28
103
|
};
|
|
29
|
-
|
|
30
104
|
//#endregion
|
|
31
|
-
export { LRUCache };
|
|
105
|
+
export { LRUCache };
|
package/lib/node/index.d.ts
CHANGED
|
@@ -3,11 +3,35 @@ import { App, Plugin } from "vuepress/core";
|
|
|
3
3
|
export * from "../shared/index.js";
|
|
4
4
|
|
|
5
5
|
//#region src/node/prepareSearchIndex.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Options for search index preparation.
|
|
8
|
+
*
|
|
9
|
+
* 搜索索引准备的配置选项。
|
|
10
|
+
*/
|
|
6
11
|
interface SearchIndexOptions {
|
|
12
|
+
/** VuePress application instance / VuePress 应用实例 */
|
|
7
13
|
app: App;
|
|
14
|
+
/** MiniSearch configuration options / MiniSearch 配置选项 */
|
|
8
15
|
searchOptions: SearchOptions;
|
|
16
|
+
/** Function to filter searchable pages / 过滤可搜索页面的函数 */
|
|
9
17
|
isSearchable: SearchPluginOptions['isSearchable'];
|
|
10
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Prepare search indexes for all pages in the VuePress application.
|
|
21
|
+
*
|
|
22
|
+
* 为 VuePress 应用中的所有页面准备搜索索引。
|
|
23
|
+
*
|
|
24
|
+
* This function indexes all pages concurrently, creating separate MiniSearch
|
|
25
|
+
* instances for each locale, and writes the indexes to temporary files.
|
|
26
|
+
*
|
|
27
|
+
* 此函数并发索引所有页面,为每个语言创建独立的 MiniSearch 实例,
|
|
28
|
+
* 并将索引写入临时文件。
|
|
29
|
+
*
|
|
30
|
+
* @param options - Search index preparation options / 搜索索引准备选项
|
|
31
|
+
* @param options.app - VuePress application instance / VuePress 应用实例
|
|
32
|
+
* @param options.isSearchable - Function to filter searchable pages / 过滤可搜索页面的函数
|
|
33
|
+
* @param options.searchOptions - MiniSearch configuration / MiniSearch 配置
|
|
34
|
+
*/
|
|
11
35
|
declare function prepareSearchIndex({
|
|
12
36
|
app,
|
|
13
37
|
isSearchable,
|
|
@@ -15,6 +39,36 @@ declare function prepareSearchIndex({
|
|
|
15
39
|
}: SearchIndexOptions): Promise<void>;
|
|
16
40
|
//#endregion
|
|
17
41
|
//#region src/node/searchPlugin.d.ts
|
|
42
|
+
/**
|
|
43
|
+
* Create a VuePress search plugin instance.
|
|
44
|
+
*
|
|
45
|
+
* 创建 VuePress 搜索插件实例。
|
|
46
|
+
*
|
|
47
|
+
* @param options - Plugin configuration options / 插件配置选项
|
|
48
|
+
* @param options.locales - Locale-specific search configurations / 特定语言的搜索配置
|
|
49
|
+
* @param options.isSearchable - Function to determine if a page should be indexed / 判断页面是否应被索引的函数
|
|
50
|
+
* @param options.searchOptions - MiniSearch options / MiniSearch 配置选项
|
|
51
|
+
* @returns VuePress plugin object / VuePress 插件对象
|
|
52
|
+
* @example
|
|
53
|
+
* // Basic usage
|
|
54
|
+
* export default {
|
|
55
|
+
* plugins: [
|
|
56
|
+
* searchPlugin()
|
|
57
|
+
* ]
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* // With custom options
|
|
61
|
+
* export default {
|
|
62
|
+
* plugins: [
|
|
63
|
+
* searchPlugin({
|
|
64
|
+
* locales: {
|
|
65
|
+
* '/zh/': { placeholder: '搜索文档' }
|
|
66
|
+
* },
|
|
67
|
+
* isSearchable: (page) => page.path !== '/secret/'
|
|
68
|
+
* })
|
|
69
|
+
* ]
|
|
70
|
+
* }
|
|
71
|
+
*/
|
|
18
72
|
declare function searchPlugin({
|
|
19
73
|
locales,
|
|
20
74
|
isSearchable,
|
package/lib/node/index.js
CHANGED
|
@@ -2,14 +2,24 @@ import MiniSearch from "minisearch";
|
|
|
2
2
|
import pMap from "p-map";
|
|
3
3
|
import { colors, getDirname, logger, path } from "vuepress/utils";
|
|
4
4
|
import { addViteOptimizeDepsInclude, getFullLocaleConfig } from "@vuepress/helper";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export * from "../shared/index.js"
|
|
8
|
-
|
|
5
|
+
export * from "../shared/index.js";
|
|
9
6
|
//#region src/node/prepareSearchIndex.ts
|
|
7
|
+
/** Directory path for storing search index files / 存储搜索索引文件的目录路径 */
|
|
10
8
|
const SEARCH_INDEX_DIR = "internal/minisearchIndex/";
|
|
9
|
+
/** Map of locale paths to their MiniSearch instances / 语言路径到 MiniSearch 实例的映射 */
|
|
11
10
|
const indexByLocales = /* @__PURE__ */ new Map();
|
|
11
|
+
/** Cache for index objects by file path / 按文件路径缓存索引对象 */
|
|
12
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
|
+
*/
|
|
13
23
|
function getIndexByLocale(locale, lang, options) {
|
|
14
24
|
let index = indexByLocales.get(locale);
|
|
15
25
|
if (!index) {
|
|
@@ -30,6 +40,14 @@ function getIndexByLocale(locale, lang, options) {
|
|
|
30
40
|
}
|
|
31
41
|
return index;
|
|
32
42
|
}
|
|
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
|
+
*/
|
|
33
51
|
function getIndexCache(filepath) {
|
|
34
52
|
let index = indexCache.get(filepath);
|
|
35
53
|
if (!index) {
|
|
@@ -38,9 +56,38 @@ function getIndexCache(filepath) {
|
|
|
38
56
|
}
|
|
39
57
|
return index;
|
|
40
58
|
}
|
|
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
|
+
*/
|
|
41
72
|
async function prepareSearchIndexPlaceholder(app) {
|
|
42
73
|
await app.writeTemp(`${SEARCH_INDEX_DIR}index.js`, "export const searchIndex = {}");
|
|
43
74
|
}
|
|
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
|
+
*/
|
|
44
91
|
async function prepareSearchIndex({ app, isSearchable, searchOptions }) {
|
|
45
92
|
const start = performance.now();
|
|
46
93
|
indexByLocales.clear();
|
|
@@ -53,15 +100,36 @@ async function prepareSearchIndex({ app, isSearchable, searchOptions }) {
|
|
|
53
100
|
}
|
|
54
101
|
if (app.env.isDebug) logger.info(`\n[${colors.green("@vuepress-plume/plugin-search")}] prepare search time spent: ${(performance.now() - start).toFixed(2)}ms`);
|
|
55
102
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Handle search index update when a page is modified.
|
|
105
|
+
*
|
|
106
|
+
* 当页面被修改时处理搜索索引更新。
|
|
107
|
+
*
|
|
108
|
+
* @param app - VuePress application instance / VuePress 应用实例
|
|
109
|
+
* @param options - Search index preparation options / 搜索索引准备选项
|
|
110
|
+
* @param options.page - VuePress page instance / VuePress 页面实例
|
|
111
|
+
* @param options.isSearchable - Function to filter searchable pages / 过滤可搜索页面的函数
|
|
112
|
+
* @param options.searchOptions - MiniSearch configuration / MiniSearch 配置
|
|
113
|
+
*/
|
|
114
|
+
async function onSearchIndexUpdated(app, { page, isSearchable, searchOptions }) {
|
|
115
|
+
if (isSearchable && !isSearchable(page)) return;
|
|
116
|
+
await indexFile(page, searchOptions, isSearchable);
|
|
117
|
+
await writeTemp(app);
|
|
61
118
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Handle search index update when a page is removed.
|
|
121
|
+
*
|
|
122
|
+
* 当页面被删除时处理搜索索引更新。
|
|
123
|
+
*
|
|
124
|
+
* @param app - VuePress application instance / VuePress 应用实例
|
|
125
|
+
* @param options - Search index preparation options / 搜索索引准备选项
|
|
126
|
+
* @param options.page - VuePress page instance / VuePress 页面实例
|
|
127
|
+
* @param options.isSearchable - Function to filter searchable pages / 过滤可搜索页面的函数
|
|
128
|
+
* @param options.searchOptions - MiniSearch configuration / MiniSearch 配置
|
|
129
|
+
*/
|
|
130
|
+
async function onSearchIndexRemoved(app, { page, isSearchable, searchOptions }) {
|
|
131
|
+
if (isSearchable && !isSearchable(page)) return;
|
|
132
|
+
if (page.filePathRelative) {
|
|
65
133
|
const fileId = page.path;
|
|
66
134
|
const locale = page.pathLocale;
|
|
67
135
|
const lang = page.lang;
|
|
@@ -71,6 +139,19 @@ async function onSearchIndexRemoved(filepath, { app, isSearchable, searchOptions
|
|
|
71
139
|
await writeTemp(app);
|
|
72
140
|
}
|
|
73
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Write all search indexes to temporary files.
|
|
144
|
+
*
|
|
145
|
+
* 将所有搜索索引写入临时文件。
|
|
146
|
+
*
|
|
147
|
+
* Creates separate JavaScript files for each locale's search index,
|
|
148
|
+
* enabling lazy loading of indexes in the client.
|
|
149
|
+
*
|
|
150
|
+
* 为每个语言的搜索索引创建独立的 JavaScript 文件,
|
|
151
|
+
* 支持客户端延迟加载索引。
|
|
152
|
+
*
|
|
153
|
+
* @param app - VuePress application instance / VuePress 应用实例
|
|
154
|
+
*/
|
|
74
155
|
async function writeTemp(app) {
|
|
75
156
|
const records = [];
|
|
76
157
|
const promises = [];
|
|
@@ -83,6 +164,21 @@ async function writeTemp(app) {
|
|
|
83
164
|
promises.push(app.writeTemp(`${SEARCH_INDEX_DIR}index.js`, `export const searchIndex = {${records.join(",")}}${app.env.isDev ? `\n${genHmrCode("searchIndex")}` : ""}`));
|
|
84
165
|
await Promise.all(promises);
|
|
85
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Index a single page for search.
|
|
169
|
+
*
|
|
170
|
+
* 为单个页面建立搜索索引。
|
|
171
|
+
*
|
|
172
|
+
* Extracts content from the page, splits it into sections based on headings,
|
|
173
|
+
* and adds each section to the appropriate locale's MiniSearch index.
|
|
174
|
+
*
|
|
175
|
+
* 从页面提取内容,根据标题将其分割为章节,
|
|
176
|
+
* 并将每个章节添加到相应语言的 MiniSearch 索引中。
|
|
177
|
+
*
|
|
178
|
+
* @param page - VuePress page object / VuePress 页面对象
|
|
179
|
+
* @param options - MiniSearch options / MiniSearch 选项
|
|
180
|
+
* @param isSearchable - Function to check if page should be indexed / 检查页面是否应被索引的函数
|
|
181
|
+
*/
|
|
86
182
|
async function indexFile(page, options, isSearchable) {
|
|
87
183
|
if (!page.filePath || page.frontmatter?.search === false) return;
|
|
88
184
|
if (isSearchable && !isSearchable(page)) return;
|
|
@@ -112,11 +208,24 @@ ${page.contentRendered}`);
|
|
|
112
208
|
}
|
|
113
209
|
}
|
|
114
210
|
}
|
|
211
|
+
/** Regex pattern for matching heading tags / 匹配标题标签的正则表达式 */
|
|
115
212
|
const headingRegex = /<h(\d*).*?>(<a.*? href="#.*?".*?>[\s\S]*?<\/a>)<\/h\1>/gi;
|
|
213
|
+
/** Regex pattern for extracting heading content / 提取标题内容的正则表达式 */
|
|
116
214
|
const headingContentRegex = /<a.*? href="#(.*?)".*?><span>([\s\S]*?)<\/span><\/a>/i;
|
|
215
|
+
/** Regex pattern for ignoring template content / 忽略模板内容的正则表达式 */
|
|
117
216
|
const ignoreHeadingRegex = /<template[^>]*>[\s\S]*<\/template>/gi;
|
|
118
217
|
/**
|
|
119
|
-
*
|
|
218
|
+
* Split HTML content into sections based on heading elements.
|
|
219
|
+
*
|
|
220
|
+
* 根据标题元素将 HTML 内容分割为章节。
|
|
221
|
+
*
|
|
222
|
+
* This generator function parses HTML content and yields section objects
|
|
223
|
+
* containing anchor, titles hierarchy, and searchable text.
|
|
224
|
+
*
|
|
225
|
+
* 此生成器函数解析 HTML 内容并生成包含锚点、标题层级和可搜索文本的章节对象。
|
|
226
|
+
*
|
|
227
|
+
* @param html - HTML content to split / 要分割的 HTML 内容
|
|
228
|
+
* @yields Section objects with anchor, titles, and text / 包含锚点、标题和文本的章节对象
|
|
120
229
|
*/
|
|
121
230
|
function* splitPageIntoSections(html) {
|
|
122
231
|
const result = html.split(headingRegex);
|
|
@@ -142,14 +251,38 @@ function* splitPageIntoSections(html) {
|
|
|
142
251
|
};
|
|
143
252
|
}
|
|
144
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Extract searchable text from HTML content by removing tags.
|
|
256
|
+
*
|
|
257
|
+
* 通过移除标签从 HTML 内容中提取可搜索文本。
|
|
258
|
+
*
|
|
259
|
+
* @param content - HTML content / HTML 内容
|
|
260
|
+
* @returns Plain text content / 纯文本内容
|
|
261
|
+
*/
|
|
145
262
|
function getSearchableText(content) {
|
|
146
263
|
content = clearHtmlTags(content);
|
|
147
264
|
return content;
|
|
148
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Remove all HTML tags from a string.
|
|
268
|
+
*
|
|
269
|
+
* 移除字符串中的所有 HTML 标签。
|
|
270
|
+
*
|
|
271
|
+
* @param str - String containing HTML / 包含 HTML 的字符串
|
|
272
|
+
* @returns String with HTML tags removed / 移除 HTML 标签后的字符串
|
|
273
|
+
*/
|
|
149
274
|
function clearHtmlTags(str) {
|
|
150
275
|
str = str.replace(ignoreHeadingRegex, "");
|
|
151
276
|
return str.replace(/<[^>]*>/g, "");
|
|
152
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Generate HMR (Hot Module Replacement) code for the search index.
|
|
280
|
+
*
|
|
281
|
+
* 为搜索索引生成 HMR(热模块替换)代码。
|
|
282
|
+
*
|
|
283
|
+
* @param m - Module name / 模块名称
|
|
284
|
+
* @returns HMR code string / HMR 代码字符串
|
|
285
|
+
*/
|
|
153
286
|
function genHmrCode(m) {
|
|
154
287
|
const func = `update${m[0].toUpperCase()}${m.slice(1)}`;
|
|
155
288
|
return `
|
|
@@ -167,153 +300,162 @@ if (import.meta.hot) {
|
|
|
167
300
|
}
|
|
168
301
|
`;
|
|
169
302
|
}
|
|
170
|
-
|
|
171
|
-
//#endregion
|
|
172
|
-
//#region src/node/locales/de.ts
|
|
173
|
-
const deSearchLocale = {
|
|
174
|
-
placeholder: "Dokumente durchsuchen",
|
|
175
|
-
resetButtonTitle: "Suche zurücksetzen",
|
|
176
|
-
backButtonTitle: "Schließen",
|
|
177
|
-
noResultsText: "Keine Suchergebnisse:",
|
|
178
|
-
footer: {
|
|
179
|
-
selectText: "Auswählen",
|
|
180
|
-
selectKeyAriaLabel: "Eingabe",
|
|
181
|
-
navigateText: "Wechseln",
|
|
182
|
-
navigateUpKeyAriaLabel: "Nach oben",
|
|
183
|
-
navigateDownKeyAriaLabel: "Nach unten",
|
|
184
|
-
closeText: "Schließen",
|
|
185
|
-
closeKeyAriaLabel: "Beenden"
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
//#endregion
|
|
190
|
-
//#region src/node/locales/en.ts
|
|
191
|
-
const enSearchLocale = {
|
|
192
|
-
placeholder: "Search",
|
|
193
|
-
resetButtonTitle: "Reset search",
|
|
194
|
-
backButtonTitle: "Close search",
|
|
195
|
-
noResultsText: "No results for",
|
|
196
|
-
footer: {
|
|
197
|
-
selectText: "to select",
|
|
198
|
-
selectKeyAriaLabel: "enter",
|
|
199
|
-
navigateText: "to navigate",
|
|
200
|
-
navigateUpKeyAriaLabel: "up arrow",
|
|
201
|
-
navigateDownKeyAriaLabel: "down arrow",
|
|
202
|
-
closeText: "to close",
|
|
203
|
-
closeKeyAriaLabel: "escape"
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
//#endregion
|
|
208
|
-
//#region src/node/locales/fr.ts
|
|
209
|
-
const frSearchLocale = {
|
|
210
|
-
placeholder: "Rechercher dans la documentation",
|
|
211
|
-
resetButtonTitle: "Réinitialiser la recherche",
|
|
212
|
-
backButtonTitle: "Fermer",
|
|
213
|
-
noResultsText: "Aucun résultat trouvé :",
|
|
214
|
-
footer: {
|
|
215
|
-
selectText: "sélectionner",
|
|
216
|
-
selectKeyAriaLabel: "Entrée",
|
|
217
|
-
navigateText: "naviguer",
|
|
218
|
-
navigateUpKeyAriaLabel: "haut",
|
|
219
|
-
navigateDownKeyAriaLabel: "bas",
|
|
220
|
-
closeText: "fermer",
|
|
221
|
-
closeKeyAriaLabel: "sortie"
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
//#endregion
|
|
226
|
-
//#region src/node/locales/ja.ts
|
|
227
|
-
const jaSearchLocale = {
|
|
228
|
-
placeholder: "ドキュメントを検索",
|
|
229
|
-
resetButtonTitle: "検索をリセット",
|
|
230
|
-
backButtonTitle: "閉じる",
|
|
231
|
-
noResultsText: "検索結果がありません:",
|
|
232
|
-
footer: {
|
|
233
|
-
selectText: "選択",
|
|
234
|
-
selectKeyAriaLabel: "入力",
|
|
235
|
-
navigateText: "切り替え",
|
|
236
|
-
navigateUpKeyAriaLabel: "上へ",
|
|
237
|
-
navigateDownKeyAriaLabel: "下へ",
|
|
238
|
-
closeText: "閉じる",
|
|
239
|
-
closeKeyAriaLabel: "終了"
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
//#endregion
|
|
244
|
-
//#region src/node/locales/ru.ts
|
|
245
|
-
const ruSearchLocale = {
|
|
246
|
-
placeholder: "Поиск по документации",
|
|
247
|
-
resetButtonTitle: "Сбросить поиск",
|
|
248
|
-
backButtonTitle: "Закрыть",
|
|
249
|
-
noResultsText: "Нет результатов поиска:",
|
|
250
|
-
footer: {
|
|
251
|
-
selectText: "Выбрать",
|
|
252
|
-
selectKeyAriaLabel: "Ввод",
|
|
253
|
-
navigateText: "Переключить",
|
|
254
|
-
navigateUpKeyAriaLabel: "Вверх",
|
|
255
|
-
navigateDownKeyAriaLabel: "Вниз",
|
|
256
|
-
closeText: "Закрыть",
|
|
257
|
-
closeKeyAriaLabel: "Выход"
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
//#endregion
|
|
262
|
-
//#region src/node/locales/zh-tw.ts
|
|
263
|
-
const zhTwSearchLocale = {
|
|
264
|
-
placeholder: "搜尋文件",
|
|
265
|
-
resetButtonTitle: "重設搜尋",
|
|
266
|
-
backButtonTitle: "關閉",
|
|
267
|
-
noResultsText: "無搜尋結果:",
|
|
268
|
-
footer: {
|
|
269
|
-
selectText: "選擇",
|
|
270
|
-
selectKeyAriaLabel: "輸入",
|
|
271
|
-
navigateText: "切換",
|
|
272
|
-
navigateUpKeyAriaLabel: "向上",
|
|
273
|
-
navigateDownKeyAriaLabel: "向下",
|
|
274
|
-
closeText: "關閉",
|
|
275
|
-
closeKeyAriaLabel: "退出"
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
//#endregion
|
|
280
|
-
//#region src/node/locales/zh.ts
|
|
281
|
-
const zhSearchLocale = {
|
|
282
|
-
placeholder: "搜索文档",
|
|
283
|
-
resetButtonTitle: "重置搜索",
|
|
284
|
-
backButtonTitle: "关闭",
|
|
285
|
-
noResultsText: "无搜索结果:",
|
|
286
|
-
footer: {
|
|
287
|
-
selectText: "选择",
|
|
288
|
-
selectKeyAriaLabel: "输入",
|
|
289
|
-
navigateText: "切换",
|
|
290
|
-
navigateUpKeyAriaLabel: "向上",
|
|
291
|
-
navigateDownKeyAriaLabel: "向下",
|
|
292
|
-
closeText: "关闭",
|
|
293
|
-
closeKeyAriaLabel: "退出"
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
|
|
297
303
|
//#endregion
|
|
298
304
|
//#region src/node/locales/index.ts
|
|
305
|
+
/**
|
|
306
|
+
* Default locale configurations for search plugin.
|
|
307
|
+
*
|
|
308
|
+
* 搜索插件的默认语言配置。
|
|
309
|
+
*
|
|
310
|
+
* Maps language codes to their respective locale strings.
|
|
311
|
+
*
|
|
312
|
+
* 将语言代码映射到相应的本地化字符串。
|
|
313
|
+
*/
|
|
299
314
|
const SEARCH_LOCALES = [
|
|
300
|
-
[["en", "en-US"],
|
|
315
|
+
[["en", "en-US"], {
|
|
316
|
+
placeholder: "Search",
|
|
317
|
+
resetButtonTitle: "Reset search",
|
|
318
|
+
backButtonTitle: "Close search",
|
|
319
|
+
noResultsText: "No results for",
|
|
320
|
+
footer: {
|
|
321
|
+
selectText: "to select",
|
|
322
|
+
selectKeyAriaLabel: "enter",
|
|
323
|
+
navigateText: "to navigate",
|
|
324
|
+
navigateUpKeyAriaLabel: "up arrow",
|
|
325
|
+
navigateDownKeyAriaLabel: "down arrow",
|
|
326
|
+
closeText: "to close",
|
|
327
|
+
closeKeyAriaLabel: "escape"
|
|
328
|
+
}
|
|
329
|
+
}],
|
|
301
330
|
[[
|
|
302
331
|
"zh",
|
|
303
332
|
"zh-CN",
|
|
304
333
|
"zh-Hans",
|
|
305
334
|
"zh-Hant"
|
|
306
|
-
],
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
335
|
+
], {
|
|
336
|
+
placeholder: "搜索文档",
|
|
337
|
+
resetButtonTitle: "重置搜索",
|
|
338
|
+
backButtonTitle: "关闭",
|
|
339
|
+
noResultsText: "无搜索结果:",
|
|
340
|
+
footer: {
|
|
341
|
+
selectText: "选择",
|
|
342
|
+
selectKeyAriaLabel: "输入",
|
|
343
|
+
navigateText: "切换",
|
|
344
|
+
navigateUpKeyAriaLabel: "向上",
|
|
345
|
+
navigateDownKeyAriaLabel: "向下",
|
|
346
|
+
closeText: "关闭",
|
|
347
|
+
closeKeyAriaLabel: "退出"
|
|
348
|
+
}
|
|
349
|
+
}],
|
|
350
|
+
[["zh-TW"], {
|
|
351
|
+
placeholder: "搜尋文件",
|
|
352
|
+
resetButtonTitle: "重設搜尋",
|
|
353
|
+
backButtonTitle: "關閉",
|
|
354
|
+
noResultsText: "無搜尋結果:",
|
|
355
|
+
footer: {
|
|
356
|
+
selectText: "選擇",
|
|
357
|
+
selectKeyAriaLabel: "輸入",
|
|
358
|
+
navigateText: "切換",
|
|
359
|
+
navigateUpKeyAriaLabel: "向上",
|
|
360
|
+
navigateDownKeyAriaLabel: "向下",
|
|
361
|
+
closeText: "關閉",
|
|
362
|
+
closeKeyAriaLabel: "退出"
|
|
363
|
+
}
|
|
364
|
+
}],
|
|
365
|
+
[["de", "de-DE"], {
|
|
366
|
+
placeholder: "Dokumente durchsuchen",
|
|
367
|
+
resetButtonTitle: "Suche zurücksetzen",
|
|
368
|
+
backButtonTitle: "Schließen",
|
|
369
|
+
noResultsText: "Keine Suchergebnisse:",
|
|
370
|
+
footer: {
|
|
371
|
+
selectText: "Auswählen",
|
|
372
|
+
selectKeyAriaLabel: "Eingabe",
|
|
373
|
+
navigateText: "Wechseln",
|
|
374
|
+
navigateUpKeyAriaLabel: "Nach oben",
|
|
375
|
+
navigateDownKeyAriaLabel: "Nach unten",
|
|
376
|
+
closeText: "Schließen",
|
|
377
|
+
closeKeyAriaLabel: "Beenden"
|
|
378
|
+
}
|
|
379
|
+
}],
|
|
380
|
+
[["fr", "fr-FR"], {
|
|
381
|
+
placeholder: "Rechercher dans la documentation",
|
|
382
|
+
resetButtonTitle: "Réinitialiser la recherche",
|
|
383
|
+
backButtonTitle: "Fermer",
|
|
384
|
+
noResultsText: "Aucun résultat trouvé :",
|
|
385
|
+
footer: {
|
|
386
|
+
selectText: "sélectionner",
|
|
387
|
+
selectKeyAriaLabel: "Entrée",
|
|
388
|
+
navigateText: "naviguer",
|
|
389
|
+
navigateUpKeyAriaLabel: "haut",
|
|
390
|
+
navigateDownKeyAriaLabel: "bas",
|
|
391
|
+
closeText: "fermer",
|
|
392
|
+
closeKeyAriaLabel: "sortie"
|
|
393
|
+
}
|
|
394
|
+
}],
|
|
395
|
+
[["ru", "ru-RU"], {
|
|
396
|
+
placeholder: "Поиск по документации",
|
|
397
|
+
resetButtonTitle: "Сбросить поиск",
|
|
398
|
+
backButtonTitle: "Закрыть",
|
|
399
|
+
noResultsText: "Нет результатов поиска:",
|
|
400
|
+
footer: {
|
|
401
|
+
selectText: "Выбрать",
|
|
402
|
+
selectKeyAriaLabel: "Ввод",
|
|
403
|
+
navigateText: "Переключить",
|
|
404
|
+
navigateUpKeyAriaLabel: "Вверх",
|
|
405
|
+
navigateDownKeyAriaLabel: "Вниз",
|
|
406
|
+
closeText: "Закрыть",
|
|
407
|
+
closeKeyAriaLabel: "Выход"
|
|
408
|
+
}
|
|
409
|
+
}],
|
|
410
|
+
[["ja", "ja-JP"], {
|
|
411
|
+
placeholder: "ドキュメントを検索",
|
|
412
|
+
resetButtonTitle: "検索をリセット",
|
|
413
|
+
backButtonTitle: "閉じる",
|
|
414
|
+
noResultsText: "検索結果がありません:",
|
|
415
|
+
footer: {
|
|
416
|
+
selectText: "選択",
|
|
417
|
+
selectKeyAriaLabel: "入力",
|
|
418
|
+
navigateText: "切り替え",
|
|
419
|
+
navigateUpKeyAriaLabel: "上へ",
|
|
420
|
+
navigateDownKeyAriaLabel: "下へ",
|
|
421
|
+
closeText: "閉じる",
|
|
422
|
+
closeKeyAriaLabel: "終了"
|
|
423
|
+
}
|
|
424
|
+
}]
|
|
312
425
|
];
|
|
313
|
-
|
|
314
426
|
//#endregion
|
|
315
427
|
//#region src/node/searchPlugin.ts
|
|
316
428
|
const __dirname = getDirname(import.meta.url);
|
|
429
|
+
/**
|
|
430
|
+
* Create a VuePress search plugin instance.
|
|
431
|
+
*
|
|
432
|
+
* 创建 VuePress 搜索插件实例。
|
|
433
|
+
*
|
|
434
|
+
* @param options - Plugin configuration options / 插件配置选项
|
|
435
|
+
* @param options.locales - Locale-specific search configurations / 特定语言的搜索配置
|
|
436
|
+
* @param options.isSearchable - Function to determine if a page should be indexed / 判断页面是否应被索引的函数
|
|
437
|
+
* @param options.searchOptions - MiniSearch options / MiniSearch 配置选项
|
|
438
|
+
* @returns VuePress plugin object / VuePress 插件对象
|
|
439
|
+
* @example
|
|
440
|
+
* // Basic usage
|
|
441
|
+
* export default {
|
|
442
|
+
* plugins: [
|
|
443
|
+
* searchPlugin()
|
|
444
|
+
* ]
|
|
445
|
+
* }
|
|
446
|
+
*
|
|
447
|
+
* // With custom options
|
|
448
|
+
* export default {
|
|
449
|
+
* plugins: [
|
|
450
|
+
* searchPlugin({
|
|
451
|
+
* locales: {
|
|
452
|
+
* '/zh/': { placeholder: '搜索文档' }
|
|
453
|
+
* },
|
|
454
|
+
* isSearchable: (page) => page.path !== '/secret/'
|
|
455
|
+
* })
|
|
456
|
+
* ]
|
|
457
|
+
* }
|
|
458
|
+
*/
|
|
317
459
|
function searchPlugin({ locales = {}, isSearchable, ...searchOptions } = {}) {
|
|
318
460
|
return (app) => ({
|
|
319
461
|
name: "@vuepress-plume/plugin-search",
|
|
@@ -349,37 +491,20 @@ function searchPlugin({ locales = {}, isSearchable, ...searchOptions } = {}) {
|
|
|
349
491
|
});
|
|
350
492
|
}
|
|
351
493
|
},
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
searchIndexWatcher.on("add", (filepath) => {
|
|
359
|
-
onSearchIndexUpdated(filepath, {
|
|
360
|
-
app,
|
|
361
|
-
isSearchable,
|
|
362
|
-
searchOptions
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
searchIndexWatcher.on("change", (filepath) => {
|
|
366
|
-
onSearchIndexUpdated(filepath, {
|
|
367
|
-
app,
|
|
368
|
-
isSearchable,
|
|
369
|
-
searchOptions
|
|
370
|
-
});
|
|
494
|
+
onPageUpdated: async (app, type, page) => {
|
|
495
|
+
if (!page?.filePathRelative) return;
|
|
496
|
+
if (type === "create" || type === "update") await onSearchIndexUpdated(app, {
|
|
497
|
+
page,
|
|
498
|
+
isSearchable,
|
|
499
|
+
searchOptions
|
|
371
500
|
});
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
searchOptions
|
|
377
|
-
});
|
|
501
|
+
else if (type === "delete") await onSearchIndexRemoved(app, {
|
|
502
|
+
page,
|
|
503
|
+
isSearchable,
|
|
504
|
+
searchOptions
|
|
378
505
|
});
|
|
379
|
-
watchers.push(searchIndexWatcher);
|
|
380
506
|
}
|
|
381
507
|
});
|
|
382
508
|
}
|
|
383
|
-
|
|
384
509
|
//#endregion
|
|
385
|
-
export { prepareSearchIndex, searchPlugin };
|
|
510
|
+
export { prepareSearchIndex, searchPlugin };
|
package/lib/shared/index.d.ts
CHANGED
|
@@ -2,38 +2,90 @@ import { Options } from "minisearch";
|
|
|
2
2
|
import { LocaleConfig, Page } from "vuepress/core";
|
|
3
3
|
|
|
4
4
|
//#region src/shared/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Locale options for search UI strings.
|
|
7
|
+
*
|
|
8
|
+
* 搜索 UI 字符串的语言选项。
|
|
9
|
+
*/
|
|
5
10
|
interface SearchLocaleOptions {
|
|
11
|
+
/** Placeholder text for search input / 搜索输入框的占位文本 */
|
|
6
12
|
placeholder: string;
|
|
13
|
+
/** Text for search button / 搜索按钮的文本 */
|
|
7
14
|
buttonText: string;
|
|
15
|
+
/** Title for reset button / 重置按钮的标题 */
|
|
8
16
|
resetButtonTitle: string;
|
|
17
|
+
/** Title for back/close button / 返回/关闭按钮的标题 */
|
|
9
18
|
backButtonTitle: string;
|
|
19
|
+
/** Text shown when no results found / 无搜索结果时显示的文本 */
|
|
10
20
|
noResultsText: string;
|
|
21
|
+
/** Footer keyboard shortcut hints / 底部键盘快捷键提示 */
|
|
11
22
|
footer: {
|
|
12
|
-
selectText: string;
|
|
13
|
-
selectKeyAriaLabel: string;
|
|
14
|
-
navigateText: string;
|
|
15
|
-
navigateUpKeyAriaLabel: string;
|
|
16
|
-
navigateDownKeyAriaLabel: string;
|
|
17
|
-
closeText: string;
|
|
23
|
+
/** Text for select action / 选择操作的文本 */selectText: string; /** ARIA label for select key / 选择键的 ARIA 标签 */
|
|
24
|
+
selectKeyAriaLabel: string; /** Text for navigate action / 导航操作的文本 */
|
|
25
|
+
navigateText: string; /** ARIA label for navigate up key / 向上导航键的 ARIA 标签 */
|
|
26
|
+
navigateUpKeyAriaLabel: string; /** ARIA label for navigate down key / 向下导航键的 ARIA 标签 */
|
|
27
|
+
navigateDownKeyAriaLabel: string; /** Text for close action / 关闭操作的文本 */
|
|
28
|
+
closeText: string; /** ARIA label for close key / 关闭键的 ARIA 标签 */
|
|
18
29
|
closeKeyAriaLabel: string;
|
|
19
30
|
};
|
|
20
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Locale configuration type for search box.
|
|
34
|
+
*
|
|
35
|
+
* 搜索框的语言配置类型。
|
|
36
|
+
*/
|
|
21
37
|
type SearchBoxLocales = LocaleConfig<SearchLocaleOptions>;
|
|
38
|
+
/**
|
|
39
|
+
* Options for the search plugin.
|
|
40
|
+
*
|
|
41
|
+
* 搜索插件的选项。
|
|
42
|
+
*/
|
|
22
43
|
interface SearchPluginOptions extends SearchOptions {
|
|
44
|
+
/** Locale-specific search configurations / 特定语言的搜索配置 */
|
|
23
45
|
locales?: SearchBoxLocales;
|
|
46
|
+
/**
|
|
47
|
+
* Function to determine if a page should be indexed.
|
|
48
|
+
*
|
|
49
|
+
* 判断页面是否应被索引的函数。
|
|
50
|
+
*
|
|
51
|
+
* @param page - VuePress page object / VuePress 页面对象
|
|
52
|
+
* @returns Whether the page should be searchable / 页面是否可搜索
|
|
53
|
+
*/
|
|
24
54
|
isSearchable?: (page: Page) => boolean;
|
|
25
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Search configuration options.
|
|
58
|
+
*
|
|
59
|
+
* 搜索配置选项。
|
|
60
|
+
*/
|
|
26
61
|
interface SearchOptions {
|
|
27
62
|
/**
|
|
63
|
+
* Whether to disable query persistence in session storage.
|
|
64
|
+
*
|
|
65
|
+
* 是否禁用会话存储中的查询持久化。
|
|
66
|
+
*
|
|
28
67
|
* @default false
|
|
29
68
|
*/
|
|
30
69
|
disableQueryPersistence?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* MiniSearch configuration options.
|
|
72
|
+
*
|
|
73
|
+
* MiniSearch 配置选项。
|
|
74
|
+
*/
|
|
31
75
|
miniSearch?: {
|
|
32
76
|
/**
|
|
77
|
+
* MiniSearch instance options.
|
|
78
|
+
*
|
|
79
|
+
* MiniSearch 实例选项。
|
|
80
|
+
*
|
|
33
81
|
* @see https://lucaong.github.io/minisearch/modules/_minisearch_.html#options
|
|
34
82
|
*/
|
|
35
83
|
options?: Pick<Options, 'extractField' | 'tokenize' | 'processTerm'>;
|
|
36
84
|
/**
|
|
85
|
+
* MiniSearch search options.
|
|
86
|
+
*
|
|
87
|
+
* MiniSearch 搜索选项。
|
|
88
|
+
*
|
|
37
89
|
* @see https://lucaong.github.io/minisearch/modules/_minisearch_.html#searchoptions-1
|
|
38
90
|
*/
|
|
39
91
|
searchOptions?: Options['searchOptions'];
|
package/lib/shared/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vuepress-plume/plugin-search",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.193",
|
|
5
5
|
"description": "The Plugin for VuePress 2 - local search",
|
|
6
6
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -31,18 +31,18 @@
|
|
|
31
31
|
"lib"
|
|
32
32
|
],
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"vuepress": "2.0.0-rc.
|
|
34
|
+
"vuepress": "2.0.0-rc.28"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@vuepress/helper": "2.0.0-rc.
|
|
37
|
+
"@vuepress/helper": "2.0.0-rc.128",
|
|
38
38
|
"@vueuse/core": "^14.2.1",
|
|
39
39
|
"@vueuse/integrations": "^14.2.1",
|
|
40
40
|
"chokidar": "5.0.0",
|
|
41
|
-
"focus-trap": "^8.0.
|
|
41
|
+
"focus-trap": "^8.0.1",
|
|
42
42
|
"mark.js": "^8.11.1",
|
|
43
43
|
"minisearch": "^7.2.0",
|
|
44
44
|
"p-map": "^7.0.4",
|
|
45
|
-
"vue": "^3.5.
|
|
45
|
+
"vue": "^3.5.31"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public",
|
|
@@ -58,6 +58,6 @@
|
|
|
58
58
|
"build": "pnpm run tsdown && pnpm run copy",
|
|
59
59
|
"clean": "rimraf --glob ./lib",
|
|
60
60
|
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
|
|
61
|
-
"tsdown": "tsdown"
|
|
61
|
+
"tsdown": "tsdown --config-loader unrun"
|
|
62
62
|
}
|
|
63
63
|
}
|