Repository: sveltejs/language-tools
Pull request #1162 (2021-09-04 06:34:27)

(svelte2tsx) fix invalidate reactive for nested object literal
#1161


Patch:
diff --git a/packages/svelte2tsx/src/svelte2tsx/nodes/ImplicitTopLevelNames.ts b/packages/svelte2tsx/src/svelte2tsx/nodes/ImplicitTopLevelNames.ts
index fb859f86..5073ffc0 100644
--- a/packages/svelte2tsx/src/svelte2tsx/nodes/ImplicitTopLevelNames.ts
+++ b/packages/svelte2tsx/src/svelte2tsx/nodes/ImplicitTopLevelNames.ts
@@ -38,8 +38,13 @@ export class ImplicitTopLevelNames {
         const start = expression.getStart() + this.astOffset;
         const end = expression.getEnd() + this.astOffset;
 
-        // $: a = { .. }  /  $: a = .. as ..  =>   () => ( .. )
-        if (ts.isObjectLiteralExpression(expression) || ts.isAsExpression(expression)) {
+        // $: a = { .. }..  /  $: a = .. as ..  =>   () => ( .. )
+        if (
+            ts.isObjectLiteralExpression(expression) ||
+            (expression.getText().startsWith('{') &&
+                this.isNodeStartsWithObjectLiteral(expression)) ||
+            ts.isAsExpression(expression)
+        ) {
             this.str.appendLeft(start, '(');
             this.str.appendRight(end, ')');
         }
@@ -50,6 +55,29 @@ export class ImplicitTopLevelNames {
         // in situations where there is a line break of ; guaranteed to be present (else the code is invalid)
     }
 
+    private isNodeStartsWithObjectLiteral(node: ts.Node) {
+        if (ts.isObjectLiteralExpression(node)) {
+            return true;
+        }
+
+        if (ts.isElementAccessExpression(node)) {
+            return this.isNodeStartsWithObjectLiteral(node.expression);
+        }
+
+        if (ts.isBinaryExpression(node)) {
+            return this.isNodeStartsWithObjectLiteral(node.left);
+        }
+
+        if (ts.isConditionalExpression(node)) {
+            return this.isNodeStartsWithObjectLiteral(node.condition);
+        }
+
+        return node
+            .getChildren()
+            .filter((e) => e.pos === node.pos)
+            .some((child) => this.isNodeStartsWithObjectLiteral(child));
+    }
+
     modifyCode(rootVariables: Set<string>) {
         for (const node of this.map.values()) {
             const names = getNamesFromLabeledStatement(node);


Test patch:
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/reactive-declare-express-starts-with-object/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/reactive-declare-express-starts-with-object/expected.tsx
new file mode 100644
index 00000000..fab7b792
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/reactive-declare-express-starts-with-object/expected.tsx
@@ -0,0 +1,15 @@
+///<reference types="svelte" />
+<></>;function render() {
+
+
+let  b = __sveltets_1_invalidate(() => ({ a: 1 }['a']));
+let  c = __sveltets_1_invalidate(() => ({ a: { b: 1} }['a']['b']));
+let  d = __sveltets_1_invalidate(() => ({ a: { b: 1} }['a']['b']?.['c']));
+let  e = __sveltets_1_invalidate(() => ({a: 1} ?? { a: 1 }));
+let  f = __sveltets_1_invalidate(() => ({a: 1}[c] ? '' : '1'));
+;
+() => (<></>);
+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/reactive-declare-express-starts-with-object/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/reactive-declare-express-starts-with-object/input.svelte
new file mode 100644
index 00000000..68eb3660
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/reactive-declare-express-starts-with-object/input.svelte
@@ -0,0 +1,8 @@
+<script>
+
+$: b = { a: 1 }['a'];
+$: c = { a: { b: 1} }['a']['b'];
+$: d = { a: { b: 1} }['a']['b']?.['c'];
+$: e = {a: 1} ?? { a: 1 };
+$: f = {a: 1}[c] ? '' : '1';
+</script>