diff --git a/src/module.ts b/src/module.ts index a6e0fd9a7..578a105de 100644 --- a/src/module.ts +++ b/src/module.ts @@ -540,11 +540,11 @@ export default defineNuxtModule({ }) // Context will use in server - nuxt.options.runtimeConfig.content = { + nuxt.options.runtimeConfig.content = defu(nuxt.options.runtimeConfig.content, { cacheVersion: CACHE_VERSION, cacheIntegrity, ...contentContext as any - } + }) // @nuxtjs/tailwindcss support // @ts-ignore - Module might not exist diff --git a/src/runtime/composables/client-db.ts b/src/runtime/composables/client-db.ts index ab608bfc9..b7d6c59f8 100644 --- a/src/runtime/composables/client-db.ts +++ b/src/runtime/composables/client-db.ts @@ -2,7 +2,7 @@ import type { Storage } from 'unstorage' // @ts-ignore import memoryDriver from 'unstorage/drivers/memory' import { createStorage, prefixStorage } from 'unstorage' -import { useRuntimeConfig, useCookie } from '#app' +import { useRuntimeConfig, useCookie, useNuxtApp } from '#app' import { withBase } from 'ufo' import { createPipelineFetcher } from '../query/match/pipeline' import { createQuery } from '../query/query' @@ -39,8 +39,8 @@ export function createDB (storage: Storage) { } } } - - return Promise.all(Array.from(keys).map(key => storage.getItem(key) as Promise)) + const items = await Promise.all(Array.from(keys).map(key => storage.getItem(key) as Promise)) + return items } return { storage, @@ -50,24 +50,45 @@ export function createDB (storage: Storage) { } let contentDatabase +let contentDatabaseInitPromise export async function useContentDatabase () { - if (!contentDatabase) { - const { clientDB } = useRuntimeConfig().public.content - contentDatabase = createDB(contentStorage) - const integrity = await contentDatabase.storage.getItem('integrity') - if (clientDB.integrity !== +integrity) { - const { contents, navigation } = await $fetch(withContentBase('cache.json')) - - for (const content of contents) { - await contentDatabase.storage.setItem(`cache:${content._id}`, content) - } + if (contentDatabaseInitPromise) { + await contentDatabaseInitPromise + } else if (!contentDatabase) { + contentDatabaseInitPromise = initContentDatabase() + contentDatabase = await contentDatabaseInitPromise + } + return contentDatabase +} - await contentDatabase.storage.setItem('navigation', navigation) +/** + * Initialize content database + * - Fetch content from cache api + * - Call `content:storage` hook to allow plugins to fill storage + */ +async function initContentDatabase () { + const nuxtApp = useNuxtApp() + const { clientDB } = useRuntimeConfig().public.content - await contentDatabase.storage.setItem('integrity', clientDB.integrity) - } + const _contentDatabase = createDB(contentStorage) + const integrity = await _contentDatabase.storage.getItem('integrity') + if (clientDB.integrity !== +integrity) { + const { contents, navigation } = await $fetch(withContentBase('cache.json')) as any + + await Promise.all( + contents.map(content => _contentDatabase.storage.setItem(`cache:${content._id}`, content)) + ) + + await _contentDatabase.storage.setItem('navigation', navigation) + + await _contentDatabase.storage.setItem('integrity', clientDB.integrity) } - return contentDatabase + + // call `content:storage` hook to allow plugins to fill storage + // @ts-ignore + await nuxtApp.callHook('content:storage', _contentDatabase.storage) + + return _contentDatabase } export async function generateNavigation (query): Promise> { diff --git a/src/runtime/composables/helpers.ts b/src/runtime/composables/helpers.ts index fc3624f5e..82175430e 100644 --- a/src/runtime/composables/helpers.ts +++ b/src/runtime/composables/helpers.ts @@ -48,7 +48,7 @@ const navKeyFromPath = (path: string, key: string, tree: NavItem[]) => { const goDeep = (path: string, tree: NavItem[]) => { for (const file of tree) { - if (path.startsWith(file._path) && file[key]) { value = file[key] } + if (path?.startsWith(file._path) && file[key]) { value = file[key] } if (file._path === path) { return } diff --git a/src/runtime/preview/preview-plugin.ts b/src/runtime/preview/preview-plugin.ts index e6962c36e..3d98c0461 100644 --- a/src/runtime/preview/preview-plugin.ts +++ b/src/runtime/preview/preview-plugin.ts @@ -1,12 +1,11 @@ import { createApp } from 'vue' -import { contentStorage } from '../composables/client-db' -import { defineNuxtPlugin, useRoute, useCookie, refreshNuxtData, useRuntimeConfig } from '#imports' +import { defineNuxtPlugin, useRoute, useCookie, refreshNuxtData, useRuntimeConfig, useNuxtApp } from '#imports' import { ContentPreviewMode } from '#components' export default defineNuxtPlugin((nuxt) => { const { previewAPI } = useRuntimeConfig().public.content - async function fetchData (token: string) { + async function fetchData (token: string, contentStorage) { // Fetch preview data from station const data = await $fetch('api/projects/preview', { baseURL: previewAPI, @@ -16,14 +15,7 @@ export default defineNuxtPlugin((nuxt) => { }) // Remove previous preview data const keys = await contentStorage.getKeys(`${token}:`) - keys.forEach(key => contentStorage.removeItem(key)) - - // Fill store with preview content - const items = [ - ...(data.files || []), - ...data.additions, - ...data.deletions.map(d => ({ ...d, parsed: { _id: d.path.replace(/\//g, ':'), __deleted: true } })) - ] + await Promise.all(keys.map(key => contentStorage.removeItem(key))) // Set preview meta await contentStorage.setItem( @@ -33,12 +25,21 @@ export default defineNuxtPlugin((nuxt) => { }) ) - for (const item of items) { - await contentStorage.setItem(`${token}:${item.parsed._id}`, JSON.stringify(item.parsed)) - } + // Fill store with preview content + const items = [ + ...(data.files || []), + ...data.additions, + ...data.deletions.map(d => ({ ...d, parsed: { _id: d.path.replace(/\//g, ':'), __deleted: true } })) + ] + + await Promise.all( + items.map(item => contentStorage.setItem(`${token}:${item.parsed._id}`, JSON.stringify(item.parsed))) + ) } - async function initializePreview () { + function initializePreview () { + let contentStorage + const nuxtApp = useNuxtApp() const query = useRoute().query || {} const previewToken = useCookie('previewToken', { sameSite: 'none', secure: true }) if (!query.preview && !previewToken.value) { @@ -55,10 +56,15 @@ export default defineNuxtPlugin((nuxt) => { createApp(ContentPreviewMode, { previewToken, apiURL: previewAPI, - onRefresh: () => fetchData(previewToken.value).then(() => refreshNuxtData()) + onRefresh: () => fetchData(previewToken.value, contentStorage).then(() => refreshNuxtData()) }).mount(el) - await fetchData(previewToken.value) + // @ts-ignore + nuxtApp.hook('content:storage', async (storage) => { + contentStorage = storage + await fetchData(previewToken.value, contentStorage) + refreshNuxtData() + }) } nuxt.hook('app:mounted', async () => {