diff --git a/packages/language-core/lib/codegen/template/elementChildren.ts b/packages/language-core/lib/codegen/template/elementChildren.ts index 125dc2dc32..a46555893f 100644 --- a/packages/language-core/lib/codegen/template/elementChildren.ts +++ b/packages/language-core/lib/codegen/template/elementChildren.ts @@ -1,8 +1,8 @@ -import type * as CompilerDOM from '@vue/compiler-dom'; +import * as CompilerDOM from '@vue/compiler-dom'; import type { Code } from '../../types'; import type { TemplateCodegenContext } from './context'; import type { TemplateCodegenOptions } from './index'; -import { generateTemplateChild } from './templateChild'; +import { generateTemplateChild, getVIfNode } from './templateChild'; export function* generateElementChildren( options: TemplateCodegenOptions, @@ -11,8 +11,113 @@ export function* generateElementChildren( enterNode = true, ): Generator { yield* ctx.generateAutoImportCompletion(); - for (const childNode of children) { - yield* generateTemplateChild(options, ctx, childNode, enterNode); + for (let i = 0; i < children.length; i++) { + const current = children[i]!; + const normalized = normalizeIfBranch(children, i); + if (normalized) { + i = normalized.end; + yield* generateTemplateChild(options, ctx, normalized.node, enterNode); + continue; + } + yield* generateTemplateChild(options, ctx, current, enterNode); } yield* ctx.generateAutoImportCompletion(); } + +function normalizeIfBranch( + children: (CompilerDOM.TemplateChildNode | CompilerDOM.SimpleExpressionNode)[], + start: number, +): { node: CompilerDOM.IfNode; end: number } | undefined { + const first = children[start]!; + if (!isTemplateChildNode(first)) { + return; + } + if (first.type === CompilerDOM.NodeTypes.IF) { + return { node: first, end: start }; + } + if (first.type !== CompilerDOM.NodeTypes.ELEMENT) { + return; + } + + const ifNode = getVIfNode(first); + if (!ifNode) { + return; + } + + let end = start; + let comments: CompilerDOM.CommentNode[] = []; + + for (let i = start + 1; i < children.length; i++) { + const sibling = children[i]!; + if (!isTemplateChildNode(sibling)) { + continue; + } + if (sibling.type === CompilerDOM.NodeTypes.COMMENT) { + comments.push(sibling); + continue; + } + if (sibling.type === CompilerDOM.NodeTypes.TEXT && !sibling.content.trim()) { + continue; + } + const elseBranch = getVElseDirective(sibling); + if (elseBranch) { + const branchNode: CompilerDOM.ElementNode = { + ...elseBranch.element, + props: elseBranch.element.props.filter(prop => prop !== elseBranch.directive), + }; + + const branch = createIfBranch(branchNode, elseBranch.directive); + if (comments.length) { + branch.children = [...comments, ...branch.children]; + } + + ifNode.branches.push(branch); + comments = []; + end = i; + continue; + } + break; + } + + // fallback if no additional branch + if (end === start) { + return; + } + + return { node: ifNode, end }; +} + +function createIfBranch(node: CompilerDOM.ElementNode, dir: CompilerDOM.DirectiveNode): CompilerDOM.IfBranchNode { + const isTemplateIf = node.tagType === CompilerDOM.ElementTypes.TEMPLATE; + return { + type: CompilerDOM.NodeTypes.IF_BRANCH, + loc: node.loc, + condition: dir.name === 'else' ? undefined : dir.exp, + children: isTemplateIf && !CompilerDOM.findDir(node, 'for') ? node.children : [node], + userKey: CompilerDOM.findProp(node, 'key'), + isTemplateIf, + }; +} + +function getVElseDirective(node: CompilerDOM.TemplateChildNode) { + if (node.type !== CompilerDOM.NodeTypes.ELEMENT) { + return; + } + const directive = node.props.find( + (prop): prop is CompilerDOM.DirectiveNode => + prop.type === CompilerDOM.NodeTypes.DIRECTIVE + && (prop.name === 'else-if' || prop.name === 'else'), + ); + if (directive) { + return { + element: node, + directive, + }; + } +} + +function isTemplateChildNode( + node: CompilerDOM.TemplateChildNode | CompilerDOM.SimpleExpressionNode, +): node is CompilerDOM.TemplateChildNode { + return node.type !== CompilerDOM.NodeTypes.SIMPLE_EXPRESSION; +} diff --git a/packages/language-core/lib/codegen/template/templateChild.ts b/packages/language-core/lib/codegen/template/templateChild.ts index 9d0d4211fd..2994ae968b 100644 --- a/packages/language-core/lib/codegen/template/templateChild.ts +++ b/packages/language-core/lib/codegen/template/templateChild.ts @@ -181,7 +181,7 @@ export function getVForNode(node: CompilerDOM.ElementNode) { } } -function getVIfNode(node: CompilerDOM.ElementNode) { +export function getVIfNode(node: CompilerDOM.ElementNode) { const ifDirective = node.props.find( (prop): prop is CompilerDOM.DirectiveNode => prop.type === CompilerDOM.NodeTypes.DIRECTIVE diff --git a/test-workspace/tsc/passedFixtures/vue3/#5492/main.vue b/test-workspace/tsc/passedFixtures/vue3/#5492/main.vue new file mode 100644 index 0000000000..89d4bdc77e --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3/#5492/main.vue @@ -0,0 +1,19 @@ + + +