|
|
@@ -40,7 +40,7 @@ import {
|
|
|
import { NOOP, isObject, isString } from '@vue/shared'
|
|
|
import type { PropsExpression } from './transforms/transformElement'
|
|
|
import { parseExpression } from '@babel/parser'
|
|
|
-import type { Expression } from '@babel/types'
|
|
|
+import type { Expression, Node } from '@babel/types'
|
|
|
import { unwrapTSNode } from './babelUtils'
|
|
|
|
|
|
export const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
|
|
|
@@ -78,15 +78,20 @@ const validFirstIdentCharRE = /[A-Za-z_$\xA0-\uFFFF]/
|
|
|
const validIdentCharRE = /[\.\?\w$\xA0-\uFFFF]/
|
|
|
const whitespaceRE = /\s+[.[]\s*|\s*[.[]\s+/g
|
|
|
|
|
|
+const getExpSource = (exp: ExpressionNode): string =>
|
|
|
+ exp.type === NodeTypes.SIMPLE_EXPRESSION ? exp.content : exp.loc.source
|
|
|
+
|
|
|
/**
|
|
|
* Simple lexer to check if an expression is a member expression. This is
|
|
|
* lax and only checks validity at the root level (i.e. does not validate exps
|
|
|
* inside square brackets), but it's ok since these are only used on template
|
|
|
* expressions and false positives are invalid expressions in the first place.
|
|
|
*/
|
|
|
-export const isMemberExpressionBrowser = (path: string): boolean => {
|
|
|
+export const isMemberExpressionBrowser = (exp: ExpressionNode): boolean => {
|
|
|
// remove whitespaces around . or [ first
|
|
|
- path = path.trim().replace(whitespaceRE, s => s.trim())
|
|
|
+ const path = getExpSource(exp)
|
|
|
+ .trim()
|
|
|
+ .replace(whitespaceRE, s => s.trim())
|
|
|
|
|
|
let state = MemberExpLexState.inMemberExp
|
|
|
let stateStack: MemberExpLexState[] = []
|
|
|
@@ -154,15 +159,19 @@ export const isMemberExpressionBrowser = (path: string): boolean => {
|
|
|
}
|
|
|
|
|
|
export const isMemberExpressionNode: (
|
|
|
- path: string,
|
|
|
+ exp: ExpressionNode,
|
|
|
context: TransformContext,
|
|
|
) => boolean = __BROWSER__
|
|
|
? (NOOP as any)
|
|
|
- : (path: string, context: TransformContext): boolean => {
|
|
|
+ : (exp, context) => {
|
|
|
try {
|
|
|
- let ret: Expression = parseExpression(path, {
|
|
|
- plugins: context.expressionPlugins,
|
|
|
- })
|
|
|
+ let ret: Node =
|
|
|
+ exp.ast ||
|
|
|
+ parseExpression(getExpSource(exp), {
|
|
|
+ plugins: context.expressionPlugins
|
|
|
+ ? [...context.expressionPlugins, 'typescript']
|
|
|
+ : ['typescript'],
|
|
|
+ })
|
|
|
ret = unwrapTSNode(ret) as Expression
|
|
|
return (
|
|
|
ret.type === 'MemberExpression' ||
|
|
|
@@ -175,10 +184,52 @@ export const isMemberExpressionNode: (
|
|
|
}
|
|
|
|
|
|
export const isMemberExpression: (
|
|
|
- path: string,
|
|
|
+ exp: ExpressionNode,
|
|
|
context: TransformContext,
|
|
|
) => boolean = __BROWSER__ ? isMemberExpressionBrowser : isMemberExpressionNode
|
|
|
|
|
|
+const fnExpRE =
|
|
|
+ /^\s*(async\s*)?(\([^)]*?\)|[\w$_]+)\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/
|
|
|
+
|
|
|
+export const isFnExpressionBrowser: (exp: ExpressionNode) => boolean = exp =>
|
|
|
+ fnExpRE.test(getExpSource(exp))
|
|
|
+
|
|
|
+export const isFnExpressionNode: (
|
|
|
+ exp: ExpressionNode,
|
|
|
+ context: TransformContext,
|
|
|
+) => boolean = __BROWSER__
|
|
|
+ ? (NOOP as any)
|
|
|
+ : (exp, context) => {
|
|
|
+ try {
|
|
|
+ let ret: Node =
|
|
|
+ exp.ast ||
|
|
|
+ parseExpression(getExpSource(exp), {
|
|
|
+ plugins: context.expressionPlugins
|
|
|
+ ? [...context.expressionPlugins, 'typescript']
|
|
|
+ : ['typescript'],
|
|
|
+ })
|
|
|
+ // parser may parse the exp as statements when it contains semicolons
|
|
|
+ if (ret.type === 'Program') {
|
|
|
+ ret = ret.body[0]
|
|
|
+ if (ret.type === 'ExpressionStatement') {
|
|
|
+ ret = ret.expression
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ret = unwrapTSNode(ret) as Expression
|
|
|
+ return (
|
|
|
+ ret.type === 'FunctionExpression' ||
|
|
|
+ ret.type === 'ArrowFunctionExpression'
|
|
|
+ )
|
|
|
+ } catch (e) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+export const isFnExpression: (
|
|
|
+ exp: ExpressionNode,
|
|
|
+ context: TransformContext,
|
|
|
+) => boolean = __BROWSER__ ? isFnExpressionBrowser : isFnExpressionNode
|
|
|
+
|
|
|
export function advancePositionWithClone(
|
|
|
pos: Position,
|
|
|
source: string,
|