Skip to content

Commit 306936b

Browse files
fix(language-core): hoist $refs type (#4763)
1 parent 950c9e1 commit 306936b

File tree

12 files changed

+81
-95
lines changed

12 files changed

+81
-95
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ declare function __VLS_asFunctionalComponent<T, K = T extends new (...args: any)
114114
: T extends (...args: any) => any ? T
115115
: (_: {}${strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates ? '' : ' & Record<string, unknown>'} } };
116116
declare function __VLS_elementAsFunction<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
117-
declare function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): Parameters<T>['length'] extends 2 ? [any] : [];
117+
declare function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
118118
declare function __VLS_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): NonNullable<__VLS_PickNotAny<
119119
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : any
120120
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any

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

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,6 @@ type __VLS_TypePropsToOption<T> = {
8080
`__VLS_OmitIndexSignature`,
8181
() => `type __VLS_OmitIndexSignature<T> = { [K in keyof T as {} extends Record<K, unknown> ? never : K]: T[K]; }${endOfLine}`
8282
);
83-
const PickRefsExpose = defineHelper(
84-
`__VLS_PickRefsExpose`,
85-
() => `
86-
type __VLS_PickRefsExpose<T> = T extends object
87-
? { [K in keyof T]: (T[K] extends any[]
88-
? Parameters<T[K][0]['expose']>[0][]
89-
: T[K] extends { expose?: (exposed: infer E) => void }
90-
? E
91-
: T[K]) | null }
92-
: never;
93-
`.trimStart()
94-
);
95-
9683
const helpers = {
9784
[PrettifyLocal.name]: PrettifyLocal,
9885
[OmitKeepDiscriminatedUnion.name]: OmitKeepDiscriminatedUnion,
@@ -101,7 +88,6 @@ type __VLS_PickRefsExpose<T> = T extends object
10188
[PropsChildren.name]: PropsChildren,
10289
[TypePropsToOption.name]: TypePropsToOption,
10390
[OmitIndexSignature.name]: OmitIndexSignature,
104-
[PickRefsExpose.name]: PickRefsExpose,
10591
};
10692
used.clear();
10793

@@ -117,7 +103,6 @@ type __VLS_PickRefsExpose<T> = T extends object
117103
get PropsChildren() { return PropsChildren.name; },
118104
get TypePropsToOption() { return TypePropsToOption.name; },
119105
get OmitIndexSignature() { return OmitIndexSignature.name; },
120-
get PickRefsExpose() { return PickRefsExpose.name; },
121106
};
122107

123108
function* generate(names: string[]) {

packages/language-core/lib/codegen/script/internalComponent.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ export function* generateInternalComponent(
4747
}
4848
yield `}${endOfLine}`; // return {
4949
yield `},${newLine}`; // setup() {
50-
if (options.vueCompilerOptions.target >= 3.5) {
51-
yield `__typeRefs: {} as __VLS_Refs,${newLine}`;
52-
}
5350
if (options.sfc.scriptSetup && options.scriptSetupRanges && !ctx.bypassDefineComponent) {
5451
const emitOptionCodes = [...generateEmitsOption(options, options.sfc.scriptSetup, options.scriptSetupRanges)];
5552
for (const code of emitOptionCodes) {

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,34 @@ export function* generateTemplateCtx(
1313
options: ScriptCodegenOptions,
1414
isClassComponent: boolean
1515
): Generator<Code> {
16-
const types = [];
16+
const exps = [];
1717
if (isClassComponent) {
18-
types.push(`typeof this`);
18+
exps.push(`this`);
1919
}
2020
else {
21-
types.push(`InstanceType<__VLS_PickNotAny<typeof __VLS_internalComponent, new () => {}>>`);
21+
exps.push(`{} as InstanceType<__VLS_PickNotAny<typeof __VLS_internalComponent, new () => {}>>`);
2222
}
2323
if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileBaseName.endsWith(ext))) {
24-
types.push(`typeof globalThis`);
24+
exps.push(`globalThis`);
2525
}
2626
if (options.sfc.styles.some(style => style.module)) {
27-
types.push(`__VLS_StyleModules`);
27+
exps.push(`{} as __VLS_StyleModules`);
28+
}
29+
30+
yield `const __VLS_ctx = `;
31+
if (exps.length === 1) {
32+
yield exps[0];
33+
yield endOfLine;
34+
}
35+
else {
36+
yield `{${newLine}`;
37+
for (const exp of exps) {
38+
yield `...`;
39+
yield exp;
40+
yield `,${newLine}`;
41+
}
42+
yield `}${endOfLine}`;
2843
}
29-
yield `let __VLS_ctx!: ${types.join(' & ')}${endOfLine}`;
3044
}
3145

3246
export function* generateTemplateComponents(options: ScriptCodegenOptions): Generator<Code> {
@@ -87,7 +101,7 @@ export function* generateTemplate(
87101
});
88102
yield* generateTemplateCtx(options, isClassComponent);
89103
yield* generateTemplateComponents(options);
90-
yield* generateTemplateBody(options, ctx, templateCodegenCtx);
104+
yield* generateTemplateBody(options, templateCodegenCtx);
91105
yield* generateInternalComponent(options, ctx, templateCodegenCtx);
92106
}
93107
else {
@@ -100,7 +114,6 @@ export function* generateTemplate(
100114

101115
function* generateTemplateBody(
102116
options: ScriptCodegenOptions,
103-
ctx: ScriptCodegenContext,
104117
templateCodegenCtx: TemplateCodegenContext
105118
): Generator<Code> {
106119
const firstClasses = new Set<string>();
@@ -142,14 +155,14 @@ function* generateTemplateBody(
142155
yield `// no template${newLine}`;
143156
if (!options.scriptSetupRanges?.slots.define) {
144157
yield `const __VLS_slots = {}${endOfLine}`;
145-
yield `const __VLS_refs = {}${endOfLine}`;
158+
yield `const $refs = {}${endOfLine}`;
146159
yield `const __VLS_inheritedAttrs = {}${endOfLine}`;
147160
}
148161
}
149162

150163
yield `const __VLS_templateResult = {`;
151164
yield `slots: ${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'},${newLine}`;
152-
yield `refs: __VLS_refs as ${ctx.localTypes.PickRefsExpose}<typeof __VLS_refs>,${newLine}`;
165+
yield `refs: $refs,${newLine}`;
153166
yield `attrs: {} as Partial<typeof __VLS_inheritedAttrs>,${newLine}`;
154167
yield `}${endOfLine}`;
155168
}

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

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,19 @@ export function* generateComponent(
225225

226226
const refName = yield* generateVScope(options, ctx, node, props);
227227
if (refName) {
228+
const varName = ctx.getInternalVariable();
229+
options.templateRefNames.set(refName, varName);
228230
ctx.usedComponentCtxVars.add(var_defineComponentCtx);
231+
232+
yield `// @ts-ignore${newLine}`;
233+
if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL
234+
&& node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION
235+
&& node.codegenNode.props.properties.find(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for')
236+
) {
237+
yield `var ${varName} = [{} as Parameters<typeof ${var_defineComponentCtx}['expose']>[0]]${endOfLine}`;
238+
} else {
239+
yield `var ${varName} = {} as Parameters<typeof ${var_defineComponentCtx}['expose']>[0]${endOfLine}`;
240+
}
229241
}
230242

231243
const usedComponentEventsVar = yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents);
@@ -257,19 +269,6 @@ export function* generateComponent(
257269

258270
if (ctx.usedComponentCtxVars.has(var_defineComponentCtx)) {
259271
yield `const ${var_defineComponentCtx} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})${endOfLine}`;
260-
if (refName) {
261-
yield `// @ts-ignore${newLine}`;
262-
if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL
263-
&& node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION
264-
&& node.codegenNode.props.properties.find(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for')
265-
) {
266-
yield `(${refName} ??= []).push(${var_defineComponentCtx})`;
267-
} else {
268-
yield `${refName} = ${var_defineComponentCtx}`;
269-
}
270-
271-
yield endOfLine;
272-
}
273272
}
274273
}
275274

@@ -335,14 +334,7 @@ export function* generateElement(
335334

336335
const refName = yield* generateVScope(options, ctx, node, node.props);
337336
if (refName) {
338-
yield `// @ts-ignore${newLine}`;
339-
yield `${refName} = __VLS_intrinsicElements`;
340-
yield* generatePropertyAccess(
341-
options,
342-
ctx,
343-
node.tag
344-
);
345-
yield endOfLine;
337+
options.templateRefNames.set(refName, `__VLS_intrinsicElements['${node.tag}']`);
346338
}
347339

348340
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
@@ -574,9 +566,7 @@ function* generateReferencesForElements(
574566
ctx.accessExternalVariable(content, startOffset);
575567
}
576568

577-
const refName = CompilerDOM.toValidAssetId(prop.value.content, '_VLS_refs' as any);
578-
options.templateRefNames.set(prop.value.content, refName);
579-
return refName;
569+
return prop.value.content;
580570
}
581571
}
582572
}

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
3232
if (options.propsAssignName) {
3333
ctx.addLocalVariable(options.propsAssignName);
3434
}
35+
ctx.addLocalVariable('$refs');
3536

3637
yield* generatePreResolveComponents();
3738

@@ -51,19 +52,17 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
5152

5253
yield* ctx.generateAutoImportCompletion();
5354

54-
yield* generateRefs()
55+
yield* generateRefs();
5556

5657
return ctx;
5758

5859
function* generateRefs(): Generator<Code> {
59-
for (const [, validId] of options.templateRefNames) {
60-
yield `let ${validId}${newLine}`;
61-
}
6260
yield `const __VLS_refs = {${newLine}`;
63-
for (const [name, validId] of options.templateRefNames) {
64-
yield `'${name}': ${validId}!,${newLine}`;
61+
for (const [name, varName] of options.templateRefNames) {
62+
yield `'${name}': ${varName}!,${newLine}`;
6563
}
6664
yield `}${endOfLine}`;
65+
yield `declare var $refs: typeof __VLS_refs${endOfLine}`;
6766
}
6867

6968
function* generateSlotsType(): Generator<Code> {

packages/language-core/lib/parsers/scriptSetupRanges.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ export function parseScriptSetupRanges(
5050
name?: string;
5151
define?: ReturnType<typeof parseDefineFunction>;
5252
}[] = [];
53-
5453
const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition');
5554
const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition');
5655
const defineProp: {
@@ -63,11 +62,12 @@ export function parseScriptSetupRanges(
6362
required: boolean;
6463
isModel?: boolean;
6564
}[] = [];
66-
const bindings = parseBindingRanges(ts, ast);
6765
const text = ast.text;
6866
const leadingCommentEndOffset = ts.getLeadingCommentRanges(text, 0)?.reverse()[0].end ?? 0;
6967
const importComponentNames = new Set<string>();
7068

69+
let bindings = parseBindingRanges(ts, ast);
70+
7171
ts.forEachChild(ast, node => {
7272
const isTypeExport = (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) && node.modifiers?.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword);
7373
if (
@@ -102,6 +102,12 @@ export function parseScriptSetupRanges(
102102
});
103103
ts.forEachChild(ast, child => visitNode(child, [ast]));
104104

105+
const templateRefNames = new Set(templateRefs.map(ref => ref.name));
106+
bindings = bindings.filter(range => {
107+
const name = text.substring(range.start, range.end);
108+
return !templateRefNames.has(name);
109+
});
110+
105111
return {
106112
leadingCommentEndOffset,
107113
importSectionEndOffset,

packages/tsc/tests/__snapshots__/dts.spec.ts.snap

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -645,26 +645,20 @@ declare var __VLS_3: {
645645
str: string;
646646
};
647647
declare var __VLS_inheritedAttrs: {};
648-
declare const __VLS_refs: {};
649648
declare const __VLS_templateResult: {
650649
slots: {
651650
"no-bind"?(_: typeof __VLS_0): any;
652651
default?(_: typeof __VLS_1): any;
653652
"named-slot"?(_: typeof __VLS_2): any;
654653
vbind?(_: typeof __VLS_3): any;
655654
};
656-
refs: __VLS_PickRefsExpose<typeof __VLS_refs>;
655+
refs: {};
657656
attrs: Partial<typeof __VLS_inheritedAttrs>;
658657
};
659658
type __VLS_Slots = typeof __VLS_templateResult['slots'];
660659
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
661660
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_Slots>;
662661
export default _default;
663-
type __VLS_PickRefsExpose<T> = T extends object ? {
664-
[K in keyof T]: (T[K] extends any[] ? Parameters<T[K][0]['expose']>[0][] : T[K] extends {
665-
expose?: (exposed: infer E) => void;
666-
} ? E : T[K]) | null;
667-
} : never;
668662
type __VLS_WithTemplateSlots<T, S> = T & {
669663
new (): {
670664
$slots: S;
@@ -676,7 +670,6 @@ type __VLS_WithTemplateSlots<T, S> = T & {
676670
exports[`vue-tsc-dts > Input: template-slots/component-define-slots.vue, Output: template-slots/component-define-slots.vue.d.ts 1`] = `
677671
"import { VNode } from 'vue';
678672
declare var __VLS_inheritedAttrs: {};
679-
declare const __VLS_refs: {};
680673
declare const __VLS_templateResult: {
681674
slots: Readonly<{
682675
default: (props: {
@@ -703,18 +696,13 @@ declare const __VLS_templateResult: {
703696
}) => VNode[];
704697
'no-bind': () => VNode[];
705698
};
706-
refs: __VLS_PickRefsExpose<typeof __VLS_refs>;
699+
refs: {};
707700
attrs: Partial<typeof __VLS_inheritedAttrs>;
708701
};
709702
type __VLS_Slots = typeof __VLS_templateResult['slots'];
710703
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
711704
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_Slots>;
712705
export default _default;
713-
type __VLS_PickRefsExpose<T> = T extends object ? {
714-
[K in keyof T]: (T[K] extends any[] ? Parameters<T[K][0]['expose']>[0][] : T[K] extends {
715-
expose?: (exposed: infer E) => void;
716-
} ? E : T[K]) | null;
717-
} : never;
718706
type __VLS_WithTemplateSlots<T, S> = T & {
719707
new (): {
720708
$slots: S;
@@ -725,7 +713,6 @@ type __VLS_WithTemplateSlots<T, S> = T & {
725713
726714
exports[`vue-tsc-dts > Input: template-slots/component-destructuring.vue, Output: template-slots/component-destructuring.vue.d.ts 1`] = `
727715
"declare var __VLS_inheritedAttrs: {};
728-
declare const __VLS_refs: {};
729716
declare const __VLS_templateResult: {
730717
slots: Readonly<{
731718
bottom: (props: {
@@ -736,18 +723,13 @@ declare const __VLS_templateResult: {
736723
num: number;
737724
}) => any[];
738725
};
739-
refs: __VLS_PickRefsExpose<typeof __VLS_refs>;
726+
refs: {};
740727
attrs: Partial<typeof __VLS_inheritedAttrs>;
741728
};
742729
type __VLS_Slots = typeof __VLS_templateResult['slots'];
743730
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
744731
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_Slots>;
745732
export default _default;
746-
type __VLS_PickRefsExpose<T> = T extends object ? {
747-
[K in keyof T]: (T[K] extends any[] ? Parameters<T[K][0]['expose']>[0][] : T[K] extends {
748-
expose?: (exposed: infer E) => void;
749-
} ? E : T[K]) | null;
750-
} : never;
751733
type __VLS_WithTemplateSlots<T, S> = T & {
752734
new (): {
753735
$slots: S;
@@ -769,26 +751,20 @@ declare var __VLS_3: {
769751
str: string;
770752
};
771753
declare var __VLS_inheritedAttrs: {};
772-
declare const __VLS_refs: {};
773754
declare const __VLS_templateResult: {
774755
slots: {
775756
"no-bind"?(_: typeof __VLS_0): any;
776757
default?(_: typeof __VLS_1): any;
777758
"named-slot"?(_: typeof __VLS_2): any;
778759
vbind?(_: typeof __VLS_3): any;
779760
};
780-
refs: __VLS_PickRefsExpose<typeof __VLS_refs>;
761+
refs: {};
781762
attrs: Partial<typeof __VLS_inheritedAttrs>;
782763
};
783764
type __VLS_Slots = typeof __VLS_templateResult['slots'];
784765
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
785766
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_Slots>;
786767
export default _default;
787-
type __VLS_PickRefsExpose<T> = T extends object ? {
788-
[K in keyof T]: (T[K] extends any[] ? Parameters<T[K][0]['expose']>[0][] : T[K] extends {
789-
expose?: (exposed: infer E) => void;
790-
} ? E : T[K]) | null;
791-
} : never;
792768
type __VLS_WithTemplateSlots<T, S> = T & {
793769
new (): {
794770
$slots: S;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module 'vue3.5' {
2+
export interface GlobalComponents {
3+
Generic: typeof import('./generic.vue')['default'];
4+
}
5+
}
6+
7+
export { };

test-workspace/tsc/passedFixtures/vue3.5/templateRef/main.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ import { exactType } from '../../shared';
66
<template>
77
<TemplateRef ref="templateRef" />
88

9-
{{ exactType($refs.templateRef?.$refs.generic?.foo, {} as 1 | undefined) }}
9+
{{ exactType($refs.templateRef.$refs.generic.foo, {} as 1) }}
1010
</template>

0 commit comments

Comments
 (0)