@@ -69,7 +69,7 @@ function parseStyleElement(
6969 }
7070 const code = textNode . value
7171 // short circuit
72- if ( ! code . includes ( " v-bind(" ) ) {
72+ if ( ! / v - b i n d (?: \( | \/ ) / u . test ( code ) ) {
7373 return
7474 }
7575
@@ -96,10 +96,14 @@ function parseStyle(
9696 cssOptions : CSSParseOption ,
9797) {
9898 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 ) ) {
103107 insertComments (
104108 document ,
105109 comments . map ( ( c ) =>
@@ -128,18 +132,20 @@ function parseStyle(
128132 references : [ ] ,
129133 }
130134
135+ const openingParenStart =
136+ locationCalculator . getOffsetWithGap ( openingParenOffset )
131137 const beforeTokens : Token [ ] = [
132138 createSimpleToken (
133- "HTMLText " ,
139+ "HTMLRawText " ,
134140 container . range [ 0 ] ,
135141 container . range [ 0 ] + 6 /* v-bind */ ,
136142 "v-bind" ,
137143 locationCalculator ,
138144 ) ,
139145 createSimpleToken (
140146 "Punctuator" ,
141- container . range [ 0 ] + 6 /* v-bind */ ,
142- container . range [ 0 ] + 7 ,
147+ openingParenStart ,
148+ openingParenStart + 1 ,
143149 "(" ,
144150 locationCalculator ,
145151 ) ,
@@ -259,11 +265,31 @@ function parseStyle(
259265 }
260266}
261267
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+
262287type VBindLocations = {
263288 range : OffsetRange
264289 expr : string
265290 exprOffset : number
266291 quote : '"' | "'" | null
292+ openingParenOffset : number
267293 comments : {
268294 type : string
269295 range : OffsetRange
@@ -279,25 +305,37 @@ function* iterateVBind(
279305 cssOptions : CSSParseOption ,
280306) : IterableIterator < VBindLocations > {
281307 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
284310 let match
285311 while ( ( match = re . exec ( code ) ) ) {
286- const startOrVBind = match [ 0 ]
287- if ( startOrVBind === '"' || startOrVBind === "'" ) {
312+ const startToken = match [ 0 ]
313+ if ( isQuote ( startToken ) ) {
288314 // 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 ) ) {
291317 // skip comment
292318 re . lastIndex = skipComment (
293319 code ,
294- startOrVBind === "/*" ? "block" : "line" ,
320+ COMMENT [ startToken ] . closing ,
295321 re . lastIndex ,
296322 )
297323 } else {
298324 // v-bind
325+ const openingParen = findVBindOpeningParen (
326+ code ,
327+ re . lastIndex ,
328+ cssOptions ,
329+ )
330+ if ( ! openingParen ) {
331+ continue
332+ }
299333 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+ )
301339 if ( ! arg ) {
302340 continue
303341 }
@@ -306,13 +344,66 @@ function* iterateVBind(
306344 expr : arg . expr ,
307345 exprOffset : arg . exprOffset ,
308346 quote : arg . quote ,
309- comments : arg . comments ,
347+ openingParenOffset : openingParen . openingParenOffset ,
348+ comments : [ ...openingParen . comments , ...arg . comments ] ,
310349 }
311350 re . lastIndex = arg . end
312351 }
313352 }
314353}
315354
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+
316407function parseVBindArg (
317408 code : string ,
318409 nextIndex : number ,
@@ -338,29 +429,26 @@ function parseVBindArg(
338429 value : string
339430 } [ ] = [ ]
340431 while ( ( match = re . exec ( code ) ) ) {
341- const startOrVBind = match [ 0 ]
342- if ( startOrVBind === '"' || startOrVBind === "'" ) {
432+ const token = match [ 0 ]
433+ if ( isQuote ( token ) ) {
343434 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 ) )
349436 stringRanges . push ( [ start , end ] )
350- } else if ( startOrVBind === "/*" || startOrVBind === "//" ) {
351- const block = startOrVBind === "/*"
437+ } else if ( isCommentStart ( token ) ) {
438+ const comment = COMMENT [ token ]
352439 const start = match . index
353440 const end = ( re . lastIndex = skipComment (
354441 code ,
355- block ? "block" : "line" ,
442+ comment . closing ,
356443 re . lastIndex ,
357444 ) )
358445 comments . push ( {
359- type : block ? "Block" : "Line" ,
446+ type : comment . type ,
360447 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+ ) ,
364452 } )
365453 } else {
366454 // closing paren
@@ -405,10 +493,9 @@ function skipString(code: string, quote: '"' | "'", nextIndex: number): number {
405493
406494function skipComment (
407495 code : string ,
408- kind : "block " | "line " ,
496+ closing : "*/ " | "\n " ,
409497 nextIndex : number ,
410498) : number {
411- const closing = kind === "block" ? "*/" : "\n"
412499 const index = code . indexOf ( closing , nextIndex )
413500 if ( index >= nextIndex ) {
414501 return index + closing . length
0 commit comments