mirror of https://github.com/vuejs/vitepress
parent
f05fe8b7f6
commit
bf8fd3a6f3
@ -0,0 +1,107 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import { Plugin } from 'vite'
|
||||||
|
import { SiteConfig, resolveSiteData } from './config'
|
||||||
|
import { createMarkdownToVueRenderFn } from './markdownToVue'
|
||||||
|
import { APP_PATH, SITE_DATA_REQUEST_PATH } from './resolver'
|
||||||
|
import createVuePlugin from '@vitejs/plugin-vue'
|
||||||
|
import slash from 'slash'
|
||||||
|
|
||||||
|
export function createVitePressPlugin(
|
||||||
|
root: string,
|
||||||
|
{ configPath, aliases, markdown, site: initialSiteData }: SiteConfig
|
||||||
|
): Plugin[] {
|
||||||
|
const markdownToVue = createMarkdownToVueRenderFn(root, markdown)
|
||||||
|
|
||||||
|
const vuePlugin = createVuePlugin({
|
||||||
|
include: [/\.vue$/, /\.md$/]
|
||||||
|
})
|
||||||
|
|
||||||
|
let siteData = initialSiteData
|
||||||
|
let stringifiedData = JSON.stringify(JSON.stringify(initialSiteData))
|
||||||
|
|
||||||
|
const vitePressPlugin: Plugin = {
|
||||||
|
name: 'vitepress',
|
||||||
|
|
||||||
|
config() {
|
||||||
|
return {
|
||||||
|
alias: aliases,
|
||||||
|
transformInclude: /\.md$/
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
resolveId(id) {
|
||||||
|
if (id === SITE_DATA_REQUEST_PATH) {
|
||||||
|
return SITE_DATA_REQUEST_PATH
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
load(id) {
|
||||||
|
if (id === SITE_DATA_REQUEST_PATH) {
|
||||||
|
return `export default ${stringifiedData}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
transform(code, id) {
|
||||||
|
if (id.endsWith('.md')) {
|
||||||
|
// transform .md files into vueSrc so plugin-vue can handle it
|
||||||
|
return markdownToVue(code, id).vueSrc
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
configureServer(server) {
|
||||||
|
// serve our index.html after vite history fallback
|
||||||
|
const indexPath = `/@fs/${path.join(APP_PATH, 'index.html')}`
|
||||||
|
return () => {
|
||||||
|
server.app.use((req, _, next) => {
|
||||||
|
if (req.url!.endsWith('.html')) {
|
||||||
|
req.url = indexPath
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleHotUpdate(file, mods, read, server) {
|
||||||
|
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
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// hot reload .md files as .vue files
|
||||||
|
if (file.endsWith('.md')) {
|
||||||
|
const content = await read()
|
||||||
|
const { pageData, vueSrc } = markdownToVue(
|
||||||
|
content.toString(),
|
||||||
|
file,
|
||||||
|
// do not inject pageData on HMR
|
||||||
|
// it leads to plugin-vue 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
|
||||||
|
server.ws.send({
|
||||||
|
type: 'custom',
|
||||||
|
event: 'vitepress:pageData',
|
||||||
|
data: {
|
||||||
|
path: `/${slash(path.relative(root, file))}`,
|
||||||
|
pageData
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// reload the content component
|
||||||
|
return vuePlugin.handleHotUpdate!(file, mods, () => vueSrc, server)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [vitePressPlugin, vuePlugin]
|
||||||
|
}
|
@ -1,139 +1,17 @@
|
|||||||
import path from 'path'
|
import { createServer as createViteServer, ServerOptions } from 'vite'
|
||||||
import {
|
import { resolveConfig } from './config'
|
||||||
createServer as createViteServer,
|
import { createVitePressPlugin } from './plugin'
|
||||||
cachedRead,
|
|
||||||
ServerConfig,
|
|
||||||
ServerPlugin
|
|
||||||
} from 'vite'
|
|
||||||
import { resolveConfig, SiteConfig, resolveSiteData } from './config'
|
|
||||||
import { createMarkdownToVueRenderFn } from './markdownToVue'
|
|
||||||
import { APP_PATH, SITE_DATA_REQUEST_PATH } from './resolver'
|
|
||||||
import { existsSync } from 'fs'
|
|
||||||
|
|
||||||
const debug = require('debug')('vitepress:serve')
|
export async function createServer(
|
||||||
const debugHmr = require('debug')('vitepress:hmr')
|
root: string = process.cwd(),
|
||||||
|
serverOptions: ServerOptions = {}
|
||||||
function createVitePressPlugin({
|
) {
|
||||||
configPath,
|
const config = await resolveConfig(root)
|
||||||
markdown,
|
|
||||||
site: initialSiteData
|
|
||||||
}: SiteConfig): ServerPlugin {
|
|
||||||
return ({ app, root, watcher, resolver }) => {
|
|
||||||
const markdownToVue = createMarkdownToVueRenderFn(root, markdown)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
if (!existsSync(file)) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
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({
|
return createViteServer({
|
||||||
...options,
|
root,
|
||||||
configureServer: createVitePressPlugin(config),
|
logLevel: 'warn',
|
||||||
resolvers: [config.resolver]
|
plugins: createVitePressPlugin(root, config),
|
||||||
|
server: serverOptions
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in new issue