From a26a854eda45e5e6bcdf433b7a6df1ba7f3afe10 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 9 Nov 2023 00:23:01 +0800 Subject: [PATCH] fix: fix style block hmr for vitepress md files close vuejs/vitepress#3129 --- packages/plugin-vue/src/handleHotUpdate.ts | 18 +++++++++++++++--- packages/plugin-vue/src/main.ts | 19 ++++++++++++++++--- .../plugin-vue/src/utils/descriptorCache.ts | 7 ++++++- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/packages/plugin-vue/src/handleHotUpdate.ts b/packages/plugin-vue/src/handleHotUpdate.ts index f12ef54b..f9e3514b 100644 --- a/packages/plugin-vue/src/handleHotUpdate.ts +++ b/packages/plugin-vue/src/handleHotUpdate.ts @@ -4,6 +4,7 @@ import type { HmrContext, ModuleNode } from 'vite' import { isCSSRequest } from 'vite' import { + cache, createDescriptor, getDescriptor, invalidateDescriptor, @@ -148,9 +149,20 @@ export async function handleHotUpdate( updateType.push(`style`) } if (updateType.length) { - // invalidate the descriptor cache so that the next transform will - // re-analyze the file and pick up the changes. - invalidateDescriptor(file) + if (file.endsWith('.vue')) { + // invalidate the descriptor cache so that the next transform will + // re-analyze the file and pick up the changes. + invalidateDescriptor(file) + } else { + // https://github.com/vuejs/vitepress/issues/3129 + // For non-vue files, e.g. .md files in VitePress, invalidating the + // descriptor will cause the main `load()` hook to attempt to read and + // parse a descriptor from a non-vue source file, leading to errors. + // To fix that we need to provide the descriptor we parsed here in the + // main cache. This assumes no other plugin is applying pre-transform to + // the file type - not impossible, but should be extremely unlikely. + cache.set(file, descriptor) + } debug(`[vue:update(${updateType.join('&')})] ${file}`) } return [...affectedModules].filter(Boolean) as ModuleNode[] diff --git a/packages/plugin-vue/src/main.ts b/packages/plugin-vue/src/main.ts index 7798d61c..60693829 100644 --- a/packages/plugin-vue/src/main.ts +++ b/packages/plugin-vue/src/main.ts @@ -40,9 +40,22 @@ export async function transformMain( const prevDescriptor = getPrevDescriptor(filename) const { descriptor, errors } = createDescriptor(filename, code, options) - if (fs.existsSync(filename)) - // set descriptor for HMR if it's not set yet - getDescriptor(filename, options, true, true) + if (fs.existsSync(filename)) { + // populate descriptor cache for HMR if it's not set yet + getDescriptor( + filename, + options, + true, + true, + // for vue files, create descriptor from fs read to be consistent with + // logic in handleHotUpdate() + // for non vue files, e.g. md files in vitepress, we assume + // `hmrContext.read` is overwriten so handleHotUpdate() is dealing with + // post-transform code, so we populate the descriptor with post-transform + // code here as well. + filename.endsWith('.vue') ? undefined : code, + ) + } if (errors.length) { errors.forEach((error) => diff --git a/packages/plugin-vue/src/utils/descriptorCache.ts b/packages/plugin-vue/src/utils/descriptorCache.ts index 297f9648..584adb39 100644 --- a/packages/plugin-vue/src/utils/descriptorCache.ts +++ b/packages/plugin-vue/src/utils/descriptorCache.ts @@ -12,6 +12,10 @@ export interface SFCParseResult { } export const cache = new Map() +// we use a separate descriptor cache for HMR purposes. +// The main cached descriptors are parsed from SFCs that may have been +// transformed by other plugins, e.g. vue-macros; +// The HMR cached descriptors are based on the raw, pre-transform SFCs. export const hmrCache = new Map() const prevCache = new Map() @@ -52,6 +56,7 @@ export function getDescriptor( options: ResolvedOptions, createIfNotFound = true, hmr = false, + code?: string, ): SFCDescriptor | undefined { const _cache = hmr ? hmrCache : cache if (_cache.has(filename)) { @@ -60,7 +65,7 @@ export function getDescriptor( if (createIfNotFound) { const { descriptor, errors } = createDescriptor( filename, - fs.readFileSync(filename, 'utf-8'), + code ?? fs.readFileSync(filename, 'utf-8'), options, hmr, )