Repository: sveltejs/svelte (JavaScript) — Bug Fix

Issue:
this.set({ value: null }) behaves differently to initial null value
https://svelte.technology/repl?version=2.9.4&gist=10632ba5ca7614778159a4f6b36584a6


Discussion:

Resolving patch:
diff --git a/src/compile/nodes/MustacheTag.ts b/src/compile/nodes/MustacheTag.ts
--- a/src/compile/nodes/MustacheTag.ts
+++ b/src/compile/nodes/MustacheTag.ts
@@ -10,7 +10,7 @@ export default class MustacheTag extends Tag {
 	) {
 		const { init } = this.renameThisMethod(
 			block,
-			value => `${this.var}.data = ${value};`
+			value => `@setData(${this.var}, ${value});`
 		);
 
 		block.addElement(
diff --git a/src/shared/dom.js b/src/shared/dom.js
--- a/src/shared/dom.js
+++ b/src/shared/dom.js
@@ -155,6 +155,10 @@ export function claimText (nodes, data) {
 	return createText(data);
 }
 
+export function setData(text, data) {
+	text.data = '' + data;
+}
+
 export function setInputType(input, type) {
 	try {
 		input.type = type;


Test patch:
diff --git a/test/cli/samples/globals/expected/Main.js b/test/cli/samples/globals/expected/Main.js
--- a/test/cli/samples/globals/expected/Main.js
+++ b/test/cli/samples/globals/expected/Main.js
@@ -26,7 +26,7 @@ var Main = (function(answer) { "use strict";
 
 			p(changed, ctx) {
 				if (changed.answer) {
-					text_1.data = ctx.answer;
+					setData(text_1, ctx.answer);
 				}
 			},
 
@@ -80,6 +80,10 @@ var Main = (function(answer) { "use strict";
 		target.appendChild(node);
 	}
 
+	function setData(text, data) {
+		text.data = '' + data;
+	}
+
 	function detachNode(node) {
 		node.parentNode.removeChild(node);
 	}
diff --git a/test/cli/samples/store/expected/Main.js b/test/cli/samples/store/expected/Main.js
--- a/test/cli/samples/store/expected/Main.js
+++ b/test/cli/samples/store/expected/Main.js
@@ -18,7 +18,7 @@ function create_main_fragment(component, ctx) {
 
 		p(changed, ctx) {
 			if (changed.$name) {
-				text_1.data = ctx.$name;
+				setData(text_1, ctx.$name);
 			}
 		},
 
@@ -75,6 +75,10 @@ function appendNode(node, target) {
 	target.appendChild(node);
 }
 
+function setData(text, data) {
+	text.data = '' + data;
+}
+
 function detachNode(node) {
 	node.parentNode.removeChild(node);
 }
diff --git a/test/js/samples/collapses-text-around-comments/expected-bundle.js b/test/js/samples/collapses-text-around-comments/expected-bundle.js
--- a/test/js/samples/collapses-text-around-comments/expected-bundle.js
+++ b/test/js/samples/collapses-text-around-comments/expected-bundle.js
@@ -25,6 +25,10 @@ function createText(data) {
 	return document.createTextNode(data);
 }
 
+function setData(text, data) {
+	text.data = '' + data;
+}
+
 function blankObject() {
 	return Object.create(null);
 }
@@ -167,7 +171,7 @@ function create_main_fragment(component, ctx) {
 
 		p(changed, ctx) {
 			if (changed.foo) {
-				text.data = ctx.foo;
+				setData(text, ctx.foo);
 			}
 		},
 
diff --git a/test/js/samples/collapses-text-around-comments/expected.js b/test/js/samples/collapses-text-around-comments/expected.js
--- a/test/js/samples/collapses-text-around-comments/expected.js
+++ b/test/js/samples/collapses-text-around-comments/expected.js
@@ -1,5 +1,5 @@
 /* generated by Svelte vX.Y.Z */
-import { appendNode, assign, createElement, createText, detachNode, init, insertNode, proto } from "svelte/shared.js";
+import { appendNode, assign, createElement, createText, detachNode, init, insertNode, proto, setData } from "svelte/shared.js";
 
 function data() {
 	return { foo: 42 }
@@ -29,7 +29,7 @@ function create_main_fragment(component, ctx) {
 
 		p(changed, ctx) {
 			if (changed.foo) {
-				text.data = ctx.foo;
+				setData(text, ctx.foo);
 			}
 		},
 
diff --git a/test/js/samples/deconflict-builtins/expected-bundle.js b/test/js/samples/deconflict-builtins/expected-bundle.js
--- a/test/js/samples/deconflict-builtins/expected-bundle.js
+++ b/test/js/samples/deconflict-builtins/expected-bundle.js
@@ -35,6 +35,10 @@ function createComment() {
 	return document.createComment('');
 }
 
+function setData(text, data) {
+	text.data = '' + data;
+}
+
 function blankObject() {
 	return Object.create(null);
 }
@@ -228,7 +232,7 @@ function create_each_block(component, ctx) {
 
 		p(changed, ctx) {
 			if ((changed.createElement) && text_value !== (text_value = ctx.node)) {
-				text.data = text_value;
+				setData(text, text_value);
 			}
 		},
 
diff --git a/test/js/samples/deconflict-builtins/expected.js b/test/js/samples/deconflict-builtins/expected.js
--- a/test/js/samples/deconflict-builtins/expected.js
+++ b/test/js/samples/deconflict-builtins/expected.js
@@ -1,5 +1,5 @@
 /* generated by Svelte vX.Y.Z */
-import { appendNode, assign, createComment, createElement, createText, destroyEach, detachNode, init, insertNode, proto } from "svelte/shared.js";
+import { appendNode, assign, createComment, createElement, createText, destroyEach, detachNode, init, insertNode, proto, setData } from "svelte/shared.js";
 
 function create_main_fragment(component, ctx) {
 	var each_anchor;
@@ -79,7 +79,7 @@ function create_each_block(component, ctx) {
 
 		p(changed, ctx) {
 			if ((changed.createElement) && text_value !== (text_value = ctx.node)) {
-				text.data = text_value;
+				setData(text, text_value);
 			}
 		},
 
diff --git a/test/js/samples/dev-warning-missing-data-computed/expected-bundle.js b/test/js/samples/dev-warning-missing-data-computed/expected-bundle.js
--- a/test/js/samples/dev-warning-missing-data-computed/expected-bundle.js
+++ b/test/js/samples/dev-warning-missing-data-computed/expected-bundle.js
@@ -31,6 +31,10 @@ function createText(data) {
 	return document.createTextNode(data);
 }
 
+function setData(text, data) {
+	text.data = '' + data;
+}
+
 function blankObject() {
 	return Object.create(null);
 }
@@ -191,11 +195,11 @@ function create_main_fragment(component, ctx) {
 
 		p: function update(changed, ctx) {
 			if ((changed.Math || changed.foo) && text_value !== (text_value = ctx.Math.max(0, ctx.foo))) {
-				text.data = text_value;
+				setData(text, text_value);
 			}
 
 			if (changed.bar) {
-				text_2.data = ctx.bar;
+				setData(text_2, ctx.bar);
 			}
 		},
 
diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js
--- a/test/js/samples/dev-warning-missing-data-computed/expected.js
+++ b/test/js/samples/dev-warning-missing-data-computed/expected.js
@@ -1,5 +1,5 @@
 /* generated by Svelte vX.Y.Z */
-import { addLoc, appendNode, assign, createElement, createText, detachNode, init, insertNode, protoDev } from "svelte/shared.js";
+import { addLoc, appendNode, assign, createElement, createText, detachNode, init, insertNode, protoDev, setData } from "svelte/shared.js";
 
 function bar({ foo }) {
 	return foo * 2;
@@ -28,11 +28,11 @@ function create_main_fragment(component, ctx) {
 
 		p: function update(changed, ctx) {
 			if ((changed.Math || changed.foo) && text_value !== (text_value = ctx.Math.max(0, ctx.foo))) {
-				text.data = text_value;
+				setData(text, text_value);
 			}
 
 			if (changed.bar) {
-				text_2.data = ctx.bar;
+				setData(text_2, ctx.bar);
 			}
 		},
 
diff --git a/test/js/samples/each-block-changed-check/expected-bundle.js b/test/js/samples/each-block-changed-check/expected-bundle.js
--- a/test/js/samples/each-block-changed-check/expected-bundle.js
+++ b/test/js/samples/each-block-changed-check/expected-bundle.js
@@ -37,6 +37,10 @@ function createText(data) {
 	return document.createTextNode(data);
 }
 
+function setData(text, data) {
+	text.data = '' + data;
+}
+
 function blankObject() {
 	return Object.create(null);
 }
@@ -207,7 +211,7 @@ function create_main_fragment(component, ctx) {
 			}
 
 			if (changed.foo) {
-				text_1.data = ctx.foo;
+				setData(text_1, ctx.foo);
 			}
 		},
 
@@ -260,11 +264,11 @@ function create_each_block(component, ctx) {
 
 		p(changed, ctx) {
 			if ((changed.comments) && text_2_value !== (text_2_value = ctx.comment.author)) {
-				text_2.data = text_2_value;
+				setData(text_2, text_2_value);
 			}
 
 			if ((changed.elapsed || changed.comments || changed.time) && text_4_value !== (text_4_value = ctx.elapsed(ctx.comment.time, ctx.time))) {
-				text_4.data = text_4_value;
+				setData(text_4, text_4_value);
 			}
 
 			if ((changed.comments) && raw_value !== (raw_value = ctx.comment.html)) {
diff --git a/test/js/samples/each-block-changed-check/expected.js b/test/js/samples/each-block-changed-check/expected.js
--- a/test/js/samples/each-block-changed-check/expected.js
+++ b/test/js/samples/each-block-changed-check/expected.js
@@ -1,5 +1,5 @@
 /* generated by Svelte vX.Y.Z */
-import { appendNode, assign, createElement, createText, destroyEach, detachAfter, detachNode, init, insertNode, proto } from "svelte/shared.js";
+import { appendNode, assign, createElement, createText, destroyEach, detachAfter, detachNode, init, insertNode, proto, setData } from "svelte/shared.js";
 
 function create_main_fragment(component, ctx) {
 	var text, p, text_1;
@@ -56,7 +56,7 @@ function create_main_fragment(component, ctx) {
 			}
 
 			if (changed.foo) {
-				text_1.data = ctx.foo;
+				setData(text_1, ctx.foo);
 			}
 		},
 
@@ -109,11 +109,11 @@ function create_each_block(component, ctx) {
 
 		p(changed, ctx) {
 			if ((changed.comments) && text_2_value !== (text_2_value = ctx.comment.author)) {
-				text_2.data = text_2_value;
+				setData(text_2, text_2_value);
 			}
 
 			if ((changed.elapsed || changed.comments || changed.time) && text_4_value !== (text_4_value = ctx.elapsed(ctx.comment.time, ctx.time))) {
-				text_4.data = text_4_value;
+				setData(text_4, text_4_value);
 			}
 
 			if ((changed.comments) && raw_value !== (raw_value = ctx.comment.html)) {
diff --git a/test/js/samples/each-block-keyed-animated/expected-bundle.js b/test/js/samples/each-block-keyed-animated/expected-bundle.js
--- a/test/js/samples/each-block-keyed-animated/expected-bundle.js
+++ b/test/js/samples/each-block-keyed-animated/expected-bundle.js
@@ -29,6 +29,10 @@ function createComment() {
 	return document.createComment('');
 }
 
+function setData(text, data) {
+	text.data = '' + data;
+}
+
 function linear(t) {
 	return t;
 }
@@ -533,7 +537,7 @@ function create_each_block(component, key_1, ctx) {
 
 		p(changed, ctx) {
 			if ((changed.things) && text_value !== (text_value = ctx.thing.name)) {
-				text.data = text_value;
+				setData(text, text_value);
 			}
 		},
 
diff --git a/test/js/samples/each-block-keyed-animated/expected.js b/test/js/samples/each-block-keyed-animated/expected.js
--- a/test/js/samples/each-block-keyed-animated/expected.js
+++ b/test/js/samples/each-block-keyed-animated/expected.js
@@ -1,5 +1,5 @@
 /* generated by Svelte vX.Y.Z */
-import { appendNode, assign, blankObject, createComment, createElement, createText, detachNode, fixAndOutroAndDestroyBlock, fixPosition, init, insertNode, proto, updateKeyedEach, wrapAnimation } from "svelte/shared.js";
+import { appendNode, assign, blankObject, createComment, createElement, createText, detachNode, fixAndOutroAndDestroyBlock, fixPosition, init, insertNode, proto, setData, updateKeyedEach, wrapAnimation } from "svelte/shared.js";
 
 function foo(node, animation, params) {
 	const dx = animation.from.left - animation.to.left;
@@ -80,7 +80,7 @@ function create_each_block(component, key_1, ctx) {
 
 		p(changed, ctx) {
 			if ((changed.things) && text_value !== (text_value = ctx.thing.name)) {
-				text.data = text_value;
+				setData(text, text_value);
 			}
 		},
 
diff --git a/test/js/samples/each-block-keyed/expected-bundle.js b/test/js/samples/each-block-keyed/expected-bundle.js
--- a/test/js/samples/each-block-keyed/expected-bundle.js
+++ b/test/js/samples/each-block-keyed/expected-bundle.js
@@ -29,6 +29,10 @@ function createComment() {
 	return document.createComment('');
 }
 
+function setData(text, data) {
+	text.data = '' + data;
+}
+
 function destroyBlock(block, lookup) {
 	block.d(1);
 	lookup[block.key] = null;
@@ -298,7 +302,7 @@ function create_each_block(component, key_1, ctx) {
 
 		p(changed, ctx) {
 			if ((changed.things) && text_value !== (text_value = ctx.thing.name)) {
-				text.data = text_value;
+				setData(text, text_value);
 			}
 		},
 
diff --git a/test/js/samples/each-block-keyed/expected.js b/test/js/samples/each-block-keyed/expected.js
--- a/test/js/samples/each-block-keyed/expected.js
+++ b/test/js/samples/each-block-keyed/expected.js
@@ -1,5 +1,5 @@
 /* generated by Svelte vX.Y.Z */
-import { appendNode, assign, blankObject, createComment, createElement, createText, destroyBlock, detachNode, init, insertNode, proto, updateKeyedEach } from "svelte/shared.js";
+import { appendNode, assign, blankObject, createComment, createElement, createText, destroyBlock, detachNode, init, insertNode, proto, setData, updateKeyedEach } from "svelte/shared.js";
 
 function create_main_fragment(component, ctx) {
 	var each_blocks_1 = [], each_lookup = blankObject(), each_anchor;
@@ -64,7 +64,7 @@ function create_each_block(component, key_1, ctx) {
 
 		p(changed, ctx) {
 			if ((changed.things) && text_value !== (text_value = ctx.thing.name)) {
-				text.data = text_value;
+				setData(text, text_value);
 			}
 		},
 
diff --git a/test/js/samples/input-files/expected.js b/test/js/samples/input-files/expected.js
--- a/test/js/samples/input-files/expected.js
+++ b/test/js/samples/input-files/expected.js
@@ -52,4 +52,4 @@ function SvelteComponent(options) {
 }
 
 assign(SvelteComponent.prototype, proto);
-export default SvelteComponent;
+export default SvelteComponent;
\ No newline at end of file
diff --git a/test/js/samples/window-binding-scroll/expected-bundle.js b/test/js/samples/window-binding-scroll/expected-bundle.js
--- a/test/js/samples/window-binding-scroll/expected-bundle.js
+++ b/test/js/samples/window-binding-scroll/expected-bundle.js
@@ -25,6 +25,10 @@ function createText(data) {
 	return document.createTextNode(data);
 }
 
+function setData(text, data) {
+	text.data = '' + data;
+}
+
 function blankObject() {
 	return Object.create(null);
 }
@@ -178,7 +182,7 @@ function create_main_fragment(component, ctx) {
 
 		p(changed, ctx) {
 			if (changed.y) {
-				text_1.data = ctx.y;
+				setData(text_1, ctx.y);
 			}
 		},
 
diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js
--- a/test/js/samples/window-binding-scroll/expected.js
+++ b/test/js/samples/window-binding-scroll/expected.js
@@ -1,5 +1,5 @@
 /* generated by Svelte vX.Y.Z */
-import { appendNode, assign, createElement, createText, detachNode, init, insertNode, proto } from "svelte/shared.js";
+import { appendNode, assign, createElement, createText, detachNode, init, insertNode, proto, setData } from "svelte/shared.js";
 
 function create_main_fragment(component, ctx) {
 	var window_updating = false, clear_window_updating = function() { window_updating = false; }, window_updating_timeout, p, text, text_1;
@@ -39,7 +39,7 @@ function create_main_fragment(component, ctx) {
 
 		p(changed, ctx) {
 			if (changed.y) {
-				text_1.data = ctx.y;
+				setData(text_1, ctx.y);
 			}
 		},
 
diff --git a/test/runtime/samples/set-null-text-node/_config.js b/test/runtime/samples/set-null-text-node/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/set-null-text-node/_config.js
@@ -0,0 +1,13 @@
+export default {
+	data: { foo: null },
+
+	html: 'foo is null',
+
+	test(assert, component, target) {
+		component.set({ foo: 42 });
+		assert.htmlEqual(target.innerHTML, 'foo is 42');
+
+		component.set({ foo: null });
+		assert.htmlEqual(target.innerHTML, 'foo is null');
+	}
+};
\ No newline at end of file
diff --git a/test/runtime/samples/set-null-text-node/main.html b/test/runtime/samples/set-null-text-node/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/set-null-text-node/main.html
@@ -0,0 +1 @@
+foo is {foo}
\ No newline at end of file