diff --git a/src/utils/template.ts b/src/utils/template.ts index 1fd342c..b028fc2 100644 --- a/src/utils/template.ts +++ b/src/utils/template.ts @@ -1,4 +1,4 @@ -import type { AttributeNode, DirectiveNode, ExpressionNode, ParentNode, RootNode, SourceLocation, TemplateChildNode, TextNode } from '@vue/compiler-dom' +import type { AttributeNode, DirectiveNode, ExpressionNode, ForParseResult, ParentNode, RootNode, SourceLocation, TemplateChildNode, TextNode } from '@vue/compiler-dom' // copy from `@vue/compiler-dom` enum NodeTypes { @@ -40,6 +40,7 @@ enum NodeTypes { interface ExpressionTrack { type: NodeTypes name?: string + forParseResult?: ForParseResult } interface Expression { @@ -163,17 +164,18 @@ export async function transpileVueTemplate( for (const item of expressions) { item.replacement = transformMap.get(item) ?? item.src - const surrounding = getSurrounding( + // the source should only have one of the quotes + const sourceQuote = getSourceQuote( content, item.loc.start.offset - offset, item.loc.end.offset - offset, ) - if (surrounding) { - const replace = surrounding.code === `"` ? `'` : `"` + if (sourceQuote !== null) { + const search = sourceQuote === `"` ? `'` : `"` item.replacement = replaceQuote( item.replacement, - surrounding.code, - replace, + search, + sourceQuote, ) } } @@ -210,25 +212,15 @@ function replaceQuote(code: string, target: string, replace: string): string { return res } -function getSurrounding(code: string, start: number, end: number) { - const empty = new Set([' ', '\n', '\r', '\t']) - let startIndex = start - 1 - let endIndex = end - - while (startIndex > 0 && empty.has(code.at(startIndex))) { - startIndex-- - } - - while (endIndex < code.length && empty.has(code.at(endIndex))) { - endIndex++ +function getSourceQuote(code: string, start: number, end: number): string | null { + const source = code.slice(start, end) + const quotes = ['"', '\''] + for (const quote of quotes) { + if (source.includes(quote)) { + return quote + } } - - const prev = startIndex >= 0 ? code.at(startIndex) : '' - const next = endIndex < code.length ? code.at(endIndex) : '' - - return prev && next && prev === next - ? { code: prev, prevAt: startIndex, nextAt: endIndex } - : undefined + return null } interface SnippetHandler { @@ -254,11 +246,26 @@ const defaultSnippetHandler: SnippetHandler = { standalone: false, } -const vSlotSnippetHandler: SnippetHandler = { +const destructureSnippetHandler: SnippetHandler = { key: (node) => { + const key = `destructure$:${node.src}` + const lastTrack = node.track.at(-1) const secondLastTrack = node.track.at(-2) + + // v-slot:xxx="{ name }" if (secondLastTrack?.type === NodeTypes.DIRECTIVE && secondLastTrack.name === 'slot') { - return `vSlot$:${node.src}` + return key + } + + // v-for="({ name }, key, index) of items" + // ^this ^this ^this ^not this + if ( + secondLastTrack?.type === NodeTypes.DIRECTIVE + && secondLastTrack.name === 'for' + && secondLastTrack?.forParseResult + && lastTrack !== secondLastTrack.forParseResult.source + ) { + return key } return null }, @@ -274,7 +281,7 @@ const vSlotSnippetHandler: SnippetHandler = { standalone: true, } -const snippetHandlers = [vSlotSnippetHandler, defaultSnippetHandler] +const snippetHandlers = [destructureSnippetHandler, defaultSnippetHandler] function getKey(expression: Expression) { for (const handler of snippetHandlers) { const key = handler.key(expression) @@ -339,7 +346,8 @@ async function transformJsSnippets(expressions: Expression[], transform: (code: // transform standalone snippets await Promise.all(standalone.map(async ({ id, handler, nodes }) => { - const line = await transform(handler.prepare(nodes[0], id)) + const prepared = handler.prepare(nodes[0], id) + const line = await transform(prepared) const res = handler.parse(line.trim(), id) if (!res) { diff --git a/test/template.test.ts b/test/template.test.ts index 3362de7..092f126 100644 --- a/test/template.test.ts +++ b/test/template.test.ts @@ -6,14 +6,17 @@ import { transpileVueTemplate } from '../src/utils/template' describe('transform typescript template', () => { it('v-for', async () => { - expect(await fixture(`
{{ item }}
`)) + expect(await fixture(`
{{ item }}
`)) .toEqual(`
{{ item }}
`) - expect(await fixture(`
{{ item }}
`)) + expect(await fixture(`
{{ item }}
`)) .toEqual(`
{{ item }}
`) expect(await fixture(`
`)) .toEqual(`
`) + + expect(await fixture(`
`)) + .toEqual(`
`) }) it('v-if', async () => {