mirror of https://github.com/vuejs/vitepress
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.
134 lines
3.9 KiB
134 lines
3.9 KiB
import path from 'path'
|
|
import {
|
|
createServer as createViteServer,
|
|
cachedRead,
|
|
ServerConfig,
|
|
ServerPlugin
|
|
} from 'vite'
|
|
import { resolveConfig, SiteConfig, resolveSiteData } from './config'
|
|
import { createMarkdownToVueRenderFn } from './markdownToVue'
|
|
import { APP_PATH, SITE_DATA_REQUEST_PATH } from './resolver'
|
|
|
|
const debug = require('debug')('vitepress:serve')
|
|
const debugHmr = require('debug')('vitepress:hmr')
|
|
|
|
function createVitePressPlugin({
|
|
configPath,
|
|
site: initialSiteData
|
|
}: SiteConfig): ServerPlugin {
|
|
return ({ app, root, watcher, resolver }) => {
|
|
const markdownToVue = createMarkdownToVueRenderFn(root)
|
|
|
|
// hot reload .md files as .vue files
|
|
watcher.on('change', async (file) => {
|
|
if (file.endsWith('.md')) {
|
|
debugHmr(`reloading ${file}`)
|
|
const content = await cachedRead(null, file)
|
|
const timestamp = Date.now()
|
|
const { pageData, vueSrc } = markdownToVue(
|
|
content.toString(),
|
|
file,
|
|
timestamp,
|
|
// do not inject pageData on HMR
|
|
// it leads to vite to think <script> has changed and reloads the
|
|
// component instead of re-rendering.
|
|
// pageData needs separate HMR logic anyway (see below)
|
|
false
|
|
)
|
|
|
|
// notify the client to update page data
|
|
watcher.send({
|
|
type: 'custom',
|
|
id: 'vitepress:pageData',
|
|
customData: {
|
|
path: resolver.fileToRequest(file),
|
|
pageData
|
|
}
|
|
})
|
|
|
|
// reload the content component
|
|
watcher.handleVueReload(file, timestamp, vueSrc)
|
|
}
|
|
})
|
|
|
|
// 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))
|
|
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)
|
|
await cachedRead(ctx, file)
|
|
|
|
// let vite know this is supposed to be treated as vue file
|
|
ctx.vue = true
|
|
|
|
const { vueSrc, pageData } = markdownToVue(
|
|
ctx.body,
|
|
file,
|
|
ctx.lastModified.getTime(),
|
|
false
|
|
)
|
|
ctx.body = vueSrc
|
|
debug(ctx.url, ctx.status)
|
|
|
|
await next()
|
|
|
|
// make sure this is the main <script> block
|
|
if (!ctx.query.type) {
|
|
// inject pageData to generated script
|
|
ctx.body += `\nexport const __pageData = ${JSON.stringify(
|
|
JSON.stringify(pageData)
|
|
)}`
|
|
}
|
|
return
|
|
}
|
|
|
|
await next()
|
|
|
|
// serve our index.html after vite history fallback
|
|
if (ctx.url.endsWith('.html')) {
|
|
await cachedRead(ctx, path.join(APP_PATH, 'index.html'))
|
|
ctx.status = 200
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
export async function createServer(options: ServerConfig = {}) {
|
|
const config = await resolveConfig(options.root)
|
|
|
|
return createViteServer({
|
|
...options,
|
|
configureServer: createVitePressPlugin(config),
|
|
resolvers: [config.resolver]
|
|
})
|
|
}
|