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,
|
||||
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'
|
||||
import { createServer as createViteServer, ServerOptions } from 'vite'
|
||||
import { resolveConfig } from './config'
|
||||
import { createVitePressPlugin } from './plugin'
|
||||
|
||||
const debug = require('debug')('vitepress:serve')
|
||||
const debugHmr = require('debug')('vitepress:hmr')
|
||||
|
||||
function createVitePressPlugin({
|
||||
configPath,
|
||||
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)
|
||||
export async function createServer(
|
||||
root: string = process.cwd(),
|
||||
serverOptions: ServerOptions = {}
|
||||
) {
|
||||
const config = await resolveConfig(root)
|
||||
|
||||
return createViteServer({
|
||||
...options,
|
||||
configureServer: createVitePressPlugin(config),
|
||||
resolvers: [config.resolver]
|
||||
root,
|
||||
logLevel: 'warn',
|
||||
plugins: createVitePressPlugin(root, config),
|
||||
server: serverOptions
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in new issue