Merge branch 'master' into pr/5427

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

@ -2,6 +2,13 @@
## 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 `_` 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))

2
package-lock.json generated

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

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

@ -30,7 +30,9 @@ To treat `*.svelte` files as HTML, open *__Edit → Config...__* and add the fol
## 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
@ -50,13 +52,7 @@ To set the filetype for a single file, use a [modeline](https://vim.fandom.com/w
## Visual Studio Code
To treat `*.svelte` files as HTML, add the following lines to your `settings.json` file:
```cson
"files.associations": {
"*.svelte": "html"
}
```
We recommend using the official [Svelte for VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
## JetBrains WebStorm

@ -471,6 +471,7 @@ The following modifiers are available:
* `preventDefault` — calls `event.preventDefault()` before running the handler
* `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)
* `nonpassive` — explicitly set `passive: false`
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase
* `once` — remove the handler after the first time it runs
* `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.
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`:

@ -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.
* `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)
* `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))
* `once` — remove the handler after the first time it runs
* `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) {
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;
component.add_reference(info.name.split('.')[0]);
component.add_reference(object);
this.expression = info.expression
? new Expression(component, this, scope, info.expression)

@ -81,6 +81,7 @@ const valid_modifiers = new Set([
'capture',
'once',
'passive',
'nonpassive',
'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 => {
if (!valid_modifiers.has(modifier)) {
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
handler.modifiers.add('passive');
}

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

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

@ -45,11 +45,17 @@ export default class EventHandlerWrapper {
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) {
args.push((opts.length === 1 && opts[0] === 'capture')
? TRUE
: x`{ ${opts.map(opt => p`${opt}: true`)} }`);
if (opts.length === 1 && opts[0] === 'capture') {
args.push(TRUE);
} else {
args.push(x`{ ${ opts.map(opt =>
opt === 'nonpassive'
? p`passive: false`
: p`${opt}: true`
) } }`);
}
} else if (block.renderer.options.dev) {
args.push(FALSE);
}

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

@ -9,6 +9,7 @@
</script>
<div on:touchstart={handleTouchstart}>
<div on:touchstart|nonpassive={handleTouchstart}>touch me</div>
<button on:click|stopPropagation|preventDefault={handleClick}>click me</button>
<button on:click|once|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 {
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 v;
let w;
let x;
let y;
[u, v, w] = [
{id: eid = writable(foo = 2), name: 'xxx'},
5,
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>
<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",
"start": {
"line": 1,

Loading…
Cancel
Save