Skip to content

Commit 38f448d

Browse files
authoredJan 24, 2024
fix: deduplicate generate props/events/slot types correctly (#2269)
Deduplication that took place when generating in dts mode was flawed
1 parent 7d4f8a7 commit 38f448d

File tree

3 files changed

+68
-25
lines changed

3 files changed

+68
-25
lines changed
 

‎packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts

+52-22
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,18 @@ class __sveltets_Render${genericsDef} {
7676
const svelteComponentClass = noSvelteComponentTyped
7777
? 'SvelteComponent'
7878
: 'SvelteComponentTyped';
79+
const [PropsName] = addTypeExport(str, className, 'Props');
80+
const [EventsName] = addTypeExport(str, className, 'Events');
81+
const [SlotsName] = addTypeExport(str, className, 'Slots');
7982

8083
if (mode === 'dts') {
8184
statement +=
82-
`export type ${className}Props${genericsDef} = ${returnType('props')};\n` +
83-
`export type ${className}Events${genericsDef} = ${returnType('events')};\n` +
84-
`export type ${className}Slots${genericsDef} = ${returnType('slots')};\n` +
85+
`export type ${PropsName}${genericsDef} = ${returnType('props')};\n` +
86+
`export type ${EventsName}${genericsDef} = ${returnType('events')};\n` +
87+
`export type ${SlotsName}${genericsDef} = ${returnType('slots')};\n` +
8588
`\n${doc}export default class${
8689
className ? ` ${className}` : ''
87-
}${genericsDef} extends ${svelteComponentClass}<${className}Props${genericsRef}, ${className}Events${genericsRef}, ${className}Slots${genericsRef}> {` +
90+
}${genericsDef} extends ${svelteComponentClass}<${PropsName}${genericsRef}, ${EventsName}${genericsRef}, ${SlotsName}${genericsRef}> {` +
8891
exportedNames.createClassGetters() +
8992
(usesAccessors ? exportedNames.createClassAccessors() : '') +
9093
'\n}';
@@ -128,32 +131,21 @@ function addSimpleComponentExport({
128131

129132
let statement: string;
130133
if (mode === 'dts' && isTsFile) {
131-
const addTypeExport = (type: string) => {
132-
const exportName = className + type;
133-
134-
if (!str.original.includes(exportName)) {
135-
return `export type ${exportName} = typeof __propDef.${type.toLowerCase()};\n`;
136-
}
137-
138-
let replacement = exportName + '_';
139-
while (str.original.includes(replacement)) {
140-
replacement += '_';
141-
}
142-
return `type ${replacement} = typeof __propDef.${type.toLowerCase()};\nexport { ${replacement} as ${exportName} };\n`;
143-
};
144-
145134
const svelteComponentClass = noSvelteComponentTyped
146135
? 'SvelteComponent'
147136
: 'SvelteComponentTyped';
137+
const [PropsName, PropsExport] = addTypeExport(str, className, 'Props');
138+
const [EventsName, EventsExport] = addTypeExport(str, className, 'Events');
139+
const [SlotsName, SlotsExport] = addTypeExport(str, className, 'Slots');
148140

149141
statement =
150142
`\nconst __propDef = ${propDef};\n` +
151-
addTypeExport('Props') +
152-
addTypeExport('Events') +
153-
addTypeExport('Slots') +
143+
PropsExport +
144+
EventsExport +
145+
SlotsExport +
154146
`\n${doc}export default class${
155147
className ? ` ${className}` : ''
156-
} extends ${svelteComponentClass}<${className}Props, ${className}Events, ${className}Slots> {` +
148+
} extends ${svelteComponentClass}<${PropsName}, ${EventsName}, ${SlotsName}> {` +
157149
exportedNames.createClassGetters() +
158150
(usesAccessors ? exportedNames.createClassAccessors() : '') +
159151
'\n}';
@@ -182,6 +174,44 @@ function addSimpleComponentExport({
182174
str.append(statement);
183175
}
184176

177+
function addTypeExport(
178+
str: MagicString,
179+
className: string,
180+
type: string
181+
): [name: string, exportstring: string] {
182+
const exportName = className + type;
183+
184+
if (!new RegExp(`\\W${exportName}\\W`).test(str.original)) {
185+
return [
186+
exportName,
187+
`export type ${exportName} = typeof __propDef.${type.toLowerCase()};\n`
188+
];
189+
}
190+
191+
let replacement = exportName + '_';
192+
while (str.original.includes(replacement)) {
193+
replacement += '_';
194+
}
195+
196+
if (
197+
// Check if there's already an export with the same name
198+
!new RegExp(
199+
`export ((const|let|var|class|interface|type) ${exportName}\\W|{[^}]*?${exportName}(}|\\W.*?}))`
200+
).test(str.original)
201+
) {
202+
return [
203+
replacement,
204+
`type ${replacement} = typeof __propDef.${type.toLowerCase()};\nexport { ${replacement} as ${exportName} };\n`
205+
];
206+
} else {
207+
return [
208+
replacement,
209+
// we assume that the author explicitly named the type the same and don't export the generated type (which has an unstable name)
210+
`type ${replacement} = typeof __propDef.${type.toLowerCase()};\n`
211+
];
212+
}
213+
}
214+
185215
function events(strictEvents: boolean, renderStr: string) {
186216
return strictEvents ? renderStr : `__sveltets_2_with_any_event(${renderStr})`;
187217
}

‎packages/svelte2tsx/test/emitDts/samples/typescript-deduplicate/expected/Test.svelte.d.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { SvelteComponentTyped } from "svelte";
2+
export interface TestEvents {
3+
foo: 'bar';
4+
}
25
import { type TestProps } from './foo';
36
declare const __propDef: {
47
props: {
@@ -16,7 +19,8 @@ declare const __propDef: {
1619
};
1720
type TestProps_ = typeof __propDef.props;
1821
export { TestProps_ as TestProps };
19-
export type TestEvents = typeof __propDef.events;
20-
export type TestSlots = typeof __propDef.slots;
21-
export default class Test extends SvelteComponentTyped<TestProps, TestEvents, TestSlots> {
22+
type TestEvents_ = typeof __propDef.events;
23+
type TestSlots_ = typeof __propDef.slots;
24+
export { TestSlots_ as TestSlots };
25+
export default class Test extends SvelteComponentTyped<TestProps_, TestEvents_, TestSlots_> {
2226
}

‎packages/svelte2tsx/test/emitDts/samples/typescript-deduplicate/src/Test.svelte

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
<script context="module" lang="ts">
2+
export interface TestEvents {
3+
foo: 'bar';
4+
}
5+
interface TestSlots {
6+
foo: 'bar';
7+
}
8+
</script>
9+
110
<script lang="ts">
211
import { eventProps, type TestProps } from './foo';
312

0 commit comments

Comments
 (0)
Please sign in to comment.