import MagicString from 'magic-string' ; import { BindingMetadata , BindingTypes , createRoot , locStub , NodeTypes , transform , parserOptions , UNREF , SimpleExpressionNode } from '@vue/compiler-dom' ; import { ScriptSetupTextRanges , SFCDescriptor , SFCScriptBlock , TextRange } from './parse' ; import { parse as _parse , ParserOptions , ParserPlugin } from '@babel/parser' ; import { babelParserDefaultPlugins , camelize , capitalize , generateCodeFrame , makeMap } from '@vue/shared' ; import { Node , Declaration , ObjectPattern , ObjectExpression , ArrayPattern , Identifier , ExportSpecifier , Function as FunctionNode , TSType , TSTypeLiteral , TSFunctionType , ObjectProperty , ArrayExpression , Statement , CallExpression , RestElement , TSInterfaceBody , AwaitExpression , VariableDeclarator , VariableDeclaration } from '@babel/types' ; import { walk } from 'estree-walker' ; import { RawSourceMap } from 'source-map' ; import { CSS_VARS_HELPER , genCssVarsCode , genNormalScriptCssVarsCode } from './cssVars' ; import { compileTemplate , SFCTemplateCompileOptions } from './compileTemplate' ; import { warnExperimental , warnOnce } from './warn' ; import { rewriteDefault } from './rewriteDefault' ; import { createCache } from './cache' ; const DEFINE_PROPS = 'defineProps' ; const DEFINE_EMITS = 'defineEmits' ; const DEFINE_EXPOSE = 'defineExpose' ; const WITH_DEFAULTS = 'withDefaults' ; const $REF = `$ref` ; const $SHALLOW_REF = '$shallowRef' ; const $COMPUTED = `$computed` ; const $FROM_REFS = `$fromRefs` ; const $RAW = `$raw` ; const isBuiltInDir = makeMap ( `once,memo,if,else,else-if,slot,text,html,on,bind,model,show,cloak,is` ) ; export interface SFCScriptCompileOptions { /**
     * Scope ID for prefixing injected CSS varialbes.
     * This must be consistent with the `id` passed to `compileStyle`.
     */ id : string ; /**
     * Production mode. Used to determine whether to generate hashed CSS variables
     */ isProd ? : boolean ; /**
     * https://babeljs.io/docs/en/babel-parser#plugins
     */ babelParserPlugins ? : ParserPlugin [ ] ; default refSugar ? : boolean ; /**
     * Compile the template and inline the resulting render function
     * directly inside setup().
     * - Only affects <script setup>
     * - This should only be used in production because it prevents the template
     * from being hot-reloaded separately from component state.
     */ inlineTemplate ? : boolean ; /**
     * Options for template compilation when inlining. Note these are options that
     * would normally be pased to `compiler-sfc`'s own `compileTemplate()`, not
     * options passed to `compiler-dom`.
     */ templateOptions ? : Partial < SFCTemplateCompileOptions > ; /**
     * Skip codegen and only return AST / binding / text range information.
     * Also makes the call error-tolerant.
     * Used for IDE support.
     */ parseOnly ? : boolean ; } interface ImportBinding { isType : boolean ; imported : string ; source : string ; rangeNode : Node ; isFromSetup : boolean ; isUsedInTemplate : boolean ; } interface VariableBinding { type : BindingTypes ; rangeNode : Node ; } /**
 * Compile `<script setup>`
 * It requires the whole SFC descriptor because we need to handle and merge
 * normal `<script>` + `<script setup>` if both are present.
 */ export function compileScript ( sfc , options ) { let { script , scriptSetup , source , filename } = sfc ; const enableRefSugar = ! ! options . refSugar ; const parseOnly = ! ! options . parseOnly ; if ( parseOnly && ! scriptSetup ) { scriptSetup = { type : 'script' , content : '' , attrs : { } , loc : locStub } ; } if ( ! options ) { options = { id : '' } ; } if ( ! options . id ) { warnOnce ( `compileScript now requires passing the \`id\` option.\n` + `Upgrade your vite or vue-loader version for compatibility with ` + `the latest experimental proposals.` ) ; } const scopeId = options . id ? options . id . replace ( /^data-v-/ , '' ) : '' ; const cssVars = sfc . cssVars ; const scriptLang = script && script . lang ; const scriptSetupLang = scriptSetup && scriptSetup . lang ; const isTS = scriptLang === 'ts' || scriptLang === 'tsx' || scriptSetupLang === 'ts' || scriptSetupLang === 'tsx' ; const plugins = [ ... babelParserDefaultPlugins ] ; if ( ! isTS || scriptLang === 'tsx' || scriptSetupLang === 'tsx' ) { plugins . push ( 'jsx' ) ; } if ( options . babelParserPlugins ) plugins . push ( ... options . babelParserPlugins ) ; if ( isTS ) plugins . push ( 'typescript' , 'decorators-legacy' ) ; if ( ! scriptSetup ) { if ( ! script ) { throw new Error ( `[@vue/compiler-sfc] SFC contains no <script> tags.` ) ; } if ( scriptLang && ! isTS && scriptLang !== 'jsx' ) { return script ; } try { const scriptAst = _parse ( script . content , { plugins , sourceType : 'module' , errorRecovery : parseOnly } ) . program . body ; const bindings = analyzeScriptBindings ( scriptAst ) ; let content = script . content ; if ( cssVars . length ) { content = rewriteDefault ( content , `__default__` , plugins ) ; content += genNormalScriptCssVarsCode ( cssVars , bindings , scopeId , ! ! options . isProd ) ; content += `\nexport default __default__` ; } return { ... script , content , bindings , scriptAst } ; } catch ( e ) { return script ; } } if ( script && scriptLang !== scriptSetupLang ) { throw new Error ( `[@vue/compiler-sfc] <script> and <script setup> must have the same ` + `language type.` ) ; } if ( scriptSetupLang && ! isTS && scriptSetupLang !== 'jsx' ) { return scriptSetup ; } const bindingMetadata = { } ; const ranges = parseOnly ? { scriptBindings : [ ] , scriptSetupBindings : [ ] } : undefined ; const defaultTempVar = `__default__` ; const helperImports = new Set ( ) ; const userImports = Object . create ( null ) ; const userImportAlias = Object . create ( null ) ; const setupBindings = Object . create ( null ) ; const refBindings = Object . create ( null ) ; const refIdentifiers = new Set ( ) ; let defaultExport ; let hasDefinePropsCall = false ; let hasDefineEmitCall = false ; let hasDefineExposeCall = false ; let propsRuntimeDecl ; let propsRuntimeDefaults ; let propsTypeDecl ; let propsTypeDeclRaw ; let propsIdentifier ; let emitsRuntimeDecl ; let emitsTypeDecl ; let emitsTypeDeclRaw ; let emitIdentifier ; let hasAwait = false ; let hasInlinedSsrRenderFn = false ; const typeDeclaredProps = { } ; const typeDeclaredEmits = new Set ( ) ; const declaredTypes = { } ; const s = new MagicString ( source ) ; const startOffset = scriptSetup . loc . start . offset ; const endOffset = scriptSetup . loc . end . offset ; const scriptStartOffset = script && script . loc . start . offset ; const scriptEndOffset = script && script . loc . end . offset ; function helper ( key ) { helperImports . add ( key ) ; return `_${ key }` ; } function parse ( input , options , offset ) { try { options . errorRecovery = parseOnly ; return _parse ( input , options ) . program . body ; } catch ( e ) { e . message = `[@vue/compiler-sfc] ${ e . message }\n\n${ sfc . filename }\n${ generateCodeFrame ( source , e . pos + offset , e . pos + offset + 1 ) }` ; throw e ; } } function error ( msg , node , end = node . end ! + startOffset ) { throw new Error ( `[@vue/compiler-sfc] ${ msg }\n\n${ sfc . filename }\n${ generateCodeFrame ( source , node . start ! + startOffset , end ) }` ) ; } function registerUserImport ( source , local , imported , isType , isFromSetup , rangeNode ) { if ( source === 'vue' && imported ) { userImportAlias [ imported ] = local ; } let isUsedInTemplate = true ; if ( isTS && sfc . template && ! sfc . template . src && ! sfc . template . lang ) { isUsedInTemplate = new RegExp ( `[^\\w$_]${ local . replace ( /\$/g , '\\$' ) }[^\\w$_]` ) . test ( resolveTemplateUsageCheckString ( sfc ) ) ; } userImports [ local ] = { isType , imported : imported || 'default' , source , rangeNode , isFromSetup , isUsedInTemplate } ; } function processDefineProps ( node ) { if ( ! isCallOf ( node , DEFINE_PROPS ) ) { return false ; } if ( hasDefinePropsCall ) { error ( `duplicate ${ DEFINE_PROPS }() call` , node ) ; } hasDefinePropsCall = true ; propsRuntimeDecl = node . arguments [ 0 ] ; if ( node . typeParameters ) { if ( propsRuntimeDecl ) { error ( `${ DEFINE_PROPS }() cannot accept both type and non-type arguments ` + `at the same time. Use one or the other.` , node ) ; } propsTypeDeclRaw = node . typeParameters . params [ 0 ] ; propsTypeDecl = resolveQualifiedType ( propsTypeDeclRaw , node => node . type === 'TSTypeLiteral' ) as TSTypeLiteral | TSInterfaceBody | undefined ; if ( ! propsTypeDecl ) { error ( `type argument passed to ${ DEFINE_PROPS }() must be a literal type, ` + `or a reference to an interface or literal type.` , propsTypeDeclRaw ) ; } } return true ; } function processWithDefaults ( node ) { if ( ! isCallOf ( node , WITH_DEFAULTS ) ) { return false ; } if ( processDefineProps ( node . arguments [ 0 ] ) ) { if ( propsRuntimeDecl ) { error ( `${ WITH_DEFAULTS } can only be used with type-based ` + `${ DEFINE_PROPS } declaration.` , node ) ; } propsRuntimeDefaults = node . arguments [ 1 ] ; } else { error ( `${ WITH_DEFAULTS }' first argument must be a ${ DEFINE_PROPS } call.` , node . arguments [ 0 ] || node ) ; } return true ; } function processDefineEmits ( node ) { if ( ! isCallOf ( node , DEFINE_EMITS ) ) { return false ; } if ( hasDefineEmitCall ) { error ( `duplicate ${ DEFINE_EMITS }() call` , node ) ; } hasDefineEmitCall = true ; emitsRuntimeDecl = node . arguments [ 0 ] ; if ( node . typeParameters ) { if ( emitsRuntimeDecl ) { error ( `${ DEFINE_EMITS }() cannot accept both type and non-type arguments ` + `at the same time. Use one or the other.` , node ) ; } emitsTypeDeclRaw = node . typeParameters . params [ 0 ] ; emitsTypeDecl = resolveQualifiedType ( emitsTypeDeclRaw , node => node . type === 'TSFunctionType' || node . type === 'TSTypeLiteral' ) as TSFunctionType | TSTypeLiteral | TSInterfaceBody | undefined ; if ( ! emitsTypeDecl ) { error ( `type argument passed to ${ DEFINE_EMITS }() must be a function type, ` + `a literal type with call signatures, or a reference to the above types.` , emitsTypeDeclRaw ) ; } } return true ; } function resolveQualifiedType ( node , qualifier ) { if ( qualifier ( node ) ) { return node ; } if ( node . type === 'TSTypeReference' && node . typeName . type === 'Identifier' ) { const refName = node . typeName . name ; const isQualifiedType = ( node ) : Node | undefined => { if ( node . type === 'TSInterfaceDeclaration' && node . id . name === refName ) { return node . body ; } else if ( node . type === 'TSTypeAliasDeclaration' && node . id . name === refName && qualifier ( node . typeAnnotation ) ) { return node . typeAnnotation ; } else if ( node . type === 'ExportNamedDeclaration' && node . declaration ) { return isQualifiedType ( node . declaration ) ; } } ; for ( const node of scriptSetupAst ) { const qualified = isQualifiedType ( node ) ; if ( qualified ) { return qualified ; } } } } function processDefineExpose ( node ) { if ( isCallOf ( node , DEFINE_EXPOSE ) ) { if ( hasDefineExposeCall ) { error ( `duplicate ${ DEFINE_EXPOSE }() call` , node ) ; } hasDefineExposeCall = true ; return true ; } return false ; } function checkInvalidScopeReference ( node , method ) { if ( ! node ) return ; walkIdentifiers ( node , id => { if ( setupBindings [ id . name ] ) { error ( `\`${ method }()\` in <script setup> cannot reference locally ` + `declared variables because it will be hoisted outside of the ` + `setup() function. If your component options requires initialization ` + `in the module scope, use a separate normal <script> to export ` + `the options instead.` , id ) ; } } ) ; } /**
     * await foo()
     * -->
     * (([__temp, __restore] = withAsyncContext(() => foo())),__temp=await __temp,__restore(),__temp)
     */ function processAwait ( node , isStatement ) { s . overwrite ( node . start ! + startOffset , node . argument . start ! + startOffset , `${ isStatement ? `;` : `` }(([__temp,__restore]=${ helper ( `withAsyncContext` ) }(()=>(` ) ; s . appendLeft ( node . end ! + startOffset , `))),__temp=await __temp,__restore()${ isStatement ? `` : `,__temp` })` ) ; } function isRefSugarCall ( callee ) { return ( callee === $REF || callee === $COMPUTED || callee === $FROM_REFS || callee === $SHALLOW_REF ) ; } function processRefSugar ( decl , statement ) { if ( ! isCallOf ( decl . init , isRefSugarCall ) ) { return ; } if ( ! enableRefSugar ) { error ( `ref sugar is an experimental proposal and must be explicitly ` + `enabled via @vue/compiler-sfc options.` , decl . init ) ; } else { warnExperimental ( `ref sugar` , `https://github.com/vuejs/rfcs/discussions/369` ) ; } const callee = ( decl . init . callee as Identifier ) . name ; const start = decl . init . start ! + startOffset ; if ( callee === $REF || callee === $SHALLOW_REF ) { if ( statement . kind !== 'let' ) { error ( `${ callee }() bindings can only be declared with let.` , decl ) ; } if ( decl . id . type !== 'Identifier' ) { error ( `${ callee }() bindings cannot be used with destructuring. ` + `If you are trying to destructure from an object of refs, ` + `use \`let { x } = $fromRefs(obj)\`.` , decl . id ) ; } registerRefBinding ( decl . id ) ; s . overwrite ( start , start + callee . length , helper ( callee === $REF ? 'ref' : 'shallowRef' ) ) ; } else if ( callee === $COMPUTED ) { if ( decl . id . type !== 'Identifier' ) { error ( `${ callee }() bindings cannot be used with destructuring.` , decl . id ) ; } registerRefBinding ( decl . id ) ; s . overwrite ( start , start + $COMPUTED . length , helper ( 'computed' ) ) ; } else if ( callee === $FROM_REFS ) { if ( ! decl . id . type . endsWith ( 'Pattern' ) ) { error ( `${ callee }() declaration must be used with destructure patterns.` , decl ) ; } if ( decl . id . type === 'ObjectPattern' ) { processRefObjectPattern ( decl . id , statement ) ; } else if ( decl . id . type === 'ArrayPattern' ) { processRefArrayPattern ( decl . id , statement ) ; } s . remove ( start , start + callee . length ) ; } } function registerRefBinding ( id ) { if ( id . name [ 0 ] === '$' ) { error ( `ref variable identifiers cannot start with $.` , id ) ; } refBindings [ id . name ] = setupBindings [ id . name ] = { type : BindingTypes . SETUP_REF , rangeNode : id } ; refIdentifiers . add ( id ) ; } function processRefObjectPattern ( pattern , statement ) { for ( const p of pattern . properties ) { let nameId ; if ( p . type === 'ObjectProperty' ) { if ( p . key . start ! === p . value . start ! ) { nameId = p . key as Identifier ; s . appendLeft ( nameId . end ! + startOffset , `: __${ nameId . name }` ) ; if ( p . value . type === 'AssignmentPattern' ) { refIdentifiers . add ( p . value . left as Identifier ) ; } } else { if ( p . value . type === 'Identifier' ) { nameId = p . value ; s . prependRight ( nameId . start ! + startOffset , `__` ) ; } else if ( p . value . type === 'ObjectPattern' ) { processRefObjectPattern ( p . value , statement ) ; } else if ( p . value . type === 'ArrayPattern' ) { processRefArrayPattern ( p . value , statement ) ; } else if ( p . value . type === 'AssignmentPattern' ) { nameId = p . value . left as Identifier ; s . prependRight ( nameId . start ! + startOffset , `__` ) ; } } } else { nameId = p . argument as Identifier ; s . prependRight ( nameId . start ! + startOffset , `__` ) ; } if ( nameId ) { registerRefBinding ( nameId ) ; s . appendLeft ( statement . end ! + startOffset , `\nconst ${ nameId . name } = ${ helper ( 'shallowRef' ) }(__${ nameId . name });` ) ; } } } function processRefArrayPattern ( pattern , statement ) { for ( const e of pattern . elements ) { if ( ! e ) continue ; let nameId ; if ( e . type === 'Identifier' ) { nameId = e ; } else if ( e . type === 'AssignmentPattern' ) { nameId = e . left as Identifier ; } else if ( e . type === 'RestElement' ) { nameId = e . argument as Identifier ; } else if ( e . type === 'ObjectPattern' ) { processRefObjectPattern ( e , statement ) ; } else if ( e . type === 'ArrayPattern' ) { processRefArrayPattern ( e , statement ) ; } if ( nameId ) { registerRefBinding ( nameId ) ; s . prependRight ( nameId . start ! + startOffset , `__` ) ; s . appendLeft ( statement . end ! + startOffset , `\nconst ${ nameId . name } = ${ helper ( 'shallowRef' ) }(__${ nameId . name });` ) ; } } } function genRuntimeProps ( props ) { const keys = Object . keys ( props ) ; if ( ! keys . length ) { return `` ; } const hasStaticDefaults = propsRuntimeDefaults && propsRuntimeDefaults . type === 'ObjectExpression' && propsRuntimeDefaults . properties . every ( node => node . type === 'ObjectProperty' && ! node . computed ) ; let propsDecls = `{
    ${ keys . map ( key => { let defaultString ; if ( hasStaticDefaults ) { const prop = ( propsRuntimeDefaults as ObjectExpression ) . properties . find ( ( node ) => node . key . name === key ) as ObjectProperty ; if ( prop ) { defaultString = `default: ${ source . slice ( prop . value . start ! + startOffset , prop . value . end ! + startOffset ) }` ; } } if ( __DEV__ ) { const { type , required } = props [ key ] ; return `${ key }: { type: ${ toRuntimeTypeString ( type ) }, required: ${ required }${ defaultString ? `, ${ defaultString }` : `` } }` ; } else { return `${ key }: ${ defaultString ? `{ ${ defaultString } }` : 'null' }` ; } } ) . join ( ',\n    ' ) }\n  }` ; if ( propsRuntimeDefaults && ! hasStaticDefaults ) { propsDecls = `${ helper ( 'mergeDefaults' ) }(${ propsDecls }, ${ source . slice ( propsRuntimeDefaults . start ! + startOffset , propsRuntimeDefaults . end ! + startOffset ) })` ; } return `\n  props: ${ propsDecls } as unknown as undefined,` ; } let scriptAst ; if ( script ) { scriptAst = parse ( script . content , { plugins , sourceType : 'module' } , scriptStartOffset ! ) ; for ( const node of scriptAst ) { if ( node . type === 'ImportDeclaration' ) { for ( const specifier of node . specifiers ) { const imported = specifier . type === 'ImportSpecifier' && specifier . imported . type === 'Identifier' && specifier . imported . name ; registerUserImport ( node . source . value , specifier . local . name , imported , node . importKind === 'type' , false , specifier . local ) ; } } else if ( node . type === 'ExportDefaultDeclaration' ) { defaultExport = node ; const start = node . start ! + scriptStartOffset ! ; s . overwrite ( start , start + `export default` . length , `const ${ defaultTempVar } =` ) ; } else if ( node . type === 'ExportNamedDeclaration' && node . specifiers ) { const defaultSpecifier = node . specifiers . find ( s => s . exported . type === 'Identifier' && s . exported . name === 'default' ) as ExportSpecifier ; if ( defaultSpecifier ) { defaultExport = node ; if ( node . specifiers . length > 1 ) { s . remove ( defaultSpecifier . start ! + scriptStartOffset ! , defaultSpecifier . end ! + scriptStartOffset ! ) ; } else { s . remove ( node . start ! + scriptStartOffset ! , node . end ! + scriptStartOffset ! ) ; } if ( node . source ) { s . prepend ( `import { ${ defaultSpecifier . local . name } as ${ defaultTempVar } } from '${ node . source . value }'\n` ) ; } else { s . append ( `\nconst ${ defaultTempVar } = ${ defaultSpecifier . local . name }\n` ) ; } } } else if ( ( node . type === 'VariableDeclaration' || node . type === 'FunctionDeclaration' || node . type === 'ClassDeclaration' ) && ! node . declare ) { walkDeclaration ( node , setupBindings , userImportAlias ) ; } } } const scriptSetupAst = parse ( scriptSetup . content , { plugins : [ ... plugins , 'topLevelAwait' ] , sourceType : 'module' } , startOffset ) ; for ( const node of scriptSetupAst ) { const start = node . start ! + startOffset ; let end = node . end ! + startOffset ; if ( node . trailingComments && node . trailingComments . length > 0 ) { const lastCommentNode = node . trailingComments [ node . trailingComments . length - 1 ] ; end = lastCommentNode . end + startOffset ; } while ( end <= source . length ) { if ( ! /\s/ . test ( source . charAt ( end ) ) ) { break ; } end ++ ; } if ( node . type === 'LabeledStatement' && node . label . name === 'ref' && node . body . type === 'ExpressionStatement' ) { error ( `ref sugar using the label syntax was an experimental proposal and ` + `has been dropped based on community feedback.` , node ) ; } if ( node . type === 'ImportDeclaration' ) { s . move ( start , end , 0 ) ; let removed = 0 ; const removeSpecifier = ( i ) => { const removeLeft = i > removed ; removed ++ ; const current = node . specifiers [ i ] ; const next = node . specifiers [ i + 1 ] ; s . remove ( removeLeft ? node . specifiers [ i - 1 ] . end ! + startOffset : current . start ! + startOffset , next && ! removeLeft ? next . start ! + startOffset : current . end ! + startOffset ) ; } ; for ( let i = 0 ; i < node . specifiers . length ; i ++ ) { const specifier = node . specifiers [ i ] ; const local = specifier . local . name ; const imported = specifier . type === 'ImportSpecifier' && specifier . imported . type === 'Identifier' && specifier . imported . name ; const source = node . source . value ; const existing = userImports [ local ] ; if ( source === 'vue' && ( imported === DEFINE_PROPS || imported === DEFINE_EMITS || imported === DEFINE_EXPOSE ) ) { warnOnce ( `\`${ imported }\` is a compiler macro and no longer needs to be imported.` ) ; removeSpecifier ( i ) ; } else if ( existing ) { if ( existing . source === source && existing . imported === imported ) { removeSpecifier ( i ) ; } else { error ( `different imports aliased to same local name.` , specifier ) ; } } else { registerUserImport ( source , local , imported , node . importKind === 'type' , true , specifier . local ) ; } } if ( node . specifiers . length && removed === node . specifiers . length ) { s . remove ( node . start ! + startOffset , node . end ! + startOffset ) ; } } if ( node . type === 'ExpressionStatement' ) { if ( processDefineProps ( node . expression ) || processDefineEmits ( node . expression ) || processWithDefaults ( node . expression ) ) { s . remove ( node . start ! + startOffset , node . end ! + startOffset ) ; } else if ( processDefineExpose ( node . expression ) ) { const callee = ( node . expression as CallExpression ) . callee ; s . overwrite ( callee . start ! + startOffset , callee . end ! + startOffset , 'expose' ) ; } } if ( node . type === 'VariableDeclaration' && ! node . declare ) { const total = node . declarations . length ; let left = total ; for ( let i = 0 ; i < total ; i ++ ) { const decl = node . declarations [ i ] ; if ( decl . init ) { const isDefineProps = processDefineProps ( decl . init ) || processWithDefaults ( decl . init ) ; if ( isDefineProps ) { propsIdentifier = scriptSetup . content . slice ( decl . id . start ! , decl . id . end ! ) ; } const isDefineEmits = processDefineEmits ( decl . init ) ; if ( isDefineEmits ) { emitIdentifier = scriptSetup . content . slice ( decl . id . start ! , decl . id . end ! ) ; } if ( isDefineProps || isDefineEmits ) { if ( left === 1 ) { s . remove ( node . start ! + startOffset , node . end ! + startOffset ) ; } else { let start = decl . start ! + startOffset ; let end = decl . end ! + startOffset ; if ( i < total - 1 ) { end = node . declarations [ i + 1 ] . start ! + startOffset ; } else { start = node . declarations [ i - 1 ] . end ! + startOffset ; } s . remove ( start , end ) ; left -- ; } } else { processRefSugar ( decl , node ) ; } } } } if ( ( node . type === 'VariableDeclaration' || node . type === 'FunctionDeclaration' || node . type === 'ClassDeclaration' ) && ! node . declare ) { walkDeclaration ( node , setupBindings , userImportAlias ) ; } if ( ( node . type === 'VariableDeclaration' && ! node . declare ) || node . type . endsWith ( 'Statement' ) ) { ; ( walk as any ) ( node , { enter ( child , parent ) { if ( isFunction ( child ) ) { this . skip ( ) ; } if ( child . type === 'AwaitExpression' ) { hasAwait = true ; processAwait ( child , parent . type === 'ExpressionStatement' ) ; } } } ) ; } if ( ( node . type === 'ExportNamedDeclaration' && node . exportKind !== 'type' ) || node . type === 'ExportAllDeclaration' || node . type === 'ExportDefaultDeclaration' ) { error ( `<script setup> cannot contain ES module exports. ` + `If you are using a previous version of <script setup>, please ` + `consult the updated RFC at https://github.com/vuejs/rfcs/pull/227.` , node ) ; } if ( isTS ) { if ( node . type === 'TSEnumDeclaration' ) { registerBinding ( setupBindings , node . id , BindingTypes . SETUP_CONST ) ; } if ( node . type . startsWith ( 'TS' ) || ( node . type === 'ExportNamedDeclaration' && node . exportKind === 'type' ) || ( node . type === 'VariableDeclaration' && node . declare ) ) { recordType ( node , declaredTypes ) ; s . move ( start , end , 0 ) ; } } } if ( parseOnly ) { for ( const key in userImports ) { const { rangeNode , isFromSetup } = userImports [ key ] ; const bindings = isFromSetup ? ranges ! . scriptSetupBindings : ranges ! . scriptBindings ; bindings . push ( toTextRange ( rangeNode ) ) ; } for ( const key in setupBindings ) { ranges ! . scriptSetupBindings . push ( toTextRange ( setupBindings [ key ] . rangeNode ) ) ; } if ( propsRuntimeDecl ) { ranges ! . propsRuntimeArg = toTextRange ( propsRuntimeDecl ) ; } if ( propsTypeDeclRaw ) { ranges ! . propsTypeArg = toTextRange ( propsTypeDeclRaw ) ; } if ( emitsRuntimeDecl ) { ranges ! . emitsRuntimeArg = toTextRange ( emitsRuntimeDecl ) ; } if ( emitsTypeDeclRaw ) { ranges ! . emitsTypeArg = toTextRange ( emitsTypeDeclRaw ) ; } if ( propsRuntimeDefaults ) { ranges ! . withDefaultsArg = toTextRange ( propsRuntimeDefaults ) ; } return { ... scriptSetup , ranges , scriptAst , scriptSetupAst } ; } if ( enableRefSugar ) { const onIdent = ( id , parent , parentStack ) => { if ( refBindings [ id . name ] && ! refIdentifiers . has ( id ) ) { if ( isStaticProperty ( parent ) && parent . shorthand ) { if ( ! ( parent as any ) . inPattern || isInDestructureAssignment ( parent , parentStack ) ) { s . appendLeft ( id . end ! + startOffset , `: ${ id . name }.value` ) ; } } else { s . appendLeft ( id . end ! + startOffset , '.value' ) ; } } } ; const onNode = ( node , parent ) => { if ( isCallOf ( node , $RAW ) ) { s . remove ( node . callee . start ! + startOffset , node . callee . end ! + startOffset ) ; return false ; } else if ( parent && isCallOf ( node , isRefSugarCall ) && ( parent . type !== 'VariableDeclarator' || node !== parent . init ) ) { error ( `${ node . callee . name } can only be used directly as a variable initializer.` , node ) ; } } ; for ( const node of scriptSetupAst ) { if ( node . type !== 'ImportDeclaration' ) { walkIdentifiers ( node , onIdent , onNode ) ; } } } if ( propsTypeDecl ) { extractRuntimeProps ( propsTypeDecl , typeDeclaredProps , declaredTypes ) ; } if ( emitsTypeDecl ) { extractRuntimeEmits ( emitsTypeDecl , typeDeclaredEmits ) ; } checkInvalidScopeReference ( propsRuntimeDecl , DEFINE_PROPS ) ; checkInvalidScopeReference ( propsRuntimeDefaults , DEFINE_PROPS ) ; checkInvalidScopeReference ( emitsRuntimeDecl , DEFINE_PROPS ) ; if ( script ) { if ( startOffset < scriptStartOffset ! ) { s . remove ( 0 , startOffset ) ; s . remove ( endOffset , scriptStartOffset ! ) ; s . remove ( scriptEndOffset ! , source . length ) ; } else { s . remove ( 0 , scriptStartOffset ! ) ; s . remove ( scriptEndOffset ! , startOffset ) ; s . remove ( endOffset , source . length ) ; } } else { s . remove ( 0 , startOffset ) ; s . remove ( endOffset , source . length ) ; } if ( scriptAst ) { Object . assign ( bindingMetadata , analyzeScriptBindings ( scriptAst ) ) ; } if ( propsRuntimeDecl ) { for ( const key of getObjectOrArrayExpressionKeys ( propsRuntimeDecl ) ) { bindingMetadata [ key ] = BindingTypes . PROPS ; } } for ( const key in typeDeclaredProps ) { bindingMetadata [ key ] = BindingTypes . PROPS ; } for ( const [ key , { isType , imported , source } ] of Object . entries ( userImports ) ) { if ( isType ) continue ; bindingMetadata [ key ] = ( imported === 'default' && source . endsWith ( '.vue' ) ) || source === 'vue' ? BindingTypes . SETUP_CONST : BindingTypes . SETUP_MAYBE_REF ; } for ( const key in setupBindings ) { bindingMetadata [ key ] = setupBindings [ key ] . type ; } for ( const key in refBindings ) { bindingMetadata [ key ] = BindingTypes . SETUP_REF ; } if ( cssVars . length ) { helperImports . add ( CSS_VARS_HELPER ) ; helperImports . add ( 'unref' ) ; s . prependRight ( startOffset , `\n${ genCssVarsCode ( cssVars , bindingMetadata , scopeId , ! ! options . isProd ) }\n` ) ; } let args = `__props` ; if ( propsTypeDecl ) { args += `: ${ scriptSetup . content . slice ( propsTypeDecl . start ! , propsTypeDecl . end ! ) }` ; } if ( propsIdentifier ) { s . prependRight ( startOffset , `\nconst ${ propsIdentifier } = __props` ) ; } if ( hasAwait ) { const any = isTS ? `:any` : `` ; s . prependRight ( startOffset , `\nlet __temp${ any }, __restore${ any }\n` ) ; } const destructureElements = hasDefineExposeCall || ! options . inlineTemplate ? [ `expose` ] : [ ] ; if ( emitIdentifier ) { destructureElements . push ( emitIdentifier === `emit` ? `emit` : `emit: ${ emitIdentifier }` ) ; } if ( destructureElements . length ) { args += `, { ${ destructureElements . join ( ', ' ) } }` ; if ( emitsTypeDecl ) { args += `: { emit: (${ scriptSetup . content . slice ( emitsTypeDecl . start ! , emitsTypeDecl . end ! ) }), expose: any, slots: any, attrs: any }` ; } } let returned ; if ( options . inlineTemplate ) { if ( sfc . template && ! sfc . template . src ) { if ( options . templateOptions && options . templateOptions . ssr ) { hasInlinedSsrRenderFn = true ; } const { code , ast , preamble , tips , errors } = compileTemplate ( { filename , source : sfc . template . content , inMap : sfc . template . map , ... options . templateOptions , id : scopeId , scoped : sfc . styles . some ( s => s . scoped ) , isProd : options . isProd , ssrCssVars : sfc . cssVars , compilerOptions : { ... ( options . templateOptions && options . templateOptions . compilerOptions ) , inline : true , isTS , bindingMetadata } } ) ; if ( tips . length ) { tips . forEach ( warnOnce ) ; } const err = errors [ 0 ] ; if ( typeof err === 'string' ) { throw new Error ( err ) ; } else if ( err ) { if ( err . loc ) { err . message += `\n\n` + sfc . filename + '\n' + generateCodeFrame ( source , err . loc . start . offset , err . loc . end . offset ) + `\n` ; } throw err ; } if ( preamble ) { s . prepend ( preamble ) ; } if ( ast && ast . helpers . includes ( UNREF ) ) { helperImports . delete ( 'unref' ) ; } returned = code ; } else { returned = `() => {}` ; } } else { const allBindings = { ... setupBindings } ; for ( const key in userImports ) { if ( ! userImports [ key ] . isType && userImports [ key ] . isUsedInTemplate ) { allBindings [ key ] = true ; } } returned = `{ ${ Object . keys ( allBindings ) . join ( ', ' ) } }` ; } if ( ! options . inlineTemplate && ! __TEST__ ) { s . appendRight ( endOffset , `\nconst __returned__ = ${ returned }\n` + `Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })\n` + `return __returned__` + `\n}\n\n` ) ; } else { s . appendRight ( endOffset , `\nreturn ${ returned }\n}\n\n` ) ; } let runtimeOptions = `` ; if ( hasInlinedSsrRenderFn ) { runtimeOptions += `\n  __ssrInlineRender: true,` ; } if ( propsRuntimeDecl ) { runtimeOptions += `\n  props: ${ scriptSetup . content . slice ( propsRuntimeDecl . start ! , propsRuntimeDecl . end ! ) . trim ( ) },` ; } else if ( propsTypeDecl ) { runtimeOptions += genRuntimeProps ( typeDeclaredProps ) ; } if ( emitsRuntimeDecl ) { runtimeOptions += `\n  emits: ${ scriptSetup . content . slice ( emitsRuntimeDecl . start ! , emitsRuntimeDecl . end ! ) . trim ( ) },` ; } else if ( emitsTypeDecl ) { runtimeOptions += genRuntimeEmits ( typeDeclaredEmits ) ; } const exposeCall = hasDefineExposeCall || options . inlineTemplate ? `` : `  expose()\n` ; if ( isTS ) { const def = defaultExport ? `\n  ...${ defaultTempVar },` : `` ; s . prependLeft ( startOffset , `\nexport default ${ helper ( `defineComponent` ) }({${ def }${ runtimeOptions }\n  ${ hasAwait ? `async ` : `` }setup(${ args }) {\n${ exposeCall }` ) ; s . appendRight ( endOffset , `})` ) ; } else { if ( defaultExport ) { s . prependLeft ( startOffset , `\n${ hasAwait ? `async ` : `` }function setup(${ args }) {\n` ) ; s . append ( `\nexport default /*#__PURE__*/ Object.assign(${ defaultTempVar }, {${ runtimeOptions }\n  setup\n})\n` ) ; } else { s . prependLeft ( startOffset , `\nexport default {${ runtimeOptions }\n  ` + `${ hasAwait ? `async ` : `` }setup(${ args }) {\n${ exposeCall }` ) ; s . appendRight ( endOffset , `}` ) ; } } if ( helperImports . size > 0 ) { s . prepend ( `import { ${ [ ... helperImports ] . map ( h => `${ h } as _${ h }` ) . join ( ', ' ) } } from 'vue'\n` ) ; } s . trim ( ) ; return { ... scriptSetup , bindings : bindingMetadata , content : s . toString ( ) , map : s . generateMap ( { source : filename , hires : true , includeContent : true } ) as unknown as RawSourceMap , scriptAst , scriptSetupAst } ; } function registerBinding ( bindings , node , type ) { bindings [ node . name ] = { type , rangeNode : node } ; } function walkDeclaration ( node , bindings , userImportAlias ) { if ( node . type === 'VariableDeclaration' ) { const isConst = node . kind === 'const' ; for ( const { id , init } of node . declarations ) { const isDefineCall = ! ! ( isConst && isCallOf ( init , c => c === DEFINE_PROPS || c === DEFINE_EMITS || c === WITH_DEFAULTS ) ) ; if ( id . type === 'Identifier' ) { let bindingType ; const userReactiveBinding = userImportAlias [ 'reactive' ] || 'reactive' ; if ( isCallOf ( init , userReactiveBinding ) ) { bindingType = BindingTypes . SETUP_LET ; } else if ( isDefineCall || ( isConst && canNeverBeRef ( init ! , userReactiveBinding ) ) ) { bindingType = BindingTypes . SETUP_CONST ; } else if ( isConst ) { if ( isCallOf ( init , userImportAlias [ 'ref' ] || 'ref' ) ) { bindingType = BindingTypes . SETUP_REF ; } else { bindingType = BindingTypes . SETUP_MAYBE_REF ; } } else { bindingType = BindingTypes . SETUP_LET ; } registerBinding ( bindings , id , bindingType ) ; } else if ( id . type === 'ObjectPattern' ) { walkObjectPattern ( id , bindings , isConst , isDefineCall ) ; } else if ( id . type === 'ArrayPattern' ) { walkArrayPattern ( id , bindings , isConst , isDefineCall ) ; } } } else if ( node . type === 'FunctionDeclaration' || node . type === 'ClassDeclaration' ) { bindings [ node . id ! . name ] = { type : BindingTypes . SETUP_CONST , rangeNode : node . id ! } ; } } function walkObjectPattern ( node , bindings , isConst , isDefineCall = false ) { for ( const p of node . properties ) { if ( p . type === 'ObjectProperty' ) { if ( p . key . type === 'Identifier' ) { if ( p . key === p . value ) { const type = isDefineCall ? BindingTypes . SETUP_CONST : isConst ? BindingTypes . SETUP_MAYBE_REF : BindingTypes . SETUP_LET ; registerBinding ( bindings , p . key , type ) ; } else { walkPattern ( p . value , bindings , isConst , isDefineCall ) ; } } } else { const type = isConst ? BindingTypes . SETUP_CONST : BindingTypes . SETUP_LET ; registerBinding ( bindings , p . argument as Identifier , type ) ; } } } function walkArrayPattern ( node , bindings , isConst , isDefineCall = false ) { for ( const e of node . elements ) { e && walkPattern ( e , bindings , isConst , isDefineCall ) ; } } function walkPattern ( node , bindings , isConst , isDefineCall = false ) { if ( node . type === 'Identifier' ) { const type = isDefineCall ? BindingTypes . SETUP_CONST : isConst ? BindingTypes . SETUP_MAYBE_REF : BindingTypes . SETUP_LET ; registerBinding ( bindings , node , type ) ; } else if ( node . type === 'RestElement' ) { const type = isConst ? BindingTypes . SETUP_CONST : BindingTypes . SETUP_LET ; registerBinding ( bindings , node . argument as Identifier , type ) ; } else if ( node . type === 'ObjectPattern' ) { walkObjectPattern ( node , bindings , isConst ) ; } else if ( node . type === 'ArrayPattern' ) { walkArrayPattern ( node , bindings , isConst ) ; } else if ( node . type === 'AssignmentPattern' ) { if ( node . left . type === 'Identifier' ) { const type = isDefineCall ? BindingTypes . SETUP_CONST : isConst ? BindingTypes . SETUP_MAYBE_REF : BindingTypes . SETUP_LET ; registerBinding ( bindings , node . left , type ) ; } else { walkPattern ( node . left , bindings , isConst ) ; } } } interface PropTypeData { key : string ; type : string [ ] ; required : boolean ; } function recordType ( node , declaredTypes ) { if ( node . type === 'TSInterfaceDeclaration' ) { declaredTypes [ node . id . name ] = [ `Object` ] ; } else if ( node . type === 'TSTypeAliasDeclaration' ) { declaredTypes [ node . id . name ] = inferRuntimeType ( node . typeAnnotation , declaredTypes ) ; } else if ( node . type === 'ExportNamedDeclaration' && node . declaration ) { recordType ( node . declaration , declaredTypes ) ; } } function extractRuntimeProps ( node , props , declaredTypes ) { const members = node . type === 'TSTypeLiteral' ? node . members : node . body ; for ( const m of members ) { if ( ( m . type === 'TSPropertySignature' || m . type === 'TSMethodSignature' ) && m . key . type === 'Identifier' ) { let type ; if ( __DEV__ ) { if ( m . type === 'TSMethodSignature' ) { type = [ 'Function' ] ; } else if ( m . typeAnnotation ) { type = inferRuntimeType ( m . typeAnnotation . typeAnnotation , declaredTypes ) ; } } props [ m . key . name ] = { key : m . key . name , required : ! m . optional , type : type || [ `null` ] } ; } } } function inferRuntimeType ( node , declaredTypes ) { switch ( node . type ) { case 'TSStringKeyword' : return [ 'String' ] ; case 'TSNumberKeyword' : return [ 'Number' ] ; case 'TSBooleanKeyword' : return [ 'Boolean' ] ; case 'TSObjectKeyword' : return [ 'Object' ] ; case 'TSTypeLiteral' : return [ 'Object' ] ; case 'TSFunctionType' : return [ 'Function' ] ; case 'TSArrayType' : case 'TSTupleType' : return [ 'Array' ] ; case 'TSLiteralType' : switch ( node . literal . type ) { case 'StringLiteral' : return [ 'String' ] ; case 'BooleanLiteral' : return [ 'Boolean' ] ; case 'NumericLiteral' : case 'BigIntLiteral' : return [ 'Number' ] ; default : return [ `null` ] ; } case 'TSTypeReference' : if ( node . typeName . type === 'Identifier' ) { if ( declaredTypes [ node . typeName . name ] ) { return declaredTypes [ node . typeName . name ] ; } switch ( node . typeName . name ) { case 'Array' : case 'Function' : case 'Object' : case 'Set' : case 'Map' : case 'WeakSet' : case 'WeakMap' : return [ node . typeName . name ] ; case 'Record' : case 'Partial' : case 'Readonly' : case 'Pick' : case 'Omit' : case 'Exclude' : case 'Extract' : case 'Required' : case 'InstanceType' : return [ 'Object' ] ; } } return [ `null` ] ; case 'TSParenthesizedType' : return inferRuntimeType ( node . typeAnnotation , declaredTypes ) ; case 'TSUnionType' : return [ ... new Set ( [ ] . concat ( ... ( node . types . map ( t => inferRuntimeType ( t , declaredTypes ) ) as any ) ) ) ] ; case 'TSIntersectionType' : return [ 'Object' ] ; default : return [ `null` ] ; } } function toRuntimeTypeString ( types ) { return types . length > 1 ? `[${ types . join ( ', ' ) }]` : types [ 0 ] ; } function extractRuntimeEmits ( node , emits ) { if ( node . type === 'TSTypeLiteral' || node . type === 'TSInterfaceBody' ) { const members = node . type === 'TSTypeLiteral' ? node . members : node . body ; for ( let t of members ) { if ( t . type === 'TSCallSignatureDeclaration' ) { extractEventNames ( t . parameters [ 0 ] , emits ) ; } } return ; } else { extractEventNames ( node . parameters [ 0 ] , emits ) ; } } function extractEventNames ( eventName , emits ) { if ( eventName . type === 'Identifier' && eventName . typeAnnotation && eventName . typeAnnotation . type === 'TSTypeAnnotation' ) { const typeNode = eventName . typeAnnotation . typeAnnotation ; if ( typeNode . type === 'TSLiteralType' ) { emits . add ( String ( typeNode . literal . value ) ) ; } else if ( typeNode . type === 'TSUnionType' ) { for ( const t of typeNode . types ) { if ( t . type === 'TSLiteralType' ) { emits . add ( String ( t . literal . value ) ) ; } } } } } function genRuntimeEmits ( emits ) { return emits . size ? `\n  emits: [${ Array . from ( emits ) . map ( p => JSON . stringify ( p ) ) . join ( ', ' ) }] as unknown as undefined,` : `` ; } function markScopeIdentifier ( node , child , knownIds ) { const { name } = child ; if ( node . scopeIds && node . scopeIds . has ( name ) ) { return ; } if ( name in knownIds ) { knownIds [ name ] ++ ; } else { knownIds [ name ] = 1 ; } ; ( node . scopeIds || ( node . scopeIds = new Set ( ) ) ) . add ( name ) ; } /**
 * Walk an AST and find identifiers that are variable references.
 * This is largely the same logic with `transformExpressions` in compiler-core
 * but with some subtle differences as this needs to handle a wider range of
 * possible syntax.
 */ export function walkIdentifiers ( root , onIdentifier , onNode ? ) { const parentStack = [ ] ; const knownIds = Object . create ( null ) ; ( walk as any ) ( root , { enter ( node , parent ) { parent && parentStack . push ( parent ) ; if ( parent && parent . type . startsWith ( 'TS' ) && parent . type !== 'TSAsExpression' && parent . type !== 'TSNonNullExpression' && parent . type !== 'TSTypeAssertion' ) { return this . skip ( ) ; } if ( onNode && onNode ( node , parent ! , parentStack ) === false ) { return this . skip ( ) ; } if ( node . type === 'Identifier' ) { if ( ! knownIds [ node . name ] && isRefIdentifier ( node , parent ! , parentStack ) ) { onIdentifier ( node , parent ! , parentStack ) ; } } else if ( isFunction ( node ) ) { if ( node . body . type === 'BlockStatement' ) { node . body . body . forEach ( p => { if ( p . type === 'VariableDeclaration' ) { for ( const decl of p . declarations ) { extractIdentifiers ( decl . id ) . forEach ( id => { markScopeIdentifier ( node , id , knownIds ) ; } ) ; } } } ) ; } node . params . forEach ( p => ( walk as any ) ( p , { enter ( child , parent ) { if ( child . type === 'Identifier' && ! isStaticPropertyKey ( child , parent ) && ! ( parent && parent . type === 'AssignmentPattern' && parent . right === child ) ) { markScopeIdentifier ( node , child , knownIds ) ; } } } ) ) ; } else if ( node . type === 'ObjectProperty' && parent ! . type === 'ObjectPattern' ) { ; ( node as any ) . inPattern = true ; } } , leave ( node , parent ) { parent && parentStack . pop ( ) ; if ( node . scopeIds ) { node . scopeIds . forEach ( ( id ) => { knownIds [ id ] -- ; if ( knownIds [ id ] === 0 ) { delete knownIds [ id ] ; } } ) ; } } } ) ; } function isRefIdentifier ( id , parent , parentStack ) { if ( ! parent ) { return true ; } if ( ( parent . type === 'VariableDeclarator' || parent . type === 'ClassDeclaration' ) && parent . id === id ) { return false ; } if ( isFunction ( parent ) ) { if ( ( parent as any ) . id === id ) { return false ; } if ( parent . params . includes ( id ) ) { return false ; } } if ( isStaticPropertyKey ( id , parent ) ) { return false ; } if ( parent . type === 'ArrayPattern' && ! isInDestructureAssignment ( parent , parentStack ) ) { return false ; } if ( ( parent . type === 'MemberExpression' || parent . type === 'OptionalMemberExpression' ) && parent . property === id && ! parent . computed ) { return false ; } if ( id . name === 'arguments' ) { return false ; } return true ; } const isStaticProperty = ( node ) : node is ObjectProperty => node && ( node . type === 'ObjectProperty' || node . type === 'ObjectMethod' ) && ! node . computed ; const isStaticPropertyKey = ( node , parent ) => isStaticProperty ( parent ) && parent . key === node ; function isFunction ( node ) { return /Function(?:Expression|Declaration)$|Method$/ . test ( node . type ) ; } function isCallOf ( node , test ) { return ! ! ( node && node . type === 'CallExpression' && node . callee . type === 'Identifier' && ( typeof test === 'string' ? node . callee . name === test : test ( node . callee . name ) ) ) ; } function canNeverBeRef ( node , userReactiveImport ) { if ( isCallOf ( node , userReactiveImport ) ) { return true ; } switch ( node . type ) { case 'UnaryExpression' : case 'BinaryExpression' : case 'ArrayExpression' : case 'ObjectExpression' : case 'FunctionExpression' : case 'ArrowFunctionExpression' : case 'UpdateExpression' : case 'ClassExpression' : case 'TaggedTemplateExpression' : return true ; case 'SequenceExpression' : return canNeverBeRef ( node . expressions [ node . expressions . length - 1 ] , userReactiveImport ) ; default : if ( node . type . endsWith ( 'Literal' ) ) { return true ; } return false ; } } function isInDestructureAssignment ( parent , parentStack ) { if ( parent && ( parent . type === 'ObjectProperty' || parent . type === 'ArrayPattern' ) ) { let i = parentStack . length ; while ( i -- ) { const p = parentStack [ i ] ; if ( p . type === 'AssignmentExpression' ) { const root = parentStack [ 0 ] ; return ! ( root . type === 'LabeledStatement' && root . label . name === 'ref' ) ; } else if ( p . type !== 'ObjectProperty' && ! p . type . endsWith ( 'Pattern' ) ) { break ; } } } return false ; } /**
 * Analyze bindings in normal `<script>`
 * Note that `compileScriptSetup` already analyzes bindings as part of its
 * compilation process so this should only be used on single `<script>` SFCs.
 */ function analyzeScriptBindings ( ast ) { for ( const node of ast ) { if ( node . type === 'ExportDefaultDeclaration' && node . declaration . type === 'ObjectExpression' ) { return analyzeBindingsFromOptions ( node . declaration ) ; } } return { } ; } function analyzeBindingsFromOptions ( node ) { const bindings = { } ; Object . defineProperty ( bindings , '__isScriptSetup' , { enumerable : false , value : false } ) ; for ( const property of node . properties ) { if ( property . type === 'ObjectProperty' && ! property . computed && property . key . type === 'Identifier' ) { if ( property . key . name === 'props' ) { for ( const key of getObjectOrArrayExpressionKeys ( property . value ) ) { bindings [ key ] = BindingTypes . PROPS ; } } else if ( property . key . name === 'inject' ) { for ( const key of getObjectOrArrayExpressionKeys ( property . value ) ) { bindings [ key ] = BindingTypes . OPTIONS ; } } else if ( property . value . type === 'ObjectExpression' && ( property . key . name === 'computed' || property . key . name === 'methods' ) ) { for ( const key of getObjectExpressionKeys ( property . value ) ) { bindings [ key ] = BindingTypes . OPTIONS ; } } } else if ( property . type === 'ObjectMethod' && property . key . type === 'Identifier' && ( property . key . name === 'setup' || property . key . name === 'data' ) ) { for ( const bodyItem of property . body . body ) { if ( bodyItem . type === 'ReturnStatement' && bodyItem . argument && bodyItem . argument . type === 'ObjectExpression' ) { for ( const key of getObjectExpressionKeys ( bodyItem . argument ) ) { bindings [ key ] = property . key . name === 'setup' ? BindingTypes . SETUP_MAYBE_REF : BindingTypes . DATA ; } } } } } return bindings ; } function getObjectExpressionKeys ( node ) { const keys = [ ] ; for ( const prop of node . properties ) { if ( ( prop . type === 'ObjectProperty' || prop . type === 'ObjectMethod' ) && ! prop . computed ) { if ( prop . key . type === 'Identifier' ) { keys . push ( prop . key . name ) ; } else if ( prop . key . type === 'StringLiteral' ) { keys . push ( prop . key . value ) ; } } } return keys ; } function getArrayExpressionKeys ( node ) { const keys = [ ] ; for ( const element of node . elements ) { if ( element && element . type === 'StringLiteral' ) { keys . push ( element . value ) ; } } return keys ; } function getObjectOrArrayExpressionKeys ( value ) { if ( value . type === 'ArrayExpression' ) { return getArrayExpressionKeys ( value ) ; } if ( value . type === 'ObjectExpression' ) { return getObjectExpressionKeys ( value ) ; } return [ ] ; } function extractIdentifiers ( param , nodes = [ ] ) { switch ( param . type ) { case 'Identifier' : nodes . push ( param ) ; break ; case 'MemberExpression' : let object = param ; while ( object . type === 'MemberExpression' ) { object = object . object ; } nodes . push ( object ) ; break ; case 'ObjectPattern' : param . properties . forEach ( prop => { if ( prop . type === 'RestElement' ) { extractIdentifiers ( prop . argument , nodes ) ; } else { extractIdentifiers ( prop . value , nodes ) ; } } ) ; break ; case 'ArrayPattern' : param . elements . forEach ( element => { if ( element ) extractIdentifiers ( element , nodes ) ; } ) ; break ; case 'RestElement' : extractIdentifiers ( param . argument , nodes ) ; break ; case 'AssignmentPattern' : extractIdentifiers ( param . left , nodes ) ; break ; } return nodes ; } function toTextRange ( node ) { return { start : node . start ! , end : node . end ! } ; } const templateUsageCheckCache = createCache < string > ( ) ; function resolveTemplateUsageCheckString ( sfc ) { const { content , ast } = sfc . template ! ; const cached = templateUsageCheckCache . get ( content ) ; if ( cached ) { return cached ; } let code = '' ; transform ( createRoot ( [ ast ] ) , { nodeTransforms : [ node => { if ( node . type === NodeTypes . ELEMENT ) { if ( ! parserOptions . isNativeTag ! ( node . tag ) && ! parserOptions . isBuiltInComponent ! ( node . tag ) ) { code += `,${ camelize ( node . tag ) },${ capitalize ( camelize ( node . tag ) ) }` ; } for ( let i = 0 ; i < node . props . length ; i ++ ) { const prop = node . props [ i ] ; if ( prop . type === NodeTypes . DIRECTIVE ) { if ( ! isBuiltInDir ( prop . name ) ) { code += `,v${ capitalize ( camelize ( prop . name ) ) }` ; } if ( prop . exp ) { code += `,${ stripStrings ( ( prop . exp as SimpleExpressionNode ) . content ) }` ; } } } } else if ( node . type === NodeTypes . INTERPOLATION ) { code += `,${ stripStrings ( ( node . content as SimpleExpressionNode ) . content ) }` ; } } ] } ) ; code += ';' ; templateUsageCheckCache . set ( content , code ) ; return code ; } function stripStrings ( exp ) { return exp . replace ( /'[^']+'|"[^"]+"/g , '' ) . replace ( /`[^`]+`/g , stripTemplateString ) ; } function stripTemplateString ( str ) { const interpMatch = str . match ( /\${[^}]+}/g ) ; if ( interpMatch ) { return interpMatch . map ( m => m . slice ( 2 , - 1 ) ) . join ( ',' ) ; } return '' ; }