Merge branch 'master' into pr/5261

pull/5261/head
Conduitry 5 years ago
commit 5238e27389

@ -1,5 +1,12 @@
# Svelte changelog # Svelte changelog
## Unreleased
* Add types to `createEventDispatcher` ([#5211](https://github.com/sveltejs/svelte/issues/5211))
* In SSR mode, do not automatically declare variables for reactive assignments to member expressions ([#5247](https://github.com/sveltejs/svelte/issues/5247))
* Include selector in message of `unused-css-selector` warning ([#5252](https://github.com/sveltejs/svelte/issues/5252))
* Fix using `<Namespaced.Component/>`s in child `{#await}`/`{#each}` contexts ([#5255](https://github.com/sveltejs/svelte/issues/5255))
## 3.24.1 ## 3.24.1
* Prevent duplicate invalidation with certain two-way component bindings ([#3180](https://github.com/sveltejs/svelte/issues/3180), [#5117](https://github.com/sveltejs/svelte/issues/5117), [#5144](https://github.com/sveltejs/svelte/issues/5144)) * Prevent duplicate invalidation with certain two-way component bindings ([#3180](https://github.com/sveltejs/svelte/issues/3180), [#5117](https://github.com/sveltejs/svelte/issues/5117), [#5144](https://github.com/sveltejs/svelte/issues/5144))

@ -544,6 +544,21 @@ Numeric input values are coerced; even though `input.value` is a string as far a
<input type="range" bind:value={num}> <input type="range" bind:value={num}>
``` ```
---
On `<input>` elements with `type="file"`, you can use `bind:files` to get the [`FileList` of selected files](https://developer.mozilla.org/en-US/docs/Web/API/FileList).
```sv
<label for="avatar">Upload a picture:</label>
<input
accept="image/png, image/jpeg"
bind:files
id="avatar"
name="avatar"
type="file"
/>
```
##### Binding `<select>` value ##### Binding `<select>` value

@ -0,0 +1,37 @@
<script>
let files;
$: if (files) {
// Note that `files` is of type `FileList`, not an Array:
// https://developer.mozilla.org/en-US/docs/Web/API/FileList
console.log(files);
for (const file of files) {
console.log(`${file.name}: ${file.size} bytes`);
}
}
</script>
<label for="avatar">Upload a picture:</label>
<input
accept="image/png, image/jpeg"
bind:files
id="avatar"
name="avatar"
type="file"
/>
<label for="many">Upload multiple files of any type:</label>
<input
bind:files
id="many"
multiple
type="file"
/>
{#if files}
<h2>Selected files:</h2>
{#each Array.from(files) as file}
<p>{file.name} ({file.size} bytes) </p>
{/each}
{/if}

@ -2,7 +2,11 @@
question: Are there any video courses? question: Are there any video courses?
--- ---
There are no official ones, but here are a couple of third-part ones that we know of. Rich Harris, the creator of Svelte, taught a course:
- [Frontend Masters](https://frontendmasters.com/courses/svelte/)
There are also a couple of third-party courses:
- [Egghead](https://egghead.io/playlists/getting-started-with-svelte-3-05a8541a) - [Egghead](https://egghead.io/playlists/getting-started-with-svelte-3-05a8541a)
- [Udemy](https://www.udemy.com/sveltejs-the-complete-guide/) - [Udemy](https://www.udemy.com/sveltejs-the-complete-guide/)

@ -1573,9 +1573,9 @@
} }
}, },
"clipboard": { "clipboard": {
"version": "2.0.4", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
"integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
"optional": true, "optional": true,
"requires": { "requires": {
"good-listener": "^1.2.2", "good-listener": "^1.2.2",
@ -3170,9 +3170,9 @@
"integrity": "sha512-plS7uY0WWiTBwWZs9KM3M88ZxHWKbrbMUDf52CPum6BqAxiLmKROmaTnmhXtv0krQ0l0HRLcFS8JDwOFyPt/OQ==" "integrity": "sha512-plS7uY0WWiTBwWZs9KM3M88ZxHWKbrbMUDf52CPum6BqAxiLmKROmaTnmhXtv0krQ0l0HRLcFS8JDwOFyPt/OQ=="
}, },
"prismjs": { "prismjs": {
"version": "1.17.1", "version": "1.21.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.21.0.tgz",
"integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", "integrity": "sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw==",
"requires": { "requires": {
"clipboard": "^2.0.0" "clipboard": "^2.0.0"
} }

@ -25,7 +25,7 @@
"pg": "^7.12.1", "pg": "^7.12.1",
"polka": "^1.0.0-next.9", "polka": "^1.0.0-next.9",
"prism-svelte": "^0.4.3", "prism-svelte": "^0.4.3",
"prismjs": "^1.17.1", "prismjs": "^1.21.0",
"sirv": "^1.0.0-next.2", "sirv": "^1.0.0-next.2",
"yootils": "0.0.16" "yootils": "0.0.16"
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -435,7 +435,7 @@ export default class Stylesheet {
child.warn_on_unused_selector((selector: Selector) => { child.warn_on_unused_selector((selector: Selector) => {
component.warn(selector.node, { component.warn(selector.node, {
code: `css-unused-selector`, code: `css-unused-selector`,
message: `Unused CSS selector` message: `Unused CSS selector "${this.source.slice(selector.node.start, selector.node.end)}"`
}); });
}); });
}); });

@ -17,6 +17,7 @@ import { Node, Identifier, ObjectExpression } from 'estree';
import EventHandler from '../Element/EventHandler'; import EventHandler from '../Element/EventHandler';
import { extract_names } from 'periscopic'; import { extract_names } from 'periscopic';
import mark_each_block_bindings from '../shared/mark_each_block_bindings'; import mark_each_block_bindings from '../shared/mark_each_block_bindings';
import { string_to_member_expression } from '../../../utils/string_to_member_expression';
export default class InlineComponentWrapper extends Wrapper { export default class InlineComponentWrapper extends Wrapper {
var: Identifier; var: Identifier;
@ -484,7 +485,7 @@ export default class InlineComponentWrapper extends Wrapper {
} else { } else {
const expression = this.node.name === 'svelte:self' const expression = this.node.name === 'svelte:self'
? component.name ? component.name
: this.renderer.reference(this.node.name); : this.renderer.reference(string_to_member_expression(this.node.name));
block.chunks.init.push(b` block.chunks.init.push(b`
${(this.node.attributes.length > 0 || this.node.bindings.length > 0) && b` ${(this.node.attributes.length > 0 || this.node.bindings.length > 0) && b`

@ -5,8 +5,7 @@ import { string_literal } from '../utils/stringify';
import Renderer from './Renderer'; import Renderer from './Renderer';
import { INode as TemplateNode } from '../nodes/interfaces'; // TODO import { INode as TemplateNode } from '../nodes/interfaces'; // TODO
import Text from '../nodes/Text'; import Text from '../nodes/Text';
import { extract_names } from '../utils/scope'; import { LabeledStatement, Statement, Node } from 'estree';
import { LabeledStatement, Statement, ExpressionStatement, AssignmentExpression, Node } from 'estree';
export default function ssr( export default function ssr(
component: Component, component: Component,
@ -72,37 +71,17 @@ export default function ssr(
}) })
: []; : [];
const injected = Array.from(component.injected_reactive_declaration_vars).filter(name => {
const variable = component.var_lookup.get(name);
return variable.injected;
});
const reactive_declarations = component.reactive_declarations.map(d => { const reactive_declarations = component.reactive_declarations.map(d => {
const body: Statement = (d.node as LabeledStatement).body; const body: Statement = (d.node as LabeledStatement).body;
let statement = b`${body}`; let statement = b`${body}`;
if (d.declaration) { if (!d.declaration) { // TODO do not add label if it's not referenced
const declared = extract_names(d.declaration);
const injected = declared.filter(name => {
return name[0] !== '$' && component.var_lookup.get(name).injected;
});
const self_dependencies = injected.filter(name => d.dependencies.has(name));
if (injected.length) {
// in some cases we need to do `let foo; [expression]`, in
// others we can do `let [expression]`
const separate = (
self_dependencies.length > 0 ||
declared.length > injected.length
);
const { left, right } = (body as ExpressionStatement).expression as AssignmentExpression;
statement = separate
? b`
${injected.map(name => b`let ${name};`)}
${statement}`
: b`
let ${left} = ${right}`;
}
} else { // TODO do not add label if it's not referenced
statement = b`$: { ${statement} }`; statement = b`$: { ${statement} }`;
} }
@ -119,6 +98,8 @@ export default function ssr(
${reactive_store_values} ${reactive_store_values}
${injected.map(name => b`let ${name};`)}
${reactive_declarations} ${reactive_declarations}
$$rendered = ${literal}; $$rendered = ${literal};
@ -129,6 +110,8 @@ export default function ssr(
: b` : b`
${reactive_store_values} ${reactive_store_values}
${injected.map(name => b`let ${name};`)}
${reactive_declarations} ${reactive_declarations}
return ${literal};`; return ${literal};`;

@ -0,0 +1,17 @@
import { MemberExpression, Identifier } from "estree";
export function string_to_member_expression(name: string) {
const parts = name.split(".");
let node: MemberExpression | Identifier = {
type: "Identifier",
name: parts[0],
};
for (let i = 1; i < parts.length; i++) {
node = {
type: "MemberExpression",
object: node,
property: { type: "Identifier", name: parts[i] },
} as MemberExpression;
}
return node;
}

@ -27,7 +27,9 @@ export function onDestroy(fn) {
get_current_component().$$.on_destroy.push(fn); get_current_component().$$.on_destroy.push(fn);
} }
export function createEventDispatcher() { export function createEventDispatcher<
EventMap extends {} = any
>(): <EventKey extends Extract<keyof EventMap, string>>(type: EventKey, detail?: EventMap[EventKey]) => void {
const component = get_current_component(); const component = get_current_component();
return (type: string, detail?: any) => { return (type: string, detail?: any) => {
@ -61,4 +63,4 @@ export function bubble(component, event) {
if (callbacks) { if (callbacks) {
callbacks.slice().forEach(fn => fn(event)); callbacks.slice().forEach(fn => fn(event));
} }
} }

@ -2,7 +2,7 @@ export default {
warnings: [{ warnings: [{
filename: "SvelteComponent.svelte", filename: "SvelteComponent.svelte",
code: `css-unused-selector`, code: `css-unused-selector`,
message: "Unused CSS selector", message: 'Unused CSS selector ".x"',
start: { start: {
line: 4, line: 4,
column: 1, column: 1,

@ -13,7 +13,7 @@ export default {
3: color: red; 3: color: red;
4: } 4: }
`, `,
message: 'Unused CSS selector', message: 'Unused CSS selector ":global(.foo) .bar"',
pos: 9, pos: 9,
start: { start: {
character: 9, character: 9,

@ -1,7 +1,7 @@
export default { export default {
warnings: [{ warnings: [{
code: `css-unused-selector`, code: `css-unused-selector`,
message: 'Unused CSS selector', message: 'Unused CSS selector "div > p"',
start: { start: {
line: 8, line: 8,
column: 1, column: 1,

@ -3,7 +3,7 @@ export default {
{ {
filename: "SvelteComponent.svelte", filename: "SvelteComponent.svelte",
code: `css-unused-selector`, code: `css-unused-selector`,
message: "Unused CSS selector", message: 'Unused CSS selector ".foo"',
start: { start: {
line: 4, line: 4,
column: 1, column: 1,
@ -27,7 +27,7 @@ export default {
{ {
filename: "SvelteComponent.svelte", filename: "SvelteComponent.svelte",
code: `css-unused-selector`, code: `css-unused-selector`,
message: "Unused CSS selector", message: 'Unused CSS selector ".baz"',
start: { start: {
line: 4, line: 4,
column: 13, column: 13,

@ -2,7 +2,7 @@ export default {
warnings: [ warnings: [
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".fooaa"',
frame: frame:
` 9: <style> ` 9: <style>
10: .foo {color: red;} 10: .foo {color: red;}
@ -16,7 +16,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".foobb"',
frame: frame:
`10: .foo {color: red;} `10: .foo {color: red;}
11: .fooaa {color: red;} 11: .fooaa {color: red;}
@ -30,7 +30,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".foodd"',
frame: frame:
`12: .foobb {color: red;} `12: .foobb {color: red;}
13: .foocc {color: red;} 13: .foocc {color: red;}
@ -44,7 +44,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".bbbar"',
frame: frame:
`18: .dd {color: red;} `18: .dd {color: red;}
19: .aabar {color: red;} 19: .aabar {color: red;}
@ -58,7 +58,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".ccbar"',
frame: frame:
`19: .aabar {color: red;} `19: .aabar {color: red;}
20: .bbbar {color: red;} 20: .bbbar {color: red;}
@ -72,7 +72,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".ddbar"',
frame: frame:
`20: .bbbar {color: red;} `20: .bbbar {color: red;}
21: .ccbar {color: red;} 21: .ccbar {color: red;}
@ -86,7 +86,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".fooaabar"',
frame: frame:
`21: .ccbar {color: red;} `21: .ccbar {color: red;}
22: .ddbar {color: red;} 22: .ddbar {color: red;}
@ -100,7 +100,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".foobbbar"',
frame: frame:
`22: .ddbar {color: red;} `22: .ddbar {color: red;}
23: .fooaabar {color: red;} 23: .fooaabar {color: red;}
@ -114,7 +114,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".fooccbar"',
frame: frame:
`23: .fooaabar {color: red;} `23: .fooaabar {color: red;}
24: .foobbbar {color: red;} 24: .foobbbar {color: red;}
@ -128,7 +128,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".unused"',
frame: frame:
`26: .fooddbar {color: red;} `26: .fooddbar {color: red;}
27: .baz {color: red;} 27: .baz {color: red;}

@ -13,7 +13,7 @@ export default {
14: .unused {color: blue;} 14: .unused {color: blue;}
^ ^
15: </style>`, 15: </style>`,
message: 'Unused CSS selector', message: 'Unused CSS selector ".unused"',
pos: 198, pos: 198,
start: { start: {
character: 198, character: 198,

@ -2,7 +2,7 @@ export default {
warnings: [ warnings: [
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".hover.unused"',
frame: ` frame: `
13: .thing.active {color: blue;} 13: .thing.active {color: blue;}
14: .hover { color: blue; } 14: .hover { color: blue; }
@ -16,7 +16,7 @@ export default {
}, },
{ {
code: 'css-unused-selector', code: 'css-unused-selector',
message: 'Unused CSS selector', message: 'Unused CSS selector ".unused"',
frame: ` frame: `
15: .hover.unused { color: blue; } 15: .hover.unused { color: blue; }
16: 16:

@ -6,7 +6,7 @@ export default {
warnings: [{ warnings: [{
filename: "SvelteComponent.svelte", filename: "SvelteComponent.svelte",
code: `css-unused-selector`, code: `css-unused-selector`,
message: "Unused CSS selector", message: 'Unused CSS selector ".maybeactive"',
start: { start: {
line: 16, line: 16,
column: 1, column: 1,

@ -2,7 +2,7 @@ export default {
warnings: [{ warnings: [{
filename: "SvelteComponent.svelte", filename: "SvelteComponent.svelte",
code: `css-unused-selector`, code: `css-unused-selector`,
message: "Unused CSS selector", message: 'Unused CSS selector ".bar"',
start: { start: {
line: 8, line: 8,
column: 1, column: 1,

@ -0,0 +1,5 @@
<script context="module">
import Tooltip from './Tooltip.svelte';
export const Widget = { Tooltip };
</script>

@ -0,0 +1,3 @@
export default {
html: '<p>i am a widget</p>'
};

@ -0,0 +1,8 @@
<script>
import { Widget } from './Widget.svelte';
let widgets = [Widget];
</script>
{#each widgets as LazyWidget}
<LazyWidget.Tooltip />
{/each}

@ -0,0 +1,5 @@
export default {
html: `
<h1>Hello world!</h1>
`,
};

@ -0,0 +1,6 @@
<script>
$: user = {};
$: user.name = 'world';
</script>
<h1>Hello {user.name}!</h1>
Loading…
Cancel
Save