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>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface ImportBinding {
 | 
			
		||||
export interface ImportBinding {
 | 
			
		||||
  isType: boolean
 | 
			
		||||
  imported: string
 | 
			
		||||
  source: string
 | 
			
		||||
@ -335,11 +335,7 @@ export function compileScript(
 | 
			
		||||
 | 
			
		||||
    let isUsedInTemplate = true
 | 
			
		||||
    if (isTS && sfc.template && !sfc.template.src && !sfc.template.lang) {
 | 
			
		||||
      isUsedInTemplate = 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))
 | 
			
		||||
      isUsedInTemplate = isImportUsed(local, sfc)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    userImports[local] = {
 | 
			
		||||
@ -1441,6 +1437,7 @@ export function compileScript(
 | 
			
		||||
  return {
 | 
			
		||||
    ...scriptSetup,
 | 
			
		||||
    bindings: bindingMetadata,
 | 
			
		||||
    imports: userImports,
 | 
			
		||||
    content: s.toString(),
 | 
			
		||||
    map: genSourceMap
 | 
			
		||||
      ? (s.generateMap({
 | 
			
		||||
@ -1960,7 +1957,7 @@ function getObjectOrArrayExpressionKeys(value: Node): string[] {
 | 
			
		||||
 | 
			
		||||
const templateUsageCheckCache = createCache<string>()
 | 
			
		||||
 | 
			
		||||
export function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
 | 
			
		||||
function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
 | 
			
		||||
  const { content, ast } = sfc.template!
 | 
			
		||||
  const cached = templateUsageCheckCache.get(content)
 | 
			
		||||
  if (cached) {
 | 
			
		||||
@ -2018,3 +2015,40 @@ function stripTemplateString(str: string): string {
 | 
			
		||||
  }
 | 
			
		||||
  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 { compileTemplate } from './compileTemplate'
 | 
			
		||||
export { compileStyle, compileStyleAsync } from './compileStyle'
 | 
			
		||||
export { compileScript, resolveTemplateUsageCheckString } from './compileScript'
 | 
			
		||||
export { compileScript } from './compileScript'
 | 
			
		||||
export { rewriteDefault } from './rewriteDefault'
 | 
			
		||||
export {
 | 
			
		||||
  shouldTransform as shouldTransformRef,
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ import { RawSourceMap, SourceMapGenerator } from 'source-map'
 | 
			
		||||
import { TemplateCompiler } from './compileTemplate'
 | 
			
		||||
import { parseCssVars } from './cssVars'
 | 
			
		||||
import { createCache } from './cache'
 | 
			
		||||
import { hmrShouldReload, ImportBinding } from './compileScript'
 | 
			
		||||
 | 
			
		||||
export interface SFCParseOptions {
 | 
			
		||||
  filename?: string
 | 
			
		||||
@ -40,6 +41,7 @@ export interface SFCScriptBlock extends SFCBlock {
 | 
			
		||||
  type: 'script'
 | 
			
		||||
  setup?: string | boolean
 | 
			
		||||
  bindings?: BindingMetadata
 | 
			
		||||
  imports?: Record<string, ImportBinding>
 | 
			
		||||
  /**
 | 
			
		||||
   * import('\@babel/types').Statement
 | 
			
		||||
   */
 | 
			
		||||
@ -49,6 +51,7 @@ export interface SFCScriptBlock extends SFCBlock {
 | 
			
		||||
   */
 | 
			
		||||
  scriptSetupAst?: any[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SFCStyleBlock extends SFCBlock {
 | 
			
		||||
  type: 'style'
 | 
			
		||||
  scoped?: boolean
 | 
			
		||||
@ -64,9 +67,21 @@ export interface SFCDescriptor {
 | 
			
		||||
  styles: SFCStyleBlock[]
 | 
			
		||||
  customBlocks: SFCBlock[]
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 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 {
 | 
			
		||||
@ -103,7 +118,8 @@ export function parse(
 | 
			
		||||
    styles: [],
 | 
			
		||||
    customBlocks: [],
 | 
			
		||||
    cssVars: [],
 | 
			
		||||
    slotted: false
 | 
			
		||||
    slotted: false,
 | 
			
		||||
    shouldForceReload: prevImports => hmrShouldReload(prevImports, descriptor)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const errors: (CompilerError | SyntaxError)[] = []
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user