fix(hmr): no need for server restart on theme change

pull/4827/merge
Divyansh Singh 4 weeks ago
parent 9fc8462726
commit d3a15673bd

@ -57,18 +57,9 @@ export interface VitePressData<T = any> {
// site data is a singleton
export const siteDataRef: Ref<SiteData> = shallowRef(
(import.meta.env.PROD ? siteData : readonly(siteData)) as SiteData
readonly(siteData) as SiteData
)
// hmr
if (import.meta.hot) {
import.meta.hot.accept('@siteData', (m) => {
if (m) {
siteDataRef.value = m.default
}
})
}
// per-app data
export function initData(route: Route): VitePressData {
const site = computed(() =>

@ -2,7 +2,6 @@ import { createRequire } from 'node:module'
import { join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import type { Alias, AliasOptions } from 'vite'
import type { SiteConfig } from './config'
const require = createRequire(import.meta.url)
const PKG_ROOT = resolve(fileURLToPath(import.meta.url), '../..')
@ -20,20 +19,8 @@ export const SITE_DATA_REQUEST_PATH = '/' + SITE_DATA_ID
const vueRuntimePath = 'vue/dist/vue.runtime.esm-bundler.js'
export function resolveAliases(
{ root, themeDir }: SiteConfig,
ssr: boolean
): AliasOptions {
const paths: Record<string, string> = {
'@theme': themeDir,
[SITE_DATA_ID]: SITE_DATA_REQUEST_PATH
}
export function resolveAliases(root: string, ssr: boolean): AliasOptions {
const aliases: Alias[] = [
...Object.keys(paths).map((p) => ({
find: p,
replacement: paths[p]
})),
{
find: /^vitepress$/,
replacement: join(DIST_CLIENT_PATH, '/index.js')

@ -233,7 +233,7 @@ export async function resolveUserConfig(
root: string,
command: 'serve' | 'build',
mode: string
): Promise<[UserConfig, string | undefined, string[]]> {
): Promise<[UserConfig, configPath: string | undefined, configDeps: string[]]> {
// load user config
const configPath = supportedConfigExtensions
.flatMap((ext) => [

@ -14,6 +14,7 @@ import {
APP_PATH,
DEFAULT_THEME_PATH,
DIST_CLIENT_PATH,
SITE_DATA_ID,
SITE_DATA_REQUEST_PATH,
resolveAliases
} from './alias'
@ -43,7 +44,8 @@ declare module 'vite' {
}
}
const themeRE = /\/\.vitepress\/theme\/index\.(m|c)?(j|t)s$/
const themeRE = /(?:^|\/)\.vitepress\/theme\/index\.(m|c)?(j|t)s$/
const startsWithThemeRE = /^@theme(?:\/|$)/
const docsearchRE = /\/@docsearch\/css\/dist\/style.css(?:$|\?)/
const hashRE = /\.([-\w]+)\.js$/
@ -131,7 +133,7 @@ export async function createVitePressPlugin(
config() {
const baseConfig: UserConfig = {
resolve: {
alias: resolveAliases(siteConfig, ssr)
alias: resolveAliases(siteConfig.root, ssr)
},
define: {
__VP_LOCAL_SEARCH__: site.themeConfig?.search?.provider === 'local',
@ -147,10 +149,7 @@ export async function createVitePressPlugin(
include: [
'vue',
'vitepress > @vue/devtools-api',
'vitepress > @vueuse/core',
siteConfig.themeDir === DEFAULT_THEME_PATH
? '@theme/index'
: undefined
'vitepress > @vueuse/core'
].filter((d) => d != null),
exclude: ['@docsearch/js', 'vitepress']
},
@ -170,10 +169,17 @@ export async function createVitePressPlugin(
: baseConfig
},
resolveId(id) {
if (id === SITE_DATA_REQUEST_PATH) {
resolveId(id, importer, resolveOptions) {
if (id === SITE_DATA_ID) {
return SITE_DATA_REQUEST_PATH
}
if (startsWithThemeRE.test(id)) {
return this.resolve(
siteConfig.themeDir + id.slice(6),
importer,
Object.assign({ skipSelf: true }, resolveOptions)
)
}
},
load(id) {
@ -260,31 +266,6 @@ export async function createVitePressPlugin(
configDeps.forEach((file) => server.watcher.add(file))
}
const onFileAddDelete = async (added: boolean, file: string) => {
const relativePath = path.posix.relative(srcDir, file)
// restart server on theme file creation / deletion
if (themeRE.test(relativePath)) {
siteConfig.logger.info(
c.green(
`${relativePath} ${added ? 'created' : 'deleted'}, restarting server...\n`
),
{ clear: true, timestamp: true }
)
await recreateServer?.()
}
// update pages, dynamicRoutes and rewrites on md file creation / deletion
if (relativePath.endsWith('.md')) await resolvePages(siteConfig)
if (!added && importerMap[relativePath]) {
delete importerMap[relativePath]
}
}
server.watcher
.on('add', onFileAddDelete.bind(null, true))
.on('unlink', onFileAddDelete.bind(null, false))
// serve our index.html after vite history fallback
return () => {
server.middlewares.use(async (req, res, next) => {
@ -369,8 +350,30 @@ export async function createVitePressPlugin(
}
},
async hotUpdate({ file }) {
async hotUpdate({ file, type }) {
if (this.environment.name !== 'client') return
const relativePath = path.posix.relative(srcDir, file)
if (themeRE.test(relativePath) && type !== 'update') {
siteConfig.themeDir =
type === 'create' ? path.posix.dirname(file) : DEFAULT_THEME_PATH
siteConfig.logger.info(c.green('page reload ') + c.dim(relativePath), {
clear: true,
timestamp: true
})
this.environment.moduleGraph.invalidateAll()
this.environment.hot.send({ type: 'full-reload' })
return []
}
// update pages, dynamicRoutes and rewrites on md file creation / deletion
if (file.endsWith('.md') && type !== 'update') {
await resolvePages(siteConfig)
}
if (type === 'delete') {
delete importerMap[relativePath]
}
if (
file === configPath ||

@ -30,7 +30,7 @@ export async function localSearchPlugin(
name: 'vitepress:local-search',
resolveId(id) {
if (id.startsWith(LOCAL_SEARCH_INDEX_ID)) {
return `/${id}`
return LOCAL_SEARCH_INDEX_REQUEST_PATH
}
},
load(id) {
@ -183,7 +183,7 @@ export async function localSearchPlugin(
records.push(
`${JSON.stringify(
locale
)}: () => import('@localSearchIndex${locale}')`
)}: () => import('${LOCAL_SEARCH_INDEX_ID}${locale}')`
)
}
return `export default {${records.join(',')}}`

Loading…
Cancel
Save