@@ -39,7 +39,7 @@ import {
39
39
import { NOOP , isObject , isString } from '@vue/shared'
40
40
import type { PropsExpression } from './transforms/transformElement'
41
41
import { parseExpression } from '@babel/parser'
42
- import type { Expression } from '@babel/types'
42
+ import type { Expression , Node } from '@babel/types'
43
43
import { unwrapTSNode } from './babelUtils'
44
44
45
45
export const isStaticExp = ( p : JSChildNode ) : p is SimpleExpressionNode =>
@@ -77,15 +77,20 @@ const validFirstIdentCharRE = /[A-Za-z_$\xA0-\uFFFF]/
77
77
const validIdentCharRE = / [ \. \? \w $ \xA0 - \uFFFF ] /
78
78
const whitespaceRE = / \s + [ . [ ] \s * | \s * [ . [ ] \s + / g
79
79
80
+ const getExpSource = ( exp : ExpressionNode ) : string =>
81
+ exp . type === NodeTypes . SIMPLE_EXPRESSION ? exp . content : exp . loc . source
82
+
80
83
/**
81
84
* Simple lexer to check if an expression is a member expression. This is
82
85
* lax and only checks validity at the root level (i.e. does not validate exps
83
86
* inside square brackets), but it's ok since these are only used on template
84
87
* expressions and false positives are invalid expressions in the first place.
85
88
*/
86
- export const isMemberExpressionBrowser = ( path : string ) : boolean => {
89
+ export const isMemberExpressionBrowser = ( exp : ExpressionNode ) : boolean => {
87
90
// 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 ( ) )
89
94
90
95
let state = MemberExpLexState . inMemberExp
91
96
let stateStack : MemberExpLexState [ ] = [ ]
@@ -152,13 +157,20 @@ export const isMemberExpressionBrowser = (path: string): boolean => {
152
157
return ! currentOpenBracketCount && ! currentOpenParensCount
153
158
}
154
159
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 ) => {
158
166
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
+ } )
162
174
ret = unwrapTSNode ( ret ) as Expression
163
175
return (
164
176
ret . type === 'MemberExpression' ||
@@ -170,9 +182,52 @@ export const isMemberExpressionNode = __BROWSER__
170
182
}
171
183
}
172
184
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
176
231
177
232
export function advancePositionWithClone (
178
233
pos : Position ,
0 commit comments