load config + provide siteData

pull/1/head
Evan You 4 years ago
parent d02ec5e3b4
commit c4002a8d2e

@ -1,5 +1,3 @@
export function usePageData() {
return {
msg: 'this is page'
}
}

@ -1,5 +1,21 @@
import serialized from '@siteData'
import { hot } from '@hmr'
import { shallowRef, readonly } from 'vue'
/**
* @param {string} data
*/
const parse = (data) =>
__DEV__ ? readonly(JSON.parse(data)) : JSON.parse(data)
// site data
export const siteDataRef = shallowRef(parse(serialized))
export function useSiteData() {
return {
msg: 'this is site'
}
return siteDataRef
}
// hmr
hot.accept('/@siteData', (m) => {
siteDataRef.value = parse(m.default)
})

@ -1,2 +1,3 @@
<div id="app"></div>
<script>window.__DEV__ = true</script>
<script type="module" src="/@app/index.js"></script>

@ -1,7 +1,7 @@
import { createApp, h } from 'vue'
import { Content } from './components/Content'
import { useRouter } from './composables/router'
import { useSiteData } from './composables/siteData'
import { siteDataRef } from './composables/siteData'
import Theme from '/@theme/index'
const App = {
@ -18,7 +18,7 @@ const App = {
const app = createApp(App)
Object.defineProperty(app.config.globalProperties, '$site', {
get: useSiteData
get: () => siteDataRef.value
})
app.component('Content', Content)

13
lib/shim.d.ts vendored

@ -1,5 +1,18 @@
declare const __DEV__: boolean
declare module "*.vue" {
import { ComponentOptions } from 'vue'
const comp: ComponentOptions
export default comp
}
declare module "@siteData" {
const data: string
export default data
}
declare module "@hmr" {
export declare const hot: {
accept(path: string, cb: (module: any) => void)
}
}

@ -13,10 +13,12 @@
import { useSiteData, usePageData } from 'vitepress'
export default {
data: () => ({
site: useSiteData(),
page: usePageData()
})
setup() {
return {
site: useSiteData(),
page: usePageData()
}
}
}
</script>

@ -5,7 +5,7 @@ import { Resolver } from 'vite'
const debug = require('debug')('vitepress:config')
export interface UserConfig<ThemeConfig = Record<string, any>> {
export interface UserConfig<ThemeConfig = any> {
base?: string
title?: string
description?: string
@ -16,7 +16,7 @@ export interface UserConfig<ThemeConfig = Record<string, any>> {
// TODO locales support etc.
}
export interface SiteData<ThemeConfig = Record<string, any>> {
export interface SiteData<ThemeConfig = any> {
title: string
description: string
base: string
@ -28,22 +28,49 @@ export interface PageData {
path: string
}
export interface ResolvedConfig<ThemeConfig = Record<string, any>> {
export interface ResolvedConfig<ThemeConfig = any> {
site: SiteData<ThemeConfig>
root: string // project root on file system
themePath: string
resolver: Resolver
}
export const getConfigPath = (root: string) =>
path.join(root, '.vitepress/config.js')
export async function resolveConfig(root: string): Promise<ResolvedConfig> {
const site = await resolveSiteData(root)
// resolve theme path
const userThemePath = path.join(root, '.vitepress/theme')
let themePath: string
try {
await fs.stat(userThemePath)
themePath = userThemePath
} catch (e) {
themePath = path.join(__dirname, '../lib/theme-default')
}
const config: ResolvedConfig = {
site,
themePath,
resolver: createResolver(themePath)
}
return config
}
export async function resolveSiteData(root: string): Promise<SiteData> {
// 1. load user config
const configPath = path.join(root, '.vitepress/config.js')
const configPath = getConfigPath(root)
let hasUserConfig = false
try {
await fs.stat(configPath)
hasUserConfig = true
debug(`loading user config at ${configPath}`)
} catch (e) {}
// always delete cache first before loading config
delete require.cache[configPath]
const userConfig: UserConfig = hasUserConfig ? require(configPath) : {}
// 2. TODO scan pages data
@ -57,22 +84,5 @@ export async function resolveConfig(root: string): Promise<ResolvedConfig> {
pages: []
}
// 4. resolve theme path
const userThemePath = path.join(root, '.vitepress/theme')
let themePath: string
try {
await fs.stat(userThemePath)
themePath = userThemePath
} catch (e) {
themePath = path.join(__dirname, '../lib/theme-default')
}
const config: ResolvedConfig = {
root,
site,
themePath,
resolver: createResolver(themePath)
}
return config
return site
}

@ -5,17 +5,25 @@ import {
Plugin,
ServerConfig
} from 'vite'
import { resolveConfig, ResolvedConfig } from './resolveConfig'
import {
resolveConfig,
ResolvedConfig,
getConfigPath,
resolveSiteData
} from './resolveConfig'
import { createMarkdownToVueRenderFn } from './markdown/markdownToVue'
import { APP_PATH } from './utils/pathResolver'
import { APP_PATH, SITE_DATA_REQUEST_PATH } from './utils/pathResolver'
const debug = require('debug')('vitepress:serve')
const debugHmr = require('debug')('vitepress:hmr')
function createVitePressPlugin({
themePath,
resolver: vitepressResolver
}: ResolvedConfig): Plugin {
function createVitePressPlugin(config: ResolvedConfig): Plugin {
const {
themePath,
site: initialSiteData,
resolver: vitepressResolver
} = config
return ({ app, root, watcher, resolver }) => {
const markdownToVue = createMarkdownToVueRenderFn(root)
@ -39,8 +47,38 @@ function createVitePressPlugin({
}
})
// hot reload handling for siteData
// the data is stringified twice so it is sent to the client as a string
// it is then parsed on the client via JSON.parse() which is faster than
// parsing the object literal as JavaScript.
let siteData = initialSiteData
let stringifiedData = JSON.stringify(JSON.stringify(initialSiteData))
const configPath = getConfigPath(root)
watcher.add(configPath)
watcher.on('change', async (file) => {
if (file === configPath) {
const newData = await resolveSiteData(root)
stringifiedData = JSON.stringify(JSON.stringify(newData))
if (newData.base !== siteData.base) {
console.warn(
`[vitepress]: config.base has changed. Please restart the dev server.`
)
}
siteData = newData
watcher.handleJSReload(SITE_DATA_REQUEST_PATH)
}
})
// inject Koa middleware
app.use(async (ctx, next) => {
// serve siteData (which is a virtual file)
if (ctx.path === SITE_DATA_REQUEST_PATH) {
ctx.type = 'js'
ctx.body = `export default ${stringifiedData}`
debug(ctx.url)
return
}
// handle .md -> vue transforms
if (ctx.path.endsWith('.md')) {
const file = resolver.requestToFile(ctx.path)

@ -4,6 +4,9 @@ import { Resolver } from "vite"
// built ts files are placed into /dist
export const APP_PATH = path.join(__dirname, '../../lib/app')
// speical virtual file
export const SITE_DATA_REQUEST_PATH = '/@siteData'
// this is a path resolver that is passed to vite
// so that we can resolve custom requests that start with /@app or /@theme
// we also need to map file paths back to their public served paths so that
@ -25,6 +28,9 @@ export function createResolver(themePath: string): Resolver {
if (filePath.startsWith(themePath)) {
return `/@theme/${path.relative(themePath, filePath)}`
}
if (filePath === SITE_DATA_REQUEST_PATH) {
return SITE_DATA_REQUEST_PATH
}
},
idToRequest(id) {
if (id === 'vitepress') {

Loading…
Cancel
Save