Merge branch 'master' into fast-hydration

pull/4309/head
Avi Marcus 6 years ago committed by GitHub
commit 124dcdbe9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

1
.gitignore vendored

@ -1,6 +1,7 @@
.idea .idea
.DS_Store .DS_Store
.nyc_output .nyc_output
.vscode
node_modules node_modules
*.map *.map
/src/compiler/compile/internal_exports.ts /src/compiler/compile/internal_exports.ts

@ -2,9 +2,50 @@
## Unreleased ## Unreleased
* Fix attaching of JS debugging comments to HTML comments ([#4565](https://github.com/sveltejs/svelte/issues/4565))
## 3.20.1
* Fix compiler regression with slots ([#4562](https://github.com/sveltejs/svelte/issues/4562))
## 3.20.0
* Allow destructuring in `{#await}` blocks ([#1851](https://github.com/sveltejs/svelte/issues/1851))
* 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))
* Prevent passing named slots other than from the top level within a component ([#3385](https://github.com/sveltejs/svelte/issues/3385))
* Allow transitions and animations to work within iframes ([#3624](https://github.com/sveltejs/svelte/issues/3624))
* Fix initialising slot fallbacks when unnecessary ([#3763](https://github.com/sveltejs/svelte/issues/3763))
* Disallow binding directly to `const` variables ([#4479](https://github.com/sveltejs/svelte/issues/4479))
* Fix re-attaching event handlers on keyed `{#each}` blocks ([#4491](https://github.com/sveltejs/svelte/issues/4491))
* 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
* Do not treat modifications to `$$props` as updates to a store called `$props` ([#4368](https://github.com/sveltejs/svelte/issues/4368))
* Deconflict `value` parameter name used in contextual bindings ([#4445](https://github.com/sveltejs/svelte/issues/4445))
* Fix dev mode validation of `{#each}` blocks using strings ([#4450](https://github.com/sveltejs/svelte/issues/4450))
## 3.19.0
* Fix indirect bindings involving elements with spreads ([#3680](https://github.com/sveltejs/svelte/issues/3680)) * Fix indirect bindings involving elements with spreads ([#3680](https://github.com/sveltejs/svelte/issues/3680))
* `$capture_state`/`$inject_state` now act on the component's entire state, rather than its props ([#3822](https://github.com/sveltejs/svelte/pull/3822))
* Warn when using `<Foo/>` and `Foo` is dynamic ([#4331](https://github.com/sveltejs/svelte/issues/4331)) * Warn when using `<Foo/>` and `Foo` is dynamic ([#4331](https://github.com/sveltejs/svelte/issues/4331))
* Display compilation warnings in `svelte/register` in dev mode ([#4364](https://github.com/sveltejs/svelte/issues/4364))
* Fix unneeded updating of keyed each blocks ([#4373](https://github.com/sveltejs/svelte/issues/4373)) * Fix unneeded updating of keyed each blocks ([#4373](https://github.com/sveltejs/svelte/issues/4373))
* Throw runtime error in dev mode for non-array-like values in `{#each}` blocks ([#4408](https://github.com/sveltejs/svelte/issues/4408))
## 3.18.2 ## 3.18.2

@ -7,11 +7,6 @@
<img src="https://img.shields.io/npm/v/svelte.svg" alt="npm version"> <img src="https://img.shields.io/npm/v/svelte.svg" alt="npm version">
</a> </a>
<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>
<a href="https://github.com/sveltejs/svelte/blob/master/LICENSE"> <a href="https://github.com/sveltejs/svelte/blob/master/LICENSE">
<img src="https://img.shields.io/npm/l/svelte.svg" alt="license"> <img src="https://img.shields.io/npm/l/svelte.svg" alt="license">
</a> </a>
@ -37,6 +32,8 @@ cd svelte
npm install 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: > Many tests depend on newlines being preserved as `<LF>`. On Windows, you can ensure this by cloning with:
> ```bash > ```bash
> git -c core.autocrlf=false clone https://github.com/sveltejs/svelte.git > git -c core.autocrlf=false clone https://github.com/sveltejs/svelte.git

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

119
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.18.2", "version": "3.20.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "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": { "@types/eslint-visitor-keys": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@ -274,9 +280,9 @@
"dev": true "dev": true
}, },
"acorn": { "acorn": {
"version": "7.1.0", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
"integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
"dev": true "dev": true
}, },
"acorn-globals": { "acorn-globals": {
@ -609,16 +615,16 @@
} }
}, },
"codecov": { "codecov": {
"version": "3.5.0", "version": "3.6.5",
"resolved": "https://registry.npmjs.org/codecov/-/codecov-3.5.0.tgz", "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.6.5.tgz",
"integrity": "sha512-/OsWOfIHaQIr7aeZ4pY0UC1PZT6kimoKFOFYFNb6wxo3iw12nRrh+mNGH72rnXxNsq6SGfesVPizm/6Q3XqcFQ==", "integrity": "sha512-v48WuDMUug6JXwmmfsMzhCHRnhUf8O3duqXvltaYJKrO1OekZWpB/eH6iIoaxMl8Qli0+u3OxptdsBOYiD7VAQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"argv": "^0.0.2", "argv": "0.0.2",
"ignore-walk": "^3.0.1", "ignore-walk": "3.0.3",
"js-yaml": "^3.13.1", "js-yaml": "3.13.1",
"teeny-request": "^3.11.3", "teeny-request": "6.0.1",
"urlgrey": "^0.4.4" "urlgrey": "0.4.4"
} }
}, },
"color-convert": { "color-convert": {
@ -1670,6 +1676,37 @@
"whatwg-encoding": "^1.0.1" "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": { "http-signature": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@ -1707,9 +1744,9 @@
"dev": true "dev": true
}, },
"ignore-walk": { "ignore-walk": {
"version": "3.0.1", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"dev": true, "dev": true,
"requires": { "requires": {
"minimatch": "^3.0.4" "minimatch": "^3.0.4"
@ -3362,6 +3399,15 @@
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
"dev": true "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": { "string-width": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
@ -3409,6 +3455,12 @@
"integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
"dev": true "dev": true
}, },
"stubs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
"integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=",
"dev": true
},
"sucrase": { "sucrase": {
"version": "3.10.1", "version": "3.10.1",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.10.1.tgz", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.10.1.tgz",
@ -3449,14 +3501,43 @@
} }
}, },
"teeny-request": { "teeny-request": {
"version": "3.11.3", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.1.tgz",
"integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", "integrity": "sha512-TAK0c9a00ELOqLrZ49cFxvPVogMUFaWY8dUsQc/0CuQPGF+BOxOQzXfE413BAk2kLomwNplvdtMpeaeGWmoc2g==",
"dev": true, "dev": true,
"requires": { "requires": {
"https-proxy-agent": "^2.2.1", "http-proxy-agent": "^4.0.0",
"https-proxy-agent": "^4.0.0",
"node-fetch": "^2.2.0", "node-fetch": "^2.2.0",
"stream-events": "^1.0.5",
"uuid": "^3.3.2" "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": { "test-exclude": {

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

@ -36,7 +36,15 @@ function registerExtension(extension) {
format: 'cjs' format: 'cjs'
}); });
const { js } = compile(fs.readFileSync(filename, 'utf-8'), options); const { js, warnings } = compile(fs.readFileSync(filename, 'utf-8'), options);
if (options.dev) {
warnings.forEach(warning => {
console.warn(`\nSvelte Warning in ${warning.filename}:`);
console.warn(warning.message);
console.warn(warning.frame);
})
}
return module._compile(js.code, filename); return module._compile(js.code, filename);
}; };

@ -42,13 +42,13 @@ Svelte uses the `export` keyword to mark a variable declaration as a *property*
--- ---
You can specify a default value, which will be used if the component's consumer doesn't specify a prop. You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that whenever a prop is removed by the consumer, its value is set to `undefined` rather than the initial value.
In development mode (see the [compiler options](docs#svelte_compile)), a warning will be printed if no default is provided and the consumer does not specify a value. To squelch this warning, ensure that a default is specified, even if it is `undefined`. In development mode (see the [compiler options](docs#svelte_compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`.
```html ```html
<script> <script>
export let bar = 'optional default value'; export let bar = 'optional default initial value';
export let baz = undefined; export let baz = undefined;
</script> </script>
``` ```

@ -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: 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 ```html
@ -102,6 +113,15 @@ An element or component can have multiple spread attributes, interspersed with r
<Widget {...$$props}/> <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 ### Text expressions
@ -795,7 +815,7 @@ transition = (node: HTMLElement, params: any) => {
A transition is triggered by an element entering or leaving the DOM as a result of a state change. A transition is triggered by an element entering or leaving the DOM as a result of a state change.
Elements inside an *outroing* block are kept in the DOM until all current transitions have completed. When a block is transitioning out, elements inside the block are kept in the DOM until all current transitions have completed.
The `transition:` directive indicates a *bidirectional* transition, which means it can be smoothly reversed while the transition is in progress. The `transition:` directive indicates a *bidirectional* transition, which means it can be smoothly reversed while the transition is in progress.
@ -1249,15 +1269,15 @@ The usual shorthand rules apply — `let:item` is equivalent to `let:item={item}
```html ```html
<!-- App.svelte --> <!-- App.svelte -->
<FancyList {items} let:item={item}> <FancyList {items} let:prop={thing}>
<div>{item.text}</div> <div>{thing.text}</div>
</FancyList> </FancyList>
<!-- FancyList.svelte --> <!-- FancyList.svelte -->
<ul> <ul>
{#each items as item} {#each items as item}
<li class="fancy"> <li class="fancy">
<slot item={item}></slot> <slot prop={item}></slot>
</li> </li>
{/each} {/each}
</ul> </ul>
@ -1270,7 +1290,7 @@ Named slots can also expose values. The `let:` directive goes on the element wit
```html ```html
<!-- App.svelte --> <!-- App.svelte -->
<FancyList {items}> <FancyList {items}>
<div slot="item" let:item={item}>{item.text}</div> <div slot="item" let:item>{item.text}</div>
<p slot="footer">Copyright (c) 2019 Svelte Industries</p> <p slot="footer">Copyright (c) 2019 Svelte Industries</p>
</FancyList> </FancyList>
@ -1278,7 +1298,7 @@ Named slots can also expose values. The `let:` directive goes on the element wit
<ul> <ul>
{#each items as item} {#each items as item}
<li class="fancy"> <li class="fancy">
<slot name="item" item={item}></slot> <slot name="item" {item}></slot>
</li> </li>
{/each} {/each}
</ul> </ul>

@ -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). 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 ```html
<svelte:options tag="my-element"> <svelte:options tag="my-element" />
<script> <script>
export let name = 'world'; export let name = 'world';

@ -113,7 +113,8 @@ const {
* `module` is `true` if the value is declared in a `context="module"` script * `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 * `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 * `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`) * `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! * `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, mutated: boolean,
reassigned: boolean, reassigned: boolean,
referenced: boolean, referenced: boolean,
referenced_from_script: boolean,
writable: boolean writable: boolean
}>, }>,
stats: { stats: {

@ -1,9 +1,9 @@
<script> <script>
import FancyButton from './FancyButton.svelte'; import CustomButton from './CustomButton.svelte';
function handleClick() { function handleClick() {
alert('clicked'); alert('clicked');
} }
</script> </script>
<FancyButton on:click={handleClick}/> <CustomButton on:click={handleClick}/>

@ -0,0 +1,22 @@
<style>
button {
height: 4rem;
width: 8rem;
background-color: #aaa;
border-color: #f1c40f;
color: #f1c40f;
font-size: 1.25rem;
background-image: linear-gradient(45deg, #f1c40f 50%, transparent 50%);
background-position: 100%;
background-size: 400%;
transition: background 300ms ease-in-out;
}
button:hover {
background-position: 0;
color: #aaa;
}
</style>
<button on:click>
Click me
</button>

@ -1,15 +0,0 @@
<style>
button {
font-family: 'Comic Sans MS', cursive;
font-size: 2em;
padding: 0.5em 1em;
color: royalblue;
background: gold;
border-radius: 1em;
box-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
</style>
<button on:click>
Click me
</button>

@ -1,9 +1,9 @@
<script> <script>
import FancyButton from './FancyButton.svelte'; import CustomButton from './CustomButton.svelte';
function handleClick() { function handleClick() {
alert('clicked'); alert('clicked');
} }
</script> </script>
<FancyButton on:click={handleClick}/> <CustomButton on:click={handleClick}/>

@ -0,0 +1,22 @@
<style>
button {
height: 4rem;
width: 8rem;
background-color: #aaa;
border-color: #f1c40f;
color: #f1c40f;
font-size: 1.25rem;
background-image: linear-gradient(45deg, #f1c40f 50%, transparent 50%);
background-position: 100%;
background-size: 400%;
transition: background 300ms ease-in-out;
}
button:hover {
background-position: 0;
color: #aaa;
}
</style>
<button>
Click me
</button>

@ -1,15 +0,0 @@
<style>
button {
font-family: 'Comic Sans MS', cursive;
font-size: 2em;
padding: 0.5em 1em;
color: royalblue;
background: gold;
border-radius: 1em;
box-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
</style>
<button>
Click me
</button>

@ -1,9 +1,9 @@
<script> <script>
import FancyButton from './FancyButton.svelte'; import CustomButton from './CustomButton.svelte';
function handleClick() { function handleClick() {
alert('clicked'); alert('clicked');
} }
</script> </script>
<FancyButton on:click={handleClick}/> <CustomButton on:click={handleClick}/>

@ -0,0 +1,22 @@
<style>
button {
height: 4rem;
width: 8rem;
background-color: #aaa;
border-color: #f1c40f;
color: #f1c40f;
font-size: 1.25rem;
background-image: linear-gradient(45deg, #f1c40f 50%, transparent 50%);
background-position: 100%;
background-size: 400%;
transition: background 300ms ease-in-out;
}
button:hover {
background-position: 0;
color: #aaa;
}
</style>
<button on:click>
Click me
</button>

@ -1,15 +0,0 @@
<style>
button {
font-family: 'Comic Sans MS', cursive;
font-size: 2em;
padding: 0.5em 1em;
color: royalblue;
background: gold;
border-radius: 1em;
box-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
</style>
<button on:click>
Click me
</button>

@ -4,7 +4,7 @@ title: DOM event forwarding
Event forwarding works for DOM events too. Event forwarding works for DOM events too.
We want to get notified of clicks on our `<FancyButton>` — to do that, we just need to forward `click` events on the `<button>` element in `FancyButton.svelte`: We want to get notified of clicks on our `<CustomButton>` — to do that, we just need to forward `click` events on the `<button>` element in `CustomButton.svelte`:
```html ```html
<button on:click> <button on:click>

@ -1358,9 +1358,9 @@
} }
}, },
"acorn": { "acorn": {
"version": "7.0.0", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
"integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==", "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
"dev": true "dev": true
}, },
"ansi-colors": { "ansi-colors": {
@ -3483,9 +3483,9 @@
} }
}, },
"sapper": { "sapper": {
"version": "0.27.8", "version": "0.27.11",
"resolved": "https://registry.npmjs.org/sapper/-/sapper-0.27.8.tgz", "resolved": "https://registry.npmjs.org/sapper/-/sapper-0.27.11.tgz",
"integrity": "sha512-78K+56yu9nGOEU0B0XjBvNchRuPEv4aHbAKK4D874S4aoapMAkHCT0bHtPK12S3P7JPxvvT8GzHaq/8NetMmbg==", "integrity": "sha512-5EaPZhlc8OnyN3UCI6dRSM1Gz5sxyzLZG/1z5nMvZhg9Ng+rSvEvJx/rW/tSHcnQPa8or7+YcbfvQHKS5oPHiw==",
"dev": true, "dev": true,
"requires": { "requires": {
"html-minifier": "^4.0.0", "html-minifier": "^4.0.0",

@ -53,7 +53,7 @@
"rollup-plugin-replace": "^2.2.0", "rollup-plugin-replace": "^2.2.0",
"rollup-plugin-svelte": "^5.1.0", "rollup-plugin-svelte": "^5.1.0",
"rollup-plugin-terser": "^5.1.1", "rollup-plugin-terser": "^5.1.1",
"sapper": "^0.27.8", "sapper": "^0.27.11",
"shelljs": "^0.8.3", "shelljs": "^0.8.3",
"svelte": "^3.12.0" "svelte": "^3.12.0"
}, },

@ -57,7 +57,10 @@
is_relaxed_gist = data.relaxed; is_relaxed_gist = data.relaxed;
const components = data.files.map(file => { const components = data.files.map(file => {
let [name, type] = file.name.split('.'); const dot = file.name.lastIndexOf(".");
let name = file.name.slice(0, dot);
let type = file.name.slice(dot + 1);
if (type === 'html') type = 'svelte'; // TODO do this on the server if (type === 'html') type = 'svelte'; // TODO do this on the server
return { name, type, source: file.source }; return { name, type, source: file.source };
}); });

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -1,7 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-width="2"><path d="m5 12h14"/><path d="m12 5 7 7-7 7"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<g fill="none" stroke="white" stroke-width="2">
<line x1='5' y1='12' x2='19' y2='12' />
<polyline points='12 5 19 12 12 19' />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 184 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m21 7-12 12-5.5-5.5 1.41-1.41 4.09 4.08 10.59-10.58z" fill="#aa1e1e"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path style="fill: #aa1e1e" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
</svg>

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 168 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m2 8 10 8 10-8" fill="none" stroke="#676778" stroke-width="2"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path style="stroke: #676778; stroke-width: 2; fill: none" d="M2,8 L12,16 L22,8"/>
</svg>

Before

Width:  |  Height:  |  Size: 221 B

After

Width:  |  Height:  |  Size: 161 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m19.5 3.09 1.41 1.41-4.5 4.5h3.59v2h-7v-7h2v3.59zm1.41 16.41-1.41 1.41-4.5-4.5v3.59h-2v-7h7v2h-3.59zm-16.41-16.41 4.5 4.5v-3.59h2v7h-7v-2h3.59l-4.5-4.5zm-1.41 16.41 4.5-4.5h-3.59v-2h7v7h-2v-3.59l-4.5 4.5z" fill="#aa1e1e"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path style="fill: #aa1e1e" d="M19.5,3.09L20.91,4.5L16.41,9H20V11H13V4H15V7.59L19.5,3.09M20.91,19.5L19.5,20.91L15,16.41V20H13V13H20V15H16.41L20.91,19.5M4.5,3.09L9,7.59V4H11V11H4V9H7.59L3.09,4.5L4.5,3.09M3.09,19.5L7.59,15H4V13H11V20H9V16.41L4.5,20.91L3.09,19.5Z" />
</svg>

Before

Width:  |  Height:  |  Size: 405 B

After

Width:  |  Height:  |  Size: 320 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m5 20h14v-2h-14m14-9h-4v-6h-6v6h-4l7 7z" fill="#aa1e1e"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#aa1e1e" d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" />
</svg>

Before

Width:  |  Height:  |  Size: 212 B

After

Width:  |  Height:  |  Size: 155 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m3 6h18v2h-18zm0 5h18v2h-18zm0 5h18v2h-18z" fill="#999"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#999" d="M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z" />
</svg>

Before

Width:  |  Height:  |  Size: 213 B

After

Width:  |  Height:  |  Size: 155 B

@ -1,7 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-width="2"><path d="m20 14.66v5.34a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2-2v-14a2 2 0 0 1 2-2h5.34"/><path d="m18 2 4 4-10 10h-4v-4z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<g style="fill: none; stroke: white; stroke-width: 2;">
<path d='M20 14.66V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5.34' />
<polygon points='18 2 22 6 12 16 8 16 8 12 18 2' />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 333 B

After

Width:  |  Height:  |  Size: 256 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m9.5 13.09 1.41 1.41-4.5 4.5h3.59v2h-7v-7h2v3.59zm1.41-3.59-1.41 1.41-4.5-4.5v3.59h-2v-7h7v2h-3.59zm3.59 3.59 4.5 4.5v-3.59h2v7h-7v-2h3.59l-4.5-4.5zm-1.41-3.59 4.5-4.5h-3.59v-2h7v7h-2v-3.59l-4.5 4.5z" fill="#aa1e1e"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path style="fill: #aa1e1e" d="M9.5,13.09L10.91,14.5L6.41,19H10V21H3V14H5V17.59L9.5,13.09M10.91,9.5L9.5,10.91L5,6.41V10H3V3H10V5H6.41L10.91,9.5M14.5,13.09L19,17.59V14H21V21H14V19H17.59L13.09,14.5L14.5,13.09M13.09,9.5L17.59,5H14V3H21V10H19V6.41L14.5,10.91L13.09,9.5Z" />
</svg>

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 315 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m12 5c4.97 0 9 2.69 9 6 0 1.68-1.04 3.2-2.71 4.29 1.07-.87 1.71-1.97 1.71-3.16 0-2.84-3.58-5.13-8-5.13v3l-4-4 4-4zm0 14c-4.97 0-9-2.69-9-6 0-1.68 1.04-3.2 2.71-4.29-1.07.87-1.71 1.97-1.71 3.17 0 2.83 3.58 5.12 8 5.12v-3l4 4-4 4z" fill="#aa1e1e"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#aa1e1e" d="M12,5C16.97,5 21,7.69 21,11C21,12.68 19.96,14.2 18.29,15.29C19.36,14.42 20,13.32 20,12.13C20,9.29 16.42,7 12,7V10L8,6L12,2V5M12,19C7.03,19 3,16.31 3,13C3,11.32 4.04,9.8 5.71,8.71C4.64,9.58 4,10.68 4,11.88C4,14.71 7.58,17 12,17V14L16,18L12,22V19Z" />
</svg>

Before

Width:  |  Height:  |  Size: 415 B

After

Width:  |  Height:  |  Size: 344 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m3 4v8.5l3-3 3 3.5c1 1 1 2 1 2v6h4v-7s0-1-.53-2-1.47-2-1.47-2l-3-3.42 2.5-2.58m6.5 0-4.46 4.47.46.53s.93 1 1.47 2c.21.4.33.79.4 1.13l5.13-5.13" fill="#aa1e1e"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#aa1e1e" d="M3,4V12.5L6,9.5L9,13C10,14 10,15 10,15V21H14V14C14,14 14,13 13.47,12C12.94,11 12,10 12,10L9,6.58L11.5,4M18,4L13.54,8.47L14,9C14,9 14.93,10 15.47,11C15.68,11.4 15.8,11.79 15.87,12.13L21,7" />
</svg>

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 258 B

@ -1,8 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#333"><path d="m9 7h-3a2 2 0 0 0 0 10h3"/><path d="m15 7h3a2 2 0 0 1 0 10h-3"/><path d="m7 12h10"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<g fill="none" stroke="#333">
<path d="M9,7L6,7A2 2 0 0 0 6,17L9,17"/>
<path d="M15,7L18,7A2 2 0 0 1 18,17L15,17"/>
<path d="M7,12L17,12"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 215 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m15 9h-10v-4h10m-3 14a3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1 -3 3m5-16h-12c-1.11 0-2 .9-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-12z" fill="#aa1e1e"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#aa1e1e" d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
</svg>

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

@ -1,16 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg enable-background="new 0 0 98.2 118" viewBox="0 0 98.2 118" xmlns="http://www.w3.org/2000/svg"><path d="m91.9 15.6c-10.9-15.7-32.6-20.3-48.2-10.3l-27.5 17.5c-7.5 4.7-12.7 12.4-14.2 21.1-1.3 7.3-.2 14.8 3.3 21.3-2.4 3.6-4 7.6-4.7 11.8-1.6 8.9.5 18.1 5.7 25.4 11 15.7 32.6 20.3 48.2 10.4l27.5-17.5c7.5-4.7 12.7-12.4 14.2-21.1 1.3-7.3.2-14.8-3.3-21.3 2.4-3.6 4-7.6 4.7-11.8 1.6-9-.4-18.2-5.7-25.5m-50.9 88.3c-8.9 2.3-18.2-1.2-23.4-8.7-3.2-4.4-4.4-9.9-3.5-15.3.2-.9.4-1.7.6-2.6l.5-1.6 1.4 1c3.3 2.4 6.9 4.2 10.8 5.4l1 .3-.1 1c-.1 1.4.3 2.9 1.1 4.1 1.6 2.3 4.4 3.4 7.1 2.7.6-.2 1.2-.4 1.7-.7l27.4-17.5c1.4-.9 2.3-2.2 2.6-3.8s-.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6-.6.2-1.2.4-1.7.7l-10.4 6.6c-1.7 1.1-3.6 1.9-5.6 2.4-8.9 2.3-18.2-1.2-23.4-8.7-3.1-4.4-4.4-9.9-3.4-15.3.9-5.2 4.1-9.9 8.6-12.7l27.4-17.5c1.7-1.1 3.6-1.9 5.6-2.5 8.9-2.3 18.2 1.2 23.4 8.7 3.2 4.4 4.4 9.9 3.5 15.3-.2.9-.4 1.7-.7 2.6l-.5 1.6-1.4-1c-3.3-2.4-6.9-4.2-10.8-5.4l-1-.3.1-1c.1-1.4-.3-2.9-1.1-4.1-1.6-2.3-4.4-3.3-7.1-2.6-.6.2-1.2.4-1.7.7l-27.4 17.6c-1.4.9-2.3 2.2-2.6 3.8s.1 3.3 1 4.6c1.6 2.3 4.4 3.3 7.1 2.6.6-.2 1.2-.4 1.7-.7l10.5-6.7c1.7-1.1 3.6-1.9 5.6-2.5 8.9-2.3 18.2 1.2 23.4 8.7 3.2 4.4 4.4 9.9 3.5 15.3-.9 5.2-4.1 9.9-8.6 12.7l-27.4 17.5c-1.8 1.1-3.7 1.9-5.7 2.5"/></svg>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 98.2 118" style="enable-background:new 0 0 98.2 118;" xml:space="preserve">
<path d="M91.9,15.6C81-0.1,59.3-4.7,43.7,5.3L16.2,22.8C8.7,27.5,3.5,35.2,2,43.9c-1.3,7.3-0.2,14.8,3.3,21.3
c-2.4,3.6-4,7.6-4.7,11.8c-1.6,8.9,0.5,18.1,5.7,25.4c11,15.7,32.6,20.3,48.2,10.4L82,95.3c7.5-4.7,12.7-12.4,14.2-21.1
c1.3-7.3,0.2-14.8-3.3-21.3c2.4-3.6,4-7.6,4.7-11.8C99.2,32.1,97.2,22.9,91.9,15.6 M41,103.9c-8.9,2.3-18.2-1.2-23.4-8.7
c-3.2-4.4-4.4-9.9-3.5-15.3c0.2-0.9,0.4-1.7,0.6-2.6l0.5-1.6l1.4,1c3.3,2.4,6.9,4.2,10.8,5.4l1,0.3l-0.1,1c-0.1,1.4,0.3,2.9,1.1,4.1
c1.6,2.3,4.4,3.4,7.1,2.7c0.6-0.2,1.2-0.4,1.7-0.7l27.4-17.5c1.4-0.9,2.3-2.2,2.6-3.8c0.3-1.6-0.1-3.3-1-4.6
c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7L48,68.3c-1.7,1.1-3.6,1.9-5.6,2.4c-8.9,2.3-18.2-1.2-23.4-8.7
c-3.1-4.4-4.4-9.9-3.4-15.3c0.9-5.2,4.1-9.9,8.6-12.7l27.4-17.5c1.7-1.1,3.6-1.9,5.6-2.5c8.9-2.3,18.2,1.2,23.4,8.7
c3.2,4.4,4.4,9.9,3.5,15.3c-0.2,0.9-0.4,1.7-0.7,2.6l-0.5,1.6l-1.4-1c-3.3-2.4-6.9-4.2-10.8-5.4l-1-0.3l0.1-1
c0.1-1.4-0.3-2.9-1.1-4.1c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7L32.5,46.1c-1.4,0.9-2.3,2.2-2.6,3.8
c-0.3,1.6,0.1,3.3,1,4.6c1.6,2.3,4.4,3.3,7.1,2.6c0.6-0.2,1.2-0.4,1.7-0.7l10.5-6.7c1.7-1.1,3.6-1.9,5.6-2.5
c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.9,5.2-4.1,9.9-8.6,12.7l-27.4,17.5C44.9,102.5,43,103.3,41,103.9"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -1,20 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg enable-background="new 0 0 98.4 118.3" viewBox="0 0 98.4 118.3" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#ff3e00" stroke-width=".25"><path d="m92 15.7c-10.9-15.6-32.5-20.3-48.2-10.3l-27.5 17.5c-7.5 4.7-12.7 12.4-14.2 21.1-1.3 7.3-.2 14.8 3.3 21.3-2.4 3.6-4 7.6-4.7 11.8-1.6 8.9.5 18.1 5.7 25.4 11 15.7 32.6 20.3 48.2 10.4l27.5-17.5c7.5-4.7 12.7-12.4 14.2-21.1 1.3-7.3.2-14.8-3.3-21.3 2.4-3.6 4-7.6 4.7-11.8 1.7-9-.4-18.1-5.7-25.5"/><path d="m41.1 104c-8.9 2.3-18.2-1.2-23.4-8.7-3.2-4.4-4.4-9.9-3.5-15.3.2-.9.4-1.7.6-2.6l.5-1.6 1.4 1c3.3 2.4 6.9 4.2 10.8 5.4l1 .3-.1 1c-.1 1.4.3 2.9 1.1 4.1 1.6 2.3 4.4 3.4 7.1 2.7.6-.2 1.2-.4 1.7-.7l27.4-17.5c1.4-.9 2.3-2.2 2.6-3.8s-.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6-.6.2-1.2.4-1.7.7l-10.5 6.7c-1.7 1.1-3.6 1.9-5.6 2.4-8.9 2.3-18.2-1.2-23.4-8.7-3.1-4.4-4.4-9.9-3.4-15.3.9-5.2 4.1-9.9 8.6-12.7l27.4-17.5c1.7-1.1 3.6-1.9 5.6-2.5 8.9-2.3 18.2 1.2 23.4 8.7 3.2 4.4 4.4 9.9 3.5 15.3-.2.9-.4 1.7-.7 2.6l-.4 1.6-1.4-1c-3.3-2.4-6.9-4.2-10.8-5.4l-1-.3.1-1c.1-1.4-.3-2.9-1.1-4.1-1.6-2.3-4.4-3.3-7.1-2.6-.6.2-1.2.4-1.7.7l-27.4 17.5c-1.4.8-2.3 2.2-2.6 3.8s.1 3.3 1 4.6c1.6 2.3 4.4 3.3 7.1 2.6.6-.2 1.2-.4 1.7-.7l10.5-6.7c1.7-1.1 3.6-1.9 5.6-2.5 8.9-2.3 18.2 1.2 23.4 8.7 3.2 4.4 4.4 9.9 3.5 15.3-.9 5.2-4.1 9.9-8.6 12.7l-27.4 17.5c-1.8 1.1-3.7 2-5.7 2.5"/></g></svg>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 98.4 118.3" style="enable-background:new 0 0 98.4 118.3;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#FF3E00;stroke-width:0.25;}
</style>
<path class="st0" d="M92,15.7C81.1,0.1,59.5-4.6,43.8,5.4L16.3,22.9C8.8,27.6,3.6,35.3,2.1,44c-1.3,7.3-0.2,14.8,3.3,21.3
c-2.4,3.6-4,7.6-4.7,11.8c-1.6,8.9,0.5,18.1,5.7,25.4c11,15.7,32.6,20.3,48.2,10.4l27.5-17.5c7.5-4.7,12.7-12.4,14.2-21.1
c1.3-7.3,0.2-14.8-3.3-21.3c2.4-3.6,4-7.6,4.7-11.8C99.4,32.2,97.3,23.1,92,15.7"/>
<path class="st0" d="M41.1,104c-8.9,2.3-18.2-1.2-23.4-8.7c-3.2-4.4-4.4-9.9-3.5-15.3c0.2-0.9,0.4-1.7,0.6-2.6l0.5-1.6l1.4,1
c3.3,2.4,6.9,4.2,10.8,5.4l1,0.3l-0.1,1c-0.1,1.4,0.3,2.9,1.1,4.1c1.6,2.3,4.4,3.4,7.1,2.7c0.6-0.2,1.2-0.4,1.7-0.7l27.4-17.5
c1.4-0.9,2.3-2.2,2.6-3.8c0.3-1.6-0.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7l-10.5,6.7
c-1.7,1.1-3.6,1.9-5.6,2.4c-8.9,2.3-18.2-1.2-23.4-8.7c-3.1-4.4-4.4-9.9-3.4-15.3c0.9-5.2,4.1-9.9,8.6-12.7l27.4-17.5
c1.7-1.1,3.6-1.9,5.6-2.5c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.2,0.9-0.4,1.7-0.7,2.6L83,42.4l-1.4-1
c-3.3-2.4-6.9-4.2-10.8-5.4l-1-0.3l0.1-1c0.1-1.4-0.3-2.9-1.1-4.1c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7L32.6,46.2
C31.2,47,30.3,48.4,30,50c-0.3,1.6,0.1,3.3,1,4.6c1.6,2.3,4.4,3.3,7.1,2.6c0.6-0.2,1.2-0.4,1.7-0.7l10.5-6.7
c1.7-1.1,3.6-1.9,5.6-2.5c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.9,5.2-4.1,9.9-8.6,12.7l-27.4,17.5
C45,102.6,43.1,103.5,41.1,104"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -1,4 +1 @@
<svg id="svelte" xmlns="http://www.w3.org/2000/svg" width="256" height="300" viewBox="0 0 256 300"> <svg height="300" viewBox="0 0 256 300" width="256" xmlns="http://www.w3.org/2000/svg"><path d="m44.41 265.87a14 14 0 0 1 -8-2.26 11.53 11.53 0 0 1 -4.66-6.18l4.86-1.78a9 9 0 0 0 3.18 3.84 8.31 8.31 0 0 0 4.81 1.42 7 7 0 0 0 4.59-1.39 4.83 4.83 0 0 0 1.71-4 4.25 4.25 0 0 0 -.47-2 5.93 5.93 0 0 0 -1.05-1.52 7 7 0 0 0 -1.84-1.22c-.83-.41-1.51-.73-2.06-.94s-1.35-.51-2.41-.87c-1.32-.46-2.31-.83-3-1.09a23.69 23.69 0 0 1 -2.61-1.32 9.63 9.63 0 0 1 -2.43-1.83 8.76 8.76 0 0 1 -1.46-2.41 8.11 8.11 0 0 1 -.57-3.32 8.29 8.29 0 0 1 3-6.45 11.79 11.79 0 0 1 8-2.61 12.16 12.16 0 0 1 7 1.89 9.11 9.11 0 0 1 3.69 5l-4.69 1.6a5.53 5.53 0 0 0 -2.31-2.56 7.77 7.77 0 0 0 -4-1 6.23 6.23 0 0 0 -3.9 1.09 3.66 3.66 0 0 0 -1.41 3.07 3.22 3.22 0 0 0 1.09 2.39 7.61 7.61 0 0 0 2.21 1.58c.8.34 2 .78 3.63 1.34l2.2.82c.48.19 1.19.49 2.11.92a14.36 14.36 0 0 1 2.14 1.17 19.05 19.05 0 0 1 1.73 1.41 7.66 7.66 0 0 1 1.51 1.8 10.77 10.77 0 0 1 .89 2.24 10.13 10.13 0 0 1 .37 2.8 9.38 9.38 0 0 1 -3.26 7.62 13 13 0 0 1 -8.59 2.75zm31.81-.6-11.91-34.73h5.56l7.89 24.21a39.48 39.48 0 0 1 1.09 4 39.48 39.48 0 0 1 1.09-4l7.79-24.21h5.51l-11.86 34.73zm27.54 0v-34.73h21.54v4.86h-16.38v9.53h10.57v4.86h-10.57v10.62h17.47v4.86zm35.78 0v-34.73h5.16v29.77h17v5zm41.58-29.77v29.77h-5.12v-29.77h-9.83v-5h24.81v5zm20.49 29.77v-34.73h21.54v4.86h-16.38v9.53h10.57v4.86h-10.57v10.62h17.47v4.86z" fill="#4a4a55"/><path d="m191.87 52.11c-16.34-23.39-48.61-30.32-71.94-15.45l-40.93 26.11a47 47 0 0 0 -21.28 31.49 49.49 49.49 0 0 0 4.88 31.74 47.16 47.16 0 0 0 -7 17.57 50.1 50.1 0 0 0 8.56 37.89c16.34 23.38 48.61 30.31 71.94 15.45l41-26.12a47 47 0 0 0 21.24-31.49 49.51 49.51 0 0 0 -4.88-31.78 47.16 47.16 0 0 0 7-17.57 50.07 50.07 0 0 0 -8.56-37.88" fill="#ff3e00"/><path d="m115.93 183.7a32.55 32.55 0 0 1 -34.93-12.95 30.11 30.11 0 0 1 -5.15-22.75 29.12 29.12 0 0 1 1-3.83l.77-2.35 2.1 1.54a52.87 52.87 0 0 0 16 8l1.52.46-.14 1.52a9.17 9.17 0 0 0 1.65 6.09 9.79 9.79 0 0 0 10.52 3.9 9 9 0 0 0 2.52-1.1l41-26.11a8.53 8.53 0 0 0 3.85-5.71 9 9 0 0 0 -1.55-6.87 9.82 9.82 0 0 0 -10.52-3.9 9.08 9.08 0 0 0 -2.51 1.1l-15.64 10a30.08 30.08 0 0 1 -8.32 3.66 32.55 32.55 0 0 1 -34.95-13 30.15 30.15 0 0 1 -5.15-22.79 28.24 28.24 0 0 1 12.77-18.93l41-26.12a30 30 0 0 1 8.33-3.65 32.54 32.54 0 0 1 34.93 13 30.11 30.11 0 0 1 5.15 22.78 29.12 29.12 0 0 1 -1 3.83l-.77 2.35-2.1-1.54a52.64 52.64 0 0 0 -16-8l-1.52-.46.14-1.52a9.2 9.2 0 0 0 -1.65-6.1 9.82 9.82 0 0 0 -10.52-3.9 9 9 0 0 0 -2.52 1.11l-41 26.11a8.49 8.49 0 0 0 -3.85 5.7 9 9 0 0 0 1.55 6.87 9.81 9.81 0 0 0 10.51 3.86 9 9 0 0 0 2.52-1.11l15.63-10a29.5 29.5 0 0 1 8.32-3.66 32.54 32.54 0 0 1 34.93 12.95 30.11 30.11 0 0 1 5.15 22.82 28.26 28.26 0 0 1 -12.77 18.94l-41 26.11a30.27 30.27 0 0 1 -8.33 3.66" fill="#fff"/></svg>
<path id="logotype" fill="#4a4a55" d="M44.41,265.87a14,14,0,0,1-8-2.26,11.53,11.53,0,0,1-4.66-6.18l4.86-1.78a9,9,0,0,0,3.18,3.84,8.31,8.31,0,0,0,4.81,1.42,7,7,0,0,0,4.59-1.39,4.83,4.83,0,0,0,1.71-4,4.25,4.25,0,0,0-.47-2A5.93,5.93,0,0,0,49.38,252a7,7,0,0,0-1.84-1.22c-.83-.41-1.51-.73-2.06-.94s-1.35-.51-2.41-.87c-1.32-.46-2.31-.83-3-1.09a23.69,23.69,0,0,1-2.61-1.32,9.63,9.63,0,0,1-2.43-1.83,8.76,8.76,0,0,1-1.46-2.41A8.11,8.11,0,0,1,33,239a8.29,8.29,0,0,1,3-6.45A11.79,11.79,0,0,1,44,229.94a12.16,12.16,0,0,1,7,1.89,9.11,9.11,0,0,1,3.69,5L50,238.43a5.53,5.53,0,0,0-2.31-2.56,7.77,7.77,0,0,0-4-1,6.23,6.23,0,0,0-3.9,1.09,3.66,3.66,0,0,0-1.41,3.07,3.22,3.22,0,0,0,1.09,2.39A7.61,7.61,0,0,0,41.68,243c.8.34,2,.78,3.63,1.34l2.2.82c.48.19,1.19.49,2.11.92a14.36,14.36,0,0,1,2.14,1.17,19.05,19.05,0,0,1,1.73,1.41A7.66,7.66,0,0,1,55,250.46a10.77,10.77,0,0,1,.89,2.24,10.13,10.13,0,0,1,.37,2.8A9.38,9.38,0,0,1,53,263.12,13,13,0,0,1,44.41,265.87Zm31.81-.6L64.31,230.54h5.56l7.89,24.21a39.48,39.48,0,0,1,1.09,4,39.48,39.48,0,0,1,1.09-4l7.79-24.21h5.51L81.38,265.27Zm27.54,0V230.54H125.3v4.86H108.92v9.53h10.57v4.86H108.92v10.62h17.47v4.86Zm35.78,0V230.54h5.16v29.77h17v5Zm41.58-29.77v29.77H176V235.5h-9.83v-5h24.81v5Zm20.49,29.77V230.54h21.54v4.86H206.77v9.53h10.57v4.86H206.77v10.62h17.47v4.86Z"/>
<path id="back" fill="#ff3e00" d="M191.87,52.11c-16.34-23.39-48.61-30.32-71.94-15.45L79,62.77A47,47,0,0,0,57.72,94.26,49.49,49.49,0,0,0,62.6,126a47.16,47.16,0,0,0-7,17.57,50.1,50.1,0,0,0,8.56,37.89c16.34,23.38,48.61,30.31,71.94,15.45l41-26.12a47,47,0,0,0,21.24-31.49,49.51,49.51,0,0,0-4.88-31.78,47.16,47.16,0,0,0,7-17.57,50.07,50.07,0,0,0-8.56-37.88"/>
<path id="front" fill="#fff" d="M115.93,183.7A32.55,32.55,0,0,1,81,170.75,30.11,30.11,0,0,1,75.85,148a29.12,29.12,0,0,1,1-3.83l.77-2.35,2.1,1.54a52.87,52.87,0,0,0,16,8l1.52.46-.14,1.52a9.17,9.17,0,0,0,1.65,6.09,9.79,9.79,0,0,0,10.52,3.9,9,9,0,0,0,2.52-1.1l41-26.11a8.53,8.53,0,0,0,3.85-5.71,9,9,0,0,0-1.55-6.87,9.82,9.82,0,0,0-10.52-3.9,9.08,9.08,0,0,0-2.51,1.1l-15.64,10a30.08,30.08,0,0,1-8.32,3.66A32.55,32.55,0,0,1,83.15,121.4,30.15,30.15,0,0,1,78,98.61,28.24,28.24,0,0,1,90.77,79.68l41-26.12a30,30,0,0,1,8.33-3.65,32.54,32.54,0,0,1,34.93,13,30.11,30.11,0,0,1,5.15,22.78,29.12,29.12,0,0,1-1,3.83l-.77,2.35-2.1-1.54a52.64,52.64,0,0,0-16-8l-1.52-.46.14-1.52a9.2,9.2,0,0,0-1.65-6.1,9.82,9.82,0,0,0-10.52-3.9,9,9,0,0,0-2.52,1.11l-41,26.11a8.49,8.49,0,0,0-3.85,5.7,9,9,0,0,0,1.55,6.87A9.81,9.81,0,0,0,111.45,114a9,9,0,0,0,2.52-1.11l15.63-10a29.5,29.5,0,0,1,8.32-3.66,32.54,32.54,0,0,1,34.93,12.95A30.11,30.11,0,0,1,178,135a28.26,28.26,0,0,1-12.77,18.94l-41,26.11a30.27,30.27,0,0,1-8.33,3.66"/></svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -1,20 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg enable-background="new 0 0 98.1 118" viewBox="0 0 98.1 118" xmlns="http://www.w3.org/2000/svg"><path d="m91.8 15.6c-10.9-15.7-32.6-20.3-48.2-10.4l-27.5 17.6c-7.5 4.7-12.7 12.4-14.2 21.1-1.3 7.3-.2 14.8 3.3 21.3-2.4 3.6-4 7.6-4.7 11.8-1.6 8.9.5 18.1 5.7 25.4 11 15.7 32.6 20.3 48.2 10.4l27.5-17.5c7.5-4.7 12.7-12.4 14.2-21.1 1.3-7.3.2-14.8-3.3-21.3 2.4-3.6 4-7.6 4.7-11.8 1.7-9-.4-18.2-5.7-25.5" fill="#ff3e00"/><path d="m40.9 103.9c-8.9 2.3-18.2-1.2-23.4-8.7-3.2-4.4-4.4-9.9-3.5-15.3.2-.9.4-1.7.6-2.6l.5-1.6 1.4 1c3.3 2.4 6.9 4.2 10.8 5.4l1 .3-.1 1c-.1 1.4.3 2.9 1.1 4.1 1.6 2.3 4.4 3.4 7.1 2.7.6-.2 1.2-.4 1.7-.7l27.4-17.5c1.4-.9 2.3-2.2 2.6-3.8s-.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6-.6.2-1.2.4-1.7.7l-10.5 6.7c-1.7 1.1-3.6 1.9-5.6 2.4-8.9 2.3-18.2-1.2-23.4-8.7-3.1-4.4-4.4-9.9-3.4-15.3.9-5.2 4.1-9.9 8.6-12.7l27.5-17.5c1.7-1.1 3.6-1.9 5.6-2.5 8.9-2.3 18.2 1.2 23.4 8.7 3.2 4.4 4.4 9.9 3.5 15.3-.2.9-.4 1.7-.7 2.6l-.5 1.6-1.4-1c-3.3-2.4-6.9-4.2-10.8-5.4l-1-.3.1-1c.1-1.4-.3-2.9-1.1-4.1-1.6-2.3-4.4-3.3-7.1-2.6-.6.2-1.2.4-1.7.7l-27.4 17.5c-1.4.9-2.3 2.2-2.6 3.8s.1 3.3 1 4.6c1.6 2.3 4.4 3.3 7.1 2.6.6-.2 1.2-.4 1.7-.7l10.5-6.7c1.7-1.1 3.6-1.9 5.6-2.5 8.9-2.3 18.2 1.2 23.4 8.7 3.2 4.4 4.4 9.9 3.5 15.3-.9 5.2-4.1 9.9-8.6 12.7l-27.5 17.5c-1.7 1.1-3.6 1.9-5.6 2.5" fill="#fff"/></svg>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 98.1 118" style="enable-background:new 0 0 98.1 118;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FF3E00;}
.st1{fill:#FFFFFF;}
</style>
<path class="st0" d="M91.8,15.6C80.9-0.1,59.2-4.7,43.6,5.2L16.1,22.8C8.6,27.5,3.4,35.2,1.9,43.9c-1.3,7.3-0.2,14.8,3.3,21.3
c-2.4,3.6-4,7.6-4.7,11.8c-1.6,8.9,0.5,18.1,5.7,25.4c11,15.7,32.6,20.3,48.2,10.4l27.5-17.5c7.5-4.7,12.7-12.4,14.2-21.1
c1.3-7.3,0.2-14.8-3.3-21.3c2.4-3.6,4-7.6,4.7-11.8C99.2,32.1,97.1,22.9,91.8,15.6"/>
<path class="st1" d="M40.9,103.9c-8.9,2.3-18.2-1.2-23.4-8.7c-3.2-4.4-4.4-9.9-3.5-15.3c0.2-0.9,0.4-1.7,0.6-2.6l0.5-1.6l1.4,1
c3.3,2.4,6.9,4.2,10.8,5.4l1,0.3l-0.1,1c-0.1,1.4,0.3,2.9,1.1,4.1c1.6,2.3,4.4,3.4,7.1,2.7c0.6-0.2,1.2-0.4,1.7-0.7L65.5,72
c1.4-0.9,2.3-2.2,2.6-3.8c0.3-1.6-0.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7l-10.5,6.7
c-1.7,1.1-3.6,1.9-5.6,2.4c-8.9,2.3-18.2-1.2-23.4-8.7c-3.1-4.4-4.4-9.9-3.4-15.3c0.9-5.2,4.1-9.9,8.6-12.7l27.5-17.5
c1.7-1.1,3.6-1.9,5.6-2.5c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.2,0.9-0.4,1.7-0.7,2.6l-0.5,1.6l-1.4-1
c-3.3-2.4-6.9-4.2-10.8-5.4l-1-0.3l0.1-1c0.1-1.4-0.3-2.9-1.1-4.1c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7L32.4,46.1
c-1.4,0.9-2.3,2.2-2.6,3.8s0.1,3.3,1,4.6c1.6,2.3,4.4,3.3,7.1,2.6c0.6-0.2,1.2-0.4,1.7-0.7l10.5-6.7c1.7-1.1,3.6-1.9,5.6-2.5
c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.9,5.2-4.1,9.9-8.6,12.7l-27.5,17.5C44.8,102.5,42.9,103.3,40.9,103.9"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m20 8-8 5-8-5v-2l8 5 8-5m0-2h-16c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-12c0-1.11-.9-2-2-2z" fill="#ff3e00"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#ff3e00" d="M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z" />
</svg>

Before

Width:  |  Height:  |  Size: 280 B

After

Width:  |  Height:  |  Size: 221 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m19 20h-15c-1.11 0-2-.9-2-2v-12c0-1.11.89-2 2-2h6l2 2h7a2 2 0 0 1 2 2h-17v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ff3e00"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#ff3e00" d="M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z" />
</svg>

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 242 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m10 4h-6c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-10c0-1.11-.9-2-2-2h-8z" fill="#ff3e00"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#ff3e00" d="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z" />
</svg>

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 200 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m11 8h2v8h-2zm-3.33 0h-3.34c-.8 0-1.33.67-1.33 1.33v5.34c0 .66.53 1.33 1.33 1.33h3.34c.8 0 1.33-.67 1.33-1.33v-2.67h-2v2h-2v-4h4v-.67c0-.66-.53-1.33-1.33-1.33m13.33 2v-2h-6v8h2v-2h2.5v-2h-2.5v-2z" fill="#ff3e00"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#ff3e00" d="M11,8H13V16H11V8M7.67,8H4.33C3.53,8 3,8.67 3,9.33V14.67C3,15.33 3.53,16 4.33,16H7.67C8.47,16 9,15.33 9,14.67V12H7V14H5V10H9V9.33C9,8.67 8.47,8 7.67,8M21,10V8H15V16H17V14H19.5V12H17V10H21Z" />
</svg>

Before

Width:  |  Height:  |  Size: 354 B

After

Width:  |  Height:  |  Size: 311 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m12 11.5a2.5 2.5 0 0 1 -2.5-2.5 2.5 2.5 0 0 1 2.5-2.5 2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1 -2.5 2.5m0-9.5a7 7 0 0 0 -7 7c0 5.25 7 13 7 13s7-7.75 7-13a7 7 0 0 0 -7-7z" fill="#ff3e00"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#ff3e00" d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" />
</svg>

Before

Width:  |  Height:  |  Size: 334 B

After

Width:  |  Height:  |  Size: 280 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m2 16v-8h2l3 3 3-3h2v8h-2v-5.17l-3 3-3-3v5.17zm14-8h3v4h2.5l-4 4.5-4-4.5h2.5z" fill="#ff3e00"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#ff3e00" d="M2,16V8H4L7,11L10,8H12V16H10V10.83L7,13.83L4,10.83V16H2M16,8H19V12H21.5L17.5,16.5L13.5,12H16V8Z" />
</svg>

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 193 B

@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?> <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m14 17h-7v-2h7m3-2h-10v-2h10m0-2h-10v-2h10m2-4h-14c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-14c0-1.11-.9-2-2-2z" fill="#ff3e00"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" viewBox="0 0 24 24">
<path fill="#ff3e00" d="M14,17H7V15H14M17,13H7V11H17M17,9H7V7H17M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z" />
</svg>

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 108 KiB

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

@ -3,27 +3,45 @@ import PendingBlock from './PendingBlock';
import ThenBlock from './ThenBlock'; import ThenBlock from './ThenBlock';
import CatchBlock from './CatchBlock'; import CatchBlock from './CatchBlock';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import { Pattern } from 'estree';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces';
import traverse_destructure_pattern from '../utils/traverse_destructure_pattern';
export default class AwaitBlock extends Node { export default class AwaitBlock extends Node {
type: 'AwaitBlock'; type: 'AwaitBlock';
expression: Expression; expression: Expression;
value: string; value: DestructurePattern;
error: string; error: DestructurePattern;
pending: PendingBlock; pending: PendingBlock;
then: ThenBlock; then: ThenBlock;
catch: CatchBlock; catch: CatchBlock;
constructor(component, parent, scope, info) { constructor(component: Component, parent, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.expression = new Expression(component, this, scope, info.expression); this.expression = new Expression(component, this, scope, info.expression);
this.value = info.value; this.value = info.value && new DestructurePattern(info.value);
this.error = info.error; this.error = info.error && new DestructurePattern(info.error);
this.pending = new PendingBlock(component, this, scope, info.pending); this.pending = new PendingBlock(component, this, scope, info.pending);
this.then = new ThenBlock(component, this, scope, info.then); this.then = new ThenBlock(component, this, scope, info.then);
this.catch = new CatchBlock(component, this, scope, info.catch); this.catch = new CatchBlock(component, this, scope, info.catch);
} }
} }
export class DestructurePattern {
pattern: Pattern;
expressions: string[];
identifier_name: string | undefined;
constructor(pattern: Pattern) {
this.pattern = pattern;
this.expressions = [];
traverse_destructure_pattern(pattern, (node) => this.expressions.push(node.name));
this.identifier_name = this.pattern.type === 'Identifier' ? this.pattern.name : undefined;
}
}

@ -72,6 +72,11 @@ export default class Binding extends Node {
}); });
variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true; 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'); const type = parent.get_static_attribute_value('type');

@ -1,16 +1,23 @@
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock'; import AbstractBlock from './shared/AbstractBlock';
import AwaitBlock from './AwaitBlock';
import Component from '../Component';
import { TemplateNode } from '../../interfaces';
export default class CatchBlock extends AbstractBlock { export default class CatchBlock extends AbstractBlock {
type: 'CatchBlock'; type: 'CatchBlock';
scope: TemplateScope; scope: TemplateScope;
constructor(component, parent, scope, info) { constructor(component: Component, parent: AwaitBlock, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.scope = scope.child(); this.scope = scope.child();
this.scope.add(parent.error, parent.expression.dependencies, this); if (parent.error) {
parent.error.expressions.forEach(expression => {
this.scope.add(expression, parent.expression.dependencies, this);
});
}
this.children = map_children(component, parent, this.scope, info.children); this.children = map_children(component, parent, this.scope, info.children);
if (!info.skip) { if (!info.skip) {

@ -278,7 +278,7 @@ export default class Element extends Node {
} }
validate_attributes() { validate_attributes() {
const { component } = this; const { component, parent } = this;
const attribute_map = new Map(); const attribute_map = new Map();
@ -395,26 +395,10 @@ export default class Element extends Node {
component.slot_outlets.add(name); component.slot_outlets.add(name);
} }
let ancestor = this.parent; if (!(parent.type === 'InlineComponent' || within_custom_element(parent))) {
do {
if (ancestor.type === 'InlineComponent') break;
if (ancestor.type === 'Element' && /-/.test(ancestor.name)) break;
if (ancestor.type === 'IfBlock' || ancestor.type === 'EachBlock') {
const type = ancestor.type === 'IfBlock' ? 'if' : 'each';
const message = `Cannot place slotted elements inside an ${type}-block`;
component.error(attribute, {
code: `invalid-slotted-content`,
message
});
}
} while (ancestor = ancestor.parent);
if (!ancestor) {
component.error(attribute, { component.error(attribute, {
code: `invalid-slotted-content`, code: `invalid-slotted-content`,
message: `Element with a slot='...' attribute must be a descendant of a component or custom element` message: `Element with a slot='...' attribute must be a child of a component or a descendant of a custom element`,
}); });
} }
} }
@ -776,3 +760,12 @@ function should_have_attribute(
message: `A11y: <${name}> element should have ${article} ${sequence} attribute` message: `A11y: <${name}> element should have ${article} ${sequence} attribute`
}); });
} }
function within_custom_element(parent: INode) {
while (parent) {
if (parent.type === 'InlineComponent') return false;
if (parent.type === 'Element' && /-/.test(parent.name)) return true;
parent = parent.parent;
}
return false;
}

@ -1,7 +1,7 @@
import Node from './shared/Node'; import Node from './shared/Node';
import Component from '../Component'; import Component from '../Component';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import { Identifier } from 'estree'; import { BasePattern, Identifier } from 'estree';
const applicable = new Set(['Identifier', 'ObjectExpression', 'ArrayExpression', 'Property']); const applicable = new Set(['Identifier', 'ObjectExpression', 'ArrayExpression', 'Property']);
@ -22,7 +22,7 @@ export default class Let extends Node {
this.value = info.expression; this.value = info.expression;
walk(info.expression, { walk(info.expression, {
enter(node) { enter(node: Identifier|BasePattern) {
if (!applicable.has(node.type)) { if (!applicable.has(node.type)) {
component.error(node as any, { component.error(node as any, {
code: 'invalid-let', code: 'invalid-let',
@ -31,16 +31,16 @@ export default class Let extends Node {
} }
if (node.type === 'Identifier') { if (node.type === 'Identifier') {
names.push(node.name); names.push((node as Identifier).name);
} }
// slightly unfortunate hack // slightly unfortunate hack
if (node.type === 'ArrayExpression') { if (node.type === 'ArrayExpression') {
(node as any).type = 'ArrayPattern'; node.type = 'ArrayPattern';
} }
if (node.type === 'ObjectExpression') { 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 TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces'; 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 { export default class Text extends Node {
type: 'Text'; type: 'Text';
data: string; data: string;
@ -13,4 +25,21 @@ export default class Text extends Node {
this.data = info.data; this.data = info.data;
this.synthetic = info.synthetic || false; 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);
}
} }

@ -1,16 +1,23 @@
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock'; import AbstractBlock from './shared/AbstractBlock';
import AwaitBlock from './AwaitBlock';
import Component from '../Component';
import { TemplateNode } from '../../interfaces';
export default class ThenBlock extends AbstractBlock { export default class ThenBlock extends AbstractBlock {
type: 'ThenBlock'; type: 'ThenBlock';
scope: TemplateScope; scope: TemplateScope;
constructor(component, parent, scope, info) { constructor(component: Component, parent: AwaitBlock, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.scope = scope.child(); this.scope = scope.child();
this.scope.add(parent.value, parent.expression.dependencies, this); if (parent.value) {
parent.value.expressions.forEach(expression => {
this.scope.add(expression, parent.expression.dependencies, this);
});
}
this.children = map_children(component, parent, this.scope, info.children); this.children = map_children(component, parent, this.scope, info.children);
if (!info.skip) { if (!info.skip) {

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

@ -46,6 +46,7 @@ export default class Block {
}>; }>;
chunks: { chunks: {
declarations: Array<Node | Node[]>;
init: Array<Node | Node[]>; init: Array<Node | Node[]>;
create: Array<Node | Node[]>; create: Array<Node | Node[]>;
claim: Array<Node | Node[]>; claim: Array<Node | Node[]>;
@ -93,6 +94,7 @@ export default class Block {
this.bindings = options.bindings; this.bindings = options.bindings;
this.chunks = { this.chunks = {
declarations: [],
init: [], init: [],
create: [], create: [],
claim: [], claim: [],
@ -292,10 +294,14 @@ export default class Block {
if (this.chunks.mount.length === 0) { if (this.chunks.mount.length === 0) {
properties.mount = noop; properties.mount = noop;
} else { } else if (this.event_listeners.length === 0) {
properties.mount = x`function #mount(#target, anchor) { properties.mount = x`function #mount(#target, anchor) {
${this.chunks.mount} ${this.chunks.mount}
}`; }`;
} else {
properties.mount = x`function #mount(#target, anchor, #remount) {
${this.chunks.mount}
}`;
} }
if (this.has_update_method || this.maintain_context) { if (this.has_update_method || this.maintain_context) {
@ -384,6 +390,8 @@ export default class Block {
const block = dev && this.get_unique_name('block'); const block = dev && this.get_unique_name('block');
const body = b` const body = b`
${this.chunks.declarations}
${Array.from(this.variables.values()).map(({ id, init }) => { ${Array.from(this.variables.values()).map(({ id, init }) => {
return init return init
? b`let ${id} = ${init}` ? b`let ${id} = ${init}`
@ -411,9 +419,8 @@ export default class Block {
return body; return body;
} }
has_content() { has_content(): boolean {
return this.renderer.options.dev || return !!this.first ||
this.first ||
this.event_listeners.length > 0 || this.event_listeners.length > 0 ||
this.chunks.intro.length > 0 || this.chunks.intro.length > 0 ||
this.chunks.outro.length > 0 || this.chunks.outro.length > 0 ||
@ -454,7 +461,10 @@ export default class Block {
if (this.event_listeners.length === 1) { if (this.event_listeners.length === 1) {
this.chunks.mount.push( this.chunks.mount.push(
b`${dispose} = ${this.event_listeners[0]};` b`
if (#remount) ${dispose}();
${dispose} = ${this.event_listeners[0]};
`
); );
this.chunks.destroy.push( this.chunks.destroy.push(
@ -462,6 +472,7 @@ export default class Block {
); );
} else { } else {
this.chunks.mount.push(b` this.chunks.mount.push(b`
if (#remount) @run_all(${dispose});
${dispose} = [ ${dispose} = [
${this.event_listeners} ${this.event_listeners}
]; ];

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

@ -71,14 +71,24 @@ export default function dom(
} }
const uses_props = component.var_lookup.has('$$props'); 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 props = component.vars.filter(variable => !variable.module && variable.export_name);
const writable_props = props.filter(variable => variable.writable); 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` ? x`
${$$props} => { ${$$props} => {
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_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 => ${writable_props.map(prop =>
b`if ('${prop.export_name}' in ${$$props}) ${renderer.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.export_name}`)};` b`if ('${prop.export_name}' in ${$$props}) ${renderer.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.export_name}`)};`
)} )}
@ -91,7 +101,10 @@ export default function dom(
const accessors = []; const accessors = [];
const not_equal = component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`; const not_equal = component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;
let dev_props_check; let inject_state; let capture_state; let dev_props_check: Node[] | Node;
let inject_state: Expression;
let capture_state: Expression;
let props_inject: Node[] | Node;
props.forEach(prop => { props.forEach(prop => {
const variable = component.var_lookup.get(prop.name); const variable = component.var_lookup.get(prop.name);
@ -164,27 +177,30 @@ export default function dom(
`; `;
} }
capture_state = (uses_props || writable_props.length > 0) ? x` const capturable_vars = component.vars.filter(v => !v.internal && !v.global && !v.name.startsWith('$$'));
() => {
return { ${component.vars.filter(prop => prop.writable).map(prop => p`${prop.name}`)} };
}
` : x`
() => {
return {};
}
`;
const writable_vars = component.vars.filter(variable => !variable.module && variable.writable); if (capturable_vars.length > 0) {
inject_state = (uses_props || writable_vars.length > 0) ? x` capture_state = x`() => ({ ${capturable_vars.map(prop => p`${prop.name}`)} })`;
${$$props} => { }
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)}
${writable_vars.map(prop => b` const injectable_vars = capturable_vars.filter(v => !v.module && v.writable && v.name[0] !== '$');
if ('${prop.name}' in $$props) ${renderer.invalidate(prop.name, x`${prop.name} = ${$$props}.${prop.name}`)};
`)} if (uses_props || injectable_vars.length > 0) {
} inject_state = x`
` : x` ${$$props} => {
${$$props} => {} ${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)}
`; ${injectable_vars.map(
v => b`if ('${v.name}' in $$props) ${renderer.invalidate(v.name, x`${v.name} = ${$$props}.${v.name}`)};`
)}
}
`;
props_inject = b`
if ($$props && "$$inject" in $$props) {
$$self.$inject_state($$props.$$inject);
}
`;
}
} }
// instrument assignments // instrument assignments
@ -194,7 +210,7 @@ export default function dom(
let execution_context: Node | null = null; let execution_context: Node | null = null;
walk(component.ast.instance.content, { walk(component.ast.instance.content, {
enter(node) { enter(node: Node) {
if (map.has(node)) { if (map.has(node)) {
scope = map.get(node) as Scope; scope = map.get(node) as Scope;
@ -206,7 +222,7 @@ export default function dom(
} }
}, },
leave(node) { leave(node: Node) {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
@ -246,11 +262,19 @@ export default function dom(
} }
const args = [x`$$self`]; const args = [x`$$self`];
if (props.length > 0 || component.has_reactive_assignments || component.slots.size > 0) { const has_invalidate = props.length > 0 ||
component.has_reactive_assignments ||
component.slots.size > 0 ||
capture_state ||
inject_state;
if (has_invalidate) {
args.push(x`$$props`, x`$$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) { if (has_create_fragment) {
body.push(b` body.push(b`
function create_fragment(#ctx) { function create_fragment(#ctx) {
@ -289,12 +313,15 @@ export default function dom(
const initial_context = renderer.context.slice(0, i + 1); const initial_context = renderer.context.slice(0, i + 1);
const has_definition = ( const has_definition = (
component.compile_options.dev ||
(instance_javascript && instance_javascript.length > 0) || (instance_javascript && instance_javascript.length > 0) ||
filtered_props.length > 0 || filtered_props.length > 0 ||
uses_props || uses_props ||
component.partly_hoisted.length > 0 || component.partly_hoisted.length > 0 ||
initial_context.length > 0 || initial_context.length > 0 ||
component.reactive_declarations.length > 0 component.reactive_declarations.length > 0 ||
capture_state ||
inject_state
); );
const definition = has_definition const definition = has_definition
@ -324,20 +351,20 @@ export default function dom(
component.reactive_declarations.forEach(d => { component.reactive_declarations.forEach(d => {
const dependencies = Array.from(d.dependencies); 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 writable = dependencies.filter(n => {
const variable = component.var_lookup.get(n); const variable = component.var_lookup.get(n);
return variable && (variable.export_name || variable.mutated || variable.reassigned); 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 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) statement = b`if (${condition}) { ${statement} }`[0] as Statement;
if (condition || uses_props) { if (condition || uses_rest_or_props) {
reactive_declarations.push(statement); reactive_declarations.push(statement);
} else { } else {
fixed_reactive_declarations.push(statement); fixed_reactive_declarations.push(statement);
@ -366,7 +393,7 @@ export default function dom(
}); });
let unknown_props_check; 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` unknown_props_check = b`
const writable_props = [${writable_props.map(prop => x`'${prop.export_name}'`)}]; const writable_props = [${writable_props.map(prop => x`'${prop.export_name}'`)}];
@_Object.keys($$props).forEach(key => { @_Object.keys($$props).forEach(key => {
@ -385,6 +412,8 @@ export default function dom(
body.push(b` body.push(b`
function ${definition}(${args}) { function ${definition}(${args}) {
${rest}
${reactive_store_declarations} ${reactive_store_declarations}
${reactive_store_subscriptions} ${reactive_store_subscriptions}
@ -395,7 +424,8 @@ export default function dom(
${unknown_props_check} ${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`[]`)}];`} ${renderer.binding_groups.length > 0 && b`const $$binding_groups = [${renderer.binding_groups.map(_ => x`[]`)}];`}
@ -409,6 +439,8 @@ export default function dom(
${injected.map(name => b`let ${name};`)} ${injected.map(name => b`let ${name};`)}
${/* before reactive declarations */ props_inject}
${reactive_declarations.length > 0 && b` ${reactive_declarations.length > 0 && b`
$$self.$$.update = () => { $$self.$$.update = () => {
${reactive_declarations} ${reactive_declarations}
@ -453,7 +485,7 @@ export default function dom(
@insert(options.target, this, options.anchor); @insert(options.target, this, options.anchor);
} }
${(props.length > 0 || uses_props) && b` ${(props.length > 0 || uses_props || uses_rest) && b`
if (options.props) { if (options.props) {
this.$set(options.props); this.$set(options.props);
@flush(); @flush();

@ -43,7 +43,7 @@ export function invalidate(renderer: Renderer, scope: Scope, node: Node, names:
if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) { if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) {
return get_invalidated(head, node); return get_invalidated(head, node);
} else { } else {
const is_store_value = head.name[0] === '$'; const is_store_value = head.name[0] === '$' && head.name[1] !== '$';
const extra_args = tail.map(variable => get_invalidated(variable)); const extra_args = tail.map(variable => get_invalidated(variable));
const pass_value = ( const pass_value = (

@ -9,6 +9,7 @@ import PendingBlock from '../../nodes/PendingBlock';
import ThenBlock from '../../nodes/ThenBlock'; import ThenBlock from '../../nodes/ThenBlock';
import CatchBlock from '../../nodes/CatchBlock'; import CatchBlock from '../../nodes/CatchBlock';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
import traverse_destructure_pattern from '../../utils/traverse_destructure_pattern';
class AwaitBlockBranch extends Wrapper { class AwaitBlockBranch extends Wrapper {
node: PendingBlock | ThenBlock | CatchBlock; node: PendingBlock | ThenBlock | CatchBlock;
@ -46,6 +47,23 @@ class AwaitBlockBranch extends Wrapper {
this.is_dynamic = this.block.dependencies.size > 0; this.is_dynamic = this.block.dependencies.size > 0;
} }
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
this.fragment.render(block, parent_node, parent_nodes);
}
render_destructure(block: Block, value, node, index) {
if (value && node.pattern.type !== 'Identifier') {
traverse_destructure_pattern(node.pattern, (node, parent, index) => {
parent[index] = x`#ctx[${block.renderer.context_lookup.get(node.name).index}]`;
});
this.block.chunks.declarations.push(b`(${node.pattern} = #ctx[${index}])`);
if (this.block.has_update_method) {
this.block.chunks.update.push(b`(${node.pattern} = #ctx[${index}])`);
}
}
}
} }
export default class AwaitBlockWrapper extends Wrapper { export default class AwaitBlockWrapper extends Wrapper {
@ -55,6 +73,9 @@ export default class AwaitBlockWrapper extends Wrapper {
then: AwaitBlockBranch; then: AwaitBlockBranch;
catch: AwaitBlockBranch; catch: AwaitBlockBranch;
value: string;
error: string;
var: Identifier = { type: 'Identifier', name: 'await_block' }; var: Identifier = { type: 'Identifier', name: 'await_block' };
constructor( constructor(
@ -71,8 +92,20 @@ export default class AwaitBlockWrapper extends Wrapper {
this.not_static_content(); this.not_static_content();
block.add_dependencies(this.node.expression.dependencies); block.add_dependencies(this.node.expression.dependencies);
if (this.node.value) block.renderer.add_to_context(this.node.value, true); if (this.node.value) {
if (this.node.error) block.renderer.add_to_context(this.node.error, true); for (const ctx of this.node.value.expressions) {
block.renderer.add_to_context(ctx, true);
}
this.value = this.node.value.identifier_name || block.get_unique_name('value').name;
block.renderer.add_to_context(this.value, true);
}
if (this.node.error) {
for (const ctx of this.node.error.expressions) {
block.renderer.add_to_context(ctx, true);
}
this.error = this.node.error.identifier_name || block.get_unique_name('error').name;
block.renderer.add_to_context(this.error, true);
}
let is_dynamic = false; let is_dynamic = false;
let has_intros = false; let has_intros = false;
@ -105,17 +138,11 @@ export default class AwaitBlockWrapper extends Wrapper {
this[status] = branch; this[status] = branch;
}); });
this.pending.block.has_update_method = is_dynamic; ['pending', 'then', 'catch'].forEach(status => {
this.then.block.has_update_method = is_dynamic; this[status].block.has_update_method = is_dynamic;
this.catch.block.has_update_method = is_dynamic; this[status].block.has_intro_method = has_intros;
this[status].block.has_outro_method = has_outros;
this.pending.block.has_intro_method = has_intros; });
this.then.block.has_intro_method = has_intros;
this.catch.block.has_intro_method = has_intros;
this.pending.block.has_outro_method = has_outros;
this.then.block.has_outro_method = has_outros;
this.catch.block.has_outro_method = has_outros;
if (has_outros) { if (has_outros) {
block.add_outro(); block.add_outro();
@ -139,8 +166,8 @@ export default class AwaitBlockWrapper extends Wrapper {
block.maintain_context = true; block.maintain_context = true;
const value_index = this.node.value && block.renderer.context_lookup.get(this.node.value).index; const value_index = this.value && block.renderer.context_lookup.get(this.value).index;
const error_index = this.node.error && block.renderer.context_lookup.get(this.node.error).index; const error_index = this.error && block.renderer.context_lookup.get(this.error).index;
const info_props: any = x`{ const info_props: any = x`{
ctx: #ctx, ctx: #ctx,
@ -205,7 +232,7 @@ export default class AwaitBlockWrapper extends Wrapper {
} else { } else {
const #child_ctx = #ctx.slice(); const #child_ctx = #ctx.slice();
${this.node.value && b`#child_ctx[${value_index}] = ${info}.resolved;`} ${this.value && b`#child_ctx[${value_index}] = ${info}.resolved;`}
${info}.block.p(#child_ctx, #dirty); ${info}.block.p(#child_ctx, #dirty);
} }
`); `);
@ -219,7 +246,7 @@ export default class AwaitBlockWrapper extends Wrapper {
block.chunks.update.push(b` block.chunks.update.push(b`
{ {
const #child_ctx = #ctx.slice(); const #child_ctx = #ctx.slice();
${this.node.value && b`#child_ctx[${value_index}] = ${info}.resolved;`} ${this.value && b`#child_ctx[${value_index}] = ${info}.resolved;`}
${info}.block.p(#child_ctx, #dirty); ${info}.block.p(#child_ctx, #dirty);
} }
`); `);
@ -242,7 +269,9 @@ export default class AwaitBlockWrapper extends Wrapper {
`); `);
[this.pending, this.then, this.catch].forEach(branch => { [this.pending, this.then, this.catch].forEach(branch => {
branch.fragment.render(branch.block, null, x`#nodes` as Identifier); branch.render(branch.block, null, x`#nodes` as Identifier);
}); });
this.then.render_destructure(block, this.value, this.node.value, value_index);
this.catch.render_destructure(block, this.error, this.node.error, error_index);
} }
} }

@ -62,6 +62,8 @@ export default class EachBlockWrapper extends Wrapper {
context_props: Array<Node | Node[]>; context_props: Array<Node | Node[]>;
index_name: Identifier; index_name: Identifier;
updates: Array<Node | Node[]> = [];
dependencies: Set<string>;
var: Identifier = { type: 'Identifier', name: 'each' }; var: Identifier = { type: 'Identifier', name: 'each' };
@ -204,6 +206,9 @@ export default class EachBlockWrapper extends Wrapper {
const snippet = this.node.expression.manipulate(block); const snippet = this.node.expression.manipulate(block);
block.chunks.init.push(b`let ${this.vars.each_block_value} = ${snippet};`); block.chunks.init.push(b`let ${this.vars.each_block_value} = ${snippet};`);
if (this.renderer.options.dev) {
block.chunks.init.push(b`@validate_each_argument(${this.vars.each_block_value});`);
}
// TODO which is better — Object.create(array) or array.slice()? // TODO which is better — Object.create(array) or array.slice()?
renderer.blocks.push(b` renderer.blocks.push(b`
@ -232,6 +237,12 @@ export default class EachBlockWrapper extends Wrapper {
update_mount_node 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) { if (this.node.key) {
this.render_keyed(args); this.render_keyed(args);
} else { } else {
@ -288,7 +299,7 @@ export default class EachBlockWrapper extends Wrapper {
`); `);
if (this.else.block.has_update_method) { if (this.else.block.has_update_method) {
block.chunks.update.push(b` this.updates.push(b`
if (!${this.vars.data_length} && ${each_block_else}) { if (!${this.vars.data_length} && ${each_block_else}) {
${each_block_else}.p(#ctx, #dirty); ${each_block_else}.p(#ctx, #dirty);
} else if (!${this.vars.data_length}) { } else if (!${this.vars.data_length}) {
@ -301,7 +312,7 @@ export default class EachBlockWrapper extends Wrapper {
} }
`); `);
} else { } else {
block.chunks.update.push(b` this.updates.push(b`
if (${this.vars.data_length}) { if (${this.vars.data_length}) {
if (${each_block_else}) { if (${each_block_else}) {
${each_block_else}.d(1); ${each_block_else}.d(1);
@ -320,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); this.fragment.render(this.block, null, x`#nodes` as Identifier);
if (this.else) { if (this.else) {
@ -412,23 +431,17 @@ export default class EachBlockWrapper extends Wrapper {
? `@outro_and_destroy_block` ? `@outro_and_destroy_block`
: `@destroy_block`; : `@destroy_block`;
const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only if (this.dependencies.size) {
this.node.expression.dynamic_dependencies().forEach((dependency: string) => { this.updates.push(b`
all_dependencies.add(dependency); const ${this.vars.each_block_value} = ${snippet};
}); ${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`}
if (all_dependencies.size) { ${this.block.has_outros && b`@group_outros();`}
block.chunks.update.push(b` ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`}
if (${block.renderer.dirty(Array.from(all_dependencies))}) { ${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`}
const ${this.vars.each_block_value} = ${snippet}; ${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`@group_outros();`} ${this.block.has_outros && b`@check_outros();`}
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`}
${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`}
${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();`}
}
`); `);
} }
@ -500,12 +513,7 @@ export default class EachBlockWrapper extends Wrapper {
} }
`); `);
const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only if (this.dependencies.size) {
this.node.expression.dynamic_dependencies().forEach((dependency: string) => {
all_dependencies.add(dependency);
});
if (all_dependencies.size) {
const has_transitions = !!(this.block.has_intro_method || this.block.has_outro_method); const has_transitions = !!(this.block.has_intro_method || this.block.has_outro_method);
const for_loop_body = this.block.has_update_method const for_loop_body = this.block.has_update_method
@ -572,6 +580,7 @@ export default class EachBlockWrapper extends Wrapper {
const update = b` const update = b`
${!this.block.has_update_method && b`const #old_length = ${this.vars.each_block_value}.length;`} ${!this.block.has_update_method && b`const #old_length = ${this.vars.each_block_value}.length;`}
${this.vars.each_block_value} = ${snippet}; ${this.vars.each_block_value} = ${snippet};
${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`}
let #i; let #i;
for (#i = ${start}; #i < ${data_length}; #i += 1) { for (#i = ${start}; #i < ${data_length}; #i += 1) {
@ -583,11 +592,7 @@ export default class EachBlockWrapper extends Wrapper {
${remove_old_blocks} ${remove_old_blocks}
`; `;
block.chunks.update.push(b` this.updates.push(update);
if (${block.renderer.dirty(Array.from(all_dependencies))}) {
${update}
}
`);
} }
if (this.block.has_outros) { if (this.block.has_outros) {

@ -17,6 +17,7 @@ import { INode } from '../../nodes/interfaces';
import Renderer from '../Renderer'; import Renderer from '../Renderer';
import Block from '../Block'; import Block from '../Block';
import { trim_start, trim_end } from '../../../utils/trim'; import { trim_start, trim_end } from '../../../utils/trim';
import { link } from '../../../utils/link';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
const wrappers = { const wrappers = {
@ -38,11 +39,6 @@ const wrappers = {
Window Window
}; };
function link(next: Wrapper, prev: Wrapper) {
prev.next = next;
if (next) next.prev = prev;
}
function trimmable_at(child: INode, next_sibling: Wrapper): boolean { function trimmable_at(child: INode, next_sibling: Wrapper): boolean {
// Whitespace is trimmable if one of the following is true: // 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 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 { b, x } from 'code-red';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import { is_head } from './shared/is_head'; import { is_head } from './shared/is_head';
import { Identifier, Node } from 'estree'; import { Identifier, Node, UnaryExpression } from 'estree';
function is_else_if(node: ElseBlock) { function is_else_if(node: ElseBlock) {
return ( return (
@ -288,7 +288,7 @@ export default class IfBlockWrapper extends Wrapper {
} }
block.chunks.init.push(b` 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}; let ${name} = ${get_block};
`); `);
@ -407,12 +407,12 @@ export default class IfBlockWrapper extends Wrapper {
if (has_else) { if (has_else) {
block.chunks.init.push(b` 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); ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#ctx);
`); `);
} else { } else {
block.chunks.init.push(b` 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); ${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,28 @@ export default class InlineComponentWrapper extends Wrapper {
const statements: Array<Node | Node[]> = []; const statements: Array<Node | Node[]> = [];
const updates: 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; let props;
const name_changes = block.get_unique_name(`${name.name}_changes`); const name_changes = block.get_unique_name(`${name.name}_changes`);
const uses_spread = !!this.node.attributes.find(a => a.is_spread); 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.renderer.remove_block(this.slots.get(slot).block);
this.slots.delete(slot);
}
}
const initial_props = this.slots.size > 0 const initial_props = this.slots.size > 0
? [ ? [
p`$$slots: { p`$$slots: {
@ -172,15 +189,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) { if (component.compile_options.dev) {
// TODO this is a terrible hack, but without it the component // TODO this is a terrible hack, but without it the component
// will complain that options.target is missing. This would // will complain that options.target is missing. This would
@ -224,7 +232,9 @@ export default class InlineComponentWrapper extends Wrapper {
const condition = dependencies.size > 0 && (dependencies.size !== all_dependencies.size) const condition = dependencies.size > 0 && (dependencies.size !== all_dependencies.size)
? renderer.dirty(Array.from(dependencies)) ? renderer.dirty(Array.from(dependencies))
: null; : null;
const unchanged = dependencies.size === 0;
let change_object;
if (attr.is_spread) { if (attr.is_spread) {
const value = attr.expression.manipulate(block); const value = attr.expression.manipulate(block);
initial_props.push(value); initial_props.push(value);
@ -233,13 +243,20 @@ export default class InlineComponentWrapper extends Wrapper {
if (attr.expression.node.type !== 'ObjectExpression') { if (attr.expression.node.type !== 'ObjectExpression') {
value_object = x`@get_spread_object(${value})`; value_object = x`@get_spread_object(${value})`;
} }
changes.push(condition ? x`${condition} && ${value_object}` : value_object); change_object = value_object;
} else { } else {
const obj = x`{ ${name}: ${attr.get_value(block)} }`; const obj = x`{ ${name}: ${attr.get_value(block)} }`;
initial_props.push(obj); initial_props.push(obj);
change_object = obj;
changes.push(condition ? x`${condition} && ${obj}` : x`${levels}[${i}]`);
} }
changes.push(
unchanged
? x`${levels}[${i}]`
: condition
? x`${condition} && ${change_object}`
: change_object
);
}); });
block.chunks.init.push(b` block.chunks.init.push(b`
@ -332,8 +349,7 @@ export default class InlineComponentWrapper extends Wrapper {
contextual_dependencies.push(object.name, property.name); contextual_dependencies.push(object.name, property.name);
} }
const value = block.get_unique_name('value'); const params = [x`#value`];
const params: any[] = [value];
if (contextual_dependencies.length > 0) { if (contextual_dependencies.length > 0) {
const args = []; const args = [];
@ -349,23 +365,23 @@ export default class InlineComponentWrapper extends Wrapper {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${id}(${value}) { function ${id}(#value) {
${callee}.call(null, ${value}, ${args}); ${callee}.call(null, #value, ${args});
} }
`); `);
block.maintain_context = true; // TODO put this somewhere more logical block.maintain_context = true; // TODO put this somewhere more logical
} else { } else {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${id}(${value}) { function ${id}(#value) {
${callee}.call(null, ${value}); ${callee}.call(null, #value);
} }
`); `);
} }
const body = b` const body = b`
function ${id}(${params}) { function ${id}(${params}) {
${lhs} = ${value}; ${lhs} = #value;
${renderer.invalidate(dependencies[0])}; ${renderer.invalidate(dependencies[0])};
} }
`; `;

@ -10,10 +10,12 @@ import get_slot_data from '../../utils/get_slot_data';
import Expression from '../../nodes/shared/Expression'; import Expression from '../../nodes/shared/Expression';
import is_dynamic from './shared/is_dynamic'; import is_dynamic from './shared/is_dynamic';
import { Identifier, ObjectExpression } from 'estree'; import { Identifier, ObjectExpression } from 'estree';
import create_debugging_comment from './shared/create_debugging_comment';
export default class SlotWrapper extends Wrapper { export default class SlotWrapper extends Wrapper {
node: Slot; node: Slot;
fragment: FragmentWrapper; fragment: FragmentWrapper;
fallback: Block | null = null;
var: Identifier = { type: 'Identifier', name: 'slot' }; var: Identifier = { type: 'Identifier', name: 'slot' };
dependencies: Set<string> = new Set(['$$scope']); dependencies: Set<string> = new Set(['$$scope']);
@ -30,9 +32,18 @@ export default class SlotWrapper extends Wrapper {
this.cannot_use_innerhtml(); this.cannot_use_innerhtml();
this.not_static_content(); this.not_static_content();
if (this.node.children.length) {
this.fallback = block.child({
comment: create_debugging_comment(this.node.children[0], this.renderer.component),
name: this.renderer.component.get_unique_name(`fallback_block`),
type: 'fallback'
});
renderer.blocks.push(this.fallback);
}
this.fragment = new FragmentWrapper( this.fragment = new FragmentWrapper(
renderer, renderer,
block, this.fallback,
node.children, node.children,
parent, parent,
strip_whitespace, strip_whitespace,
@ -103,86 +114,94 @@ export default class SlotWrapper extends Wrapper {
get_slot_context_fn = 'null'; get_slot_context_fn = 'null';
} }
let has_fallback = !!this.fallback;
if (this.fallback) {
this.fragment.render(this.fallback, null, x`#nodes` as Identifier);
has_fallback = this.fallback.has_content();
if (!has_fallback) {
renderer.remove_block(this.fallback);
}
}
const slot = block.get_unique_name(`${sanitize(slot_name)}_slot`); const slot = block.get_unique_name(`${sanitize(slot_name)}_slot`);
const slot_definition = block.get_unique_name(`${sanitize(slot_name)}_slot_template`); const slot_definition = block.get_unique_name(`${sanitize(slot_name)}_slot_template`);
const slot_or_fallback = has_fallback ? block.get_unique_name(`${sanitize(slot_name)}_slot_or_fallback`) : slot;
block.chunks.init.push(b` block.chunks.init.push(b`
const ${slot_definition} = ${renderer.reference('$$slots')}.${slot_name}; const ${slot_definition} = ${renderer.reference('$$slots')}.${slot_name};
const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}); const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn});
${has_fallback ? b`const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null}
`); `);
// TODO this is a dreadful hack! Should probably make this nicer
const { create, claim, hydrate, mount, update, destroy } = block.chunks;
block.chunks.create = [];
block.chunks.claim = [];
block.chunks.hydrate = [];
block.chunks.mount = [];
block.chunks.update = [];
block.chunks.destroy = [];
const listeners = block.event_listeners;
block.event_listeners = [];
this.fragment.render(block, parent_node, parent_nodes);
block.render_listeners(`_${slot.name}`);
block.event_listeners = listeners;
if (block.chunks.create.length) create.push(b`if (!${slot}) { ${block.chunks.create} }`);
if (block.chunks.claim.length) claim.push(b`if (!${slot}) { ${block.chunks.claim} }`);
if (block.chunks.hydrate.length) hydrate.push(b`if (!${slot}) { ${block.chunks.hydrate} }`);
if (block.chunks.mount.length) mount.push(b`if (!${slot}) { ${block.chunks.mount} }`);
if (block.chunks.update.length) update.push(b`if (!${slot}) { ${block.chunks.update} }`);
if (block.chunks.destroy.length) destroy.push(b`if (!${slot}) { ${block.chunks.destroy} }`);
block.chunks.create = create;
block.chunks.claim = claim;
block.chunks.hydrate = hydrate;
block.chunks.mount = mount;
block.chunks.update = update;
block.chunks.destroy = destroy;
block.chunks.create.push( block.chunks.create.push(
b`if (${slot}) ${slot}.c();` b`if (${slot_or_fallback}) ${slot_or_fallback}.c();`
); );
if (renderer.options.hydratable) { if (renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(
b`if (${slot}) ${slot}.l(${parent_nodes});` b`if (${slot_or_fallback}) ${slot_or_fallback}.l(${parent_nodes});`
); );
} }
block.chunks.mount.push(b` block.chunks.mount.push(b`
if (${slot}) { if (${slot_or_fallback}) {
${slot}.m(${parent_node || '#target'}, ${parent_node ? 'null' : 'anchor'}); ${slot_or_fallback}.m(${parent_node || '#target'}, ${parent_node ? 'null' : 'anchor'});
} }
`); `);
block.chunks.intro.push( block.chunks.intro.push(
b`@transition_in(${slot}, #local);` b`@transition_in(${slot_or_fallback}, #local);`
); );
block.chunks.outro.push( block.chunks.outro.push(
b`@transition_out(${slot}, #local);` b`@transition_out(${slot_or_fallback}, #local);`
); );
const dynamic_dependencies = Array.from(this.dependencies).filter(name => { const is_dependency_dynamic = name => {
if (name === '$$scope') return true; if (name === '$$scope') return true;
if (this.node.scope.is_let(name)) return true; if (this.node.scope.is_let(name)) return true;
const variable = renderer.component.var_lookup.get(name); const variable = renderer.component.var_lookup.get(name);
return is_dynamic(variable); return is_dynamic(variable);
}); };
const dynamic_dependencies = Array.from(this.dependencies).filter(is_dependency_dynamic);
block.chunks.update.push(b` const fallback_dynamic_dependencies = has_fallback
if (${slot} && ${slot}.p && ${renderer.dirty(dynamic_dependencies)}) { ? Array.from(this.fallback.dependencies).filter(is_dependency_dynamic)
: [];
const slot_update = b`
if (${slot}.p && ${renderer.dirty(dynamic_dependencies)}) {
${slot}.p( ${slot}.p(
@get_slot_context(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}), @get_slot_context(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}),
@get_slot_changes(${slot_definition}, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}) @get_slot_changes(${slot_definition}, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn})
); );
} }
`); `;
const fallback_update = has_fallback && fallback_dynamic_dependencies.length > 0 && b`
if (${slot_or_fallback} && ${slot_or_fallback}.p && ${renderer.dirty(fallback_dynamic_dependencies)}) {
${slot_or_fallback}.p(#ctx, #dirty);
}
`;
if (fallback_update) {
block.chunks.update.push(b`
if (${slot}) {
${slot_update}
} else {
${fallback_update}
}
`);
} else {
block.chunks.update.push(b`
if (${slot}) {
${slot_update}
}
`);
}
block.chunks.destroy.push( block.chunks.destroy.push(
b`if (${slot}) ${slot}.d(detaching);` b`if (${slot_or_fallback}) ${slot_or_fallback}.d(detaching);`
); );
} }
} }

@ -5,36 +5,6 @@ import Wrapper from './shared/Wrapper';
import { x } from 'code-red'; import { x } from 'code-red';
import { Identifier } from 'estree'; 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 { export default class TextWrapper extends Wrapper {
node: Text; node: Text;
data: string; data: string;
@ -50,7 +20,7 @@ export default class TextWrapper extends Wrapper {
) { ) {
super(renderer, block, parent, node); super(renderer, block, parent, node);
this.skip = should_skip(this.node); this.skip = this.node.should_skip();
this.data = data; this.data = data;
this.var = (this.skip ? null : x`t`) as unknown as Identifier; this.var = (this.skip ? null : x`t`) as unknown as Identifier;
} }

@ -16,8 +16,16 @@ export default function create_debugging_comment(
let d; let d;
if (node.type === 'InlineComponent' || node.type === 'Element') { if (node.type === 'InlineComponent' || node.type === 'Element') {
d = node.children.length ? node.children[0].start : node.start; if (node.children.length) {
while (source[d - 1] !== '>') d -= 1; d = node.children[0].start;
while (source[d - 1] !== '>') d -= 1;
} else {
d = node.start;
while (source[d] !== '>') d += 1;
d += 1;
}
} else if (node.type === 'Text' || node.type === 'Comment') {
d = node.end;
} else { } else {
// @ts-ignore // @ts-ignore
d = node.expression ? node.expression.node.end : c; d = node.expression ? node.expression.node.end : c;

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

@ -14,7 +14,7 @@ export default function(node: AwaitBlock, renderer: Renderer, options: RenderOpt
renderer.add_expression(x` renderer.add_expression(x`
function(__value) { function(__value) {
if (@is_promise(__value)) return ${pending}; if (@is_promise(__value)) return ${pending};
return (function(${node.value}) { return ${then}; }(__value)); return (function(${node.value ? node.value.pattern : ''}) { return ${then}; }(__value));
}(${node.expression.node}) }(${node.expression.node})
`); `);
} }

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

@ -2,6 +2,7 @@ import { string_literal } from '../../utils/stringify';
import Renderer, { RenderOptions } from '../Renderer'; import Renderer, { RenderOptions } from '../Renderer';
import { get_slot_scope } from './shared/get_slot_scope'; import { get_slot_scope } from './shared/get_slot_scope';
import InlineComponent from '../../nodes/InlineComponent'; import InlineComponent from '../../nodes/InlineComponent';
import remove_whitespace_children from './utils/remove_whitespace_children';
import { p, x } from 'code-red'; import { p, x } from 'code-red';
function get_prop_value(attribute) { function get_prop_value(attribute) {
@ -67,12 +68,14 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
const slot_fns = []; const slot_fns = [];
if (node.children.length) { const children = remove_whitespace_children(node.children, node.next);
if (children.length) {
const slot_scopes = new Map(); const slot_scopes = new Map();
renderer.push(); renderer.push();
renderer.render(node.children, Object.assign({}, options, { renderer.render(children, Object.assign({}, options, {
slot_scopes slot_scopes
})); }));
@ -82,9 +85,11 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
}); });
slot_scopes.forEach(({ input, output }, name) => { slot_scopes.forEach(({ input, output }, name) => {
slot_fns.push( if (!is_empty_template_literal(output)) {
p`${name}: (${input}) => ${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})`); 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 } : { code: null, map: null } :
component.stylesheet.render(options.filename, true); 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_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$');
const reactive_store_values = reactive_stores const reactive_store_values = reactive_stores
.map(({ name }) => { .map(({ name }) => {
@ -130,6 +134,7 @@ export default function ssr(
return ${literal};`; return ${literal};`;
const blocks = [ const blocks = [
rest,
...reactive_stores.map(({ name }) => { ...reactive_stores.map(({ name }) => {
const store_name = name.slice(1); const store_name = name.slice(1);
const store = component.var_lookup.get(store_name); 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);
}

@ -0,0 +1,35 @@
import { Pattern, Identifier, RestElement } from "estree";
import { Node } from "acorn";
export default function traverse_destructure_pattern(
node: Pattern,
callback: (node: Identifier, parent: Node, key: string | number) => void
) {
function traverse(node: Pattern, parent, key) {
switch (node.type) {
case "Identifier":
return callback(node, parent, key);
case "ArrayPattern":
for (let i = 0; i < node.elements.length; i++) {
const element = node.elements[i];
traverse(element, node.elements, i);
}
break;
case "ObjectPattern":
for (let i = 0; i < node.properties.length; i++) {
const property = node.properties[i];
if (property.type === "Property") {
traverse(property.value, property, "value");
} else {
traverse((property as any) as RestElement, node.properties, i);
}
}
break;
case "RestElement":
return traverse(node.argument, node, 'argument');
case "AssignmentPattern":
return traverse(node.left, node, 'left');
}
}
traverse(node, null, null);
}

@ -5,6 +5,9 @@ import { reserved } from '../utils/names';
import full_char_code_at from '../utils/full_char_code_at'; import full_char_code_at from '../utils/full_char_code_at';
import { TemplateNode, Ast, ParserOptions, Fragment, Style, Script } from '../interfaces'; import { TemplateNode, Ast, ParserOptions, Fragment, Style, Script } from '../interfaces';
import error from '../utils/error'; import error from '../utils/error';
import { is_bracket_open, is_bracket_close, is_bracket_pair, get_bracket_close } from './utils/bracket';
import { parse_expression_at } from './acorn';
import { Pattern } from 'estree';
type ParserState = (parser: Parser) => (ParserState | void); type ParserState = (parser: Parser) => (ParserState | void);
@ -170,6 +173,51 @@ export class Parser {
return identifier; return identifier;
} }
read_destructure_pattern(): Pattern {
const start = this.index;
let i = this.index;
const code = full_char_code_at(this.template, i);
if (isIdentifierStart(code, true)) {
return { type: 'Identifier', name: this.read_identifier() };
}
if (!is_bracket_open(code)) {
this.error({
code: 'unexpected-token',
message: 'Expected identifier or destructure pattern',
});
}
const bracket_stack = [code];
i += code <= 0xffff ? 1 : 2;
while (i < this.template.length) {
const code = full_char_code_at(this.template, i);
if (is_bracket_open(code)) {
bracket_stack.push(code);
} else if (is_bracket_close(code)) {
if (!is_bracket_pair(bracket_stack[bracket_stack.length - 1], code)) {
this.error({
code: 'unexpected-token',
message: `Expected ${String.fromCharCode(get_bracket_close(bracket_stack[bracket_stack.length - 1]))}`
});
}
bracket_stack.pop();
if (bracket_stack.length === 0) {
i += code <= 0xffff ? 1 : 2;
break;
}
}
i += code <= 0xffff ? 1 : 2;
}
this.index = i;
const pattern_string = this.template.slice(start, i);
return (parse_expression_at(`(${pattern_string} = 1)`, 0) as any).left as Pattern;
}
read_until(pattern: RegExp) { read_until(pattern: RegExp) {
if (this.index >= this.template.length) if (this.index >= this.template.length)
this.error({ this.error({

@ -196,7 +196,7 @@ export default function mustache(parser: Parser) {
if (!parser.eat('}')) { if (!parser.eat('}')) {
parser.require_whitespace(); parser.require_whitespace();
await_block[is_then ? 'value': 'error'] = parser.read_identifier(); await_block[is_then ? 'value': 'error'] = parser.read_destructure_pattern();
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
} }
@ -305,7 +305,7 @@ export default function mustache(parser: Parser) {
const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then'); const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then');
if (await_block_shorthand) { if (await_block_shorthand) {
parser.require_whitespace(); parser.require_whitespace();
block.value = parser.read_identifier(); block.value = parser.read_destructure_pattern();
parser.allow_whitespace(); parser.allow_whitespace();
} }

@ -241,7 +241,7 @@ function read_tag_name(parser: Parser) {
while (i--) { while (i--) {
const fragment = parser.stack[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; legal = true;
break; break;
} }
@ -250,7 +250,7 @@ function read_tag_name(parser: Parser) {
if (!legal) { if (!legal) {
parser.error({ parser.error({
code: `invalid-self-placement`, 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); }, start);
} }

@ -0,0 +1,28 @@
const SQUARE_BRACKET_OPEN = "[".charCodeAt(0);
const SQUARE_BRACKET_CLOSE = "]".charCodeAt(0);
const CURLY_BRACKET_OPEN = "{".charCodeAt(0);
const CURLY_BRACKET_CLOSE = "}".charCodeAt(0);
export function is_bracket_open(code) {
return code === SQUARE_BRACKET_OPEN || code === CURLY_BRACKET_OPEN;
}
export function is_bracket_close(code) {
return code === SQUARE_BRACKET_CLOSE || code === CURLY_BRACKET_CLOSE;
}
export function is_bracket_pair(open, close) {
return (
(open === SQUARE_BRACKET_OPEN && close === SQUARE_BRACKET_CLOSE) ||
(open === CURLY_BRACKET_OPEN && close === CURLY_BRACKET_CLOSE)
);
}
export function get_bracket_close(open) {
if (open === SQUARE_BRACKET_OPEN) {
return SQUARE_BRACKET_CLOSE;
}
if (open === CURLY_BRACKET_OPEN) {
return CURLY_BRACKET_CLOSE;
}
}

@ -21,7 +21,7 @@ export default function error(message: string, props: {
filename: string; filename: string;
start: number; start: number;
end?: number; end?: number;
}) { }): never {
const error = new CompileError(message); const error = new CompileError(message);
error.name = props.name; error.name = props.name;

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

@ -79,6 +79,23 @@ export function set_data_dev(text, data) {
text.data = data; text.data = data;
} }
export function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {
msg += ' You can use a spread to convert this iterable into an array.';
}
throw new Error(msg);
}
}
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>; type Props = Record<string, any>;
export interface SvelteComponentDev { export interface SvelteComponentDev {
@ -110,6 +127,10 @@ export class SvelteComponentDev extends SvelteComponent {
console.warn(`Component was already destroyed`); // eslint-disable-line no-console console.warn(`Component was already destroyed`); // eslint-disable-line no-console
}; };
} }
$capture_state() {}
$inject_state() {}
} }
export function loop_guard(timeout) { export function loop_guard(timeout) {

@ -56,7 +56,7 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list
function insert(block) { function insert(block) {
transition_in(block, 1); transition_in(block, 1);
block.m(node, next); block.m(node, next, lookup.has(block.key));
lookup.set(block.key, block); lookup.set(block.key, block);
next = block.first; next = block.first;
n--; n--;

@ -1,9 +1,13 @@
import { element } from './dom'; import { element } from './dom';
import { raf } from './environment'; import { raf } from './environment';
let stylesheet; interface ExtendedDoc extends Document {
__svelte_stylesheet: CSSStyleSheet;
__svelte_rules: Record<string, true>;
}
const active_docs = new Set<ExtendedDoc>();
let active = 0; let active = 0;
let current_rules = {};
// https://github.com/darkskyapp/string-hash/blob/master/index.js // https://github.com/darkskyapp/string-hash/blob/master/index.js
function hash(str: string) { function hash(str: string) {
@ -25,14 +29,12 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b:
const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`;
const name = `__svelte_${hash(rule)}_${uid}`; const name = `__svelte_${hash(rule)}_${uid}`;
const doc = node.ownerDocument as ExtendedDoc;
active_docs.add(doc);
const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style') as HTMLStyleElement).sheet as CSSStyleSheet);
const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {});
if (!current_rules[name]) { if (!current_rules[name]) {
if (!stylesheet) {
const style = element('style');
document.head.appendChild(style);
stylesheet = style.sheet;
}
current_rules[name] = true; current_rules[name] = true;
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
} }
@ -45,22 +47,28 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b:
} }
export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string) { export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string) {
node.style.animation = (node.style.animation || '') const previous = (node.style.animation || '').split(', ');
.split(', ') const next = previous.filter(name
.filter(name ? anim => anim.indexOf(name) < 0 // remove specific animation
? anim => anim.indexOf(name) < 0 // remove specific animation : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations
: anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations );
) const deleted = previous.length - next.length;
.join(', '); if (deleted) {
node.style.animation = next.join(', ');
if (name && !--active) clear_rules(); active -= deleted;
if (!active) clear_rules();
}
} }
export function clear_rules() { export function clear_rules() {
raf(() => { raf(() => {
if (active) return; if (active) return;
let i = stylesheet.cssRules.length; active_docs.forEach(doc => {
while (i--) stylesheet.deleteRule(i); const stylesheet = doc.__svelte_stylesheet;
current_rules = {}; let i = stylesheet.cssRules.length;
while (i--) stylesheet.deleteRule(i);
doc.__svelte_rules = {};
});
active_docs.clear();
}); });
} }

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

@ -2,15 +2,15 @@ export default {
warnings: [{ warnings: [{
code: "avoid-is", code: "avoid-is",
message: "The 'is' attribute is not supported cross-browser and should be avoided", message: "The 'is' attribute is not supported cross-browser and should be avoided",
pos: 97, pos: 98,
start: { start: {
character: 97, character: 98,
column: 8, column: 8,
line: 7 line: 7
}, },
end: { end: {
character: 114, character: 116,
column: 25, column: 26,
line: 7 line: 7
} }
}] }]

@ -0,0 +1,2 @@
class CustomButton extends HTMLButtonElement {}
customElements.define('custom-button', CustomButton, { extends: 'button' });

@ -1,2 +0,0 @@
class FancyButton extends HTMLButtonElement {}
customElements.define('fancy-button', FancyButton, { extends: 'button' });

@ -1,7 +1,7 @@
<svelte:options tag="custom-element"/> <svelte:options tag="custom-element"/>
<script> <script>
import './fancy-button.js'; import './custom-button.js';
</script> </script>
<button is="fancy-button">click me</button> <button is="custom-button">click me</button>

@ -11,5 +11,5 @@ export default function (target) {
const el = target.querySelector('custom-element'); const el = target.querySelector('custom-element');
const button = el.shadowRoot.querySelector('button'); const button = el.shadowRoot.querySelector('button');
assert.ok(button instanceof customElements.get('fancy-button')); assert.ok(button instanceof customElements.get('custom-button'));
} }

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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save