@@ -69,7 +69,7 @@ function parseStyleElement(
69
69
}
70
70
const code = textNode . value
71
71
// short circuit
72
- if ( ! code . includes ( " v-bind(" ) ) {
72
+ if ( ! / v - b i n d (?: \( | \/ ) / u . test ( code ) ) {
73
73
return
74
74
}
75
75
@@ -96,10 +96,14 @@ function parseStyle(
96
96
cssOptions : CSSParseOption ,
97
97
) {
98
98
let textStart = 0
99
- for ( const { range, expr, exprOffset, quote, comments } of iterateVBind (
100
- code ,
101
- cssOptions ,
102
- ) ) {
99
+ for ( const {
100
+ range,
101
+ expr,
102
+ exprOffset,
103
+ quote,
104
+ openingParenOffset,
105
+ comments,
106
+ } of iterateVBind ( code , cssOptions ) ) {
103
107
insertComments (
104
108
document ,
105
109
comments . map ( ( c ) =>
@@ -128,18 +132,20 @@ function parseStyle(
128
132
references : [ ] ,
129
133
}
130
134
135
+ const openingParenStart =
136
+ locationCalculator . getOffsetWithGap ( openingParenOffset )
131
137
const beforeTokens : Token [ ] = [
132
138
createSimpleToken (
133
- "HTMLText " ,
139
+ "HTMLRawText " ,
134
140
container . range [ 0 ] ,
135
141
container . range [ 0 ] + 6 /* v-bind */ ,
136
142
"v-bind" ,
137
143
locationCalculator ,
138
144
) ,
139
145
createSimpleToken (
140
146
"Punctuator" ,
141
- container . range [ 0 ] + 6 /* v-bind */ ,
142
- container . range [ 0 ] + 7 ,
147
+ openingParenStart ,
148
+ openingParenStart + 1 ,
143
149
"(" ,
144
150
locationCalculator ,
145
151
) ,
@@ -259,11 +265,31 @@ function parseStyle(
259
265
}
260
266
}
261
267
268
+ function isQuote ( c : string ) : c is '"' | "'" {
269
+ return c === '"' || c === "'"
270
+ }
271
+
272
+ function isCommentStart ( c : string ) : c is "/*" | "//" {
273
+ return c === "/*" || c === "//"
274
+ }
275
+
276
+ const COMMENT = {
277
+ "/*" : {
278
+ type : "Block" as const ,
279
+ closing : "*/" as const ,
280
+ } ,
281
+ "//" : {
282
+ type : "Line" as const ,
283
+ closing : "\n" as const ,
284
+ } ,
285
+ }
286
+
262
287
type VBindLocations = {
263
288
range : OffsetRange
264
289
expr : string
265
290
exprOffset : number
266
291
quote : '"' | "'" | null
292
+ openingParenOffset : number
267
293
comments : {
268
294
type : string
269
295
range : OffsetRange
@@ -279,25 +305,37 @@ function* iterateVBind(
279
305
cssOptions : CSSParseOption ,
280
306
) : IterableIterator < VBindLocations > {
281
307
const re = cssOptions . inlineComment
282
- ? / " | ' | \/ [ * / ] | \b v - b i n d \( / gu
283
- : / " | ' | \/ \* | \b v - b i n d \( / gu
308
+ ? / " | ' | \/ [ * / ] | \b v - b i n d / gu
309
+ : / " | ' | \/ \* | \b v - b i n d / gu
284
310
let match
285
311
while ( ( match = re . exec ( code ) ) ) {
286
- const startOrVBind = match [ 0 ]
287
- if ( startOrVBind === '"' || startOrVBind === "'" ) {
312
+ const startToken = match [ 0 ]
313
+ if ( isQuote ( startToken ) ) {
288
314
// skip string
289
- re . lastIndex = skipString ( code , startOrVBind , re . lastIndex )
290
- } else if ( startOrVBind === "/*" || startOrVBind === "//" ) {
315
+ re . lastIndex = skipString ( code , startToken , re . lastIndex )
316
+ } else if ( isCommentStart ( startToken ) ) {
291
317
// skip comment
292
318
re . lastIndex = skipComment (
293
319
code ,
294
- startOrVBind === "/*" ? "block" : "line" ,
320
+ COMMENT [ startToken ] . closing ,
295
321
re . lastIndex ,
296
322
)
297
323
} else {
298
324
// v-bind
325
+ const openingParen = findVBindOpeningParen (
326
+ code ,
327
+ re . lastIndex ,
328
+ cssOptions ,
329
+ )
330
+ if ( ! openingParen ) {
331
+ continue
332
+ }
299
333
const start = match . index
300
- const arg = parseVBindArg ( code , re . lastIndex , cssOptions )
334
+ const arg = parseVBindArg (
335
+ code ,
336
+ openingParen . openingParenOffset + 1 ,
337
+ cssOptions ,
338
+ )
301
339
if ( ! arg ) {
302
340
continue
303
341
}
@@ -306,13 +344,66 @@ function* iterateVBind(
306
344
expr : arg . expr ,
307
345
exprOffset : arg . exprOffset ,
308
346
quote : arg . quote ,
309
- comments : arg . comments ,
347
+ openingParenOffset : openingParen . openingParenOffset ,
348
+ comments : [ ...openingParen . comments , ...arg . comments ] ,
310
349
}
311
350
re . lastIndex = arg . end
312
351
}
313
352
}
314
353
}
315
354
355
+ function findVBindOpeningParen (
356
+ code : string ,
357
+ nextIndex : number ,
358
+ cssOptions : CSSParseOption ,
359
+ ) : {
360
+ openingParenOffset : number
361
+ comments : {
362
+ type : string
363
+ range : OffsetRange
364
+ value : string
365
+ } [ ]
366
+ } | null {
367
+ const re = cssOptions . inlineComment ? / \/ [ * / ] | [ \s \S ] / gu : / \/ \* | [ \s \S ] / gu
368
+ re . lastIndex = nextIndex
369
+ let match
370
+ const comments : {
371
+ type : string
372
+ range : OffsetRange
373
+ value : string
374
+ } [ ] = [ ]
375
+ while ( ( match = re . exec ( code ) ) ) {
376
+ const token = match [ 0 ]
377
+ if ( token === "(" ) {
378
+ return {
379
+ openingParenOffset : match . index ,
380
+ comments,
381
+ }
382
+ } else if ( isCommentStart ( token ) ) {
383
+ // Comment between `v-bind` and opening paren.
384
+ const comment = COMMENT [ token ]
385
+ const start = match . index
386
+ const end = ( re . lastIndex = skipComment (
387
+ code ,
388
+ comment . closing ,
389
+ re . lastIndex ,
390
+ ) )
391
+ comments . push ( {
392
+ type : comment . type ,
393
+ range : [ start , end ] ,
394
+ value : code . slice (
395
+ start + token . length ,
396
+ end - comment . closing . length ,
397
+ ) ,
398
+ } )
399
+ continue
400
+ }
401
+ // There were no opening parens.
402
+ return null
403
+ }
404
+ return null
405
+ }
406
+
316
407
function parseVBindArg (
317
408
code : string ,
318
409
nextIndex : number ,
@@ -338,29 +429,26 @@ function parseVBindArg(
338
429
value : string
339
430
} [ ] = [ ]
340
431
while ( ( match = re . exec ( code ) ) ) {
341
- const startOrVBind = match [ 0 ]
342
- if ( startOrVBind === '"' || startOrVBind === "'" ) {
432
+ const token = match [ 0 ]
433
+ if ( isQuote ( token ) ) {
343
434
const start = match . index
344
- const end = ( re . lastIndex = skipString (
345
- code ,
346
- startOrVBind ,
347
- re . lastIndex ,
348
- ) )
435
+ const end = ( re . lastIndex = skipString ( code , token , re . lastIndex ) )
349
436
stringRanges . push ( [ start , end ] )
350
- } else if ( startOrVBind === "/*" || startOrVBind === "//" ) {
351
- const block = startOrVBind === "/*"
437
+ } else if ( isCommentStart ( token ) ) {
438
+ const comment = COMMENT [ token ]
352
439
const start = match . index
353
440
const end = ( re . lastIndex = skipComment (
354
441
code ,
355
- block ? "block" : "line" ,
442
+ comment . closing ,
356
443
re . lastIndex ,
357
444
) )
358
445
comments . push ( {
359
- type : block ? "Block" : "Line" ,
446
+ type : comment . type ,
360
447
range : [ start , end ] ,
361
- value : block
362
- ? code . slice ( start + 2 , end - 2 )
363
- : code . slice ( start + 2 , end - 1 ) ,
448
+ value : code . slice (
449
+ start + token . length ,
450
+ end - comment . closing . length ,
451
+ ) ,
364
452
} )
365
453
} else {
366
454
// closing paren
@@ -405,10 +493,9 @@ function skipString(code: string, quote: '"' | "'", nextIndex: number): number {
405
493
406
494
function skipComment (
407
495
code : string ,
408
- kind : "block " | "line " ,
496
+ closing : "*/ " | "\n " ,
409
497
nextIndex : number ,
410
498
) : number {
411
- const closing = kind === "block" ? "*/" : "\n"
412
499
const index = code . indexOf ( closing , nextIndex )
413
500
if ( index >= nextIndex ) {
414
501
return index + closing . length
0 commit comments