From 96428312ecb52acd3e90baffdf69819573e6d643 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 12 Nov 2017 15:55:02 -0500 Subject: [PATCH 01/15] check component exists after _bind before continuing - fixes #917 --- src/shared/index.js | 9 ++++-- .../Nested.html | 1 + .../_config.js | 29 +++++++++++++++++++ .../main.html | 14 +++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 test/runtime/samples/component-binding-self-destroying/Nested.html create mode 100644 test/runtime/samples/component-binding-self-destroying/_config.js create mode 100644 test/runtime/samples/component-binding-self-destroying/main.html diff --git a/src/shared/index.js b/src/shared/index.js index d55e55c22b..f9d0f91998 100644 --- a/src/shared/index.js +++ b/src/shared/index.js @@ -156,9 +156,12 @@ export function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } export function _setDev(newState) { diff --git a/test/runtime/samples/component-binding-self-destroying/Nested.html b/test/runtime/samples/component-binding-self-destroying/Nested.html new file mode 100644 index 0000000000..3fb5ca4da3 --- /dev/null +++ b/test/runtime/samples/component-binding-self-destroying/Nested.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-binding-self-destroying/_config.js b/test/runtime/samples/component-binding-self-destroying/_config.js new file mode 100644 index 0000000000..e1d61c733a --- /dev/null +++ b/test/runtime/samples/component-binding-self-destroying/_config.js @@ -0,0 +1,29 @@ +export default { + solo: true, + + data: { + show: true + }, + + html: ` + + `, + + test(assert, component, target, window) { + const click = new window.MouseEvent('click'); + + target.querySelector('button').dispatchEvent(click); + + assert.equal(component.get('show'), false); + assert.htmlEqual(target.innerHTML, ` + + `); + + target.querySelector('button').dispatchEvent(click); + + assert.equal(component.get('show'), true); + assert.htmlEqual(target.innerHTML, ` + + `); + } +}; diff --git a/test/runtime/samples/component-binding-self-destroying/main.html b/test/runtime/samples/component-binding-self-destroying/main.html new file mode 100644 index 0000000000..74fa144d02 --- /dev/null +++ b/test/runtime/samples/component-binding-self-destroying/main.html @@ -0,0 +1,14 @@ +{{#if show}} + +{{else}} + +{{/if}} + + \ No newline at end of file From 0b56e203005890661070e19ca0cbc5fac0a95dcb Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 12 Nov 2017 16:15:40 -0500 Subject: [PATCH 02/15] oops --- .../samples/component-binding-self-destroying/_config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/runtime/samples/component-binding-self-destroying/_config.js b/test/runtime/samples/component-binding-self-destroying/_config.js index e1d61c733a..27a7ab108e 100644 --- a/test/runtime/samples/component-binding-self-destroying/_config.js +++ b/test/runtime/samples/component-binding-self-destroying/_config.js @@ -1,6 +1,4 @@ export default { - solo: true, - data: { show: true }, From fb675fd5e3b615a7367400944ba76db6c8386885 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 12 Nov 2017 16:42:02 -0500 Subject: [PATCH 03/15] update snapshot tests --- .../collapses-text-around-comments/expected-bundle.js | 9 ++++++--- test/js/samples/component-static/expected-bundle.js | 9 ++++++--- test/js/samples/computed-collapsed-if/expected-bundle.js | 9 ++++++--- test/js/samples/css-media-query/expected-bundle.js | 9 ++++++--- .../samples/css-shadow-dom-keyframes/expected-bundle.js | 9 ++++++--- .../samples/each-block-changed-check/expected-bundle.js | 9 ++++++--- test/js/samples/event-handlers-custom/expected-bundle.js | 9 ++++++--- test/js/samples/if-block-no-update/expected-bundle.js | 9 ++++++--- test/js/samples/if-block-simple/expected-bundle.js | 9 ++++++--- .../inline-style-optimized-multiple/expected-bundle.js | 9 ++++++--- .../inline-style-optimized-url/expected-bundle.js | 9 ++++++--- .../js/samples/inline-style-optimized/expected-bundle.js | 9 ++++++--- .../samples/inline-style-unoptimized/expected-bundle.js | 9 ++++++--- .../input-without-blowback-guard/expected-bundle.js | 9 ++++++--- test/js/samples/legacy-input-type/expected-bundle.js | 9 ++++++--- test/js/samples/legacy-quote-class/expected-bundle.js | 9 ++++++--- test/js/samples/media-bindings/expected-bundle.js | 9 ++++++--- .../js/samples/non-imported-component/expected-bundle.js | 9 ++++++--- .../onrender-onteardown-rewritten/expected-bundle.js | 9 ++++++--- test/js/samples/setup-method/expected-bundle.js | 9 ++++++--- .../samples/use-elements-as-anchors/expected-bundle.js | 9 ++++++--- 21 files changed, 126 insertions(+), 63 deletions(-) diff --git a/test/js/samples/collapses-text-around-comments/expected-bundle.js b/test/js/samples/collapses-text-around-comments/expected-bundle.js index 7fb59ff390..a5776bc195 100644 --- a/test/js/samples/collapses-text-around-comments/expected-bundle.js +++ b/test/js/samples/collapses-text-around-comments/expected-bundle.js @@ -157,9 +157,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/component-static/expected-bundle.js b/test/js/samples/component-static/expected-bundle.js index c3a2661cc5..94f69ca2ca 100644 --- a/test/js/samples/component-static/expected-bundle.js +++ b/test/js/samples/component-static/expected-bundle.js @@ -133,9 +133,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/computed-collapsed-if/expected-bundle.js b/test/js/samples/computed-collapsed-if/expected-bundle.js index 8a703ed250..c378d78026 100644 --- a/test/js/samples/computed-collapsed-if/expected-bundle.js +++ b/test/js/samples/computed-collapsed-if/expected-bundle.js @@ -133,9 +133,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/css-media-query/expected-bundle.js b/test/js/samples/css-media-query/expected-bundle.js index 6fa07bfcdb..11ffa87f21 100644 --- a/test/js/samples/css-media-query/expected-bundle.js +++ b/test/js/samples/css-media-query/expected-bundle.js @@ -153,9 +153,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/css-shadow-dom-keyframes/expected-bundle.js b/test/js/samples/css-shadow-dom-keyframes/expected-bundle.js index 89287471cb..86edac7465 100644 --- a/test/js/samples/css-shadow-dom-keyframes/expected-bundle.js +++ b/test/js/samples/css-shadow-dom-keyframes/expected-bundle.js @@ -145,9 +145,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/each-block-changed-check/expected-bundle.js b/test/js/samples/each-block-changed-check/expected-bundle.js index c73357a79d..f421b344b2 100644 --- a/test/js/samples/each-block-changed-check/expected-bundle.js +++ b/test/js/samples/each-block-changed-check/expected-bundle.js @@ -165,9 +165,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/event-handlers-custom/expected-bundle.js b/test/js/samples/event-handlers-custom/expected-bundle.js index 24b32d9b1a..46c109100a 100644 --- a/test/js/samples/event-handlers-custom/expected-bundle.js +++ b/test/js/samples/event-handlers-custom/expected-bundle.js @@ -145,9 +145,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/if-block-no-update/expected-bundle.js b/test/js/samples/if-block-no-update/expected-bundle.js index aea386c29a..0277095a83 100644 --- a/test/js/samples/if-block-no-update/expected-bundle.js +++ b/test/js/samples/if-block-no-update/expected-bundle.js @@ -149,9 +149,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/if-block-simple/expected-bundle.js b/test/js/samples/if-block-simple/expected-bundle.js index be77d10110..40acbe3959 100644 --- a/test/js/samples/if-block-simple/expected-bundle.js +++ b/test/js/samples/if-block-simple/expected-bundle.js @@ -149,9 +149,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/inline-style-optimized-multiple/expected-bundle.js b/test/js/samples/inline-style-optimized-multiple/expected-bundle.js index e36eec0994..eff6f53cfa 100644 --- a/test/js/samples/inline-style-optimized-multiple/expected-bundle.js +++ b/test/js/samples/inline-style-optimized-multiple/expected-bundle.js @@ -149,9 +149,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/inline-style-optimized-url/expected-bundle.js b/test/js/samples/inline-style-optimized-url/expected-bundle.js index cbd51d1473..3a126946ad 100644 --- a/test/js/samples/inline-style-optimized-url/expected-bundle.js +++ b/test/js/samples/inline-style-optimized-url/expected-bundle.js @@ -149,9 +149,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/inline-style-optimized/expected-bundle.js b/test/js/samples/inline-style-optimized/expected-bundle.js index 7dd9e42578..0c127a6391 100644 --- a/test/js/samples/inline-style-optimized/expected-bundle.js +++ b/test/js/samples/inline-style-optimized/expected-bundle.js @@ -149,9 +149,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/inline-style-unoptimized/expected-bundle.js b/test/js/samples/inline-style-unoptimized/expected-bundle.js index 8d383eb38b..d6f545111a 100644 --- a/test/js/samples/inline-style-unoptimized/expected-bundle.js +++ b/test/js/samples/inline-style-unoptimized/expected-bundle.js @@ -149,9 +149,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/input-without-blowback-guard/expected-bundle.js b/test/js/samples/input-without-blowback-guard/expected-bundle.js index 4b42f9e572..03756ad56c 100644 --- a/test/js/samples/input-without-blowback-guard/expected-bundle.js +++ b/test/js/samples/input-without-blowback-guard/expected-bundle.js @@ -153,9 +153,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/legacy-input-type/expected-bundle.js b/test/js/samples/legacy-input-type/expected-bundle.js index 19c61b0d19..7211738f45 100644 --- a/test/js/samples/legacy-input-type/expected-bundle.js +++ b/test/js/samples/legacy-input-type/expected-bundle.js @@ -151,9 +151,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/legacy-quote-class/expected-bundle.js b/test/js/samples/legacy-quote-class/expected-bundle.js index a88e843b6f..1575bf4064 100644 --- a/test/js/samples/legacy-quote-class/expected-bundle.js +++ b/test/js/samples/legacy-quote-class/expected-bundle.js @@ -168,9 +168,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/media-bindings/expected-bundle.js b/test/js/samples/media-bindings/expected-bundle.js index 19b7247c74..92eb665731 100644 --- a/test/js/samples/media-bindings/expected-bundle.js +++ b/test/js/samples/media-bindings/expected-bundle.js @@ -161,9 +161,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/non-imported-component/expected-bundle.js b/test/js/samples/non-imported-component/expected-bundle.js index 549c018e7e..575be613fa 100644 --- a/test/js/samples/non-imported-component/expected-bundle.js +++ b/test/js/samples/non-imported-component/expected-bundle.js @@ -147,9 +147,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js b/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js index 6199e5061f..f1e701bd04 100644 --- a/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js +++ b/test/js/samples/onrender-onteardown-rewritten/expected-bundle.js @@ -133,9 +133,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/setup-method/expected-bundle.js b/test/js/samples/setup-method/expected-bundle.js index 3ec6edbb1b..0c545ea70c 100644 --- a/test/js/samples/setup-method/expected-bundle.js +++ b/test/js/samples/setup-method/expected-bundle.js @@ -133,9 +133,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { diff --git a/test/js/samples/use-elements-as-anchors/expected-bundle.js b/test/js/samples/use-elements-as-anchors/expected-bundle.js index 0529108287..8805cc3847 100644 --- a/test/js/samples/use-elements-as-anchors/expected-bundle.js +++ b/test/js/samples/use-elements-as-anchors/expected-bundle.js @@ -157,9 +157,12 @@ function _set(newState) { this._state = assign({}, oldState, newState); this._recompute(changed, this._state); if (this._bind) this._bind(changed, this._state); - dispatchObservers(this, this._observers.pre, changed, this._state, oldState); - this._fragment.p(changed, this._state); - dispatchObservers(this, this._observers.post, changed, this._state, oldState); + + if (this._fragment) { + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); + } } function callAll(fns) { From 06425e30613309069701274659f4f53a78000306 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 10 Nov 2017 09:19:55 -0500 Subject: [PATCH 04/15] use prepare npm script to support installing from git --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index e40e408efb..780a8c2a2d 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "precodecov": "npm run coverage", "lint": "eslint src test/*.js", "build": "node src/shared/_build.js && rollup -c", + "prepare": "npm run build", "dev": "node src/shared/_build.js && rollup -c -w", "pretest": "npm run build", "prepublishOnly": "npm run lint && npm test", From d28942d91a5e3d93fddfcd90edec1e2bfb5adb0b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 13 Nov 2017 21:53:15 -0500 Subject: [PATCH 05/15] dont use innerHTML for options inside optgroups - fixes #915 --- src/generators/dom/preprocess.ts | 4 +- .../binding-select-optgroup/_config.js | 39 +++++++++++++++++++ .../samples/binding-select-optgroup/main.html | 8 ++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 test/runtime/samples/binding-select-optgroup/_config.js create mode 100644 test/runtime/samples/binding-select-optgroup/main.html diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index a93b2b4048..94b8ea444d 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -309,7 +309,7 @@ const preprocessors = { stripWhitespace: boolean, nextSibling: Node ) => { - if (node.name === 'slot') { + if (node.name === 'slot' || node.name === 'option') { cannotUseInnerHTML(node); } @@ -369,8 +369,6 @@ const preprocessors = { // so that if `foo.qux` changes, we know that we need to // mark `bar` and `baz` as dirty too if (node.name === 'select') { - cannotUseInnerHTML(node); - const value = node.attributes.find( (attribute: Node) => attribute.name === 'value' ); diff --git a/test/runtime/samples/binding-select-optgroup/_config.js b/test/runtime/samples/binding-select-optgroup/_config.js new file mode 100644 index 0000000000..0d651b6654 --- /dev/null +++ b/test/runtime/samples/binding-select-optgroup/_config.js @@ -0,0 +1,39 @@ +export default { + skip: true, // JSDOM + + html: ` +

Hello Harry!

+ + + `, + + test(assert, component, target, window) { + const select = target.querySelector('select'); + const options = [...target.querySelectorAll('option')]; + + assert.deepEqual(options, select.options); + assert.equal(component.get('name'), 'Harry'); + + const change = new window.Event('change'); + + options[1].selected = true; + select.dispatchEvent(change); + + assert.equal(component.get('name'), 'World'); + assert.htmlEqual(target.innerHTML, ` +

Hello World!

+ + + `); + }, +}; diff --git a/test/runtime/samples/binding-select-optgroup/main.html b/test/runtime/samples/binding-select-optgroup/main.html new file mode 100644 index 0000000000..2160d0e2a8 --- /dev/null +++ b/test/runtime/samples/binding-select-optgroup/main.html @@ -0,0 +1,8 @@ +

Hello {{name}}!

+ + \ No newline at end of file From ea3f58a786da3b10a4ebe68e85df27fcdf529250 Mon Sep 17 00:00:00 2001 From: esarbanis Date: Thu, 16 Nov 2017 11:39:58 +0200 Subject: [PATCH 06/15] Use el.dataset.foo = bar instead of setAttribute(el, 'data-foo', bar) Closes #858 --- .../dom/visitors/Element/Attribute.ts | 14 +- .../samples/do-use-dataset/expected-bundle.js | 236 +++++++++++++++++ test/js/samples/do-use-dataset/expected.js | 55 ++++ test/js/samples/do-use-dataset/input.html | 2 + .../dont-use-dataset-in-legacy/_config.js | 5 + .../expected-bundle.js | 240 ++++++++++++++++++ .../dont-use-dataset-in-legacy/expected.js | 55 ++++ .../dont-use-dataset-in-legacy/input.html | 2 + 8 files changed, 607 insertions(+), 2 deletions(-) create mode 100644 test/js/samples/do-use-dataset/expected-bundle.js create mode 100644 test/js/samples/do-use-dataset/expected.js create mode 100644 test/js/samples/do-use-dataset/input.html create mode 100644 test/js/samples/dont-use-dataset-in-legacy/_config.js create mode 100644 test/js/samples/dont-use-dataset-in-legacy/expected-bundle.js create mode 100644 test/js/samples/dont-use-dataset-in-legacy/expected.js create mode 100644 test/js/samples/dont-use-dataset-in-legacy/input.html diff --git a/src/generators/dom/visitors/Element/Attribute.ts b/src/generators/dom/visitors/Element/Attribute.ts index 837e0945db..2baf4a7d96 100644 --- a/src/generators/dom/visitors/Element/Attribute.ts +++ b/src/generators/dom/visitors/Element/Attribute.ts @@ -3,7 +3,6 @@ import deindent from '../../../../utils/deindent'; import visitStyleAttribute, { optimizeStyle } from './StyleAttribute'; import { stringify } from '../../../../utils/stringify'; import getExpressionPrecedence from '../../../../utils/getExpressionPrecedence'; -import getStaticAttributeValue from '../../../../utils/getStaticAttributeValue'; import { DomGenerator } from '../../index'; import Block from '../../Block'; import { Node } from '../../../../interfaces'; @@ -56,6 +55,11 @@ export default function visitAttribute( const isLegacyInputType = generator.legacy && name === 'type' && node.name === 'input'; + const isDataSet = /^data-/.test(name) && !generator.legacy; + const camelCaseName = isDataSet ? name.replace('data-', '').replace(/(-\w)/g, function (m) { + return m[1].toUpperCase(); + }) : name; + if (isDynamic) { let value; @@ -163,6 +167,11 @@ export default function visitAttribute( `${state.parentNode}.${propertyName} = ${init};` ); updater = `${state.parentNode}.${propertyName} = ${shouldCache || isSelectValueAttribute ? last : value};`; + } else if (isDataSet) { + block.builders.hydrate.addLine( + `${state.parentNode}.dataset.${camelCaseName} = ${init};` + ); + updater = `${state.parentNode}.dataset.${camelCaseName} = ${shouldCache || isSelectValueAttribute ? last : value};`; } else { block.builders.hydrate.addLine( `${method}(${state.parentNode}, "${name}", ${init});` @@ -198,6 +207,7 @@ export default function visitAttribute( const statement = ( isLegacyInputType ? `@setInputType(${state.parentNode}, ${value});` : propertyName ? `${state.parentNode}.${propertyName} = ${value};` : + isDataSet ? `${state.parentNode}.dataset.${camelCaseName} = ${value};` : `${method}(${state.parentNode}, "${name}", ${value});` ); @@ -221,4 +231,4 @@ export default function visitAttribute( block.builders.hydrate.addLine(updateValue); if (isDynamic) block.builders.update.addLine(updateValue); } -} \ No newline at end of file +} diff --git a/test/js/samples/do-use-dataset/expected-bundle.js b/test/js/samples/do-use-dataset/expected-bundle.js new file mode 100644 index 0000000000..efd52585f3 --- /dev/null +++ b/test/js/samples/do-use-dataset/expected-bundle.js @@ -0,0 +1,236 @@ +function noop() {} + +function assign(target) { + var k, + source, + i = 1, + len = arguments.length; + for (; i < len; i++) { + source = arguments[i]; + for (k in source) target[k] = source[k]; + } + + return target; +} + +function insertNode(node, target, anchor) { + target.insertBefore(node, anchor); +} + +function detachNode(node) { + node.parentNode.removeChild(node); +} + +function createElement(name) { + return document.createElement(name); +} + +function createText(data) { + return document.createTextNode(data); +} + +function blankObject() { + return Object.create(null); +} + +function destroy(detach) { + this.destroy = noop; + this.fire('destroy'); + this.set = this.get = noop; + + if (detach !== false) this._fragment.u(); + this._fragment.d(); + this._fragment = this._state = null; +} + +function differs(a, b) { + return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); +} + +function dispatchObservers(component, group, changed, newState, oldState) { + for (var key in group) { + if (!changed[key]) continue; + + var newValue = newState[key]; + var oldValue = oldState[key]; + + var callbacks = group[key]; + if (!callbacks) continue; + + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; + + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; + } + } +} + +function fire(eventName, data) { + var handlers = + eventName in this._handlers && this._handlers[eventName].slice(); + if (!handlers) return; + + for (var i = 0; i < handlers.length; i += 1) { + handlers[i].call(this, data); + } +} + +function get(key) { + return key ? this._state[key] : this._state; +} + +function init(component, options) { + component.options = options; + + component._observers = { pre: blankObject(), post: blankObject() }; + component._handlers = blankObject(); + component._root = options._root || component; + component._bind = options._bind; +} + +function observe(key, callback, options) { + var group = options && options.defer + ? this._observers.post + : this._observers.pre; + + (group[key] || (group[key] = [])).push(callback); + + if (!options || options.init !== false) { + callback.__calling = true; + callback.call(this, this._state[key]); + callback.__calling = false; + } + + return { + cancel: function() { + var index = group[key].indexOf(callback); + if (~index) group[key].splice(index, 1); + } + }; +} + +function on(eventName, handler) { + if (eventName === 'teardown') return this.on('destroy', handler); + + var handlers = this._handlers[eventName] || (this._handlers[eventName] = []); + handlers.push(handler); + + return { + cancel: function() { + var index = handlers.indexOf(handler); + if (~index) handlers.splice(index, 1); + } + }; +} + +function set(newState) { + this._set(assign({}, newState)); + if (this._root._lock) return; + this._root._lock = true; + callAll(this._root._beforecreate); + callAll(this._root._oncreate); + callAll(this._root._aftercreate); + this._root._lock = false; +} + +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state); + if (this._bind) this._bind(changed, this._state); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + +function callAll(fns) { + while (fns && fns.length) fns.pop()(); +} + +function _mount(target, anchor) { + this._fragment.m(target, anchor); +} + +function _unmount() { + this._fragment.u(); +} + +var proto = { + destroy: destroy, + get: get, + fire: fire, + observe: observe, + on: on, + set: set, + teardown: destroy, + _recompute: noop, + _set: _set, + _mount: _mount, + _unmount: _unmount +}; + +/* generated by Svelte vX.Y.Z */ +function create_main_fragment(state, component) { + var div, text, div_1; + + return { + c: function create() { + div = createElement("div"); + text = createText("\n"); + div_1 = createElement("div"); + this.h(); + }, + + h: function hydrate() { + div.dataset.foo = "bar"; + div_1.dataset.foo = state.bar; + }, + + m: function mount(target, anchor) { + insertNode(div, target, anchor); + insertNode(text, target, anchor); + insertNode(div_1, target, anchor); + }, + + p: function update(changed, state) { + if (changed.bar) { + div_1.dataset.foo = state.bar; + } + }, + + u: function unmount() { + detachNode(div); + detachNode(text); + detachNode(div_1); + }, + + d: noop + }; +} + +function SvelteComponent(options) { + init(this, options); + this._state = assign({}, options.data); + + this._fragment = create_main_fragment(this._state, this); + + if (options.target) { + this._fragment.c(); + this._fragment.m(options.target, options.anchor || null); + } +} + +assign(SvelteComponent.prototype, proto); + +export default SvelteComponent; diff --git a/test/js/samples/do-use-dataset/expected.js b/test/js/samples/do-use-dataset/expected.js new file mode 100644 index 0000000000..7e67abbfa0 --- /dev/null +++ b/test/js/samples/do-use-dataset/expected.js @@ -0,0 +1,55 @@ +/* generated by Svelte vX.Y.Z */ +import { assign, createElement, createText, detachNode, init, insertNode, noop, proto } from "svelte/shared.js"; + +function create_main_fragment(state, component) { + var div, text, div_1; + + return { + c: function create() { + div = createElement("div"); + text = createText("\n"); + div_1 = createElement("div"); + this.h(); + }, + + h: function hydrate() { + div.dataset.foo = "bar"; + div_1.dataset.foo = state.bar; + }, + + m: function mount(target, anchor) { + insertNode(div, target, anchor); + insertNode(text, target, anchor); + insertNode(div_1, target, anchor); + }, + + p: function update(changed, state) { + if (changed.bar) { + div_1.dataset.foo = state.bar; + } + }, + + u: function unmount() { + detachNode(div); + detachNode(text); + detachNode(div_1); + }, + + d: noop + }; +} + +function SvelteComponent(options) { + init(this, options); + this._state = assign({}, options.data); + + this._fragment = create_main_fragment(this._state, this); + + if (options.target) { + this._fragment.c(); + this._fragment.m(options.target, options.anchor || null); + } +} + +assign(SvelteComponent.prototype, proto); +export default SvelteComponent; diff --git a/test/js/samples/do-use-dataset/input.html b/test/js/samples/do-use-dataset/input.html new file mode 100644 index 0000000000..acd9d623b4 --- /dev/null +++ b/test/js/samples/do-use-dataset/input.html @@ -0,0 +1,2 @@ +
+
diff --git a/test/js/samples/dont-use-dataset-in-legacy/_config.js b/test/js/samples/dont-use-dataset-in-legacy/_config.js new file mode 100644 index 0000000000..67924a4ffe --- /dev/null +++ b/test/js/samples/dont-use-dataset-in-legacy/_config.js @@ -0,0 +1,5 @@ +export default { + options: { + legacy: true + } +}; diff --git a/test/js/samples/dont-use-dataset-in-legacy/expected-bundle.js b/test/js/samples/dont-use-dataset-in-legacy/expected-bundle.js new file mode 100644 index 0000000000..4eaea5ee0a --- /dev/null +++ b/test/js/samples/dont-use-dataset-in-legacy/expected-bundle.js @@ -0,0 +1,240 @@ +function noop() {} + +function assign(target) { + var k, + source, + i = 1, + len = arguments.length; + for (; i < len; i++) { + source = arguments[i]; + for (k in source) target[k] = source[k]; + } + + return target; +} + +function insertNode(node, target, anchor) { + target.insertBefore(node, anchor); +} + +function detachNode(node) { + node.parentNode.removeChild(node); +} + +function createElement(name) { + return document.createElement(name); +} + +function createText(data) { + return document.createTextNode(data); +} + +function setAttribute(node, attribute, value) { + node.setAttribute(attribute, value); +} + +function blankObject() { + return Object.create(null); +} + +function destroy(detach) { + this.destroy = noop; + this.fire('destroy'); + this.set = this.get = noop; + + if (detach !== false) this._fragment.u(); + this._fragment.d(); + this._fragment = this._state = null; +} + +function differs(a, b) { + return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); +} + +function dispatchObservers(component, group, changed, newState, oldState) { + for (var key in group) { + if (!changed[key]) continue; + + var newValue = newState[key]; + var oldValue = oldState[key]; + + var callbacks = group[key]; + if (!callbacks) continue; + + for (var i = 0; i < callbacks.length; i += 1) { + var callback = callbacks[i]; + if (callback.__calling) continue; + + callback.__calling = true; + callback.call(component, newValue, oldValue); + callback.__calling = false; + } + } +} + +function fire(eventName, data) { + var handlers = + eventName in this._handlers && this._handlers[eventName].slice(); + if (!handlers) return; + + for (var i = 0; i < handlers.length; i += 1) { + handlers[i].call(this, data); + } +} + +function get(key) { + return key ? this._state[key] : this._state; +} + +function init(component, options) { + component.options = options; + + component._observers = { pre: blankObject(), post: blankObject() }; + component._handlers = blankObject(); + component._root = options._root || component; + component._bind = options._bind; +} + +function observe(key, callback, options) { + var group = options && options.defer + ? this._observers.post + : this._observers.pre; + + (group[key] || (group[key] = [])).push(callback); + + if (!options || options.init !== false) { + callback.__calling = true; + callback.call(this, this._state[key]); + callback.__calling = false; + } + + return { + cancel: function() { + var index = group[key].indexOf(callback); + if (~index) group[key].splice(index, 1); + } + }; +} + +function on(eventName, handler) { + if (eventName === 'teardown') return this.on('destroy', handler); + + var handlers = this._handlers[eventName] || (this._handlers[eventName] = []); + handlers.push(handler); + + return { + cancel: function() { + var index = handlers.indexOf(handler); + if (~index) handlers.splice(index, 1); + } + }; +} + +function set(newState) { + this._set(assign({}, newState)); + if (this._root._lock) return; + this._root._lock = true; + callAll(this._root._beforecreate); + callAll(this._root._oncreate); + callAll(this._root._aftercreate); + this._root._lock = false; +} + +function _set(newState) { + var oldState = this._state, + changed = {}, + dirty = false; + + for (var key in newState) { + if (differs(newState[key], oldState[key])) changed[key] = dirty = true; + } + if (!dirty) return; + + this._state = assign({}, oldState, newState); + this._recompute(changed, this._state); + if (this._bind) this._bind(changed, this._state); + dispatchObservers(this, this._observers.pre, changed, this._state, oldState); + this._fragment.p(changed, this._state); + dispatchObservers(this, this._observers.post, changed, this._state, oldState); +} + +function callAll(fns) { + while (fns && fns.length) fns.pop()(); +} + +function _mount(target, anchor) { + this._fragment.m(target, anchor); +} + +function _unmount() { + this._fragment.u(); +} + +var proto = { + destroy: destroy, + get: get, + fire: fire, + observe: observe, + on: on, + set: set, + teardown: destroy, + _recompute: noop, + _set: _set, + _mount: _mount, + _unmount: _unmount +}; + +/* generated by Svelte vX.Y.Z */ +function create_main_fragment(state, component) { + var div, text, div_1; + + return { + c: function create() { + div = createElement("div"); + text = createText("\n"); + div_1 = createElement("div"); + this.h(); + }, + + h: function hydrate() { + setAttribute(div, "data-foo", "bar"); + setAttribute(div_1, "data-foo", state.bar); + }, + + m: function mount(target, anchor) { + insertNode(div, target, anchor); + insertNode(text, target, anchor); + insertNode(div_1, target, anchor); + }, + + p: function update(changed, state) { + if (changed.bar) { + setAttribute(div_1, "data-foo", state.bar); + } + }, + + u: function unmount() { + detachNode(div); + detachNode(text); + detachNode(div_1); + }, + + d: noop + }; +} + +function SvelteComponent(options) { + init(this, options); + this._state = assign({}, options.data); + + this._fragment = create_main_fragment(this._state, this); + + if (options.target) { + this._fragment.c(); + this._fragment.m(options.target, options.anchor || null); + } +} + +assign(SvelteComponent.prototype, proto); + +export default SvelteComponent; diff --git a/test/js/samples/dont-use-dataset-in-legacy/expected.js b/test/js/samples/dont-use-dataset-in-legacy/expected.js new file mode 100644 index 0000000000..a10c40c13d --- /dev/null +++ b/test/js/samples/dont-use-dataset-in-legacy/expected.js @@ -0,0 +1,55 @@ +/* generated by Svelte vX.Y.Z */ +import { assign, createElement, createText, detachNode, init, insertNode, noop, proto, setAttribute } from "svelte/shared.js"; + +function create_main_fragment(state, component) { + var div, text, div_1; + + return { + c: function create() { + div = createElement("div"); + text = createText("\n"); + div_1 = createElement("div"); + this.h(); + }, + + h: function hydrate() { + setAttribute(div, "data-foo", "bar"); + setAttribute(div_1, "data-foo", state.bar); + }, + + m: function mount(target, anchor) { + insertNode(div, target, anchor); + insertNode(text, target, anchor); + insertNode(div_1, target, anchor); + }, + + p: function update(changed, state) { + if (changed.bar) { + setAttribute(div_1, "data-foo", state.bar); + } + }, + + u: function unmount() { + detachNode(div); + detachNode(text); + detachNode(div_1); + }, + + d: noop + }; +} + +function SvelteComponent(options) { + init(this, options); + this._state = assign({}, options.data); + + this._fragment = create_main_fragment(this._state, this); + + if (options.target) { + this._fragment.c(); + this._fragment.m(options.target, options.anchor || null); + } +} + +assign(SvelteComponent.prototype, proto); +export default SvelteComponent; diff --git a/test/js/samples/dont-use-dataset-in-legacy/input.html b/test/js/samples/dont-use-dataset-in-legacy/input.html new file mode 100644 index 0000000000..acd9d623b4 --- /dev/null +++ b/test/js/samples/dont-use-dataset-in-legacy/input.html @@ -0,0 +1,2 @@ +
+
From 8ae0cac67e80ed058ac56a8ea6609c3df0359f31 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 18 Nov 2017 13:35:50 -0500 Subject: [PATCH 07/15] -> v1.41.4 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98068b6b26..05d03f06f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Svelte changelog +## 1.41.4 + +* Handle self-destructive bindings ([#917](https://github.com/sveltejs/svelte/issues/917)) +* Prevent `innerHTML` with `