@@ -56,14 +56,67 @@ const nonIdentifierRE = /^\d|[^\$\w]/
56
56
export const isSimpleIdentifier = ( name : string ) : boolean =>
57
57
! nonIdentifierRE . test ( name )
58
58
59
- const memberExpRE = / ^ [ A - Z a - z _ $ \xA0 - \uFFFF ] [ \w $ \xA0 - \uFFFF ] * (?: \s * \. \s * [ A - Z a - z _ $ \xA0 - \uFFFF ] [ \w $ \xA0 - \uFFFF ] * | \[ ( .+ ) \] ) * $ /
59
+ const enum MemberExpLexState {
60
+ inMemberExp ,
61
+ inBrackets ,
62
+ inString
63
+ }
64
+
65
+ const validFirstIdentCharRE = / [ A - Z a - z _ $ \xA0 - \uFFFF ] /
66
+ const validIdentCharRE = / [ \. \w $ \xA0 - \uFFFF ] /
67
+ const whitespaceRE = / \s + [ . [ ] \s * | \s * [ . [ ] \s + / g
68
+
69
+ /**
70
+ * Simple lexer to check if an expression is a member expression. This is
71
+ * lax and only checks validity at the root level (i.e. does not validate exps
72
+ * inside square brackets), but it's ok since these are only used on template
73
+ * expressions and false positives are invalid expressions in the first place.
74
+ */
60
75
export const isMemberExpression = ( path : string ) : boolean => {
61
- if ( ! path ) return false
62
- const matched = memberExpRE . exec ( path . trim ( ) )
63
- if ( ! matched ) return false
64
- if ( ! matched [ 1 ] ) return true
65
- if ( ! / [ \[ \] ] / . test ( matched [ 1 ] ) ) return true
66
- return isMemberExpression ( matched [ 1 ] . trim ( ) )
76
+ // remove whitespaces around . or [ first
77
+ path = path . trim ( ) . replace ( whitespaceRE , s => s . trim ( ) )
78
+
79
+ let state = MemberExpLexState . inMemberExp
80
+ let prevState = MemberExpLexState . inMemberExp
81
+ let currentOpenBracketCount = 0
82
+ let currentStringType : "'" | '"' | '`' | null = null
83
+
84
+ for ( let i = 0 ; i < path . length ; i ++ ) {
85
+ const char = path . charAt ( i )
86
+ switch ( state ) {
87
+ case MemberExpLexState . inMemberExp :
88
+ if ( char === '[' ) {
89
+ prevState = state
90
+ state = MemberExpLexState . inBrackets
91
+ currentOpenBracketCount ++
92
+ } else if (
93
+ ! ( i === 0 ? validFirstIdentCharRE : validIdentCharRE ) . test ( char )
94
+ ) {
95
+ return false
96
+ }
97
+ break
98
+ case MemberExpLexState . inBrackets :
99
+ if ( char === `'` || char === `"` || char === '`' ) {
100
+ prevState = state
101
+ state = MemberExpLexState . inString
102
+ currentStringType = char
103
+ } else if ( char === `[` ) {
104
+ currentOpenBracketCount ++
105
+ } else if ( char === `]` ) {
106
+ if ( ! -- currentOpenBracketCount ) {
107
+ state = prevState
108
+ }
109
+ }
110
+ break
111
+ case MemberExpLexState . inString :
112
+ if ( char === currentStringType ) {
113
+ state = prevState
114
+ currentStringType = null
115
+ }
116
+ break
117
+ }
118
+ }
119
+ return ! currentOpenBracketCount
67
120
}
68
121
69
122
export function getInnerRange (