Repository: sveltejs/svelte (JavaScript)
Date: 2022-03-24 02:50:59+00:00
Task type: Bug Fix

Issue:
style: shorthand missing reactivity in prod mode
### Describe the bug

The `style:<prop>` or `style:<prop>={prop}` shorthands can lose reactivity in prod mode in some cases. The particular use-case I'm seeing is a structure like this

```html
<div>
  <div style:color />
</div>
```

where the inner `<div>` has no dynamic content, so in production mode the `.innerHTML` optimization is applied. This then generates a `noop` update function for the component.

Adding dynamic content to the inner `<div>` will disable the `.innerHTML` optimization and restore reactivity. Using the longhand style declaration like `style="color: {color};"` will also work around this issue.

### Reproduction

https://svelte.dev/repl/3f698e2bd883418d9561b44277df585c?version=3.46.4

1. Swap to JS output tab
2. Look at `p` function in dev mode, see that reactivity exists
3. Set `dev` to `false`
4. Look at `p` function, it's now a `noop`

**Important Note**: the rendered output in the REPL seems to always use dev mode, so the result will look correct. You have to check the actual "JS output" tab to see the issue in the source.

### Logs

_No response_

### System Info

```shell
System:
    OS: Windows 10 10.0.19044
    CPU: (24) x64 AMD Ryzen 9 5900X 12-Core Processor
    Memory: 96.87 GB / 127.90 GB
  Browsers:
    Edge: Spartan (44.19041.1266.0), Chromium (99.0.1150.46)
    Internet Explorer: 11.0.19041.1566
  npmPackages:
    svelte: ^3.44.0 => 3.46.4
    vite: ^2.8.0 => 2.8.6
    @sveltejs/vite-plugin-svelte: ^1.0.0-next.40 => 1.0.0-next.40
```


### Severity

annoyance
style: shorthand missing reactivity in prod mode
### Describe the bug

The `style:<prop>` or `style:<prop>={prop}` shorthands can lose reactivity in prod mode in some cases. The particular use-case I'm seeing is a structure like this

```html
<div>
  <div style:color />
</div>
```

where the inner `<div>` has no dynamic content, so in production mode the `.innerHTML` optimization is applied. This then generates a `noop` update function for the component.

Adding dynamic content to the inner `<div>` will disable the `.innerHTML` optimization and restore reactivity. Using the longhand style declaration like `style="color: {color};"` will also work around this issue.

### Reproduction

https://svelte.dev/repl/3f698e2bd883418d9561b44277df585c?version=3.46.4

1. Swap to JS output tab
2. Look at `p` function in dev mode, see that reactivity exists
3. Set `dev` to `false`
4. Look at `p` function, it's now a `noop`

**Important Note**: the rendered output in the REPL seems to always use dev mode, so the result will look correct. You have to check the actual "JS output" tab to see the issue in the source.

### Logs

_No response_

### System Info

```shell
System:
    OS: Windows 10 10.0.19044
    CPU: (24) x64 AMD Ryzen 9 5900X 12-Core Processor
    Memory: 96.87 GB / 127.90 GB
  Browsers:
    Edge: Spartan (44.19041.1266.0), Chromium (99.0.1150.46)
    Internet Explorer: 11.0.19041.1566
  npmPackages:
    svelte: ^3.44.0 => 3.46.4
    vite: ^2.8.0 => 2.8.6
    @sveltejs/vite-plugin-svelte: ^1.0.0-next.40 => 1.0.0-next.40
```


### Severity

annoyance


Issue discussion:
My guess is that we need to enhance the following condition to also bail when a style attribute is used:

https://github.com/sveltejs/svelte/blob/0eee98a50affd6791f66509b4640770289de42cc/src/compiler/compile/render_dom/wrappers/Element/index.ts#L225
My guess is that we need to enhance the following condition to also bail when a style attribute is used:

https://github.com/sveltejs/svelte/blob/0eee98a50affd6791f66509b4640770289de42cc/src/compiler/compile/render_dom/wrappers/Element/index.ts#L225

Solution patch:
diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts
--- a/src/compiler/compile/render_dom/wrappers/Element/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts
@@ -219,6 +219,7 @@ export default class ElementWrapper extends Wrapper {
 				node.classes.length > 0 ||
 				node.intro || node.outro ||
 				node.handlers.length > 0 ||
+				node.styles.length > 0 ||
 				this.node.name === 'option' ||
 				renderer.options.dev
 			) {


Test patch:
diff --git a/test/runtime/samples/inline-style-directive/_config.js b/test/runtime/samples/inline-style-directive/_config.js
--- a/test/runtime/samples/inline-style-directive/_config.js
+++ b/test/runtime/samples/inline-style-directive/_config.js
@@ -1,6 +1,8 @@
 export default {
 	html: `
-		<p style="color: red;"></p>
+		<div>
+			<p style="color: red;"></p>
+		</div>
 	`,
 
 	test({ assert, target, window }) {
diff --git a/test/runtime/samples/inline-style-directive/main.svelte b/test/runtime/samples/inline-style-directive/main.svelte
--- a/test/runtime/samples/inline-style-directive/main.svelte
+++ b/test/runtime/samples/inline-style-directive/main.svelte
@@ -2,4 +2,6 @@
 	export let myColor = "red";
 </script>
 
-<p style:color={myColor} />
+<div>
+	<p style:color={myColor} />
+</div>
diff --git a/test/runtime/samples/inline-style/_config.js b/test/runtime/samples/inline-style/_config.js
--- a/test/runtime/samples/inline-style/_config.js
+++ b/test/runtime/samples/inline-style/_config.js
@@ -4,9 +4,9 @@ export default {
 	`,
 
 	test({ assert, component, target, window }) {
-		const p = target.querySelector('div');
+		const div = target.querySelector('div');
 
-		const styles = window.getComputedStyle(p);
+		const styles = window.getComputedStyle(div);
 		assert.equal(styles.color, 'red');
 	}
 };