Skip to content

Commit e70cb29

Browse files
whysopaulKazariEX
andauthored
fix(language-core): detect duplicate event listeners across name formats (#6094)
Co-authored-by: KazariEX <zinkawaii@qq.com>
1 parent cbd4ea0 commit e70cb29

4 files changed

Lines changed: 74 additions & 26 deletions

File tree

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

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,15 @@ export function* generateElementEvents(
1919
getCtxVar: () => string,
2020
getPropsVar: () => string,
2121
): Generator<Code> {
22-
let emitsVar: string | undefined;
22+
const definitions: {
23+
prop: CompilerDOM.DirectiveNode;
24+
source: string;
25+
offset: number | undefined;
26+
emitPrefix: string;
27+
propPrefix: string;
28+
propName: string;
29+
emitName: string;
30+
}[] = [];
2331

2432
for (const prop of node.props) {
2533
if (
@@ -32,13 +40,8 @@ export function* generateElementEvents(
3240
&& (!prop.arg || prop.arg.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.isStatic)
3341
)
3442
) {
35-
if (!emitsVar) {
36-
emitsVar = ctx.getInternalVariable();
37-
yield `let ${emitsVar}!: ${names.ResolveEmits}<typeof ${componentOriginalVar}, typeof ${getCtxVar()}.emit>${endOfLine}`;
38-
}
39-
4043
let source = prop.arg?.loc.source ?? 'model-value';
41-
let start = prop.arg?.loc.start.offset;
44+
let offset = prop.arg?.loc.start.offset;
4245
let propPrefix = 'on-';
4346
let emitPrefix = '';
4447
if (prop.name === 'model') {
@@ -47,33 +50,61 @@ export function* generateElementEvents(
4750
}
4851
else if (source.startsWith('vue:')) {
4952
source = source.slice('vue:'.length);
50-
start = start! + 'vue:'.length;
53+
offset = offset! + 'vue:'.length;
5154
propPrefix = 'onVnode-';
5255
emitPrefix = 'vnode-';
5356
}
5457
const propName = camelize(propPrefix + source);
5558
const emitName = emitPrefix + source;
56-
const camelizedEmitName = camelize(emitName);
5759

58-
yield `const ${ctx.getInternalVariable()}: ${names.NormalizeComponentEvent}<typeof ${getPropsVar()}, typeof ${emitsVar}, '${propName}', '${emitName}', '${camelizedEmitName}'> = (${newLine}`;
59-
if (prop.name === 'on') {
60-
yield `{ `;
61-
yield* generateEventArg(options, source, start!, emitPrefix.slice(0, -1), codeFeatures.navigation);
62-
yield `: {} as any } as typeof ${emitsVar},${newLine}`;
63-
}
64-
yield `{ `;
65-
if (prop.name === 'on') {
66-
yield* generateEventArg(options, source, start!, propPrefix.slice(0, -1));
67-
yield `: `;
68-
yield* generateEventExpression(options, ctx, prop);
69-
}
70-
else {
71-
yield `'${propName}': `;
72-
yield* generateModelEventExpression(options, ctx, prop);
73-
}
74-
yield `})${endOfLine}`;
60+
definitions.push({
61+
prop,
62+
source,
63+
offset,
64+
emitPrefix,
65+
propPrefix,
66+
propName,
67+
emitName,
68+
});
69+
}
70+
}
71+
72+
if (!definitions.length) {
73+
return;
74+
}
75+
76+
const emitsVar = ctx.getInternalVariable();
77+
yield `let ${emitsVar}!: ${names.ResolveEmits}<typeof ${componentOriginalVar}, typeof ${getCtxVar()}.emit>${endOfLine}`;
78+
79+
yield `const ${ctx.getInternalVariable()}: `;
80+
for (let i = 0; i < definitions.length; i++) {
81+
const { propName, emitName } = definitions[i]!;
82+
if (i > 0) {
83+
yield ` & `;
84+
}
85+
yield `${names.NormalizeComponentEvent}<typeof ${getPropsVar()}, typeof ${emitsVar}, '${propName}', '${emitName}', '${
86+
camelize(emitName)
87+
}'>`;
88+
}
89+
yield ` = {${newLine}`;
90+
for (const { prop, source, offset, emitPrefix, propPrefix, propName } of definitions) {
91+
if (prop.name === 'on') {
92+
yield `...{ `;
93+
yield* generateEventArg(options, source, offset!, emitPrefix.slice(0, -1), codeFeatures.navigation);
94+
yield `: {} as any } as typeof ${emitsVar},${newLine}`;
95+
}
96+
if (prop.name === 'on') {
97+
yield* generateEventArg(options, source, offset!, propPrefix.slice(0, -1));
98+
yield `: `;
99+
yield* generateEventExpression(options, ctx, prop);
100+
}
101+
else {
102+
yield `'${propName}': `;
103+
yield* generateModelEventExpression(options, ctx, prop);
75104
}
105+
yield `,${newLine}`;
76106
}
107+
yield `}${endOfLine}`;
77108
}
78109

79110
export function* generateEventArg(

test-workspace/tsc/#6094/main.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script setup lang="ts">
2+
declare function Comp(props: {
3+
onFooBar?: () => void;
4+
}): void;
5+
</script>
6+
7+
<template>
8+
<!-- @vue-expect-error -->
9+
<Comp @foo-bar="void 0" @fooBar="void 0" />
10+
</template>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"include": ["**/*"]
4+
}

test-workspace/tsc/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,9 @@
520520
{
521521
"path": "./#6084/tsconfig.json"
522522
},
523+
{
524+
"path": "./#6094/tsconfig.json"
525+
},
523526
{
524527
"path": "./#625/tsconfig.json"
525528
},

0 commit comments

Comments
 (0)