@@ -12,7 +12,7 @@ const cachedCamelCase = (() => {
12
12
}
13
13
} ) ( )
14
14
const equalCamel = ( string , match ) => string === match || string === cachedCamelCase ( match )
15
- const startsWithCamel = ( string , match ) => string . startsWith ( match ) || string . startsWith ( cachedCamelCase ( match ) )
15
+
16
16
const keyModifiers = [ 'ctrl' , 'shift' , 'alt' , 'meta' ]
17
17
const keyCodes = {
18
18
esc : 27 ,
@@ -40,43 +40,39 @@ const keyNames = {
40
40
delete : [ 'Backspace' , 'Delete' ] ,
41
41
}
42
42
43
- export default function ( babel ) {
43
+ export default function ( babel ) {
44
44
const t = babel . types
45
45
46
46
function genGuard ( expression ) {
47
- return t . ifStatement ( expression , t . returnStatement ( t . nullStatement ( ) ) )
47
+ return t . ifStatement ( expression , t . returnStatement ( t . nullLiteral ( ) ) )
48
48
}
49
49
50
50
function genCallExpression ( expression , args = [ ] ) {
51
51
return t . callExpression ( expression , args )
52
52
}
53
53
54
- function genCallExpressionWithEvent ( expression ) {
55
- return genCallExpression ( expression , [ t . identifier ( '$event' ) ] )
56
- }
57
-
58
54
function genEventExpression ( name ) {
59
55
return t . memberExpression ( t . identifier ( '$event' ) , t . identifier ( name ) )
60
56
}
61
57
62
58
function not ( expression ) {
63
- return t . unaryStatement ( '!' , expression )
59
+ return t . unaryExpression ( '!' , expression )
64
60
}
65
61
66
62
function notEq ( left , right ) {
67
- return t . binaryStatement ( left , '!==' , right )
63
+ return t . binaryExpression ( '!==' , left , right )
68
64
}
69
65
70
66
function and ( left , right ) {
71
- return t . binaryStatement ( left , '&&' , right )
67
+ return t . logicalExpression ( '&&' , left , right )
72
68
}
73
69
74
- function and ( left , right ) {
75
- return t . binaryStatement ( left , '||' , right )
70
+ function or ( left , right ) {
71
+ return t . logicalExpression ( '||' , left , right )
76
72
}
77
73
78
74
function hasButton ( ) {
79
- return t . binaryStatement ( t . stringLiteral ( 'button' ) , 'in' , t . identifier ( '$event' ) )
75
+ return t . binaryExpression ( 'in' , t . stringLiteral ( 'button' ) , t . identifier ( '$event' ) )
80
76
}
81
77
82
78
const modifierCode = {
@@ -95,36 +91,36 @@ export default function (babel) {
95
91
// meta: genGuard(`!$event.metaKey`),
96
92
meta : ( ) => genGuard ( not ( genEventExpression ( 'metaKey' ) ) ) ,
97
93
// left: genGuard(`'button' in $event && $event.button !== 0`),
98
- left : ( ) => genGuard ( and ( hasButton ( ) , notEq ( genEventExpression ( 'button' ) , t . numberLiteral ( 0 ) ) ) ) ,
94
+ left : ( ) => genGuard ( and ( hasButton ( ) , notEq ( genEventExpression ( 'button' ) , t . numericLiteral ( 0 ) ) ) ) ,
99
95
// middle: genGuard(`'button' in $event && $event.button !== 1`),
100
- middle : ( ) => genGuard ( and ( hasButton ( ) , notEq ( genEventExpression ( 'button' ) , t . numberLiteral ( 1 ) ) ) ) ,
96
+ middle : ( ) => genGuard ( and ( hasButton ( ) , notEq ( genEventExpression ( 'button' ) , t . numericLiteral ( 1 ) ) ) ) ,
101
97
// right: genGuard(`'button' in $event && $event.button !== 2`)
102
- right : ( ) => genGuard ( and ( hasButton ( ) , notEq ( genEventExpression ( 'button' ) , t . numberLiteral ( 2 ) ) ) ) ,
98
+ right : ( ) => genGuard ( and ( hasButton ( ) , notEq ( genEventExpression ( 'button' ) , t . numericLiteral ( 2 ) ) ) ) ,
103
99
}
104
100
105
101
function genHandlerFunction ( body ) {
106
- return t . functionExpression ( [ t . identifier ( '$event' ) ] , t . blockStatement ( body instanceof Array ? body : [ body ] ) )
102
+ return t . arrowFunctionExpression ( [ t . identifier ( '$event' ) ] , t . blockStatement ( body instanceof Array ? body : [ body ] ) )
107
103
}
108
104
109
105
/**
110
106
* @param {Path<JSXAttribute> } handlerPath
111
107
*/
112
108
function parse ( handlerPath ) {
113
109
const namePath = handlerPath . get ( 'name' )
114
- let name = t . isJSXNamespacedName ( namePath ) ?
115
- `${ namePath . get ( 'namespace.name' ) . node } :${ namePath . get ( 'name.name' ) . node } ` :
116
- namePath . get ( 'name' ) . node
110
+ let name = t . isJSXNamespacedName ( namePath )
111
+ ? `${ namePath . get ( 'namespace.name' ) . node } :${ namePath . get ( 'name.name' ) . node } `
112
+ : namePath . get ( 'name' ) . node
117
113
118
114
const normalizedName = camelCase ( name )
119
115
120
116
let modifiers
121
- let argument ;
122
- [ name , ...modifiers ] = name . split ( '_' ) ;
123
- [ name , argument ] = name . split ( ':' )
117
+ let argument
118
+ ; [ name , ...modifiers ] = name . split ( '_' )
119
+ ; [ name , argument ] = name . split ( ':' )
124
120
125
121
if ( ! equalCamel ( name , 'v-on' ) || ! argument ) {
126
122
return {
127
- isInvalid : false
123
+ isInvalid : false ,
128
124
}
129
125
}
130
126
@@ -145,23 +141,16 @@ export default function (babel) {
145
141
* @param {Path<JSXAttribute> } handlerPath
146
142
*/
147
143
function genHandler ( handlerPath ) {
148
- const {
149
- modifiers,
150
- isInvalid,
151
- expression,
152
- event
153
- } = parse ( handlerPath )
144
+ let { modifiers, isInvalid, expression, event } = parse ( handlerPath )
145
+ let isNative = false
154
146
155
147
if ( isInvalid ) return
156
148
157
- const isFunctionExpression = t . isArrowFunctionExpression ( expression ) || t . isFunctionExpression ( expression )
158
-
159
- if ( ! isFunctionExpression ) throw new Error ( 'Only function expression is supported with v-on.' )
160
-
161
- if ( ! modifiers ) {
149
+ if ( ! modifiers || modifiers . length === 0 ) {
162
150
return {
163
151
event,
164
- expression
152
+ expression,
153
+ isNative,
165
154
}
166
155
}
167
156
@@ -171,20 +160,28 @@ export default function (babel) {
171
160
172
161
for ( const key of modifiers ) {
173
162
if ( modifierCode [ key ] ) {
174
- genModifierCode . push ( modifierCode [ key ] ( ) )
175
-
163
+ const modifierStatement = modifierCode [ key ] ( )
164
+ genModifierCode . push (
165
+ t . isExpression ( modifierStatement ) ? t . expressionStatement ( modifierStatement ) : modifierStatement ,
166
+ )
176
167
if ( keyCodes [ key ] ) {
177
168
keys . push ( key )
178
169
}
179
170
} else if ( key === 'exact' ) {
180
171
genModifierCode . push (
181
172
genGuard (
182
173
keyModifiers
183
- . filter ( keyModifier => ! modifiers . includes ( keyModifier ) )
184
- . map ( keyModifier => genEventExpression ( keyModifier + 'Key' ) )
185
- . reduce ( ( acc , item ) => acc ? or ( acc , item ) : item ) ,
174
+ . filter ( keyModifier => ! modifiers . includes ( keyModifier ) )
175
+ . map ( keyModifier => genEventExpression ( keyModifier + 'Key' ) )
176
+ . reduce ( ( acc , item ) => ( acc ? or ( acc , item ) : item ) ) ,
186
177
) ,
187
178
)
179
+ } else if ( key === 'capture' ) {
180
+ event = '!' + event
181
+ } else if ( key === 'once' ) {
182
+ event = '~' + event
183
+ } else if ( key === 'native' ) {
184
+ isNative = true
188
185
} else {
189
186
keys . push ( key )
190
187
}
@@ -195,16 +192,23 @@ export default function (babel) {
195
192
}
196
193
197
194
if ( genModifierCode . length ) {
198
- code . concat ( genModifierCode )
195
+ code . push ( ... genModifierCode )
199
196
}
200
197
201
- code . push (
202
- t . returnStatement ( genCallExpression ( expression , [ t . identifier ( '$event' ) ] ) )
203
- )
198
+ if ( code . length === 0 ) {
199
+ return {
200
+ event,
201
+ expression,
202
+ isNative,
203
+ }
204
+ }
205
+
206
+ code . push ( t . returnStatement ( genCallExpression ( expression , [ t . identifier ( '$event' ) ] ) ) )
204
207
205
208
return {
206
209
event,
207
- expression : genHandlerFunction ( code )
210
+ expression : genHandlerFunction ( code ) ,
211
+ isNative,
208
212
}
209
213
}
210
214
@@ -216,7 +220,7 @@ export default function (babel) {
216
220
const keyVal = parseInt ( key , 10 )
217
221
218
222
if ( keyVal ) {
219
- return notEq ( genEventExpression ( 'keyCode' ) , t . numberLiteral ( keyVal ) )
223
+ return notEq ( genEventExpression ( 'keyCode' ) , t . numericLiteral ( keyVal ) )
220
224
}
221
225
222
226
const keyCode = keyCodes [ key ]
@@ -225,40 +229,58 @@ export default function (babel) {
225
229
return t . callExpression ( t . memberExpression ( t . thisExpression ( ) , t . identifier ( '_k' ) ) , [
226
230
genEventExpression ( 'keyCode' ) ,
227
231
t . stringLiteral ( `${ key } ` ) ,
228
- t . stringLiteral ( `${ keyCode } ` ) ,
232
+ keyCode
233
+ ? Array . isArray ( keyCode )
234
+ ? t . arrayExpression ( keyCode . map ( number => t . numericLiteral ( number ) ) )
235
+ : t . numericLiteral ( keyCode )
236
+ : t . identifier ( 'undefined' ) ,
229
237
genEventExpression ( 'key' ) ,
230
- t . stringLiteral ( `${ keyName } ` ) ,
238
+ keyName
239
+ ? Array . isArray ( keyName )
240
+ ? t . arrayExpression ( keyName . map ( number => t . stringLiteral ( number ) ) )
241
+ : t . stringLiteral ( `${ keyName } ` )
242
+ : t . identifier ( 'undefined' ) ,
231
243
] )
232
244
}
233
245
246
+ function addEvent ( event , expression , isNative , attributes ) {
247
+ if ( event [ 0 ] !== '~' && event [ 0 ] !== '!' ) {
248
+ attributes . push (
249
+ t . jSXAttribute (
250
+ t . jSXIdentifier ( `${ isNative ? 'nativeOn' : 'on' } -${ event } ` ) ,
251
+ t . jSXExpressionContainer ( expression ) ,
252
+ ) ,
253
+ )
254
+ } else {
255
+ attributes . push (
256
+ t . jSXSpreadAttribute (
257
+ t . objectExpression ( [
258
+ t . objectProperty (
259
+ t . identifier ( 'on' ) ,
260
+ t . objectExpression ( [ t . objectProperty ( t . stringLiteral ( event ) , expression ) ] ) ,
261
+ ) ,
262
+ ] ) ,
263
+ ) ,
264
+ )
265
+ }
266
+ }
267
+
234
268
return {
235
269
inherits : syntaxJsx ,
236
270
visitor : {
237
271
Program ( path ) {
238
272
path . traverse ( {
239
273
JSXAttribute ( path ) {
240
- const {
241
- event,
242
- expression
243
- } = genHandler ( path )
274
+ const { event, expression, isNative } = genHandler ( path )
244
275
245
276
if ( event ) {
246
277
path . remove ( )
247
- const tag = path . parentPath . get ( 'name.name' )
248
- const isNative = tag [ 0 ] < 'A' || 'Z' < tag [ 1 ]
249
-
250
- path . parentPath . node . attributes . push (
251
- t . jSXAttribute (
252
- t . jSXNamespacedName (
253
- t . jSXIdentifier ( isNative ? 'v-native-on' : 'v-on' ) , t . jSXIdentifier ( event )
254
- ) ,
255
- t . jSXExpressionContainer ( expression )
256
- )
257
- )
278
+
279
+ addEvent ( event , expression , isNative , path . parentPath . node . attributes )
258
280
}
259
281
} ,
260
282
} )
261
283
} ,
262
284
} ,
263
285
}
264
- }
286
+ }
0 commit comments