Skip to content

Commit 2a3ea17

Browse files
committed
feat(language-core): report unknown events when strictTemplates is enabled
close #3718
1 parent b7fdfc3 commit 2a3ea17

File tree

7 files changed

+34
-54
lines changed

7 files changed

+34
-54
lines changed

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,6 @@ declare global {
8080
: (_: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'} } };
8181
function __VLS_elementAsFunction<T>(tag: T, endTag?: T): (_: T${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
8282
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): Parameters<T>['length'] extends 2 ? [any] : [];
83-
function __VLS_pickEvent<E1, E2>(emitEvent: E1, propEvent: E2): __VLS_FillingEventArg<
84-
__VLS_PickNotAny<
85-
__VLS_AsFunctionOrAny<E2>,
86-
__VLS_AsFunctionOrAny<E1>
87-
>
88-
> | undefined;
8983
function __VLS_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): __VLS_PickNotAny<
9084
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : any
9185
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export function* generateComponent(
4545
offset: number;
4646
astHolder: any;
4747
} | undefined;
48-
let usedComponentEventsVar = false;
4948

5049
if (isComponentTag) {
5150
for (const prop of node.props) {
@@ -239,7 +238,7 @@ export function* generateComponent(
239238
yield* generateVScope(options, ctx, node, props);
240239

241240
ctx.usedComponentCtxVars.add(componentCtxVar);
242-
yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents, () => usedComponentEventsVar = true);
241+
const usedComponentEventsVar = yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents);
243242

244243
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
245244
if (slotDir) {

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

Lines changed: 24 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { generateCamelized } from './camelized';
88
import type { TemplateCodegenContext } from './context';
99
import type { TemplateCodegenOptions } from './index';
1010
import { generateInterpolation } from './interpolation';
11-
import { generateObjectProperty } from './objectProperty';
1211

1312
export function* generateElementEvents(
1413
options: TemplateCodegenOptions,
@@ -18,51 +17,40 @@ export function* generateElementEvents(
1817
componentInstanceVar: string,
1918
emitVar: string,
2019
eventsVar: string,
21-
used: () => void,
2220
): Generator<Code> {
21+
let usedComponentEventsVar = false;
22+
let propsVar: string | undefined;
2323
for (const prop of node.props) {
2424
if (
2525
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
2626
&& prop.name === 'on'
2727
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
28+
&& !prop.arg.loc.source.startsWith('[')
29+
&& !prop.arg.loc.source.endsWith(']')
2830
) {
29-
used();
30-
const eventVar = ctx.getInternalVariable();
31-
yield `let ${eventVar} = {${newLine}`;
32-
yield `/**__VLS_emit,${emitVar},${prop.arg.loc.source}*/${newLine}`;
33-
yield `'${prop.arg.loc.source}': __VLS_pickEvent(`;
34-
yield `${eventsVar}['${prop.arg.loc.source}'], `;
35-
yield `({} as __VLS_FunctionalComponentProps<typeof ${componentVar}, typeof ${componentInstanceVar}>)`;
36-
yield* generateEventArg(options, ctx, prop.arg, true, false);
37-
yield `) }${endOfLine}`;
38-
yield `${eventVar} = { `;
39-
if (prop.arg.loc.source.startsWith('[') && prop.arg.loc.source.endsWith(']')) {
40-
yield `[(`;
41-
yield* generateInterpolation(
42-
options,
43-
ctx,
44-
prop.arg.loc.source.slice(1, -1),
45-
prop.arg.loc,
46-
prop.arg.loc.start.offset + 1,
47-
ctx.codeFeatures.all,
48-
'',
49-
'',
50-
);
51-
yield `)!]`;
31+
usedComponentEventsVar = true;
32+
if (!propsVar) {
33+
propsVar = ctx.getInternalVariable();
34+
yield `let ${propsVar}!: __VLS_FunctionalComponentProps<typeof ${componentVar}, typeof ${componentInstanceVar}>${endOfLine}`;
5235
}
53-
else {
54-
yield* generateObjectProperty(
55-
options,
56-
ctx,
57-
prop.arg.loc.source,
58-
prop.arg.loc.start.offset,
59-
ctx.codeFeatures.withoutHighlightAndCompletionAndNavigation,
60-
prop.arg.loc
61-
);
36+
const originalPropName = camelize('on-' + prop.arg.loc.source);
37+
yield `const ${ctx.getInternalVariable()}: `;
38+
if (!options.vueCompilerOptions.strictTemplates) {
39+
yield `Record<string, unknown> & `;
6240
}
41+
yield `Partial<`;
42+
yield `__VLS_IsAny<__VLS_AsFunctionOrAny<typeof ${propsVar}['${originalPropName}']>> extends false${newLine}`;
43+
yield `? typeof ${propsVar}${newLine}`;
44+
yield `: __VLS_IsAny<typeof ${eventsVar}['${prop.arg.loc.source}']> extends false${newLine}`;
45+
yield `? {${newLine}`;
46+
yield `/**__VLS_emit,${emitVar},${prop.arg.loc.source}*/${newLine}`;
47+
yield `'${originalPropName}': typeof ${eventsVar}['${prop.arg.loc.source}']${newLine}`;
48+
yield `}${newLine}`;
49+
yield `: typeof ${propsVar}${newLine}`;
50+
yield `> = {${newLine}`;
51+
yield* generateEventArg(options, ctx, prop.arg, true);
6352
yield `: `;
6453
yield* generateEventExpression(options, ctx, prop);
65-
yield newLine;
6654
yield `}${endOfLine}`;
6755
}
6856
else if (
@@ -85,6 +73,7 @@ export function* generateElementEvents(
8573
yield endOfLine;
8674
}
8775
}
76+
return usedComponentEventsVar;
8877
}
8978

9079
const eventArgFeatures: VueCodeInformation = {
@@ -108,7 +97,6 @@ export function* generateEventArg(
10897
options: TemplateCodegenOptions,
10998
ctx: TemplateCodegenContext,
11099
arg: CompilerDOM.SimpleExpressionNode,
111-
access: boolean,
112100
enableHover: boolean,
113101
): Generator<Code> {
114102
const features = enableHover
@@ -132,9 +120,6 @@ export function* generateEventArg(
132120
yield `]`;
133121
}
134122
else if (variableNameRegex.test(camelize(arg.loc.source))) {
135-
if (access) {
136-
yield `.`;
137-
}
138123
yield ['', 'template', arg.loc.start.offset, features];
139124
yield `on`;
140125
yield* generateCamelized(
@@ -144,9 +129,6 @@ export function* generateEventArg(
144129
);
145130
}
146131
else {
147-
if (access) {
148-
yield `[`;
149-
}
150132
yield* wrapWith(
151133
arg.loc.start.offset,
152134
arg.loc.end.offset,
@@ -161,9 +143,6 @@ export function* generateEventArg(
161143
),
162144
`'`,
163145
);
164-
if (access) {
165-
yield `]`;
166-
}
167146
}
168147
}
169148

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export function* generateElementProps(
4444
&& prop.name === 'on'
4545
) {
4646
if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
47-
yield* generateEventArg(options, ctx, prop.arg, false, true);
47+
yield* generateEventArg(options, ctx, prop.arg, true);
4848
yield `: `;
4949
yield* generateEventExpression(options, ctx, prop);
5050
yield `,${newLine}`;

test-workspace/tsc/vue2_strictTemplate/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
],
1111
"exclude": [
1212
"../vue3_strictTemplate/#3140",
13+
"../vue3_strictTemplate/#3718",
1314
"../vue3_strictTemplate/intrinsicProps",
1415
]
1516
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<Teleport to="/contact" @eventDoesNotExist="() => { }" />
3+
</template>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<template>
2+
<!-- @vue-expect-error -->
3+
<Teleport to="/contact" @eventDoesNotExist="() => { }" />
4+
</template>

0 commit comments

Comments
 (0)