Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugin-vue): distinguish HMR and transform descriptor #232

Merged
merged 1 commit into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions packages/plugin-vue/src/handleHotUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isCSSRequest } from 'vite'
import {
createDescriptor,
getDescriptor,
setPrevDescriptor,
invalidateDescriptor,
} from './utils/descriptorCache'
import {
getResolvedScript,
Expand All @@ -26,16 +26,14 @@ export async function handleHotUpdate(
{ file, modules, read }: HmrContext,
options: ResolvedOptions,
): Promise<ModuleNode[] | void> {
const prevDescriptor = getDescriptor(file, options, false)
const prevDescriptor = getDescriptor(file, options, false, true)
if (!prevDescriptor) {
// file hasn't been requested yet (e.g. async component)
return
}

setPrevDescriptor(file, prevDescriptor)

const content = await read()
const { descriptor } = createDescriptor(file, content, options)
const { descriptor } = createDescriptor(file, content, options, true)

let needRerender = false
const affectedModules = new Set<ModuleNode | undefined>()
Expand Down Expand Up @@ -150,6 +148,9 @@ 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)
debug(`[vue:update(${updateType.join('&')})] ${file}`)
}
return [...affectedModules].filter(Boolean) as ModuleNode[]
Expand Down
5 changes: 4 additions & 1 deletion packages/plugin-vue/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { addMapping, fromMap, toEncodedMap } from '@jridgewell/gen-mapping'
import { normalizePath, transformWithEsbuild } from 'vite'
import {
createDescriptor,
getDescriptor,
getPrevDescriptor,
setSrcDescriptor,
} from './utils/descriptorCache'
Expand All @@ -35,10 +36,12 @@ export async function transformMain(
) {
const { devServer, isProduction, devToolsEnabled } = options

// prev descriptor is only set and used for hmr
const prevDescriptor = getPrevDescriptor(filename)
const { descriptor, errors } = createDescriptor(filename, code, options)

// set descriptor for HMR if it's not set yet
getDescriptor(filename, options, true, true)

if (errors.length) {
errors.forEach((error) =>
pluginContext.error(createRollupError(filename, error)),
Expand Down
26 changes: 16 additions & 10 deletions packages/plugin-vue/src/utils/descriptorCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ export interface SFCParseResult {
}

export const cache = new Map<string, SFCDescriptor>()
export const hmrCache = new Map<string, SFCDescriptor>()
const prevCache = new Map<string, SFCDescriptor | undefined>()

export function createDescriptor(
filename: string,
source: string,
{ root, isProduction, sourceMap, compiler }: ResolvedOptions,
hmr = false,
): SFCParseResult {
const { descriptor, errors } = compiler.parse(source, {
filename,
Expand All @@ -28,37 +30,41 @@ export function createDescriptor(
// project (relative to root) and on different systems.
const normalizedPath = slash(path.normalize(path.relative(root, filename)))
descriptor.id = getHash(normalizedPath + (isProduction ? source : ''))

cache.set(filename, descriptor)
;(hmr ? hmrCache : cache).set(filename, descriptor)
return { descriptor, errors }
}

export function getPrevDescriptor(filename: string): SFCDescriptor | undefined {
return prevCache.get(filename)
}

export function setPrevDescriptor(
filename: string,
entry: SFCDescriptor,
): void {
prevCache.set(filename, entry)
export function invalidateDescriptor(filename: string, hmr = false): void {
const _cache = hmr ? hmrCache : cache
const prev = _cache.get(filename)
_cache.delete(filename)
if (prev) {
prevCache.set(filename, prev)
}
}

export function getDescriptor(
filename: string,
options: ResolvedOptions,
createIfNotFound = true,
hmr = false,
): SFCDescriptor | undefined {
if (cache.has(filename)) {
return cache.get(filename)!
const _cache = hmr ? hmrCache : cache
if (_cache.has(filename)) {
return _cache.get(filename)!
}
if (createIfNotFound) {
const { descriptor, errors } = createDescriptor(
filename,
fs.readFileSync(filename, 'utf-8'),
options,
hmr,
)
if (errors.length) {
if (errors.length && !hmr) {
throw errors[0]
}
return descriptor
Expand Down