Merge branch 'master' into pr/5427

pull/5427/head
Conduitry 5 years ago
commit 403d21eca1

@ -2,6 +2,13 @@
## Unreleased ## Unreleased
* Add `|nonpassive` event modifier, explicitly passing `passive: false` ([#2068](https://github.com/sveltejs/svelte/issues/2068))
* Fix keyed `{#each}` not reacting to key changing ([#5444](https://github.com/sveltejs/svelte/issues/5444))
* Fix destructuring into store values ([#5449](https://github.com/sveltejs/svelte/issues/5449))
* Fix erroneous `missing-declaration` warning with `use:obj.method` ([#5451](https://github.com/sveltejs/svelte/issues/5451))
## 3.26.0
* Support `use:obj.method` as actions ([#3935](https://github.com/sveltejs/svelte/issues/3935)) * Support `use:obj.method` as actions ([#3935](https://github.com/sveltejs/svelte/issues/3935))
* Support `_` as numeric separator ([#5407](https://github.com/sveltejs/svelte/issues/5407)) * Support `_` as numeric separator ([#5407](https://github.com/sveltejs/svelte/issues/5407))
* Fix assignments to properties on store values ([#5412](https://github.com/sveltejs/svelte/issues/5412)) * Fix assignments to properties on store values ([#5412](https://github.com/sveltejs/svelte/issues/5412))

2
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.25.1", "version": "3.26.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.25.1", "version": "3.26.0",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"module": "index.mjs", "module": "index.mjs",
"main": "index", "main": "index",

@ -30,7 +30,9 @@ To treat `*.svelte` files as HTML, open *__Edit → Config...__* and add the fol
## Vim/Neovim ## Vim/Neovim
To treat all `*.svelte` files as HTML, add the following line to your `init.vim`: You can use the [coc-svelte extension](https://github.com/coc-extensions/coc-svelte) which utilises the official language-server.
As an alternative you can treat all `*.svelte` files as HTML. Add the following line to your `init.vim`:
``` ```
au! BufNewFile,BufRead *.svelte set ft=html au! BufNewFile,BufRead *.svelte set ft=html
@ -50,13 +52,7 @@ To set the filetype for a single file, use a [modeline](https://vim.fandom.com/w
## Visual Studio Code ## Visual Studio Code
To treat `*.svelte` files as HTML, add the following lines to your `settings.json` file: We recommend using the official [Svelte for VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
```cson
"files.associations": {
"*.svelte": "html"
}
```
## JetBrains WebStorm ## JetBrains WebStorm

@ -471,6 +471,7 @@ The following modifiers are available:
* `preventDefault` — calls `event.preventDefault()` before running the handler * `preventDefault` — calls `event.preventDefault()` before running the handler
* `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element * `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
* `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so) * `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
* `nonpassive` — explicitly set `passive: false`
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase
* `once` — remove the handler after the first time it runs * `once` — remove the handler after the first time it runs
* `self` — only trigger handler if event.target is the element itself * `self` — only trigger handler if event.target is the element itself

@ -13,7 +13,7 @@ First, you'll need to integrate Svelte with a build tool. There are officially m
Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process. Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process.
You'll also want to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting. [Read this guide to learn how](blog/setting-up-your-editor). You'll also want to configure your text editor. If you're using VS Code, install the [Svelte extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode), otherwise follow [this guide](blog/setting-up-your-editor) to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting.
Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`: Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`:

@ -21,6 +21,7 @@ The full list of modifiers:
* `preventDefault` — calls `event.preventDefault()` before running the handler. Useful for client-side form handling, for example. * `preventDefault` — calls `event.preventDefault()` before running the handler. Useful for client-side form handling, for example.
* `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element * `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
* `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so) * `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
* `nonpassive` — explicitly set `passive: false`
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase ([MDN docs](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture)) * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase ([MDN docs](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture))
* `once` — remove the handler after the first time it runs * `once` — remove the handler after the first time it runs
* `self` — only trigger handler if event.target is the element itself * `self` — only trigger handler if event.target is the element itself

@ -11,10 +11,11 @@ export default class Action extends Node {
constructor(component: Component, parent, scope, info) { constructor(component: Component, parent, scope, info) {
super(component, parent, scope, info); super(component, parent, scope, info);
component.warn_if_undefined(info.name, info, scope); const object = info.name.split('.')[0];
component.warn_if_undefined(object, info, scope);
this.name = info.name; this.name = info.name;
component.add_reference(info.name.split('.')[0]); component.add_reference(object);
this.expression = info.expression this.expression = info.expression
? new Expression(component, this, scope, info.expression) ? new Expression(component, this, scope, info.expression)

@ -81,6 +81,7 @@ const valid_modifiers = new Set([
'capture', 'capture',
'once', 'once',
'passive', 'passive',
'nonpassive',
'self' 'self'
]); ]);
@ -771,6 +772,13 @@ export default class Element extends Node {
}); });
} }
if (handler.modifiers.has('passive') && handler.modifiers.has('nonpassive')) {
component.error(handler, {
code: 'invalid-event-modifier',
message: `The 'passive' and 'nonpassive' modifiers cannot be used together`
});
}
handler.modifiers.forEach(modifier => { handler.modifiers.forEach(modifier => {
if (!valid_modifiers.has(modifier)) { if (!valid_modifiers.has(modifier)) {
component.error(handler, { component.error(handler, {
@ -805,7 +813,7 @@ export default class Element extends Node {
} }
}); });
if (passive_events.has(handler.name) && handler.can_make_passive && !handler.modifiers.has('preventDefault')) { if (passive_events.has(handler.name) && handler.can_make_passive && !handler.modifiers.has('preventDefault') && !handler.modifiers.has('nonpassive')) {
// touch/wheel events should be passive by default // touch/wheel events should be passive by default
handler.modifiers.add('passive'); handler.modifiers.add('passive');
} }

@ -36,38 +36,41 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names:
return renderer.invalidate(variable.name, undefined, main_execution_context); return renderer.invalidate(variable.name, undefined, main_execution_context);
} }
if (head) { if (!head) {
return node;
}
component.has_reactive_assignments = true; component.has_reactive_assignments = true;
if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) { if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) {
return get_invalidated(head, node); return get_invalidated(head, node);
} else { }
const is_store_value = head.name[0] === '$' && head.name[1] !== '$'; const is_store_value = head.name[0] === '$' && head.name[1] !== '$';
const extra_args = tail.map(variable => get_invalidated(variable)).filter(Boolean); const extra_args = tail.map(variable => get_invalidated(variable)).filter(Boolean);
if (is_store_value) {
return x`@set_store_value(${head.name.slice(1)}, ${node}, ${head.name}, ${extra_args})`;
}
let invalidate;
if (!main_execution_context) {
const pass_value = ( const pass_value = (
!main_execution_context &&
(
extra_args.length > 0 || extra_args.length > 0 ||
(node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') || (node.type === 'AssignmentExpression' && node.left.type !== 'Identifier') ||
(node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier')) (node.type === 'UpdateExpression' && (!node.prefix || node.argument.type !== 'Identifier'))
)
); );
if (pass_value) { if (pass_value) {
extra_args.unshift({ extra_args.unshift({
type: 'Identifier', type: 'Identifier',
name: head.name name: head.name
}); });
} }
invalidate = x`$$invalidate(${renderer.context_lookup.get(head.name).index}, ${node}, ${extra_args})`;
let invalidate = is_store_value } else {
? x`@set_store_value(${head.name.slice(1)}, ${node}, ${head.name})` // skip `$$invalidate` if it is in the main execution context
: !main_execution_context invalidate = extra_args.length ? [node, ...extra_args] : node;
? x`$$invalidate(${renderer.context_lookup.get(head.name).index}, ${node}, ${extra_args})` }
: extra_args.length
? [node, ...extra_args]
: node;
if (head.subscribable && head.reassigned) { if (head.subscribable && head.reassigned) {
const subscribe = `$$subscribe_${head.name}`; const subscribe = `$$subscribe_${head.name}`;
@ -76,7 +79,3 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names:
return invalidate; return invalidate;
} }
}
return node;
}

@ -239,6 +239,11 @@ export default class EachBlockWrapper extends Wrapper {
this.node.expression.dynamic_dependencies().forEach((dependency: string) => { this.node.expression.dynamic_dependencies().forEach((dependency: string) => {
all_dependencies.add(dependency); all_dependencies.add(dependency);
}); });
if (this.node.key) {
this.node.key.dynamic_dependencies().forEach((dependency: string) => {
all_dependencies.add(dependency);
});
}
this.dependencies = all_dependencies; this.dependencies = all_dependencies;
if (this.node.key) { if (this.node.key) {

@ -45,11 +45,17 @@ export default class EventHandlerWrapper {
const args = []; const args = [];
const opts = ['passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod)); const opts = ['nonpassive', 'passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod));
if (opts.length) { if (opts.length) {
args.push((opts.length === 1 && opts[0] === 'capture') if (opts.length === 1 && opts[0] === 'capture') {
? TRUE args.push(TRUE);
: x`{ ${opts.map(opt => p`${opt}: true`)} }`); } else {
args.push(x`{ ${ opts.map(opt =>
opt === 'nonpassive'
? p`passive: false`
: p`${opt}: true`
) } }`);
}
} else if (block.renderer.options.dev) { } else if (block.renderer.options.dev) {
args.push(FALSE); args.push(FALSE);
} }

@ -16,41 +16,49 @@ import {
} from "svelte/internal"; } from "svelte/internal";
function create_fragment(ctx) { function create_fragment(ctx) {
let div; let div1;
let button0; let div0;
let t1; let t1;
let button1; let button0;
let t3; let t3;
let button1;
let t5;
let button2; let button2;
let mounted; let mounted;
let dispose; let dispose;
return { return {
c() { c() {
div = element("div"); div1 = element("div");
div0 = element("div");
div0.textContent = "touch me";
t1 = space();
button0 = element("button"); button0 = element("button");
button0.textContent = "click me"; button0.textContent = "click me";
t1 = space(); t3 = space();
button1 = element("button"); button1 = element("button");
button1.textContent = "or me"; button1.textContent = "or me";
t3 = space(); t5 = space();
button2 = element("button"); button2 = element("button");
button2.textContent = "or me!"; button2.textContent = "or me!";
}, },
m(target, anchor) { m(target, anchor) {
insert(target, div, anchor); insert(target, div1, anchor);
append(div, button0); append(div1, div0);
append(div, t1); append(div1, t1);
append(div, button1); append(div1, button0);
append(div, t3); append(div1, t3);
append(div, button2); append(div1, button1);
append(div1, t5);
append(div1, button2);
if (!mounted) { if (!mounted) {
dispose = [ dispose = [
listen(div0, "touchstart", handleTouchstart, { passive: false }),
listen(button0, "click", stop_propagation(prevent_default(handleClick))), listen(button0, "click", stop_propagation(prevent_default(handleClick))),
listen(button1, "click", handleClick, { once: true, capture: true }), listen(button1, "click", handleClick, { once: true, capture: true }),
listen(button2, "click", handleClick, true), listen(button2, "click", handleClick, true),
listen(div, "touchstart", handleTouchstart, { passive: true }) listen(div1, "touchstart", handleTouchstart, { passive: true })
]; ];
mounted = true; mounted = true;
@ -60,7 +68,7 @@ function create_fragment(ctx) {
i: noop, i: noop,
o: noop, o: noop,
d(detaching) { d(detaching) {
if (detaching) detach(div); if (detaching) detach(div1);
mounted = false; mounted = false;
run_all(dispose); run_all(dispose);
} }

@ -9,6 +9,7 @@
</script> </script>
<div on:touchstart={handleTouchstart}> <div on:touchstart={handleTouchstart}>
<div on:touchstart|nonpassive={handleTouchstart}>touch me</div>
<button on:click|stopPropagation|preventDefault={handleClick}>click me</button> <button on:click|stopPropagation|preventDefault={handleClick}>click me</button>
<button on:click|once|capture={handleClick}>or me</button> <button on:click|once|capture={handleClick}>or me</button>
<button on:click|capture={handleClick}>or me!</button> <button on:click|capture={handleClick}>or me!</button>

@ -0,0 +1,27 @@
let count = 0;
let value = 'foo';
export default {
props: {
value() {
count++;
return value;
}
},
html: `
<div>foo</div>
<div>foo</div>
`,
test({ assert, component, target }) {
value = 'bar';
component.id = 1;
assert.equal(count, 4);
assert.htmlEqual(target.innerHTML, `
<div>bar</div>
<div>bar</div>
`);
}
};

@ -0,0 +1,8 @@
<script>
export let id = 0;
export let value;
</script>
{#each ['foo', 'bar'] as key (id + key)}
<div>{value()}</div>
{/each}

@ -0,0 +1,9 @@
// destructure to store value
export default {
skip_if_ssr: true, // pending https://github.com/sveltejs/svelte/issues/3582
html: `<h1>2 2 xxx 5 6 9 10 2</h1>`,
async test({ assert, target, component }) {
await component.update();
assert.htmlEqual(target.innerHTML, `<h1>11 11 yyy 12 13 14 15 11</h1>`);
}
};

@ -0,0 +1,29 @@
<script>
import { writable } from 'svelte/store';
let eid = writable(1);
let foo;
const u = writable(2);
const v = writable(3);
const w = writable(4);
const x = writable(5);
const y = writable(6);
[$u, $v, $w] = [
{id: eid = writable(foo = 2), name: 'xxx'},
5,
6
];
({ a: $x, b: $y } = { a: 9, b: 10 });
$: z = $u.id;
export function update() {
[$u, $v, $w] = [
{id: eid = writable(foo = 11), name: 'yyy'},
12,
13
];
({ a: $x, b: $y } = { a: 14, b: 15 });
}
</script>
<h1>{foo} {$eid} {$u.name} {$v} {$w} {$x} {$y} {$z}</h1>

@ -1,3 +1,9 @@
// destructure to store
export default { export default {
html: `<h1>2 2 xxx 5 6</h1>` html: `<h1>2 2 xxx 5 6 9 10 2</h1>`,
skip_if_ssr: true,
async test({ assert, target, component }) {
await component.update();
assert.htmlEqual(target.innerHTML, `<h1>11 11 yyy 12 13 14 15 11</h1>`);
}
}; };

@ -6,11 +6,24 @@
let u; let u;
let v; let v;
let w; let w;
let x;
let y;
[u, v, w] = [ [u, v, w] = [
{id: eid = writable(foo = 2), name: 'xxx'}, {id: eid = writable(foo = 2), name: 'xxx'},
5, 5,
writable(6) writable(6)
]; ];
({ a: x, b: y } = { a: writable(9), b: writable(10) });
$: z = u.id;
export function update() {
[u, v, w] = [
{id: eid = writable(foo = 11), name: 'yyy'},
12,
writable(13)
];
({ a: x, b: y } = { a: writable(14), b: writable(15) });
}
</script> </script>
<h1>{foo} {$eid} {u.name} {v} {$w}</h1> <h1>{foo} {$eid} {u.name} {v} {$w} {$x} {$y} {$z}</h1>

@ -0,0 +1,11 @@
<script>
const obj = {
foo : "bar",
action(element, { leet }) {
element.foo = this.foo + leet;
},
}
</script>
<button use:obj.action={{ leet: 1337 }}>action</button>
<button use:foo.action={{ leet: 1337 }}>action</button>

@ -0,0 +1,17 @@
[
{
"code": "missing-declaration",
"end": {
"character": 217,
"column": 39,
"line": 11
},
"message": "'foo' is not defined",
"pos": 186,
"start": {
"character": 186,
"column": 8,
"line": 11
}
}
]

@ -0,0 +1,15 @@
[{
"message": "The 'passive' and 'nonpassive' modifiers cannot be used together",
"code": "invalid-event-modifier",
"start": {
"line": 1,
"column": 5,
"character": 5
},
"end": {
"line": 1,
"column": 51,
"character": 51
},
"pos": 5
}]

@ -0,0 +1,3 @@
<div on:touchstart|nonpassive|passive={handleWheel}>
oops
</div>

@ -1,5 +1,5 @@
[{ [{
"message": "Valid event modifiers are preventDefault, stopPropagation, capture, once, passive or self", "message": "Valid event modifiers are preventDefault, stopPropagation, capture, once, passive, nonpassive or self",
"code": "invalid-event-modifier", "code": "invalid-event-modifier",
"start": { "start": {
"line": 1, "line": 1,

Loading…
Cancel
Save