From 9d7907ef8b65efeda2698f61490b964d2f1a7069 Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Thu, 2 May 2024 18:08:26 +0200
Subject: [PATCH] fix: rework bindable types strategy (#2361)

Instead of using types that declare whether or not a type is bindable directly as part of the property, we're introducing a new for-types-only field to `SvelteComponent`: `$$bindings`, which is typed as the keys of the properties that are bindable (string by default, i.e. everything's bindable; for backwards compat). language-tools can then produce code that assigns to this property which results in an error we can display if the binding is invalid.
This means we can revert a lot of the changes we made to make the previous version of bindable types work
---
 .../features/DiagnosticsProvider.ts           | 110 +++++----
 .../typescript/features/RenameProvider.ts     | 156 +++++-------
 .../test/plugins/svelte/SveltePlugin.test.ts  |   4 +-
 .../svelte/features/getDiagnostics.test.ts    |   8 +-
 .../features/RenameProvider.test.ts           | 231 ++++++++++++++++++
 .../$$props-usage/expected_svelte_5.json      |  32 ---
 .../$store-bind/expected_svelte_5.json        |  46 ----
 .../fixtures/bind-this/expected_svelte_5.json |   6 +-
 .../fixtures/bindings/expected_svelte_5.json  |  25 +-
 .../component-props-js/expected_svelte_5.json |  83 -------
 .../component-props-ts/expected_svelte_5.json |  83 -------
 .../component-props-js/expected_svelte_5.json | 102 --------
 .../component-props-ts/expected_svelte_5.json | 102 --------
 .../component-invalid/expected_svelte_5.json  |  57 -----
 .../expected_svelte_5.json                    |  19 --
 .../rename/rename-runes-importer.svelte       |   8 +
 .../testfiles/rename/rename-runes.svelte      |   6 +
 .../src/htmlxtojsx_v2/nodes/Binding.ts        |  20 +-
 .../src/svelte2tsx/addComponentExport.ts      |  13 +-
 packages/svelte2tsx/src/svelte2tsx/index.ts   |   1 +
 .../src/svelte2tsx/nodes/ExportedNames.ts     |  39 +--
 packages/svelte2tsx/svelte-shims-v4.d.ts      |  16 +-
 .../expected/TestRunes.svelte.d.ts            |   5 +-
 .../samples/binding-bare/expected-svelte5.js  |   4 +-
 .../samples/binding/expected-svelte5.js       |   8 +-
 .../editing-binding/expected-svelte5.js       |   2 +-
 .../runes-best-effort-types/expectedv2.ts     |   1 +
 .../samples/runes-bindable/expectedv2.ts      |   3 +-
 .../runes-looking-like-stores/expectedv2.ts   |   1 +
 .../samples/runes-with-slots/expectedv2.ts    |   2 +-
 .../svelte2tsx/samples/runes/expectedv2.ts    |   1 +
 .../expectedv2.ts                             |   1 +
 .../expectedv2.ts                             |   1 +
 .../ts-runes-best-effort-types/expectedv2.ts  |   1 +
 .../samples/ts-runes-bindable/expectedv2.ts   |   3 +-
 .../samples/ts-runes-generics/expectedv2.ts   |   3 +-
 .../samples/ts-runes-with-slot/expectedv2.ts  |   2 +-
 .../svelte2tsx/samples/ts-runes/expectedv2.ts |   1 +
 .../expectedv2.ts                             |   1 +
 .../expectedv2.ts                             |   1 +
 40 files changed, 457 insertions(+), 751 deletions(-)
 delete mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/$$props-usage/expected_svelte_5.json
 delete mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/$store-bind/expected_svelte_5.json
 delete mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs-nostrict/component-props-js/expected_svelte_5.json
 delete mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs-nostrict/component-props-ts/expected_svelte_5.json
 delete mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs/component-props-js/expected_svelte_5.json
 delete mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs/component-props-ts/expected_svelte_5.json
 delete mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/component-invalid/expected_svelte_5.json
 delete mode 100644 packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/sveltekit-autotypings/expected_svelte_5.json
 create mode 100644 packages/language-server/test/plugins/typescript/testfiles/rename/rename-runes-importer.svelte
 create mode 100644 packages/language-server/test/plugins/typescript/testfiles/rename/rename-runes.svelte

diff --git a/packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts b/packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts
index 485b041f2..195569ca7 100644
--- a/packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts
+++ b/packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts
@@ -145,20 +145,33 @@ export class DiagnosticsProviderImpl implements DiagnosticsProvider {
 
         diagnostics = resolveNoopsInReactiveStatements(lang, diagnostics);
 
-        return diagnostics
-            .map<Diagnostic>((diagnostic) => ({
-                range: convertRange(tsDoc, diagnostic),
-                severity: mapSeverity(diagnostic.category),
+        const mapRange = rangeMapper(tsDoc, document, lang);
+        const noFalsePositive = isNoFalsePositive(document, tsDoc);
+        const converted: Diagnostic[] = [];
+
+        for (const tsDiag of diagnostics) {
+            let diagnostic: Diagnostic = {
+                range: convertRange(tsDoc, tsDiag),
+                severity: mapSeverity(tsDiag.category),
                 source: isTypescript ? 'ts' : 'js',
-                message: ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
-                code: diagnostic.code,
-                tags: getDiagnosticTag(diagnostic)
-            }))
-            .map(mapRange(tsDoc, document, lang))
-            .filter(hasNoNegativeLines)
-            .filter(isNoFalsePositive(document, tsDoc))
-            .map(adjustIfNecessary)
-            .map(swapDiagRangeStartEndIfNecessary);
+                message: ts.flattenDiagnosticMessageText(tsDiag.messageText, '\n'),
+                code: tsDiag.code,
+                tags: getDiagnosticTag(tsDiag)
+            };
+            diagnostic = mapRange(diagnostic);
+
+            moveBindingErrorMessage(tsDiag, tsDoc, diagnostic, document);
+
+            if (!hasNoNegativeLines(diagnostic) || !noFalsePositive(diagnostic)) {
+                continue;
+            }
+
+            diagnostic = adjustIfNecessary(diagnostic);
+            diagnostic = swapDiagRangeStartEndIfNecessary(diagnostic);
+            converted.push(diagnostic);
+        }
+
+        return converted;
     }
 
     private async getLSAndTSDoc(document: Document) {
@@ -166,7 +179,43 @@ export class DiagnosticsProviderImpl implements DiagnosticsProvider {
     }
 }
 
-function mapRange(
+function moveBindingErrorMessage(
+    tsDiag: ts.Diagnostic,
+    tsDoc: SvelteDocumentSnapshot,
+    diagnostic: Diagnostic,
+    document: Document
+) {
+    if (
+        tsDiag.code === DiagnosticCode.TYPE_X_NOT_ASSIGNABLE_TO_TYPE_Y &&
+        tsDiag.start &&
+        tsDoc.getText(tsDiag.start, tsDiag.start + tsDiag.length!).endsWith('.$$bindings')
+    ) {
+        let node = tsDoc.svelteNodeAt(diagnostic.range.start);
+        while (node && node.type !== 'InlineComponent') {
+            node = node.parent!;
+        }
+        if (node) {
+            let name = tsDoc.getText(
+                tsDiag.start + tsDiag.length!,
+                tsDiag.start + tsDiag.length! + 100
+            );
+            const quoteIdx = name.indexOf("'");
+            name = name.substring(quoteIdx + 1, name.indexOf("'", quoteIdx + 1));
+            const binding: any = node.attributes.find(
+                (attr: any) => attr.type === 'Binding' && attr.name === name
+            );
+            if (binding) {
+                diagnostic.message = 'Cannot bind: to this property\n\n' + diagnostic.message;
+                diagnostic.range = {
+                    start: document.positionAt(binding.start),
+                    end: document.positionAt(binding.end)
+                };
+            }
+        }
+    }
+}
+
+function rangeMapper(
     snapshot: SvelteDocumentSnapshot,
     document: Document,
     lang: ts.LanguageService
@@ -194,7 +243,7 @@ function mapRange(
                 diagnostic.code as number
             ) ||
                 (DiagnosticCode.TYPE_X_NOT_ASSIGNABLE_TO_TYPE_Y &&
-                    diagnostic.message.includes("'PropsWithChildren<"))) &&
+                    diagnostic.message.includes("'Properties<"))) &&
             !hasNonZeroRange({ range })
         ) {
             const node = getNodeIfIsInStartTag(document.html, document.offsetAt(range.start));
@@ -327,37 +376,6 @@ function adjustIfNecessary(diagnostic: Diagnostic): Diagnostic {
         };
     }
 
-    if (
-        (diagnostic.code === DiagnosticCode.TYPE_X_NOT_ASSIGNABLE_TO_TYPE_Y ||
-            diagnostic.code === DiagnosticCode.TYPE_X_NOT_ASSIGNABLE_TO_TYPE_Y_DID_YOU_MEAN) &&
-        diagnostic.message.includes("'Bindable<")
-    ) {
-        const countBindable = (diagnostic.message.match(/'Bindable\</g) || []).length;
-        const countBinding = (diagnostic.message.match(/'Binding\</g) || []).length;
-        if (countBindable === 1 && countBinding === 0) {
-            // Remove distracting Bindable<...> from diagnostic message
-            const start = diagnostic.message.indexOf("'Bindable<");
-            const startType = start + "'Bindable".length;
-            const end = traverseTypeString(diagnostic.message, startType, '>');
-            diagnostic.message =
-                diagnostic.message.substring(0, start + 1) +
-                diagnostic.message.substring(startType + 1, end) +
-                diagnostic.message.substring(end + 1);
-        } else if (countBinding === 3 && countBindable === 1) {
-            // Only keep Type '...' is not assignable to type '...' in
-            // Type Bindings<...> is not assignable to type Bindable<...>, Type Binding<...> is not assignable to type Bindable<...>, Type '...' is not assignable to type '...'
-            const lines = diagnostic.message.split('\n');
-            if (lines.length === 3) {
-                diagnostic.message = lines[2].trimStart();
-            }
-        }
-
-        return {
-            ...diagnostic,
-            message: diagnostic.message
-        };
-    }
-
     return diagnostic;
 }
 
diff --git a/packages/language-server/src/plugins/typescript/features/RenameProvider.ts b/packages/language-server/src/plugins/typescript/features/RenameProvider.ts
index 3ba763e3c..281ae659f 100644
--- a/packages/language-server/src/plugins/typescript/features/RenameProvider.ts
+++ b/packages/language-server/src/plugins/typescript/features/RenameProvider.ts
@@ -31,6 +31,7 @@ import {
 } from './utils';
 import { LSConfigManager } from '../../../ls-config';
 import { isAttributeName, isEventHandler } from '../svelte-ast-utils';
+import { Identifier } from 'estree';
 
 interface TsRenameLocation extends ts.RenameLocation {
     range: Range;
@@ -38,7 +39,6 @@ interface TsRenameLocation extends ts.RenameLocation {
 }
 
 const bind = 'bind:';
-const bindShortHandGeneratedLength = ':__sveltets_2_binding('.length;
 
 export class RenameProviderImpl implements RenameProvider {
     constructor(
@@ -76,7 +76,7 @@ export class RenameProviderImpl implements RenameProvider {
 
         const renameLocations = lang.findRenameLocations(
             tsDoc.filePath,
-            offset + (renameInfo.bindShorthand || 0),
+            offset,
             false,
             false,
             true
@@ -101,7 +101,8 @@ export class RenameProviderImpl implements RenameProvider {
         convertedRenameLocations = this.checkShortHandBindingOrSlotLetLocation(
             lang,
             convertedRenameLocations,
-            docs
+            docs,
+            newName
         );
 
         const additionalRenameForPropRenameInsideComponentWithProp =
@@ -160,7 +161,6 @@ export class RenameProviderImpl implements RenameProvider {
     ):
         | (ts.RenameInfoSuccess & {
               isStore?: boolean;
-              bindShorthand?: number;
           })
         | null {
         // Don't allow renames in error-state, because then there is no generated svelte2tsx-code
@@ -170,14 +170,6 @@ export class RenameProviderImpl implements RenameProvider {
         }
 
         const svelteNode = tsDoc.svelteNodeAt(originalPosition);
-
-        let bindOffset = 0;
-        const bindingShorthand = this.getBindingShorthand(tsDoc, originalPosition, svelteNode);
-        if (bindingShorthand) {
-            bindOffset = bindingShorthand.end - bindingShorthand.start;
-            generatedOffset += bindShortHandGeneratedLength + bindOffset;
-        }
-
         const renameInfo = lang.getRenameInfo(tsDoc.filePath, generatedOffset, {
             allowRenameOfImportPath: false
         });
@@ -211,11 +203,6 @@ export class RenameProviderImpl implements RenameProvider {
             }
         }
 
-        if (bindOffset) {
-            renameInfo.triggerSpan.start -= bindShortHandGeneratedLength + bindOffset;
-            (renameInfo as any).bindShorthand = bindShortHandGeneratedLength + bindOffset;
-        }
-
         return renameInfo;
     }
 
@@ -366,48 +353,8 @@ export class RenameProviderImpl implements RenameProvider {
                 return location;
             }
 
-            const { parent } = snapshot;
-
-            const bindingShorthand = this.getBindingShorthand(snapshot, location.range.start);
-            if (bindingShorthand) {
-                // bind:|foo| -> bind:|newName|={foo}
-                const name = parent
-                    .getText()
-                    .substring(bindingShorthand.start, bindingShorthand.end);
-                return {
-                    ...location,
-                    prefixText: '',
-                    suffixText: `={${name}}`
-                };
-            }
-
-            let rangeStart = parent.offsetAt(location.range.start);
-
-            // suffix is of the form `: oldVarName` -> hints at a shorthand
-            if (
-                !location.suffixText?.trimStart()?.startsWith(':') ||
-                !getNodeIfIsInStartTag(parent.html, rangeStart)
-            ) {
-                return location;
-            }
-
-            if (snapshot.getOriginalText().charAt(rangeStart - 1) === '{') {
-                // {|foo|} -> |{foo|}
-                rangeStart--;
-                return {
-                    ...location,
-                    range: {
-                        start: parent.positionAt(rangeStart),
-                        end: location.range.end
-                    },
-                    // |{foo|} -> newName=|{foo|}
-                    newName: parent.getText(location.range),
-                    prefixText: `${newName}={`,
-                    suffixText: ''
-                };
-            }
-
-            return location;
+            const shorthandLocation = this.transformShorthand(snapshot, location, newName);
+            return shorthandLocation || location;
         });
     }
 
@@ -595,7 +542,8 @@ export class RenameProviderImpl implements RenameProvider {
     private checkShortHandBindingOrSlotLetLocation(
         lang: ts.LanguageService,
         renameLocations: TsRenameLocation[],
-        snapshots: SnapshotMap
+        snapshots: SnapshotMap,
+        newName?: string
     ): TsRenameLocation[] {
         return renameLocations.map((location) => {
             const sourceFile = lang.getProgram()?.getSourceFile(location.fileName);
@@ -616,6 +564,16 @@ export class RenameProviderImpl implements RenameProvider {
 
             const { parent } = snapshot;
 
+            if (snapshot.isSvelte5Plus && newName && location.suffixText) {
+                // Svelte 5 runes mode, thanks to $props(), is much easier to handle rename-wise.
+                // Notably, it doesn't need the "additional props rename locations" logic, because
+                // these renames already appear here.
+                const shorthandLocation = this.transformShorthand(snapshot, location, newName);
+                if (shorthandLocation) {
+                    return shorthandLocation;
+                }
+            }
+
             let rangeStart = parent.offsetAt(location.range.start);
             let prefixText = location.prefixText?.trimRight();
 
@@ -638,38 +596,6 @@ export class RenameProviderImpl implements RenameProvider {
                         suffixText: '}'
                     };
                 }
-
-                if (snapshot.isSvelte5Plus) {
-                    const bindingShorthand = this.getBindingShorthand(
-                        snapshot,
-                        location.range.start
-                    );
-                    if (bindingShorthand) {
-                        const name = parent
-                            .getText()
-                            .substring(bindingShorthand.start, bindingShorthand.end);
-                        const start = {
-                            line: location.range.start.line,
-                            character: location.range.start.character - name.length
-                        };
-                        // If binding is followed by the closing tag, start is one character too soon,
-                        // else binding is ending one character too far
-                        if (parent.getText().charAt(parent.offsetAt(start)) === ':') {
-                            start.character++;
-                        } else {
-                            location.range.end.character--;
-                        }
-                        return {
-                            ...location,
-                            range: {
-                                start: start,
-                                end: location.range.end
-                            },
-                            prefixText: name + '={',
-                            suffixText: '}'
-                        };
-                    }
-                }
             }
 
             if (!prefixText || prefixText.slice(-1) !== ':') {
@@ -714,16 +640,54 @@ export class RenameProviderImpl implements RenameProvider {
         });
     }
 
-    private getBindingShorthand(
+    private transformShorthand(
+        snapshot: SvelteDocumentSnapshot,
+        location: TsRenameLocation,
+        newName: string
+    ): TsRenameLocation | undefined {
+        const shorthand = this.getBindingOrAttrShorthand(snapshot, location.range.start);
+        if (shorthand) {
+            if (shorthand.isBinding) {
+                // bind:|foo| -> bind:|newName|={foo}
+                return {
+                    ...location,
+                    prefixText: '',
+                    suffixText: `={${shorthand.id.name}}`
+                };
+            } else {
+                return {
+                    ...location,
+                    range: {
+                        // {|foo|} -> |{foo|}
+                        start: {
+                            line: location.range.start.line,
+                            character: location.range.start.character - 1
+                        },
+                        end: location.range.end
+                    },
+                    // |{foo|} -> newName=|{foo|}
+                    newName: shorthand.id.name,
+                    prefixText: `${newName}={`,
+                    suffixText: ''
+                };
+            }
+        }
+    }
+
+    private getBindingOrAttrShorthand(
         snapshot: SvelteDocumentSnapshot,
         position: Position,
         svelteNode = snapshot.svelteNodeAt(position)
-    ) {
+    ): { id: Identifier; isBinding: boolean } | undefined {
         if (
-            svelteNode?.parent?.type === 'Binding' &&
+            (svelteNode?.parent?.type === 'Binding' ||
+                svelteNode?.parent?.type === 'AttributeShorthand') &&
             svelteNode.parent.expression.end === svelteNode.parent.end
         ) {
-            return svelteNode.parent.expression;
+            return {
+                id: svelteNode as any as Identifier,
+                isBinding: svelteNode.parent.type === 'Binding'
+            };
         }
     }
 }
diff --git a/packages/language-server/test/plugins/svelte/SveltePlugin.test.ts b/packages/language-server/test/plugins/svelte/SveltePlugin.test.ts
index 2712a095c..1e57caea1 100644
--- a/packages/language-server/test/plugins/svelte/SveltePlugin.test.ts
+++ b/packages/language-server/test/plugins/svelte/SveltePlugin.test.ts
@@ -39,7 +39,7 @@ describe('Svelte Plugin', () => {
         const diagnostic = Diagnostic.create(
             Range.create(1, 0, 1, 21),
             isSvelte5Plus
-                ? '<img> element should have an alt attribute'
+                ? '`<img>` element should have an alt attribute'
                 : 'A11y: <img> element should have an alt attribute',
             DiagnosticSeverity.Warning,
             isSvelte5Plus ? 'a11y_missing_attribute' : 'a11y-missing-attribute',
@@ -57,7 +57,7 @@ describe('Svelte Plugin', () => {
             Range.create(0, 10, 0, 18),
             isSvelte5Plus ? 'Can only bind to state or props' : 'whatever is not declared',
             DiagnosticSeverity.Error,
-            isSvelte5Plus ? 'invalid_binding_value' : 'binding-undeclared',
+            isSvelte5Plus ? 'bind_invalid_value' : 'binding-undeclared',
             'svelte'
         );
 
diff --git a/packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts b/packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts
index 9f7ef475b..0915cc35a 100644
--- a/packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts
+++ b/packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts
@@ -459,7 +459,7 @@ describe('SveltePlugin#getDiagnostics', () => {
                     "Component has unused export property 'name'. If it is for external reference only, please consider using `export const name`",
                 severity: 2,
                 source: 'svelte',
-                code: isSvelte5Plus ? 'unused_export_let' : 'unused-export-let'
+                code: isSvelte5Plus ? 'export_let_unused' : 'unused-export-let'
             }
         ]);
     });
@@ -477,7 +477,7 @@ describe('SveltePlugin#getDiagnostics', () => {
                 severity: 2,
                 source: 'svelte',
                 code: isSvelte5Plus
-                    ? 'no_reactive_declaration'
+                    ? 'reactive_declaration_invalid_placement'
                     : 'module-script-reactive-declaration'
             }
         ]);
@@ -489,7 +489,7 @@ describe('SveltePlugin#getDiagnostics', () => {
 
         assert.deepStrictEqual(diagnostics, [
             {
-                code: isSvelte5Plus ? 'unused_export_let' : 'unused-export-let',
+                code: isSvelte5Plus ? 'export_let_unused' : 'unused-export-let',
                 message:
                     "Component has unused export property 'unused1'. If it is for external reference only, please consider using `export const unused1`",
                 range: {
@@ -506,7 +506,7 @@ describe('SveltePlugin#getDiagnostics', () => {
                 source: 'svelte'
             },
             {
-                code: isSvelte5Plus ? 'unused_export_let' : 'unused-export-let',
+                code: isSvelte5Plus ? 'export_let_unused' : 'unused-export-let',
                 message:
                     "Component has unused export property 'unused2'. If it is for external reference only, please consider using `export const unused2`",
                 range: {
diff --git a/packages/language-server/test/plugins/typescript/features/RenameProvider.test.ts b/packages/language-server/test/plugins/typescript/features/RenameProvider.test.ts
index 3ad669246..d3ce11c92 100644
--- a/packages/language-server/test/plugins/typescript/features/RenameProvider.test.ts
+++ b/packages/language-server/test/plugins/typescript/features/RenameProvider.test.ts
@@ -2,6 +2,7 @@ import * as assert from 'assert';
 import * as path from 'path';
 import ts from 'typescript';
 import { Position } from 'vscode-languageserver';
+import { VERSION } from 'svelte/compiler';
 import { Document, DocumentManager } from '../../../../src/lib/documents';
 import { LSConfigManager } from '../../../../src/ls-config';
 import { RenameProviderImpl } from '../../../../src/plugins/typescript/features/RenameProvider';
@@ -12,6 +13,7 @@ import { serviceWarmup } from '../test-utils';
 
 const testDir = path.join(__dirname, '..');
 const renameTestDir = path.join(testDir, 'testfiles', 'rename');
+const isSvelte5Plus = +VERSION.split('.')[0] >= 5;
 
 describe('RenameProvider', function () {
     serviceWarmup(this, renameTestDir, pathToUrl(testDir));
@@ -46,6 +48,8 @@ describe('RenameProvider', function () {
         const renameDocPropWithSlotEvents = await openDoc('rename-prop-with-slot-events.svelte');
         const renameDocShorthand = await openDoc('rename-shorthand.svelte');
         const renameSlotLet = await openDoc('rename-slot-let.svelte');
+        const renameRunes = await openDoc('rename-runes.svelte');
+        const renameRunesImporter = await openDoc('rename-runes-importer.svelte');
 
         return {
             provider,
@@ -60,6 +64,8 @@ describe('RenameProvider', function () {
             renameDocPropWithSlotEvents,
             renameDocShorthand,
             renameSlotLet,
+            renameRunes,
+            renameRunesImporter,
             docManager
         };
 
@@ -871,4 +877,229 @@ describe('RenameProvider', function () {
         // Hacky, but it works. Needed due to testing both new and old transformation
         __resetCache();
     });
+
+    // -------------------- put tests that only run in Svelte 5 below this line and everything else above --------------------
+    if (!isSvelte5Plus) return;
+
+    it('renames $props() prop from inside component', async () => {
+        const { provider, renameRunes } = await setup();
+
+        const result = await provider.rename(renameRunes, Position.create(1, 40), 'newName');
+
+        assert.deepStrictEqual(result, {
+            changes: {
+                [getUri('rename-runes.svelte')]: [
+                    {
+                        newText: 'newName',
+                        range: {
+                            start: {
+                                line: 1,
+                                character: 38
+                            },
+                            end: {
+                                line: 1,
+                                character: 41
+                            }
+                        }
+                    },
+                    {
+                        newText: 'newName: foo',
+                        range: {
+                            start: {
+                                line: 1,
+                                character: 10
+                            },
+                            end: {
+                                line: 1,
+                                character: 13
+                            }
+                        }
+                    }
+                ],
+                [getUri('rename-runes-importer.svelte')]: [
+                    {
+                        newText: 'newName={foo',
+                        range: {
+                            start: {
+                                line: 6,
+                                character: 13
+                            },
+                            end: {
+                                line: 6,
+                                character: 17
+                            }
+                        }
+                    },
+                    {
+                        newText: 'newName',
+                        range: {
+                            start: {
+                                line: 7,
+                                character: 13
+                            },
+                            end: {
+                                line: 7,
+                                character: 16
+                            }
+                        }
+                    }
+                ]
+            }
+        });
+    });
+
+    it('renames $props() binding from inside component', async () => {
+        const { provider, renameRunes } = await setup();
+
+        const result = await provider.rename(renameRunes, Position.create(1, 54), 'newName');
+
+        console.log(JSON.stringify(result, null, 2));
+
+        assert.deepStrictEqual(result, {
+            changes: {
+                [getUri('rename-runes.svelte')]: [
+                    {
+                        newText: 'newName',
+                        range: {
+                            start: {
+                                line: 1,
+                                character: 52
+                            },
+                            end: {
+                                line: 1,
+                                character: 55
+                            }
+                        }
+                    },
+                    {
+                        newText: 'newName: bar',
+                        range: {
+                            start: {
+                                line: 1,
+                                character: 15
+                            },
+                            end: {
+                                line: 1,
+                                character: 18
+                            }
+                        }
+                    }
+                ],
+                [getUri('rename-runes-importer.svelte')]: [
+                    {
+                        newText: 'newName={bar}',
+                        range: {
+                            start: {
+                                line: 6,
+                                character: 24
+                            },
+                            end: {
+                                line: 6,
+                                character: 27
+                            }
+                        }
+                    },
+                    {
+                        newText: 'newName',
+                        range: {
+                            start: {
+                                line: 7,
+                                character: 28
+                            },
+                            end: {
+                                line: 7,
+                                character: 31
+                            }
+                        }
+                    }
+                ]
+            }
+        });
+    });
+
+    // blocked by https://github.com/microsoft/TypeScript/pull/57201
+    it.skip('renames $props() prop inside consumer', async () => {
+        const { provider, renameRunes } = await setup();
+
+        const result = await provider.rename(renameRunes, Position.create(7, 15), 'newName');
+
+        assert.deepStrictEqual(result, {
+            changes: {
+                // TODO complete once test can be unskipped
+                [getUri('rename-runes.svelte')]: [],
+                [getUri('rename-runes-importer.svelte')]: []
+            }
+        });
+    });
+
+    it('renames $props() binding in consumer', async () => {
+        const { provider, renameRunesImporter } = await setup();
+
+        const result = await provider.rename(
+            renameRunesImporter,
+            Position.create(7, 30),
+            'newName'
+        );
+
+        assert.deepStrictEqual(result, {
+            changes: {
+                [getUri('rename-runes.svelte')]: [
+                    {
+                        newText: 'newName',
+                        range: {
+                            start: {
+                                line: 1,
+                                character: 52
+                            },
+                            end: {
+                                line: 1,
+                                character: 55
+                            }
+                        }
+                    },
+                    {
+                        newText: 'newName: bar',
+                        range: {
+                            start: {
+                                line: 1,
+                                character: 15
+                            },
+                            end: {
+                                line: 1,
+                                character: 18
+                            }
+                        }
+                    }
+                ],
+                [getUri('rename-runes-importer.svelte')]: [
+                    {
+                        newText: 'newName={bar}',
+                        range: {
+                            start: {
+                                line: 6,
+                                character: 24
+                            },
+                            end: {
+                                line: 6,
+                                character: 27
+                            }
+                        }
+                    },
+                    {
+                        newText: 'newName',
+                        range: {
+                            start: {
+                                line: 7,
+                                character: 28
+                            },
+                            end: {
+                                line: 7,
+                                character: 31
+                            }
+                        }
+                    }
+                ]
+            }
+        });
+    });
 });
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/$$props-usage/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/$$props-usage/expected_svelte_5.json
deleted file mode 100644
index f98179a38..000000000
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/$$props-usage/expected_svelte_5.json
+++ /dev/null
@@ -1,32 +0,0 @@
-[
-    {
-        "range": {
-            "start": { "line": 10, "character": 7 },
-            "end": { "line": 10, "character": 16 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 11, "character": 43 },
-            "end": { "line": 11, "character": 54 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Object literal may only specify known properties, and '\"invalidProp\"' does not exist in type 'WithBindings<{ exported1: string; exported2?: string | undefined; exported3: string; }>'.",
-        "code": 2353,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 12, "character": 1 }, "end": { "line": 12, "character": 6 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type '{}' is missing the following properties from type 'WithBindings<{ exported1: string; exported2?: string | undefined; exported3: string; }>': exported1, exported3",
-        "code": 2739,
-        "tags": []
-    }
-]
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/$store-bind/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/$store-bind/expected_svelte_5.json
deleted file mode 100644
index 9c6757bf6..000000000
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/$store-bind/expected_svelte_5.json
+++ /dev/null
@@ -1,46 +0,0 @@
-[
-    {
-        "range": {
-            "start": { "line": 17, "character": 24 },
-            "end": { "line": 17, "character": 34 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'number' is not assignable to type 'boolean'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 18, "character": 16 },
-            "end": { "line": 18, "character": 20 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'number'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 19, "character": 24 },
-            "end": { "line": 19, "character": 41 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'number' is not assignable to type 'boolean'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 20, "character": 16 },
-            "end": { "line": 20, "character": 20 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'Binding<boolean>' is not assignable to type 'Bindable<number>'.",
-        "code": 2322,
-        "tags": []
-    }
-]
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bind-this/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bind-this/expected_svelte_5.json
index 09171f654..06c967066 100644
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bind-this/expected_svelte_5.json
+++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bind-this/expected_svelte_5.json
@@ -36,7 +36,7 @@
         },
         "severity": 1,
         "source": "ts",
-        "message": "Type 'Component' is not assignable to type 'OtherComponent'.\n  The types of '$$prop_def.prop' are incompatible between these types.\n    Type 'boolean' is not assignable to type 'string'.",
+        "message": "Type 'Component' is not assignable to type 'OtherComponent'.\n  Type '{ prop: boolean; }' is not assignable to type '{ prop: string; }'.\n    Types of property 'prop' are incompatible.\n      Type 'boolean' is not assignable to type 'string'.",
         "code": 2322,
         "tags": []
     },
@@ -58,7 +58,7 @@
         },
         "severity": 1,
         "source": "ts",
-        "message": "Type '{}' is not assignable to type 'PropsWithChildren<{ prop: boolean; }, any> | undefined'.",
+        "message": "Type '{}' is not assignable to type 'Properties<{ prop: boolean; }, any> | undefined'.",
         "code": 2322,
         "tags": []
     },
@@ -91,7 +91,7 @@
         },
         "severity": 1,
         "source": "ts",
-        "message": "Type '{}' is not assignable to type 'PropsWithChildren<{ prop: boolean; }, any> | undefined'.",
+        "message": "Type '{}' is not assignable to type 'Properties<{ prop: boolean; }, any> | undefined'.",
         "code": 2322,
         "tags": []
     }
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings/expected_svelte_5.json
index abd096989..2bc241532 100644
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings/expected_svelte_5.json
+++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/bindings/expected_svelte_5.json
@@ -1,14 +1,14 @@
 [
     {
         "code": 2322,
-        "message": "Type 'Binding<string>' is not assignable to type 'string'.",
+        "message": "Cannot bind: to this property\n\nType '\"readonly\"' is not assignable to type '\"can_bind\"'.",
         "range": {
             "end": {
                 "character": 20,
                 "line": 25
             },
             "start": {
-                "character": 12,
+                "character": 7,
                 "line": 25
             }
         },
@@ -18,7 +18,7 @@
     },
     {
         "code": 2353,
-        "message": "Object literal may only specify known properties, and 'only_bind' does not exist in type '{ readonly?: string | undefined; can_bind?: Bindable<string | undefined>; }'.",
+        "message": "Object literal may only specify known properties, and 'only_bind' does not exist in type '$$ComponentProps'.",
         "range": {
             "end": {
                 "character": 21,
@@ -33,9 +33,26 @@
         "source": "ts",
         "tags": []
     },
+    {
+        "code": 2322,
+        "message": "Cannot bind: to this property\n\nType '\"only_bind\"' is not assignable to type '\"can_bind\"'.",
+        "range": {
+            "end": {
+                "character": 21,
+                "line": 26
+            },
+            "start": {
+                "character": 7,
+                "line": 26
+            }
+        },
+        "severity": 1,
+        "source": "ts",
+        "tags": []
+    },
     {
         "code": 2353,
-        "message": "Object literal may only specify known properties, and 'only_bind' does not exist in type '{ readonly?: string | undefined; can_bind?: Bindable<string | undefined>; }'.",
+        "message": "Object literal may only specify known properties, and 'only_bind' does not exist in type '$$ComponentProps'.",
         "range": {
             "end": {
                 "character": 17,
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs-nostrict/component-props-js/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs-nostrict/component-props-js/expected_svelte_5.json
deleted file mode 100644
index b17cb454d..000000000
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs-nostrict/component-props-js/expected_svelte_5.json
+++ /dev/null
@@ -1,83 +0,0 @@
-[
-    {
-        "range": { "start": { "line": 6, "character": 1 }, "end": { "line": 6, "character": 9 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Property 'required' is missing in type '{}' but required in type 'WithBindings<{ required: string; optional1?: string; optional2?: string; }>'.",
-        "code": 2741,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 8, "character": 57 }, "end": { "line": 8, "character": 68 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Object literal may only specify known properties, and '\"doesntExist\"' does not exist in type 'WithBindings<{ required: string; optional1?: string; optional2?: string; }>'.",
-        "code": 2353,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 10 }, "end": { "line": 9, "character": 18 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 26 }, "end": { "line": 9, "character": 35 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 43 }, "end": { "line": 9, "character": 52 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 10, "character": 1 }, "end": { "line": 10, "character": 9 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Property 'required' is missing in type '{}' but required in type 'WithBindings<{ required: string; optional1?: string; optional2?: string; }>'.",
-        "code": 2741,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 10 },
-            "end": { "line": 12, "character": 18 }
-        },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 26 },
-            "end": { "line": 12, "character": 35 }
-        },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 43 },
-            "end": { "line": 12, "character": 52 }
-        },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    }
-]
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs-nostrict/component-props-ts/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs-nostrict/component-props-ts/expected_svelte_5.json
deleted file mode 100644
index 1468ca1c4..000000000
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs-nostrict/component-props-ts/expected_svelte_5.json
+++ /dev/null
@@ -1,83 +0,0 @@
-[
-    {
-        "range": { "start": { "line": 6, "character": 1 }, "end": { "line": 6, "character": 9 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Property 'required' is missing in type '{}' but required in type 'WithBindings<{ required: string; optional1?: string; optional2?: string; }>'.",
-        "code": 2741,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 8, "character": 57 }, "end": { "line": 8, "character": 68 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Object literal may only specify known properties, and '\"doesntExist\"' does not exist in type 'WithBindings<{ required: string; optional1?: string; optional2?: string; }>'.",
-        "code": 2353,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 10 }, "end": { "line": 9, "character": 18 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 26 }, "end": { "line": 9, "character": 35 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 43 }, "end": { "line": 9, "character": 52 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 10, "character": 1 }, "end": { "line": 10, "character": 9 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Property 'required' is missing in type '{}' but required in type 'WithBindings<{ required: string; optional1?: string; optional2?: string; }>'.",
-        "code": 2741,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 10 },
-            "end": { "line": 12, "character": 18 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 26 },
-            "end": { "line": 12, "character": 35 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 43 },
-            "end": { "line": 12, "character": 52 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    }
-]
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs/component-props-js/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs/component-props-js/expected_svelte_5.json
deleted file mode 100644
index 89c2f2631..000000000
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs/component-props-js/expected_svelte_5.json
+++ /dev/null
@@ -1,102 +0,0 @@
-[
-    {
-        "range": { "start": { "line": 6, "character": 1 }, "end": { "line": 6, "character": 9 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Property 'required' is missing in type '{}' but required in type 'WithBindings<{ required: string; optional1?: string | undefined; optional2?: string | undefined; }>'.",
-        "code": 2741,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 7, "character": 10 }, "end": { "line": 7, "character": 18 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'undefined' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 8, "character": 57 }, "end": { "line": 8, "character": 68 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Object literal may only specify known properties, and '\"doesntExist\"' does not exist in type 'WithBindings<{ required: string; optional1?: string | undefined; optional2?: string | undefined; }>'.",
-        "code": 2353,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 10 }, "end": { "line": 9, "character": 18 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 26 }, "end": { "line": 9, "character": 35 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'true' is not assignable to type 'string | undefined'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 43 }, "end": { "line": 9, "character": 52 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'true' is not assignable to type 'string | undefined'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 10, "character": 1 }, "end": { "line": 10, "character": 9 } },
-        "severity": 1,
-        "source": "js",
-        "message": "Property 'required' is missing in type '{}' but required in type 'WithBindings<{ required: string; optional1?: string | undefined; optional2?: string | undefined; }>'.",
-        "code": 2741,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 11, "character": 10 },
-            "end": { "line": 11, "character": 18 }
-        },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'undefined' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 10 },
-            "end": { "line": 12, "character": 18 }
-        },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 26 },
-            "end": { "line": 12, "character": 35 }
-        },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'true' is not assignable to type 'string | undefined'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 43 },
-            "end": { "line": 12, "character": 52 }
-        },
-        "severity": 1,
-        "source": "js",
-        "message": "Type 'true' is not assignable to type 'string | undefined'.",
-        "code": 2322,
-        "tags": []
-    }
-]
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs/component-props-ts/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs/component-props-ts/expected_svelte_5.json
deleted file mode 100644
index 097f3edf7..000000000
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/checkjs/component-props-ts/expected_svelte_5.json
+++ /dev/null
@@ -1,102 +0,0 @@
-[
-    {
-        "range": { "start": { "line": 6, "character": 1 }, "end": { "line": 6, "character": 9 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Property 'required' is missing in type '{}' but required in type 'WithBindings<{ required: string; optional1?: string | undefined; optional2?: string | undefined; }>'.",
-        "code": 2741,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 7, "character": 10 }, "end": { "line": 7, "character": 18 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'undefined' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 8, "character": 57 }, "end": { "line": 8, "character": 68 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Object literal may only specify known properties, and '\"doesntExist\"' does not exist in type 'WithBindings<{ required: string; optional1?: string | undefined; optional2?: string | undefined; }>'.",
-        "code": 2353,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 10 }, "end": { "line": 9, "character": 18 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 26 }, "end": { "line": 9, "character": 35 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'true' is not assignable to type 'string | undefined'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 9, "character": 43 }, "end": { "line": 9, "character": 52 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'true' is not assignable to type 'string | undefined'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": { "start": { "line": 10, "character": 1 }, "end": { "line": 10, "character": 9 } },
-        "severity": 1,
-        "source": "ts",
-        "message": "Property 'required' is missing in type '{}' but required in type 'WithBindings<{ required: string; optional1?: string | undefined; optional2?: string | undefined; }>'.",
-        "code": 2741,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 11, "character": 10 },
-            "end": { "line": 11, "character": 18 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'undefined' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 10 },
-            "end": { "line": 12, "character": 18 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'string'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 26 },
-            "end": { "line": 12, "character": 35 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'true' is not assignable to type 'string | undefined'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 12, "character": 43 },
-            "end": { "line": 12, "character": 52 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'true' is not assignable to type 'string | undefined'.",
-        "code": 2322,
-        "tags": []
-    }
-]
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/component-invalid/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/component-invalid/expected_svelte_5.json
deleted file mode 100644
index 81e4cc4cd..000000000
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/component-invalid/expected_svelte_5.json
+++ /dev/null
@@ -1,57 +0,0 @@
-[
-    {
-        "range": {
-            "start": { "line": 19, "character": 1 },
-            "end": { "line": 19, "character": 11 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Argument of type 'typeof DoesntWork' is not assignable to parameter of type 'ConstructorOfATypedSvelteComponent'.\n  Type 'DoesntWork' is missing the following properties from type 'ATypedSvelteComponent': $$prop_def, $$events_def, $$slot_def, $on\n\nPossible causes:\n- You use the instance type of a component where you should use the constructor type\n- Type definitions are missing for this Svelte Component. If you are using Svelte 3.31+, use SvelteComponentTyped to add a definition:\n  import type { SvelteComponentTyped } from \"svelte\";\n  class ComponentName extends SvelteComponentTyped<{propertyName: string;}> {}",
-        "code": 2345,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 20, "character": 10 },
-            "end": { "line": 20, "character": 25 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Type 'boolean' is not assignable to type 'Binding<never>'.",
-        "code": 2322,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 21, "character": 24 },
-            "end": { "line": 21, "character": 34 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Argument of type 'typeof DoesntWork' is not assignable to parameter of type 'ConstructorOfATypedSvelteComponent'.\n\nPossible causes:\n- You use the instance type of a component where you should use the constructor type\n- Type definitions are missing for this Svelte Component. If you are using Svelte 3.31+, use SvelteComponentTyped to add a definition:\n  import type { SvelteComponentTyped } from \"svelte\";\n  class ComponentName extends SvelteComponentTyped<{propertyName: string;}> {}",
-        "code": 2345,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 24, "character": 1 },
-            "end": { "line": 24, "character": 11 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Argument of type 'typeof DoesntWork' is not assignable to parameter of type 'ConstructorOfATypedSvelteComponent'.\n\nPossible causes:\n- You use the instance type of a component where you should use the constructor type\n- Type definitions are missing for this Svelte Component. If you are using Svelte 3.31+, use SvelteComponentTyped to add a definition:\n  import type { SvelteComponentTyped } from \"svelte\";\n  class ComponentName extends SvelteComponentTyped<{propertyName: string;}> {}",
-        "code": 2345,
-        "tags": []
-    },
-    {
-        "range": {
-            "start": { "line": 27, "character": 24 },
-            "end": { "line": 27, "character": 34 }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Argument of type 'typeof DoesntWork' is not assignable to parameter of type 'ConstructorOfATypedSvelteComponent'.\n\nPossible causes:\n- You use the instance type of a component where you should use the constructor type\n- Type definitions are missing for this Svelte Component. If you are using Svelte 3.31+, use SvelteComponentTyped to add a definition:\n  import type { SvelteComponentTyped } from \"svelte\";\n  class ComponentName extends SvelteComponentTyped<{propertyName: string;}> {}",
-        "code": 2345,
-        "tags": []
-    }
-]
diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/sveltekit-autotypings/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/sveltekit-autotypings/expected_svelte_5.json
deleted file mode 100644
index 5243c7e4b..000000000
--- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/sveltekit-autotypings/expected_svelte_5.json
+++ /dev/null
@@ -1,19 +0,0 @@
-[
-    {
-        "range": {
-            "start": {
-                "line": 5,
-                "character": 1
-            },
-            "end": {
-                "line": 5,
-                "character": 5
-            }
-        },
-        "severity": 1,
-        "source": "ts",
-        "message": "Property 'data' is missing in type '{}' but required in type 'WithBindings<{ data: { exists: boolean; }; }>'.",
-        "code": 2741,
-        "tags": []
-    }
-]
diff --git a/packages/language-server/test/plugins/typescript/testfiles/rename/rename-runes-importer.svelte b/packages/language-server/test/plugins/typescript/testfiles/rename/rename-runes-importer.svelte
new file mode 100644
index 000000000..0856cf355
--- /dev/null
+++ b/packages/language-server/test/plugins/typescript/testfiles/rename/rename-runes-importer.svelte
@@ -0,0 +1,8 @@
+<script>
+    import RenameRunes from "./rename-runes.svelte";
+    let foo = $state('');
+    let bar = $state('');
+</script>
+
+<RenameRunes {foo} bind:bar />
+<RenameRunes foo={foo} bind:bar={bar} />
diff --git a/packages/language-server/test/plugins/typescript/testfiles/rename/rename-runes.svelte b/packages/language-server/test/plugins/typescript/testfiles/rename/rename-runes.svelte
new file mode 100644
index 000000000..5ccefe729
--- /dev/null
+++ b/packages/language-server/test/plugins/typescript/testfiles/rename/rename-runes.svelte
@@ -0,0 +1,6 @@
+<script lang="ts">
+    let { foo, bar = $bindable() }: { foo?: string; bar?: string; } = $props();
+</script>
+
+{foo}
+{bar}
\ No newline at end of file
diff --git a/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts b/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts
index 16bffb8b8..5bb17cca7 100644
--- a/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts
+++ b/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts
@@ -125,18 +125,14 @@ export function handleBinding(
     const value: TransformationArray | undefined = isShorthand
         ? preserveBind && element instanceof Element
             ? [rangeWithTrailingPropertyAccess(str.original, attr.expression)]
-            : isSvelte5Plus
-              ? [
-                    `__sveltets_2_binding(${str.original.substring(attr.expression.start, attr.expression.end)})`
-                ]
-              : undefined
-        : element instanceof Element || !isSvelte5Plus
-          ? [rangeWithTrailingPropertyAccess(str.original, attr.expression)]
-          : [
-                '__sveltets_2_binding(',
-                rangeWithTrailingPropertyAccess(str.original, attr.expression),
-                ')'
-            ];
+            : undefined
+        : [rangeWithTrailingPropertyAccess(str.original, attr.expression)];
+
+    if (isSvelte5Plus && element instanceof InlineComponent) {
+        // To check if property is actually bindable
+        element.appendToStartEnd([`${element.name}.$$bindings = '${attr.name}';`]);
+    }
+
     if (element instanceof Element) {
         element.addAttribute(name, value);
     } else {
diff --git a/packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts b/packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts
index 3de132df3..fd7c04610 100644
--- a/packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts
+++ b/packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts
@@ -20,6 +20,7 @@ export interface AddComponentExportPara {
     componentDocumentation: ComponentDocumentation;
     mode: 'ts' | 'dts' | 'tsx';
     generics: Generics;
+    usesSlots: boolean;
     noSvelteComponentTyped?: boolean;
 }
 
@@ -47,6 +48,7 @@ function addGenericsComponentExport({
     usesAccessors,
     str,
     generics,
+    usesSlots,
     noSvelteComponentTyped
 }: AddComponentExportPara) {
     const genericsDef = generics.toDefinitionString();
@@ -86,7 +88,10 @@ class __sveltets_Render${genericsDef} {
      */
     let customConstructor = '';
     if (exportedNames.hasPropsRune()) {
-        customConstructor = `\n    constructor(options: import('svelte').ComponentConstructorOptions<__sveltets_2_PropsWithChildren<${returnType('props')}, ${returnType('slots')}>>) { super(options); }`;
+        if (!usesSlots) {
+            customConstructor = `\n    constructor(options: import('svelte').ComponentConstructorOptions<${returnType('props')}>) { super(options); }`;
+        }
+        customConstructor += exportedNames.createBindingsStr();
     }
 
     if (mode === 'dts') {
@@ -128,6 +133,7 @@ function addSimpleComponentExport({
     mode,
     usesAccessors,
     str,
+    usesSlots,
     noSvelteComponentTyped
 }: AddComponentExportPara) {
     const propDef = props(
@@ -146,7 +152,10 @@ function addSimpleComponentExport({
      */
     let customConstructor = '';
     if (exportedNames.hasPropsRune()) {
-        customConstructor = `\n    constructor(options = __sveltets_2_runes_constructor(${propDef})) { super(options); }`;
+        if (!usesSlots) {
+            customConstructor = `\n    constructor(options = __sveltets_2_runes_constructor(${propDef})) { super(options); }`;
+        }
+        customConstructor += exportedNames.createBindingsStr();
     }
 
     let statement: string;
diff --git a/packages/svelte2tsx/src/svelte2tsx/index.ts b/packages/svelte2tsx/src/svelte2tsx/index.ts
index 181f8da18..96d8f91b8 100644
--- a/packages/svelte2tsx/src/svelte2tsx/index.ts
+++ b/packages/svelte2tsx/src/svelte2tsx/index.ts
@@ -437,6 +437,7 @@ export function svelte2tsx(
         isTsFile: options?.isTsFile,
         exportedNames,
         usesAccessors,
+        usesSlots: slots.size > 0,
         fileName: options?.filename,
         componentDocumentation,
         mode: options.mode,
diff --git a/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts b/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts
index 209533261..fdf0ecfbf 100644
--- a/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts
+++ b/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts
@@ -614,41 +614,11 @@ export class ExportedNames {
         const names = Array.from(this.exports.entries());
 
         if (this.$props.type) {
-            return (
-                '{} as any as ' +
-                (this.$props.bindings.length
-                    ? `__sveltets_2_Bindings<${this.$props.type}, ${this.$props.bindings.map((b) => `"${b}"`).join('|')}>`
-                    : this.$props.type)
-            );
+            return '{} as any as ' + this.$props.type;
         }
 
         if (this.$props.comment) {
-            let result = this.$props.comment + '({})';
-
-            // Try our best to incorporate __sveltets_2_Bindings here
-            let idx = this.$props.comment.indexOf('@type');
-            if (idx !== -1 && /[\s{]/.test(this.$props.comment[idx + 5])) {
-                idx = this.$props.comment.indexOf('{', idx);
-                const end = this.$props.comment.lastIndexOf('}');
-                if (idx !== -1 && end !== -1 && idx < end) {
-                    const has_bindings = this.$props.bindings.length;
-
-                    if (has_bindings) {
-                        idx++;
-                        result =
-                            this.$props.comment.slice(0, idx) +
-                            (has_bindings ? '__sveltets_2_Bindings<' : '') +
-                            this.$props.comment.slice(idx, end) +
-                            (has_bindings
-                                ? `, ${this.$props.bindings.map((b) => `"${b}"`).join('|')}>`
-                                : '') +
-                            this.$props.comment.slice(end) +
-                            '({})';
-                    }
-                }
-            }
-
-            return result;
+            return this.$props.comment + '({})';
         }
 
         if (this.usesRunes()) {
@@ -700,6 +670,11 @@ export class ExportedNames {
         return `{${returnElements.join(' , ')}} as {${returnElementsType.join(', ')}}`;
     }
 
+    createBindingsStr(): string {
+        // will be just the empty strings for zero bindings, which is impossible to create a binding for, so it works out fine
+        return `\n    $$bindings = __sveltets_$$bindings('${this.$props.bindings.join("', '")}');`;
+    }
+
     /**
      * In runes mode, exports are no longer part of props because you cannot `bind:` to them,
      * which is why we need a separate return type for them.
diff --git a/packages/svelte2tsx/svelte-shims-v4.d.ts b/packages/svelte2tsx/svelte-shims-v4.d.ts
index 51ca1dd9e..e83032bc7 100644
--- a/packages/svelte2tsx/svelte-shims-v4.d.ts
+++ b/packages/svelte2tsx/svelte-shims-v4.d.ts
@@ -93,7 +93,7 @@ declare function __sveltets_2_with_any<Props = {}, Events = {}, Slots = {}>(
 
 declare function __sveltets_2_with_any_event<Props = {}, Events = {}, Slots = {}>(
     render: {props: Props, events: Events, slots: Slots }
-): {props: Expand<Props>, events: Events & {[evt: string]: CustomEvent<any>;}, slots: Slots }
+): {props: Props, events: Events & {[evt: string]: CustomEvent<any>;}, slots: Slots }
 
 declare function __sveltets_2_store_get<T = any>(store: SvelteStore<T>): T
 declare function __sveltets_2_store_get<Store extends SvelteStore<any> | undefined | null>(store: Store): Store extends SvelteStore<infer T> ? T : Store;
@@ -229,16 +229,6 @@ declare function __sveltets_2_ensureComponent<T extends ConstructorOfATypedSvelt
 
 declare function __sveltets_2_ensureArray<T extends ArrayLike<unknown> | Iterable<unknown>>(array: T): T extends ArrayLike<infer U> ? U[] : T extends Iterable<infer U> ? Iterable<U> : any[];
 
-declare type __sveltets_2_Bindings<Props extends Record<string, any>, Bindings extends string> = {
-    [K in keyof Props]: K extends Bindings ?
-        // @ts-ignore not available in Svelte 4
-        import('svelte').Bindable<Props[K]> : 
-        Props[K]
-    };
-declare function __sveltets_2_binding<T>(prop: T): 
-    // @ts-ignore not available in Svelte 4    
-    import('svelte').Binding<T>;
-
 type __sveltets_2_PropsWithChildren<Props, Slots> = Props &
     (Slots extends { default: any }
         // This is unfortunate because it means "accepts no props" turns into "accepts any prop"
@@ -248,4 +238,6 @@ type __sveltets_2_PropsWithChildren<Props, Slots> = Props &
         ? any
         : { children?: any }
         : {});
-declare function __sveltets_2_runes_constructor<Props extends {}, Events extends {}, Slots extends {}>(render: {props: Props, events: Events, slots: Slots }): import("svelte").ComponentConstructorOptions<__sveltets_2_PropsWithChildren<Props, Slots>>;
+declare function __sveltets_2_runes_constructor<Props extends {}>(render: {props: Props }): import("svelte").ComponentConstructorOptions<Props>;
+
+declare function __sveltets_$$bindings<Bindings extends string[]>(...bindings: Bindings): Bindings[number];
diff --git a/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes.svelte.d.ts b/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes.svelte.d.ts
index 237326ae8..e13bde503 100644
--- a/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes.svelte.d.ts
+++ b/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes.svelte.d.ts
@@ -2,7 +2,7 @@ import { SvelteComponent } from "svelte";
 declare const __propDef: {
     props: {
         foo: string;
-        bar?: import("svelte").Bindable<number>;
+        bar?: number;
     };
     events: {
         [evt: string]: CustomEvent<any>;
@@ -15,8 +15,9 @@ export type TestRunesSlots = typeof __propDef.slots;
 export default class TestRunes extends SvelteComponent<TestRunesProps, TestRunesEvents, TestRunesSlots> {
     constructor(options?: import("svelte").ComponentConstructorOptions<{
         foo: string;
-        bar?: import("svelte").Bindable<number>;
+        bar?: number;
     }>);
+    $$bindings: "bar";
     get baz(): () => void;
 }
 export {};
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-bare/expected-svelte5.js b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-bare/expected-svelte5.js
index 9abd39296..bb934f658 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-bare/expected-svelte5.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-bare/expected-svelte5.js
@@ -1,5 +1,5 @@
  { svelteHTML.createElement("input", {  "type":`text`,"bind:value":value,});/*Ωignore_startΩ*/() => value = __sveltets_2_any(null);/*Ωignore_endΩ*/}
  { svelteHTML.createElement("input", {  "type":`checkbox`,"bind:checked":checked,});/*Ωignore_startΩ*/() => checked = __sveltets_2_any(null);/*Ωignore_endΩ*/}
 
- { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); new $$_tupnI0C({ target: __sveltets_2_any(), props: {  "type":`text`,value:__sveltets_2_binding(value),}});/*Ωignore_startΩ*/() => value = __sveltets_2_any(null);/*Ωignore_endΩ*/}
-  { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); new $$_tupnI0C({ target: __sveltets_2_any(), props: {  "type":`checkbox`,checked:__sveltets_2_binding(checked),}});/*Ωignore_startΩ*/() => checked = __sveltets_2_any(null);/*Ωignore_endΩ*/}
\ No newline at end of file
+ { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: {  "type":`text`,value,}});/*Ωignore_startΩ*/() => value = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';}
+  { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: {  "type":`checkbox`,checked,}});/*Ωignore_startΩ*/() => checked = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'checked';}
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding/expected-svelte5.js b/packages/svelte2tsx/test/htmlx2jsx/samples/binding/expected-svelte5.js
index 274bfaf63..6c881fec9 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding/expected-svelte5.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding/expected-svelte5.js
@@ -2,7 +2,7 @@
  { svelteHTML.createElement("input", {   "type":`text`,"bind:value":test,});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/}
  { svelteHTML.createElement("input", {   "type":`text`,"bind:value":test,});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/}
 
-  { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); new $$_tupnI0C({ target: __sveltets_2_any(), props: {   "type":`text`,value:__sveltets_2_binding(test),}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/}
- { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); new $$_tupnI0C({ target: __sveltets_2_any(), props: {    "type":`text`,value:__sveltets_2_binding(test),}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/}
- { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); new $$_tupnI0C({ target: __sveltets_2_any(), props: {    "type":`text`,value:__sveltets_2_binding(test),}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/}
- { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); new $$_tupnI0C({ target: __sveltets_2_any(), props: {   "type":`text`,value:__sveltets_2_binding(test),}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/ Input}
\ No newline at end of file
+  { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: {   "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';}
+ { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: {    "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';}
+ { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: {    "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';}
+ { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: {   "type":`text`,value:test,}});/*Ωignore_startΩ*/() => test = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value'; Input}
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/editing-binding/expected-svelte5.js b/packages/svelte2tsx/test/htmlx2jsx/samples/editing-binding/expected-svelte5.js
index 7bcb65399..7bb207fea 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/editing-binding/expected-svelte5.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/editing-binding/expected-svelte5.js
@@ -1,3 +1,3 @@
   { svelteHTML.createElement("input", { });obj = __sveltets_2_any(null);}
  { svelteHTML.createElement("input", { "bind:value":obj.,});/*Ωignore_startΩ*/() => obj = __sveltets_2_any(null);/*Ωignore_endΩ*/}
- { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); new $$_tupnI0C({ target: __sveltets_2_any(), props: {   value:__sveltets_2_binding(obj.),}});/*Ωignore_startΩ*/() => obj = __sveltets_2_any(null);/*Ωignore_endΩ*/}
\ No newline at end of file
+ { const $$_tupnI0C = __sveltets_2_ensureComponent(Input); const $$_tupnI0 = new $$_tupnI0C({ target: __sveltets_2_any(), props: {   value:obj.,}});/*Ωignore_startΩ*/() => obj = __sveltets_2_any(null);/*Ωignore_endΩ*/$$_tupnI0.$$bindings = 'value';}
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes-best-effort-types/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes-best-effort-types/expectedv2.ts
index 1ec409f7d..1d4b54dcd 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/runes-best-effort-types/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes-best-effort-types/expectedv2.ts
@@ -8,4 +8,5 @@ return { props: /** @type {$$ComponentProps} */({}), slots: {}, events: {} }}
 
 export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_partial(__sveltets_2_with_any_event(render())))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes-bindable/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes-bindable/expectedv2.ts
index f3cd3b456..9267ded4e 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/runes-bindable/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes-bindable/expectedv2.ts
@@ -4,8 +4,9 @@
     let/** @typedef {{ a: unknown, b?: unknown }} $$ComponentProps *//** @type {$$ComponentProps} */ { a, b = $bindable() } = $props();
 ;
 async () => {};
-return { props: /** @type {__sveltets_2_Bindings<$$ComponentProps, "b">} */({}), slots: {}, events: {} }}
+return { props: /** @type {$$ComponentProps} */({}), slots: {}, events: {} }}
 
 export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_partial(__sveltets_2_with_any_event(render())))) { super(options); }
+    $$bindings = __sveltets_$$bindings('b');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes-looking-like-stores/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes-looking-like-stores/expectedv2.ts
index 3f52d204a..246499cf6 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/runes-looking-like-stores/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes-looking-like-stores/expectedv2.ts
@@ -12,4 +12,5 @@ return { props: /** @type {$$ComponentProps} */({}), slots: {}, events: {} }}
 
 export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_partial(__sveltets_2_with_any_event(render())))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes-with-slots/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes-with-slots/expectedv2.ts
index 5d0b294a8..b2f64fcb7 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/runes-with-slots/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes-with-slots/expectedv2.ts
@@ -13,5 +13,5 @@ async () => {
 return { props: /** @type {SomeType} */({}), slots: {'default': {x:x, y:y}}, events: {} }}
 
 export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) {
-    constructor(options = __sveltets_2_runes_constructor(__sveltets_2_partial(__sveltets_2_with_any_event(render())))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes/expectedv2.ts
index 27e928d7c..888397861 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/runes/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes/expectedv2.ts
@@ -11,4 +11,5 @@ return { props: /** @type {$$ComponentProps} */({}), slots: {}, events: {} }}
 
 export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_partial(__sveltets_2_with_any_event(render())))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune-no-changes/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune-no-changes/expectedv2.ts
index f1d1c1d07..c416a1d74 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune-no-changes/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune-no-changes/expectedv2.ts
@@ -11,5 +11,6 @@ return { props: /** @type {$$ComponentProps} */({}), exports: /** @type {snapsho
 
 export default class Page__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_partial(__sveltets_2_with_any_event(render())))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
     get snapshot() { return render().exports.snapshot }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune/expectedv2.ts
index e49b9dd31..7195620ab 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune/expectedv2.ts
@@ -9,5 +9,6 @@ return { props: /** @type {$$ComponentProps} */({}), exports: /** @type {snapsho
 
 export default class Page__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(__sveltets_2_with_any_event(render()))) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_partial(__sveltets_2_with_any_event(render())))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
     get snapshot() { return render().exports.snapshot }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-best-effort-types/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-best-effort-types/expectedv2.ts
index 4708d001d..530b51e2e 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-best-effort-types/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-best-effort-types/expectedv2.ts
@@ -8,4 +8,5 @@ return { props: {} as any as $$ComponentProps, slots: {}, events: {} }}
 
 export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_with_any_event(render())) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_with_any_event(render()))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-bindable/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-bindable/expectedv2.ts
index 715a909f9..bc9f4863e 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-bindable/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-bindable/expectedv2.ts
@@ -4,8 +4,9 @@
     let { a, b = $bindable() }: $$ComponentProps = $props();
 ;
 async () => {};
-return { props: {} as any as __sveltets_2_Bindings<$$ComponentProps, "b">, slots: {}, events: {} }}
+return { props: {} as any as $$ComponentProps, slots: {}, events: {} }}
 
 export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_with_any_event(render())) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_with_any_event(render()))) { super(options); }
+    $$bindings = __sveltets_$$bindings('b');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-generics/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-generics/expectedv2.ts
index 49f1e4a69..8ee4f9c16 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-generics/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-generics/expectedv2.ts
@@ -22,5 +22,6 @@ class __sveltets_Render<T> {
 
 import { SvelteComponentTyped as __SvelteComponentTyped__ } from "svelte" 
 export default class Input__SvelteComponent_<T> extends __SvelteComponentTyped__<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> {
-    constructor(options: import('svelte').ComponentConstructorOptions<__sveltets_2_PropsWithChildren<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['slots']>>>) { super(options); }
+    constructor(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-with-slot/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-with-slot/expectedv2.ts
index 2f4a66108..ab9ba5dc8 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-with-slot/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-with-slot/expectedv2.ts
@@ -26,5 +26,5 @@ class __sveltets_Render<T> {
 
 import { SvelteComponentTyped as __SvelteComponentTyped__ } from "svelte" 
 export default class Input__SvelteComponent_<T> extends __SvelteComponentTyped__<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> {
-    constructor(options: import('svelte').ComponentConstructorOptions<__sveltets_2_PropsWithChildren<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['slots']>>>) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes/expectedv2.ts
index cde57ce7f..a79725a86 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes/expectedv2.ts
@@ -10,4 +10,5 @@ return { props: {} as any as $$ComponentProps, slots: {}, events: {} }}
 
 export default class Input__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_with_any_event(render())) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_with_any_event(render()))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged/expectedv2.ts
index 1ef4b622a..6699fbec3 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged/expectedv2.ts
@@ -9,5 +9,6 @@ return { props: {} as any as $$ComponentProps, exports: {} as any as { snapshot:
 
 export default class Page__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_with_any_event(render())) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_with_any_event(render()))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
     get snapshot() { return render().exports.snapshot }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune/expectedv2.ts
index be250d042..b3afa9ba5 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune/expectedv2.ts
@@ -9,5 +9,6 @@ return { props: {} as any as $$ComponentProps, exports: {} as any as { snapshot:
 
 export default class Page__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_with_any_event(render())) {
     constructor(options = __sveltets_2_runes_constructor(__sveltets_2_with_any_event(render()))) { super(options); }
+    $$bindings = __sveltets_$$bindings('');
     get snapshot() { return render().exports.snapshot }
 }
\ No newline at end of file