Skip to content

Commit 6ef1534

Browse files
authored
Fix incorrect AST for commented CSS vars injection. (#120)
1 parent 86ec4d1 commit 6ef1534

File tree

17 files changed

+1172
-499
lines changed

17 files changed

+1172
-499
lines changed

Diff for: src/style/index.ts

+120-33
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function parseStyleElement(
6969
}
7070
const code = textNode.value
7171
// short circuit
72-
if (!code.includes("v-bind(")) {
72+
if (!/v-bind(?:\(|\/)/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+
262287
type 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-
? /"|'|\/[*/]|\bv-bind\(/gu
283-
: /"|'|\/\*|\bv-bind\(/gu
308+
? /"|'|\/[*/]|\bv-bind/gu
309+
: /"|'|\/\*|\bv-bind/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+
316407
function 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

406494
function 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

Comments
 (0)