Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: deduplicate generate props/events/slot types correctly #2269

Merged
merged 1 commit into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 52 additions & 22 deletions packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,18 @@ class __sveltets_Render${genericsDef} {
const svelteComponentClass = noSvelteComponentTyped
? 'SvelteComponent'
: 'SvelteComponentTyped';
const [PropsName] = addTypeExport(str, className, 'Props');
const [EventsName] = addTypeExport(str, className, 'Events');
const [SlotsName] = addTypeExport(str, className, 'Slots');

if (mode === 'dts') {
statement +=
`export type ${className}Props${genericsDef} = ${returnType('props')};\n` +
`export type ${className}Events${genericsDef} = ${returnType('events')};\n` +
`export type ${className}Slots${genericsDef} = ${returnType('slots')};\n` +
`export type ${PropsName}${genericsDef} = ${returnType('props')};\n` +
`export type ${EventsName}${genericsDef} = ${returnType('events')};\n` +
`export type ${SlotsName}${genericsDef} = ${returnType('slots')};\n` +
`\n${doc}export default class${
className ? ` ${className}` : ''
}${genericsDef} extends ${svelteComponentClass}<${className}Props${genericsRef}, ${className}Events${genericsRef}, ${className}Slots${genericsRef}> {` +
}${genericsDef} extends ${svelteComponentClass}<${PropsName}${genericsRef}, ${EventsName}${genericsRef}, ${SlotsName}${genericsRef}> {` +
exportedNames.createClassGetters() +
(usesAccessors ? exportedNames.createClassAccessors() : '') +
'\n}';
Expand Down Expand Up @@ -128,32 +131,21 @@ function addSimpleComponentExport({

let statement: string;
if (mode === 'dts' && isTsFile) {
const addTypeExport = (type: string) => {
const exportName = className + type;

if (!str.original.includes(exportName)) {
return `export type ${exportName} = typeof __propDef.${type.toLowerCase()};\n`;
}

let replacement = exportName + '_';
while (str.original.includes(replacement)) {
replacement += '_';
}
return `type ${replacement} = typeof __propDef.${type.toLowerCase()};\nexport { ${replacement} as ${exportName} };\n`;
};

const svelteComponentClass = noSvelteComponentTyped
? 'SvelteComponent'
: 'SvelteComponentTyped';
const [PropsName, PropsExport] = addTypeExport(str, className, 'Props');
const [EventsName, EventsExport] = addTypeExport(str, className, 'Events');
const [SlotsName, SlotsExport] = addTypeExport(str, className, 'Slots');

statement =
`\nconst __propDef = ${propDef};\n` +
addTypeExport('Props') +
addTypeExport('Events') +
addTypeExport('Slots') +
PropsExport +
EventsExport +
SlotsExport +
`\n${doc}export default class${
className ? ` ${className}` : ''
} extends ${svelteComponentClass}<${className}Props, ${className}Events, ${className}Slots> {` +
} extends ${svelteComponentClass}<${PropsName}, ${EventsName}, ${SlotsName}> {` +
exportedNames.createClassGetters() +
(usesAccessors ? exportedNames.createClassAccessors() : '') +
'\n}';
Expand Down Expand Up @@ -182,6 +174,44 @@ function addSimpleComponentExport({
str.append(statement);
}

function addTypeExport(
str: MagicString,
className: string,
type: string
): [name: string, exportstring: string] {
const exportName = className + type;

if (!new RegExp(`\\W${exportName}\\W`).test(str.original)) {
return [
exportName,
`export type ${exportName} = typeof __propDef.${type.toLowerCase()};\n`
];
}

let replacement = exportName + '_';
while (str.original.includes(replacement)) {
replacement += '_';
}

if (
// Check if there's already an export with the same name
!new RegExp(
`export ((const|let|var|class|interface|type) ${exportName}\\W|{[^}]*?${exportName}(}|\\W.*?}))`
).test(str.original)
) {
return [
replacement,
`type ${replacement} = typeof __propDef.${type.toLowerCase()};\nexport { ${replacement} as ${exportName} };\n`
];
} else {
return [
replacement,
// we assume that the author explicitly named the type the same and don't export the generated type (which has an unstable name)
`type ${replacement} = typeof __propDef.${type.toLowerCase()};\n`
];
}
}

function events(strictEvents: boolean, renderStr: string) {
return strictEvents ? renderStr : `__sveltets_2_with_any_event(${renderStr})`;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { SvelteComponentTyped } from "svelte";
export interface TestEvents {
foo: 'bar';
}
import { type TestProps } from './foo';
declare const __propDef: {
props: {
Expand All @@ -16,7 +19,8 @@ declare const __propDef: {
};
type TestProps_ = typeof __propDef.props;
export { TestProps_ as TestProps };
export type TestEvents = typeof __propDef.events;
export type TestSlots = typeof __propDef.slots;
export default class Test extends SvelteComponentTyped<TestProps, TestEvents, TestSlots> {
type TestEvents_ = typeof __propDef.events;
type TestSlots_ = typeof __propDef.slots;
export { TestSlots_ as TestSlots };
export default class Test extends SvelteComponentTyped<TestProps_, TestEvents_, TestSlots_> {
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
<script context="module" lang="ts">
export interface TestEvents {
foo: 'bar';
}
interface TestSlots {
foo: 'bar';
}
</script>

<script lang="ts">
import { eventProps, type TestProps } from './foo';

Expand Down