import siteData from '@siteData' import { useDark, usePreferredDark } from '@vueuse/core' import { computed, inject, readonly, ref, shallowRef, watch, type InjectionKey, type Ref } from 'vue' import { APPEARANCE_KEY, createTitle, inBrowser, resolveSiteDataByRoute, type PageData, type SiteData } from '../shared' import type { Route } from './router' export const dataSymbol: InjectionKey = Symbol() export interface VitePressData { /** * Site-level metadata */ site: Ref> /** * themeConfig from .vitepress/config.js */ theme: Ref /** * Page-level metadata */ page: Ref /** * page frontmatter data */ frontmatter: Ref /** * dynamic route params */ params: Ref title: Ref description: Ref lang: Ref dir: Ref localeIndex: Ref isDark: Ref /** * Current location hash */ hash: Ref } // site data is a singleton export const siteDataRef: Ref = shallowRef( (import.meta.env.PROD ? siteData : readonly(siteData)) as SiteData ) // hmr if (import.meta.hot) { import.meta.hot.accept('@siteData', (m) => { if (m) { siteDataRef.value = m.default } }) } // per-app data export function initData(route: Route): VitePressData { const site = computed(() => resolveSiteDataByRoute(siteDataRef.value, route.data.relativePath) ) const appearance = site.value.appearance // fine with reactivity being lost here, config change triggers a restart const isDark = appearance === 'force-dark' ? ref(true) : appearance === 'force-auto' ? usePreferredDark() : appearance ? useDark({ storageKey: APPEARANCE_KEY, initialValue: () => (appearance === 'dark' ? 'dark' : 'auto'), ...(typeof appearance === 'object' ? appearance : {}) }) : ref(false) const hashRef = ref(inBrowser ? location.hash : '') if (inBrowser) { window.addEventListener('hashchange', () => { hashRef.value = location.hash }) } watch( () => route.data, () => { hashRef.value = inBrowser ? location.hash : '' } ) return { site, theme: computed(() => site.value.themeConfig), page: computed(() => route.data), frontmatter: computed(() => route.data.frontmatter), params: computed(() => route.data.params), lang: computed(() => site.value.lang), dir: computed(() => route.data.frontmatter.dir || site.value.dir), localeIndex: computed(() => site.value.localeIndex || 'root'), title: computed(() => createTitle(site.value, route.data)), description: computed( () => route.data.description || site.value.description ), isDark, hash: computed(() => hashRef.value) } } export function useData(): VitePressData { const data = inject(dataSymbol) if (!data) { throw new Error('vitepress data not properly injected in app') } return data }