@@ -38,14 +38,25 @@ import {
38
38
defaultOnError ,
39
39
defaultOnWarn
40
40
} from './errors'
41
- import { forAliasRE , isCoreComponent , isStaticArgOf } from './utils'
41
+ import {
42
+ forAliasRE ,
43
+ isCoreComponent ,
44
+ isSimpleIdentifier ,
45
+ isStaticArgOf
46
+ } from './utils'
42
47
import { decodeHTML } from 'entities/lib/decode.js'
48
+ import {
49
+ parse ,
50
+ parseExpression ,
51
+ type ParserOptions as BabelOptions
52
+ } from '@babel/parser'
43
53
44
54
type OptionalOptions =
45
55
| 'decodeEntities'
46
56
| 'whitespace'
47
57
| 'isNativeTag'
48
58
| 'isBuiltInComponent'
59
+ | 'expressionPlugins'
49
60
| keyof CompilerCompatOptions
50
61
51
62
export type MergedParserOptions = Omit <
@@ -64,7 +75,8 @@ export const defaultParserOptions: MergedParserOptions = {
64
75
isCustomElement : NO ,
65
76
onError : defaultOnError ,
66
77
onWarn : defaultOnWarn ,
67
- comments : __DEV__
78
+ comments : __DEV__ ,
79
+ prefixIdentifiers : false
68
80
}
69
81
70
82
let currentOptions : MergedParserOptions = defaultParserOptions
@@ -116,7 +128,7 @@ const tokenizer = new Tokenizer(stack, {
116
128
}
117
129
addNode ( {
118
130
type : NodeTypes . INTERPOLATION ,
119
- content : createSimpleExpression ( exp , false , getLoc ( innerStart , innerEnd ) ) ,
131
+ content : createExp ( exp , false , getLoc ( innerStart , innerEnd ) ) ,
120
132
loc : getLoc ( start , end )
121
133
} )
122
134
} ,
@@ -245,7 +257,7 @@ const tokenizer = new Tokenizer(stack, {
245
257
setLocEnd ( ( currentProp as AttributeNode ) . nameLoc , end )
246
258
} else {
247
259
const isStatic = arg [ 0 ] !== `[`
248
- ; ( currentProp as DirectiveNode ) . arg = createSimpleExpression (
260
+ ; ( currentProp as DirectiveNode ) . arg = createExp (
249
261
isStatic ? arg : arg . slice ( 1 , - 1 ) ,
250
262
isStatic ,
251
263
getLoc ( start , end ) ,
@@ -346,10 +358,25 @@ const tokenizer = new Tokenizer(stack, {
346
358
}
347
359
} else {
348
360
// directive
349
- currentProp . exp = createSimpleExpression (
361
+ let expParseMode = ExpParseMode . Normal
362
+ if ( ! __BROWSER__ ) {
363
+ if ( currentProp . name === 'for' ) {
364
+ expParseMode = ExpParseMode . Skip
365
+ } else if ( currentProp . name === 'slot' ) {
366
+ expParseMode = ExpParseMode . Params
367
+ } else if (
368
+ currentProp . name === 'on' &&
369
+ currentAttrValue . includes ( ';' )
370
+ ) {
371
+ expParseMode = ExpParseMode . Statements
372
+ }
373
+ }
374
+ currentProp . exp = createExp (
350
375
currentAttrValue ,
351
376
false ,
352
- getLoc ( currentAttrStartIndex , currentAttrEndIndex )
377
+ getLoc ( currentAttrStartIndex , currentAttrEndIndex ) ,
378
+ ConstantTypes . NOT_CONSTANT ,
379
+ expParseMode
353
380
)
354
381
if ( currentProp . name === 'for' ) {
355
382
currentProp . forParseResult = parseForExpression ( currentProp . exp )
@@ -477,10 +504,20 @@ function parseForExpression(
477
504
478
505
const [ , LHS , RHS ] = inMatch
479
506
480
- const createAliasExpression = ( content : string , offset : number ) => {
507
+ const createAliasExpression = (
508
+ content : string ,
509
+ offset : number ,
510
+ asParam = false
511
+ ) => {
481
512
const start = loc . start . offset + offset
482
513
const end = start + content . length
483
- return createSimpleExpression ( content , false , getLoc ( start , end ) )
514
+ return createExp (
515
+ content ,
516
+ false ,
517
+ getLoc ( start , end ) ,
518
+ ConstantTypes . NOT_CONSTANT ,
519
+ asParam ? ExpParseMode . Params : ExpParseMode . Normal
520
+ )
484
521
}
485
522
486
523
const result : ForParseResult = {
@@ -502,7 +539,7 @@ function parseForExpression(
502
539
let keyOffset : number | undefined
503
540
if ( keyContent ) {
504
541
keyOffset = exp . indexOf ( keyContent , trimmedOffset + valueContent . length )
505
- result . key = createAliasExpression ( keyContent , keyOffset )
542
+ result . key = createAliasExpression ( keyContent , keyOffset , true )
506
543
}
507
544
508
545
if ( iteratorMatch [ 2 ] ) {
@@ -516,14 +553,15 @@ function parseForExpression(
516
553
result . key
517
554
? keyOffset ! + keyContent . length
518
555
: trimmedOffset + valueContent . length
519
- )
556
+ ) ,
557
+ true
520
558
)
521
559
}
522
560
}
523
561
}
524
562
525
563
if ( valueContent ) {
526
- result . value = createAliasExpression ( valueContent , trimmedOffset )
564
+ result . value = createAliasExpression ( valueContent , trimmedOffset , true )
527
565
}
528
566
529
567
return result
@@ -929,8 +967,58 @@ function dirToAttr(dir: DirectiveNode): AttributeNode {
929
967
return attr
930
968
}
931
969
932
- function emitError ( code : ErrorCodes , index : number ) {
933
- currentOptions . onError ( createCompilerError ( code , getLoc ( index , index ) ) )
970
+ enum ExpParseMode {
971
+ Normal ,
972
+ Params ,
973
+ Statements ,
974
+ Skip
975
+ }
976
+
977
+ function createExp (
978
+ content : SimpleExpressionNode [ 'content' ] ,
979
+ isStatic : SimpleExpressionNode [ 'isStatic' ] = false ,
980
+ loc : SourceLocation ,
981
+ constType : ConstantTypes = ConstantTypes . NOT_CONSTANT ,
982
+ parseMode = ExpParseMode . Normal
983
+ ) {
984
+ const exp = createSimpleExpression ( content , isStatic , loc , constType )
985
+ if (
986
+ ! __BROWSER__ &&
987
+ ! isStatic &&
988
+ currentOptions . prefixIdentifiers &&
989
+ parseMode !== ExpParseMode . Skip &&
990
+ content . trim ( )
991
+ ) {
992
+ if ( isSimpleIdentifier ( content ) ) {
993
+ exp . ast = null // fast path
994
+ return exp
995
+ }
996
+ try {
997
+ const plugins = currentOptions . expressionPlugins
998
+ const options : BabelOptions = {
999
+ plugins : plugins ? [ ...plugins , 'typescript' ] : [ 'typescript' ]
1000
+ }
1001
+ if ( parseMode === ExpParseMode . Statements ) {
1002
+ // v-on with multi-inline-statements, pad 1 char
1003
+ exp . ast = parse ( ` ${ content } ` , options ) . program
1004
+ } else if ( parseMode === ExpParseMode . Params ) {
1005
+ exp . ast = parseExpression ( `(${ content } )=>{}` , options )
1006
+ } else {
1007
+ // normal exp, wrap with parens
1008
+ exp . ast = parseExpression ( `(${ content } )` , options )
1009
+ }
1010
+ } catch ( e : any ) {
1011
+ exp . ast = false // indicate an error
1012
+ emitError ( ErrorCodes . X_INVALID_EXPRESSION , loc . start . offset , e . message )
1013
+ }
1014
+ }
1015
+ return exp
1016
+ }
1017
+
1018
+ function emitError ( code : ErrorCodes , index : number , message ?: string ) {
1019
+ currentOptions . onError (
1020
+ createCompilerError ( code , getLoc ( index , index ) , undefined , message )
1021
+ )
934
1022
}
935
1023
936
1024
function reset ( ) {
0 commit comments