Merge master into pr/3822

pull/3822/head
rixo 6 years ago
commit ae5d759da7

@ -1,5 +1,6 @@
**/_actual.js
**/expected.js
_output
test/*/samples/*/output.js
node_modules

1
.gitignore vendored

@ -23,6 +23,7 @@ node_modules
/test/sourcemaps/samples/*/output.css.map
/yarn-error.log
_actual*.*
_output
/types
/site/cypress/screenshots/

@ -1,10 +1,30 @@
# Svelte changelog
## Unreleased
## 3.16.5
* Better fix for cascading invalidations and fix some regressions ([#4098](https://github.com/sveltejs/svelte/issues/4098), [#4114](https://github.com/sveltejs/svelte/issues/4114), [#4120](https://github.com/sveltejs/svelte/issues/4120))
## 3.16.4
* Fix slots with props not propagating through to inner slots ([#4061](https://github.com/sveltejs/svelte/issues/4061))
* Fix noting autosubscribed stores as `referenced` in `vars` for tooling ([#4081](https://github.com/sveltejs/svelte/issues/4081))
* Fix cascading invalidations in certain situations ([#4094](https://github.com/sveltejs/svelte/issues/4094))
## 3.16.3
* Fix bitmask overflow when using slotted components ([#4077](https://github.com/sveltejs/svelte/issues/4077))
* Remove unnecessary `$$invalidate` calls from init block ([#4018](https://github.com/sveltejs/svelte/issues/4018))
## 3.16.2
* Handle slot updates when parent component has a bitmask overflow ([#4078](https://github.com/sveltejs/svelte/pull/4078))
## 3.16.1
* Fix unused export warning for props used as stores ([#4021](https://github.com/sveltejs/svelte/issues/4021))
* Fix `{:then}` without resolved value containing `{#each}` ([#4022](https://github.com/sveltejs/svelte/issues/4022))
* Fix incorrect code generated with `loopGuardTimeout` ([#4034](https://github.com/sveltejs/svelte/issues/4034))
* Fix handling of bitmask overflow and globals ([#4037](https://github.com/sveltejs/svelte/issues/4037))
* Fix `{:then}` containing `{#if}` ([#4044](https://github.com/sveltejs/svelte/issues/4044))
* Fix bare `import`s in `format: 'cjs'` output mode ([#4055](https://github.com/sveltejs/svelte/issues/4050))
* Warn when using a known global as a component name ([#4070](https://github.com/sveltejs/svelte/issues/4070))

@ -11,8 +11,8 @@
<img src="https://packagephobia.now.sh/badge?p=svelte" alt="install size">
</a>
<a href="https://travis-ci.org/sveltejs/svelte">
<img src="https://api.travis-ci.org/sveltejs/svelte.svg?branch=master"
<a href="https://github.com/sveltejs/svelte/actions">
<img src="https://github.com/sveltejs/svelte/workflows/CI/badge.svg?branch=master"
alt="build status">
</a>

8
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.15.0",
"version": "3.16.5",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -500,9 +500,9 @@
"dev": true
},
"code-red": {
"version": "0.0.26",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.26.tgz",
"integrity": "sha512-W4t68vk3xJjmkbuAKfEtaj7E+K82BtV+A4VjBlxHA6gDoSLc+sTB643JdJMSk27vpp5iEqHFuGnHieQGy/GmUQ==",
"version": "0.0.27",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.27.tgz",
"integrity": "sha512-MSILIi8kkSGag76TW+PRfBP/dN++Ei+uTeiUfqW4Es5teFNrbsAWtyAbPwxKI1vxEsBx64loAfGxS1kVCo1d2g==",
"dev": true,
"requires": {
"acorn": "^7.1.0",

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.16.0",
"version": "3.16.5",
"description": "Cybernetically enhanced web apps",
"module": "index.mjs",
"main": "index",
@ -64,7 +64,7 @@
"acorn": "^7.1.0",
"agadoo": "^1.1.0",
"c8": "^5.0.1",
"code-red": "0.0.26",
"code-red": "0.0.27",
"codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0",

@ -29,13 +29,33 @@
font-weight: 700;
}
div {
display: flex;
flex-direction: row;
padding: 0.2rem 3rem;
margin: 0 -3rem;
}
div.active {
background: rgba(0, 0, 0, 0.15) calc(100% - 3rem) 47% no-repeat
url(/icons/arrow-right.svg);
background-size: 1em 1em;
color: white;
}
div.active.loading {
background: rgba(0, 0, 0, 0.1) calc(100% - 3rem) 47% no-repeat
url(/icons/loading.svg);
background-size: 1em 1em;
color: white;
}
a {
display: flex;
flex: 1 1 auto;
position: relative;
color: var(--sidebar-text);
border-bottom: none;
padding: 0.2rem 3rem;
margin: 0 -3rem;
font-size: 1.6rem;
align-items: center;
justify-content: start;
@ -45,18 +65,11 @@
color: white;
}
a.active {
background: rgba(0, 0, 0, 0.15) calc(100% - 3rem) 50% no-repeat
url(/icons/arrow-right.svg);
background-size: 1em 1em;
color: white;
}
a.active.loading {
background: rgba(0, 0, 0, 0.1) calc(100% - 3rem) 50% no-repeat
url(/icons/loading.svg);
background-size: 1em 1em;
color: white;
.repl-link {
flex: 0 1 auto;
font-size: 1.2rem;
font-weight: 700;
margin-right: 2.5rem;
}
.thumbnail {
@ -72,27 +85,31 @@
<ul class="examples-toc">
{#each sections as section}
<li>
<span class="section-title">
{section.title}
</span>
<li>
<span class="section-title">{section.title}</span>
{#each section.examples as example}
<a
href="examples#{example.slug}"
class="row"
class:active="{example.slug === active_section}"
class:loading="{isLoading}"
>
<img
class="thumbnail"
alt="{example.title} thumbnail"
src="examples/thumbnails/{example.slug}.jpg"
/>
{#each section.examples as example}
<div
class="row"
class:active={example.slug === active_section}
class:loading={isLoading}>
<a
href="examples#{example.slug}"
class="row"
class:active={example.slug === active_section}
class:loading={isLoading}>
<img
class="thumbnail"
alt="{example.title} thumbnail"
src="examples/thumbnails/{example.slug}.jpg" />
<span>{example.title}</span>
</a>
{/each}
</li>
<span>{example.title}</span>
</a>
{#if example.slug === active_section}
<a href="repl/{example.slug}" class="repl-link">REPL</a>
{/if}
</div>
{/each}
</li>
{/each}
</ul>

@ -85,7 +85,8 @@
<div style="grid-area: start; display: flex; flex-direction: column; min-width: 0" slot="how">
<pre class="language-bash" style="margin: 0 0 1em 0; min-width: 0; min-height: 0">
npx degit sveltejs/template my-svelte-project
npx degit <a href="https://github.com/sveltejs/template" style="user-select: initial;">sveltejs/template</a> my-svelte-project
<span class="token comment"># or download and extract <a href="https://github.com/sveltejs/template/archive/master.zip">this .zip file</a></span>
cd my-svelte-project
npm install

@ -187,7 +187,7 @@
<svelte:head>
<title>{name} • REPL • Svelte</title>
<meta name="twitter:title" content="{name} | Svelte REPL">
<meta name="twitter:title" content="Svelte REPL">
<meta name="twitter:description" content="Cybernetically enhanced web apps">
<meta name="Description" content="Interactive Svelte playground">
</svelte:head>

@ -203,7 +203,10 @@ export default class Component {
const subscribable_name = name.slice(1);
const variable = this.var_lookup.get(subscribable_name);
if (variable) variable.subscribable = true;
if (variable) {
variable.referenced = true;
variable.subscribable = true;
}
} else {
this.used_names.add(name);
}

@ -33,7 +33,7 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
const { name, filename, loopGuardTimeout, dev } = options;
Object.keys(options).forEach(key => {
if (valid_options.indexOf(key) === -1) {
if (!valid_options.includes(key)) {
const match = fuzzymatch(key, valid_options);
let message = `Unrecognized option '${key}'`;
if (match) message += ` (did you mean '${match}'?)`;

@ -15,6 +15,11 @@ interface ContextMember {
priority: number;
}
type BitMasks = Array<{
n: number;
names: string[];
}>;
export default class Renderer {
component: Component; // TODO Maybe Renderer shouldn't know about Component?
options: CompileOptions;
@ -81,8 +86,6 @@ export default class Renderer {
null
);
this.context_overflow = this.context.length > 31;
// TODO messy
this.blocks.forEach(block => {
if (block instanceof Block) {
@ -94,6 +97,8 @@ export default class Renderer {
this.fragment.render(this.block, null, x`#nodes` as Identifier);
this.context_overflow = this.context.length > 31;
this.context.forEach(member => {
const { variable } = member;
if (variable) {
@ -199,65 +204,53 @@ export default class Renderer {
? x`$$self.$$.dirty`
: x`#dirty`) as Identifier | MemberExpression;
const get_bitmask = () => names.reduce((bitmask, name) => {
const member = renderer.context_lookup.get(name);
if (!member) return bitmask;
const get_bitmask = () => {
const bitmask: BitMasks = [];
names.forEach((name) => {
const member = renderer.context_lookup.get(name);
if (member.index.value === -1) {
throw new Error(`unset index`);
}
if (!member) return;
const value = member.index.value as number;
const i = (value / 31) | 0;
const n = 1 << (value % 31);
if (member.index.value === -1) {
throw new Error(`unset index`);
}
if (!bitmask[i]) bitmask[i] = { n: 0, names: [] };
const value = member.index.value as number;
const i = (value / 31) | 0;
const n = 1 << (value % 31);
bitmask[i].n |= n;
bitmask[i].names.push(name);
if (!bitmask[i]) bitmask[i] = { n: 0, names: [] };
bitmask[i].n |= n;
bitmask[i].names.push(name);
});
return bitmask;
}, Array((this.context.length / 31) | 0).fill(null));
let operator;
let left;
let right;
};
return {
get type() {
// we make the type a getter, even though it's always
// a BinaryExpression, because it gives us an opportunity
// to lazily create the node. TODO would be better if
// context was determined before rendering, so that
// this indirection was unnecessary
// Using a ParenthesizedExpression allows us to create
// the expression lazily. TODO would be better if
// context was determined before rendering, so that
// this indirection was unnecessary
type: 'ParenthesizedExpression',
get expression() {
const bitmask = get_bitmask();
if (!bitmask.length) {
return x`${dirty} & /*${names.join(', ')}*/ 0` as BinaryExpression;
}
if (renderer.context_overflow) {
const expression = bitmask
return bitmask
.map((b, i) => ({ b, i }))
.filter(({ b }) => b)
.map(({ b, i }) => x`${dirty}[${i}] & /*${b.names.join(', ')}*/ ${b.n}`)
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`);
({ operator, left, right } = expression);
} else {
({ operator, left, right } = x`${dirty} & /*${names.join(', ')}*/ ${bitmask[0] ? bitmask[0].n : 0}` as BinaryExpression); // TODO the `: 0` case should never apply
}
return 'BinaryExpression';
},
get operator() {
return operator;
},
get left() {
return left;
},
get right() {
return right;
return x`${dirty} & /*${names.join(', ')}*/ ${bitmask[0].n}` as BinaryExpression;
}
} as Expression;
} as any;
}
reference(node: string | Identifier | MemberExpression) {

@ -3,7 +3,7 @@ import Component from '../Component';
import Renderer from './Renderer';
import { CompileOptions } from '../../interfaces';
import { walk } from 'estree-walker';
import { extract_names } from '../utils/scope';
import { extract_names, Scope } from '../utils/scope';
import { invalidate } from './invalidate';
import Block from './Block';
import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree';
@ -203,11 +203,18 @@ export default function dom(
if (component.ast.instance) {
let scope = component.instance_scope;
const map = component.instance_scope_map;
let execution_context: Node | null = null;
walk(component.ast.instance.content, {
enter: (node) => {
enter(node) {
if (map.has(node)) {
scope = map.get(node);
scope = map.get(node) as Scope;
if (!execution_context && !scope.block) {
execution_context = node;
}
} else if (!execution_context && node.type === 'LabeledStatement' && node.label.name === '$') {
execution_context = node;
}
},
@ -216,6 +223,10 @@ export default function dom(
scope = scope.parent;
}
if (execution_context === node) {
execution_context = null;
}
if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
@ -225,7 +236,7 @@ export default function dom(
// onto the initial function call
const names = new Set(extract_names(assignee));
this.replace(invalidate(renderer, scope, node, names));
this.replace(invalidate(renderer, scope, node, names, execution_context === null));
}
}
});

@ -1,42 +1,50 @@
import { nodes_match } from '../../utils/nodes_match';
import { Scope } from '../utils/scope';
import { x } from 'code-red';
import { Node } from 'estree';
import { Node, Expression } from 'estree';
import Renderer from './Renderer';
import { Var } from '../../interfaces';
export function invalidate(renderer: Renderer, scope: Scope, node: Node, names: Set<string>) {
export function invalidate(renderer: Renderer, scope: Scope, node: Node, names: Set<string>, main_execution_context: boolean = false) {
const { component } = renderer;
const [head, ...tail] = Array.from(names).filter(name => {
const owner = scope.find_owner(name);
if (owner && owner !== component.instance_scope) return false;
const [head, ...tail] = Array.from(names)
.filter(name => {
const owner = scope.find_owner(name);
return !owner || owner === component.instance_scope;
})
.map(name => component.var_lookup.get(name))
.filter(variable => {
return variable && (
!variable.hoistable &&
!variable.global &&
!variable.module &&
(
variable.referenced ||
variable.subscribable ||
variable.is_reactive_dependency ||
variable.export_name ||
variable.name[0] === '$'
)
);
}) as Var[];
const variable = component.var_lookup.get(name);
function get_invalidated(variable: Var, node?: Expression) {
if (main_execution_context && !variable.subscribable && variable.name[0] !== '$') {
return node || x`${variable.name}`;
}
return variable && (
!variable.hoistable &&
!variable.global &&
!variable.module &&
(
variable.referenced ||
variable.subscribable ||
variable.is_reactive_dependency ||
variable.export_name ||
variable.name[0] === '$'
)
);
});
return renderer.invalidate(variable.name);
}
if (head) {
component.has_reactive_assignments = true;
if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) {
return renderer.invalidate(head);
return get_invalidated(head, node);
} else {
const is_store_value = head[0] === '$';
const variable = component.var_lookup.get(head);
const extra_args = tail.map(name => renderer.invalidate(name));
const is_store_value = head.name[0] === '$';
const extra_args = tail.map(variable => get_invalidated(variable));
const pass_value = (
extra_args.length > 0 ||
@ -47,16 +55,18 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names:
if (pass_value) {
extra_args.unshift({
type: 'Identifier',
name: head
name: head.name
});
}
let invalidate = is_store_value
? x`@set_store_value(${head.slice(1)}, ${node}, ${extra_args})`
: x`$$invalidate(${renderer.context_lookup.get(head).index}, ${node}, ${extra_args})`;
? x`@set_store_value(${head.name.slice(1)}, ${node}, ${extra_args})`
: !main_execution_context
? x`$$invalidate(${renderer.context_lookup.get(head.name).index}, ${node}, ${extra_args})`
: node;
if (variable.subscribable && variable.reassigned) {
const subscribe = `$$subscribe_${head}`;
if (head.subscribable && head.reassigned) {
const subscribe = `$$subscribe_${head.name}`;
invalidate = x`${subscribe}(${invalidate})}`;
}

@ -182,11 +182,9 @@ export default class InlineComponentWrapper extends Wrapper {
});
});
const non_let_dependencies = Array.from(fragment_dependencies).filter(name => !this.node.scope.is_let(name));
const dynamic_attributes = this.node.attributes.filter(a => a.get_dependencies().length > 0);
if (!uses_spread && (dynamic_attributes.length > 0 || this.node.bindings.length > 0 || non_let_dependencies.length > 0)) {
if (!uses_spread && (dynamic_attributes.length > 0 || this.node.bindings.length > 0 || fragment_dependencies.size > 0)) {
updates.push(b`const ${name_changes} = {};`);
}
@ -266,9 +264,9 @@ export default class InlineComponentWrapper extends Wrapper {
}
}
if (non_let_dependencies.length > 0) {
if (fragment_dependencies.size > 0) {
updates.push(b`
if (${renderer.dirty(non_let_dependencies)}) {
if (${renderer.dirty(Array.from(fragment_dependencies))}) {
${name_changes}.$$scope = { dirty: #dirty, ctx: #ctx };
}`);
}

@ -2,6 +2,7 @@ import Let from '../../../nodes/Let';
import { x, p } from 'code-red';
import Block from '../../Block';
import TemplateScope from '../../../nodes/shared/TemplateScope';
import { BinaryExpression } from 'estree';
export function get_slot_definition(block: Block, scope: TemplateScope, lets: Let[]) {
if (lets.length === 0) return { block, scope };
@ -28,21 +29,48 @@ export function get_slot_definition(block: Block, scope: TemplateScope, lets: Le
properties: Array.from(names).map(name => p`${block.renderer.context_lookup.get(name).index}: ${name}`)
};
const changes = Array.from(names)
.map(name => {
const { context_lookup } = block.renderer;
const { context_lookup } = block.renderer;
const literal = {
type: 'Literal',
get value() {
// i am well aware that this code is gross
// TODO make it less gross
const changes = {
type: 'ParenthesizedExpression',
get expression() {
if (block.renderer.context_overflow) {
const grouped = [];
Array.from(names).forEach(name => {
const i = context_lookup.get(name).index.value as number;
return 1 << i;
const g = Math.floor(i / 31);
if (!grouped[g]) grouped[g] = [];
grouped[g].push({ name, n: i % 31 });
});
const elements = [];
for (let g = 0; g < grouped.length; g += 1) {
elements[g] = grouped[g]
? grouped[g]
.map(({ name, n }) => x`${name} ? ${1 << n} : 0`)
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`)
: x`0`;
}
};
return x`${name} ? ${literal} : 0`;
})
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`);
return {
type: 'ArrayExpression',
elements
};
}
return Array.from(names)
.map(name => {
const i = context_lookup.get(name).index.value as number;
return x`${name} ? ${1 << i} : 0`;
})
.reduce((lhs, rhs) => x`${lhs} | ${rhs}`) as BinaryExpression;
}
};
return {
block,

@ -73,8 +73,9 @@ function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
$$.fragment && $$.fragment.p($$.ctx, $$.dirty);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}

@ -77,9 +77,23 @@ export function get_slot_context(definition, ctx, $$scope, fn) {
}
export function get_slot_changes(definition, $$scope, dirty, fn) {
return definition[2] && fn
? $$scope.dirty | definition[2](fn(dirty))
: $$scope.dirty;
if (definition[2] && fn) {
const lets = definition[2](fn(dirty));
if (typeof $$scope.dirty === 'object') {
const merged = [];
const len = Math.max($$scope.dirty.length, lets.length);
for (let i = 0; i < len; i += 1) {
merged[i] = $$scope.dirty[i] | lets[i];
}
return merged;
}
return $$scope.dirty | lets;
}
return $$scope.dirty;
}
export function exclude_internal_props(props) {

@ -1,6 +1,7 @@
import * as jsdom from 'jsdom';
import * as assert from 'assert';
import * as glob from 'tiny-glob/sync.js';
import * as path from 'path';
import * as fs from 'fs';
import * as colors from 'kleur';
@ -237,3 +238,16 @@ export function useFakeTimers() {
}
};
}
export function mkdirp(dir) {
const parent = path.dirname(dir);
if (parent === dir) return;
mkdirp(parent);
try {
fs.mkdirSync(dir);
} catch (err) {
// do nothing
}
}

@ -43,7 +43,7 @@ function instance($$self, $$props, $$invalidate) {
let $foo;
const foo = writable(0);
component_subscribe($$self, foo, value => $$invalidate(0, $foo = value));
return [$foo];
return [$foo, foo];
}
class Component extends SvelteComponent {

@ -0,0 +1,75 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
append,
detach,
element,
init,
insert,
noop,
safe_not_equal,
set_data,
text
} from "svelte/internal";
function create_fragment(ctx) {
let p;
let t0;
let t1;
return {
c() {
p = element("p");
t0 = text("x: ");
t1 = text(/*x*/ ctx[0]);
},
m(target, anchor) {
insert(target, p, anchor);
append(p, t0);
append(p, t1);
},
p(ctx, [dirty]) {
if (dirty & /*x*/ 1) set_data(t1, /*x*/ ctx[0]);
},
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(p);
}
};
}
function instance($$self, $$props, $$invalidate) {
let x = 0;
let y = 1;
x += 1;
{
x += 2;
}
setTimeout(
function foo() {
$$invalidate(0, x += 10);
$$invalidate(1, y += 20);
},
1000
);
$$self.$$.update = () => {
if ($$self.$$.dirty & /*x, y*/ 3) {
$: $$invalidate(0, x += y);
}
};
return [x];
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, {});
}
}
export default Component;

@ -0,0 +1,19 @@
<script>
let x = 0;
let y = 1;
x += 1;
{
x += 2;
}
setTimeout(function foo() {
x += 10;
y += 20;
}, 1000);
$: x += y;
</script>
<p>x: {x}</p>

@ -3,6 +3,7 @@ import * as path from "path";
import * as fs from "fs";
import { rollup } from 'rollup';
import * as virtual from 'rollup-plugin-virtual';
import * as glob from 'tiny-glob/sync.js';
import { clear_loops, flush, set_now, set_raf } from "../../internal";
import {
@ -10,7 +11,8 @@ import {
loadConfig,
loadSvelte,
env,
setupHtmlEqual
setupHtmlEqual,
mkdirp
} from "../helpers.js";
let svelte$;
@ -90,6 +92,33 @@ describe("runtime", () => {
const window = env();
glob('**/*.svelte', { cwd }).forEach(file => {
if (file[0] === '_') return;
const dir = `${cwd}/_output/${hydrate ? 'hydratable' : 'normal'}`;
const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`;
if (fs.existsSync(out)) {
fs.unlinkSync(out);
}
mkdirp(dir);
try {
const { js } = compile(
fs.readFileSync(`${cwd}/${file}`, 'utf-8'),
{
...compileOptions,
filename: file
}
);
fs.writeFileSync(out, js.code);
} catch (err) {
// do nothing
}
});
return Promise.resolve()
.then(() => {
// hack to support transition tests
@ -195,7 +224,7 @@ describe("runtime", () => {
} else {
throw err;
}
}).catch(err => {
}).catch(err => {
failed.add(dir);
showOutput(cwd, compileOptions, compile); // eslint-disable-line no-console
throw err;

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

@ -0,0 +1,35 @@
<script>
export let x1;
export let x2;
export let x3;
export let x4;
export let x5;
export let x6;
export let x7;
export let x8;
export let x9;
export let x10;
export let x11;
export let x12;
export let x13;
export let x14;
export let x15;
export let x16;
export let x17;
export let x18;
export let x19;
export let x20;
export let x21;
export let x22;
export let x23;
export let x24;
export let x25;
export let x26;
export let x27;
export let x28;
export let x29;
export let x30;
export let x31;
export let x32;
</script>
<p {...potato}>{x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + x22 + x23 + x24 + x25 + x26 + x27 + x28 + x29 + x30 + x31 + x32}</p>

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

@ -0,0 +1,4 @@
<script>
let x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31;
</script>
<A>foo</A>

@ -0,0 +1,73 @@
<script>
export let d1 = 'd1';
export let d2 = 'd2';
export let d3 = 'd3';
export let d4 = 'd4';
export let d5 = 'd5';
export let d6 = 'd6';
export let d7 = 'd7';
export let d8 = 'd8';
export let d9 = 'd9';
export let d10 = 'd10';
export let d11 = 'd11';
export let d12 = 'd12';
export let d13 = 'd13';
export let d14 = 'd14';
export let d15 = 'd15';
export let d16 = 'd16';
export let d17 = 'd17';
export let d18 = 'd18';
export let d19 = 'd19';
export let d20 = 'd20';
export let d21 = 'd21';
export let d22 = 'd22';
export let d23 = 'd23';
export let d24 = 'd24';
export let d25 = 'd25';
export let d26 = 'd26';
export let d27 = 'd27';
export let d28 = 'd28';
export let d29 = 'd29';
export let d30 = 'd30';
export let d31 = 'd31';
export let d32 = 'd32';
export let d33 = 'd33';
$: dummy = d32 + ':' + d33;
</script>
<p>{d1}</p>
<p>{d2}</p>
<p>{d3}</p>
<p>{d4}</p>
<p>{d5}</p>
<p>{d6}</p>
<p>{d7}</p>
<p>{d8}</p>
<p>{d9}</p>
<p>{d10}</p>
<p>{d11}</p>
<p>{d12}</p>
<p>{d13}</p>
<p>{d14}</p>
<p>{d15}</p>
<p>{d16}</p>
<p>{d17}</p>
<p>{d18}</p>
<p>{d19}</p>
<p>{d20}</p>
<p>{d21}</p>
<p>{d22}</p>
<p>{d23}</p>
<p>{d24}</p>
<p>{d25}</p>
<p>{d26}</p>
<p>{d27}</p>
<p>{d28}</p>
<p>{d29}</p>
<p>{d30}</p>
<p>{d31}</p>
<p>{d32}</p>
<p>{d33}</p>
<slot dummy={dummy}></slot>

@ -0,0 +1,96 @@
export default {
html: `
<p>d1</p>
<p>d2</p>
<p>d3</p>
<p>d4</p>
<p>d5</p>
<p>d6</p>
<p>d7</p>
<p>d8</p>
<p>d9</p>
<p>d10</p>
<p>d11</p>
<p>d12</p>
<p>d13</p>
<p>d14</p>
<p>d15</p>
<p>d16</p>
<p>d17</p>
<p>d18</p>
<p>d19</p>
<p>d20</p>
<p>d21</p>
<p>d22</p>
<p>d23</p>
<p>d24</p>
<p>d25</p>
<p>d26</p>
<p>d27</p>
<p>d28</p>
<p>d29</p>
<p>d30</p>
<p>d31</p>
<p>2</p>
<p>1</p>
<p>0:1</p>
<p>2:1</p>
<p>0</p>
<p>1</p>
<p>2</p>
`,
test({ assert, component, target }) {
component.reads = {};
component._0 = 'a';
component._1 = 'b';
component._2 = 'c';
assert.htmlEqual(target.innerHTML, `
<p>d1</p>
<p>d2</p>
<p>d3</p>
<p>d4</p>
<p>d5</p>
<p>d6</p>
<p>d7</p>
<p>d8</p>
<p>d9</p>
<p>d10</p>
<p>d11</p>
<p>d12</p>
<p>d13</p>
<p>d14</p>
<p>d15</p>
<p>d16</p>
<p>d17</p>
<p>d18</p>
<p>d19</p>
<p>d20</p>
<p>d21</p>
<p>d22</p>
<p>d23</p>
<p>d24</p>
<p>d25</p>
<p>d26</p>
<p>d27</p>
<p>d28</p>
<p>d29</p>
<p>d30</p>
<p>d31</p>
<p>c</p>
<p>b</p>
<p>a:b</p>
<p>c:b</p>
<p>a</p>
<p>b</p>
<p>c</p>
`);
assert.deepEqual(component.reads, {
_0: 2,
_1: 2,
});
}
};

@ -0,0 +1,30 @@
<script>
import Echo from './Echo.svelte';
export let reads = {};
export let _0 = '0';
export let _1 = '1';
export let _2 = '2';
$: bar = read(_0, '_0') + ':' + read(_1, '_1');
const read = (value, label) => {
if (!reads[label]) reads[label] = 0;
reads[label] += 1;
return value;
};
</script>
<Echo
let:dummy
d33={_1}
d32={_2}
>
<p>{bar}</p>
<p>{dummy}</p>
<p>{_0}</p>
<p>{_1}</p>
<p>{_2}</p>
</Echo>

@ -0,0 +1,5 @@
<script>
export let dummy;
</script>
<slot dummy={dummy}></slot>

@ -0,0 +1,124 @@
export default {
html: `
<p>0</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p>20</p>
<p>21</p>
<p>22</p>
<p>23</p>
<p>24</p>
<p>25</p>
<p>26</p>
<p>27</p>
<p>28</p>
<p>29</p>
<p>30</p>
<p>31</p>
<p>32</p>
<p>33</p>
<p>34</p>
<p>35</p>
<p>36</p>
<p>37</p>
<p>38</p>
<p>39</p>
<p>40</p>
<p>5:36</p>
<p>6:37</p>
<p>38</p>
<p>0</p>
`,
test({ assert, component, target }) {
component.reads = {};
component._0 = 'a';
component._30 = 'b';
component._31 = 'c';
component._32 = 'd';
component._40 = 'e';
component._5 = 'f';
component._6 = 'g';
component._36 = 'h';
component._37 = 'i';
assert.htmlEqual(target.innerHTML, `
<p>a</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>f</p>
<p>g</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
<p>11</p>
<p>12</p>
<p>13</p>
<p>14</p>
<p>15</p>
<p>16</p>
<p>17</p>
<p>18</p>
<p>19</p>
<p>20</p>
<p>21</p>
<p>22</p>
<p>23</p>
<p>24</p>
<p>25</p>
<p>26</p>
<p>27</p>
<p>28</p>
<p>29</p>
<p>b</p>
<p>c</p>
<p>d</p>
<p>33</p>
<p>34</p>
<p>35</p>
<p>h</p>
<p>i</p>
<p>38</p>
<p>39</p>
<p>e</p>
<p>f:h</p>
<p>g:i</p>
<p>38</p>
<p>a</p>
`);
assert.deepEqual(component.reads, {
_0: 1,
_5: 3,
_6: 3,
_30: 1,
_31: 1,
_32: 1,
_36: 3,
_37: 3,
_40: 1
});
}
};

@ -0,0 +1,107 @@
<script>
import Echo from './Echo.svelte';
export let reads = {};
export let _0 = '0';
export let _1 = '1';
export let _2 = '2';
export let _3 = '3';
export let _4 = '4';
export let _5 = '5';
export let _6 = '6';
export let _7 = '7';
export let _8 = '8';
export let _9 = '9';
export let _10 = '10';
export let _11 = '11';
export let _12 = '12';
export let _13 = '13';
export let _14 = '14';
export let _15 = '15';
export let _16 = '16';
export let _17 = '17';
export let _18 = '18';
export let _19 = '19';
export let _20 = '20';
export let _21 = '21';
export let _22 = '22';
export let _23 = '23';
export let _24 = '24';
export let _25 = '25';
export let _26 = '26';
export let _27 = '27';
export let _28 = '28';
export let _29 = '29';
export let _30 = '30';
export let _31 = '31';
export let _32 = '32';
export let _33 = '33';
export let _34 = '34';
export let _35 = '35';
export let _36 = '36';
export let _37 = '37';
export let _38 = '38';
export let _39 = '39';
export let _40 = '40';
$: foo = read(_6, '_6') + ':' + read(_37, '_37');
$: bar = read(_38, '_38');
const read = (value, label) => {
if (!reads[label]) reads[label] = 0;
reads[label] += 1;
return value;
};
</script>
<Echo dummy={_0} let:dummy>
<p>{read(_0, '_0')}</p>
<p>{read(_1, '_1')}</p>
<p>{read(_2, '_2')}</p>
<p>{read(_3, '_3')}</p>
<p>{read(_4, '_4')}</p>
<p>{read(_5, '_5')}</p>
<p>{read(_6, '_6')}</p>
<p>{read(_7, '_7')}</p>
<p>{read(_8, '_8')}</p>
<p>{read(_9, '_9')}</p>
<p>{read(_10, '_10')}</p>
<p>{read(_11, '_11')}</p>
<p>{read(_12, '_12')}</p>
<p>{read(_13, '_13')}</p>
<p>{read(_14, '_14')}</p>
<p>{read(_15, '_15')}</p>
<p>{read(_16, '_16')}</p>
<p>{read(_17, '_17')}</p>
<p>{read(_18, '_18')}</p>
<p>{read(_19, '_19')}</p>
<p>{read(_20, '_20')}</p>
<p>{read(_21, '_21')}</p>
<p>{read(_22, '_22')}</p>
<p>{read(_23, '_23')}</p>
<p>{read(_24, '_24')}</p>
<p>{read(_25, '_25')}</p>
<p>{read(_26, '_26')}</p>
<p>{read(_27, '_27')}</p>
<p>{read(_28, '_28')}</p>
<p>{read(_29, '_29')}</p>
<p>{read(_30, '_30')}</p>
<p>{read(_31, '_31')}</p>
<p>{read(_32, '_32')}</p>
<p>{read(_33, '_33')}</p>
<p>{read(_34, '_34')}</p>
<p>{read(_35, '_35')}</p>
<p>{read(_36, '_36')}</p>
<p>{read(_37, '_37')}</p>
<p>{read(_38, '_38')}</p>
<p>{read(_39, '_39')}</p>
<p>{read(_40, '_40')}</p>
<p>{read(_5, '_5') + ':' + read(_36, '_36')}</p>
<p>{foo}</p>
<p>{bar}</p>
<p>{dummy}</p>
</Echo>

@ -0,0 +1,5 @@
<script>
export let prop
</script>
<slot value={prop} />

@ -0,0 +1,12 @@
export default {
props: {
prop: 'a',
},
html: 'a',
test({ assert, component, target }) {
component.prop = 'b';
assert.htmlEqual( target.innerHTML, 'b' );
}
};

@ -0,0 +1,10 @@
<script>
import Outer from './Outer.svelte'
import Inner from './Inner.svelte'
export let prop
</script>
<Outer {prop} let:value>
<Inner>{value}</Inner>
</Outer>

@ -0,0 +1,8 @@
export default {
html: `
<p>OK</p>
<p>OK</p>
<pre>one</pre>
<pre>two</pre>
`
};

@ -0,0 +1,13 @@
<script>
let a = () => true;
export let data = [{ foo: [{ foo: [{bar: "one"}, {bar: "two"}] }] }];
</script>
{#each data as datum}
{#if datum.foo && a()}
<p>OK</p>
<svelte:self data={datum.foo}/>
{:else}
<pre>{datum.bar}</pre>
{/if}
{/each}

@ -0,0 +1,12 @@
<script>
import {writable} from 'svelte/store';
import Widget from './Widget.svelte';
let a = (writable({}));
let b = () => true;
</script>
<!-- if (reactive && non-reactive) -->
{#if $a || b() }
<Widget></Widget>
{:else}
<pre>fail</pre>
{/if}

@ -0,0 +1,22 @@
export default {
html: `
<input class="input" placeholder="Type here" type="text">
Dirty: false
Valid: false
`,
async test({ assert, target, window }) {
const input = target.querySelector('input');
input.value = 'foo';
const inputEvent = new window.InputEvent('input');
await input.dispatchEvent(inputEvent);
assert.htmlEqual(target.innerHTML, `
<input class="input" placeholder="Type here" type="text">
Dirty: true
Valid: true
`);
},
};

@ -0,0 +1,29 @@
<script>
import { writable } from 'svelte/store';
export function createValidator() {
const { subscribe, set } = writable({ dirty: false, valid: false });
function action(node, binding) {
return {
update(value) {
set({ dirty: true, valid: value !== '' });
}
};
}
return [{ subscribe }, action];
}
const [validity, validate] = createValidator();
let email = null;
</script>
<input class="input"
type="text"
bind:value={email}
placeholder="Type here"
use:validate={email}
/>
Dirty: {$validity.dirty}
Valid: {$validity.valid}

@ -0,0 +1,44 @@
export default {
html: `
<input>
<div></div>
<div>simple</div>
<button>click me</button>
`,
async test({ assert, component, target, window }) {
const input = target.querySelector('input');
const button = target.querySelector('button');
const inputEvent = new window.InputEvent('input');
const clickEvent = new window.MouseEvent('click');
input.value = 'foo';
await input.dispatchEvent(inputEvent);
assert.htmlEqual(target.innerHTML, `
<input>
<div>foo</div>
<div>foo</div>
<button>click me</button>
`);
await button.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<input>
<div>foo</div>
<div>clicked</div>
<button>click me</button>
`);
input.value = 'bar';
await input.dispatchEvent(inputEvent);
assert.htmlEqual(target.innerHTML, `
<input>
<div>bar</div>
<div>bar</div>
<button>click me</button>
`);
}
};

@ -0,0 +1,20 @@
<script>
import {writable} from 'svelte/store';
function action(node, binding) {
return {
update: (value) => s.set(value),
}
}
let s = writable("simple");
let v = "";
function click() {
s.set('clicked');
}
</script>
<input bind:value={v} use:action={v}>
<div>{v}</div>
<div>{$s}</div>
<button on:click={click}>click me</button>

@ -0,0 +1,44 @@
export default {
html: `
<div></div>
<div>simple</div>
<input>
<button>click me</button>
`,
async test({ assert, component, target, window }) {
const input = target.querySelector('input');
const button = target.querySelector('button');
const inputEvent = new window.InputEvent('input');
const clickEvent = new window.MouseEvent('click');
input.value = 'foo';
await input.dispatchEvent(inputEvent);
assert.htmlEqual(target.innerHTML, `
<div>foo</div>
<div>foo</div>
<input>
<button>click me</button>
`);
await button.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>foo</div>
<div>clicked</div>
<input>
<button>click me</button>
`);
input.value = 'bar';
await input.dispatchEvent(inputEvent);
assert.htmlEqual(target.innerHTML, `
<div>bar</div>
<div>bar</div>
<input>
<button>click me</button>
`);
}
};

@ -0,0 +1,20 @@
<script>
import {writable} from 'svelte/store';
function action(node, binding) {
return {
update: (value) => s.set(value),
}
}
let s = writable("simple");
let v = "";
function click() {
s.set('clicked');
}
</script>
<div>{v}</div>
<div>{$s}</div>
<input bind:value={v} use:action={v}>
<button on:click={click}>click me</button>

@ -8,7 +8,7 @@ export default {
module: false,
mutated: false,
reassigned: false,
referenced: false,
referenced: true,
referenced_from_script: false,
writable: true
},

Loading…
Cancel
Save