From b12afd18df0cb5544bebeb57a8aa615acbd6819f Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Wed, 9 Oct 2024 20:29:48 +0200 Subject: [PATCH] fix: include named exports in svelte 5 type (#2528) fixes sveltejs/svelte#13508 --- packages/svelte2tsx/repl/index.svelte | 10 ++++---- packages/svelte2tsx/src/svelte2tsx/index.ts | 23 ++++++++++++++--- .../src/svelte2tsx/nodes/ExportedNames.ts | 24 +++++++++++------- .../processInstanceScriptContent.ts | 12 +++++++-- .../export-list-runes.v5/expectedv2.ts | 25 +++++++++++++++++++ .../samples/export-list-runes.v5/input.svelte | 19 ++++++++++++++ .../renamed-exports-runes.v5/expectedv2.ts | 13 ++++++++++ .../renamed-exports-runes.v5/input.svelte | 7 ++++++ .../ts-export-list-runes.v5/expectedv2.ts | 25 +++++++++++++++++++ .../ts-export-list-runes.v5/input.svelte | 19 ++++++++++++++ 10 files changed, 157 insertions(+), 20 deletions(-) create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/expectedv2.ts create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/input.svelte create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/expectedv2.ts create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/input.svelte create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/expectedv2.ts create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/input.svelte diff --git a/packages/svelte2tsx/repl/index.svelte b/packages/svelte2tsx/repl/index.svelte index 49517cc72..4b1efc883 100644 --- a/packages/svelte2tsx/repl/index.svelte +++ b/packages/svelte2tsx/repl/index.svelte @@ -1,7 +1,7 @@ + + - -{#if value} - -{/if} diff --git a/packages/svelte2tsx/src/svelte2tsx/index.ts b/packages/svelte2tsx/src/svelte2tsx/index.ts index 91e0d6b5a..87e6779c4 100644 --- a/packages/svelte2tsx/src/svelte2tsx/index.ts +++ b/packages/svelte2tsx/src/svelte2tsx/index.ts @@ -44,6 +44,7 @@ type TemplateProcessResult = { events: ComponentEvents; resolvedStores: string[]; usesAccessors: boolean; + isRunes: boolean; }; function processSvelteTemplate( @@ -64,6 +65,7 @@ function processSvelteTemplate( let uses$$restProps = false; let uses$$slots = false; let usesAccessors = !!options.accessors; + let isRunes = false; const componentDocumentation = new ComponentDocumentation(); @@ -92,6 +94,9 @@ function processSvelteTemplate( usesAccessors = true; } break; + case 'runes': + isRunes = true; + break; } } }; @@ -303,7 +308,8 @@ function processSvelteTemplate( uses$$slots, componentDocumentation, resolvedStores, - usesAccessors + usesAccessors, + isRunes }; } @@ -342,7 +348,8 @@ export function svelte2tsx( events, componentDocumentation, resolvedStores, - usesAccessors + usesAccessors, + isRunes } = processSvelteTemplate(str, options.parse || parse, { ...options, svelte5Plus @@ -370,7 +377,14 @@ export function svelte2tsx( : instanceScriptTarget; const implicitStoreValues = new ImplicitStoreValues(resolvedStores, renderFunctionStart); //move the instance script and process the content - let exportedNames = new ExportedNames(str, 0, basename, options?.isTsFile, svelte5Plus); + let exportedNames = new ExportedNames( + str, + 0, + basename, + options?.isTsFile, + svelte5Plus, + isRunes + ); let generics = new Generics(str, 0, { attributes: [] } as any); let uses$$SlotsInterface = false; if (scriptTag) { @@ -387,7 +401,8 @@ export function svelte2tsx( /**hasModuleScripts */ !!moduleScriptTag, options?.isTsFile, basename, - svelte5Plus + svelte5Plus, + isRunes ); uses$$props = uses$$props || res.uses$$props; uses$$restProps = uses$$restProps || res.uses$$restProps; diff --git a/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts b/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts index 3f5ec412a..8184da952 100644 --- a/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts +++ b/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts @@ -17,6 +17,7 @@ interface ExportedName { identifierText?: string; required?: boolean; doc?: string; + isNamedExport?: boolean; } export class ExportedNames { @@ -54,7 +55,8 @@ export class ExportedNames { private astOffset: number, private basename: string, private isTsFile: boolean, - private isSvelte5Plus: boolean + private isSvelte5Plus: boolean, + private isRunes: boolean ) {} handleVariableStatement(node: ts.VariableStatement, parent: ts.Node): void { @@ -127,9 +129,9 @@ export class ExportedNames { if (ts.isNamedExports(exportClause)) { for (const ne of exportClause.elements) { if (ne.propertyName) { - this.addExport(ne.propertyName, false, ne.name); + this.addExport(ne.propertyName, false, ne.name, undefined, undefined, true); } else { - this.addExport(ne.name, false); + this.addExport(ne.name, false, undefined, undefined, undefined, true); } } //we can remove entire statement @@ -552,24 +554,26 @@ export class ExportedNames { isLet: boolean, target: ts.Identifier = null, type: ts.TypeNode = null, - required = false + required = false, + isNamedExport = false ): void { const existingDeclaration = this.possibleExports.get(name.text); - if (target) { this.exports.set(name.text, { isLet: isLet || existingDeclaration?.isLet, type: type?.getText() || existingDeclaration?.type, identifierText: target.text, required: required || existingDeclaration?.required, - doc: this.getDoc(target) || existingDeclaration?.doc + doc: this.getDoc(target) || existingDeclaration?.doc, + isNamedExport }); } else { this.exports.set(name.text, { isLet: isLet || existingDeclaration?.isLet, type: existingDeclaration?.type, required: existingDeclaration?.required, - doc: existingDeclaration?.doc + doc: existingDeclaration?.doc, + isNamedExport }); } @@ -706,7 +710,9 @@ export class ExportedNames { */ createExportsStr(): string { const names = Array.from(this.exports.entries()); - const others = names.filter(([, { isLet }]) => !isLet); + const others = names.filter( + ([, { isLet, isNamedExport }]) => !isLet || (this.usesRunes() && isNamedExport) + ); const needsAccessors = this.usesAccessors && names.length > 0 && !this.usesRunes(); // runes mode doesn't support accessors if (this.isSvelte5Plus) { @@ -803,6 +809,6 @@ export class ExportedNames { } usesRunes() { - return this.hasRunesGlobals || this.hasPropsRune(); + return this.hasRunesGlobals || this.hasPropsRune() || this.isRunes; } } diff --git a/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts b/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts index e226c2b1b..044aeb83a 100644 --- a/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts +++ b/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts @@ -42,7 +42,8 @@ export function processInstanceScriptContent( hasModuleScript: boolean, isTSFile: boolean, basename: string, - isSvelte5Plus: boolean + isSvelte5Plus: boolean, + isRunes: boolean ): InstanceScriptProcessResult { const htmlx = str.original; const scriptContent = htmlx.substring(script.content.start, script.content.end); @@ -54,7 +55,14 @@ export function processInstanceScriptContent( ts.ScriptKind.TS ); const astOffset = script.content.start; - const exportedNames = new ExportedNames(str, astOffset, basename, isTSFile, isSvelte5Plus); + const exportedNames = new ExportedNames( + str, + astOffset, + basename, + isTSFile, + isSvelte5Plus, + isRunes + ); const generics = new Generics(str, astOffset, script); const interfacesAndTypes = new InterfacesAndTypes(); diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/expectedv2.ts new file mode 100644 index 000000000..e99e80294 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/expectedv2.ts @@ -0,0 +1,25 @@ +/// +;function render() { + + let name1 = "world" + let name2/*Ωignore_startΩ*/;name2 = __sveltets_2_any(name2);/*Ωignore_endΩ*/ + + let rename1 = ''; + let rename2/*Ωignore_startΩ*/;rename2 = __sveltets_2_any(rename2);/*Ωignore_endΩ*/; + + class Foo {} + function bar() {} + const baz = ''; + + class RenameFoo {} + function renamebar() {} + const renamebaz = ''; + + +; +async () => { { svelteHTML.createElement("svelte:options", {"runes":true,});} + +}; +return { props: /** @type {Record} */ ({}), exports: /** @type {{name1: typeof name1,name2: typeof name2,renamed1: typeof rename1,renamed2: typeof rename2,Foo: typeof Foo,bar: typeof bar,baz: typeof baz,RenamedFoo: typeof RenameFoo,renamedbar: typeof renamebar,renamedbaz: typeof renamebaz}} */ ({}), bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} +const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/input.svelte new file mode 100644 index 000000000..999d14122 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/input.svelte @@ -0,0 +1,19 @@ + + + diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/expectedv2.ts new file mode 100644 index 000000000..8461d6616 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/expectedv2.ts @@ -0,0 +1,13 @@ +/// +;function render() { + + let name = "world" + let name2 = "world" + +; +async () => { { svelteHTML.createElement("svelte:options", {"runes":true,});} + +}; +return { props: /** @type {Record} */ ({}), exports: /** @type {{name3: typeof name,name4: typeof name2}} */ ({}), bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} +const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/input.svelte new file mode 100644 index 000000000..4b1efc883 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/input.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/expectedv2.ts new file mode 100644 index 000000000..45c93ecf4 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/expectedv2.ts @@ -0,0 +1,25 @@ +/// +;function render() { + + let name1: string = "world"/*Ωignore_startΩ*/;name1 = __sveltets_2_any(name1);/*Ωignore_endΩ*/ + let name2: string/*Ωignore_startΩ*/;name2 = __sveltets_2_any(name2);/*Ωignore_endΩ*/; + let name3: string = ''/*Ωignore_startΩ*/;name3 = __sveltets_2_any(name3);/*Ωignore_endΩ*/;let name4: string/*Ωignore_startΩ*/;name4 = __sveltets_2_any(name4);/*Ωignore_endΩ*/; + + let rename1: string = ''/*Ωignore_startΩ*/;rename1 = __sveltets_2_any(rename1);/*Ωignore_endΩ*/; + let rename2: string/*Ωignore_startΩ*/;rename2 = __sveltets_2_any(rename2);/*Ωignore_endΩ*/; + + class Foo {} + function bar() {} + const baz: string = ''; + + class RenameFoo {} + function renamebar() {} + const renamebaz: string = ''; + + +; +async () => { { svelteHTML.createElement("svelte:options", {"runes":true,});} +}; +return { props: {} as Record, exports: {} as any as { name1: string,name2: string,name3: string,name4: string,renamed1: string,renamed2: string,Foo: typeof Foo,bar: typeof bar,baz: string,RenamedFoo: typeof RenameFoo,renamedbar: typeof renamebar,renamedbaz: string }, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} +const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/input.svelte new file mode 100644 index 000000000..11ceadd74 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/input.svelte @@ -0,0 +1,19 @@ + +