feat(compiler-sfc): expose properties for more accurate HMR
ref #4358 reverts #4908
This commit is contained in:
parent
90083f5718
commit
68c45e73da
@ -113,7 +113,7 @@ export interface SFCScriptCompileOptions {
|
|||||||
templateOptions?: Partial<SFCTemplateCompileOptions>
|
templateOptions?: Partial<SFCTemplateCompileOptions>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportBinding {
|
export interface ImportBinding {
|
||||||
isType: boolean
|
isType: boolean
|
||||||
imported: string
|
imported: string
|
||||||
source: string
|
source: string
|
||||||
@ -335,11 +335,7 @@ export function compileScript(
|
|||||||
|
|
||||||
let isUsedInTemplate = true
|
let isUsedInTemplate = true
|
||||||
if (isTS && sfc.template && !sfc.template.src && !sfc.template.lang) {
|
if (isTS && sfc.template && !sfc.template.src && !sfc.template.lang) {
|
||||||
isUsedInTemplate = new RegExp(
|
isUsedInTemplate = isImportUsed(local, sfc)
|
||||||
// #4274 escape $ since it's a special char in regex
|
|
||||||
// (and is the only regex special char that is valid in identifiers)
|
|
||||||
`[^\\w$_]${local.replace(/\$/g, '\\$')}[^\\w$_]`
|
|
||||||
).test(resolveTemplateUsageCheckString(sfc))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userImports[local] = {
|
userImports[local] = {
|
||||||
@ -1441,6 +1437,7 @@ export function compileScript(
|
|||||||
return {
|
return {
|
||||||
...scriptSetup,
|
...scriptSetup,
|
||||||
bindings: bindingMetadata,
|
bindings: bindingMetadata,
|
||||||
|
imports: userImports,
|
||||||
content: s.toString(),
|
content: s.toString(),
|
||||||
map: genSourceMap
|
map: genSourceMap
|
||||||
? (s.generateMap({
|
? (s.generateMap({
|
||||||
@ -1960,7 +1957,7 @@ function getObjectOrArrayExpressionKeys(value: Node): string[] {
|
|||||||
|
|
||||||
const templateUsageCheckCache = createCache<string>()
|
const templateUsageCheckCache = createCache<string>()
|
||||||
|
|
||||||
export function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
||||||
const { content, ast } = sfc.template!
|
const { content, ast } = sfc.template!
|
||||||
const cached = templateUsageCheckCache.get(content)
|
const cached = templateUsageCheckCache.get(content)
|
||||||
if (cached) {
|
if (cached) {
|
||||||
@ -2018,3 +2015,40 @@ function stripTemplateString(str: string): string {
|
|||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isImportUsed(local: string, sfc: SFCDescriptor): boolean {
|
||||||
|
return new RegExp(
|
||||||
|
// #4274 escape $ since it's a special char in regex
|
||||||
|
// (and is the only regex special char that is valid in identifiers)
|
||||||
|
`[^\\w$_]${local.replace(/\$/g, '\\$')}[^\\w$_]`
|
||||||
|
).test(resolveTemplateUsageCheckString(sfc))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: this comparison assumes the prev/next script are already identical,
|
||||||
|
* and only checks the special case where <script setup lang="ts"> unused import
|
||||||
|
* pruning result changes due to template changes.
|
||||||
|
*/
|
||||||
|
export function hmrShouldReload(
|
||||||
|
prevImports: Record<string, ImportBinding>,
|
||||||
|
next: SFCDescriptor
|
||||||
|
): boolean {
|
||||||
|
if (
|
||||||
|
!next.scriptSetup ||
|
||||||
|
(next.scriptSetup.lang !== 'ts' && next.scriptSetup.lang !== 'tsx')
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each previous import, check if its used status remain the same based on
|
||||||
|
// the next descriptor's template
|
||||||
|
for (const key in prevImports) {
|
||||||
|
// if an import was previous unused, but now is used, we need to force
|
||||||
|
// reload so that the script now includes that import.
|
||||||
|
if (!prevImports[key].isUsedInTemplate && isImportUsed(key, next)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
export { parse } from './parse'
|
export { parse } from './parse'
|
||||||
export { compileTemplate } from './compileTemplate'
|
export { compileTemplate } from './compileTemplate'
|
||||||
export { compileStyle, compileStyleAsync } from './compileStyle'
|
export { compileStyle, compileStyleAsync } from './compileStyle'
|
||||||
export { compileScript, resolveTemplateUsageCheckString } from './compileScript'
|
export { compileScript } from './compileScript'
|
||||||
export { rewriteDefault } from './rewriteDefault'
|
export { rewriteDefault } from './rewriteDefault'
|
||||||
export {
|
export {
|
||||||
shouldTransform as shouldTransformRef,
|
shouldTransform as shouldTransformRef,
|
||||||
|
@ -11,6 +11,7 @@ import { RawSourceMap, SourceMapGenerator } from 'source-map'
|
|||||||
import { TemplateCompiler } from './compileTemplate'
|
import { TemplateCompiler } from './compileTemplate'
|
||||||
import { parseCssVars } from './cssVars'
|
import { parseCssVars } from './cssVars'
|
||||||
import { createCache } from './cache'
|
import { createCache } from './cache'
|
||||||
|
import { hmrShouldReload, ImportBinding } from './compileScript'
|
||||||
|
|
||||||
export interface SFCParseOptions {
|
export interface SFCParseOptions {
|
||||||
filename?: string
|
filename?: string
|
||||||
@ -40,6 +41,7 @@ export interface SFCScriptBlock extends SFCBlock {
|
|||||||
type: 'script'
|
type: 'script'
|
||||||
setup?: string | boolean
|
setup?: string | boolean
|
||||||
bindings?: BindingMetadata
|
bindings?: BindingMetadata
|
||||||
|
imports?: Record<string, ImportBinding>
|
||||||
/**
|
/**
|
||||||
* import('\@babel/types').Statement
|
* import('\@babel/types').Statement
|
||||||
*/
|
*/
|
||||||
@ -49,6 +51,7 @@ export interface SFCScriptBlock extends SFCBlock {
|
|||||||
*/
|
*/
|
||||||
scriptSetupAst?: any[]
|
scriptSetupAst?: any[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SFCStyleBlock extends SFCBlock {
|
export interface SFCStyleBlock extends SFCBlock {
|
||||||
type: 'style'
|
type: 'style'
|
||||||
scoped?: boolean
|
scoped?: boolean
|
||||||
@ -64,9 +67,21 @@ export interface SFCDescriptor {
|
|||||||
styles: SFCStyleBlock[]
|
styles: SFCStyleBlock[]
|
||||||
customBlocks: SFCBlock[]
|
customBlocks: SFCBlock[]
|
||||||
cssVars: string[]
|
cssVars: string[]
|
||||||
// whether the SFC uses :slotted() modifier.
|
/**
|
||||||
// this is used as a compiler optimization hint.
|
* whether the SFC uses :slotted() modifier.
|
||||||
|
* this is used as a compiler optimization hint.
|
||||||
|
*/
|
||||||
slotted: boolean
|
slotted: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare with an existing descriptor to determine whether HMR should perform
|
||||||
|
* a reload vs. re-render.
|
||||||
|
*
|
||||||
|
* Note: this comparison assumes the prev/next script are already identical,
|
||||||
|
* and only checks the special case where <script setup lang="ts"> unused import
|
||||||
|
* pruning result changes due to template changes.
|
||||||
|
*/
|
||||||
|
shouldForceReload: (prevImports: Record<string, ImportBinding>) => boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SFCParseResult {
|
export interface SFCParseResult {
|
||||||
@ -103,7 +118,8 @@ export function parse(
|
|||||||
styles: [],
|
styles: [],
|
||||||
customBlocks: [],
|
customBlocks: [],
|
||||||
cssVars: [],
|
cssVars: [],
|
||||||
slotted: false
|
slotted: false,
|
||||||
|
shouldForceReload: prevImports => hmrShouldReload(prevImports, descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
const errors: (CompilerError | SyntaxError)[] = []
|
const errors: (CompilerError | SyntaxError)[] = []
|
||||||
|
Loading…
Reference in New Issue
Block a user