From d6952f37b76dfe4c780c90de49b7b882818a43e7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 27 Feb 2023 11:37:33 +0800 Subject: [PATCH] wip: hmr for dynamic routes on add/delete --- src/node/plugin.ts | 2 +- src/node/plugins/dynamicRoutesPlugin.ts | 53 ++++++++++++++++++++----- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/node/plugin.ts b/src/node/plugin.ts index e0f60e7b..47a3d9e3 100644 --- a/src/node/plugin.ts +++ b/src/node/plugin.ts @@ -349,6 +349,6 @@ export async function createVitePressPlugin( webFontsPlugin(siteConfig.useWebFonts), ...(userViteConfig?.plugins || []), staticDataPlugin, - await dynamicRoutesPlugin(siteConfig.dynamicRoutes) + await dynamicRoutesPlugin(siteConfig) ] } diff --git a/src/node/plugins/dynamicRoutesPlugin.ts b/src/node/plugins/dynamicRoutesPlugin.ts index 1b7f943d..ff71fc7c 100644 --- a/src/node/plugins/dynamicRoutesPlugin.ts +++ b/src/node/plugins/dynamicRoutesPlugin.ts @@ -1,7 +1,13 @@ -import { loadConfigFromFile, type Plugin, type ViteDevServer } from 'vite' +import { + loadConfigFromFile, + normalizePath, + type Plugin, + type ViteDevServer +} from 'vite' import fs from 'fs-extra' import c from 'picocolors' import path from 'path' +import type { SiteConfig } from '../config' export const dynamicRouteRE = /\[(\.\.\.)?\w+\]/ @@ -32,25 +38,54 @@ type ResolvedRouteConfig = UserRouteConfig & { } export const dynamicRoutesPlugin = async ( - initialRoutes: string[] + config: SiteConfig ): Promise => { let server: ViteDevServer - let [resolvedRoutes, routeFileToModulesMap] = await resolveRoutes( - initialRoutes - ) + let routes = config.dynamicRoutes + let [resolvedRoutes, routeFileToModulesMap] = await resolveRoutes(routes) + + // TODO: make this more efficient by only reloading the invalidated route + // TODO: invlidate modules for paths that are no longer present + async function invlidateRoutes() { + ;[resolvedRoutes, routeFileToModulesMap] = await resolveRoutes(routes) + } return { name: 'vitepress:dynamic-routes', configureServer(_server) { server = _server + + const onFileAddDelete = ( + file: string, + updateRoutes: (route: string) => void + ) => { + if (dynamicRouteRE.test(file) && /\.(md|paths\.[jt]s)$/.test(file)) { + if (file.endsWith('.md')) { + updateRoutes(normalizePath(path.relative(config.root, file))) + } + invlidateRoutes().then(() => { + server.ws.send({ type: 'full-reload' }) + }) + } + } + + server.watcher + .on('add', (file) => { + onFileAddDelete(file, (route) => routes.push(route)) + }) + .on('unlink', (file) => { + onFileAddDelete(file, (route) => { + routes = routes.filter((r) => r !== route) + }) + }) }, load(id) { const matched = resolvedRoutes.find((r) => r.path === id) if (matched) { const { route, params, content } = matched - const routeFile = path.resolve(route) + const routeFile = normalizePath(path.resolve(config.root, route)) routeFileToModulesMap[routeFile].push(id) let baseContent = fs.readFileSync(routeFile, 'utf-8') @@ -75,12 +110,8 @@ export const dynamicRoutesPlugin = async ( 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 - ) + await invlidateRoutes() } for (const id of mods) { ctx.modules.push(server.moduleGraph.getModuleById(id)!)