You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vitepress/src/client/app/utils.ts

106 lines
3.0 KiB

import { tryOnUnmounted } from '@vueuse/core'
import { h, onMounted, shallowRef, type AsyncComponentLoader } from 'vue'
import {
EXTERNAL_URL_RE,
inBrowser,
sanitizeFileName,
type Awaitable
} from '../shared'
import { siteDataRef } from './data'
export { escapeHtml as _escapeHtml, inBrowser } from '../shared'
/**
* Join two paths by resolving the slash collision.
*/
export function joinPath(base: string, path: string) {
return `${base}${path}`.replace(/\/+/g, '/')
}
/**
* Prepend base to internal (non-relative) urls
*/
export function withBase(path: string) {
return EXTERNAL_URL_RE.test(path) || !path.startsWith('/')
? path
: joinPath(siteDataRef.value.base, path)
}
/**
* Converts a url path to the corresponding js chunk filename.
*/
export function pathToFile(path: string) {
let pagePath = path.replace(/\.html$/, '')
pagePath = decodeURIComponent(pagePath)
pagePath = pagePath.replace(/\/$/, '/index') // /foo/ -> /foo/index
if (import.meta.env.DEV) {
// always force re-fetch content in dev
pagePath += `.md?t=${Date.now()}`
} else {
// in production, each .md file is built into a .md.js file following
// the path conversion scheme.
// /foo/bar.html -> ./foo_bar.md
if (inBrowser) {
const base = import.meta.env.BASE_URL
pagePath =
sanitizeFileName(
pagePath.slice(base.length).replace(/\//g, '_') || 'index'
) + '.md'
// client production build needs to account for page hash, which is
// injected directly in the page's html
let pageHash = __VP_HASH_MAP__[pagePath.toLowerCase()]
if (!pageHash) {
pagePath = pagePath.endsWith('_index.md')
? pagePath.slice(0, -9) + '.md'
: pagePath.slice(0, -3) + '_index.md'
pageHash = __VP_HASH_MAP__[pagePath.toLowerCase()]
}
if (!pageHash) return null
pagePath = `${base}${__ASSETS_DIR__}/${pagePath}.${pageHash}.js`
} else {
// ssr build uses much simpler name mapping
pagePath = `./${sanitizeFileName(
pagePath.slice(1).replace(/\//g, '_')
)}.md.js`
}
}
return pagePath
}
export let contentUpdatedCallbacks: (() => any)[] = []
/**
* Register callback that is called every time the markdown content is updated
* in the DOM.
*/
export function onContentUpdated(fn: () => any) {
contentUpdatedCallbacks.push(fn)
tryOnUnmounted(() => {
contentUpdatedCallbacks = contentUpdatedCallbacks.filter((f) => f !== fn)
})
}
export function defineClientComponent(
loader: AsyncComponentLoader,
args?: any[],
cb?: () => Awaitable<void>
) {
return {
setup() {
const comp = shallowRef()
onMounted(async () => {
let res = await loader()
// interop module default
if (res && (res.__esModule || res[Symbol.toStringTag] === 'Module')) {
res = res.default
}
comp.value = res
await cb?.()
})
return () => (comp.value ? h(comp.value, ...(args ?? [])) : null)
}
}
}