1
- import type { AttributeNode , DirectiveNode , ExpressionNode , ParentNode , RootNode , SourceLocation , TemplateChildNode , TextNode } from '@vue/compiler-dom'
1
+ import type { AttributeNode , DirectiveNode , ExpressionNode , ForParseResult , ParentNode , RootNode , SourceLocation , TemplateChildNode , TextNode } from '@vue/compiler-dom'
2
2
3
3
// copy from `@vue/compiler-dom`
4
4
enum NodeTypes {
@@ -40,6 +40,7 @@ enum NodeTypes {
40
40
interface ExpressionTrack {
41
41
type : NodeTypes
42
42
name ?: string
43
+ forParseResult ?: ForParseResult
43
44
}
44
45
45
46
interface Expression {
@@ -163,17 +164,18 @@ export async function transpileVueTemplate(
163
164
for ( const item of expressions ) {
164
165
item . replacement = transformMap . get ( item ) ?? item . src
165
166
166
- const surrounding = getSurrounding (
167
+ // the source should only have one of the quotes
168
+ const sourceQuote = getSourceQuote (
167
169
content ,
168
170
item . loc . start . offset - offset ,
169
171
item . loc . end . offset - offset ,
170
172
)
171
- if ( surrounding ) {
172
- const replace = surrounding . code === `"` ? `'` : `"`
173
+ if ( sourceQuote !== null ) {
174
+ const search = sourceQuote === `"` ? `'` : `"`
173
175
item . replacement = replaceQuote (
174
176
item . replacement ,
175
- surrounding . code ,
176
- replace ,
177
+ search ,
178
+ sourceQuote ,
177
179
)
178
180
}
179
181
}
@@ -210,25 +212,15 @@ function replaceQuote(code: string, target: string, replace: string): string {
210
212
return res
211
213
}
212
214
213
- function getSurrounding ( code : string , start : number , end : number ) {
214
- const empty = new Set < string | undefined > ( [ ' ' , '\n' , '\r' , '\t' ] )
215
- let startIndex = start - 1
216
- let endIndex = end
217
-
218
- while ( startIndex > 0 && empty . has ( code . at ( startIndex ) ) ) {
219
- startIndex --
220
- }
221
-
222
- while ( endIndex < code . length && empty . has ( code . at ( endIndex ) ) ) {
223
- endIndex ++
215
+ function getSourceQuote ( code : string , start : number , end : number ) : string | null {
216
+ const source = code . slice ( start , end )
217
+ const quotes = [ '"' , '\'' ]
218
+ for ( const quote of quotes ) {
219
+ if ( source . includes ( quote ) ) {
220
+ return quote
221
+ }
224
222
}
225
-
226
- const prev = startIndex >= 0 ? code . at ( startIndex ) : ''
227
- const next = endIndex < code . length ? code . at ( endIndex ) : ''
228
-
229
- return prev && next && prev === next
230
- ? { code : prev , prevAt : startIndex , nextAt : endIndex }
231
- : undefined
223
+ return null
232
224
}
233
225
234
226
interface SnippetHandler {
@@ -254,11 +246,26 @@ const defaultSnippetHandler: SnippetHandler = {
254
246
standalone : false ,
255
247
}
256
248
257
- const vSlotSnippetHandler : SnippetHandler = {
249
+ const destructureSnippetHandler : SnippetHandler = {
258
250
key : ( node ) => {
251
+ const key = `destructure$:${ node . src } `
252
+ const lastTrack = node . track . at ( - 1 )
259
253
const secondLastTrack = node . track . at ( - 2 )
254
+
255
+ // v-slot:xxx="{ name }"
260
256
if ( secondLastTrack ?. type === NodeTypes . DIRECTIVE && secondLastTrack . name === 'slot' ) {
261
- return `vSlot$:${ node . src } `
257
+ return key
258
+ }
259
+
260
+ // v-for="({ name }, key, index) of items"
261
+ // ^this ^this ^this ^not this
262
+ if (
263
+ secondLastTrack ?. type === NodeTypes . DIRECTIVE
264
+ && secondLastTrack . name === 'for'
265
+ && secondLastTrack ?. forParseResult
266
+ && lastTrack !== secondLastTrack . forParseResult . source
267
+ ) {
268
+ return key
262
269
}
263
270
return null
264
271
} ,
@@ -274,7 +281,7 @@ const vSlotSnippetHandler: SnippetHandler = {
274
281
standalone : true ,
275
282
}
276
283
277
- const snippetHandlers = [ vSlotSnippetHandler , defaultSnippetHandler ]
284
+ const snippetHandlers = [ destructureSnippetHandler , defaultSnippetHandler ]
278
285
function getKey ( expression : Expression ) {
279
286
for ( const handler of snippetHandlers ) {
280
287
const key = handler . key ( expression )
@@ -339,7 +346,8 @@ async function transformJsSnippets(expressions: Expression[], transform: (code:
339
346
340
347
// transform standalone snippets
341
348
await Promise . all ( standalone . map ( async ( { id, handler, nodes } ) => {
342
- const line = await transform ( handler . prepare ( nodes [ 0 ] , id ) )
349
+ const prepared = handler . prepare ( nodes [ 0 ] , id )
350
+ const line = await transform ( prepared )
343
351
344
352
const res = handler . parse ( line . trim ( ) , id )
345
353
if ( ! res ) {
0 commit comments