Skip to content

Commit 68c45e7

Browse files
committed
feat(compiler-sfc): expose properties for more accurate HMR
ref vuejs#4358 reverts vuejs#4908
1 parent 90083f5 commit 68c45e7

File tree

3 files changed

+61
-11
lines changed

3 files changed

+61
-11
lines changed

packages/compiler-sfc/src/compileScript.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export interface SFCScriptCompileOptions {
113113
templateOptions?: Partial<SFCTemplateCompileOptions>
114114
}
115115

116-
interface ImportBinding {
116+
export interface ImportBinding {
117117
isType: boolean
118118
imported: string
119119
source: string
@@ -335,11 +335,7 @@ export function compileScript(
335335

336336
let isUsedInTemplate = true
337337
if (isTS && sfc.template && !sfc.template.src && !sfc.template.lang) {
338-
isUsedInTemplate = new RegExp(
339-
// #4274 escape $ since it's a special char in regex
340-
// (and is the only regex special char that is valid in identifiers)
341-
`[^\\w$_]${local.replace(/\$/g, '\\$')}[^\\w$_]`
342-
).test(resolveTemplateUsageCheckString(sfc))
338+
isUsedInTemplate = isImportUsed(local, sfc)
343339
}
344340

345341
userImports[local] = {
@@ -1441,6 +1437,7 @@ export function compileScript(
14411437
return {
14421438
...scriptSetup,
14431439
bindings: bindingMetadata,
1440+
imports: userImports,
14441441
content: s.toString(),
14451442
map: genSourceMap
14461443
? (s.generateMap({
@@ -1960,7 +1957,7 @@ function getObjectOrArrayExpressionKeys(value: Node): string[] {
19601957

19611958
const templateUsageCheckCache = createCache<string>()
19621959

1963-
export function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
1960+
function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
19641961
const { content, ast } = sfc.template!
19651962
const cached = templateUsageCheckCache.get(content)
19661963
if (cached) {
@@ -2018,3 +2015,40 @@ function stripTemplateString(str: string): string {
20182015
}
20192016
return ''
20202017
}
2018+
2019+
function isImportUsed(local: string, sfc: SFCDescriptor): boolean {
2020+
return new RegExp(
2021+
// #4274 escape $ since it's a special char in regex
2022+
// (and is the only regex special char that is valid in identifiers)
2023+
`[^\\w$_]${local.replace(/\$/g, '\\$')}[^\\w$_]`
2024+
).test(resolveTemplateUsageCheckString(sfc))
2025+
}
2026+
2027+
/**
2028+
* Note: this comparison assumes the prev/next script are already identical,
2029+
* and only checks the special case where <script setup lang="ts"> unused import
2030+
* pruning result changes due to template changes.
2031+
*/
2032+
export function hmrShouldReload(
2033+
prevImports: Record<string, ImportBinding>,
2034+
next: SFCDescriptor
2035+
): boolean {
2036+
if (
2037+
!next.scriptSetup ||
2038+
(next.scriptSetup.lang !== 'ts' && next.scriptSetup.lang !== 'tsx')
2039+
) {
2040+
return false
2041+
}
2042+
2043+
// for each previous import, check if its used status remain the same based on
2044+
// the next descriptor's template
2045+
for (const key in prevImports) {
2046+
// if an import was previous unused, but now is used, we need to force
2047+
// reload so that the script now includes that import.
2048+
if (!prevImports[key].isUsedInTemplate && isImportUsed(key, next)) {
2049+
return true
2050+
}
2051+
}
2052+
2053+
return false
2054+
}

packages/compiler-sfc/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
export { parse } from './parse'
33
export { compileTemplate } from './compileTemplate'
44
export { compileStyle, compileStyleAsync } from './compileStyle'
5-
export { compileScript, resolveTemplateUsageCheckString } from './compileScript'
5+
export { compileScript } from './compileScript'
66
export { rewriteDefault } from './rewriteDefault'
77
export {
88
shouldTransform as shouldTransformRef,

packages/compiler-sfc/src/parse.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { RawSourceMap, SourceMapGenerator } from 'source-map'
1111
import { TemplateCompiler } from './compileTemplate'
1212
import { parseCssVars } from './cssVars'
1313
import { createCache } from './cache'
14+
import { hmrShouldReload, ImportBinding } from './compileScript'
1415

1516
export interface SFCParseOptions {
1617
filename?: string
@@ -40,6 +41,7 @@ export interface SFCScriptBlock extends SFCBlock {
4041
type: 'script'
4142
setup?: string | boolean
4243
bindings?: BindingMetadata
44+
imports?: Record<string, ImportBinding>
4345
/**
4446
* import('\@babel/types').Statement
4547
*/
@@ -49,6 +51,7 @@ export interface SFCScriptBlock extends SFCBlock {
4951
*/
5052
scriptSetupAst?: any[]
5153
}
54+
5255
export interface SFCStyleBlock extends SFCBlock {
5356
type: 'style'
5457
scoped?: boolean
@@ -64,9 +67,21 @@ export interface SFCDescriptor {
6467
styles: SFCStyleBlock[]
6568
customBlocks: SFCBlock[]
6669
cssVars: string[]
67-
// whether the SFC uses :slotted() modifier.
68-
// this is used as a compiler optimization hint.
70+
/**
71+
* whether the SFC uses :slotted() modifier.
72+
* this is used as a compiler optimization hint.
73+
*/
6974
slotted: boolean
75+
76+
/**
77+
* compare with an existing descriptor to determine whether HMR should perform
78+
* a reload vs. re-render.
79+
*
80+
* Note: this comparison assumes the prev/next script are already identical,
81+
* and only checks the special case where <script setup lang="ts"> unused import
82+
* pruning result changes due to template changes.
83+
*/
84+
shouldForceReload: (prevImports: Record<string, ImportBinding>) => boolean
7085
}
7186

7287
export interface SFCParseResult {
@@ -103,7 +118,8 @@ export function parse(
103118
styles: [],
104119
customBlocks: [],
105120
cssVars: [],
106-
slotted: false
121+
slotted: false,
122+
shouldForceReload: prevImports => hmrShouldReload(prevImports, descriptor)
107123
}
108124

109125
const errors: (CompilerError | SyntaxError)[] = []

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy