@ampernic/vitepress-theme-alt-docs 0.1.5 → 0.1.10
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/README.md +15 -2
- package/dist/components/ADGlobalSearch.vue +231 -37
- package/dist/config/shared.js +1 -0
- package/dist/config/shared.mjs +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -39,11 +39,24 @@ export default defineConfig({
|
|
|
39
39
|
| `ADProducts` | Переключатель продуктов/дистрибутивов |
|
|
40
40
|
| `ADProductsSidebar` | Боковая панель с выбором продукта |
|
|
41
41
|
| `ADVersioning` | Переключатель версий документации |
|
|
42
|
-
| `ADNavBarSearch` |
|
|
43
|
-
| `
|
|
42
|
+
| `ADNavBarSearch` | Кнопка открытия глобального поиска в навбаре |
|
|
43
|
+
| `ADGlobalSearch` | Глобальный поиск по всем дистрибутивам через Pagefind `mergeIndex` |
|
|
44
|
+
| `ADSearch` | Компонент поиска для одного сайта (Pagefind) |
|
|
44
45
|
| `ADAssetLink` | Ссылка на файл-ассет |
|
|
45
46
|
| `ADInlineImage` | Встроенное изображение с нормализацией пути |
|
|
46
47
|
|
|
48
|
+
### ADGlobalSearch
|
|
49
|
+
|
|
50
|
+
Поисковая модалка для индексного сайта, объединяющая индексы всех задеплоенных дистрибутивов.
|
|
51
|
+
|
|
52
|
+
**Как работает:**
|
|
53
|
+
1. Последовательно пробует `import(/{distro}/pagefind/pagefind.js)` для каждого дистрибутива из списка
|
|
54
|
+
2. HEAD-проверяет `pagefind-entry.json` для остальных дистрибутивов — только подтверждённые идут в `mergeIndex`
|
|
55
|
+
> Важно: вызов `mergeIndex()` для несуществующего пути необратимо ломает SharedWorker Pagefind, все последующие запросы тоже падают
|
|
56
|
+
3. Загружает `pagefind/distro-meta.json` с каждого сайта — получает точный список версий по дистрибутиву
|
|
57
|
+
> `pagefind.filters()` при объединённых индексах возвращает значения со всех сайтов без изоляции, поэтому для dropdown версий используется manifest, а не API Pagefind
|
|
58
|
+
4. Отображает фильтры по дистрибутиву и версии; при смене дистрибутива версии обновляются из manifest
|
|
59
|
+
|
|
47
60
|
## Включённые плагины
|
|
48
61
|
|
|
49
62
|
Тема автоматически подключает:
|
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { onKeyStroke, useScrollLock } from '@vueuse/core'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { useRouter } from 'vitepress'
|
|
4
|
+
import { nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'
|
|
5
|
+
|
|
6
|
+
// ── Dropdown ──────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
interface DropdownRect { top: number; left: number; width: number }
|
|
9
|
+
|
|
10
|
+
function useDropdown() {
|
|
11
|
+
const open = ref(false)
|
|
12
|
+
const rect = ref<DropdownRect | null>(null)
|
|
13
|
+
const triggerRef = ref<HTMLElement | null>(null)
|
|
14
|
+
|
|
15
|
+
function toggle() {
|
|
16
|
+
if (!open.value && triggerRef.value) {
|
|
17
|
+
const r = triggerRef.value.getBoundingClientRect()
|
|
18
|
+
rect.value = { top: r.bottom + 4, left: r.left, width: r.width }
|
|
19
|
+
}
|
|
20
|
+
open.value = !open.value
|
|
21
|
+
}
|
|
22
|
+
function close() { open.value = false }
|
|
23
|
+
function onOutside(e: MouseEvent) {
|
|
24
|
+
if (triggerRef.value && !triggerRef.value.contains(e.target as Node)) close()
|
|
25
|
+
}
|
|
26
|
+
onMounted(() => document.addEventListener('mousedown', onOutside))
|
|
27
|
+
onUnmounted(() => document.removeEventListener('mousedown', onOutside))
|
|
28
|
+
|
|
29
|
+
return { open, rect, triggerRef, toggle, close }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const distroDropdown = useDropdown()
|
|
33
|
+
const versionDropdown = useDropdown()
|
|
5
34
|
|
|
6
35
|
const emit = defineEmits<{ (e: 'close'): void }>()
|
|
7
|
-
const { site } = useData()
|
|
8
36
|
const router = useRouter()
|
|
9
37
|
|
|
10
38
|
const ALL_DISTROS = [
|
|
@@ -41,19 +69,21 @@ const indexLoading = ref(true)
|
|
|
41
69
|
const loadError = ref(false)
|
|
42
70
|
const availableDistros = ref<string[]>([])
|
|
43
71
|
const availableVersions = ref<string[]>([])
|
|
72
|
+
// Per-distro version map from distro-meta.json — pagefind's merged-index filter API
|
|
73
|
+
// leaks version values across distros, so we use this static manifest instead.
|
|
74
|
+
const distroVersionMap = ref<Record<string, string[]>>({})
|
|
44
75
|
|
|
45
76
|
async function loadIndexes() {
|
|
46
|
-
//
|
|
47
|
-
const base = site.value.base === '/' ? '' : site.value.base.replace(/\/+$/, '')
|
|
77
|
+
// Always use absolute paths from origin root — current site base is irrelevant
|
|
48
78
|
|
|
49
79
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
80
|
let pf: any = null
|
|
51
81
|
let firstIdx = -1
|
|
52
82
|
|
|
83
|
+
// Find first available distro to get the pagefind module
|
|
53
84
|
for (let i = 0; i < ALL_DISTROS.length; i++) {
|
|
54
85
|
try {
|
|
55
|
-
|
|
56
|
-
pf = await import(/* @vite-ignore */ `${base}/${ALL_DISTROS[i]}/pagefind/pagefind.js`)
|
|
86
|
+
pf = await import(/* @vite-ignore */ `/${ALL_DISTROS[i]}/pagefind/pagefind.js`)
|
|
57
87
|
firstIdx = i
|
|
58
88
|
break
|
|
59
89
|
} catch { /* distro not deployed yet */ }
|
|
@@ -61,23 +91,60 @@ async function loadIndexes() {
|
|
|
61
91
|
|
|
62
92
|
if (!pf) throw new Error('no pagefind index found')
|
|
63
93
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
94
|
+
// init() must be called before mergeIndex()
|
|
95
|
+
await pf.init()
|
|
96
|
+
|
|
97
|
+
// Pre-check which distros have a pagefind index before calling mergeIndex.
|
|
98
|
+
// A failed mergeIndex() permanently corrupts pagefind's SharedWorker state,
|
|
99
|
+
// making subsequent filters()/search() calls fail with "Failed to load Pagefind metadata".
|
|
100
|
+
const allDistros = [ALL_DISTROS[firstIdx], ...ALL_DISTROS.slice(firstIdx + 1)]
|
|
101
|
+
const metaResults = await Promise.all(
|
|
102
|
+
allDistros.map(async distro => {
|
|
103
|
+
try {
|
|
104
|
+
const r = await fetch(`/${distro}/pagefind/pagefind-entry.json`, { method: 'HEAD' })
|
|
105
|
+
if (!r.ok) return null
|
|
106
|
+
// Fetch distro-meta.json for accurate per-distro filter values.
|
|
107
|
+
// pagefind's merged-index filter API leaks values across distros.
|
|
108
|
+
const meta = await fetch(`/${distro}/pagefind/distro-meta.json`)
|
|
109
|
+
.then(r => r.ok ? r.json() : null)
|
|
110
|
+
.catch(() => null) as Record<string, string[]> | null
|
|
111
|
+
return { distro, meta }
|
|
112
|
+
} catch { return null }
|
|
113
|
+
}),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const confirmed = metaResults.filter((m): m is { distro: string; meta: Record<string, string[]> | null } => m !== null)
|
|
117
|
+
const available = confirmed.slice(1).map(m => m.distro)
|
|
118
|
+
|
|
119
|
+
// Build per-distro version map from distro-meta.json manifests
|
|
120
|
+
const versionMap: Record<string, string[]> = {}
|
|
121
|
+
const allVersionsSet = new Set<string>()
|
|
122
|
+
for (const { distro, meta } of confirmed) {
|
|
123
|
+
const versions = sortVersions(meta?.version ?? [])
|
|
124
|
+
versionMap[distro] = versions
|
|
125
|
+
for (const v of versions) allVersionsSet.add(v)
|
|
68
126
|
}
|
|
127
|
+
distroVersionMap.value = versionMap
|
|
69
128
|
|
|
70
|
-
|
|
129
|
+
// Merge only confirmed indexes in parallel
|
|
130
|
+
await Promise.allSettled(
|
|
131
|
+
available.map(distro => pf.mergeIndex(`/${distro}/pagefind`)),
|
|
132
|
+
)
|
|
71
133
|
|
|
72
134
|
const filters = await pf.filters() as Record<string, Record<string, number>>
|
|
73
135
|
availableDistros.value = Object.keys(filters.distro ?? {}).sort()
|
|
74
|
-
|
|
136
|
+
// Use versions from manifests (accurate) rather than pagefind filters (leaky across indexes)
|
|
137
|
+
availableVersions.value = sortVersions([...allVersionsSet])
|
|
138
|
+
|
|
139
|
+
return pf
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function sortVersions(versions: string[]): string[] {
|
|
143
|
+
return versions.sort((a, b) => {
|
|
75
144
|
const [aMaj, aMin] = a.split('.').map(Number)
|
|
76
145
|
const [bMaj, bMin] = b.split('.').map(Number)
|
|
77
146
|
return bMaj !== aMaj ? bMaj - aMaj : bMin - aMin
|
|
78
147
|
})
|
|
79
|
-
|
|
80
|
-
return pf
|
|
81
148
|
}
|
|
82
149
|
|
|
83
150
|
// ── Filters ───────────────────────────────────────────────────────────────────
|
|
@@ -85,7 +152,20 @@ async function loadIndexes() {
|
|
|
85
152
|
const selectedDistro = ref('')
|
|
86
153
|
const selectedVersion = ref('')
|
|
87
154
|
|
|
88
|
-
|
|
155
|
+
// When distro changes: reset version and update available versions from the manifest
|
|
156
|
+
watch(selectedDistro, () => {
|
|
157
|
+
selectedVersion.value = ''
|
|
158
|
+
if (!selectedDistro.value) {
|
|
159
|
+
// Show union of all versions across all distros
|
|
160
|
+
const all = new Set<string>()
|
|
161
|
+
for (const versions of Object.values(distroVersionMap.value)) {
|
|
162
|
+
for (const v of versions) all.add(v)
|
|
163
|
+
}
|
|
164
|
+
availableVersions.value = sortVersions([...all])
|
|
165
|
+
} else {
|
|
166
|
+
availableVersions.value = distroVersionMap.value[selectedDistro.value] ?? []
|
|
167
|
+
}
|
|
168
|
+
})
|
|
89
169
|
|
|
90
170
|
// ── Search ────────────────────────────────────────────────────────────────────
|
|
91
171
|
|
|
@@ -160,6 +240,13 @@ onMounted(async () => {
|
|
|
160
240
|
inputEl.value?.focus()
|
|
161
241
|
try {
|
|
162
242
|
pagefind.value = await loadIndexes()
|
|
243
|
+
// Re-trigger search if user already typed while indexes were loading
|
|
244
|
+
if (query.value.trim()) {
|
|
245
|
+
const saved = query.value
|
|
246
|
+
query.value = ''
|
|
247
|
+
await nextTick()
|
|
248
|
+
query.value = saved
|
|
249
|
+
}
|
|
163
250
|
} catch {
|
|
164
251
|
loadError.value = true
|
|
165
252
|
} finally {
|
|
@@ -199,16 +286,41 @@ function onOverlayClick(e: MouseEvent) { if (e.target === e.currentTarget) close
|
|
|
199
286
|
|
|
200
287
|
<!-- Filters row (shown once indexes loaded and have data) -->
|
|
201
288
|
<div v-if="!indexLoading && (availableDistros.length || availableVersions.length)" class="gs-filters">
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
<
|
|
210
|
-
|
|
211
|
-
|
|
289
|
+
|
|
290
|
+
<!-- Distro dropdown -->
|
|
291
|
+
<div class="gs-dd" :class="{ 'is-open': distroDropdown.open.value }">
|
|
292
|
+
<button :ref="(el) => { distroDropdown.triggerRef.value = el as HTMLElement | null }" class="gs-dd-trigger" :class="{ 'is-active': selectedDistro }" @click="versionDropdown.close(); distroDropdown.toggle()">
|
|
293
|
+
<span>{{ selectedDistro ? (DISTRO_NAMES[selectedDistro] ?? selectedDistro) : 'Все дистрибутивы' }}</span>
|
|
294
|
+
<svg class="gs-dd-chevron" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M7 10l5 5 5-5z"/></svg>
|
|
295
|
+
</button>
|
|
296
|
+
<Teleport to="body">
|
|
297
|
+
<ul v-if="distroDropdown.open.value && distroDropdown.rect.value" class="gs-dd-list gs-dd-list--fixed"
|
|
298
|
+
:style="{ top: distroDropdown.rect.value.top + 'px', left: distroDropdown.rect.value.left + 'px', width: distroDropdown.rect.value.width + 'px' }">
|
|
299
|
+
<li class="gs-dd-item" :class="{ 'is-selected': selectedDistro === '' }"
|
|
300
|
+
@click="selectedDistro = ''; distroDropdown.close()">Все дистрибутивы</li>
|
|
301
|
+
<li v-for="d in availableDistros" :key="d" class="gs-dd-item" :class="{ 'is-selected': selectedDistro === d }"
|
|
302
|
+
@click="selectedDistro = d; distroDropdown.close()">{{ DISTRO_NAMES[d] ?? d }}</li>
|
|
303
|
+
</ul>
|
|
304
|
+
</Teleport>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<!-- Version dropdown -->
|
|
308
|
+
<div class="gs-dd" :class="{ 'is-open': versionDropdown.open.value }">
|
|
309
|
+
<button :ref="(el) => { versionDropdown.triggerRef.value = el as HTMLElement | null }" class="gs-dd-trigger" :class="{ 'is-active': selectedVersion }" @click="distroDropdown.close(); versionDropdown.toggle()">
|
|
310
|
+
<span>{{ selectedVersion || 'Все версии' }}</span>
|
|
311
|
+
<svg class="gs-dd-chevron" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M7 10l5 5 5-5z"/></svg>
|
|
312
|
+
</button>
|
|
313
|
+
<Teleport to="body">
|
|
314
|
+
<ul v-if="versionDropdown.open.value && versionDropdown.rect.value" class="gs-dd-list gs-dd-list--fixed"
|
|
315
|
+
:style="{ top: versionDropdown.rect.value.top + 'px', left: versionDropdown.rect.value.left + 'px', width: versionDropdown.rect.value.width + 'px' }">
|
|
316
|
+
<li class="gs-dd-item" :class="{ 'is-selected': selectedVersion === '' }"
|
|
317
|
+
@click="selectedVersion = ''; versionDropdown.close()">Все версии</li>
|
|
318
|
+
<li v-for="v in availableVersions" :key="v" class="gs-dd-item" :class="{ 'is-selected': selectedVersion === v }"
|
|
319
|
+
@click="selectedVersion = v; versionDropdown.close()">{{ v }}</li>
|
|
320
|
+
</ul>
|
|
321
|
+
</Teleport>
|
|
322
|
+
</div>
|
|
323
|
+
|
|
212
324
|
</div>
|
|
213
325
|
|
|
214
326
|
<!-- Results -->
|
|
@@ -326,26 +438,107 @@ function onOverlayClick(e: MouseEvent) { if (e.target === e.currentTarget) close
|
|
|
326
438
|
.gs-filters {
|
|
327
439
|
display: flex;
|
|
328
440
|
gap: 8px;
|
|
329
|
-
padding:
|
|
441
|
+
padding: 10px 16px;
|
|
330
442
|
border-bottom: 1px solid var(--vp-c-divider);
|
|
331
|
-
background: var(--vp-c-default-soft);
|
|
332
443
|
}
|
|
333
444
|
|
|
334
|
-
|
|
445
|
+
/* ── Custom dropdown ── */
|
|
446
|
+
.gs-dd {
|
|
447
|
+
position: relative;
|
|
335
448
|
flex: 1;
|
|
336
449
|
min-width: 0;
|
|
337
|
-
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.gs-dd-trigger {
|
|
453
|
+
width: 100%;
|
|
454
|
+
display: flex;
|
|
455
|
+
align-items: center;
|
|
456
|
+
justify-content: space-between;
|
|
457
|
+
gap: 6px;
|
|
458
|
+
padding: 6px 10px;
|
|
338
459
|
font-size: 13px;
|
|
339
|
-
|
|
460
|
+
font-family: inherit;
|
|
461
|
+
background: var(--vp-c-bg-soft);
|
|
340
462
|
border: 1px solid var(--vp-c-divider);
|
|
341
|
-
border-radius:
|
|
342
|
-
color: var(--vp-c-text-
|
|
463
|
+
border-radius: 8px;
|
|
464
|
+
color: var(--vp-c-text-2);
|
|
343
465
|
cursor: pointer;
|
|
344
466
|
outline: none;
|
|
467
|
+
text-align: left;
|
|
468
|
+
transition: border-color 0.15s, color 0.15s;
|
|
469
|
+
white-space: nowrap;
|
|
470
|
+
overflow: hidden;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.gs-dd-trigger span {
|
|
474
|
+
overflow: hidden;
|
|
475
|
+
text-overflow: ellipsis;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.gs-dd-trigger.is-active {
|
|
479
|
+
color: var(--vp-c-text-1);
|
|
480
|
+
border-color: var(--vp-c-brand-1);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.gs-dd-trigger:hover {
|
|
484
|
+
border-color: var(--vp-c-text-2);
|
|
485
|
+
color: var(--vp-c-text-1);
|
|
345
486
|
}
|
|
346
487
|
|
|
347
|
-
.gs-
|
|
488
|
+
.gs-dd.is-open .gs-dd-trigger {
|
|
348
489
|
border-color: var(--vp-c-brand-1);
|
|
490
|
+
box-shadow: 0 0 0 2px var(--vp-c-brand-soft);
|
|
491
|
+
color: var(--vp-c-text-1);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.gs-dd-chevron {
|
|
495
|
+
flex-shrink: 0;
|
|
496
|
+
color: var(--vp-c-text-3);
|
|
497
|
+
transition: transform 0.15s;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.gs-dd.is-open .gs-dd-chevron {
|
|
501
|
+
transform: rotate(180deg);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.gs-dd-list {
|
|
505
|
+
list-style: none;
|
|
506
|
+
margin: 0;
|
|
507
|
+
padding: 4px;
|
|
508
|
+
background: var(--vp-c-bg-elv);
|
|
509
|
+
border: 1px solid var(--vp-c-divider);
|
|
510
|
+
border-radius: 8px;
|
|
511
|
+
box-shadow: var(--vp-shadow-3);
|
|
512
|
+
max-height: 220px;
|
|
513
|
+
overflow-y: auto;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.gs-dd-list--fixed {
|
|
517
|
+
position: fixed;
|
|
518
|
+
z-index: 200;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.gs-dd-item {
|
|
522
|
+
padding: 6px 10px;
|
|
523
|
+
font-size: 13px;
|
|
524
|
+
border-radius: 6px;
|
|
525
|
+
cursor: pointer;
|
|
526
|
+
color: var(--vp-c-text-2);
|
|
527
|
+
transition: background 0.1s, color 0.1s;
|
|
528
|
+
white-space: nowrap;
|
|
529
|
+
overflow: hidden;
|
|
530
|
+
text-overflow: ellipsis;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.gs-dd-item:hover {
|
|
534
|
+
background: var(--vp-c-default-soft);
|
|
535
|
+
color: var(--vp-c-text-1);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.gs-dd-item.is-selected {
|
|
539
|
+
background: var(--vp-c-brand-soft);
|
|
540
|
+
color: var(--vp-c-brand-1);
|
|
541
|
+
font-weight: 500;
|
|
349
542
|
}
|
|
350
543
|
|
|
351
544
|
.gs-results {
|
|
@@ -456,16 +649,17 @@ function onOverlayClick(e: MouseEvent) { if (e.target === e.currentTarget) close
|
|
|
456
649
|
padding: 8px 16px;
|
|
457
650
|
border-top: 1px solid var(--vp-c-divider);
|
|
458
651
|
font-size: 12px;
|
|
459
|
-
color: var(--vp-c-text-
|
|
652
|
+
color: var(--vp-c-text-2);
|
|
460
653
|
}
|
|
461
654
|
|
|
462
655
|
kbd {
|
|
463
|
-
background: var(--vp-c-
|
|
464
|
-
border: 1px solid var(--vp-c-
|
|
656
|
+
background: var(--vp-c-bg-soft);
|
|
657
|
+
border: 1px solid var(--vp-c-text-3);
|
|
465
658
|
border-radius: 4px;
|
|
466
659
|
padding: 1px 5px;
|
|
467
660
|
font-size: 11px;
|
|
468
661
|
font-family: inherit;
|
|
662
|
+
color: var(--vp-c-text-1);
|
|
469
663
|
}
|
|
470
664
|
</style>
|
|
471
665
|
|
package/dist/config/shared.js
CHANGED
package/dist/config/shared.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampernic/vitepress-theme-alt-docs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Shared VitePress theme for ALT Linux documentation",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"author": "Ampernic",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"markdown-it-kbd": "^1.0.0",
|
|
35
35
|
"vitepress-plugin-tabs": "^0.6.0",
|
|
36
36
|
"@ampernic/vitepress-plugin-alt-docs-versioning": "0.1.2",
|
|
37
|
+
"@ampernic/vitepress-plugin-cross-site-router": "0.1.2",
|
|
37
38
|
"@ampernic/vitepress-plugin-html-image": "0.1.2",
|
|
38
|
-
"@ampernic/vitepress-plugin-pagefind": "0.1.
|
|
39
|
-
"@ampernic/vitepress-plugin-cross-site-router": "0.1.2"
|
|
39
|
+
"@ampernic/vitepress-plugin-pagefind": "0.1.8"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"builtin-modules": "^3.3.0",
|