diff --git a/CHANGELOG.md b/CHANGELOG.md
index f24bc543b7..7bcc280213 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
* Handle `width`/`height` attributes when spreading ([#6752](https://github.com/sveltejs/svelte/issues/6752))
* Add support for resize observer bindings (`
`) ([#8022](https://github.com/sveltejs/svelte/pull/8022))
+* Update interpolated style directive properly when using spread ([#8438](https://github.com/sveltejs/svelte/issues/8438))
+* Remove style directive property when value is `undefined` ([#8462](https://github.com/sveltejs/svelte/issues/8462))
+* Ensure version is typed as `string` instead of the literal `__VERSION__` ([#8498](https://github.com/sveltejs/svelte/issues/8498))
## 3.58.0
diff --git a/site/content/docs/02-template-syntax/01-svelte-components.md b/site/content/docs/02-template-syntax/01-svelte-components.md
index 460562fed8..d1f353629b 100644
--- a/site/content/docs/02-template-syntax/01-svelte-components.md
+++ b/site/content/docs/02-template-syntax/01-svelte-components.md
@@ -36,7 +36,9 @@ Svelte uses the `export` keyword to mark a variable declaration as a _property_
```
-You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that whenever a prop is removed by the consumer, its value is set to `undefined` rather than the initial value.
+---
+
+You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that if the values of props are subsequently updated, then any prop whose value is not specified will be set to `undefined` (rather than its initial value).
In development mode (see the [compiler options](/docs/svelte-compiler#svelte-compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`.
diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts
index cefebfcff3..e2c0ece733 100644
--- a/src/compiler/compile/render_dom/wrappers/Element/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts
@@ -1240,19 +1240,27 @@ export default class ElementWrapper extends Wrapper {
block.chunks.hydrate.push(updater);
+ const self_deps = expression.dynamic_dependencies();
+ const all_deps = new Set([
+ ...self_deps,
+ ...this.dynamic_style_dependencies
+ ]);
+
+ let condition = block.renderer.dirty([...all_deps]);
+
// Assume that style has changed through the spread attribute
if (has_spread) {
+ if (should_cache && all_deps.size) {
+ // Update the cached value
+ block.chunks.update.push(b`
+ if (${condition}) {
+ ${cached_snippet} = ${snippet};
+ }`
+ );
+ }
block.chunks.update.push(updater);
} else {
- const self_deps = expression.dynamic_dependencies();
- const all_deps = new Set([
- ...self_deps,
- ...this.dynamic_style_dependencies
- ]);
-
- if (all_deps.size === 0) return;
-
- let condition = block.renderer.dirty([...all_deps]);
+ if (all_deps.size === 0) return;
if (should_cache) {
condition = x`${condition} && ${cached_snippet} !== (${cached_snippet} = ${snippet})`;
diff --git a/src/compiler/index.ts b/src/compiler/index.ts
index 76eb45d37f..57277ac756 100644
--- a/src/compiler/index.ts
+++ b/src/compiler/index.ts
@@ -3,5 +3,5 @@ export { default as parse } from './parse/index';
export { default as preprocess } from './preprocess/index';
export { walk } from 'estree-walker';
-export const VERSION = '__VERSION__';
+export const VERSION: string = '__VERSION__';
// additional exports added through generate-type-definitions.js
diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts
index c025313c15..066fa6eb1c 100644
--- a/src/runtime/internal/dom.ts
+++ b/src/runtime/internal/dom.ts
@@ -307,7 +307,7 @@ export function attr(node: Element, attribute: string, value?: string) {
else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value);
}
-/**
+/**
* List of attributes that should always be set through the attr method,
* because updating them through the property setter doesn't work reliably.
* In the example of `width`/`height`, the problem is that the setter only
@@ -641,7 +641,7 @@ export function set_input_type(input, type) {
}
export function set_style(node, key, value, important) {
- if (value === null) {
+ if (value == null) {
node.style.removeProperty(key);
} else {
node.style.setProperty(key, value, important ? 'important' : '');
diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts
index 67c2bcb15c..e5184eb25b 100644
--- a/src/runtime/store/index.ts
+++ b/src/runtime/store/index.ts
@@ -12,8 +12,15 @@ export type Updater
= (value: T) => T;
/** Cleanup logic callback. */
type Invalidator = (value?: T) => void;
-/** Start and stop notification callbacks. */
-export type StartStopNotifier = (set: Subscriber) => Unsubscriber | void;
+/**
+ * Start and stop notification callbacks.
+ * This function is called when the first subscriber subscribes.
+ *
+ * @param {(value: T) => void} set Function that sets the value of the store.
+ * @returns {void | (() => void)} Optionally, a cleanup function that is called when the last remaining
+ * subscriber unsubscribes.
+ */
+export type StartStopNotifier = (set: (value: T) => void) => void | (() => void);
/** Readable interface for subscribing. */
export interface Readable {
@@ -48,7 +55,7 @@ const subscriber_queue = [];
/**
* Creates a `Readable` store that allows reading by subscription.
* @param value initial value
- * @param {StartStopNotifier}start start and stop notifications for subscriptions
+ * @param {StartStopNotifier} [start]
*/
export function readable(value?: T, start?: StartStopNotifier): Readable {
return {
@@ -59,7 +66,7 @@ export function readable(value?: T, start?: StartStopNotifier): Readable(value?: T, start: StartStopNotifier = noop): Writable {
let stop: Unsubscriber;
diff --git a/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js
new file mode 100644
index 0000000000..af1030c25a
--- /dev/null
+++ b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js
@@ -0,0 +1,18 @@
+export default {
+ html: `
+
+ `,
+
+ test({ assert, target, window, component }) {
+ const div = target.querySelector('div');
+ const styles = window.getComputedStyle(div);
+ assert.equal(styles.backgroundColor, 'rgb(255, 0, 0)');
+
+ {
+ component.backgroundColor = 128;
+ const div = target.querySelector('div');
+ const styles = window.getComputedStyle(div);
+ assert.equal(styles.backgroundColor, 'rgb(128, 0, 0)');
+ }
+ }
+};
diff --git a/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte
new file mode 100644
index 0000000000..4f44ae2e67
--- /dev/null
+++ b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/test/runtime/samples/inline-style-become-undefined/_config.js b/test/runtime/samples/inline-style-become-undefined/_config.js
new file mode 100644
index 0000000000..a2a0727efa
--- /dev/null
+++ b/test/runtime/samples/inline-style-become-undefined/_config.js
@@ -0,0 +1,11 @@
+export default {
+ async test({ assert, target, window }) {
+ const div = target.querySelector('div');
+ const click = new window.MouseEvent('click');
+
+ assert.htmlEqual(target.innerHTML, '');
+ await div.dispatchEvent(click);
+ await Promise.resolve();
+ assert.htmlEqual(target.innerHTML, '');
+ }
+};
diff --git a/test/runtime/samples/inline-style-become-undefined/main.svelte b/test/runtime/samples/inline-style-become-undefined/main.svelte
new file mode 100644
index 0000000000..ee38934fc7
--- /dev/null
+++ b/test/runtime/samples/inline-style-become-undefined/main.svelte
@@ -0,0 +1,9 @@
+
+
+