Merge branch 'master' into pr/5929

pull/5929/head
Conduitry 6 years ago
commit b6024d4448

@ -1,5 +1,12 @@
# Svelte changelog
## Unreleased
* Throw a parser error for `class:` directives with an empty class name ([#5858](https://github.com/sveltejs/svelte/issues/5858))
* Fix type inference for derived stores ([#5935](https://github.com/sveltejs/svelte/pull/5935))
* Make parameters of built-in animations and transitions optional ([#5936](https://github.com/sveltejs/svelte/pull/5936))
* Make `SvelteComponentDev` typings more forgiving ([#5937](https://github.com/sveltejs/svelte/pull/5937))
## 3.32.0
* Allow multiple instances of the same action on an element ([#5516](https://github.com/sveltejs/svelte/issues/5516))

@ -1292,19 +1292,19 @@ Components can have child content, in the same way that elements can.
The content is exposed in the child component using the `<slot>` element, which can contain fallback content that is rendered if no children are provided.
```sv
<!-- App.svelte -->
<Widget></Widget>
<Widget>
<p>this is some child content that will overwrite the default slot content</p>
</Widget>
<!-- Widget.svelte -->
<div>
<slot>
this fallback content will be rendered when no content is provided, like in the first example
</slot>
</div>
<!-- App.svelte -->
<Widget></Widget> <!-- this component will render the default content -->
<Widget>
<p>this is some child content that will overwrite the default slot content</p>
</Widget>
```
#### [`<slot name="`*name*`">`](slot_name)
@ -1314,18 +1314,18 @@ The content is exposed in the child component using the `<slot>` element, which
Named slots allow consumers to target specific areas. They can also have fallback content.
```sv
<!-- App.svelte -->
<Widget>
<h1 slot="header">Hello</h1>
<p slot="footer">Copyright (c) 2019 Svelte Industries</p>
</Widget>
<!-- Widget.svelte -->
<div>
<slot name="header">No header was provided</slot>
<p>Some content between header and footer</p>
<slot name="footer"></slot>
</div>
<!-- App.svelte -->
<Widget>
<h1 slot="header">Hello</h1>
<p slot="footer">Copyright (c) 2019 Svelte Industries</p>
</Widget>
```
#### [`$$slots`](slots_object)
@ -1337,20 +1337,21 @@ Named slots allow consumers to target specific areas. They can also have fallbac
Note that explicitly passing in an empty named slot will add that slot's name to `$$slots`. For example, if a parent passes `<div slot="title" />` to a child component, `$$slots.title` will be truthy within the child.
```sv
<!-- App.svelte -->
<Card>
<h1 slot="title">Blog Post Title</h1>
</Card>
<!-- Card.svelte -->
<div>
<slot name="title"></slot>
{#if $$slots.description}
<!-- This slot and the <hr> before it will not render. -->
<!-- This <hr> and slot will render only if a slot named "description" is provided. -->
<hr>
<slot name="description"></slot>
{/if}
</div>
<!-- App.svelte -->
<Card>
<h1 slot="title">Blog Post Title</h1>
<!-- No slot named "description" was provided so the optional slot will not be rendered. -->
</Card>
```
#### [`<slot let:`*name*`={`*value*`}>`](slot_let)
@ -1362,11 +1363,6 @@ Slots can be rendered zero or more times, and can pass values *back* to the pare
The usual shorthand rules apply — `let:item` is equivalent to `let:item={item}`, and `<slot {item}>` is equivalent to `<slot item={item}>`.
```sv
<!-- App.svelte -->
<FancyList {items} let:prop={thing}>
<div>{thing.text}</div>
</FancyList>
<!-- FancyList.svelte -->
<ul>
{#each items as item}
@ -1375,6 +1371,11 @@ The usual shorthand rules apply — `let:item` is equivalent to `let:item={item}
</li>
{/each}
</ul>
<!-- App.svelte -->
<FancyList {items} let:prop={thing}>
<div>{thing.text}</div>
</FancyList>
```
---
@ -1382,12 +1383,6 @@ The usual shorthand rules apply — `let:item` is equivalent to `let:item={item}
Named slots can also expose values. The `let:` directive goes on the element with the `slot` attribute.
```sv
<!-- App.svelte -->
<FancyList {items}>
<div slot="item" let:item>{item.text}</div>
<p slot="footer">Copyright (c) 2019 Svelte Industries</p>
</FancyList>
<!-- FancyList.svelte -->
<ul>
{#each items as item}
@ -1398,6 +1393,12 @@ Named slots can also expose values. The `let:` directive goes on the element wit
</ul>
<slot name="footer"></slot>
<!-- App.svelte -->
<FancyList {items}>
<div slot="item" let:item>{item.text}</div>
<p slot="footer">Copyright (c) 2019 Svelte Industries</p>
</FancyList>
```

@ -485,7 +485,7 @@ export default function dom(
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot, props: ${init_props} }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
@init(this, { target: this.shadowRoot, props: ${init_props} }, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
${dev_props_check}
@ -537,7 +537,7 @@ export default function dom(
constructor(options) {
super(${options.dev && 'options'});
${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`}
@init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
@init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment' : 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`}
${dev_props_check}

@ -321,7 +321,7 @@ export default class ElementWrapper extends Wrapper {
literal.quasis.push(state.quasi);
block.chunks.create.push(
b`${node}.${this.can_use_innerhtml ? 'innerHTML': 'textContent'} = ${literal};`
b`${node}.${this.can_use_innerhtml ? 'innerHTML' : 'textContent'} = ${literal};`
);
}
} else {

@ -173,7 +173,7 @@ export default class SlotWrapper extends Wrapper {
if (${slot}.p && ${renderer.dirty(dynamic_dependencies)}) {
@update_slot_spread(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_spread_changes_fn}, ${get_slot_context_fn});
}
`: b`
` : b`
if (${slot}.p && ${renderer.dirty(dynamic_dependencies)}) {
@update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_context_fn});
}

@ -196,7 +196,7 @@ export default function mustache(parser: Parser) {
if (!parser.eat('}')) {
parser.require_whitespace();
await_block[is_then ? 'value': 'error'] = read_context(parser);
await_block[is_then ? 'value' : 'error'] = read_context(parser);
parser.allow_whitespace();
parser.eat('}', true);
}
@ -204,7 +204,7 @@ export default function mustache(parser: Parser) {
const new_block: TemplateNode = {
start,
end: null,
type: is_then ? 'ThenBlock': 'CatchBlock',
type: is_then ? 'ThenBlock' : 'CatchBlock',
children: [],
skip: false
};

@ -387,6 +387,13 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
}, start);
}
if (type === 'Class' && directive_name === '') {
parser.error({
code: 'invalid-class-directive',
message: 'Class binding name cannot be empty'
}, start + colon_index + 1);
}
if (value[0]) {
if ((value as any[]).length > 1 || value[0].type === 'Text') {
parser.error({

@ -16,7 +16,7 @@ interface FlipParams {
easing?: (t: number) => number;
}
export function flip(node: Element, animation: { from: DOMRect; to: DOMRect }, params: FlipParams): AnimationConfig {
export function flip(node: Element, animation: { from: DOMRect; to: DOMRect }, params: FlipParams = {}): AnimationConfig {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
const scaleX = animation.from.width / node.clientWidth;

@ -115,6 +115,20 @@ export class SvelteComponentDev extends SvelteComponent {
* ### DO NOT USE!
*/
$$prop_def: Props;
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$events_def: any;
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$slot_def: any;
constructor(options: {
target: Element;

@ -14,7 +14,7 @@ function tick_spring<T>(ctx: TickContext<T>, last_value: T, current_value: T, ta
// @ts-ignore
const delta = target_value - current_value;
// @ts-ignore
const velocity = (current_value - last_value) / (ctx.dt||1/60); // guard div by 0
const velocity = (current_value - last_value) / (ctx.dt || 1 / 60); // guard div by 0
const spring = ctx.opts.stiffness * delta;
const damper = ctx.opts.damping * velocity;
const acceleration = (spring - damper) * ctx.inv_mass;
@ -80,7 +80,7 @@ export function spring<T=any>(value?: T, opts: SpringOpts = {}): Spring<T> {
let inv_mass_recovery_rate = 0;
let cancel_task = false;
function set(new_value: T, opts: SpringUpdateOpts={}): Promise<void> {
function set(new_value: T, opts: SpringUpdateOpts = {}): Promise<void> {
target_value = new_value;
const token = current_token = {};

@ -125,10 +125,12 @@ type StoresValues<T> = T extends Readable<infer U> ? U :
*
* @param stores - input stores
* @param fn - function callback that aggregates the values
* @param initial_value - when used asynchronously
*/
export function derived<S extends Stores, T>(
stores: S,
fn: (values: StoresValues<S>) => T
fn: (values: StoresValues<S>, set: (value: T) => void) => Unsubscriber | void,
initial_value?: T
): Readable<T>;
/**
@ -137,12 +139,10 @@ export function derived<S extends Stores, T>(
*
* @param stores - input stores
* @param fn - function callback that aggregates the values
* @param initial_value - when used asynchronously
*/
export function derived<S extends Stores, T>(
stores: S,
fn: (values: StoresValues<S>, set: (value: T) => void) => Unsubscriber | void,
initial_value?: T
fn: (values: StoresValues<S>) => T
): Readable<T>;
export function derived<T>(stores: Stores, fn: Function, initial_value?: T): Readable<T> {

@ -25,7 +25,7 @@ export function blur(node: Element, {
easing = cubicInOut,
amount = 5,
opacity = 0
}: BlurParams): TransitionConfig {
}: BlurParams = {}): TransitionConfig {
const style = getComputedStyle(node);
const target_opacity = +style.opacity;
const f = style.filter === 'none' ? '' : style.filter;
@ -50,7 +50,7 @@ export function fade(node: Element, {
delay = 0,
duration = 400,
easing = linear
}: FadeParams): TransitionConfig {
}: FadeParams = {}): TransitionConfig {
const o = +getComputedStyle(node).opacity;
return {
@ -77,7 +77,7 @@ export function fly(node: Element, {
x = 0,
y = 0,
opacity = 0
}: FlyParams): TransitionConfig {
}: FlyParams = {}): TransitionConfig {
const style = getComputedStyle(node);
const target_opacity = +style.opacity;
const transform = style.transform === 'none' ? '' : style.transform;
@ -104,7 +104,7 @@ export function slide(node: Element, {
delay = 0,
duration = 400,
easing = cubicOut
}: SlideParams): TransitionConfig {
}: SlideParams = {}): TransitionConfig {
const style = getComputedStyle(node);
const opacity = +style.opacity;
const height = parseFloat(style.height);
@ -146,7 +146,7 @@ export function scale(node: Element, {
easing = cubicOut,
start = 0,
opacity = 0
}: ScaleParams): TransitionConfig {
}: ScaleParams = {}): TransitionConfig {
const style = getComputedStyle(node);
const target_opacity = +style.opacity;
const transform = style.transform === 'none' ? '' : style.transform;
@ -177,7 +177,7 @@ export function draw(node: SVGElement & { getTotalLength(): number }, {
speed,
duration,
easing = cubicInOut
}: DrawParams): TransitionConfig {
}: DrawParams = {}): TransitionConfig {
const len = node.getTotalLength();
if (duration === undefined) {
@ -237,7 +237,7 @@ export function crossfade({ fallback, ...defaults }: CrossfadeParams & {
css: (t, u) => `
opacity: ${t * opacity};
transform-origin: top left;
transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${t + (1-t) * dw}, ${t + (1-t) * dh});
transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${t + (1 - t) * dw}, ${t + (1 - t) * dh});
`
};
}

@ -0,0 +1,10 @@
{
"code": "invalid-class-directive",
"message": "Class binding name cannot be empty",
"start": {
"line": 1,
"column": 10,
"character": 10
},
"pos": 10
}

@ -33,6 +33,10 @@ describe('ssr', () => {
return setupHtmlEqual();
});
let saved_window;
before(() => saved_window = global.window);
after(() => global.window = saved_window);
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
if (dir[0] === '.') return;

@ -2,16 +2,6 @@ import MagicString from 'magic-string';
let indent_size = 4;
let comment_multi = true;
// TODO
// Using magic-string's own .toUrl() method results in mysterious runtime failures.
// If tests are being run in `PUBLISH=true` mode AND at least one runtime test has been run prior to this test, then magic-string's btoa implementation fails with window not being declared. This is despite it previously checking that window.btoa is available. Presumably there's some sort of context thing going on, either with JSDOM or with Node itself.
// The tests pass when they're not run with `PUBLISH=true` (meaning, they currently pass in CI), and they also pass if you skip all runtime tests.
// I've spent too much time on this already, so for now to unblock the release, I am using the following workaround, which manually serializes the sourcemaps using Node Buffer APIs.
function toUrl(map) {
return 'data:application/json;charset=utf-8;base64,' + Buffer.from(map.toString(), 'utf-8').toString('base64');
}
function get_processor(tag_name, search, replace) {
return {
[tag_name]: ({ content, filename }) => {
@ -29,8 +19,8 @@ function get_processor(tag_name, search, replace) {
const map_opts = { source: filename, hires: true, includeContent: false };
const map = ms.generateMap(map_opts);
const attach_line = (tag_name == 'style' || comment_multi)
? `\n/*# sourceMappingURL=${toUrl(map)} */`
: `\n//# sourceMappingURL=${toUrl(map)}` // only in script
? `\n/*# sourceMappingURL=${map.toUrl()} */`
: `\n//# sourceMappingURL=${map.toUrl()}` // only in script
;
code = ms.toString() + attach_line;

@ -6,7 +6,7 @@ export default {
style: async ({ content, filename }) => {
const src = new MagicString(content);
const idx = content.indexOf('baritone');
src.overwrite(idx, idx+'baritone'.length, 'bar');
src.overwrite(idx, idx + 'baritone'.length, 'bar');
const map = SourceMapGenerator.fromSourceMap(
await new SourceMapConsumer(

@ -19,7 +19,7 @@ export default {
preprocess: [
{
style: ({ content, filename }) => {
const external =`/* Filename from preprocess: ${filename} */` + external_code;
const external = `/* Filename from preprocess: ${filename} */` + external_code;
return magic_string_bundle([
{ code: external, filename: external_relative_filename },
{ code: content, filename }

Loading…
Cancel
Save