Merge branch 'master' into api-reference

pull/2206/head
Rich Harris 7 years ago
commit 3da8562688

2
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.0.0-beta.20",
"version": "3.0.0-beta.21",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.0.0-beta.20",
"version": "3.0.0-beta.21",
"description": "The magical disappearing UI framework",
"module": "index.mjs",
"main": "index",

@ -121,7 +121,7 @@ function MoreRealisticComponent(props) {
<p>Selected {selected ? selected.name : 'nothing'}</p>
<ul>
${props.items.map(item =>
{props.items.map(item =>
<li>
<button onClick={() => setSelected(item)}>
{item.name}
@ -147,4 +147,4 @@ Svelte is explicitly designed to prevent you from ending up in that situation.
It's important to understand that virtual DOM *isn't a feature*. It's a means to an end, the end being declarative, state-driven UI development. Virtual DOM is valuable because it allows you to build apps without thinking about state transitions, with performance that is *generally good enough*. That means less buggy code, and more time spent on creative tasks instead of tedious ones.
But it turns out that we can achieve a similar programming model without using virtual DOM — and that's where Svelte comes in.
But it turns out that we can achieve a similar programming model without using virtual DOM — and that's where Svelte comes in.

@ -109,8 +109,8 @@
<div>
<video
poster="http://svelte-assets.surge.sh/caminandes-llamigos.jpg"
src="http://svelte-assets.surge.sh/caminandes-llamigos.mp4"
poster="https://svelte-assets.surge.sh/caminandes-llamigos.jpg"
src="https://svelte-assets.surge.sh/caminandes-llamigos.mp4"
on:mousemove={handleMousemove}
on:mousedown={handleMousedown}
></video>

@ -109,8 +109,8 @@
<div>
<video
poster="http://svelte-assets.surge.sh/caminandes-llamigos.jpg"
src="http://svelte-assets.surge.sh/caminandes-llamigos.mp4"
poster="https://svelte-assets.surge.sh/caminandes-llamigos.jpg"
src="https://svelte-assets.surge.sh/caminandes-llamigos.mp4"
on:mousemove={handleMousemove}
on:mousedown={handleMousedown}
bind:currentTime={time}

@ -10,14 +10,13 @@
<script>
import { onMount } from 'svelte';
import { locate } from 'locate-character';
import { process_example } from '../../components/Repl/process_example.js';
import AppControls from './_components/AppControls/index.svelte';
import Repl from '../../components/Repl/index.svelte';
export let version, example, gist_id;
console.log({ example });
export let version;
export let example;
export let gist_id;
let repl;
let gist;
@ -52,7 +51,7 @@
relaxed = false;
fetch(`gist/${gist_id}`).then(r => r.json()).then(data => {
gist = data;
const { id, description, files } = data;
const { description, files } = data;
name = description;
@ -84,36 +83,28 @@
repl.set({ components });
});
}
});
} else {
relaxed = true;
fetch(`examples/${example}.json`).then(async response => {
if (response.ok) {
const data = await response.json();
function load_example(slug) {
console.log(`loading ${slug}`);
name = data.title;
relaxed = true;
fetch(`examples/${slug}.json`).then(async response => {
if (response.ok) {
const data = await response.json();
name = data.title;
const components = process_example(data.files);
repl.set({ components });
const components = process_example(data.files);
repl.set({ components });
gist = null;
}
});
}
gist = null;
}
});
}
});
function handle_fork(event) {
example = null;
gist = event.detail.gist;
gist_id = gist.id;
}
$: if (process.browser && example) {
load_example(example);
}
</script>
<style>
@ -134,7 +125,7 @@
width: 100%;
height: 100%;
top: 0;
z-index: 11;
z-index: 111;
}
.pane { width: 100%; height: 100% }

@ -648,7 +648,7 @@ export default class Component {
this.add_reference(name.slice(1));
const variable = this.var_lookup.get(name.slice(1));
variable.subscribable = true;
if (variable) variable.subscribable = true;
} else {
this.add_var({
name,
@ -1164,9 +1164,11 @@ export default class Component {
const variable = this.var_lookup.get(name);
if (!variable) return name;
if (variable && variable.hoistable) return name;
this.add_reference(name); // TODO we can probably remove most other occurrences of this
if (variable.hoistable) return name;
return `ctx.${name}`;
}

@ -493,11 +493,12 @@ export default class Element extends Node {
});
}
if (check_type_attribute() !== 'checkbox') {
component.error(binding, {
code: `invalid-binding`,
message: `'${name}' binding can only be used with <input type="checkbox">`
});
const type = check_type_attribute();
if (type !== 'checkbox') {
let message = `'${name}' binding can only be used with <input type="checkbox">`;
if (type === 'radio') message += ` — for <input type="radio">, use 'group' binding`;
component.error(binding, { code: `invalid-binding`, message });
}
} else if (name === 'group') {
if (this.name !== 'input') {
@ -512,14 +513,14 @@ export default class Element extends Node {
if (type !== 'checkbox' && type !== 'radio') {
component.error(binding, {
code: `invalid-binding`,
message: `'checked' binding can only be used with <input type="checkbox"> or <input type="radio">`
message: `'group' binding can only be used with <input type="checkbox"> or <input type="radio">`
});
}
} else if (name == 'files') {
if (this.name !== 'input') {
component.error(binding, {
code: `invalid-binding`,
message: `'files' binding acn only be used with <input type="file">`
message: `'files' is not a valid binding on <${this.name}> elements`
});
}
@ -635,23 +636,6 @@ export default class Element extends Node {
});
}
get_static_attribute_value(name: string) {
const attribute = this.attributes.find(
(attr: Attribute) => attr.type === 'Attribute' && attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.is_true) return true;
if (attribute.chunks.length === 0) return '';
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
return attribute.chunks[0].data;
}
return null;
}
is_media_node() {
return this.name === 'audio' || this.name === 'video';
}

@ -5,6 +5,7 @@ import Attribute from './Attribute';
export default class Slot extends Element {
type: 'Element';
name: string;
slot_name: string;
attributes: Attribute[];
children: Node[];
@ -27,8 +28,8 @@ export default class Slot extends Element {
});
}
const slot_name = attr.value[0].data;
if (slot_name === 'default') {
this.slot_name = attr.value[0].data;
if (this.slot_name === 'default') {
component.error(attr, {
code: `invalid-slot-name`,
message: `default is a reserved word — it cannot be used as a slot name`
@ -46,6 +47,8 @@ export default class Slot extends Element {
// validator.slots.add(slot_name);
});
if (!this.slot_name) this.slot_name = 'default';
// if (node.attributes.length === 0) && validator.slots.has('default')) {
// validator.error(node, {
// code: `duplicate-slot`,
@ -53,21 +56,4 @@ export default class Slot extends Element {
// });
// }
}
get_static_attribute_value(name: string) {
const attribute = this.attributes.find(
attr => attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.is_true) return true;
if (attribute.chunks.length === 0) return '';
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
return attribute.chunks[0].data;
}
return null;
}
}

@ -364,7 +364,18 @@ export default class Expression {
let body = code.slice(node.body.start, node.body.end).trim();
if (node.body.type !== 'BlockStatement') {
if (pending_assignments.size > 0) {
const insert = Array.from(pending_assignments).map(name => component.invalidate(name)).join('; ');
const dependencies = new Set();
pending_assignments.forEach(name => {
if (template_scope.names.has(name)) {
template_scope.dependencies_for_name.get(name).forEach(dependency => {
dependencies.add(dependency);
});
} else {
dependencies.add(name);
}
});
const insert = Array.from(dependencies).map(name => component.invalidate(name)).join('; ');
pending_assignments = new Set();
component.has_reactive_assignments = true;

@ -37,17 +37,34 @@ export default class Node {
}
}
find_nearest(selector: RegExp) {
if (selector.test(this.type)) return this;
if (this.parent) return this.parent.find_nearest(selector);
}
get_static_attribute_value(name: string) {
const attribute = this.attributes.find(
(attr: Attribute) => attr.type === 'Attribute' && attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.is_true) return true;
if (attribute.chunks.length === 0) return '';
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
return attribute.chunks[0].data;
}
return null;
}
has_ancestor(type: string) {
return this.parent ?
this.parent.type === type || this.parent.has_ancestor(type) :
false;
}
find_nearest(selector: RegExp) {
if (selector.test(this.type)) return this;
if (this.parent) return this.parent.find_nearest(selector);
}
warn_if_empty_block() {
if (!/Block$/.test(this.type) || !this.children) return;
if (this.children.length > 1) return;

@ -10,7 +10,6 @@ import add_to_set from '../utils/add_to_set';
import get_object from '../utils/get_object';
import { extract_names } from '../utils/scope';
import { nodes_match } from '../../utils/nodes_match';
import { sanitize } from '../../utils/names';
export default function dom(
component: Component,
@ -305,8 +304,7 @@ export default function dom(
const reactive_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$');
if (renderer.slots.size > 0) {
const arr = Array.from(renderer.slots);
filtered_declarations.push(...arr.map(name => `$$slot_${sanitize(name)}`), '$$scope');
filtered_declarations.push('$$slots', '$$scope');
}
if (renderer.binding_groups.length > 0) {
@ -399,7 +397,7 @@ export default function dom(
${component.javascript}
${renderer.slots.size && `let { ${[...renderer.slots].map(name => `$$slot_${sanitize(name)}`).join(', ')}, $$scope } = $$props;`}
${renderer.slots.size && `let { $$slots = {}, $$scope } = $$props;`}
${renderer.binding_groups.length > 0 && `const $$binding_groups = [${renderer.binding_groups.map(_ => `[]`).join(', ')}];`}

@ -207,8 +207,8 @@ export default class EachBlockWrapper extends Wrapper {
if (needs_anchor) {
block.add_element(
this.vars.anchor,
`@comment()`,
parent_nodes && `@comment()`,
`@empty()`,
parent_nodes && `@empty()`,
parent_node
);
}
@ -300,8 +300,8 @@ export default class EachBlockWrapper extends Wrapper {
this.block.first = this.block.get_unique_name('first');
this.block.add_element(
this.block.first,
`@comment()`,
parent_nodes && `@comment()`,
`@empty()`,
parent_nodes && `@empty()`,
null
);
}

@ -117,7 +117,7 @@ export default class AttributeWrapper {
updater = `@set_input_type(${element.var}, ${should_cache ? last : value});`;
} else if (is_select_value_attribute) {
// annoying special case
const is_multiple_select = element.get_static_attribute_value('multiple');
const is_multiple_select = element.node.get_static_attribute_value('multiple');
const i = block.get_unique_name('i');
const option = block.get_unique_name('option');

@ -153,7 +153,7 @@ export default class BindingWrapper {
break;
case 'value':
if (parent.get_static_attribute_value('type') === 'file') {
if (parent.node.get_static_attribute_value('type') === 'file') {
update_dom = null;
}
}

@ -20,6 +20,7 @@ import add_event_handlers from '../shared/add_event_handlers';
import add_actions from '../shared/add_actions';
import create_debugging_comment from '../shared/create_debugging_comment';
import { get_context_merger } from '../shared/get_context_merger';
import Slot from '../../../nodes/Slot';
const events = [
{
@ -213,8 +214,7 @@ export default class ElementWrapper extends Wrapper {
const { renderer } = this;
if (this.node.name === 'slot') {
const slotName = this.get_static_attribute_value('name') || 'default';
renderer.slots.add(slotName);
renderer.slots.add((this.node as Slot).slot_name);
}
if (this.node.name === 'noscript') return;
@ -804,23 +804,6 @@ export default class ElementWrapper extends Wrapper {
});
}
get_static_attribute_value(name: string) {
const attribute = this.node.attributes.find(
(attr: Attribute) => attr.type === 'Attribute' && attr.name.toLowerCase() === name
);
if (!attribute) return null;
if (attribute.is_true) return true;
if (attribute.chunks.length === 0) return '';
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
return attribute.chunks[0].data;
}
return null;
}
add_css_class(class_name = this.component.stylesheet.id) {
const class_attribute = this.attributes.find(a => a.name === 'class');
if (class_attribute && !class_attribute.is_true) {

@ -185,8 +185,8 @@ export default class IfBlockWrapper extends Wrapper {
if (needs_anchor) {
block.add_element(
anchor,
`@comment()`,
parent_nodes && `@comment()`,
`@empty()`,
parent_nodes && `@empty()`,
parent_node
);
}

@ -119,16 +119,19 @@ export default class InlineComponentWrapper extends Wrapper {
const uses_spread = !!this.node.attributes.find(a => a.is_spread);
const slot_props = Array.from(this.slots).map(([name, slot]) => `$$slot_${sanitize(name)}: [${slot.block.name}${slot.fn ? `, ${slot.fn}` : ''}]`);
if (slot_props.length > 0) slot_props.push(`$$scope: { ctx }`);
const slot_props = Array.from(this.slots).map(([name, slot]) => `${quote_name_if_necessary(name)}: [${slot.block.name}${slot.fn ? `, ${slot.fn}` : ''}]`);
const initial_props = slot_props.length > 0
? [`$$slots: ${stringify_props(slot_props)}`, `$$scope: { ctx }`]
: [];
const attribute_object = uses_spread
? stringify_props(slot_props)
? stringify_props(initial_props)
: stringify_props(
this.node.attributes.map(attr => `${quote_name_if_necessary(attr.name)}: ${attr.get_value(block)}`).concat(slot_props)
this.node.attributes.map(attr => `${quote_name_if_necessary(attr.name)}: ${attr.get_value(block)}`).concat(initial_props)
);
if (this.node.attributes.length || this.node.bindings.length || slot_props.length) {
if (this.node.attributes.length || this.node.bindings.length || initial_props.length) {
if (!uses_spread && this.node.bindings.length === 0) {
component_opts.push(`props: ${attribute_object}`);
} else {

@ -4,7 +4,7 @@ import Block from '../Block';
import Slot from '../../nodes/Slot';
import FragmentWrapper from './Fragment';
import deindent from '../../utils/deindent';
import { sanitize } from '../../../utils/names';
import { sanitize, quote_prop_if_necessary } from '../../../utils/names';
import add_to_set from '../../utils/add_to_set';
import get_slot_data from '../../utils/get_slot_data';
import { stringify_props } from '../../utils/stringify_props';
@ -42,6 +42,10 @@ export default class SlotWrapper extends Wrapper {
});
block.add_dependencies(this.dependencies);
// we have to do this, just in case
block.add_intro();
block.add_outro();
}
render(
@ -51,7 +55,7 @@ export default class SlotWrapper extends Wrapper {
) {
const { renderer } = this;
const slot_name = this.node.get_static_attribute_value('name') || 'default';
const { slot_name } = this.node;
renderer.slots.add(slot_name);
let get_slot_changes;
@ -95,7 +99,7 @@ export default class SlotWrapper extends Wrapper {
const slot_definition = block.get_unique_name(`${sanitize(slot_name)}_slot`);
block.builders.init.add_block(deindent`
const ${slot_definition} = ctx.$$slot_${sanitize(slot_name)};
const ${slot_definition} = ctx.$$slots${quote_prop_if_necessary(slot_name)};
const ${slot} = @create_slot(${slot_definition}, ctx, ${get_slot_context});
`);
@ -137,6 +141,14 @@ export default class SlotWrapper extends Wrapper {
}
`);
block.builders.intro.add_line(
`if (${slot} && ${slot}.i) ${slot}.i(#local);`
);
block.builders.outro.add_line(
`if (${slot} && ${slot}.o) ${slot}.o(#local);`
);
let update_conditions = [...this.dependencies].map(name => `changed.${name}`).join(' || ');
if (this.dependencies.size > 1) update_conditions = `(${update_conditions})`;

@ -53,8 +53,8 @@ export default class Wrapper {
if (needs_anchor) {
block.add_element(
anchor,
`@comment()`,
parent_nodes && `@comment()`,
`@empty()`,
parent_nodes && `@empty()`,
parent_node
);
}

@ -2,10 +2,7 @@ import { quote_prop_if_necessary } from '../../../utils/names';
import get_slot_data from '../../utils/get_slot_data';
export default function(node, renderer, options) {
const name = node.attributes.find(attribute => attribute.name === 'name');
const slot_name = name && name.chunks[0].data || 'default';
const prop = quote_prop_if_necessary(slot_name);
const prop = quote_prop_if_necessary(node.slot_name);
const slot_data = get_slot_data(node.attributes, true);

@ -50,8 +50,8 @@ export function space() {
return text(' ');
}
export function comment() {
return document.createComment('');
export function empty() {
return text('');
}
export function listen(node, event, handler, options) {

@ -191,8 +191,7 @@ export default function tag(parser: Parser) {
}
if (name === 'svelte:component') {
// TODO post v2, treat this just as any other attribute
const index = element.attributes.findIndex(attr => attr.name === 'this');
const index = element.attributes.findIndex(attr => attr.type === 'Attribute' && attr.name === 'this');
if (!~index) {
parser.error({
code: `missing-component-definition`,

@ -111,5 +111,9 @@ export function quote_prop_if_necessary(name: string) {
}
export function sanitize(name: string) {
return name.replace(/[^a-zA-Z]+/g, '_').replace(/^_/, '').replace(/_$/, '');
}
return name
.replace(/[^a-zA-Z0-9_]+/g, '_')
.replace(/^_/, '')
.replace(/_$/, '')
.replace(/^[0-9]/, '_$&');
}

@ -1,8 +1,9 @@
import { run_all, noop, get_store_value, safe_not_equal } from './internal';
export function readable(start, value) {
const { set, subscribe } = writable(value, () => start(set));
return { subscribe };
export function readable(value, start) {
return {
subscribe: writable(value, start).subscribe
};
}
export function writable(value, start = noop) {
@ -25,7 +26,7 @@ export function writable(value, start = noop) {
function subscribe(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.push(subscriber);
if (subscribers.length === 1) stop = start() || noop;
if (subscribers.length === 1) stop = start(set) || noop;
run(value);
return () => {
@ -45,7 +46,7 @@ export function derive(stores, fn) {
const auto = fn.length < 2;
let value = {};
return readable(set => {
return readable(undefined, set => {
let inited = false;
const values = [];

@ -2,10 +2,10 @@
import {
SvelteComponent as SvelteComponent_1,
append,
comment,
destroy_each,
detach,
element,
empty,
init,
insert,
noop,
@ -66,7 +66,7 @@ function create_fragment(ctx) {
each_blocks[i].c();
}
each_1_anchor = comment();
each_1_anchor = empty();
},
m(target, anchor) {

@ -2,10 +2,10 @@
import {
SvelteComponent as SvelteComponent_1,
append,
comment,
destroy_each,
detach,
element,
empty,
init,
insert,
noop,
@ -66,7 +66,7 @@ function create_fragment(ctx) {
each_blocks[i].c();
}
each_1_anchor = comment();
each_1_anchor = empty();
},
m(target, anchor) {

@ -3,10 +3,10 @@ import {
SvelteComponent as SvelteComponent_1,
append,
blank_object,
comment,
create_animation,
detach,
element,
empty,
fix_and_outro_and_destroy_block,
fix_position,
init,
@ -89,7 +89,7 @@ function create_fragment(ctx) {
c() {
for (i = 0; i < each_blocks.length; i += 1) each_blocks[i].c();
each_1_anchor = comment();
each_1_anchor = empty();
},
m(target, anchor) {

@ -3,10 +3,10 @@ import {
SvelteComponent as SvelteComponent_1,
append,
blank_object,
comment,
destroy_block,
detach,
element,
empty,
init,
insert,
noop,
@ -73,7 +73,7 @@ function create_fragment(ctx) {
c() {
for (i = 0; i < each_blocks.length; i += 1) each_blocks[i].c();
each_1_anchor = comment();
each_1_anchor = empty();
},
m(target, anchor) {

@ -1,9 +1,9 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent as SvelteComponent_1,
comment,
detach,
element,
empty,
init,
insert,
noop,
@ -68,7 +68,7 @@ function create_fragment(ctx) {
return {
c() {
if_block.c();
if_block_anchor = comment();
if_block_anchor = empty();
},
m(target, anchor) {

@ -1,9 +1,9 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent as SvelteComponent_1,
comment,
detach,
element,
empty,
init,
insert,
noop,
@ -40,7 +40,7 @@ function create_fragment(ctx) {
return {
c() {
if (if_block) if_block.c();
if_block_anchor = comment();
if_block_anchor = empty();
},
m(target, anchor) {

@ -2,10 +2,10 @@
import {
SvelteComponent as SvelteComponent_1,
add_render_callback,
comment,
create_in_transition,
detach,
element,
empty,
init,
insert,
noop,
@ -21,7 +21,7 @@ function create_if_block(ctx) {
return {
c() {
if (if_block) if_block.c();
if_block_anchor = comment();
if_block_anchor = empty();
},
m(target, anchor) {
@ -98,7 +98,7 @@ function create_fragment(ctx) {
return {
c() {
if (if_block) if_block.c();
if_block_anchor = comment();
if_block_anchor = empty();
},
m(target, anchor) {

@ -2,9 +2,9 @@
import {
SvelteComponent as SvelteComponent_1,
append,
comment,
detach,
element,
empty,
init,
insert,
noop,
@ -153,7 +153,7 @@ function create_fragment(ctx) {
if (if_block3) if_block3.c();
t7 = space();
if (if_block4) if_block4.c();
if_block4_anchor = comment();
if_block4_anchor = empty();
},
m(target, anchor) {

@ -0,0 +1,8 @@
<div>
<slot name="header1" />
<slot name="-header2_" />
<slot name="3header" />
<slot name="_header4" />
<slot name="header-5" />
<slot name="header&5" />
</div>

@ -0,0 +1,12 @@
export default {
html: `
<div>
<h1 slot="header1">Header 1</h1>
<h2 slot="-header2_">Header 2</h2>
<h3 slot="3header">Header 3</h3>
<h4 slot="_header4">Header 4</h4>
<h5 slot="header-5">Header 5</h5>
<h5 slot="header&5">Header 5b</h5>
</div>
`
};

@ -0,0 +1,12 @@
<script>
import Nested from './Nested.svelte';
</script>
<Nested>
<h1 slot="header1">Header 1</h1>
<h2 slot="-header2_">Header 2</h2>
<h3 slot="3header">Header 3</h3>
<h4 slot="_header4">Header 4</h4>
<h5 slot="header-5">Header 5</h5>
<h5 slot="header&5">Header 5b</h5>
</Nested>

@ -4,4 +4,4 @@
export let test;
</script>
<svelte:component this={Foo} bind:this={test}/>
<svelte:component bind:this={test} this={Foo}/>

@ -0,0 +1,35 @@
export default {
html: `
<button>off</button>
<button>on</button>
<button>off</button>
<p>on: 1</p>
`,
async test({ assert, component, target, window }) {
const buttons = target.querySelectorAll('button');
const event = new window.MouseEvent('click');
await buttons[0].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<button>on</button>
<button>on</button>
<button>off</button>
<p>on: 2</p>
`);
await buttons[2].dispatchEvent(event);
assert.htmlEqual(target.innerHTML, `
<button>on</button>
<button>on</button>
<button>on</button>
<p>on: 3</p>
`);
assert.deepEqual(component.switches, [
{ on: true },
{ on: true },
{ on: true }
]);
}
};

@ -0,0 +1,15 @@
<script>
export let switches = [
{ on: false },
{ on: true },
{ on: false }
];
</script>
{#each switches as s}
<button on:click="{() => s.on = !s.on}">
{s.on ? 'on' : 'off'}
</button>
{/each}
<p>on: {switches.filter(s => !!s.on).length}</p>

@ -0,0 +1,3 @@
export default {
error: 'missingGlobal is not defined'
};

@ -0,0 +1,9 @@
<script>
export let visible;
</script>
<div>
{#if visible}
<slot/>
{/if}
</div>

@ -0,0 +1,26 @@
export default {
props: {
visible: false
},
html: `
<div></div>
`,
test({ assert, component, target, window, raf }) {
component.visible = true;
const p = target.querySelector('p');
assert.equal(p.foo, 0);
raf.tick(50);
assert.equal(p.foo, 0.5);
component.visible = false;
raf.tick(75);
assert.equal(p.foo, 0.25);
raf.tick(100);
assert.equal(p.foo, 0);
}
};

@ -0,0 +1,18 @@
<script>
import Nested from './Nested.svelte';
export let visible;
function foo(node, params) {
return {
duration: 100,
tick: t => {
node.foo = t;
}
};
}
</script>
<Nested {visible}>
<p transition:foo>slotted</p>
</Nested>

@ -66,7 +66,7 @@ describe('store', () => {
let running;
let tick;
const store = readable(set => {
const store = readable(undefined, set => {
tick = set;
running = true;
@ -192,7 +192,7 @@ describe('store', () => {
describe('get', () => {
it('gets the current value of a store', () => {
const store = readable(() => {}, 42);
const store = readable(42, () => {});
assert.equal(get(store), 42);
});
});

@ -1,6 +1,16 @@
export default {
test(assert, vars) {
assert.deepEqual(vars, [
{
export_name: null,
injected: false,
module: false,
mutated: false,
name: 'hoistable_foo',
reassigned: false,
referenced: true,
writable: false
},
{
export_name: null,
injected: false,

@ -1,5 +1,7 @@
<script>
import hoistable_foo from '';
let foo;
</script>
<div use:hoistable_foo/>
<div use:foo/>

@ -1,6 +1,16 @@
export default {
test(assert, vars) {
assert.deepEqual(vars, [
{
export_name: null,
injected: false,
module: false,
mutated: false,
name: 'hoistable_foo',
reassigned: false,
referenced: true,
writable: false
},
{
export_name: null,
injected: false,

@ -1,7 +1,11 @@
<script>
import hoistable_foo from '';
let foo;
</script>
{#each [] as x (x)}
<div animate:hoistable_foo/>
{/each}
{#each [] as x (x)}
<div animate:foo/>
{/each}

@ -1,6 +1,36 @@
export default {
test(assert, vars) {
assert.deepEqual(vars, [
{
export_name: null,
injected: false,
module: false,
mutated: false,
name: 'hoistable_foo',
reassigned: false,
referenced: true,
writable: false
},
{
export_name: null,
injected: false,
module: false,
mutated: false,
name: 'hoistable_bar',
reassigned: false,
referenced: true,
writable: false
},
{
export_name: null,
injected: false,
module: false,
mutated: false,
name: 'hoistable_baz',
reassigned: false,
referenced: true,
writable: false
},
{
export_name: null,
injected: false,

@ -1,9 +1,13 @@
<script>
import { hoistable_foo, hoistable_bar, hoistable_baz } from '';
let foo;
let bar;
let baz;
</script>
<div in:hoistable_foo/>
<div out:hoistable_bar/>
<div transition:hoistable_baz/>
<div in:foo/>
<div out:bar/>
<div transition:baz/>

Loading…
Cancel
Save