@@ -39,7 +39,7 @@ import {
3939import { NOOP , isObject , isString } from '@vue/shared'
4040import type { PropsExpression } from './transforms/transformElement'
4141import { parseExpression } from '@babel/parser'
42- import type { Expression } from '@babel/types'
42+ import type { Expression , Node } from '@babel/types'
4343import { unwrapTSNode } from './babelUtils'
4444
4545export const isStaticExp = ( p : JSChildNode ) : p is SimpleExpressionNode =>
@@ -77,15 +77,20 @@ const validFirstIdentCharRE = /[A-Za-z_$\xA0-\uFFFF]/
7777const validIdentCharRE = / [ \. \? \w $ \xA0 - \uFFFF ] /
7878const whitespaceRE = / \s + [ . [ ] \s * | \s * [ . [ ] \s + / g
7979
80+ const getExpSource = ( exp : ExpressionNode ) : string =>
81+ exp . type === NodeTypes . SIMPLE_EXPRESSION ? exp . content : exp . loc . source
82+
8083/**
8184 * Simple lexer to check if an expression is a member expression. This is
8285 * lax and only checks validity at the root level (i.e. does not validate exps
8386 * inside square brackets), but it's ok since these are only used on template
8487 * expressions and false positives are invalid expressions in the first place.
8588 */
86- export const isMemberExpressionBrowser = ( path : string ) : boolean => {
89+ export const isMemberExpressionBrowser = ( exp : ExpressionNode ) : boolean => {
8790 // remove whitespaces around . or [ first
88- path = path . trim ( ) . replace ( whitespaceRE , s => s . trim ( ) )
91+ const path = getExpSource ( exp )
92+ . trim ( )
93+ . replace ( whitespaceRE , s => s . trim ( ) )
8994
9095 let state = MemberExpLexState . inMemberExp
9196 let stateStack : MemberExpLexState [ ] = [ ]
@@ -152,13 +157,20 @@ export const isMemberExpressionBrowser = (path: string): boolean => {
152157 return ! currentOpenBracketCount && ! currentOpenParensCount
153158}
154159
155- export const isMemberExpressionNode = __BROWSER__
156- ? ( NOOP as any as ( path : string , context : TransformContext ) => boolean )
157- : ( path : string , context : TransformContext ) : boolean => {
160+ export const isMemberExpressionNode : (
161+ exp : ExpressionNode ,
162+ context : TransformContext ,
163+ ) => boolean = __BROWSER__
164+ ? ( NOOP as any )
165+ : ( exp , context ) => {
158166 try {
159- let ret : Expression = parseExpression ( path , {
160- plugins : context . expressionPlugins ,
161- } )
167+ let ret : Node =
168+ exp . ast ||
169+ parseExpression ( getExpSource ( exp ) , {
170+ plugins : context . expressionPlugins
171+ ? [ ...context . expressionPlugins , 'typescript' ]
172+ : [ 'typescript' ] ,
173+ } )
162174 ret = unwrapTSNode ( ret ) as Expression
163175 return (
164176 ret . type === 'MemberExpression' ||
@@ -170,9 +182,52 @@ export const isMemberExpressionNode = __BROWSER__
170182 }
171183 }
172184
173- export const isMemberExpression = __BROWSER__
174- ? isMemberExpressionBrowser
175- : isMemberExpressionNode
185+ export const isMemberExpression : (
186+ exp : ExpressionNode ,
187+ context : TransformContext ,
188+ ) => boolean = __BROWSER__ ? isMemberExpressionBrowser : isMemberExpressionNode
189+
190+ const fnExpRE =
191+ / ^ \s * ( a s y n c \s * ) ? ( \( [ ^ ) ] * ?\) | [ \w $ _ ] + ) \s * ( : [ ^ = ] + ) ? = > | ^ \s * ( a s y n c \s + ) ? f u n c t i o n (?: \s + [ \w $ ] + ) ? \s * \( /
192+
193+ export const isFnExpressionBrowser : ( exp : ExpressionNode ) => boolean = exp =>
194+ fnExpRE . test ( getExpSource ( exp ) )
195+
196+ export const isFnExpressionNode : (
197+ exp : ExpressionNode ,
198+ context : TransformContext ,
199+ ) => boolean = __BROWSER__
200+ ? ( NOOP as any )
201+ : ( exp , context ) => {
202+ try {
203+ let ret : Node =
204+ exp . ast ||
205+ parseExpression ( getExpSource ( exp ) , {
206+ plugins : context . expressionPlugins
207+ ? [ ...context . expressionPlugins , 'typescript' ]
208+ : [ 'typescript' ] ,
209+ } )
210+ // parser may parse the exp as statements when it contains semicolons
211+ if ( ret . type === 'Program' ) {
212+ ret = ret . body [ 0 ]
213+ if ( ret . type === 'ExpressionStatement' ) {
214+ ret = ret . expression
215+ }
216+ }
217+ ret = unwrapTSNode ( ret ) as Expression
218+ return (
219+ ret . type === 'FunctionExpression' ||
220+ ret . type === 'ArrowFunctionExpression'
221+ )
222+ } catch ( e ) {
223+ return false
224+ }
225+ }
226+
227+ export const isFnExpression : (
228+ exp : ExpressionNode ,
229+ context : TransformContext ,
230+ ) => boolean = __BROWSER__ ? isFnExpressionBrowser : isFnExpressionNode
176231
177232export function advancePositionWithClone (
178233 pos : Position ,
0 commit comments