wip: basic hmr for dynamic routes

pull/2005/head
Evan You 2 years ago
parent 9c4c2117a6
commit 08df389db8

@ -1,4 +1,4 @@
import { loadConfigFromFile, type Plugin } from 'vite'
import { loadConfigFromFile, type Plugin, type ViteDevServer } from 'vite'
import fs from 'fs-extra'
import c from 'picocolors'
import path from 'path'
@ -32,9 +32,67 @@ type ResolvedRouteConfig = UserRouteConfig & {
}
export const dynamicRoutesPlugin = async (
routes: string[]
initialRoutes: string[]
): Promise<Plugin> => {
let server: ViteDevServer
let [resolvedRoutes, routeFileToModulesMap] = await resolveRoutes(
initialRoutes
)
return {
name: 'vitepress:dynamic-routes',
configureServer(_server) {
server = _server
},
load(id) {
const matched = resolvedRoutes.find((r) => r.path === id)
if (matched) {
const { route, params, content } = matched
const routeFile = path.resolve(route)
routeFileToModulesMap[routeFile].push(id)
let baseContent = fs.readFileSync(routeFile, 'utf-8')
// inject raw content
// this is intended for integration with CMS
// we use a speical injection syntax so the content is rendered as
// static local content instead of included as runtime data.
if (content) {
baseContent = baseContent.replace(/<!--\s*@content\s*-->/, content)
}
// params are injected with special markers and extracted as part of
// __pageData in ../markdownTovue.ts
return `__VP_PARAMS_START${JSON.stringify(
params
)}__VP_PARAMS_END__${baseContent}`
}
},
async handleHotUpdate(ctx) {
const mods = routeFileToModulesMap[ctx.file]
if (mods) {
// path loader module updated, reset loaded routes
// TODO: make this more efficient by only reloading the invalidated route
// TODO: invlidate modules for paths that are no longer present
if (/\.paths\.[jt]s$/.test(ctx.file)) {
;[resolvedRoutes, routeFileToModulesMap] = await resolveRoutes(
initialRoutes
)
}
for (const id of mods) {
ctx.modules.push(server.moduleGraph.getModuleById(id)!)
}
}
}
}
}
async function resolveRoutes(routes: string[]) {
const pendingResolveRoutes: Promise<ResolvedRouteConfig[]>[] = []
const routeFileToModulesMap: Record<string, string[]> = {}
for (const route of routes) {
// locate corresponding route paths file
@ -66,15 +124,20 @@ export const dynamicRoutesPlugin = async (
}
if (mod) {
// route md file and route paths loader file point to the same array
routeFileToModulesMap[mod.path] = routeFileToModulesMap[
path.resolve(route)
] = []
const resolveRoute = async (): Promise<ResolvedRouteConfig[]> => {
const loader = mod.config.paths
const paths = await (typeof loader === 'function' ? loader() : loader)
return paths.map((userConfig) => {
return {
route,
path:
'/' +
route.replace(/\[(\w+)\]/g, (_, key) => userConfig.params[key]),
route,
...userConfig
}
})
@ -83,28 +146,8 @@ export const dynamicRoutesPlugin = async (
}
}
const resolvedRoutes = (await Promise.all(pendingResolveRoutes)).flat()
return {
name: 'vitepress:dynamic-routes',
load(id) {
const matched = resolvedRoutes.find((r) => r.path === id)
if (matched) {
const { route, params, content } = matched
let baseContent = fs.readFileSync(route, 'utf-8')
// inject raw content at build time
if (content) {
baseContent = baseContent.replace(/<!--\s*@content\s*-->/, content)
}
// params are injected with special markers and extracted as part of
// __pageData in ../markdownTovue.ts
return `__VP_PARAMS_START${JSON.stringify(
params
)}__VP_PARAMS_END__${baseContent}`
}
}
// TODO HMR
}
return [
(await Promise.all(pendingResolveRoutes)).flat(),
routeFileToModulesMap
] as const
}

Loading…
Cancel
Save