Repository: sveltejs/svelte (JavaScript)
Date: 2021-01-01 17:36:25+00:00
Task type: Bug Fix

Issue:
Runtime error if methods in use:obj.nested.method are deeper than 2 level
**Describe the bug**
Introduced in #3935 support of methods in `use:obj.action` causes runtime exception if method is nested in multiple objects

```svelte
<button use:ctx.button.behavior>Open</button>
```

**Logs**
```
ctx[0]['button.behavior'] is not a function
```

**To Reproduce**
https://svelte.dev/repl/b9576a0efc17479287e6bba62c1e9e85?version=3.31.0

**Expected behavior**
No exceptions, actions are applied to HTML node as usual 
OR 
compiler error that prevents usage of nested objects in action definition

**Stacktraces**
Seems like code causing the error is generated in `/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts:35`

**Information about your Svelte project:**

- Firefox 84.0.1 x64
- Windows 10
- Svelte 3.31.0
- Node 15.4.0 
- Snowpack + Rollup 

**Severity**
Somewhat annoying, but not critical. There is a workaround for now, but I think this should be addressed at the compiler level, at least to prevent unexpected runtime crashes.

```svelte
<script>
	let ctx = {
                // or just use flat objects without extra nesting, obviously
		'button.behavior':  (node) => {
			console.log(node);
		},
	};
	
</script>

<button use:ctx.button.behavior>Open </button>
```
UPD: after some fiddling, using flat objects and avoiding nesting is the best option for now, since different IDEs react differently when workaround above is present. VSCode highlights it as missing property, WebStorm - does not


Issue discussion:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 * Fix checkbox `bind:group` in nested `{#each}` contexts ([#5811](https://github.com/sveltejs/svelte/issues/5811))
 * Add graphics roles as known ARIA roles ([#5822](https://github.com/sveltejs/svelte/pull/5822))
 * Fix local transitions if a parent has a cancelled outro transition ([#5822](https://github.com/sveltejs/svelte/issues/5822))
+* Support `use:obj.some.deep.function` as actions ([#5844](https://github.com/sveltejs/svelte/issues/5844))
 
 ## 3.31.0
 
diff --git a/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts b/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts
--- a/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts
+++ b/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts
@@ -31,8 +31,9 @@ export function add_action(block: Block, target: string, action: Action) {
 	const fn = block.renderer.reference(obj);
 
 	if (properties.length) {
+		const member_expression = properties.reduce((lhs, rhs) => x`${lhs}.${rhs}`, fn);
 		block.event_listeners.push(
-			x`@action_destroyer(${id} = ${fn}.${properties.join('.')}(${target}, ${snippet}))`
+			x`@action_destroyer(${id} = ${member_expression}(${target}, ${snippet}))`
 		);
 	} else {
 		block.event_listeners.push(


Test patch:
diff --git a/test/runtime/samples/action-object-deep/_config.js b/test/runtime/samples/action-object-deep/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/action-object-deep/_config.js
@@ -0,0 +1,8 @@
+export default {
+	html: `
+		<button>action</button>
+	`,
+	async test({ assert, target, window }) {
+		assert.equal(target.querySelector('button').foo, 'bar1337');
+	}
+};
diff --git a/test/runtime/samples/action-object-deep/main.svelte b/test/runtime/samples/action-object-deep/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/action-object-deep/main.svelte
@@ -0,0 +1,12 @@
+<script>
+	const obj = {
+		deep: {
+			foo : 'bar',
+			action(element, { leet }) {
+				element.foo = this.foo + leet;
+			}
+		}
+	};
+</script>
+
+<button use:obj.deep.action={{ leet: 1337 }}>action</button>
\ No newline at end of file