Merge branch 'master' into pr/3625

pull/3625/head
Conduitry 6 years ago
commit 71c397d3c1

@ -2,7 +2,22 @@
## Unreleased
* Allow `<svelte:self>` to be used in a slot ([#2798](https://github.com/sveltejs/svelte/issues/2798))
* Expose object of unknown props in `$$restProps` ([#2930](https://github.com/sveltejs/svelte/issues/2930))
* Disallow binding directly to `const` variables ([#4479](https://github.com/sveltejs/svelte/issues/4479))
* Fix updating keyed `{#each}` blocks with `{:else}` ([#4536](https://github.com/sveltejs/svelte/issues/4536), [#4549](https://github.com/sveltejs/svelte/issues/4549))
* Fix hydration of top-level content ([#4542](https://github.com/sveltejs/svelte/issues/4542))
## 3.19.2
* In `dev` mode, display a runtime warning when a component is passed an unexpected slot ([#1020](https://github.com/sveltejs/svelte/issues/1020), [#1447](https://github.com/sveltejs/svelte/issues/1447))
* In `vars` array, correctly indicate whether `module` variables are `mutated` or `reassigned` ([#3215](https://github.com/sveltejs/svelte/issues/3215))
* Fix spread props not updating in certain situations ([#3521](https://github.com/sveltejs/svelte/issues/3521), [#4480](https://github.com/sveltejs/svelte/issues/4480))
* Use the fallback content for slots if they are passed only whitespace ([#4092](https://github.com/sveltejs/svelte/issues/4092))
* Fix bitmask overflow for `{#if}` blocks ([#4263](https://github.com/sveltejs/svelte/issues/4263))
* In `dev` mode, check for unknown props even if the component has no writable props ([#4323](https://github.com/sveltejs/svelte/issues/4323))
* Exclude global variables from `$capture_state` ([#4463](https://github.com/sveltejs/svelte/issues/4463))
* Fix bitmask overflow for slots ([#4481](https://github.com/sveltejs/svelte/issues/4481))
## 3.19.1

@ -32,6 +32,8 @@ cd svelte
npm install
```
> Do not use Yarn to install the dependencies, as the specific package versions in `package-lock.json` are used to build and test Svelte.
> Many tests depend on newlines being preserved as `<LF>`. On Windows, you can ensure this by cloning with:
> ```bash
> git -c core.autocrlf=false clone https://github.com/sveltejs/svelte.git

119
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.19.1",
"version": "3.19.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -137,6 +137,12 @@
}
}
},
"@tootallnate/once": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz",
"integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==",
"dev": true
},
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@ -274,9 +280,9 @@
"dev": true
},
"acorn": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
"integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
"dev": true
},
"acorn-globals": {
@ -609,16 +615,16 @@
}
},
"codecov": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/codecov/-/codecov-3.5.0.tgz",
"integrity": "sha512-/OsWOfIHaQIr7aeZ4pY0UC1PZT6kimoKFOFYFNb6wxo3iw12nRrh+mNGH72rnXxNsq6SGfesVPizm/6Q3XqcFQ==",
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/codecov/-/codecov-3.6.5.tgz",
"integrity": "sha512-v48WuDMUug6JXwmmfsMzhCHRnhUf8O3duqXvltaYJKrO1OekZWpB/eH6iIoaxMl8Qli0+u3OxptdsBOYiD7VAQ==",
"dev": true,
"requires": {
"argv": "^0.0.2",
"ignore-walk": "^3.0.1",
"js-yaml": "^3.13.1",
"teeny-request": "^3.11.3",
"urlgrey": "^0.4.4"
"argv": "0.0.2",
"ignore-walk": "3.0.3",
"js-yaml": "3.13.1",
"teeny-request": "6.0.1",
"urlgrey": "0.4.4"
}
},
"color-convert": {
@ -1670,6 +1676,37 @@
"whatwg-encoding": "^1.0.1"
}
},
"http-proxy-agent": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
"integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
"dev": true,
"requires": {
"@tootallnate/once": "1",
"agent-base": "6",
"debug": "4"
},
"dependencies": {
"agent-base": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz",
"integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==",
"dev": true,
"requires": {
"debug": "4"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
}
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@ -1707,9 +1744,9 @@
"dev": true
},
"ignore-walk": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"dev": true,
"requires": {
"minimatch": "^3.0.4"
@ -3362,6 +3399,15 @@
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
"dev": true
},
"stream-events": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
"integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
"dev": true,
"requires": {
"stubs": "^3.0.0"
}
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
@ -3409,6 +3455,12 @@
"integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
"dev": true
},
"stubs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
"integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=",
"dev": true
},
"sucrase": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.10.1.tgz",
@ -3449,14 +3501,43 @@
}
},
"teeny-request": {
"version": "3.11.3",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz",
"integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.1.tgz",
"integrity": "sha512-TAK0c9a00ELOqLrZ49cFxvPVogMUFaWY8dUsQc/0CuQPGF+BOxOQzXfE413BAk2kLomwNplvdtMpeaeGWmoc2g==",
"dev": true,
"requires": {
"https-proxy-agent": "^2.2.1",
"http-proxy-agent": "^4.0.0",
"https-proxy-agent": "^4.0.0",
"node-fetch": "^2.2.0",
"stream-events": "^1.0.5",
"uuid": "^3.3.2"
},
"dependencies": {
"agent-base": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
"integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==",
"dev": true
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"https-proxy-agent": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
"integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
"dev": true,
"requires": {
"agent-base": "5",
"debug": "4"
}
}
}
},
"test-exclude": {

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

@ -58,6 +58,17 @@ Or they can *be* JavaScript expressions.
---
Boolean attributes are included on the element if their value is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) and excluded if it's [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy).
All other attributes are included unless their value is [nullish](https://developer.mozilla.org/en-US/docs/Glossary/Nullish) (`null` or `undefined`).
```html
<input required={false} placeholder="This input field is not required">
<div title={null}>This div has no title attribute</div>
```
---
An expression might include characters that would cause syntax highlighting to fail in regular HTML, so quoting the value is permitted. The quotes do not affect how the value is parsed:
```html
@ -102,6 +113,15 @@ An element or component can have multiple spread attributes, interspersed with r
<Widget {...$$props}/>
```
---
*`$$restProps`* contains only the props which are *not* declared with `export`. It can be used to pass down other unknown attributes to an element in a component.
```html
<input {...$$restProps}>
```
---
### Text expressions

@ -974,7 +974,7 @@ app.count += 1;
Svelte components can also be compiled to custom elements (aka web components) using the `customElement: true` compiler option. You should specify a tag name for the component using the `<svelte:options>` [element](docs#svelte_options).
```html
<svelte:options tag="my-element">
<svelte:options tag="my-element" />
<script>
export let name = 'world';

@ -113,7 +113,8 @@ const {
* `module` is `true` if the value is declared in a `context="module"` script
* `mutated` is `true` if the value's properties are assigned to inside the component
* `reassigned` is `true` if the value is reassigned inside the component
* `referenced` is `true` if the value is used outside the declaration
* `referenced` is `true` if the value is used in the template
* `referenced_from_script` is `true` if the value is used in the `<script>` outside the declaration
* `writable` is `true` if the value was declared with `let` or `var` (but not `const`, `class` or `function`)
* `stats` is an object used by the Svelte developer team for diagnosing the compiler. Avoid relying on it to stay the same!
@ -144,6 +145,7 @@ compiled: {
mutated: boolean,
reassigned: boolean,
referenced: boolean,
referenced_from_script: boolean,
writable: boolean
}>,
stats: {

@ -1358,9 +1358,9 @@
}
},
"acorn": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz",
"integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
"dev": true
},
"ansi-colors": {

@ -28,6 +28,7 @@ import { Node, ImportDeclaration, Identifier, Program, ExpressionStatement, Assi
import add_to_set from './utils/add_to_set';
import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x, b } from 'code-red';
import { is_reserved_keyword } from './utils/reserved_keywords';
interface ComponentOptions {
namespace?: string;
@ -185,7 +186,7 @@ export default class Component {
if (variable) {
variable.referenced = true;
} else if (name === '$$props') {
} else if (is_reserved_keyword(name)) {
this.add_var({
name,
injected: true,
@ -239,7 +240,7 @@ export default class Component {
const program: any = { type: 'Program', body: result.js };
walk(program, {
enter: (node, parent, key) => {
enter: (node: Node, parent: Node, key) => {
if (node.type === 'Identifier') {
if (node.name[0] === '@') {
if (node.name[1] === '_') {
@ -526,7 +527,7 @@ export default class Component {
if (!script) return;
walk(script.content, {
enter(node) {
enter(node: Node) {
if (node.type === 'LabeledStatement' && node.label.name === '$') {
component.warn(node as any, {
code: 'module-script-reactive-declaration',
@ -649,7 +650,7 @@ export default class Component {
reassigned: true,
initialised: true,
});
} else if (name === '$$props') {
} else if (is_reserved_keyword(name)) {
this.add_var({
name,
injected: true,
@ -715,7 +716,7 @@ export default class Component {
let scope_updated = false;
walk(content, {
enter(node, parent, prop, index) {
enter(node: Node, parent, prop, index) {
if (map.has(node)) {
scope = map.get(node);
}
@ -741,7 +742,7 @@ export default class Component {
component.warn_on_undefined_store_value_references(node, parent, scope);
},
leave(node) {
leave(node: Node) {
// do it on leave, to prevent infinite loop
if (component.compile_options.dev && component.compile_options.loopGuardTimeout > 0) {
const to_replace_for_loop_protect = component.loop_protect(node, scope, component.compile_options.loopGuardTimeout);
@ -780,12 +781,12 @@ export default class Component {
const component = this;
const { content } = script;
const { instance_scope, instance_scope_map: map } = this;
const { instance_scope, module_scope, instance_scope_map: map } = this;
let scope = instance_scope;
walk(content, {
enter(node, parent) {
enter(node: Node, parent: Node) {
if (map.has(node)) {
scope = map.get(node);
}
@ -797,7 +798,12 @@ export default class Component {
const deep = assignee.type === 'MemberExpression';
names.forEach(name => {
if (scope.find_owner(name) === instance_scope) {
const scope_owner = scope.find_owner(name);
if (
scope_owner !== null
? scope_owner === instance_scope
: module_scope && module_scope.has(name)
) {
const variable = component.var_lookup.get(name);
variable[deep ? 'mutated' : 'reassigned'] = true;
}
@ -813,7 +819,7 @@ export default class Component {
}
},
leave(node) {
leave(node: Node) {
if (map.has(node)) {
scope = scope.parent;
}
@ -881,7 +887,7 @@ export default class Component {
let scope = instance_scope;
walk(this.ast.instance.content, {
enter(node, parent, key, index) {
enter(node: Node, parent, key, index) {
if (/Function/.test(node.type)) {
return this.skip();
}
@ -958,7 +964,7 @@ export default class Component {
}
},
leave(node, parent, _key, index) {
leave(node: Node, parent, _key, index) {
if (map.has(node)) {
scope = scope.parent;
}
@ -1059,7 +1065,7 @@ export default class Component {
walking.add(fn_declaration);
walk(fn_declaration, {
enter(node, parent) {
enter(node: Node, parent) {
if (!hoistable) return this.skip();
if (map.has(node)) {
@ -1107,7 +1113,7 @@ export default class Component {
}
},
leave(node) {
leave(node: Node) {
if (map.has(node)) {
scope = scope.parent;
}
@ -1150,7 +1156,7 @@ export default class Component {
const map = this.instance_scope_map;
walk(node.body, {
enter(node, parent) {
enter(node: Node, parent) {
if (map.has(node)) {
scope = map.get(node);
}
@ -1190,7 +1196,7 @@ export default class Component {
}
},
leave(node) {
leave(node: Node) {
if (map.has(node)) {
scope = scope.parent;
}
@ -1271,7 +1277,7 @@ export default class Component {
warn_if_undefined(name: string, node, template_scope: TemplateScope) {
if (name[0] === '$') {
if (name === '$' || name[1] === '$' && name !== '$$props') {
if (name === '$' || name[1] === '$' && !is_reserved_keyword(name)) {
this.error(node, {
code: 'illegal-global',
message: `${name} is an illegal variable name`
@ -1280,7 +1286,7 @@ export default class Component {
this.has_reactive_assignments = true; // TODO does this belong here?
if (name === '$$props') return;
if (is_reserved_keyword(name)) return;
name = name.slice(1);
}

@ -72,6 +72,11 @@ export default class Binding extends Node {
});
variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true;
if (info.expression.type === 'Identifier' && !variable.writable) component.error(this.expression.node, {
code: 'invalid-binding',
message: 'Cannot bind to a variable which is not writable',
});
}
const type = parent.get_static_attribute_value('type');

@ -1,7 +1,7 @@
import Node from './shared/Node';
import Component from '../Component';
import { walk } from 'estree-walker';
import { Identifier } from 'estree';
import { BasePattern, Identifier } from 'estree';
const applicable = new Set(['Identifier', 'ObjectExpression', 'ArrayExpression', 'Property']);
@ -22,7 +22,7 @@ export default class Let extends Node {
this.value = info.expression;
walk(info.expression, {
enter(node) {
enter(node: Identifier|BasePattern) {
if (!applicable.has(node.type)) {
component.error(node as any, {
code: 'invalid-let',
@ -31,16 +31,16 @@ export default class Let extends Node {
}
if (node.type === 'Identifier') {
names.push(node.name);
names.push((node as Identifier).name);
}
// slightly unfortunate hack
if (node.type === 'ArrayExpression') {
(node as any).type = 'ArrayPattern';
node.type = 'ArrayPattern';
}
if (node.type === 'ObjectExpression') {
(node as any).type = 'ObjectPattern';
node.type = 'ObjectPattern';
}
}
});

@ -3,6 +3,18 @@ import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces';
// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
// list is almost certainly very incomplete)
const elements_without_text = new Set([
'audio',
'datalist',
'dl',
'optgroup',
'select',
'video',
]);
export default class Text extends Node {
type: 'Text';
data: string;
@ -13,4 +25,21 @@ export default class Text extends Node {
this.data = info.data;
this.synthetic = info.synthetic || false;
}
should_skip() {
if (/\S/.test(this.data)) return false;
const parent_element = this.find_nearest(/(?:Element|InlineComponent|Head)/);
if (!parent_element) return false;
if (parent_element.type === 'Head') return true;
if (parent_element.type === 'InlineComponent') return parent_element.children.length === 1 && this === parent_element.children[0];
// svg namespace exclusions
if (/svg$/.test(parent_element.namespace)) {
if (this.prev && this.prev.type === "Element" && this.prev.name === "tspan") return false;
}
return parent_element.namespace || elements_without_text.has(parent_element.name);
}
}

@ -13,6 +13,7 @@ import { b } from 'code-red';
import { invalidate } from '../../render_dom/invalidate';
import { Node, FunctionExpression, Identifier } from 'estree';
import { TemplateNode } from '../../../interfaces';
import { is_reserved_keyword } from '../../utils/reserved_keywords';
type Owner = Wrapper | TemplateNode;
@ -143,7 +144,7 @@ export default class Expression {
}
},
leave(node) {
leave(node: Node) {
if (map.has(node)) {
scope = scope.parent;
}
@ -158,7 +159,7 @@ export default class Expression {
dynamic_dependencies() {
return Array.from(this.dependencies).filter(name => {
if (this.template_scope.is_let(name)) return true;
if (name === '$$props') return true;
if (is_reserved_keyword(name)) return true;
const variable = this.component.var_lookup.get(name);
return is_dynamic(variable);
@ -338,7 +339,7 @@ export default class Expression {
});
}
return (this.manipulated = node);
return (this.manipulated = node as Node);
}
}
@ -355,7 +356,7 @@ function get_function_name(_node, parent) {
}
function is_contextual(component: Component, scope: TemplateScope, name: string) {
if (name === '$$props') return true;
if (is_reserved_keyword(name)) return true;
// if it's a name below root scope, it's contextual
if (!scope.is_top_level(name)) return true;

@ -412,8 +412,7 @@ export default class Block {
}
has_content() {
return this.renderer.options.dev ||
this.first ||
return this.first ||
this.event_listeners.length > 0 ||
this.chunks.intro.length > 0 ||
this.chunks.outro.length > 0 ||

@ -5,6 +5,7 @@ import FragmentWrapper from './wrappers/Fragment';
import { x } from 'code-red';
import { Node, Identifier, MemberExpression, Literal, Expression, BinaryExpression } from 'estree';
import flatten_reference from '../utils/flatten_reference';
import { reserved_keywords } from '../utils/reserved_keywords';
interface ContextMember {
name: string;
@ -50,9 +51,11 @@ export default class Renderer {
// ensure store values are included in context
component.vars.filter(v => v.subscribable).forEach(v => this.add_to_context(`$${v.name}`));
if (component.var_lookup.has('$$props')) {
this.add_to_context('$$props');
reserved_keywords.forEach(keyword => {
if (component.var_lookup.has(keyword)) {
this.add_to_context(keyword);
}
});
if (component.slots.size > 0) {
this.add_to_context('$$scope');
@ -230,6 +233,7 @@ export default class Renderer {
return bitmask;
};
// TODO: context-overflow make it less gross
return {
// Using a ParenthesizedExpression allows us to create
// the expression lazily. TODO would be better if

@ -71,14 +71,24 @@ export default function dom(
}
const uses_props = component.var_lookup.has('$$props');
const $$props = uses_props ? `$$new_props` : `$$props`;
const uses_rest = component.var_lookup.has('$$restProps');
const $$props = uses_props || uses_rest ? `$$new_props` : `$$props`;
const props = component.vars.filter(variable => !variable.module && variable.export_name);
const writable_props = props.filter(variable => variable.writable);
const set = (uses_props || writable_props.length > 0 || component.slots.size > 0)
const omit_props_names = component.get_unique_name('omit_props_names');
const compute_rest = x`@compute_rest_props($$props, ${omit_props_names.name})`;
const rest = uses_rest ? b`
const ${omit_props_names.name} = [${props.map(prop => `"${prop.export_name}"`).join(',')}];
let $$restProps = ${compute_rest};
` : null;
const set = (uses_props || uses_rest || writable_props.length > 0 || component.slots.size > 0)
? x`
${$$props} => {
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`)}
${uses_rest && !uses_props && x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`}
${uses_rest && renderer.invalidate('$$restProps', x`$$restProps = ${compute_rest}`)}
${writable_props.map(prop =>
b`if ('${prop.export_name}' in ${$$props}) ${renderer.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.export_name}`)};`
)}
@ -200,7 +210,7 @@ export default function dom(
let execution_context: Node | null = null;
walk(component.ast.instance.content, {
enter(node) {
enter(node: Node) {
if (map.has(node)) {
scope = map.get(node) as Scope;
@ -212,7 +222,7 @@ export default function dom(
}
},
leave(node) {
leave(node: Node) {
if (map.has(node)) {
scope = scope.parent;
}
@ -259,9 +269,12 @@ export default function dom(
inject_state;
if (has_invalidate) {
args.push(x`$$props`, x`$$invalidate`);
} else if (component.compile_options.dev) {
// $$props arg is still needed for unknown prop check
args.push(x`$$props`);
}
const has_create_fragment = block.has_content();
const has_create_fragment = component.compile_options.dev || block.has_content();
if (has_create_fragment) {
body.push(b`
function create_fragment(#ctx) {
@ -300,6 +313,7 @@ export default function dom(
const initial_context = renderer.context.slice(0, i + 1);
const has_definition = (
component.compile_options.dev ||
(instance_javascript && instance_javascript.length > 0) ||
filtered_props.length > 0 ||
uses_props ||
@ -337,20 +351,20 @@ export default function dom(
component.reactive_declarations.forEach(d => {
const dependencies = Array.from(d.dependencies);
const uses_props = !!dependencies.find(n => n === '$$props');
const uses_rest_or_props = !!dependencies.find(n => n === '$$props' || n === '$$restProps');
const writable = dependencies.filter(n => {
const variable = component.var_lookup.get(n);
return variable && (variable.export_name || variable.mutated || variable.reassigned);
});
const condition = !uses_props && writable.length > 0 && renderer.dirty(writable, true);
const condition = !uses_rest_or_props && writable.length > 0 && renderer.dirty(writable, true);
let statement = d.node; // TODO remove label (use d.node.body) if it's not referenced
if (condition) statement = b`if (${condition}) { ${statement} }`[0] as Statement;
if (condition || uses_props) {
if (condition || uses_rest_or_props) {
reactive_declarations.push(statement);
} else {
fixed_reactive_declarations.push(statement);
@ -379,7 +393,7 @@ export default function dom(
});
let unknown_props_check;
if (component.compile_options.dev && !component.var_lookup.has('$$props') && writable_props.length) {
if (component.compile_options.dev && !(uses_props || uses_rest)) {
unknown_props_check = b`
const writable_props = [${writable_props.map(prop => x`'${prop.export_name}'`)}];
@_Object.keys($$props).forEach(key => {
@ -398,6 +412,8 @@ export default function dom(
body.push(b`
function ${definition}(${args}) {
${rest}
${reactive_store_declarations}
${reactive_store_subscriptions}
@ -408,7 +424,8 @@ export default function dom(
${unknown_props_check}
${component.slots.size ? b`let { $$slots = {}, $$scope } = $$props;` : null}
${component.slots.size || component.compile_options.dev ? b`let { $$slots = {}, $$scope } = $$props;` : null}
${component.compile_options.dev && b`@validate_slots('${component.tag}', $$slots, [${[...component.slots.keys()].map(key => `'${key}'`).join(',')}]);`}
${renderer.binding_groups.length > 0 && b`const $$binding_groups = [${renderer.binding_groups.map(_ => x`[]`)}];`}
@ -468,7 +485,7 @@ export default function dom(
@insert(options.target, this, options.anchor);
}
${(props.length > 0 || uses_props) && b`
${(props.length > 0 || uses_props || uses_rest) && b`
if (options.props) {
this.$set(options.props);
@flush();

@ -62,6 +62,8 @@ export default class EachBlockWrapper extends Wrapper {
context_props: Array<Node | Node[]>;
index_name: Identifier;
updates: Array<Node | Node[]> = [];
dependencies: Set<string>;
var: Identifier = { type: 'Identifier', name: 'each' };
@ -235,6 +237,12 @@ export default class EachBlockWrapper extends Wrapper {
update_mount_node
};
const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only
this.node.expression.dynamic_dependencies().forEach((dependency: string) => {
all_dependencies.add(dependency);
});
this.dependencies = all_dependencies;
if (this.node.key) {
this.render_keyed(args);
} else {
@ -291,7 +299,7 @@ export default class EachBlockWrapper extends Wrapper {
`);
if (this.else.block.has_update_method) {
block.chunks.update.push(b`
this.updates.push(b`
if (!${this.vars.data_length} && ${each_block_else}) {
${each_block_else}.p(#ctx, #dirty);
} else if (!${this.vars.data_length}) {
@ -304,7 +312,7 @@ export default class EachBlockWrapper extends Wrapper {
}
`);
} else {
block.chunks.update.push(b`
this.updates.push(b`
if (${this.vars.data_length}) {
if (${each_block_else}) {
${each_block_else}.d(1);
@ -323,6 +331,14 @@ export default class EachBlockWrapper extends Wrapper {
`);
}
if (this.updates.length) {
block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(all_dependencies))}) {
${this.updates}
}
`);
}
this.fragment.render(this.block, null, x`#nodes` as Identifier);
if (this.else) {
@ -415,14 +431,8 @@ export default class EachBlockWrapper extends Wrapper {
? `@outro_and_destroy_block`
: `@destroy_block`;
const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only
this.node.expression.dynamic_dependencies().forEach((dependency: string) => {
all_dependencies.add(dependency);
});
if (all_dependencies.size) {
block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(all_dependencies))}) {
if (this.dependencies.size) {
this.updates.push(b`
const ${this.vars.each_block_value} = ${snippet};
${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`}
@ -432,7 +442,6 @@ export default class EachBlockWrapper extends Wrapper {
${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context});
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`}
${this.block.has_outros && b`@check_outros();`}
}
`);
}
@ -504,12 +513,7 @@ export default class EachBlockWrapper extends Wrapper {
}
`);
const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only
this.node.expression.dynamic_dependencies().forEach((dependency: string) => {
all_dependencies.add(dependency);
});
if (all_dependencies.size) {
if (this.dependencies.size) {
const has_transitions = !!(this.block.has_intro_method || this.block.has_outro_method);
const for_loop_body = this.block.has_update_method
@ -588,11 +592,7 @@ export default class EachBlockWrapper extends Wrapper {
${remove_old_blocks}
`;
block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(all_dependencies))}) {
${update}
}
`);
this.updates.push(update);
}
if (this.block.has_outros) {

@ -17,6 +17,7 @@ import { INode } from '../../nodes/interfaces';
import Renderer from '../Renderer';
import Block from '../Block';
import { trim_start, trim_end } from '../../../utils/trim';
import { link } from '../../../utils/link';
import { Identifier } from 'estree';
const wrappers = {
@ -38,11 +39,6 @@ const wrappers = {
Window
};
function link(next: Wrapper, prev: Wrapper) {
prev.next = next;
if (next) next.prev = prev;
}
function trimmable_at(child: INode, next_sibling: Wrapper): boolean {
// Whitespace is trimmable if one of the following is true:
// The child and its sibling share a common nearest each block (not at an each block boundary)

@ -9,7 +9,7 @@ import FragmentWrapper from './Fragment';
import { b, x } from 'code-red';
import { walk } from 'estree-walker';
import { is_head } from './shared/is_head';
import { Identifier, Node } from 'estree';
import { Identifier, Node, UnaryExpression } from 'estree';
function is_else_if(node: ElseBlock) {
return (
@ -288,7 +288,7 @@ export default class IfBlockWrapper extends Wrapper {
}
block.chunks.init.push(b`
let ${current_block_type} = ${select_block_type}(#ctx, -1);
let ${current_block_type} = ${select_block_type}(#ctx, ${this.get_initial_dirty_bit()});
let ${name} = ${get_block};
`);
@ -407,12 +407,12 @@ export default class IfBlockWrapper extends Wrapper {
if (has_else) {
block.chunks.init.push(b`
${current_block_type_index} = ${select_block_type}(#ctx, -1);
${current_block_type_index} = ${select_block_type}(#ctx, ${this.get_initial_dirty_bit()});
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#ctx);
`);
} else {
block.chunks.init.push(b`
if (~(${current_block_type_index} = ${select_block_type}(#ctx, -1))) {
if (~(${current_block_type_index} = ${select_block_type}(#ctx, ${this.get_initial_dirty_bit()}))) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#ctx);
}
`);
@ -587,4 +587,21 @@ export default class IfBlockWrapper extends Wrapper {
`);
}
}
get_initial_dirty_bit() {
const _this = this;
// TODO: context-overflow make it less gross
const val: UnaryExpression = x`-1` as UnaryExpression;
return {
get type() {
return _this.renderer.context_overflow ? 'ArrayExpression' : 'UnaryExpression';
},
// as [-1]
elements: [val],
// as -1
operator: val.operator,
prefix: val.prefix,
argument: val.argument,
};
}
}

@ -138,11 +138,27 @@ export default class InlineComponentWrapper extends Wrapper {
const statements: Array<Node | Node[]> = [];
const updates: Array<Node | Node[]> = [];
if (this.fragment) {
this.renderer.add_to_context('$$scope', true);
const default_slot = this.slots.get('default');
this.fragment.nodes.forEach((child) => {
child.render(default_slot.block, null, x`#nodes` as unknown as Identifier);
});
}
let props;
const name_changes = block.get_unique_name(`${name.name}_changes`);
const uses_spread = !!this.node.attributes.find(a => a.is_spread);
// removing empty slot
for (const slot of this.slots.keys()) {
if (!this.slots.get(slot).block.has_content()) {
this.slots.delete(slot);
}
}
const initial_props = this.slots.size > 0
? [
p`$$slots: {
@ -172,15 +188,6 @@ export default class InlineComponentWrapper extends Wrapper {
}
}
if (this.fragment) {
this.renderer.add_to_context('$$scope', true);
const default_slot = this.slots.get('default');
this.fragment.nodes.forEach((child) => {
child.render(default_slot.block, null, x`#nodes` as unknown as Identifier);
});
}
if (component.compile_options.dev) {
// TODO this is a terrible hack, but without it the component
// will complain that options.target is missing. This would
@ -224,7 +231,9 @@ export default class InlineComponentWrapper extends Wrapper {
const condition = dependencies.size > 0 && (dependencies.size !== all_dependencies.size)
? renderer.dirty(Array.from(dependencies))
: null;
const unchanged = dependencies.size === 0;
let change_object;
if (attr.is_spread) {
const value = attr.expression.manipulate(block);
initial_props.push(value);
@ -233,13 +242,20 @@ export default class InlineComponentWrapper extends Wrapper {
if (attr.expression.node.type !== 'ObjectExpression') {
value_object = x`@get_spread_object(${value})`;
}
changes.push(condition ? x`${condition} && ${value_object}` : value_object);
change_object = value_object;
} else {
const obj = x`{ ${name}: ${attr.get_value(block)} }`;
initial_props.push(obj);
changes.push(condition ? x`${condition} && ${obj}` : x`${levels}[${i}]`);
change_object = obj;
}
changes.push(
unchanged
? x`${levels}[${i}]`
: condition
? x`${condition} && ${change_object}`
: change_object
);
});
block.chunks.init.push(b`

@ -5,36 +5,6 @@ import Wrapper from './shared/Wrapper';
import { x } from 'code-red';
import { Identifier } from 'estree';
// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
// list is almost certainly very incomplete)
const elements_without_text = new Set([
'audio',
'datalist',
'dl',
'optgroup',
'select',
'video',
]);
// TODO this should probably be in Fragment
function should_skip(node: Text) {
if (/\S/.test(node.data)) return false;
const parent_element = node.find_nearest(/(?:Element|InlineComponent|Head)/);
if (!parent_element) return false;
if (parent_element.type === 'Head') return true;
if (parent_element.type === 'InlineComponent') return parent_element.children.length === 1 && node === parent_element.children[0];
// svg namespace exclusions
if (/svg$/.test(parent_element.namespace)) {
if (node.prev && node.prev.type === "Element" && node.prev.name === "tspan") return false;
}
return parent_element.namespace || elements_without_text.has(parent_element.name);
}
export default class TextWrapper extends Wrapper {
node: Text;
data: string;
@ -50,7 +20,7 @@ export default class TextWrapper extends Wrapper {
) {
super(renderer, block, parent, node);
this.skip = should_skip(this.node);
this.skip = this.node.should_skip();
this.data = data;
this.var = (this.skip ? null : x`t`) as unknown as Identifier;
}

@ -63,7 +63,7 @@ export function get_slot_definition(block: Block, scope: TemplateScope, lets: Le
const { context_lookup } = block.renderer;
// i am well aware that this code is gross
// TODO make it less gross
// TODO: context-overflow make it less gross
const changes = {
type: 'ParenthesizedExpression',
get expression() {

@ -6,10 +6,14 @@ import Renderer, { RenderOptions } from '../Renderer';
import Element from '../../nodes/Element';
import { x } from 'code-red';
import Expression from '../../nodes/shared/Expression';
import remove_whitespace_children from './utils/remove_whitespace_children';
export default function(node: Element, renderer: Renderer, options: RenderOptions & {
slot_scopes: Map<any, any>;
}) {
const children = remove_whitespace_children(node.children, node.next);
// awkward special case
let node_contents;
@ -133,7 +137,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
if (node_contents !== undefined) {
if (contenteditable) {
renderer.push();
renderer.render(node.children, options);
renderer.render(children, options);
const result = renderer.pop();
renderer.add_expression(x`($$value => $$value === void 0 ? ${result} : $$value)(${node_contents})`);
@ -145,7 +149,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
renderer.add_string(`</${node.name}>`);
}
} else if (slot && nearest_inline_component) {
renderer.render(node.children, options);
renderer.render(children, options);
if (!is_void(node.name)) {
renderer.add_string(`</${node.name}>`);
@ -163,10 +167,11 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
output: renderer.pop()
});
} else {
renderer.render(node.children, options);
renderer.render(children, options);
if (!is_void(node.name)) {
renderer.add_string(`</${node.name}>`);
}
}
}

@ -2,6 +2,7 @@ import { string_literal } from '../../utils/stringify';
import Renderer, { RenderOptions } from '../Renderer';
import { get_slot_scope } from './shared/get_slot_scope';
import InlineComponent from '../../nodes/InlineComponent';
import remove_whitespace_children from './utils/remove_whitespace_children';
import { p, x } from 'code-red';
function get_prop_value(attribute) {
@ -67,12 +68,14 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
const slot_fns = [];
if (node.children.length) {
const children = remove_whitespace_children(node.children, node.next);
if (children.length) {
const slot_scopes = new Map();
renderer.push();
renderer.render(node.children, Object.assign({}, options, {
renderer.render(children, Object.assign({}, options, {
slot_scopes
}));
@ -82,9 +85,11 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
});
slot_scopes.forEach(({ input, output }, name) => {
if (!is_empty_template_literal(output)) {
slot_fns.push(
p`${name}: (${input}) => ${output}`
);
}
});
}
@ -94,3 +99,11 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
renderer.add_expression(x`@validate_component(${expression}, "${node.name}").$$render($$result, ${props}, ${bindings}, ${slots})`);
}
function is_empty_template_literal(template_literal) {
return (
template_literal.expressions.length === 0 &&
template_literal.quasis.length === 1 &&
template_literal.quasis[0].value.raw === ""
);
}

@ -0,0 +1,73 @@
import { INode } from '../../../nodes/interfaces';
import { trim_end, trim_start } from '../../../../utils/trim';
import { link } from '../../../../utils/link';
// similar logic from `compile/render_dom/wrappers/Fragment`
// We want to remove trailing whitespace inside an element/component/block,
// *unless* there is no whitespace between this node and its next sibling
export default function remove_whitespace_children(children: INode[], next?: INode): INode[] {
const nodes: INode[] = [];
let last_child: INode;
let i = children.length;
while (i--) {
const child = children[i];
if (child.type === 'Text') {
if (child.should_skip()) {
continue;
}
let { data } = child;
if (nodes.length === 0) {
const should_trim = next
? next.type === 'Text' &&
/^\s/.test(next.data) &&
trimmable_at(child, next)
: !child.has_ancestor('EachBlock');
if (should_trim) {
data = trim_end(data);
if (!data) continue;
}
}
// glue text nodes (which could e.g. be separated by comments) together
if (last_child && last_child.type === 'Text') {
last_child.data = data + last_child.data;
continue;
}
nodes.unshift(child);
link(last_child, last_child = child);
} else {
nodes.unshift(child);
link(last_child, last_child = child);
}
}
const first = nodes[0];
if (first && first.type === 'Text') {
first.data = trim_start(first.data);
if (!first.data) {
first.var = null;
nodes.shift();
if (nodes[0]) {
nodes[0].prev = null;
}
}
}
return nodes;
}
function trimmable_at(child: INode, next_sibling: INode): boolean {
// Whitespace is trimmable if one of the following is true:
// The child and its sibling share a common nearest each block (not at an each block boundary)
// The next sibling's previous node is an each block
return (
next_sibling.find_nearest(/EachBlock/) ===
child.find_nearest(/EachBlock/) || next_sibling.prev.type === 'EachBlock'
);
}

@ -31,6 +31,10 @@ export default function ssr(
{ code: null, map: null } :
component.stylesheet.render(options.filename, true);
const uses_rest = component.var_lookup.has('$$restProps');
const props = component.vars.filter(variable => !variable.module && variable.export_name);
const rest = uses_rest ? b`let $$restProps = @compute_rest_props($$props, [${props.map(prop => `"${prop.export_name}"`).join(',')}]);` : null;
const reactive_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$');
const reactive_store_values = reactive_stores
.map(({ name }) => {
@ -130,6 +134,7 @@ export default function ssr(
return ${literal};`;
const blocks = [
rest,
...reactive_stores.map(({ name }) => {
const store_name = name.slice(1);
const store = component.var_lookup.get(store_name);

@ -0,0 +1,5 @@
export const reserved_keywords = new Set(["$$props", "$$restProps"]);
export function is_reserved_keyword(name) {
return reserved_keywords.has(name);
}

@ -241,7 +241,7 @@ function read_tag_name(parser: Parser) {
while (i--) {
const fragment = parser.stack[i];
if (fragment.type === 'IfBlock' || fragment.type === 'EachBlock') {
if (fragment.type === 'IfBlock' || fragment.type === 'EachBlock' || fragment.type === 'InlineComponent') {
legal = true;
break;
}
@ -250,7 +250,7 @@ function read_tag_name(parser: Parser) {
if (!legal) {
parser.error({
code: `invalid-self-placement`,
message: `<svelte:self> components can only exist inside if-blocks or each-blocks`
message: `<svelte:self> components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components`
}, start);
}

@ -0,0 +1,4 @@
export function link<T extends { next?: T; prev?: T }>(next: T, prev: T) {
prev.next = next;
if (next) next.prev = prev;
}

@ -1,7 +1,7 @@
import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler';
import { current_component, set_current_component } from './lifecycle';
import { blank_object, is_function, run, run_all, noop } from './utils';
import { children } from './dom';
import { children, detach } from './dom';
import { transition_in } from './transitions';
interface Fragment {
@ -146,8 +146,10 @@ export function init(component, options, instance, create_fragment, not_equal, p
if (options.target) {
if (options.hydrate) {
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment!.l(children(options.target));
$$.fragment && $$.fragment!.l(nodes);
nodes.forEach(detach);
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment!.c();

@ -89,6 +89,13 @@ export function validate_each_argument(arg) {
}
}
export function validate_slots(name, slot, keys) {
for (const slot_key of Object.keys(slot)) {
if (!~keys.indexOf(slot_key)) {
console.warn(`<${name}> received an unexpected slot "${slot_key}".`);
}
}
}
type Props = Record<string, any>;
export interface SvelteComponentDev {

@ -83,7 +83,11 @@ export function get_slot_changes(definition, $$scope, dirty, fn) {
if (definition[2] && fn) {
const lets = definition[2](fn(dirty));
if (typeof $$scope.dirty === 'object') {
if ($$scope.dirty === undefined) {
return lets;
}
if (typeof lets === 'object') {
const merged = [];
const len = Math.max($$scope.dirty.length, lets.length);
for (let i = 0; i < len; i += 1) {
@ -105,6 +109,13 @@ export function exclude_internal_props(props) {
return result;
}
export function compute_rest_props(props, keys) {
const rest = {};
keys = new Set(keys);
for (const k in props) if (!keys.has(k) && k[0] !== '$') rest[k] = props[k];
return rest;
}
export function once(fn) {
let ran = false;
return function(this: any, ...args) {

@ -0,0 +1,2 @@
<main>This should be thrown away</main>
<div>hello</div>

@ -0,0 +1 @@
<main>This should be thrown away</main>

@ -14,6 +14,7 @@ import {
space,
subscribe,
text,
validate_slots,
validate_store
} from "svelte/internal";
@ -114,6 +115,9 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
if ("prop" in $$props) $$subscribe_prop($$invalidate(0, prop = $$props.prop));
if ("alias" in $$props) $$invalidate(1, realName = $$props.alias);

@ -12,7 +12,8 @@ import {
safe_not_equal,
set_data_dev,
space,
text
text,
validate_slots
} from "svelte/internal";
const file = undefined;
@ -75,6 +76,9 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
if ("name" in $$props) $$invalidate(0, name = $$props.name);
};

@ -14,7 +14,8 @@ import {
set_data_dev,
space,
text,
validate_each_argument
validate_each_argument,
validate_slots
} from "svelte/internal";
const file = undefined;
@ -179,6 +180,9 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
if ("things" in $$props) $$invalidate(0, things = $$props.things);
if ("foo" in $$props) $$invalidate(1, foo = $$props.foo);

@ -14,7 +14,8 @@ import {
set_data_dev,
space,
text,
validate_each_argument
validate_each_argument,
validate_slots
} from "svelte/internal";
const file = undefined;
@ -171,6 +172,9 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
if ("things" in $$props) $$invalidate(0, things = $$props.things);
if ("foo" in $$props) $$invalidate(1, foo = $$props.foo);

@ -4,7 +4,8 @@ import {
dispatch_dev,
init,
noop,
safe_not_equal
safe_not_equal,
validate_slots
} from "svelte/internal";
const file = undefined;
@ -50,6 +51,14 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let obj = { x: 5 };
let kobzol = 5;
const writable_props = [];
Object.keys($$props).forEach(key => {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$capture_state = () => ({ obj, kobzol });
$$self.$inject_state = $$props => {

@ -11,7 +11,8 @@ import {
safe_not_equal,
space,
text,
validate_each_argument
validate_each_argument,
validate_slots
} from "svelte/internal";
const file = undefined;
@ -134,10 +135,22 @@ function create_fragment(ctx) {
return block;
}
function instance($$self, $$props) {
const writable_props = [];
Object.keys($$props).forEach(key => {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
return [];
}
class Component extends SvelteComponentDev {
constructor(options) {
super(options);
init(this, options, null, create_fragment, safe_not_equal, {});
init(this, options, instance, create_fragment, safe_not_equal, {});
dispatch_dev("SvelteRegisterComponent", {
component: this,

@ -12,7 +12,8 @@ import {
safe_not_equal,
set_data_dev,
space,
text
text,
validate_slots
} from "svelte/internal";
const file = undefined;
@ -72,6 +73,9 @@ function instance($$self, $$props, $$invalidate) {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
});
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
};

@ -6,13 +6,16 @@ import {
detach_dev,
dispatch_dev,
element,
globals,
init,
insert_dev,
loop_guard,
noop,
safe_not_equal
safe_not_equal,
validate_slots
} from "svelte/internal";
const { console: console_1 } = globals;
const file = undefined;
function create_fragment(ctx) {
@ -102,6 +105,15 @@ function instance($$self, $$props, $$invalidate) {
} while (true);
}
const writable_props = [];
Object.keys($$props).forEach(key => {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console_1.warn(`<Component> was created with unknown prop '${key}'`);
});
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
function div_binding($$value) {
binding_callbacks[$$value ? "unshift" : "push"](() => {
$$invalidate(0, node = $$value);

@ -1,6 +1,6 @@
{
"code": "invalid-self-placement",
"message": "<svelte:self> components can only exist inside if-blocks or each-blocks",
"message": "<svelte:self> components can only exist inside {#if} blocks, {#each} blocks, or slots passed to components",
"start": {
"line": 1,
"column": 1,

@ -0,0 +1,12 @@
<script>
export let a;
export function b() {}
export let c = 1;
$: length = Object.keys($$restProps).length;
$: values = Object.values($$restProps);
</script>
<div>Length: {length}</div>
<div>Values: {values.join(',')}</div>
<div {...$$restProps} />

@ -0,0 +1,54 @@
export default {
props: {
a: 3,
b: 4,
c: 5,
d: 6
},
html: `
<div>Length: 3</div>
<div>Values: 4,5,1</div>
<div d="4" e="5" foo="1"></div>
<button></button><button></button><button></button><button></button>
`,
async test({ assert, target, window, }) {
const [btn1, btn2, btn3, btn4] = target.querySelectorAll('button');
const clickEvent = new window.MouseEvent('click');
await btn1.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>Length: 3</div>
<div>Values: 4,5,1</div>
<div d="4" e="5" foo="1"></div>
<button></button><button></button><button></button><button></button>
`);
await btn2.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>Length: 3</div>
<div>Values: 34,5,1</div>
<div d="34" e="5" foo="1"></div>
<button></button><button></button><button></button><button></button>
`);
await btn3.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>Length: 3</div>
<div>Values: 34,5,31</div>
<div d="34" e="5" foo="31"></div>
<button></button><button></button><button></button><button></button>
`);
await btn4.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>Length: 4</div>
<div>Values: 34,5,31,2</div>
<div d="34" e="5" foo="31" bar="2"></div>
<button></button><button></button><button></button><button></button>
`);
}
};

@ -0,0 +1,25 @@
<script>
import App from './App.svelte';
let a = 1, b = 2, c = 3, d = 4, e = 5;
let f = { foo: 1 };
function updateProps() {
a = 31;
b = 32;
}
function updateRest() {
d = 34;
}
function updateSpread() {
f.foo = 31;
}
function updateSpread2() {
f.bar = 2;
}
</script>
<App {a} {b} {c} {d} {e} {...f} />
<button on:click={updateProps}></button>
<button on:click={updateRest}></button>
<button on:click={updateSpread}></button>
<button on:click={updateSpread2}></button>

@ -0,0 +1,13 @@
<script>
export let a;
export function b() {}
export let c = 1;
$: length = Object.keys($$restProps).length;
$: values = Object.values($$restProps);
</script>
<div>Length: {length}</div>
<div>Values: {values.join(',')}</div>
<div {...$$restProps} />
<div {...$$props} />

@ -0,0 +1,60 @@
export default {
props: {
a: 3,
b: 4,
c: 5,
d: 6
},
html: `
<div>Length: 3</div>
<div>Values: 4,5,1</div>
<div d="4" e="5" foo="1"></div>
<div a="1" b="2" c="3" d="4" e="5" foo="1"></div>
<button></button><button></button><button></button><button></button>
`,
async test({ assert, target, window, }) {
const [btn1, btn2, btn3, btn4] = target.querySelectorAll('button');
const clickEvent = new window.MouseEvent('click');
await btn1.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>Length: 3</div>
<div>Values: 4,5,1</div>
<div d="4" e="5" foo="1"></div>
<div a="31" b="32" c="3" d="4" e="5" foo="1"></div>
<button></button><button></button><button></button><button></button>
`);
await btn2.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>Length: 3</div>
<div>Values: 34,5,1</div>
<div d="34" e="5" foo="1"></div>
<div a="31" b="32" c="3" d="34" e="5" foo="1"></div>
<button></button><button></button><button></button><button></button>
`);
await btn3.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>Length: 3</div>
<div>Values: 34,5,31</div>
<div d="34" e="5" foo="31"></div>
<div a="31" b="32" c="3" d="34" e="5" foo="31"></div>
<button></button><button></button><button></button><button></button>
`);
await btn4.dispatchEvent(clickEvent);
assert.htmlEqual(target.innerHTML, `
<div>Length: 4</div>
<div>Values: 34,5,31,2</div>
<div d="34" e="5" foo="31" bar="2"></div>
<div a="31" b="32" c="3" d="34" e="5" foo="31" bar="2"></div>
<button></button><button></button><button></button><button></button>
`);
}
};

@ -0,0 +1,25 @@
<script>
import App from './App.svelte';
let a = 1, b = 2, c = 3, d = 4, e = 5;
let f = { foo: 1 };
function updateProps() {
a = 31;
b = 32;
}
function updateRest() {
d = 34;
}
function updateSpread() {
f.foo = 31;
}
function updateSpread2() {
f.bar = 2;
}
</script>
<App {a} {b} {c} {d} {e} {...f} />
<button on:click={updateProps}></button>
<button on:click={updateRest}></button>
<button on:click={updateSpread}></button>
<button on:click={updateSpread2}></button>

@ -0,0 +1,24 @@
export default {
html: `
012345678910111213141516171819202122232425262728293031323334353637383940
expected: true
if: true
<button></button>
`,
async test({ assert, component, target, window }) {
const button = target.querySelector("button");
await button.dispatchEvent(new window.MouseEvent("click"));
assert.htmlEqual(
target.innerHTML,
`
112345678910111213141516171819202122232425262728293031323334353637383940
expected: false
if: false
<div></div>
<button></button>
`
);
}
};

@ -0,0 +1,62 @@
<script>
import { fade } from 'svelte/transition';
export let _a = [];
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';
function update() {
_0 = '1';
}
</script>
{_0}{_1}{_2}{_3}{_4}{_5}{_6}{_7}{_8}{_9}{_10}{_11}{_12}{_13}{_14}{_15}{_16}{_17}{_18}{_19}{_20}{_21}{_22}{_23}{_24}{_25}{_26}{_27}{_28}{_29}{_30}{_31}{_32}{_33}{_34}{_35}{_36}{_37}{_38}{_39}{_40}
expected: {_a.indexOf(_0) && _0 === '0' && _1 === '1'}
{#if _a.indexOf(_0) && _0 === '0' && _1 === '1'}
if: true
{:else}
if: false
<div out:fade></div>
{/if}
<button on:click={update}></button>

@ -0,0 +1,9 @@
<script>
let dummy = 0;
function increment () {
dummy = 1;
}
</script>
<slot dummy={dummy}></slot>
<button on:click={increment}></button>

@ -0,0 +1,30 @@
export default {
html: `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>0</p>
<button></button>
`,
async test({ assert, component, target, window }) {
// change from inside
const button = target.querySelector('button');
await button.dispatchEvent(new window.MouseEvent('click'));
assert.htmlEqual(target.innerHTML, `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>1</p>
<button></button>
`);
// change from outside
component._0 = 'a';
component._40 = 'b';
assert.htmlEqual(target.innerHTML, `
<p>a_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39b</p>
<p>1</p>
<button></button>
`);
}
};

@ -0,0 +1,11 @@
<script>
import Echo from './Echo.svelte';
export let _0 = '_0', _1 = '_1', _2 = '_2', _3 = '_3', _4 = '_4', _5 = '_5', _6 = '_6', _7 = '_7', _8 = '_8', _9 = '_9', _10 = '_10', _11 = '_11', _12 = '_12', _13 = '_13', _14 = '_14', _15 = '_15', _16 = '_16', _17 = '_17', _18 = '_18', _19 = '_19', _20 = '_20', _21 = '_21', _22 = '_22', _23 = '_23', _24 = '_24', _25 = '_25', _26 = '_26', _27 = '_27', _28 = '_28', _29 = '_29', _30 = '_30', _31 = '_31', _32 = '_32', _33 = '_33', _34 = '_34', _35 = '_35', _36 = '_36', _37 = '_37', _38 = '_38', _39 = '_39', _40 = '_40';
</script>
<Echo let:dummy>
<p>{_0}{_1}{_2}{_3}{_4}{_5}{_6}{_7}{_8}{_9}{_10}{_11}{_12}{_13}{_14}{_15}{_16}{_17}{_18}{_19}{_20}{_21}{_22}{_23}{_24}{_25}{_26}{_27}{_28}{_29}{_30}{_31}{_32}{_33}{_34}{_35}{_36}{_37}{_38}{_39}{_40}</p>
<p>{dummy}</p>
</Echo>

@ -0,0 +1,11 @@
<script>
export let _0 = '_0', _1 = '_1', _2 = '_2', _3 = '_3', _4 = '_4', _5 = '_5', _6 = '_6', _7 = '_7', _8 = '_8', _9 = '_9', _10 = '_10', _11 = '_11', _12 = '_12', _13 = '_13', _14 = '_14', _15 = '_15', _16 = '_16', _17 = '_17', _18 = '_18', _19 = '_19', _20 = '_20', _21 = '_21', _22 = '_22', _23 = '_23', _24 = '_24', _25 = '_25', _26 = '_26', _27 = '_27', _28 = '_28', _29 = '_29', _30 = '_30', _31 = '_31', _32 = '_32', _33 = '_33', _34 = '_34', _35 = '_35', _36 = '_36', _37 = '_37', _38 = '_38', _39 = '_39', _40 = '_40';
let dummy = 0;
function increment () {
dummy = 1;
}
</script>
<p>{_0}{_1}{_2}{_3}{_4}{_5}{_6}{_7}{_8}{_9}{_10}{_11}{_12}{_13}{_14}{_15}{_16}{_17}{_18}{_19}{_20}{_21}{_22}{_23}{_24}{_25}{_26}{_27}{_28}{_29}{_30}{_31}{_32}{_33}{_34}{_35}{_36}{_37}{_38}{_39}{_40}</p>
<slot dummy={dummy}></slot>
<button on:click={increment}></button>

@ -0,0 +1,41 @@
export default {
html: `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>0</p>
<p>0</p>
<button></button>
`,
async test({ assert, component, target, window }) {
// change from inside
const button = target.querySelector('button');
await button.dispatchEvent(new window.MouseEvent('click'));
assert.htmlEqual(target.innerHTML, `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>0</p>
<p>1</p>
<button></button>
`);
// change from outside
component._0 = 'a';
assert.htmlEqual(target.innerHTML, `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>a</p>
<p>1</p>
<button></button>
`);
// change from outside through props
component._40 = 'b';
assert.htmlEqual(target.innerHTML, `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39b</p>
<p>a</p>
<p>1</p>
<button></button>
`);
}
};

@ -0,0 +1,12 @@
<script>
import Echo from './Echo.svelte';
export let _0 = 0, _40;
</script>
<Echo _40={_40} let:dummy>
<p>{_0}</p>
<p>{dummy}</p>
</Echo>

@ -0,0 +1,13 @@
<script>
export let _0 = '_0', _1 = '_1', _2 = '_2', _3 = '_3', _4 = '_4', _5 = '_5', _6 = '_6', _7 = '_7', _8 = '_8', _9 = '_9', _10 = '_10', _11 = '_11', _12 = '_12', _13 = '_13', _14 = '_14', _15 = '_15', _16 = '_16', _17 = '_17', _18 = '_18', _19 = '_19', _20 = '_20', _21 = '_21', _22 = '_22', _23 = '_23', _24 = '_24', _25 = '_25', _26 = '_26', _27 = '_27', _28 = '_28', _29 = '_29', _30 = '_30', _31 = '_31', _32 = '_32', _33 = '_33', _34 = '_34', _35 = '_35', _36 = '_36', _37 = '_37', _38 = '_38', _39 = '_39', _40 = '_40';
export let b = 'b';
let dummy = 0;
function increment () {
dummy = 1;
}
</script>
<p>{_0}{_1}{_2}{_3}{_4}{_5}{_6}{_7}{_8}{_9}{_10}{_11}{_12}{_13}{_14}{_15}{_16}{_17}{_18}{_19}{_20}{_21}{_22}{_23}{_24}{_25}{_26}{_27}{_28}{_29}{_30}{_31}{_32}{_33}{_34}{_35}{_36}{_37}{_38}{_39}{_40}</p>
<p>{b}</p>
<slot dummy={dummy}></slot>
<button on:click={increment}></button>

@ -0,0 +1,49 @@
export default {
html: `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>b</p>
<p>-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40</p>
<p>0</p>
<p>0</p>
<button></button>
`,
async test({ assert, component, target, window }) {
// change from inside
const button = target.querySelector('button');
await button.dispatchEvent(new window.MouseEvent('click'));
assert.htmlEqual(target.innerHTML, `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>b</p>
<p>-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40</p>
<p>0</p>
<p>1</p>
<button></button>
`);
// change from outside
component.a = 'AA';
assert.htmlEqual(target.innerHTML, `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>b</p>
<p>-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40</p>
<p>AA</p>
<p>1</p>
<button></button>
`);
// change from outside through props
component.b = 'BB';
assert.htmlEqual(target.innerHTML, `
<p>_0_1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_35_36_37_38_39_40</p>
<p>BB</p>
<p>-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40</p>
<p>AA</p>
<p>1</p>
<button></button>
`);
}
};

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

@ -0,0 +1,4 @@
<div>
<slot><p class='default'>default fallback content</p></slot>
<slot name='bar'>bar fallback</slot>
</div>

@ -0,0 +1,13 @@
export default {
html: `
<div>
<p class="default">default fallback content</p>
<input slot="bar">
</div>
<div>
<p class="default">default fallback content</p>
bar fallback
</div>
`
};

@ -0,0 +1,10 @@
<script>
import Nested from "./Nested.svelte";
</script>
<Nested>
<input slot="bar">
</Nested>
<Nested>
</Nested>

@ -0,0 +1,9 @@
export default {
compileOptions: {
dev: true
},
warnings: [
'<Nested> received an unexpected slot "default".',
'<Nested> received an unexpected slot "slot1".'
]
};

@ -0,0 +1,7 @@
<script>
import Nested from "./Nested.svelte";
</script>
<Nested>
<input slot="slot1">
<input slot="slot2">
</Nested>

@ -0,0 +1,9 @@
export default {
compileOptions: {
dev: true
},
warnings: [
`<Foo> was created with unknown prop 'fo'`
]
};

@ -0,0 +1,5 @@
<script>
import Foo from './Foo.svelte';
</script>
<Foo fo="sho"/>

@ -4,4 +4,4 @@
</script>
<div>{foo}</div>
<div>{JSON.stringify($$props)}</div>
<div>{JSON.stringify($$restProps)}</div>

@ -0,0 +1,7 @@
<script>
export let foo = undefined;
</script>
<div>{foo}</div>
<div>{JSON.stringify($$props)}</div>

@ -0,0 +1,7 @@
export default {
compileOptions: {
dev: true
},
warnings: []
};

@ -0,0 +1,5 @@
<script>
import Foo from './Foo.svelte';
</script>
<Foo fo="sho"/>

@ -0,0 +1,37 @@
export default {
props: {
animals: ['alpaca', 'baboon', 'capybara'],
foo: 'something else'
},
html: `
before
<p>alpaca</p>
<p>baboon</p>
<p>capybara</p>
after
`,
test({ assert, component, target }) {
component.animals = [];
assert.htmlEqual(target.innerHTML, `
before
<p>no animals, but rather something else</p>
after
`);
component.foo = 'something other';
assert.htmlEqual(target.innerHTML, `
before
<p>no animals, but rather something other</p>
after
`);
component.animals = ['wombat'];
assert.htmlEqual(target.innerHTML, `
before
<p>wombat</p>
after
`);
}
};

@ -0,0 +1,12 @@
<script>
export let animals;
export let foo;
</script>
before
{#each animals as animal (animal)}
<p>{animal}</p>
{:else}
<p>no animals, but rather {foo}</p>
{/each}
after

@ -0,0 +1,37 @@
export default {
props: {
animals: ['alpaca', 'baboon', 'capybara'],
foo: 'something else'
},
html: `
before
<p>alpaca</p>
<p>baboon</p>
<p>capybara</p>
after
`,
test({ assert, component, target }) {
component.animals = [];
assert.htmlEqual(target.innerHTML, `
before
<p>no animals, but rather something else</p>
after
`);
component.foo = 'something other';
assert.htmlEqual(target.innerHTML, `
before
<p>no animals, but rather something other</p>
after
`);
component.animals = ['wombat'];
assert.htmlEqual(target.innerHTML, `
before
<p>wombat</p>
after
`);
}
};

@ -0,0 +1,12 @@
<script>
export let animals;
export let foo;
</script>
before
{#each animals as animal}
<p>{animal}</p>
{:else}
<p>no animals, but rather {foo}</p>
{/each}
after

@ -0,0 +1,7 @@
<script>
export let count;
</script>
{#if count > 0}
<slot count={count-1}></slot>
{/if}

@ -0,0 +1,3 @@
export default {
html: '5 4 3 2 1 0',
};

@ -0,0 +1,10 @@
<script>
import Countdown from './Countdown.svelte';
export let count = 5;
</script>
{count}
<Countdown {count} let:count={i}>
<svelte:self count={i} />
</Countdown>

@ -0,0 +1,13 @@
<script>
export let foo;
export let baz;
export let qux;
export let quux;
export let selected;
</script>
<p>foo: {foo}</p>
<p>baz: {baz} ({typeof baz})</p>
<p>qux: {qux}</p>
<p>quux: {quux}</p>
<p>selected: {selected}</p>

@ -0,0 +1,76 @@
export default {
props: {
list: [{
foo: 'lol',
baz: 40 + 2,
qux: 5,
quux: 'core'
}, {
foo: 'lolzz',
baz: 50 + 2,
qux: 1,
quux: 'quuxx'
}],
},
html: `
<div>
<p>foo: lol</p>
<p>baz: 42 (number)</p>
<p>qux: 0</p>
<p>quux: core</p>
<p>selected: true</p>
<p>foo: lolzz</p>
<p>baz: 52 (number)</p>
<p>qux: 0</p>
<p>quux: quuxx</p>
<p>selected: false</p>
</div>
`,
test({ assert, component, target }) {
component.list = [{
foo: 'lol',
baz: 40 + 3,
qux: 8,
quux: 'heart'
}, {
foo: 'lolzz',
baz: 50 + 3,
qux: 8,
quux: 'heartxx'
}];
assert.htmlEqual(target.innerHTML, `
<div>
<p>foo: lol</p>
<p>baz: 43 (number)</p>
<p>qux: 0</p>
<p>quux: heart</p>
<p>selected: true</p>
<p>foo: lolzz</p>
<p>baz: 53 (number)</p>
<p>qux: 0</p>
<p>quux: heartxx</p>
<p>selected: false</p>
</div>
`);
component.qux = 1;
assert.htmlEqual(target.innerHTML, `
<div>
<p>foo: lol</p>
<p>baz: 43 (number)</p>
<p>qux: 1</p>
<p>quux: heart</p>
<p>selected: false</p>
<p>foo: lolzz</p>
<p>baz: 53 (number)</p>
<p>qux: 1</p>
<p>quux: heartxx</p>
<p>selected: true</p>
</div>
`);
}
};

@ -0,0 +1,12 @@
<script>
import Widget from './Widget.svelte';
export let list;
export let qux = 0;
</script>
<div>
{#each list as item, index (item.foo)}
<Widget {...item} qux={qux} selected={qux === index} />
{/each}
</div>

@ -0,0 +1,7 @@
<script>
const dummy = {
foo: 'bar'
};
</script>
<input bind:value={dummy.foo}>

@ -0,0 +1,15 @@
[{
"code": "invalid-binding",
"message": "Cannot bind to a variable which is not writable",
"pos": 61,
"start": {
"line": 5,
"column": 19,
"character": 61
},
"end": {
"line": 5,
"column": 24,
"character": 66
}
}]

@ -0,0 +1,5 @@
<script>
const dummy = 'foo';
</script>
<input bind:value={dummy}>

@ -0,0 +1,72 @@
export default {
test(assert, vars) {
assert.deepEqual(vars, [
{
name: "a",
export_name: null,
injected: false,
module: true,
mutated: false,
reassigned: true,
referenced: false,
referenced_from_script: false,
writable: true
},
{
name: "b",
export_name: null,
injected: false,
module: true,
mutated: true,
reassigned: false,
referenced: false,
referenced_from_script: false,
writable: true
},
{
name: "c",
export_name: null,
injected: false,
module: true,
mutated: false,
reassigned: false,
referenced: false,
referenced_from_script: false,
writable: true
},
{
name: "d",
export_name: null,
injected: false,
module: true,
mutated: false,
reassigned: false,
referenced: false,
referenced_from_script: false,
writable: true
},
{
name: "c",
export_name: null,
injected: false,
module: false,
mutated: false,
reassigned: true,
referenced: false,
referenced_from_script: true,
writable: true
},
{
name: "foo",
export_name: null,
injected: false,
module: false,
mutated: false,
reassigned: false,
referenced: false,
referenced_from_script: false,
writable: false
}
]);
}
};

@ -0,0 +1,18 @@
<script context='module'>
let a = {};
let b = {};
let c = {};
let d = {};
</script>
<script>
let c = {};
a = 1;
b.x = 1;
function foo() {
let d = {};
c = 1;
d.x = 1;
}
</script>
Loading…
Cancel
Save