From 52dbc882a7e7396e76d17be8fa5357b3be14e188 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 25 Dec 2019 00:53:18 +0800 Subject: [PATCH 1/9] feat order attributes + actions too (#4156) Co-authored-by: Conduitry --- .../render_dom/wrappers/Element/index.ts | 52 +++++++++--------- .../render_dom/wrappers/shared/add_actions.ts | 54 +++++++++---------- .../wrappers/shared/add_event_handlers.ts | 10 +++- src/runtime/internal/utils.ts | 6 ++- .../action-custom-event-handler/expected.js | 10 ++-- test/js/samples/action/expected.js | 7 +-- .../apply-directives-in-order-2/_config.js | 38 +++++++++++++ .../apply-directives-in-order-2/main.svelte | 34 ++++++++++++ 8 files changed, 149 insertions(+), 62 deletions(-) create mode 100644 test/runtime/samples/apply-directives-in-order-2/_config.js create mode 100644 test/runtime/samples/apply-directives-in-order-2/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 0478702c7c..ef33022402 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -16,8 +16,8 @@ import { dimensions } from '../../../../utils/patterns'; import Binding from './Binding'; import InlineComponentWrapper from '../InlineComponent'; import add_to_set from '../../../utils/add_to_set'; -import add_event_handlers from '../shared/add_event_handlers'; -import add_actions from '../shared/add_actions'; +import { add_event_handler } from '../shared/add_event_handlers'; +import { add_action } from '../shared/add_actions'; import create_debugging_comment from '../shared/create_debugging_comment'; import { get_slot_definition } from '../shared/get_slot_definition'; import bind_this from '../shared/bind_this'; @@ -25,6 +25,7 @@ import { is_head } from '../shared/is_head'; import { Identifier } from 'estree'; import EventHandler from './EventHandler'; import { extract_names } from 'periscopic'; +import Action from '../../../nodes/Action'; const events = [ { @@ -380,7 +381,6 @@ export default class ElementWrapper extends Wrapper { this.add_directives_in_order(block); this.add_transitions(block); this.add_animation(block); - this.add_actions(block); this.add_classes(block); this.add_manual_style_scoping(block); @@ -441,6 +441,8 @@ export default class ElementWrapper extends Wrapper { bindings: Binding[]; } + type OrderedAttribute = EventHandler | BindingGroup | Binding | Action; + const bindingGroups = events .map(event => ({ events: event.event_names, @@ -452,29 +454,37 @@ export default class ElementWrapper extends Wrapper { const this_binding = this.bindings.find(b => b.node.name === 'this'); - function getOrder (item: EventHandler | BindingGroup | Binding) { + function getOrder (item: OrderedAttribute) { if (item instanceof EventHandler) { return item.node.start; } else if (item instanceof Binding) { return item.node.start; + } else if (item instanceof Action) { + return item.start; } else { return item.bindings[0].node.start; } } - const ordered: Array = [].concat(bindingGroups, this.event_handlers, this_binding).filter(Boolean); - - ordered.sort((a, b) => getOrder(a) - getOrder(b)); - - ordered.forEach(bindingGroupOrEventHandler => { - if (bindingGroupOrEventHandler instanceof EventHandler) { - add_event_handlers(block, this.var, [bindingGroupOrEventHandler]); - } else if (bindingGroupOrEventHandler instanceof Binding) { - this.add_this_binding(block, bindingGroupOrEventHandler); - } else { - this.add_bindings(block, bindingGroupOrEventHandler); - } - }); + ([ + ...bindingGroups, + ...this.event_handlers, + this_binding, + ...this.node.actions + ] as OrderedAttribute[]) + .filter(Boolean) + .sort((a, b) => getOrder(a) - getOrder(b)) + .forEach(item => { + if (item instanceof EventHandler) { + add_event_handler(block, this.var, item); + } else if (item instanceof Binding) { + this.add_this_binding(block, item); + } else if (item instanceof Action) { + add_action(block, this.var, item); + } else { + this.add_bindings(block, item); + } + }); } add_bindings(block: Block, bindingGroup) { @@ -701,10 +711,6 @@ export default class ElementWrapper extends Wrapper { `); } - add_event_handlers(block: Block) { - add_event_handlers(block, this.var, this.event_handlers); - } - add_transitions( block: Block ) { @@ -866,10 +872,6 @@ export default class ElementWrapper extends Wrapper { `); } - add_actions(block: Block) { - add_actions(block, this.var, this.node.actions); - } - add_classes(block: Block) { const has_spread = this.node.attributes.some(attr => attr.is_spread); this.node.classes.forEach(class_directive => { diff --git a/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts b/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts index e8f1ecb916..3d8c4fbbc0 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts @@ -7,42 +7,40 @@ export default function add_actions( target: string, actions: Action[] ) { - actions.forEach(action => { - const { expression } = action; - let snippet; - let dependencies; - - if (expression) { - snippet = expression.manipulate(block); - dependencies = expression.dynamic_dependencies(); - } + actions.forEach(action => add_action(block, target, action)); +} - const id = block.get_unique_name( - `${action.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action` - ); +export function add_action(block: Block, target: string, action: Action) { + const { expression } = action; + let snippet; + let dependencies; - block.add_variable(id); + if (expression) { + snippet = expression.manipulate(block); + dependencies = expression.dynamic_dependencies(); + } - const fn = block.renderer.reference(action.name); + const id = block.get_unique_name( + `${action.name.replace(/[^a-zA-Z0-9_$]/g, '_')}_action` + ); - block.chunks.mount.push( - b`${id} = ${fn}.call(null, ${target}, ${snippet}) || {};` - ); + block.add_variable(id); + + const fn = block.renderer.reference(action.name); - if (dependencies && dependencies.length > 0) { - let condition = x`@is_function(${id}.update)`; + block.event_listeners.push( + x`@action_destroyer(${id} = ${fn}.call(null, ${target}, ${snippet}))` + ); - if (dependencies.length > 0) { - condition = x`${condition} && ${block.renderer.dirty(dependencies)}`; - } + if (dependencies && dependencies.length > 0) { + let condition = x`${id} && @is_function(${id}.update)`; - block.chunks.update.push( - b`if (${condition}) ${id}.update.call(null, ${snippet});` - ); + if (dependencies.length > 0) { + condition = x`${condition} && ${block.renderer.dirty(dependencies)}`; } - block.chunks.destroy.push( - b`if (${id} && @is_function(${id}.destroy)) ${id}.destroy();` + block.chunks.update.push( + b`if (${condition}) ${id}.update.call(null, ${snippet});` ); - }); + } } diff --git a/src/compiler/compile/render_dom/wrappers/shared/add_event_handlers.ts b/src/compiler/compile/render_dom/wrappers/shared/add_event_handlers.ts index ca4f5a24c7..23a37715cc 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/add_event_handlers.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/add_event_handlers.ts @@ -6,5 +6,13 @@ export default function add_event_handlers( target: string, handlers: EventHandler[] ) { - handlers.forEach(handler => handler.render(block, target)); + handlers.forEach(handler => add_event_handler(block, target, handler)); +} + +export function add_event_handler( + block: Block, + target: string, + handler: EventHandler +) { + handler.render(block, target); } diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index b844f1dd4c..c94a135869 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -120,4 +120,8 @@ export function set_store_value(store, ret, value = ret) { return ret; } -export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); \ No newline at end of file +export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); + +export function action_destroyer(action_result) { + return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; +} \ No newline at end of file diff --git a/test/js/samples/action-custom-event-handler/expected.js b/test/js/samples/action-custom-event-handler/expected.js index 6c3a2a5b0b..da42603895 100644 --- a/test/js/samples/action-custom-event-handler/expected.js +++ b/test/js/samples/action-custom-event-handler/expected.js @@ -1,6 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent, + action_destroyer, detach, element, init, @@ -13,24 +14,25 @@ import { function create_fragment(ctx) { let button; let foo_action; + let dispose; return { c() { button = element("button"); button.textContent = "foo"; + dispose = action_destroyer(foo_action = foo.call(null, button, /*foo_function*/ ctx[1])); }, m(target, anchor) { insert(target, button, anchor); - foo_action = foo.call(null, button, /*foo_function*/ ctx[1]) || ({}); }, p(ctx, [dirty]) { - if (is_function(foo_action.update) && dirty & /*bar*/ 1) foo_action.update.call(null, /*foo_function*/ ctx[1]); + if (foo_action && is_function(foo_action.update) && dirty & /*bar*/ 1) foo_action.update.call(null, /*foo_function*/ ctx[1]); }, i: noop, o: noop, d(detaching) { if (detaching) detach(button); - if (foo_action && is_function(foo_action.destroy)) foo_action.destroy(); + dispose(); } }; } @@ -40,7 +42,7 @@ function handleFoo(bar) { } function foo(node, callback) { - + } function instance($$self, $$props, $$invalidate) { diff --git a/test/js/samples/action/expected.js b/test/js/samples/action/expected.js index 78fc4855c6..dc3ebb5cf8 100644 --- a/test/js/samples/action/expected.js +++ b/test/js/samples/action/expected.js @@ -1,12 +1,12 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent, + action_destroyer, attr, detach, element, init, insert, - is_function, noop, safe_not_equal } from "svelte/internal"; @@ -14,23 +14,24 @@ import { function create_fragment(ctx) { let a; let link_action; + let dispose; return { c() { a = element("a"); a.textContent = "Test"; attr(a, "href", "#"); + dispose = action_destroyer(link_action = link.call(null, a)); }, m(target, anchor) { insert(target, a, anchor); - link_action = link.call(null, a) || ({}); }, p: noop, i: noop, o: noop, d(detaching) { if (detaching) detach(a); - if (link_action && is_function(link_action.destroy)) link_action.destroy(); + dispose(); } }; } diff --git a/test/runtime/samples/apply-directives-in-order-2/_config.js b/test/runtime/samples/apply-directives-in-order-2/_config.js new file mode 100644 index 0000000000..a74ce41cb6 --- /dev/null +++ b/test/runtime/samples/apply-directives-in-order-2/_config.js @@ -0,0 +1,38 @@ +const value = []; +export default { + props: { + value, + }, + + async test({ assert, component, target, window }) { + const inputs = target.querySelectorAll('input'); + + const event = new window.Event('input'); + + for (const input of inputs) { + input.value = 'h'; + await input.dispatchEvent(event); + } + + assert.deepEqual(value, [ + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + ]); + }, +}; diff --git a/test/runtime/samples/apply-directives-in-order-2/main.svelte b/test/runtime/samples/apply-directives-in-order-2/main.svelte new file mode 100644 index 0000000000..e91c4b6a14 --- /dev/null +++ b/test/runtime/samples/apply-directives-in-order-2/main.svelte @@ -0,0 +1,34 @@ + + + + + + + + From b59673155cc0e9fd300e33115cd02d99093bf43c Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 24 Dec 2019 10:52:10 -0500 Subject: [PATCH 2/9] site: in dev mode, proxy /repl/[id].json requests to real server --- site/src/routes/repl/[id]/index.json.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/site/src/routes/repl/[id]/index.json.js b/site/src/routes/repl/[id]/index.json.js index e34329a0e2..de41a4cecd 100644 --- a/site/src/routes/repl/[id]/index.json.js +++ b/site/src/routes/repl/[id]/index.json.js @@ -73,6 +73,18 @@ export async function get(req, res) { }); } + if (process.env.NODE_ENV === 'development') { + // In dev, proxy requests to load particular REPLs to the real server. + // This avoids needing to connect to the real database server. + req.pipe( + require('https').request({ host: 'svelte.dev', path: req.url }) + ).once('response', res_proxy => { + res_proxy.pipe(res); + res.writeHead(res_proxy.statusCode, res_proxy.headers); + }).once('error', () => res.end()); + return; + } + const [row] = await query(` select g.*, u.uid as owner from gists g left join users u on g.user_id = u.id From 8a596936d2cad9e39ccbbc2bf7e6498cc50d1e64 Mon Sep 17 00:00:00 2001 From: David Kondrad Date: Tue, 24 Dec 2019 12:07:11 -0500 Subject: [PATCH 3/9] dynamic events: validate handler before executing (#4105) --- .../wrappers/Element/EventHandler.ts | 2 +- .../samples/event-handler-dynamic/expected.js | 3 +- .../event-handler-dynamic-hash/_config.js | 56 +++++++++++++++++++ .../event-handler-dynamic-hash/main.svelte | 23 ++++++++ .../event-handler-dynamic-invalid/_config.js | 28 ++++++++++ .../event-handler-dynamic-invalid/main.svelte | 13 +++++ .../_config.js | 16 ++++++ .../main.svelte | 7 +++ .../_config.js | 16 ++++++ .../main.svelte | 11 ++++ .../_config.js | 16 ++++++ .../main.svelte | 13 +++++ .../_config.js | 19 +++++++ .../main.svelte | 20 +++++++ .../event-handler-dynamic-multiple/_config.js | 14 +++++ .../main.svelte | 11 ++++ .../samples/event-handler-dynamic/_config.js | 10 +++- 17 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 test/runtime/samples/event-handler-dynamic-hash/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic-hash/main.svelte create mode 100644 test/runtime/samples/event-handler-dynamic-invalid/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic-invalid/main.svelte create mode 100644 test/runtime/samples/event-handler-dynamic-modifier-once/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic-modifier-once/main.svelte create mode 100644 test/runtime/samples/event-handler-dynamic-modifier-prevent-default/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic-modifier-prevent-default/main.svelte create mode 100644 test/runtime/samples/event-handler-dynamic-modifier-self/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic-modifier-self/main.svelte create mode 100644 test/runtime/samples/event-handler-dynamic-modifier-stop-propagation/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic-modifier-stop-propagation/main.svelte create mode 100644 test/runtime/samples/event-handler-dynamic-multiple/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic-multiple/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts index 75fa17db2f..03183ee576 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts @@ -30,7 +30,7 @@ export default class EventHandlerWrapper { if (this.node.reassigned) { block.maintain_context = true; - return x`function () { ${snippet}.apply(this, arguments); }`; + return x`function () { if (@is_function(${snippet})) ${snippet}.apply(this, arguments); }`; } return snippet; } diff --git a/test/js/samples/event-handler-dynamic/expected.js b/test/js/samples/event-handler-dynamic/expected.js index 34c33151bf..42c6b2951a 100644 --- a/test/js/samples/event-handler-dynamic/expected.js +++ b/test/js/samples/event-handler-dynamic/expected.js @@ -6,6 +6,7 @@ import { element, init, insert, + is_function, listen, noop, run_all, @@ -46,7 +47,7 @@ function create_fragment(ctx) { listen(button0, "click", /*updateHandler1*/ ctx[2]), listen(button1, "click", /*updateHandler2*/ ctx[3]), listen(button2, "click", function () { - /*clickHandler*/ ctx[0].apply(this, arguments); + if (is_function(/*clickHandler*/ ctx[0])) /*clickHandler*/ ctx[0].apply(this, arguments); }) ]; }, diff --git a/test/runtime/samples/event-handler-dynamic-hash/_config.js b/test/runtime/samples/event-handler-dynamic-hash/_config.js new file mode 100644 index 0000000000..e60e561524 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-hash/_config.js @@ -0,0 +1,56 @@ +export default { + html: ` +

+ + +

+

0

+ + `, + + async test({ assert, component, target, window }) { + const [updateButton1, updateButton2, button] = target.querySelectorAll( + 'button' + ); + + const event = new window.MouseEvent('click'); + let err = ""; + window.addEventListener('error', (e) => { + e.preventDefault(); + err = e.message; + }); + + await button.dispatchEvent(event); + assert.equal(err, "", err); + assert.htmlEqual(target.innerHTML, ` +

+ + +

+

0

+ + `); + + await updateButton1.dispatchEvent(event); + await button.dispatchEvent(event); + assert.htmlEqual(target.innerHTML, ` +

+ + +

+

1

+ + `); + + await updateButton2.dispatchEvent(event); + await button.dispatchEvent(event); + assert.htmlEqual(target.innerHTML, ` +

+ + +

+

2

+ + `); + }, +}; diff --git a/test/runtime/samples/event-handler-dynamic-hash/main.svelte b/test/runtime/samples/event-handler-dynamic-hash/main.svelte new file mode 100644 index 0000000000..c81af02006 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-hash/main.svelte @@ -0,0 +1,23 @@ + + +

+ + +

+ +

{ number }

+ + \ No newline at end of file diff --git a/test/runtime/samples/event-handler-dynamic-invalid/_config.js b/test/runtime/samples/event-handler-dynamic-invalid/_config.js new file mode 100644 index 0000000000..ba1777f945 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-invalid/_config.js @@ -0,0 +1,28 @@ +export default { + html: ` + + `, + + async test({ assert, component, target, window }) { + const [buttonUndef, buttonNull, buttonInvalid] = target.querySelectorAll( + 'button' + ); + + const event = new window.MouseEvent('click'); + let err = ""; + window.addEventListener('error', (e) => { + e.preventDefault(); + err = e.message; + }); + + // All three should not throw if proper checking is done in runtime code + await buttonUndef.dispatchEvent(event); + assert.equal(err, "", err); + + await buttonNull.dispatchEvent(event); + assert.equal(err, "", err); + + await buttonInvalid.dispatchEvent(event); + assert.equal(err, "", err); + }, +}; diff --git a/test/runtime/samples/event-handler-dynamic-invalid/main.svelte b/test/runtime/samples/event-handler-dynamic-invalid/main.svelte new file mode 100644 index 0000000000..f4e8c5fdb7 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-invalid/main.svelte @@ -0,0 +1,13 @@ + + + + + diff --git a/test/runtime/samples/event-handler-dynamic-modifier-once/_config.js b/test/runtime/samples/event-handler-dynamic-modifier-once/_config.js new file mode 100644 index 0000000000..41daf374c8 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-modifier-once/_config.js @@ -0,0 +1,16 @@ +export default { + html: ` + + `, + + async test({ assert, component, target, window }) { + const button = target.querySelector('button'); + const event = new window.MouseEvent('click'); + + await button.dispatchEvent(event); + assert.equal(component.count, 1); + + await button.dispatchEvent(event); + assert.equal(component.count, 1); + } +}; diff --git a/test/runtime/samples/event-handler-dynamic-modifier-once/main.svelte b/test/runtime/samples/event-handler-dynamic-modifier-once/main.svelte new file mode 100644 index 0000000000..d363d708ba --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-modifier-once/main.svelte @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/event-handler-dynamic-modifier-prevent-default/_config.js b/test/runtime/samples/event-handler-dynamic-modifier-prevent-default/_config.js new file mode 100644 index 0000000000..4fa032bf37 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-modifier-prevent-default/_config.js @@ -0,0 +1,16 @@ +export default { + html: ` + + `, + + async test({ assert, component, target, window }) { + const button = target.querySelector('button'); + const event = new window.MouseEvent('click', { + cancelable: true + }); + + await button.dispatchEvent(event); + + assert.ok(component.default_was_prevented); + } +}; diff --git a/test/runtime/samples/event-handler-dynamic-modifier-prevent-default/main.svelte b/test/runtime/samples/event-handler-dynamic-modifier-prevent-default/main.svelte new file mode 100644 index 0000000000..49d42f3305 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-modifier-prevent-default/main.svelte @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/event-handler-dynamic-modifier-self/_config.js b/test/runtime/samples/event-handler-dynamic-modifier-self/_config.js new file mode 100644 index 0000000000..6d7d29e482 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-modifier-self/_config.js @@ -0,0 +1,16 @@ +export default { + html: ` +
+ +
+ `, + + async test({ assert, component, target, window }) { + const button = target.querySelector('button'); + const event = new window.MouseEvent('click'); + + await button.dispatchEvent(event); + + assert.ok(!component.inner_clicked); + }, +}; diff --git a/test/runtime/samples/event-handler-dynamic-modifier-self/main.svelte b/test/runtime/samples/event-handler-dynamic-modifier-self/main.svelte new file mode 100644 index 0000000000..b57d88ec02 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-modifier-self/main.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/test/runtime/samples/event-handler-dynamic-modifier-stop-propagation/_config.js b/test/runtime/samples/event-handler-dynamic-modifier-stop-propagation/_config.js new file mode 100644 index 0000000000..8517429e5c --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-modifier-stop-propagation/_config.js @@ -0,0 +1,19 @@ +export default { + html: ` +
+ +
+ `, + + async test({ assert, component, target, window }) { + const button = target.querySelector('button'); + const event = new window.MouseEvent('click', { + bubbles: true + }); + + await button.dispatchEvent(event); + + assert.ok(component.inner_clicked); + assert.ok(!component.outer_clicked); + } +}; diff --git a/test/runtime/samples/event-handler-dynamic-modifier-stop-propagation/main.svelte b/test/runtime/samples/event-handler-dynamic-modifier-stop-propagation/main.svelte new file mode 100644 index 0000000000..bad7359927 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-modifier-stop-propagation/main.svelte @@ -0,0 +1,20 @@ + + +
+ +
\ No newline at end of file diff --git a/test/runtime/samples/event-handler-dynamic-multiple/_config.js b/test/runtime/samples/event-handler-dynamic-multiple/_config.js new file mode 100644 index 0000000000..cf17c61f60 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-multiple/_config.js @@ -0,0 +1,14 @@ +export default { + html: ` + + `, + + async test({ assert, component, target, window }) { + const button = target.querySelector('button'); + const event = new window.MouseEvent('click'); + + await button.dispatchEvent(event); + assert.equal(component.clickHandlerOne, 1); + assert.equal(component.clickHandlerTwo, 1); + } +}; diff --git a/test/runtime/samples/event-handler-dynamic-multiple/main.svelte b/test/runtime/samples/event-handler-dynamic-multiple/main.svelte new file mode 100644 index 0000000000..2dbbe61ea6 --- /dev/null +++ b/test/runtime/samples/event-handler-dynamic-multiple/main.svelte @@ -0,0 +1,11 @@ + + + diff --git a/test/runtime/samples/event-handler-dynamic/_config.js b/test/runtime/samples/event-handler-dynamic/_config.js index 41cfd6e729..e60e561524 100644 --- a/test/runtime/samples/event-handler-dynamic/_config.js +++ b/test/runtime/samples/event-handler-dynamic/_config.js @@ -14,8 +14,14 @@ export default { ); const event = new window.MouseEvent('click'); + let err = ""; + window.addEventListener('error', (e) => { + e.preventDefault(); + err = e.message; + }); await button.dispatchEvent(event); + assert.equal(err, "", err); assert.htmlEqual(target.innerHTML, `

@@ -24,7 +30,7 @@ export default {

0

`); - + await updateButton1.dispatchEvent(event); await button.dispatchEvent(event); assert.htmlEqual(target.innerHTML, ` @@ -35,7 +41,7 @@ export default {

1

`); - + await updateButton2.dispatchEvent(event); await button.dispatchEvent(event); assert.htmlEqual(target.innerHTML, ` From b6081482382b436b827182afcb84e17043139078 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 24 Dec 2019 12:12:48 -0500 Subject: [PATCH 4/9] -> v3.16.7 --- CHANGELOG.md | 6 ++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e41ce6f7c4..eba4bbe3e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Svelte changelog +## 3.16.7 + +* Also apply actions in the order they're given along with other directives ([#2446](https://github.com/sveltejs/svelte/issues/2446), [#4156](https://github.com/sveltejs/svelte/pull/4156)) +* Check whether a dynamic event handler is a function before calling it ([#4090](https://github.com/sveltejs/svelte/issues/4090)) +* Correctly mark event handlers as dynamic when they involve an expression used in a `bind:` elsewhere ([#4155](https://github.com/sveltejs/svelte/pull/4155)) + ## 3.16.6 * Fix CSS specificity bug when encapsulating styles ([#1277](https://github.com/sveltejs/svelte/issues/1277)) diff --git a/package-lock.json b/package-lock.json index 5b7af2a23f..1051ac0afc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.16.6", + "version": "3.16.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e64b5248f7..d1fee1428d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.16.6", + "version": "3.16.7", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 5f24e97639a4da513afce7ddae3cea07fcb55b2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 22:05:14 +0000 Subject: [PATCH 5/9] Bump handlebars from 4.1.2 to 4.5.3 Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.1.2 to 4.5.3. - [Release notes](https://github.com/wycats/handlebars.js/releases) - [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md) - [Commits](https://github.com/wycats/handlebars.js/compare/v4.1.2...v4.5.3) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1051ac0afc..c9b6efdc75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1496,9 +1496,9 @@ "dev": true }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, "requires": { "neo-async": "^2.6.0", From 7fb35dd0dc372a2ed3dc5faa8ac4dac07464c0c2 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sat, 28 Dec 2019 17:50:20 +0800 Subject: [PATCH 6/9] fix safari input jumping --- .../render_dom/wrappers/Element/Attribute.ts | 10 +++ test/js/samples/input-value/expected.js | 77 +++++++++++++++++++ test/js/samples/input-value/input.svelte | 11 +++ 3 files changed, 98 insertions(+) create mode 100644 test/js/samples/input-value/expected.js create mode 100644 test/js/samples/input-value/input.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index d796dc2ab3..ecddaaebaa 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -76,6 +76,8 @@ export default class AttributeWrapper { const is_select_value_attribute = name === 'value' && element.node.name === 'select'; + const is_input_value = name === 'value' && element.node.name === 'input'; + const should_cache = is_src || this.node.should_cache() || is_select_value_attribute; // TODO is this necessary? const last = should_cache && block.get_unique_name( @@ -147,6 +149,14 @@ export default class AttributeWrapper { : x`${condition} && (${last} !== (${last} = ${value}))`; } + if (is_input_value) { + const type = element.node.get_static_attribute_value('type'); + + if (type === null || type === "" || type === "text" || type === "email" || type === "password") { + condition = x`${condition} && ${element.var}.${property_name} !== ${should_cache ? last : value}`; + } + } + if (block.has_outros) { condition = x`!#current || ${condition}`; } diff --git a/test/js/samples/input-value/expected.js b/test/js/samples/input-value/expected.js new file mode 100644 index 0000000000..81753441e4 --- /dev/null +++ b/test/js/samples/input-value/expected.js @@ -0,0 +1,77 @@ +/* generated by Svelte vX.Y.Z */ +import { + SvelteComponent, + append, + detach, + element, + init, + insert, + listen, + noop, + safe_not_equal, + set_data, + space, + text +} from "svelte/internal"; + +function create_fragment(ctx) { + let input; + let t0; + let h1; + let t1; + let t2; + let dispose; + + return { + c() { + input = element("input"); + t0 = space(); + h1 = element("h1"); + t1 = text(/*name*/ ctx[0]); + t2 = text("!"); + input.value = /*name*/ ctx[0]; + dispose = listen(input, "input", /*onInput*/ ctx[1]); + }, + m(target, anchor) { + insert(target, input, anchor); + insert(target, t0, anchor); + insert(target, h1, anchor); + append(h1, t1); + append(h1, t2); + }, + p(ctx, [dirty]) { + if (dirty & /*name*/ 1 && input.value !== /*name*/ ctx[0]) { + input.value = /*name*/ ctx[0]; + } + + if (dirty & /*name*/ 1) set_data(t1, /*name*/ ctx[0]); + }, + i: noop, + o: noop, + d(detaching) { + if (detaching) detach(input); + if (detaching) detach(t0); + if (detaching) detach(h1); + dispose(); + } + }; +} + +function instance($$self, $$props, $$invalidate) { + let name = "change me"; + + function onInput(event) { + $$invalidate(0, name = event.target.value); + } + + return [name, onInput]; +} + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, instance, create_fragment, safe_not_equal, {}); + } +} + +export default Component; \ No newline at end of file diff --git a/test/js/samples/input-value/input.svelte b/test/js/samples/input-value/input.svelte new file mode 100644 index 0000000000..476458a195 --- /dev/null +++ b/test/js/samples/input-value/input.svelte @@ -0,0 +1,11 @@ + + + + +

{name}!

\ No newline at end of file From aa8e470b41da1ccdea315a2654100aa387708397 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sun, 29 Dec 2019 10:41:40 -0500 Subject: [PATCH 7/9] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eba4bbe3e3..af8c4a155d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Prevent text input cursor jumping in Safari with one-way binding ([#3449](https://github.com/sveltejs/svelte/issues/3449)) + ## 3.16.7 * Also apply actions in the order they're given along with other directives ([#2446](https://github.com/sveltejs/svelte/issues/2446), [#4156](https://github.com/sveltejs/svelte/pull/4156)) From dac64a363cdcdd2b1d691eee7050feb3d918d8e0 Mon Sep 17 00:00:00 2001 From: Anthony Le Goas Date: Sun, 29 Dec 2019 16:47:27 +0100 Subject: [PATCH 8/9] site: factor out Contributors component (#4177) --- .../routes/_components/Contributors.svelte | 26 +++++++++++++++++++ site/src/routes/index.svelte | 22 ++-------------- 2 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 site/src/routes/_components/Contributors.svelte diff --git a/site/src/routes/_components/Contributors.svelte b/site/src/routes/_components/Contributors.svelte new file mode 100644 index 0000000000..74eb3fa50f --- /dev/null +++ b/site/src/routes/_components/Contributors.svelte @@ -0,0 +1,26 @@ + + + + +{#each contributors as contributor, i} + + {contributor} + +{/each} diff --git a/site/src/routes/index.svelte b/site/src/routes/index.svelte index edf1e4d36c..73fbffbc6a 100644 --- a/site/src/routes/index.svelte +++ b/site/src/routes/index.svelte @@ -1,9 +1,9 @@