Skip to content

Commit

Permalink
fix(language-core): do not generate element for <template> with `v-…
Browse files Browse the repository at this point in the history
…slot` (vuejs#5077)
  • Loading branch information
KazariEX authored Dec 31, 2024
1 parent 74bd7de commit 31c6995
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 124 deletions.
2 changes: 1 addition & 1 deletion packages/language-core/lib/codegen/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
: T extends (...args: any) => any ? T
: (_: {}${strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates ? '' : ' & Record<string, unknown>'} } };
function __VLS_elementAsFunction<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
function __VLS_normalizeSlot<S>(s: S): S extends () => infer R ? (props: {}) => R : S;
function __VLS_tryAsConstant<const T>(t: T): T;
Expand Down
3 changes: 0 additions & 3 deletions packages/language-core/lib/codegen/template/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
expVar: string;
varName: string;
}[] = [];
const hasSlotElements = new Set<CompilerDOM.ElementNode>();;
const blockConditions: string[] = [];
const scopedClasses: {
source: string;
Expand All @@ -136,7 +135,6 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
specialVars,
accessExternalVariables,
lastGenericComment,
hasSlotElements,
blockConditions,
scopedClasses,
emptyClassOffsets,
Expand All @@ -146,7 +144,6 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
inheritedAttrVars,
templateRefs,
currentComponent: undefined as {
node: CompilerDOM.ElementNode;
ctxVar: string;
used: boolean;
} | undefined,
Expand Down
121 changes: 7 additions & 114 deletions packages/language-core/lib/codegen/template/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { camelize, capitalize } from '@vue/shared';
import type { Code, VueCodeInformation } from '../../types';
import { getSlotsPropertyName, hyphenateTag } from '../../utils/shared';
import { createVBindShorthandInlayHintInfo } from '../inlayHints';
import { collectVars, createTsAst, endOfLine, newLine, normalizeAttributeValue, variableNameRegex, wrapWith } from '../utils';
import { endOfLine, newLine, normalizeAttributeValue, variableNameRegex, wrapWith } from '../utils';
import { generateCamelized } from '../utils/camelized';
import type { TemplateCodegenContext } from './context';
import { generateElementChildren } from './elementChildren';
Expand All @@ -12,10 +12,9 @@ import { generateElementEvents } from './elementEvents';
import { type FailedPropExpression, generateElementProps } from './elementProps';
import type { TemplateCodegenOptions } from './index';
import { generateInterpolation } from './interpolation';
import { generateObjectProperty } from './objectProperty';
import { generatePropertyAccess } from './propertyAccess';
import { collectStyleScopedClassReferences } from './styleScopedClasses';
import { generateTemplateChild } from './templateChild';
import { generateVSlot } from './vSlot';

const colonReg = /:/g;

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

ctx.currentComponent = {
node,
ctxVar: var_defineComponentCtx,
used: false
};
Expand Down Expand Up @@ -285,10 +283,10 @@ export function* generateComponent(

const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (slotDir) {
yield* generateComponentSlot(options, ctx, node, slotDir);
yield* generateVSlot(options, ctx, node, slotDir);
}
else {
yield* generateElementChildren(options, ctx, node);
yield* generateElementChildren(options, ctx, node, true);
}

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

yield `__VLS_elementAsFunction(__VLS_intrinsicElements`;
yield `__VLS_asFunctionalElement(__VLS_intrinsicElements`;
yield* generatePropertyAccess(
options,
ctx,
Expand Down Expand Up @@ -351,17 +349,11 @@ export function* generateElement(
ctx.singleRootElType = `typeof __VLS_nativeElements['${node.tag}']`;
}

const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (slotDir && ctx.currentComponent) {
yield* generateComponentSlot(options, ctx, node, slotDir);
}
else {
yield* generateElementChildren(options, ctx, node);
}

if (hasVBindAttrs(options, ctx, node)) {
ctx.inheritedAttrVars.add(`__VLS_intrinsicElements.${node.tag}`);
}

yield* generateElementChildren(options, ctx, node);
}

function* generateFailedPropExps(
Expand Down Expand Up @@ -479,105 +471,6 @@ function* generateComponentGeneric(
ctx.lastGenericComment = undefined;
}

function* generateComponentSlot(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
slotDir: CompilerDOM.DirectiveNode
): Generator<Code> {
yield `{${newLine}`;
if (ctx.currentComponent) {
ctx.currentComponent.used = true;
ctx.hasSlotElements.add(ctx.currentComponent.node);
}
const slotBlockVars: string[] = [];
yield `const {`;
if (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) {
yield* generateObjectProperty(
options,
ctx,
slotDir.arg.loc.source,
slotDir.arg.loc.start.offset,
slotDir.arg.isStatic ? ctx.codeFeatures.withoutHighlight : ctx.codeFeatures.all,
slotDir.arg.loc,
false,
true
);
}
else {
yield* wrapWith(
slotDir.loc.start.offset,
slotDir.loc.start.offset + (slotDir.rawName?.length ?? 0),
ctx.codeFeatures.withoutHighlightAndCompletion,
`default`
);
}
yield `: __VLS_thisSlot } = ${ctx.currentComponent!.ctxVar}.slots!${endOfLine}`;

if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
const slotAst = createTsAst(options.ts, slotDir, `(${slotDir.exp.content}) => {}`);
collectVars(options.ts, slotAst, slotAst, slotBlockVars);
if (!slotDir.exp.content.includes(':')) {
yield `const [`;
yield [
slotDir.exp.content,
'template',
slotDir.exp.loc.start.offset,
ctx.codeFeatures.all,
];
yield `] = __VLS_getSlotParams(__VLS_thisSlot)${endOfLine}`;
}
else {
yield `const `;
yield [
slotDir.exp.content,
'template',
slotDir.exp.loc.start.offset,
ctx.codeFeatures.all,
];
yield ` = __VLS_getSlotParam(__VLS_thisSlot)${endOfLine}`;
}
}

for (const varName of slotBlockVars) {
ctx.addLocalVariable(varName);
}

yield* ctx.resetDirectiveComments('end of slot children start');

let prev: CompilerDOM.TemplateChildNode | undefined;
for (const childNode of node.children) {
yield* generateTemplateChild(options, ctx, childNode, prev);
prev = childNode;
}

for (const varName of slotBlockVars) {
ctx.removeLocalVariable(varName);
}
let isStatic = true;
if (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
isStatic = slotDir.arg.isStatic;
}
if (isStatic && slotDir && !slotDir.arg) {
yield `${ctx.currentComponent!.ctxVar}.slots!['`;
yield [
'',
'template',
slotDir.loc.start.offset + (
slotDir.loc.source.startsWith('#')
? '#'.length : slotDir.loc.source.startsWith('v-slot:')
? 'v-slot:'.length
: 0
),
ctx.codeFeatures.completion,
];
yield `'/* empty slot name completion */]${newLine}`;
}

yield* ctx.generateAutoImportCompletion();
yield `}${newLine}`;
}

function* generateReferencesForElements(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { generateTemplateChild } from './templateChild';
export function* generateElementChildren(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode
node: CompilerDOM.ElementNode,
isDefaultSlot: boolean = false
): Generator<Code> {
yield* ctx.resetDirectiveComments('end of element children start');
let prev: CompilerDOM.TemplateChildNode | undefined;
Expand All @@ -21,10 +22,9 @@ export function* generateElementChildren(
// fix https://github.com/vuejs/language-tools/issues/932
if (
ctx.currentComponent
&& !ctx.hasSlotElements.has(node)
&& isDefaultSlot
&& node.children.length
&& node.tagType !== CompilerDOM.ElementTypes.ELEMENT
&& node.tagType !== CompilerDOM.ElementTypes.TEMPLATE
&& node.tagType === CompilerDOM.ElementTypes.COMPONENT
) {
ctx.currentComponent.used = true;
yield `${ctx.currentComponent.ctxVar}.slots!.`;
Expand Down
13 changes: 11 additions & 2 deletions packages/language-core/lib/codegen/template/templateChild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { generateInterpolation } from './interpolation';
import { generateSlotOutlet } from './slotOutlet';
import { generateVFor } from './vFor';
import { generateVIf } from './vIf';
import { generateVSlot } from './vSlot';

// @ts-ignore
const transformContext: CompilerDOM.TransformContext = {
Expand Down Expand Up @@ -83,9 +84,17 @@ export function* generateTemplateChild(
else if (vIfNode) {
yield* generateVIf(options, ctx, vIfNode);
}
else if (node.tagType === CompilerDOM.ElementTypes.SLOT) {
yield* generateSlotOutlet(options, ctx, node);
}
else {
if (node.tagType === CompilerDOM.ElementTypes.SLOT) {
yield* generateSlotOutlet(options, ctx, node);
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (
node.tagType === CompilerDOM.ElementTypes.TEMPLATE
&& ctx.currentComponent
&& slotDir
) {
yield* generateVSlot(options, ctx, node, slotDir);
}
else if (
node.tagType === CompilerDOM.ElementTypes.ELEMENT
Expand Down
109 changes: 109 additions & 0 deletions packages/language-core/lib/codegen/template/vSlot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import * as CompilerDOM from '@vue/compiler-dom';
import type { Code } from '../../types';
import { collectVars, createTsAst, endOfLine, newLine, wrapWith } from '../utils';
import type { TemplateCodegenContext } from './context';
import type { TemplateCodegenOptions } from './index';
import { generateObjectProperty } from './objectProperty';
import { generateTemplateChild } from './templateChild';

export function* generateVSlot(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
slotDir: CompilerDOM.DirectiveNode
): Generator<Code> {
if (!ctx.currentComponent) {
return;
}
ctx.currentComponent.used = true;
const slotBlockVars: string[] = [];
yield `{${newLine}`;

yield `const { `;
if (slotDir.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) {
yield* generateObjectProperty(
options,
ctx,
slotDir.arg.loc.source,
slotDir.arg.loc.start.offset,
slotDir.arg.isStatic ? ctx.codeFeatures.withoutHighlight : ctx.codeFeatures.all,
slotDir.arg.loc,
false,
true
);
}
else {
yield* wrapWith(
slotDir.loc.start.offset,
slotDir.loc.start.offset + (slotDir.rawName?.length ?? 0),
ctx.codeFeatures.withoutHighlightAndCompletion,
`default`
);
}
yield `: __VLS_thisSlot } = ${ctx.currentComponent.ctxVar}.slots!${endOfLine}`;

if (slotDir.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
const slotAst = createTsAst(options.ts, slotDir, `(${slotDir.exp.content}) => {}`);
collectVars(options.ts, slotAst, slotAst, slotBlockVars);
if (!slotDir.exp.content.includes(':')) {
yield `const [`;
yield [
slotDir.exp.content,
'template',
slotDir.exp.loc.start.offset,
ctx.codeFeatures.all,
];
yield `] = __VLS_getSlotParams(__VLS_thisSlot)${endOfLine}`;
}
else {
yield `const `;
yield [
slotDir.exp.content,
'template',
slotDir.exp.loc.start.offset,
ctx.codeFeatures.all,
];
yield ` = __VLS_getSlotParam(__VLS_thisSlot)${endOfLine}`;
}
}

for (const varName of slotBlockVars) {
ctx.addLocalVariable(varName);
}

yield* ctx.resetDirectiveComments('end of slot children start');

let prev: CompilerDOM.TemplateChildNode | undefined;
for (const childNode of node.children) {
yield* generateTemplateChild(options, ctx, childNode, prev);
prev = childNode;
}

for (const varName of slotBlockVars) {
ctx.removeLocalVariable(varName);
}

let isStatic = true;
if (slotDir.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
isStatic = slotDir.arg.isStatic;
}
if (isStatic && !slotDir.arg) {
yield `${ctx.currentComponent.ctxVar}.slots!['`;
yield [
'',
'template',
slotDir.loc.start.offset + (
slotDir.loc.source.startsWith('#')
? '#'.length
: slotDir.loc.source.startsWith('v-slot:')
? 'v-slot:'.length
: 0
),
ctx.codeFeatures.completion,
];
yield `'/* empty slot name completion */]${endOfLine}`;
}

yield* ctx.generateAutoImportCompletion();
yield `}${newLine}`;
}

0 comments on commit 31c6995

Please sign in to comment.