Skip to content

Commit 32e455f

Browse files
committed
fix(language-core): correct codegen of native element refs
1 parent 72c7059 commit 32e455f

File tree

11 files changed

+36
-99
lines changed

11 files changed

+36
-99
lines changed

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,7 @@ export function generateGlobalTypes({
4545
const __VLS_unref: typeof import('${lib}').unref;
4646
const __VLS_placeholder: any;
4747
48-
const __VLS_nativeElements = {
49-
...{} as SVGElementTagNameMap,
50-
...{} as HTMLElementTagNameMap,
51-
};
52-
48+
type __VLS_NativeElements = __VLS_SpreadMerge<SVGElementTagNameMap, HTMLElementTagNameMap>;
5349
type __VLS_IntrinsicElements = ${(
5450
target >= 3.3
5551
? `import('${lib}/jsx-runtime').JSX.IntrinsicElements;`
@@ -68,7 +64,7 @@ export function generateGlobalTypes({
6864
type __VLS_GlobalDirectives = import('${lib}').GlobalDirectives;
6965
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
7066
type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
71-
type __VLS_unknownDirective = (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
67+
type __VLS_SpreadMerge<A, B> = Omit<A, keyof B> & B;
7268
type __VLS_WithComponent<N0 extends string, LocalComponents, Self, N1 extends string, N2 extends string, N3 extends string> =
7369
N1 extends keyof LocalComponents ? N1 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N1] } :
7470
N2 extends keyof LocalComponents ? N2 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N2] } :
@@ -152,7 +148,7 @@ export function generateGlobalTypes({
152148
? NonNullable<T['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']>
153149
: T extends (...args: any) => any
154150
? T
155-
: __VLS_unknownDirective;
151+
: (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
156152
function __VLS_makeOptional<T>(t: T): { [K in keyof T]?: T[K] };
157153
function __VLS_asFunctionalComponent<T, K = T extends new (...args: any) => any ? InstanceType<T> : unknown>(t: T, instance?: K):
158154
T extends new (...args: any) => any

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
6969
const bindingAttrLocs: CompilerDOM.SourceLocation[] = [];
7070
const inheritedAttrVars = new Set<string>();
7171
const templateRefs = new Map<string, {
72-
varName: string;
72+
typeExp: string;
7373
offset: number;
7474
}>();
7575

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

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const colonReg = /:/g;
2121
export function* generateComponent(
2222
options: TemplateCodegenOptions,
2323
ctx: TemplateCodegenContext,
24-
node: CompilerDOM.ElementNode
24+
node: CompilerDOM.ElementNode,
25+
isVForChild: boolean
2526
): Generator<Code> {
2627
const tagOffsets = [node.loc.start.offset + options.template.content.slice(node.loc.start.offset).indexOf(node.tag)];
2728
if (!node.isSelfClosing && options.template.lang === 'html') {
@@ -255,12 +256,6 @@ export function* generateComponent(
255256
yield* generateElementEvents(options, ctx, node, componentFunctionalVar, componentVNodeVar, componentCtxVar);
256257
yield* generateElementDirectives(options, ctx, node);
257258

258-
if (hasVBindAttrs(options, ctx, node)) {
259-
const attrsVar = ctx.getInternalVariable();
260-
ctx.inheritedAttrVars.add(attrsVar);
261-
yield `let ${attrsVar}!: Parameters<typeof ${componentFunctionalVar}>[0];\n`;
262-
}
263-
264259
const [refName, offset] = yield* generateElementReference(options, ctx, node);
265260
const isRootNode = node === ctx.singleRootNode;
266261

@@ -269,25 +264,28 @@ export function* generateComponent(
269264
ctx.currentComponent.used = true;
270265

271266
yield `var ${componentInstanceVar} = {} as (Parameters<NonNullable<typeof ${componentCtxVar}['expose']>>[0] | null)`;
272-
if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL
273-
&& node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION
274-
&& node.codegenNode.props.properties.some(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for')
275-
) {
267+
if (isVForChild) {
276268
yield `[]`;
277269
}
278270
yield `${endOfLine}`;
279271

280-
if (refName) {
272+
if (refName && offset) {
281273
ctx.templateRefs.set(refName, {
282-
varName: ctx.getHoistVariable(componentInstanceVar),
283-
offset: offset!
274+
typeExp: `typeof ${ctx.getHoistVariable(componentInstanceVar)}`,
275+
offset
284276
});
285277
}
286278
if (isRootNode) {
287279
ctx.singleRootElType = `NonNullable<typeof ${componentInstanceVar}>['$el']`;
288280
}
289281
}
290282

283+
if (hasVBindAttrs(options, ctx, node)) {
284+
const attrsVar = ctx.getInternalVariable();
285+
ctx.inheritedAttrVars.add(attrsVar);
286+
yield `let ${attrsVar}!: Parameters<typeof ${componentFunctionalVar}>[0]${endOfLine}`;
287+
}
288+
291289
collectStyleScopedClassReferences(options, ctx, node);
292290

293291
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
@@ -358,18 +356,18 @@ export function* generateElement(
358356
yield* generateElementDirectives(options, ctx, node);
359357

360358
const [refName, offset] = yield* generateElementReference(options, ctx, node);
361-
if (refName) {
362-
let element = `__VLS_nativeElements['${node.tag}']`;
359+
if (refName && offset) {
360+
let typeExp = `__VLS_NativeElements['${node.tag}']`;
363361
if (isVForChild) {
364-
element = `[${element}]`;
362+
typeExp += `[]`;
365363
}
366364
ctx.templateRefs.set(refName, {
367-
varName: element,
368-
offset: offset!
365+
typeExp,
366+
offset
369367
});
370368
}
371369
if (ctx.singleRootNode === node) {
372-
ctx.singleRootElType = `typeof __VLS_nativeElements['${node.tag}']`;
370+
ctx.singleRootElType = `__VLS_NativeElements['${node.tag}']`;
373371
}
374372

375373
if (hasVBindAttrs(options, ctx, node)) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,15 @@ function* generateTemplateRefs(
133133
ctx: TemplateCodegenContext
134134
): Generator<Code> {
135135
yield `type __VLS_TemplateRefs = {${newLine}`;
136-
for (const [name, { varName, offset }] of ctx.templateRefs) {
136+
for (const [name, { typeExp, offset }] of ctx.templateRefs) {
137137
yield* generateObjectProperty(
138138
options,
139139
ctx,
140140
name,
141141
offset,
142142
ctx.codeFeatures.navigationAndCompletion
143143
);
144-
yield `: typeof ${varName},${newLine}`;
144+
yield `: ${typeExp},${newLine}`;
145145
}
146146
yield `}${endOfLine}`;
147147
return `__VLS_TemplateRefs`;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export function* generateTemplateChild(
113113
}
114114
else {
115115
const { currentComponent } = ctx;
116-
yield* generateComponent(options, ctx, node);
116+
yield* generateComponent(options, ctx, node, isVForChild);
117117
ctx.currentComponent = currentComponent;
118118
}
119119
}

test-workspace/tsc/passedFixtures/vue3/#4777/template-ref.vue

Lines changed: 0 additions & 16 deletions
This file was deleted.

test-workspace/tsc/passedFixtures/vue3/templateRef/components.d.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { useTemplateRef } from 'vue';
33
import { exactType } from '../../shared';
4-
import TemplateRef from './template-ref.vue';
4+
import TemplateRefs from './template-refs.vue';
55
66
function Comp() {
77
const foo = useTemplateRef('templateRef');
@@ -11,9 +11,9 @@ function Comp() {
1111
</script>
1212

1313
<template>
14-
<TemplateRef ref="templateRef" />
14+
<TemplateRefs ref="templateRefs" />
1515

16-
{{ exactType($refs.templateRef?.$refs.generic?.foo, {} as (1 | undefined)) }}
16+
{{ exactType($refs.templateRefs?.$refs.generic?.foo, {} as (1 | undefined)) }}
1717

1818
<Comp />
1919
</template>

test-workspace/tsc/passedFixtures/vue3/templateRef/template-ref.vue renamed to test-workspace/tsc/passedFixtures/vue3/templateRef/template-refs.vue

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import { useTemplateRef } from 'vue';
33
import { exactType } from '../../shared';
4+
import Generic from './generic.vue';
45
56
const comp1 = useTemplateRef('generic');
67
if (comp1.value) {
@@ -14,20 +15,23 @@ if (comp2.value) {
1415
1516
const comp3 = useTemplateRef('native');
1617
if (comp3.value) {
17-
exactType(comp3.value.href, {} as string);
18+
exactType(comp3.value, {} as HTMLAnchorElement);
1819
}
1920
2021
const comp4 = useTemplateRef('v-for-native');
2122
if (comp4.value) {
22-
exactType(comp4.value[0]?.href, {} as string | undefined);
23+
exactType(comp4.value, {} as HTMLAnchorElement[]);
2324
}
25+
26+
// @ts-expect-error
27+
useTemplateRef('unknown');
2428
</script>
2529

2630
<template>
27-
<GenericGlobal ref="generic" :foo="1"/>
31+
<Generic ref="generic" :foo="1"/>
2832
{{ exactType(comp1?.foo, {} as 1 | undefined) }}
2933

30-
<GenericGlobal v-for="i in 4" ref="v-for-generic" :foo="i"/>
34+
<Generic v-for="i in 4" ref="v-for-generic" :foo="i"/>
3135
{{ exactType(comp2?.[0]?.foo, {} as number | undefined) }}
3236

3337
<a ref="native"></a>

0 commit comments

Comments
 (0)