Skip to content

Commit 5548db0

Browse files
fix(language-core): enhance getVIfNode to support v-else-if directives (#5765)
Co-authored-by: Johnson Chu <[email protected]>
1 parent 6db3b25 commit 5548db0

File tree

3 files changed

+108
-9
lines changed

3 files changed

+108
-9
lines changed
Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import type * as CompilerDOM from '@vue/compiler-dom';
1+
import * as CompilerDOM from '@vue/compiler-dom';
22
import type { Code } from '../../types';
33
import type { TemplateCodegenContext } from './context';
44
import type { TemplateCodegenOptions } from './index';
5-
import { generateTemplateChild } from './templateChild';
5+
import { generateTemplateChild, getVIfNode } from './templateChild';
66

77
export function* generateElementChildren(
88
options: TemplateCodegenOptions,
@@ -11,8 +11,92 @@ export function* generateElementChildren(
1111
enterNode = true,
1212
): Generator<Code> {
1313
const endScope = ctx.startScope();
14-
for (const childNode of children) {
15-
yield* generateTemplateChild(options, ctx, childNode, enterNode);
14+
for (let i = 0; i < children.length; i++) {
15+
let current = children[i];
16+
[current, i] = normalizeIfBranch(children, i);
17+
yield* generateTemplateChild(options, ctx, current, enterNode);
1618
}
1719
yield* endScope();
1820
}
21+
22+
function normalizeIfBranch(
23+
children: (CompilerDOM.TemplateChildNode | CompilerDOM.SimpleExpressionNode)[],
24+
start: number,
25+
): [node: typeof children[number], end: number] {
26+
const first = children[start]!;
27+
if (first.type !== CompilerDOM.NodeTypes.ELEMENT) {
28+
return [first, start];
29+
}
30+
31+
const ifNode = getVIfNode(first);
32+
if (!ifNode) {
33+
return [first, start];
34+
}
35+
36+
let end = start;
37+
let comments: CompilerDOM.CommentNode[] = [];
38+
39+
for (let i = start + 1; i < children.length; i++) {
40+
const sibling = children[i]!;
41+
if (sibling.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
42+
continue;
43+
}
44+
if (sibling.type === CompilerDOM.NodeTypes.COMMENT) {
45+
comments.push(sibling);
46+
continue;
47+
}
48+
if (sibling.type === CompilerDOM.NodeTypes.TEXT && !sibling.content.trim()) {
49+
continue;
50+
}
51+
const elseBranch = getVElseDirective(sibling);
52+
if (elseBranch) {
53+
const branchNode: CompilerDOM.ElementNode = {
54+
...elseBranch.element,
55+
props: elseBranch.element.props.filter(prop => prop !== elseBranch.directive),
56+
};
57+
58+
const branch = createIfBranch(branchNode, elseBranch.directive);
59+
if (comments.length) {
60+
branch.children = [...comments, ...branch.children];
61+
}
62+
63+
ifNode.branches.push(branch);
64+
comments = [];
65+
end = i;
66+
continue;
67+
}
68+
break;
69+
}
70+
71+
return [ifNode, end];
72+
}
73+
74+
// source: https://github.com/vuejs/core/blob/25ebe3a42cd80ac0256355c2740a0258cdd7419d/packages/compiler-core/src/transforms/vIf.ts#L207
75+
function createIfBranch(node: CompilerDOM.ElementNode, dir: CompilerDOM.DirectiveNode): CompilerDOM.IfBranchNode {
76+
const isTemplateIf = node.tagType === CompilerDOM.ElementTypes.TEMPLATE;
77+
return {
78+
type: CompilerDOM.NodeTypes.IF_BRANCH,
79+
loc: node.loc,
80+
condition: dir.name === 'else' ? undefined : dir.exp,
81+
children: isTemplateIf && !CompilerDOM.findDir(node, 'for') ? node.children : [node],
82+
userKey: CompilerDOM.findProp(node, 'key'),
83+
isTemplateIf,
84+
};
85+
}
86+
87+
function getVElseDirective(node: CompilerDOM.TemplateChildNode) {
88+
if (node.type !== CompilerDOM.NodeTypes.ELEMENT) {
89+
return;
90+
}
91+
const directive = node.props.find(
92+
(prop): prop is CompilerDOM.DirectiveNode =>
93+
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
94+
&& (prop.name === 'else-if' || prop.name === 'else'),
95+
);
96+
if (directive) {
97+
return {
98+
element: node,
99+
directive,
100+
};
101+
}
102+
}

packages/language-core/lib/codegen/template/templateChild.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,9 @@ export function* generateTemplateChild(
5252
}
5353
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
5454
const vForNode = getVForNode(node);
55-
const vIfNode = getVIfNode(node);
5655
if (vForNode) {
5756
yield* generateVFor(options, ctx, vForNode);
5857
}
59-
else if (vIfNode) {
60-
yield* generateVIf(options, ctx, vIfNode);
61-
}
6258
else if (node.tagType === CompilerDOM.ElementTypes.SLOT) {
6359
yield* generateSlotOutlet(options, ctx, node);
6460
}
@@ -181,7 +177,7 @@ export function getVForNode(node: CompilerDOM.ElementNode) {
181177
}
182178
}
183179

184-
function getVIfNode(node: CompilerDOM.ElementNode) {
180+
export function getVIfNode(node: CompilerDOM.ElementNode) {
185181
const ifDirective = node.props.find(
186182
(prop): prop is CompilerDOM.DirectiveNode =>
187183
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script setup lang="ts">
2+
import { defineComponent } from 'vue';
3+
const Comp = defineComponent({})
4+
const cond1: true | 'text' = Math.random() > 0.5 ? true : 'text'
5+
const cond2 = true
6+
</script>
7+
8+
<template>
9+
<Comp>
10+
<template #default v-if="cond1">1</template>
11+
<template #default v-else-if="cond2">2</template>
12+
13+
<div v-if="cond1 === true"></div>
14+
<div v-else-if="cond1.length"></div>
15+
16+
<template #default v-if="cond1 === true">1</template>
17+
<template #default v-else-if="cond1.length">2</template>
18+
</Comp>
19+
</template>

0 commit comments

Comments
 (0)