Skip to content

Commit 31c6995

Browse files
authored
fix(language-core): do not generate element for <template> with v-slot (#5077)
1 parent 74bd7de commit 31c6995

File tree

6 files changed

+132
-124
lines changed

6 files changed

+132
-124
lines changed

packages/language-core/lib/codegen/globalTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates
144144
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
145145
: T extends (...args: any) => any ? T
146146
: (_: {}${strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates ? '' : ' & Record<string, unknown>'} } };
147-
function __VLS_elementAsFunction<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
147+
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
148148
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
149149
function __VLS_normalizeSlot<S>(s: S): S extends () => infer R ? (props: {}) => R : S;
150150
function __VLS_tryAsConstant<const T>(t: T): T;

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
116116
expVar: string;
117117
varName: string;
118118
}[] = [];
119-
const hasSlotElements = new Set<CompilerDOM.ElementNode>();;
120119
const blockConditions: string[] = [];
121120
const scopedClasses: {
122121
source: string;
@@ -136,7 +135,6 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
136135
specialVars,
137136
accessExternalVariables,
138137
lastGenericComment,
139-
hasSlotElements,
140138
blockConditions,
141139
scopedClasses,
142140
emptyClassOffsets,
@@ -146,7 +144,6 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
146144
inheritedAttrVars,
147145
templateRefs,
148146
currentComponent: undefined as {
149-
node: CompilerDOM.ElementNode;
150147
ctxVar: string;
151148
used: boolean;
152149
} | undefined,

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

Lines changed: 7 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { camelize, capitalize } from '@vue/shared';
33
import type { Code, VueCodeInformation } from '../../types';
44
import { getSlotsPropertyName, hyphenateTag } from '../../utils/shared';
55
import { createVBindShorthandInlayHintInfo } from '../inlayHints';
6-
import { collectVars, createTsAst, endOfLine, newLine, normalizeAttributeValue, variableNameRegex, wrapWith } from '../utils';
6+
import { endOfLine, newLine, normalizeAttributeValue, variableNameRegex, wrapWith } from '../utils';
77
import { generateCamelized } from '../utils/camelized';
88
import type { TemplateCodegenContext } from './context';
99
import { generateElementChildren } from './elementChildren';
@@ -12,10 +12,9 @@ import { generateElementEvents } from './elementEvents';
1212
import { type FailedPropExpression, generateElementProps } from './elementProps';
1313
import type { TemplateCodegenOptions } from './index';
1414
import { generateInterpolation } from './interpolation';
15-
import { generateObjectProperty } from './objectProperty';
1615
import { generatePropertyAccess } from './propertyAccess';
1716
import { collectStyleScopedClassReferences } from './styleScopedClasses';
18-
import { generateTemplateChild } from './templateChild';
17+
import { generateVSlot } from './vSlot';
1918

2019
const colonReg = /:/g;
2120

@@ -43,7 +42,6 @@ export function* generateComponent(
4342
const isComponentTag = node.tag.toLowerCase() === 'component';
4443

4544
ctx.currentComponent = {
46-
node,
4745
ctxVar: var_defineComponentCtx,
4846
used: false
4947
};
@@ -285,10 +283,10 @@ export function* generateComponent(
285283

286284
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
287285
if (slotDir) {
288-
yield* generateComponentSlot(options, ctx, node, slotDir);
286+
yield* generateVSlot(options, ctx, node, slotDir);
289287
}
290288
else {
291-
yield* generateElementChildren(options, ctx, node);
289+
yield* generateElementChildren(options, ctx, node, true);
292290
}
293291

294292
if (ctx.currentComponent.used) {
@@ -308,7 +306,7 @@ export function* generateElement(
308306
: undefined;
309307
const failedPropExps: FailedPropExpression[] = [];
310308

311-
yield `__VLS_elementAsFunction(__VLS_intrinsicElements`;
309+
yield `__VLS_asFunctionalElement(__VLS_intrinsicElements`;
312310
yield* generatePropertyAccess(
313311
options,
314312
ctx,
@@ -351,17 +349,11 @@ export function* generateElement(
351349
ctx.singleRootElType = `typeof __VLS_nativeElements['${node.tag}']`;
352350
}
353351

354-
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
355-
if (slotDir && ctx.currentComponent) {
356-
yield* generateComponentSlot(options, ctx, node, slotDir);
357-
}
358-
else {
359-
yield* generateElementChildren(options, ctx, node);
360-
}
361-
362352
if (hasVBindAttrs(options, ctx, node)) {
363353
ctx.inheritedAttrVars.add(`__VLS_intrinsicElements.${node.tag}`);
364354
}
355+
356+
yield* generateElementChildren(options, ctx, node);
365357
}
366358

367359
function* generateFailedPropExps(
@@ -479,105 +471,6 @@ function* generateComponentGeneric(
479471
ctx.lastGenericComment = undefined;
480472
}
481473

482-
function* generateComponentSlot(
483-
options: TemplateCodegenOptions,
484-
ctx: TemplateCodegenContext,
485-
node: CompilerDOM.ElementNode,
486-
slotDir: CompilerDOM.DirectiveNode
487-
): Generator<Code> {
488-
yield `{${newLine}`;
489-
if (ctx.currentComponent) {
490-
ctx.currentComponent.used = true;
491-
ctx.hasSlotElements.add(ctx.currentComponent.node);
492-
}
493-
const slotBlockVars: string[] = [];
494-
yield `const {`;
495-
if (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) {
496-
yield* generateObjectProperty(
497-
options,
498-
ctx,
499-
slotDir.arg.loc.source,
500-
slotDir.arg.loc.start.offset,
501-
slotDir.arg.isStatic ? ctx.codeFeatures.withoutHighlight : ctx.codeFeatures.all,
502-
slotDir.arg.loc,
503-
false,
504-
true
505-
);
506-
}
507-
else {
508-
yield* wrapWith(
509-
slotDir.loc.start.offset,
510-
slotDir.loc.start.offset + (slotDir.rawName?.length ?? 0),
511-
ctx.codeFeatures.withoutHighlightAndCompletion,
512-
`default`
513-
);
514-
}
515-
yield `: __VLS_thisSlot } = ${ctx.currentComponent!.ctxVar}.slots!${endOfLine}`;
516-
517-
if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
518-
const slotAst = createTsAst(options.ts, slotDir, `(${slotDir.exp.content}) => {}`);
519-
collectVars(options.ts, slotAst, slotAst, slotBlockVars);
520-
if (!slotDir.exp.content.includes(':')) {
521-
yield `const [`;
522-
yield [
523-
slotDir.exp.content,
524-
'template',
525-
slotDir.exp.loc.start.offset,
526-
ctx.codeFeatures.all,
527-
];
528-
yield `] = __VLS_getSlotParams(__VLS_thisSlot)${endOfLine}`;
529-
}
530-
else {
531-
yield `const `;
532-
yield [
533-
slotDir.exp.content,
534-
'template',
535-
slotDir.exp.loc.start.offset,
536-
ctx.codeFeatures.all,
537-
];
538-
yield ` = __VLS_getSlotParam(__VLS_thisSlot)${endOfLine}`;
539-
}
540-
}
541-
542-
for (const varName of slotBlockVars) {
543-
ctx.addLocalVariable(varName);
544-
}
545-
546-
yield* ctx.resetDirectiveComments('end of slot children start');
547-
548-
let prev: CompilerDOM.TemplateChildNode | undefined;
549-
for (const childNode of node.children) {
550-
yield* generateTemplateChild(options, ctx, childNode, prev);
551-
prev = childNode;
552-
}
553-
554-
for (const varName of slotBlockVars) {
555-
ctx.removeLocalVariable(varName);
556-
}
557-
let isStatic = true;
558-
if (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
559-
isStatic = slotDir.arg.isStatic;
560-
}
561-
if (isStatic && slotDir && !slotDir.arg) {
562-
yield `${ctx.currentComponent!.ctxVar}.slots!['`;
563-
yield [
564-
'',
565-
'template',
566-
slotDir.loc.start.offset + (
567-
slotDir.loc.source.startsWith('#')
568-
? '#'.length : slotDir.loc.source.startsWith('v-slot:')
569-
? 'v-slot:'.length
570-
: 0
571-
),
572-
ctx.codeFeatures.completion,
573-
];
574-
yield `'/* empty slot name completion */]${newLine}`;
575-
}
576-
577-
yield* ctx.generateAutoImportCompletion();
578-
yield `}${newLine}`;
579-
}
580-
581474
function* generateReferencesForElements(
582475
options: TemplateCodegenOptions,
583476
ctx: TemplateCodegenContext,

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import { generateTemplateChild } from './templateChild';
88
export function* generateElementChildren(
99
options: TemplateCodegenOptions,
1010
ctx: TemplateCodegenContext,
11-
node: CompilerDOM.ElementNode
11+
node: CompilerDOM.ElementNode,
12+
isDefaultSlot: boolean = false
1213
): Generator<Code> {
1314
yield* ctx.resetDirectiveComments('end of element children start');
1415
let prev: CompilerDOM.TemplateChildNode | undefined;
@@ -21,10 +22,9 @@ export function* generateElementChildren(
2122
// fix https://github.com/vuejs/language-tools/issues/932
2223
if (
2324
ctx.currentComponent
24-
&& !ctx.hasSlotElements.has(node)
25+
&& isDefaultSlot
2526
&& node.children.length
26-
&& node.tagType !== CompilerDOM.ElementTypes.ELEMENT
27-
&& node.tagType !== CompilerDOM.ElementTypes.TEMPLATE
27+
&& node.tagType === CompilerDOM.ElementTypes.COMPONENT
2828
) {
2929
ctx.currentComponent.used = true;
3030
yield `${ctx.currentComponent.ctxVar}.slots!.`;

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { generateInterpolation } from './interpolation';
88
import { generateSlotOutlet } from './slotOutlet';
99
import { generateVFor } from './vFor';
1010
import { generateVIf } from './vIf';
11+
import { generateVSlot } from './vSlot';
1112

1213
// @ts-ignore
1314
const transformContext: CompilerDOM.TransformContext = {
@@ -83,9 +84,17 @@ export function* generateTemplateChild(
8384
else if (vIfNode) {
8485
yield* generateVIf(options, ctx, vIfNode);
8586
}
87+
else if (node.tagType === CompilerDOM.ElementTypes.SLOT) {
88+
yield* generateSlotOutlet(options, ctx, node);
89+
}
8690
else {
87-
if (node.tagType === CompilerDOM.ElementTypes.SLOT) {
88-
yield* generateSlotOutlet(options, ctx, node);
91+
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
92+
if (
93+
node.tagType === CompilerDOM.ElementTypes.TEMPLATE
94+
&& ctx.currentComponent
95+
&& slotDir
96+
) {
97+
yield* generateVSlot(options, ctx, node, slotDir);
8998
}
9099
else if (
91100
node.tagType === CompilerDOM.ElementTypes.ELEMENT
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import * as CompilerDOM from '@vue/compiler-dom';
2+
import type { Code } from '../../types';
3+
import { collectVars, createTsAst, endOfLine, newLine, wrapWith } from '../utils';
4+
import type { TemplateCodegenContext } from './context';
5+
import type { TemplateCodegenOptions } from './index';
6+
import { generateObjectProperty } from './objectProperty';
7+
import { generateTemplateChild } from './templateChild';
8+
9+
export function* generateVSlot(
10+
options: TemplateCodegenOptions,
11+
ctx: TemplateCodegenContext,
12+
node: CompilerDOM.ElementNode,
13+
slotDir: CompilerDOM.DirectiveNode
14+
): Generator<Code> {
15+
if (!ctx.currentComponent) {
16+
return;
17+
}
18+
ctx.currentComponent.used = true;
19+
const slotBlockVars: string[] = [];
20+
yield `{${newLine}`;
21+
22+
yield `const { `;
23+
if (slotDir.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) {
24+
yield* generateObjectProperty(
25+
options,
26+
ctx,
27+
slotDir.arg.loc.source,
28+
slotDir.arg.loc.start.offset,
29+
slotDir.arg.isStatic ? ctx.codeFeatures.withoutHighlight : ctx.codeFeatures.all,
30+
slotDir.arg.loc,
31+
false,
32+
true
33+
);
34+
}
35+
else {
36+
yield* wrapWith(
37+
slotDir.loc.start.offset,
38+
slotDir.loc.start.offset + (slotDir.rawName?.length ?? 0),
39+
ctx.codeFeatures.withoutHighlightAndCompletion,
40+
`default`
41+
);
42+
}
43+
yield `: __VLS_thisSlot } = ${ctx.currentComponent.ctxVar}.slots!${endOfLine}`;
44+
45+
if (slotDir.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
46+
const slotAst = createTsAst(options.ts, slotDir, `(${slotDir.exp.content}) => {}`);
47+
collectVars(options.ts, slotAst, slotAst, slotBlockVars);
48+
if (!slotDir.exp.content.includes(':')) {
49+
yield `const [`;
50+
yield [
51+
slotDir.exp.content,
52+
'template',
53+
slotDir.exp.loc.start.offset,
54+
ctx.codeFeatures.all,
55+
];
56+
yield `] = __VLS_getSlotParams(__VLS_thisSlot)${endOfLine}`;
57+
}
58+
else {
59+
yield `const `;
60+
yield [
61+
slotDir.exp.content,
62+
'template',
63+
slotDir.exp.loc.start.offset,
64+
ctx.codeFeatures.all,
65+
];
66+
yield ` = __VLS_getSlotParam(__VLS_thisSlot)${endOfLine}`;
67+
}
68+
}
69+
70+
for (const varName of slotBlockVars) {
71+
ctx.addLocalVariable(varName);
72+
}
73+
74+
yield* ctx.resetDirectiveComments('end of slot children start');
75+
76+
let prev: CompilerDOM.TemplateChildNode | undefined;
77+
for (const childNode of node.children) {
78+
yield* generateTemplateChild(options, ctx, childNode, prev);
79+
prev = childNode;
80+
}
81+
82+
for (const varName of slotBlockVars) {
83+
ctx.removeLocalVariable(varName);
84+
}
85+
86+
let isStatic = true;
87+
if (slotDir.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
88+
isStatic = slotDir.arg.isStatic;
89+
}
90+
if (isStatic && !slotDir.arg) {
91+
yield `${ctx.currentComponent.ctxVar}.slots!['`;
92+
yield [
93+
'',
94+
'template',
95+
slotDir.loc.start.offset + (
96+
slotDir.loc.source.startsWith('#')
97+
? '#'.length
98+
: slotDir.loc.source.startsWith('v-slot:')
99+
? 'v-slot:'.length
100+
: 0
101+
),
102+
ctx.codeFeatures.completion,
103+
];
104+
yield `'/* empty slot name completion */]${endOfLine}`;
105+
}
106+
107+
yield* ctx.generateAutoImportCompletion();
108+
yield `}${newLine}`;
109+
}

0 commit comments

Comments
 (0)