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'
22
33// copy from `@vue/compiler-dom`
44enum NodeTypes {
@@ -40,6 +40,7 @@ enum NodeTypes {
4040interface ExpressionTrack {
4141 type : NodeTypes
4242 name ?: string
43+ forParseResult ?: ForParseResult
4344}
4445
4546interface Expression {
@@ -163,17 +164,18 @@ export async function transpileVueTemplate(
163164 for ( const item of expressions ) {
164165 item . replacement = transformMap . get ( item ) ?? item . src
165166
166- const surrounding = getSurrounding (
167+ // the source should only have one of the quotes
168+ const sourceQuote = getSourceQuote (
167169 content ,
168170 item . loc . start . offset - offset ,
169171 item . loc . end . offset - offset ,
170172 )
171- if ( surrounding ) {
172- const replace = surrounding . code === `"` ? `'` : `"`
173+ if ( sourceQuote !== null ) {
174+ const search = sourceQuote === `"` ? `'` : `"`
173175 item . replacement = replaceQuote (
174176 item . replacement ,
175- surrounding . code ,
176- replace ,
177+ search ,
178+ sourceQuote ,
177179 )
178180 }
179181 }
@@ -210,25 +212,15 @@ function replaceQuote(code: string, target: string, replace: string): string {
210212 return res
211213}
212214
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+ }
224222 }
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
232224}
233225
234226interface SnippetHandler {
@@ -254,11 +246,26 @@ const defaultSnippetHandler: SnippetHandler = {
254246 standalone : false ,
255247}
256248
257- const vSlotSnippetHandler : SnippetHandler = {
249+ const destructureSnippetHandler : SnippetHandler = {
258250 key : ( node ) => {
251+ const key = `destructure$:${ node . src } `
252+ const lastTrack = node . track . at ( - 1 )
259253 const secondLastTrack = node . track . at ( - 2 )
254+
255+ // v-slot:xxx="{ name }"
260256 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
262269 }
263270 return null
264271 } ,
@@ -274,7 +281,7 @@ const vSlotSnippetHandler: SnippetHandler = {
274281 standalone : true ,
275282}
276283
277- const snippetHandlers = [ vSlotSnippetHandler , defaultSnippetHandler ]
284+ const snippetHandlers = [ destructureSnippetHandler , defaultSnippetHandler ]
278285function getKey ( expression : Expression ) {
279286 for ( const handler of snippetHandlers ) {
280287 const key = handler . key ( expression )
@@ -339,7 +346,8 @@ async function transformJsSnippets(expressions: Expression[], transform: (code:
339346
340347 // transform standalone snippets
341348 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 )
343351
344352 const res = handler . parse ( line . trim ( ) , id )
345353 if ( ! res ) {
0 commit comments