diff --git a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts
index c7a35a63f..4f84e9dd9 100644
--- a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts
+++ b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts
@@ -2021,4 +2021,80 @@ describe('DiagnosticsProvider', () => {
}
]);
});
+
+ it('diagnoses bindings with $store', async () => {
+ const { plugin, document } = setup('bind-to-$store.svelte');
+
+ const diagnostics = await plugin.getDiagnostics(document);
+ assert.deepStrictEqual(diagnostics, [
+ {
+ range: {
+ start: {
+ line: 19,
+ character: 33
+ },
+ end: {
+ line: 19,
+ character: 34
+ }
+ },
+ 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 'boolean' is not assignable to type 'number'.",
+ code: 2322,
+ tags: []
+ },
+ {
+ range: {
+ start: {
+ line: 21,
+ character: 24
+ },
+ end: {
+ line: 21,
+ character: 41
+ }
+ },
+ severity: 1,
+ source: 'ts',
+ message: "Type 'number' is not assignable to type 'boolean'.",
+ code: 2322,
+ tags: []
+ },
+ {
+ range: {
+ start: {
+ line: 22,
+ character: 16
+ },
+ end: {
+ line: 22,
+ character: 20
+ }
+ },
+ severity: 1,
+ source: 'ts',
+ message: "Type 'boolean' is not assignable to type 'number'.",
+ code: 2322,
+ tags: []
+ }
+ ]);
+ });
});
diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics/bind-to-$store.svelte b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/bind-to-$store.svelte
new file mode 100644
index 000000000..2275ee68c
--- /dev/null
+++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics/bind-to-$store.svelte
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/svelte2tsx/src/htmlxtojsx/nodes/binding.ts b/packages/svelte2tsx/src/htmlxtojsx/nodes/binding.ts
index aa3b9be7b..7d503444d 100644
--- a/packages/svelte2tsx/src/htmlxtojsx/nodes/binding.ts
+++ b/packages/svelte2tsx/src/htmlxtojsx/nodes/binding.ts
@@ -13,6 +13,11 @@ const oneWayBindingAttributes: Map = new Map(
])
)
);
+/**
+ * List of all binding names that are transformed to sth like `binding = variable`.
+ * This applies to readonly bindings and the this binding.
+ */
+export const assignmentBindings = new Set([...oneWayBindingAttributes.keys(), 'this']);
/**
* Transform bind:xxx into something that conforms to JSX
@@ -57,8 +62,8 @@ export function handleBinding(
const thisType = getInstanceTypeSimple(el, str);
if (thisType) {
- str.overwrite(attr.start, attr.expression.start, '{...__sveltets_1_empty((');
- const instanceOfThisAssignment = ' = ' + surroundWithIgnoreComments(thisType) + '))}';
+ str.overwrite(attr.start, attr.expression.start, '{...__sveltets_1_empty(');
+ const instanceOfThisAssignment = ' = ' + surroundWithIgnoreComments(thisType) + ')}';
str.overwrite(attr.expression.end, attr.end, instanceOfThisAssignment);
return;
}
diff --git a/packages/svelte2tsx/src/svelte2tsx/nodes/Stores.ts b/packages/svelte2tsx/src/svelte2tsx/nodes/Stores.ts
index 06faee332..e9f46c3c3 100644
--- a/packages/svelte2tsx/src/svelte2tsx/nodes/Stores.ts
+++ b/packages/svelte2tsx/src/svelte2tsx/nodes/Stores.ts
@@ -2,9 +2,11 @@ import MagicString from 'magic-string';
import { Node } from 'estree-walker';
import { ScopeStack, Scope } from '../utils/Scope';
import { isObjectKey, isMember } from '../../utils/svelteAst';
+import { assignmentBindings } from '../../htmlxtojsx/nodes/binding';
export function handleStore(node: Node, parent: Node, str: MagicString): void {
const storename = node.name.slice(1);
+
//handle assign to
if (parent.type == 'AssignmentExpression' && parent.left == node && parent.operator == '=') {
const dollar = str.original.indexOf('$', node.start);
@@ -13,6 +15,7 @@ export function handleStore(node: Node, parent: Node, str: MagicString): void {
str.appendLeft(parent.end, ')');
return;
}
+
// handle Assignment operators ($store +=, -=, *=, /=, %=, **=, etc.)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment
const operators = ['+=', '-=', '*=', '/=', '%=', '**=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
@@ -31,6 +34,7 @@ export function handleStore(node: Node, parent: Node, str: MagicString): void {
str.appendLeft(parent.end, ')');
return;
}
+
// handle $store++, $store--, ++$store, --$store
if (parent.type == 'UpdateExpression') {
let simpleOperator;
@@ -55,10 +59,21 @@ export function handleStore(node: Node, parent: Node, str: MagicString): void {
return;
}
+ const dollar = str.original.indexOf('$', node.start);
+
+ // handle bindings which are transformed to assignments. These need special treatment because
+ // `(__sveltets_1_store_get(foo), foo$) = something` is syntactically invalid
+ // Therefore remove the outer commas. Note: This relies on the binding expression wrapping
+ // this statement with __sveltets_1_empty
+ if (parent.type === 'Binding' && assignmentBindings.has(parent.name)) {
+ str.overwrite(dollar, dollar + 1, '__sveltets_1_store_get(', { contentOnly: true });
+ str.prependLeft(node.end, `), $${storename}`);
+ return;
+ }
+
// we change "$store" references into "(__sveltets_1_store_get(store), $store)"
// - in order to get ts errors if store is not assignable to SvelteStore
// - use $store variable defined above to get ts flow control
- const dollar = str.original.indexOf('$', node.start);
str.overwrite(dollar, dollar + 1, '(__sveltets_1_store_get(', { contentOnly: true });
str.prependLeft(node.end, `), $${storename})`);
}
diff --git a/packages/svelte2tsx/svelte-shims.d.ts b/packages/svelte2tsx/svelte-shims.d.ts
index 084a88b9a..f179c52b9 100644
--- a/packages/svelte2tsx/svelte-shims.d.ts
+++ b/packages/svelte2tsx/svelte-shims.d.ts
@@ -160,7 +160,7 @@ declare function __sveltets_1_with_any_event(store: SvelteStore): T
declare function __sveltets_1_any(dummy: any): any;
-declare function __sveltets_1_empty(dummy: any): {};
+declare function __sveltets_1_empty(...dummy: any[]): {};
declare function __sveltets_1_componentType(): AConstructorTypeOf>
declare function __sveltets_1_invalidate(getValue: () => T): T
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-component/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-component/expected.jsx
index 7a8e490a3..b50b5b0b9 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-component/expected.jsx
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-component/expected.jsx
@@ -1 +1 @@
-<>>
\ No newline at end of file
+<>>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-body/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-body/expected.jsx
index 2997455b2..b3b8138b4 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-body/expected.jsx
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-body/expected.jsx
@@ -1 +1 @@
-<>>
\ No newline at end of file
+<>>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-component/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-component/expected.jsx
index dbf6b4ffe..b5bea26f8 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-component/expected.jsx
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-component/expected.jsx
@@ -1 +1 @@
-<>>
\ No newline at end of file
+<>>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-self/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-self/expected.jsx
index e57f57e85..9e56b2094 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-self/expected.jsx
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this-svelte-self/expected.jsx
@@ -1,3 +1,3 @@
<>{(false) ? <>
-
+
> : <>>}>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this/expected.jsx
index 0aa088562..cdd5bb8f6 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this/expected.jsx
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/binding-this/expected.jsx
@@ -1 +1 @@
-<>>
\ No newline at end of file
+<>>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/sourcemaps/samples/component-props/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/component-props/mappings.jsx
index 953be8533..f1a7caec5 100644
--- a/packages/svelte2tsx/test/sourcemaps/samples/component-props/mappings.jsx
+++ b/packages/svelte2tsx/test/sourcemaps/samples/component-props/mappings.jsx
@@ -27,11 +27,11 @@
> {/**
/>>↲ [generated] line 19
diff --git a/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx
index 63a5658ca..3ca70f65a 100644
--- a/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx
+++ b/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx
@@ -323,11 +323,11 @@ s
{/**
------------------------------------------------------------------------------------------------------------------------------------------------------ */}
- {/**
- ╚╚╚
↲ [generated] line 139
- ╚╚╚
↲
- ╚╚╚
↲
- ╚╚╚
↲ [original] line 278
+
{/**
+ ╚╚╚
↲ [generated] line 139
+ ╚╚╚
↲
+ ╚╚╚
↲
+ ╚╚╚
↲ [original] line 278
------------------------------------------------------------------------------------------------------------------------------------------------------ */}
{ chapter.html} {/**
╚╚╚╚{•chapter.html}↲ [generated] line 140
@@ -400,11 +400,11 @@ s
+<>>;function render() {
+<>
+
+
+
+
+
>
+return { props: {}, slots: {}, getters: {}, events: {} }}
+
+export default class Input__SvelteComponent_ extends __sveltets_1_createSvelte2TsxComponent(__sveltets_1_partial(__sveltets_1_with_any_event(render()))) {
+}
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/binding-assignment-$store/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/binding-assignment-$store/input.svelte
new file mode 100644
index 000000000..b508059e9
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/binding-assignment-$store/input.svelte
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/slot-bind-this/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/slot-bind-this/expected.tsx
index 449338254..ff47d9a87 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/slot-bind-this/expected.tsx
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/slot-bind-this/expected.tsx
@@ -1,7 +1,7 @@
///
<>>;function render() {
/*Ωignore_startΩ*/;const __sveltets_ensureSlot = __sveltets_1_createEnsureSlot();/*Ωignore_endΩ*/
-<>
>
+<>
>
return { props: {}, slots: {'s': {}}, getters: {}, events: {} }}
export default class Input__SvelteComponent_ extends __sveltets_1_createSvelte2TsxComponent(__sveltets_1_partial(__sveltets_1_with_any_event(render()))) {