diff --git a/src/client/app/data.ts b/src/client/app/data.ts index 16288e87..72fb7831 100644 --- a/src/client/app/data.ts +++ b/src/client/app/data.ts @@ -57,18 +57,9 @@ export interface VitePressData { // site data is a singleton export const siteDataRef: Ref = 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(() => diff --git a/src/node/alias.ts b/src/node/alias.ts index ad9eae4b..e30e557c 100644 --- a/src/node/alias.ts +++ b/src/node/alias.ts @@ -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 = { - '@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') diff --git a/src/node/config.ts b/src/node/config.ts index fba1e0c0..80342d9e 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -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) => [ diff --git a/src/node/plugin.ts b/src/node/plugin.ts index 0183bfbc..34e02d4f 100644 --- a/src/node/plugin.ts +++ b/src/node/plugin.ts @@ -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 || diff --git a/src/node/plugins/localSearchPlugin.ts b/src/node/plugins/localSearchPlugin.ts index a5ed5f06..61803981 100644 --- a/src/node/plugins/localSearchPlugin.ts +++ b/src/node/plugins/localSearchPlugin.ts @@ -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(',')}}`