From 1f75da44e5bea5a46cfe0be6aef66c150d30637a Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 20 Nov 2024 12:40:26 +0100 Subject: [PATCH 1/2] fix: ensure hoisted interfaces are moved after hoisted imports #2594 As a drive-by, I also ensures that the dts generation knows about the hoisted interfaces so it does not transform them into types anymore --- .../svelte2tsx/nodes/HoistableInterfaces.ts | 8 +++++- .../processInstanceScriptContent.ts | 25 +++++++++++------ .../expected/TestRunes2.svelte.d.ts | 6 ++-- .../expectedv2.ts | 6 ++-- .../expectedv2.ts | 4 +-- .../expectedv2.ts | 4 +-- .../expectedv2.ts | 28 +++++++++++++++++++ .../input.svelte | 17 +++++++++++ .../samples/ts-runes.v5/expectedv2.ts | 2 +- .../expectedv2.ts | 2 +- 10 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-5.v5/expectedv2.ts create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-5.v5/input.svelte diff --git a/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts b/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts index d64776d27..647926ed0 100644 --- a/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts +++ b/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts @@ -332,6 +332,7 @@ export class HoistableInterfaces { } const hoistable = this.determineHoistableInterfaces(); + if (hoistable.has(this.props_interface.name)) { for (const [name, node] of hoistable) { let pos = node.pos + astOffset; @@ -343,12 +344,17 @@ export class HoistableInterfaces { } if (/\s/.test(str.original[pos])) { pos++; - str.prependRight(pos, '\n'); } + + // jsdoc comments would be ignored if they are on the same line as the ;, so we add a newline, too + str.prependRight(pos, ';\n'); + str.appendLeft(node.end + astOffset, ';'); } str.move(pos, node.end + astOffset, scriptStart); } + + return hoistable; } } diff --git a/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts b/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts index 5b504ec6a..6f575952b 100644 --- a/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts +++ b/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts @@ -310,21 +310,30 @@ export function processInstanceScriptContent( moveNode(node, str, astOffset, script.start, tsAst); } + const hoistable = exportedNames.hoistableInterfaces.moveHoistableInterfaces( + str, + astOffset, + script.start + 1, // +1 because imports are also moved at that position, and we want to move interfaces after imports + generics.getReferences() + ); + if (mode === 'dts') { // Transform interface declarations to type declarations because indirectly // using interfaces inside the return type of a function is forbidden. // This is not a problem for intellisense/type inference but it will // break dts generation (file will not be generated). - transformInterfacesToTypes(tsAst, str, astOffset, nodesToMove); + if (hoistable) { + transformInterfacesToTypes( + tsAst, + str, + astOffset, + [...hoistable.values()].concat(nodesToMove) + ); + } else { + transformInterfacesToTypes(tsAst, str, astOffset, nodesToMove); + } } - exportedNames.hoistableInterfaces.moveHoistableInterfaces( - str, - astOffset, - script.start, - generics.getReferences() - ); - return { exportedNames, events, diff --git a/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes2.svelte.d.ts b/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes2.svelte.d.ts index 885a90203..1cbf712b3 100644 --- a/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes2.svelte.d.ts +++ b/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes2.svelte.d.ts @@ -1,9 +1,9 @@ +import type { X } from './x'; /** asd */ -type Props = { +interface Props { foo: string; bar?: X; -}; -import type { X } from './x'; +} declare const TestRunes2: import("svelte").Component; type TestRunes2 = ReturnType; export default TestRunes2; diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-1.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-1.v5/expectedv2.ts index 36c5a9cc3..2687887c0 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-1.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-1.v5/expectedv2.ts @@ -1,13 +1,13 @@ /// ; let value = 1; -; - type NoComma = true +;;; + type NoComma = true;; type Dependency = { a: number; b: typeof value; c: NoComma - } + };; /** A comment */ interface Props { diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-2.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-2.v5/expectedv2.ts index 145efb553..556d5bb46 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-2.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-2.v5/expectedv2.ts @@ -1,11 +1,11 @@ /// ; let value = 1; -; +;;; interface Dependency { a: number; b: typeof value; - };type $$ComponentProps = { a: Dependency, b: string };;function render() { + };;type $$ComponentProps = { a: Dependency, b: string };function render() { diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-4.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-4.v5/expectedv2.ts index f16fc5f24..28978197d 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-4.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-4.v5/expectedv2.ts @@ -1,8 +1,8 @@ /// - +;; interface Dependency { a: number; - } + };; interface Props { [k: string]: Dependency; diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-5.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-5.v5/expectedv2.ts new file mode 100644 index 000000000..e3b3517c3 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-5.v5/expectedv2.ts @@ -0,0 +1,28 @@ +/// +; + import X from './X'; +;; + +import { readable } from 'svelte/store'; +; + + /** I should not be sandwitched between the imports */ + interface Props { + foo?: string; + };function render() { + + + + const store = readable(1)/*Ωignore_startΩ*/;let $store = __sveltets_2_store_get(store);/*Ωignore_endΩ*/ + + let { foo }: Props = $props() +; +async () => { + + + +$store;}; +return { props: {} as any as Props, exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} +const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; +export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-5.v5/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-5.v5/input.svelte new file mode 100644 index 000000000..91254d3c8 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-hoistable-props-5.v5/input.svelte @@ -0,0 +1,17 @@ + + + + +{$store} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes.v5/expectedv2.ts index b9ee4758c..b15ca4992 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes.v5/expectedv2.ts @@ -1,5 +1,5 @@ /// -;type $$ComponentProps = { a: number, b: string };;function render() { +;;type $$ComponentProps = { a: number, b: string };function render() { let { a, b }:/*Ωignore_startΩ*/$$ComponentProps/*Ωignore_endΩ*/ = $props(); let x = $state(0); diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged.v5/expectedv2.ts index 2bdf2e4ea..51e17c6ce 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged.v5/expectedv2.ts @@ -1,5 +1,5 @@ /// -;type $$ComponentProps = {form: boolean, data: true };;function render() { +;;type $$ComponentProps = {form: boolean, data: true };function render() { const snapshot: any = {}; let { form, data }:/*Ωignore_startΩ*/$$ComponentProps/*Ωignore_endΩ*/ = $props(); From a517b8bc3ace8dac434746e2eacf8ee7ae068019 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 20 Nov 2024 12:44:11 +0100 Subject: [PATCH 2/2] name --- .../src/svelte2tsx/processInstanceScriptContent.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts b/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts index 6f575952b..4790739cb 100644 --- a/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts +++ b/packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts @@ -310,7 +310,7 @@ export function processInstanceScriptContent( moveNode(node, str, astOffset, script.start, tsAst); } - const hoistable = exportedNames.hoistableInterfaces.moveHoistableInterfaces( + const hoisted = exportedNames.hoistableInterfaces.moveHoistableInterfaces( str, astOffset, script.start + 1, // +1 because imports are also moved at that position, and we want to move interfaces after imports @@ -322,12 +322,12 @@ export function processInstanceScriptContent( // using interfaces inside the return type of a function is forbidden. // This is not a problem for intellisense/type inference but it will // break dts generation (file will not be generated). - if (hoistable) { + if (hoisted) { transformInterfacesToTypes( tsAst, str, astOffset, - [...hoistable.values()].concat(nodesToMove) + [...hoisted.values()].concat(nodesToMove) ); } else { transformInterfacesToTypes(tsAst, str, astOffset, nodesToMove);