merge master -> optimise-build

pull/3766/head
Rich Harris 6 years ago
commit 282634d01b

@ -1,6 +1,20 @@
# Svelte changelog # Svelte changelog
## Unreleased ## 3.14.1
* Deconflict block method names with other variables ([#3900](https://github.com/sveltejs/svelte/issues/3900))
* Fix entity encoding issue in text nodes with constant expressions ([#3911](https://github.com/sveltejs/svelte/issues/3911))
* Make code for unknown prop warnings compatible with older js engines ([#3914](https://github.com/sveltejs/svelte/issues/3914))
## 3.14.0
* Add `loopGuardTimeout` option that augments `for`/`while` loops to prevent infinite loops, primarily for use in the REPL ([#3887](https://github.com/sveltejs/svelte/pull/3887))
* Keep component bindings in sync when changed in reactive statements ([#3382](https://github.com/sveltejs/svelte/issues/3382))
* Update attributes before bindings ([#3857](https://github.com/sveltejs/svelte/issues/3857))
* Prevent variable naming conflict ([#3899](https://github.com/sveltejs/svelte/issues/3899))
## 3.13.0
* New structured code generation, which eliminates a number of edge cases and obscure bugs ([#3539](https://github.com/sveltejs/svelte/pull/3539)) * New structured code generation, which eliminates a number of edge cases and obscure bugs ([#3539](https://github.com/sveltejs/svelte/pull/3539))
@ -24,6 +38,24 @@ Also:
* Flush changes in newly attached block when using `{#await}` ([#3660](https://github.com/sveltejs/svelte/issues/3660)) * Flush changes in newly attached block when using `{#await}` ([#3660](https://github.com/sveltejs/svelte/issues/3660))
* Throw exception immediately when calling `createEventDispatcher()` after component instantiation ([#3667](https://github.com/sveltejs/svelte/pull/3667)) * Throw exception immediately when calling `createEventDispatcher()` after component instantiation ([#3667](https://github.com/sveltejs/svelte/pull/3667))
* Fix globals shadowing contextual template scope ([#3674](https://github.com/sveltejs/svelte/issues/3674)) * Fix globals shadowing contextual template scope ([#3674](https://github.com/sveltejs/svelte/issues/3674))
* Fix `<svelte:window>` bindings to stores ([#3832](https://github.com/sveltejs/svelte/issues/3832))
* Deconflict generated var names with builtins ([#3724](https://github.com/sveltejs/svelte/issues/3724))
* Allow spring/tweened values to be initially undefined ([#3761](https://github.com/sveltejs/svelte/issues/3761))
* Warn if using `<svelte:options tag="...">` without `customElement: true` option ([#3782](https://github.com/sveltejs/svelte/pull/3782))
* Add `Event` to list of known globals ([#3810](https://github.com/sveltejs/svelte/pull/3810))
* Throw helpful error on empty CSS declaration ([#3801](https://github.com/sveltejs/svelte/issues/3801))
* Support `easing` param on `fade` transition ([#3823](https://github.com/sveltejs/svelte/pull/3823))
* Generate valid names from filenames with unicode characters ([#3845](https://github.com/sveltejs/svelte/issues/3845))
* Don't generate any code for markup-less components ([#2200](https://github.com/sveltejs/svelte/issues/2200))
* Deconflict with internal name `block` ([#3854](https://github.com/sveltejs/svelte/issues/3854))
* Set attributes before bindings, to prevent erroneous assignments to `input.files` ([#3828](https://github.com/sveltejs/svelte/issues/3828))
* Smarter unused CSS detection ([#3825](https://github.com/sveltejs/svelte/pull/3825))
* Allow dynamic event handlers ([#3040](https://github.com/sveltejs/svelte/issues/3040))
* Prevent erroneous `"undefined"` class name ([#3876](https://github.com/sveltejs/svelte/pull/3876))
* Prevent resetting of `src` attribute unless changed ([#3579](https://github.com/sveltejs/svelte/pull/3579))
* Prevent hydration of void element 'children' ([#3882](https://github.com/sveltejs/svelte/issues/3882))
* Hoist globals even if mentioned in `<script>` block ([#3745](https://github.com/sveltejs/svelte/pull/3745))
## 3.12.1 ## 3.12.1

@ -96,6 +96,17 @@ Test samples are kept in `/test/xxx/samples` folder.
1. To run test, run `npm run test` 1. To run test, run `npm run test`
1. To run test for a specific feature, you can use the `-g` (aka `--grep`) option. For example, to only run test involving transitions, run `npm run test -- -g transition`. 1. To run test for a specific feature, you can use the `-g` (aka `--grep`) option. For example, to only run test involving transitions, run `npm run test -- -g transition`.
##### Running solo test
1. To run only one test, rename the test sample folder to end with `.solo`. For example, to run the `test/js/samples/action` only, rename it to `test/js/samples/action.solo`.
1. To run only one test suite, rename the test suite folder to end with `.solo`. For example, to run the `test/js` test suite only, rename it to `test/js.solo`.
1. Remember to rename the test folder back. The CI will fail if there's a solo test.
##### Updating `.expected` files
1. Tests suites like `css`, `js`, `server-side-rendering` asserts that the generated output has to match the content in the `.expected` file. For example, in the `js` test suites, the generated js code is compared against the content in `expected.js`.
1. To update the content of the `.expected` file, run the test with `--update` flag. (`npm run test --update`)
#### Breaking changes #### Breaking changes
When adding a new breaking change, follow this template in your pull request: When adding a new breaking change, follow this template in your pull request:

36
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.13.0-alpha.2", "version": "3.14.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -30,6 +30,16 @@
"integrity": "sha512-KioOCsSvSvXx6xUNLiJz+P+VMb7NRcePjoefOr74Y5P6lEKsiOn35eZyZzgpK4XCNJdXTDR7+zykj0lwxRvZ2g==", "integrity": "sha512-KioOCsSvSvXx6xUNLiJz+P+VMb7NRcePjoefOr74Y5P6lEKsiOn35eZyZzgpK4XCNJdXTDR7+zykj0lwxRvZ2g==",
"dev": true "dev": true
}, },
"@rollup/plugin-replace": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.2.1.tgz",
"integrity": "sha512-dgq5ijT8fK18KTb1inenZ61ivTayV7pvbz2+ivT+VN20BOgJVM1fqoBETqGHKgFVm/J9BhR82mQyAtxfpPv1lQ==",
"dev": true,
"requires": {
"magic-string": "^0.25.2",
"rollup-pluginutils": "^2.6.0"
}
},
"@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",
@ -490,14 +500,14 @@
"dev": true "dev": true
}, },
"code-red": { "code-red": {
"version": "0.0.18", "version": "0.0.21",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.18.tgz", "resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.21.tgz",
"integrity": "sha512-g7W6RwRqBbQTtMaUqrNWDyyl2GK0Uulk/uZPzGdgTXpOGX/LA8bW67EKQLdQgpYfd6APhZVwoX2lrL7mnJOWkA==", "integrity": "sha512-luUVxyGNwTU/lG+lY+pitXHJzgDvFa0WfxA4Nsd7uk/S366mHY8B86pwAcyRcKY9BE/HvryVNXCR691Q5ry+Ww==",
"dev": true, "dev": true,
"requires": { "requires": {
"acorn": "^7.1.0", "acorn": "^7.1.0",
"is-reference": "^1.1.4", "is-reference": "^1.1.4",
"periscopic": "^1.0.2", "periscopic": "^2.0.1",
"sourcemap-codec": "^1.4.6" "sourcemap-codec": "^1.4.6"
} }
}, },
@ -2713,9 +2723,9 @@
"dev": true "dev": true
}, },
"periscopic": { "periscopic": {
"version": "1.0.2", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-1.0.2.tgz", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-2.0.1.tgz",
"integrity": "sha512-KpKBKadLf8THXOxswQBhOY8E1lVVhfUidacPtQBrq7KDXaNkQLUPiTmXagzqpJGECP3/0gDXYFO6CZHVbGvOSw==", "integrity": "sha512-twJ8e4RatllMAcbmBqKj8cvZ94HtqSzbb8hJoGj4iSCcCHXxKb06HRxOq4heyq2x/6mKynJDvTTreHCz+m6lJw==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-reference": "^1.1.4" "is-reference": "^1.1.4"
@ -3109,16 +3119,6 @@
"rollup-pluginutils": "^2.8.1" "rollup-pluginutils": "^2.8.1"
} }
}, },
"rollup-plugin-replace": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz",
"integrity": "sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==",
"dev": true,
"requires": {
"magic-string": "^0.25.2",
"rollup-pluginutils": "^2.6.0"
}
},
"rollup-plugin-sucrase": { "rollup-plugin-sucrase": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-sucrase/-/rollup-plugin-sucrase-2.1.0.tgz", "resolved": "https://registry.npmjs.org/rollup-plugin-sucrase/-/rollup-plugin-sucrase-2.1.0.tgz",

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.13.0-alpha.2", "version": "3.14.1",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"module": "index.mjs", "module": "index.mjs",
"main": "index", "main": "index",
@ -56,6 +56,7 @@
}, },
"homepage": "https://github.com/sveltejs/svelte#README", "homepage": "https://github.com/sveltejs/svelte#README",
"devDependencies": { "devDependencies": {
"@rollup/plugin-replace": "^2.2.1",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "^8.10.53", "@types/node": "^8.10.53",
"@typescript-eslint/eslint-plugin": "^1.13.0", "@typescript-eslint/eslint-plugin": "^1.13.0",
@ -63,7 +64,7 @@
"acorn": "^7.1.0", "acorn": "^7.1.0",
"agadoo": "^1.1.0", "agadoo": "^1.1.0",
"c8": "^5.0.1", "c8": "^5.0.1",
"code-red": "0.0.18", "code-red": "0.0.21",
"codecov": "^3.5.0", "codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22", "css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0", "eslint": "^6.3.0",
@ -76,12 +77,12 @@
"locate-character": "^2.0.5", "locate-character": "^2.0.5",
"magic-string": "^0.25.3", "magic-string": "^0.25.3",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"periscopic": "^2.0.1",
"puppeteer": "^1.19.0", "puppeteer": "^1.19.0",
"rollup": "^1.21.4", "rollup": "^1.21.4",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-json": "^4.0.0", "rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-sucrase": "^2.1.0", "rollup-plugin-sucrase": "^2.1.0",
"rollup-plugin-typescript": "^1.0.1", "rollup-plugin-typescript": "^1.0.1",
"rollup-plugin-virtual": "^1.0.1", "rollup-plugin-virtual": "^1.0.1",

@ -1,5 +1,5 @@
import fs from 'fs'; import fs from 'fs';
import replace from 'rollup-plugin-replace'; import replace from '@rollup/plugin-replace';
import resolve from 'rollup-plugin-node-resolve'; import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs'; import commonjs from 'rollup-plugin-commonjs';
import json from 'rollup-plugin-json'; import json from 'rollup-plugin-json';

@ -6,7 +6,9 @@ authorURL: https://twitter.com/Rich_Harris
draft: true draft: true
--- ---
*Coming soon* This post will walk you through setting up your editor so that recognises Svelte files: *__Coming soon__*
This post will walk you through setting up your editor so that recognises Svelte files:
* eslint-plugin-svelte3 * eslint-plugin-svelte3
* svelte-vscode * svelte-vscode
@ -14,7 +16,7 @@ draft: true
## Atom ## Atom
To treat `*.svelte` files as HTML, open Edit → Config... and add the following lines to your `core` section: To treat `*.svelte` files as HTML, open *__Edit → Config...__* and add the following lines to your `core` section:
```cson ```cson
"*": "*":
@ -45,3 +47,23 @@ To set the filetype for a single file, use a [modeline](https://vim.fandom.com/w
``` ```
<!-- vim: set ft=html :--> <!-- vim: set ft=html :-->
``` ```
## Visual Studio Code
To treat `*.svelte` files as HTML, add the following lines to your `settings.json` file:
```cson
"files.associations": {
"*.svelte": "html"
}
```
## JetBrains WebStorm
To treat `*.svelte` files as HTML in WebStorm, you need to create a new file type association. Please refer to the [JetBrains website](https://www.jetbrains.com/help/webstorm/creating-and-registering-file-types.html) to see how.
## Sublime Text 3
Open any `.svelte` file.
Go to *__View → Syntax → Open all with current extension as... → HTML__*.

@ -1146,7 +1146,7 @@ bind:property={variable}
--- ---
You can bind to component props using the same mechanism. You can bind to component props using the same syntax as for elements.
```html ```html
<Keypad bind:value={pin}/> <Keypad bind:value={pin}/>

@ -53,6 +53,7 @@ The following options can be passed to the compiler. None are required:
| `tag` | string | null | `tag` | string | null
| `accessors` | boolean | `false` | `accessors` | boolean | `false`
| `css` | boolean | `true` | `css` | boolean | `true`
| `loopGuardTimeout` | number | 0
| `preserveComments` | boolean | `false` | `preserveComments` | boolean | `false`
| `preserveWhitespace` | boolean | `false` | `preserveWhitespace` | boolean | `false`
| `outputFilename` | string | `null` | `outputFilename` | string | `null`
@ -73,6 +74,7 @@ The following options can be passed to the compiler. None are required:
| `customElement` | `false` | If `true`, tells the compiler to generate a custom element constructor instead of a regular Svelte component. | `customElement` | `false` | If `true`, tells the compiler to generate a custom element constructor instead of a regular Svelte component.
| `tag` | `null` | A `string` that tells Svelte what tag name to register the custom element with. It must be a lowercase alphanumeric string with at least one hyphen, e.g. `"my-element"`. | `tag` | `null` | A `string` that tells Svelte what tag name to register the custom element with. It must be a lowercase alphanumeric string with at least one hyphen, e.g. `"my-element"`.
| `css` | `true` | If `true`, styles will be included in the JavaScript class and injected at runtime. It's recommended that you set this to `false` and use the CSS that is statically generated, as it will result in smaller JavaScript bundles and better performance. | `css` | `true` | If `true`, styles will be included in the JavaScript class and injected at runtime. It's recommended that you set this to `false` and use the CSS that is statically generated, as it will result in smaller JavaScript bundles and better performance.
| `loopGuardTimeout` | 0 | A `number` that tells Svelte to break the loop if it blocks the thread for more than `loopGuardTimeout` ms. This is useful to prevent infinite loops. **Only available when `dev: true`**
| `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out. | `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out.
| `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than optimised by Svelte. | `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than optimised by Svelte.
| `outputFilename` | `null` | A `string` used for your JavaScript sourcemap. | `outputFilename` | `null` | A `string` used for your JavaScript sourcemap.

@ -43,8 +43,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #666; background-color: #666;
-webkit-mask: url(logo-mask.svg) 50% 50% no-repeat; -webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(logo-mask.svg) 50% 50% no-repeat; mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
} }
</style> </style>

@ -33,7 +33,7 @@
font-family: 'Overpass'; font-family: 'Overpass';
letter-spacing: 0.12em; letter-spacing: 0.12em;
color: #676778; color: #676778;
font-weight: 100; font-weight: 400;
} }
.centered span { .centered span {
@ -71,4 +71,4 @@
toggle me toggle me
</label> </label>
<link href="https://fonts.googleapis.com/css?family=Overpass:100" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Overpass:100,400" rel="stylesheet">

@ -49,8 +49,6 @@
<!-- svelte-ignore a11y-autofocus --> <!-- svelte-ignore a11y-autofocus -->
<button autofocus on:click={close}>close modal</button> <button autofocus on:click={close}>close modal</button>
<a href="argh">argh</a>
</div> </div>
<style> <style>
@ -80,4 +78,4 @@
button { button {
display: block; display: block;
} }
</style> </style>

@ -2,18 +2,9 @@
<script> <script>
let people = [ let people = [
{ { first: 'Hans', last: 'Emil' },
first: 'Hans', { first: 'Max', last: 'Mustermann' },
last: 'Emil' { first: 'Roman', last: 'Tisch' }
},
{
first: 'Max',
last: 'Mustermann'
},
{
first: 'Roman',
last: 'Tisch'
}
]; ];
let prefix = ''; let prefix = '';
@ -39,7 +30,9 @@
} }
function update() { function update() {
people[i] = { first, last }; selected.first = first;
selected.last = last;
people = people;
} }
function remove() { function remove() {

@ -30,3 +30,12 @@ function addNumber() {
numbers[numbers.length] = numbers.length + 1; numbers[numbers.length] = numbers.length + 1;
} }
``` ```
A simple rule of thumb: the name of the updated variable must appear on the left hand side of the assignment. For example this...
```js
const foo = obj.foo;
foo.bar = 'baz';
```
...won't update references to `obj.foo.bar`, unless you follow it up with `obj = obj`.

@ -43,8 +43,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #666; background-color: #666;
-webkit-mask: url(logo-mask.svg) 50% 50% no-repeat; -webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(logo-mask.svg) 50% 50% no-repeat; mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
} }
</style> </style>

@ -43,8 +43,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #666; background-color: #666;
-webkit-mask: url(logo-mask.svg) 50% 50% no-repeat; -webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(logo-mask.svg) 50% 50% no-repeat; mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
} }
</style> </style>

@ -28,7 +28,7 @@ Clicking the buttons causes the progress bar to animate to its new value. It's a
</script> </script>
``` ```
> The `svelte/easing` module contains the [Penner easing equations](http://robertpenner.com/easing/), or you can supply your own `p => t` function where `p` and `t` are both values between 0 and 1. > The `svelte/easing` module contains the [Penner easing equations](https://web.archive.org/web/20190805215728/http://robertpenner.com/easing/), or you can supply your own `p => t` function where `p` and `t` are both values between 0 and 1.
The full set of options available to `tweened`: The full set of options available to `tweened`:
@ -37,4 +37,4 @@ The full set of options available to `tweened`:
* `easing` — a `p => t` function * `easing` — a `p => t` function
* `interpolate` — a custom `(from, to) => t => value` function for interpolating between arbitrary values. By default, Svelte will interpolate between numbers, dates, and identically-shaped arrays and objects (as long as they only contain numbers and dates or other valid arrays and objects). If you want to interpolate (for example) colour strings or transformation matrices, supply a custom interpolator * `interpolate` — a custom `(from, to) => t => value` function for interpolating between arbitrary values. By default, Svelte will interpolate between numbers, dates, and identically-shaped arrays and objects (as long as they only contain numbers and dates or other valid arrays and objects). If you want to interpolate (for example) colour strings or transformation matrices, supply a custom interpolator
You can also pass these options to `progress.set` and `progress.update` as a second argument, in which case they will override the defaults. The `set` and `update` methods both return a promise that resolves when the tween completes. You can also pass these options to `progress.set` and `progress.update` as a second argument, in which case they will override the defaults. The `set` and `update` methods both return a promise that resolves when the tween completes.

@ -3,7 +3,6 @@
import flash from './flash.js'; import flash from './flash.js';
export let todo; export let todo;
export let toggle;
let div; let div;

@ -5,7 +5,6 @@
import flash from './flash.js'; import flash from './flash.js';
export let todo; export let todo;
export let toggle;
let div; let div;

@ -1291,17 +1291,23 @@
} }
}, },
"@sveltejs/svelte-repl": { "@sveltejs/svelte-repl": {
"version": "0.1.9", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/@sveltejs/svelte-repl/-/svelte-repl-0.1.9.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/svelte-repl/-/svelte-repl-0.1.13.tgz",
"integrity": "sha512-OXDfHwT5O7UXVYnf4ndTk3dKMITTmWcMty4/lOFte80ui01i47QiVy3GEe9G8FkcU1YBe+c06MMnIgm7j0Ln7Q==", "integrity": "sha512-griMkrRRzAQq22awKaBN5cewGly4xeeo8qpDs8ZhQxmEk5TcX3dMhIGMLuPTSv3Yr0s2c6TDJXcN+ORJn//fpQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"codemirror": "^5.48.4", "codemirror": "^5.49.2",
"estree-walker": "^0.6.1", "estree-walker": "^0.9.0",
"sourcemap-codec": "^1.4.6", "sourcemap-codec": "^1.4.6",
"yootils": "0.0.16" "yootils": "0.0.16"
}, },
"dependencies": { "dependencies": {
"estree-walker": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.9.0.tgz",
"integrity": "sha512-12U47o7XHUX329+x3FzNVjCx3SHEzMF0nkDv7r/HnBzX/xNTKxajBk6gyygaxrAFtLj39219oMfbtxv4KpaOiA==",
"dev": true
},
"sourcemap-codec": { "sourcemap-codec": {
"version": "1.4.6", "version": "1.4.6",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz",
@ -1587,9 +1593,9 @@
"dev": true "dev": true
}, },
"codemirror": { "codemirror": {
"version": "5.48.4", "version": "5.49.2",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.48.4.tgz", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.49.2.tgz",
"integrity": "sha512-pUhZXDQ6qXSpWdwlgAwHEkd4imA0kf83hINmUEzJpmG80T/XLtDDEzZo8f6PQLuRCcUQhmzqqIo3ZPTRaWByRA==", "integrity": "sha512-dwJ2HRPHm8w51WB5YTF9J7m6Z5dtkqbU9ntMZ1dqXyFB9IpjoUFDj80ahRVEoVanfIp6pfASJbOlbWdEf8FOzQ==",
"dev": true "dev": true
}, },
"color-convert": { "color-convert": {
@ -3368,12 +3374,6 @@
"requires": { "requires": {
"@babel/helper-module-imports": "^7.0.0", "@babel/helper-module-imports": "^7.0.0",
"rollup-pluginutils": "^2.8.1" "rollup-pluginutils": "^2.8.1"
},
"dependencies": {
"estree-walker": {
"version": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
}
} }
}, },
"rollup-plugin-commonjs": { "rollup-plugin-commonjs": {
@ -3411,10 +3411,6 @@
"rollup-pluginutils": "^2.8.1" "rollup-pluginutils": "^2.8.1"
}, },
"dependencies": { "dependencies": {
"estree-walker": {
"version": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
},
"resolve": { "resolve": {
"version": "1.11.1", "version": "1.11.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
@ -3458,12 +3454,6 @@
"rollup-pluginutils": "^2.8.1", "rollup-pluginutils": "^2.8.1",
"serialize-javascript": "^1.7.0", "serialize-javascript": "^1.7.0",
"terser": "^4.1.0" "terser": "^4.1.0"
},
"dependencies": {
"estree-walker": {
"version": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
}
} }
}, },
"rollup-pluginutils": { "rollup-pluginutils": {
@ -3473,14 +3463,6 @@
"dev": true, "dev": true,
"requires": { "requires": {
"estree-walker": "^0.6.1" "estree-walker": "^0.6.1"
},
"dependencies": {
"estree-walker": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
"dev": true
}
} }
}, },
"safe-buffer": { "safe-buffer": {

@ -36,7 +36,7 @@
"@babel/runtime": "^7.6.0", "@babel/runtime": "^7.6.0",
"@sindresorhus/slugify": "^0.9.1", "@sindresorhus/slugify": "^0.9.1",
"@sveltejs/site-kit": "^1.1.4", "@sveltejs/site-kit": "^1.1.4",
"@sveltejs/svelte-repl": "^0.1.9", "@sveltejs/svelte-repl": "^0.1.13",
"degit": "^2.1.4", "degit": "^2.1.4",
"dotenv": "^8.1.0", "dotenv": "^8.1.0",
"esm": "^3.2.25", "esm": "^3.2.25",

@ -13,6 +13,9 @@ const mode = process.env.NODE_ENV;
const dev = mode === 'development'; const dev = mode === 'development';
const legacy = !!process.env.SAPPER_LEGACY_BUILD; const legacy = !!process.env.SAPPER_LEGACY_BUILD;
const onwarn = (warning, onwarn) => (warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]@sapper[/\\]/.test(warning.message)) || onwarn(warning);
const dedupe = importee => importee === 'svelte' || importee.startsWith('svelte/');
export default { export default {
client: { client: {
input: config.client.input(), input: config.client.input(),
@ -28,7 +31,10 @@ export default {
hydratable: true, hydratable: true,
emitCss: true emitCss: true
}), }),
resolve(), resolve({
browser: true,
dedupe
}),
commonjs(), commonjs(),
json(), json(),
@ -53,6 +59,7 @@ export default {
module: true module: true
}) })
], ],
onwarn
}, },
server: { server: {
@ -67,7 +74,9 @@ export default {
generate: 'ssr', generate: 'ssr',
dev dev
}), }),
resolve(), resolve({
dedupe
}),
commonjs(), commonjs(),
json() json()
], ],
@ -78,6 +87,7 @@ export default {
require('module').builtinModules || Object.keys(process.binding('natives')) require('module').builtinModules || Object.keys(process.binding('natives'))
) )
], ],
onwarn
}, },
serviceworker: { serviceworker: {

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 103 124">
<path style="fill:black" d='M96.33,20.61C85.38,4.93,63.74.28,48.09,10.25L20.61,27.77A31.46,31.46,0,0,0,6.37,48.88,33.22,33.22,0,0,0,9.64,70.2,31.52,31.52,0,0,0,4.93,82a33.61,33.61,0,0,0,5.73,25.41c11,15.68,32.6,20.33,48.25,10.36l27.48-17.52a31.48,31.48,0,0,0,14.24-21.11A33.22,33.22,0,0,0,97.36,57.8,31.52,31.52,0,0,0,102.07,46a33.57,33.57,0,0,0-5.74-25.41
M45.41,108.86A21.81,21.81,0,0,1,22,100.18,20.2,20.2,0,0,1,18.53,84.9a19,19,0,0,1,.65-2.57l.52-1.58,1.41,1a35.32,35.32,0,0,0,10.75,5.37l1,.31-.1,1a6.2,6.2,0,0,0,1.11,4.08A6.57,6.57,0,0,0,41,95.19a6,6,0,0,0,1.68-.74L70.11,76.94a5.76,5.76,0,0,0,2.59-3.83,6.09,6.09,0,0,0-1-4.6,6.58,6.58,0,0,0-7.06-2.62,6.21,6.21,0,0,0-1.69.74L52.43,73.31a19.88,19.88,0,0,1-5.58,2.45,21.82,21.82,0,0,1-23.43-8.68A20.2,20.2,0,0,1,20,51.8a19,19,0,0,1,8.56-12.7L56,21.59a19.88,19.88,0,0,1,5.58-2.45A21.81,21.81,0,0,1,85,27.82,20.2,20.2,0,0,1,88.47,43.1a19,19,0,0,1-.65,2.57l-.52,1.58-1.41-1a35.32,35.32,0,0,0-10.75-5.37l-1-.31.1-1a6.2,6.2,0,0,0-1.11-4.08,6.57,6.57,0,0,0-7.06-2.62,6,6,0,0,0-1.68.74L36.89,51.06a5.71,5.71,0,0,0-2.58,3.83,6,6,0,0,0,1,4.6,6.58,6.58,0,0,0,7.06,2.62,6.21,6.21,0,0,0,1.69-.74l10.48-6.68a19.88,19.88,0,0,1,5.58-2.45,21.82,21.82,0,0,1,23.43,8.68A20.2,20.2,0,0,1,87,76.2a19,19,0,0,1-8.56,12.7L51,106.41a19.88,19.88,0,0,1-5.58,2.45' />
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -27,7 +27,7 @@ import Slot from './nodes/Slot';
import { Node, ImportDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal } from 'estree'; import { Node, ImportDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal } from 'estree';
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 } from 'code-red'; import { print, x, b } from 'code-red';
interface ComponentOptions { interface ComponentOptions {
namespace?: string; namespace?: string;
@ -240,8 +240,7 @@ export default class Component {
const { compile_options, name } = this; const { compile_options, name } = this;
const { format = 'esm' } = compile_options; const { format = 'esm' } = compile_options;
// TODO reinstate banner (along with fragment marker comments) const banner = `${this.file ? `${this.file} ` : ``}generated by Svelte v${'__VERSION__'}`;
const banner = `/* ${this.file ? `${this.file} ` : ``}generated by Svelte v${'__VERSION__'} */`;
const program: any = { type: 'Program', body: result }; const program: any = { type: 'Program', body: result };
@ -718,11 +717,13 @@ export default class Component {
let scope = instance_scope; let scope = instance_scope;
const toRemove = []; const to_remove = [];
const remove = (parent, prop, index) => { const remove = (parent, prop, index) => {
toRemove.unshift([parent, prop, index]); to_remove.unshift([parent, prop, index]);
}; };
const to_insert = new Map();
walk(content, { walk(content, {
enter(node, parent, prop, index) { enter(node, parent, prop, index) {
if (map.has(node)) { if (map.has(node)) {
@ -748,16 +749,41 @@ export default class Component {
} }
component.warn_on_undefined_store_value_references(node, parent, scope); component.warn_on_undefined_store_value_references(node, parent, scope);
if (component.compile_options.dev && component.compile_options.loopGuardTimeout > 0) {
const to_insert_for_loop_protect = component.loop_protect(node, prop, index, component.compile_options.loopGuardTimeout);
if (to_insert_for_loop_protect) {
if (!Array.isArray(parent[prop])) {
parent[prop] = {
type: 'BlockStatement',
body: [to_insert_for_loop_protect.node, node],
};
} else {
// can't insert directly, will screw up the index in the for-loop of estree-walker
if (!to_insert.has(parent)) {
to_insert.set(parent, []);
}
to_insert.get(parent).push(to_insert_for_loop_protect);
}
}
}
}, },
leave(node) { leave(node) {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
if (to_insert.has(node)) {
const nodes_to_insert = to_insert.get(node);
for (const { index, prop, node: node_to_insert } of nodes_to_insert.reverse()) {
node[prop].splice(index, 0, node_to_insert);
}
to_insert.delete(node);
}
}, },
}); });
for (const [parent, prop, index] of toRemove) { for (const [parent, prop, index] of to_remove) {
if (parent) { if (parent) {
if (index !== null) { if (index !== null) {
parent[prop].splice(index, 1); parent[prop].splice(index, 1);
@ -837,6 +863,32 @@ export default class Component {
} }
} }
loop_protect(node, prop, index, timeout) {
if (node.type === 'WhileStatement' ||
node.type === 'ForStatement' ||
node.type === 'DoWhileStatement') {
const guard = this.get_unique_name('guard');
this.add_var({
name: guard.name,
internal: true,
});
const before = b`const ${guard} = @loop_guard(${timeout})`;
const inside = b`${guard}();`;
// wrap expression statement with BlockStatement
if (node.body.type !== 'BlockStatement') {
node.body = {
type: 'BlockStatement',
body: [node.body],
};
}
node.body.body.push(inside[0]);
return { index, prop, node: before[0] };
}
return null;
}
invalidate(name, value?) { invalidate(name, value?) {
const variable = this.var_lookup.get(name); const variable = this.var_lookup.get(name);

@ -44,7 +44,7 @@ function edit_source(source, sveltePath) {
function esm( function esm(
program: any, program: any,
name: Identifier, name: Identifier,
_banner: string, banner: string,
sveltePath: string, sveltePath: string,
internal_path: string, internal_path: string,
helpers: Array<{ name: string; alias: Identifier }>, helpers: Array<{ name: string; alias: Identifier }>,
@ -98,6 +98,8 @@ function esm(
}; };
program.body = b` program.body = b`
/* ${banner} */
${import_declaration} ${import_declaration}
${internal_globals} ${internal_globals}
${imports} ${imports}
@ -112,7 +114,7 @@ function esm(
function cjs( function cjs(
program: any, program: any,
name: Identifier, name: Identifier,
_banner: string, banner: string,
sveltePath: string, sveltePath: string,
internal_path: string, internal_path: string,
helpers: Array<{ name: string; alias: Identifier }>, helpers: Array<{ name: string; alias: Identifier }>,
@ -188,6 +190,8 @@ function cjs(
const exports = module_exports.map(x => b`exports.${{ type: 'Identifier', name: x.as }} = ${{ type: 'Identifier', name: x.name }};`); const exports = module_exports.map(x => b`exports.${{ type: 'Identifier', name: x.as }} = ${{ type: 'Identifier', name: x.name }};`);
program.body = b` program.body = b`
/* ${banner} */
"use strict"; "use strict";
${internal_requires} ${internal_requires}
${internal_globals} ${internal_globals}

@ -3,6 +3,7 @@ import Stylesheet from './Stylesheet';
import { gather_possible_values, UNKNOWN } from './gather_possible_values'; import { gather_possible_values, UNKNOWN } from './gather_possible_values';
import { CssNode } from './interfaces'; import { CssNode } from './interfaces';
import Component from '../Component'; import Component from '../Component';
import Element from '../nodes/Element';
enum BlockAppliesToNode { enum BlockAppliesToNode {
NotPossible, NotPossible,
@ -34,8 +35,8 @@ export default class Selector {
this.used = this.blocks[0].global; this.used = this.blocks[0].global;
} }
apply(node: CssNode, stack: CssNode[]) { apply(node: Element, stack: Element[]) {
const to_encapsulate: CssNode[] = []; const to_encapsulate: any[] = [];
apply_selector(this.local_blocks.slice(), node, stack.slice(), to_encapsulate); apply_selector(this.local_blocks.slice(), node, stack.slice(), to_encapsulate);
@ -132,7 +133,7 @@ export default class Selector {
} }
} }
function apply_selector(blocks: Block[], node: CssNode, stack: CssNode[], to_encapsulate: any[]): boolean { function apply_selector(blocks: Block[], node: Element, stack: Element[], to_encapsulate: any[]): boolean {
const block = blocks.pop(); const block = blocks.pop();
if (!block) return false; if (!block) return false;
@ -259,16 +260,84 @@ function attribute_matches(node: CssNode, name: string, expected_value: string,
const attr = node.attributes.find((attr: CssNode) => attr.name === name); const attr = node.attributes.find((attr: CssNode) => attr.name === name);
if (!attr) return false; if (!attr) return false;
if (attr.is_true) return operator === null; if (attr.is_true) return operator === null;
if (attr.chunks.length > 1) return true;
if (!expected_value) return true; if (!expected_value) return true;
const value = attr.chunks[0]; if (attr.chunks.length === 1) {
const value = attr.chunks[0];
if (!value) return false; if (!value) return false;
if (value.type === 'Text') return test_attribute(operator, expected_value, case_insensitive, value.data); if (value.type === 'Text') return test_attribute(operator, expected_value, case_insensitive, value.data);
}
const possible_values = new Set(); const possible_values = new Set();
gather_possible_values(value.node, possible_values);
let prev_values = [];
for (const chunk of attr.chunks) {
const current_possible_values = new Set();
if (chunk.type === 'Text') {
current_possible_values.add(chunk.data);
} else {
gather_possible_values(chunk.node, current_possible_values);
}
// impossible to find out all combinations
if (current_possible_values.has(UNKNOWN)) return true;
if (prev_values.length > 0) {
const start_with_space = [];
const remaining = [];
current_possible_values.forEach((current_possible_value: string) => {
if (/^\s/.test(current_possible_value)) {
start_with_space.push(current_possible_value);
} else {
remaining.push(current_possible_value);
}
});
if (remaining.length > 0) {
if (start_with_space.length > 0) {
prev_values.forEach(prev_value => possible_values.add(prev_value));
}
const combined = [];
prev_values.forEach((prev_value: string) => {
remaining.forEach((value: string) => {
combined.push(prev_value + value);
});
});
prev_values = combined;
start_with_space.forEach((value: string) => {
if (/\s$/.test(value)) {
possible_values.add(value);
} else {
prev_values.push(value);
}
});
continue;
} else {
prev_values.forEach(prev_value => possible_values.add(prev_value));
prev_values = [];
}
}
current_possible_values.forEach((current_possible_value: string) => {
if (/\s$/.test(current_possible_value)) {
possible_values.add(current_possible_value);
} else {
prev_values.push(current_possible_value);
}
});
if (prev_values.length < current_possible_values.size) {
prev_values.push(' ');
}
if (prev_values.length > 20) {
// might grow exponentially, bail out
return true;
}
}
prev_values.forEach(prev_value => possible_values.add(prev_value));
if (possible_values.has(UNKNOWN)) return true; if (possible_values.has(UNKNOWN)) return true;
for (const value of possible_values) { for (const value of possible_values) {

@ -25,13 +25,14 @@ const valid_options = [
'customElement', 'customElement',
'tag', 'tag',
'css', 'css',
'loopGuardTimeout',
'preserveComments', 'preserveComments',
'preserveWhitespace', 'preserveWhitespace',
'optimiseAst', 'optimiseAst',
]; ];
function validate_options(options: CompileOptions, warnings: Warning[]) { function validate_options(options: CompileOptions, warnings: Warning[]) {
const { name, filename } = options; const { name, filename, loopGuardTimeout, dev } = options;
Object.keys(options).forEach(key => { Object.keys(options).forEach(key => {
if (valid_options.indexOf(key) === -1) { if (valid_options.indexOf(key) === -1) {
@ -56,6 +57,16 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
toString: () => message, toString: () => message,
}); });
} }
if (loopGuardTimeout && !dev) {
const message = 'options.loopGuardTimeout is for options.dev = true only';
warnings.push({
code: `options-loop-guard-timeout`,
message,
filename,
toString: () => message,
});
}
} }
export default function compile(source: string, options: CompileOptions = {}) { export default function compile(source: string, options: CompileOptions = {}) {

@ -151,6 +151,10 @@ export default class Element extends Node {
} }
} }
// Binding relies on Attribute, defer its evaluation
const order = ['Binding']; // everything else is -1
info.attributes.sort((a, b) => order.indexOf(a.type) - order.indexOf(b.type));
info.attributes.forEach(node => { info.attributes.forEach(node => {
switch (node.type) { switch (node.type) {
case 'Action': case 'Action':

@ -1,8 +1,6 @@
import Node from './shared/Node'; import Node from './shared/Node';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import Component from '../Component'; import Component from '../Component';
import { b, x } from 'code-red';
import Block from '../render_dom/Block';
import { sanitize } from '../../utils/names'; import { sanitize } from '../../utils/names';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
@ -14,6 +12,7 @@ export default class EventHandler extends Node {
handler_name: Identifier; handler_name: Identifier;
uses_context = false; uses_context = false;
can_make_passive = false; can_make_passive = false;
reassigned?: boolean;
constructor(component: Component, parent, template_scope, info) { constructor(component: Component, parent, template_scope, info) {
super(component, parent, template_scope, info); super(component, parent, template_scope, info);
@ -22,7 +21,7 @@ export default class EventHandler extends Node {
this.modifiers = new Set(info.modifiers); this.modifiers = new Set(info.modifiers);
if (info.expression) { if (info.expression) {
this.expression = new Expression(component, this, template_scope, info.expression, true); this.expression = new Expression(component, this, template_scope, info.expression);
this.uses_context = this.expression.uses_context; this.uses_context = this.expression.uses_context;
if (/FunctionExpression/.test(info.expression.type) && info.expression.params.length === 0) { if (/FunctionExpression/.test(info.expression.type) && info.expression.params.length === 0) {
@ -42,34 +41,12 @@ export default class EventHandler extends Node {
if (node && (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression') && node.params.length === 0) { if (node && (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression') && node.params.length === 0) {
this.can_make_passive = true; this.can_make_passive = true;
} }
this.reassigned = component.var_lookup.get(info.expression.name).reassigned;
} }
} }
} else { } else {
const id = component.get_unique_name(`${sanitize(this.name)}_handler`); this.handler_name = component.get_unique_name(`${sanitize(this.name)}_handler`);
component.add_var({
name: id.name,
internal: true,
referenced: true
});
component.partly_hoisted.push(b`
function ${id}(event) {
@bubble($$self, event);
}
`);
this.handler_name = id;
} }
} }
// TODO move this? it is specific to render-dom
render(block: Block) {
if (this.expression) {
return this.expression.manipulate(block);
}
// this.component.add_reference(this.handler_name);
return x`#ctx.${this.handler_name}`;
}
} }

@ -203,13 +203,11 @@ export default class Block {
} }
add_variable(id: Identifier, init?: Node) { add_variable(id: Identifier, init?: Node) {
this.variables.forEach(v => { if (this.variables.has(id.name)) {
if (v.id.name === id.name) { throw new Error(
throw new Error( `Variable '${id.name}' already initialised with a different value`
`Variable '${id.name}' already initialised with a different value` );
); }
}
});
this.variables.set(id.name, { id, init }); this.variables.set(id.name, { id, init });
} }
@ -268,7 +266,7 @@ export default class Block {
: this.chunks.hydrate : this.chunks.hydrate
); );
properties.create = x`function create() { properties.create = x`function #create() {
${this.chunks.create} ${this.chunks.create}
${hydrate} ${hydrate}
}`; }`;
@ -278,7 +276,7 @@ export default class Block {
if (this.chunks.claim.length === 0 && this.chunks.hydrate.length === 0) { if (this.chunks.claim.length === 0 && this.chunks.hydrate.length === 0) {
properties.claim = noop; properties.claim = noop;
} else { } else {
properties.claim = x`function claim(#nodes) { properties.claim = x`function #claim(#nodes) {
${this.chunks.claim} ${this.chunks.claim}
${this.renderer.options.hydratable && this.chunks.hydrate.length > 0 && b`this.h();`} ${this.renderer.options.hydratable && this.chunks.hydrate.length > 0 && b`this.h();`}
}`; }`;
@ -286,7 +284,7 @@ export default class Block {
} }
if (this.renderer.options.hydratable && this.chunks.hydrate.length > 0) { if (this.renderer.options.hydratable && this.chunks.hydrate.length > 0) {
properties.hydrate = x`function hydrate() { properties.hydrate = x`function #hydrate() {
${this.chunks.hydrate} ${this.chunks.hydrate}
}`; }`;
} }
@ -294,7 +292,7 @@ export default class Block {
if (this.chunks.mount.length === 0) { if (this.chunks.mount.length === 0) {
properties.mount = noop; properties.mount = noop;
} else { } else {
properties.mount = x`function mount(#target, anchor) { properties.mount = x`function #mount(#target, anchor) {
${this.chunks.mount} ${this.chunks.mount}
}`; }`;
} }
@ -304,7 +302,7 @@ export default class Block {
properties.update = noop; properties.update = noop;
} else { } else {
const ctx = this.maintain_context ? x`#new_ctx` : x`#ctx`; const ctx = this.maintain_context ? x`#new_ctx` : x`#ctx`;
properties.update = x`function update(#changed, ${ctx}) { properties.update = x`function #update(#changed, ${ctx}) {
${this.maintain_context && b`#ctx = ${ctx};`} ${this.maintain_context && b`#ctx = ${ctx};`}
${this.chunks.update} ${this.chunks.update}
}`; }`;
@ -312,15 +310,15 @@ export default class Block {
} }
if (this.has_animation) { if (this.has_animation) {
properties.measure = x`function measure() { properties.measure = x`function #measure() {
${this.chunks.measure} ${this.chunks.measure}
}`; }`;
properties.fix = x`function fix() { properties.fix = x`function #fix() {
${this.chunks.fix} ${this.chunks.fix}
}`; }`;
properties.animate = x`function animate() { properties.animate = x`function #animate() {
${this.chunks.animate} ${this.chunks.animate}
}`; }`;
} }
@ -329,7 +327,7 @@ export default class Block {
if (this.chunks.intro.length === 0) { if (this.chunks.intro.length === 0) {
properties.intro = noop; properties.intro = noop;
} else { } else {
properties.intro = x`function intro(#local) { properties.intro = x`function #intro(#local) {
${this.has_outros && b`if (#current) return;`} ${this.has_outros && b`if (#current) return;`}
${this.chunks.intro} ${this.chunks.intro}
}`; }`;
@ -338,7 +336,7 @@ export default class Block {
if (this.chunks.outro.length === 0) { if (this.chunks.outro.length === 0) {
properties.outro = noop; properties.outro = noop;
} else { } else {
properties.outro = x`function outro(#local) { properties.outro = x`function #outro(#local) {
${this.chunks.outro} ${this.chunks.outro}
}`; }`;
} }
@ -347,7 +345,7 @@ export default class Block {
if (this.chunks.destroy.length === 0) { if (this.chunks.destroy.length === 0) {
properties.destroy = noop; properties.destroy = noop;
} else { } else {
properties.destroy = x`function destroy(detaching) { properties.destroy = x`function #destroy(detaching) {
${this.chunks.destroy} ${this.chunks.destroy}
}`; }`;
} }
@ -376,6 +374,8 @@ export default class Block {
d: ${properties.destroy} d: ${properties.destroy}
}`; }`;
const block = dev && this.get_unique_name('block');
const body = b` const body = b`
${Array.from(this.variables.values()).map(({ id, init }) => { ${Array.from(this.variables.values()).map(({ id, init }) => {
return init return init
@ -387,9 +387,15 @@ export default class Block {
${dev ${dev
? b` ? b`
const block = ${return_value}; const ${block} = ${return_value};
@dispatch_dev("SvelteRegisterBlock", { block, id: ${this.name || 'create_fragment'}.name, type: "${this.type}", source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}", ctx: #ctx }); @dispatch_dev("SvelteRegisterBlock", {
return block;` block: ${block},
id: ${this.name || 'create_fragment'}.name,
type: "${this.type}",
source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}",
ctx: #ctx
});
return ${block};`
: b` : b`
return ${return_value};` return ${return_value};`
} }
@ -398,21 +404,36 @@ export default class Block {
return body; return body;
} }
has_content() {
return this.renderer.options.dev ||
this.first ||
this.event_listeners.length > 0 ||
this.chunks.intro.length > 0 ||
this.chunks.outro.length > 0 ||
this.chunks.create.length > 0 ||
this.chunks.hydrate.length > 0 ||
this.chunks.claim.length > 0 ||
this.chunks.mount.length > 0 ||
this.chunks.update.length > 0 ||
this.chunks.destroy.length > 0 ||
this.has_animation;
}
render() { render() {
const key = this.key && this.get_unique_name('key'); const key = this.key && this.get_unique_name('key');
const args: any[] = [x`#ctx`]; const args: any[] = [x`#ctx`];
if (key) args.unshift(key); if (key) args.unshift(key);
// TODO include this.comment const fn = b`function ${this.name}(${args}) {
// ${this.comment && `// ${escape(this.comment, { only_escape_at_symbol: true })}`} ${this.get_contents(key)}
}`;
return b` return this.comment
function ${this.name}(${args}) { ? b`
${this.get_contents(key)} // ${this.comment}
} ${fn}`
`; : fn;
} }
render_listeners(chunk: string = '') { render_listeners(chunk: string = '') {

@ -241,11 +241,16 @@ export default function dom(
args.push(x`$$props`, x`$$invalidate`); args.push(x`$$props`, x`$$invalidate`);
} }
body.push(b` const has_create_fragment = block.has_content();
function create_fragment(#ctx) { if (has_create_fragment) {
${block.get_contents()} body.push(b`
} function create_fragment(#ctx) {
${block.get_contents()}
}
`);
}
body.push(b`
${component.extract_javascript(component.ast.module)} ${component.extract_javascript(component.ast.module)}
${component.fully_hoisted} ${component.fully_hoisted}
@ -364,7 +369,7 @@ export default function dom(
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 => {
if (!writable_props.includes(key) && !key.startsWith('$$')) @_console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); if (!~writable_props.indexOf(key) && key.slice(0, 2) !== '$$') @_console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`);
}); });
`; `;
} }
@ -437,7 +442,7 @@ export default function dom(
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`} ${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot }, ${definition}, create_fragment, ${not_equal}, ${prop_names}); @init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_names});
${dev_props_check} ${dev_props_check}
@ -489,7 +494,7 @@ export default function dom(
constructor(options) { constructor(options) {
super(${options.dev && `options`}); super(${options.dev && `options`});
${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`} ${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`}
@init(this, options, ${definition}, create_fragment, ${not_equal}, ${prop_names}); @init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_names});
${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`} ${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`}
${dev_props_check} ${dev_props_check}

@ -3,21 +3,24 @@ import Wrapper from './shared/Wrapper';
import { b } from 'code-red'; import { b } from 'code-red';
import Body from '../../nodes/Body'; import Body from '../../nodes/Body';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
import EventHandler from './Element/EventHandler';
export default class BodyWrapper extends Wrapper { export default class BodyWrapper extends Wrapper {
node: Body; node: Body;
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
this.node.handlers.forEach(handler => { this.node.handlers
const snippet = handler.render(block); .map(handler => new EventHandler(handler, this))
.forEach(handler => {
const snippet = handler.get_snippet(block);
block.chunks.init.push(b` block.chunks.init.push(b`
@_document.body.addEventListener("${handler.name}", ${snippet}); @_document.body.addEventListener("${handler.node.name}", ${snippet});
`); `);
block.chunks.destroy.push(b` block.chunks.destroy.push(b`
@_document.body.removeEventListener("${handler.name}", ${snippet}); @_document.body.removeEventListener("${handler.node.name}", ${snippet});
`); `);
}); });
} }
} }

@ -7,7 +7,6 @@ import { b, x } from 'code-red';
import Expression from '../../../nodes/shared/Expression'; import Expression from '../../../nodes/shared/Expression';
import Text from '../../../nodes/Text'; import Text from '../../../nodes/Text';
import { changed } from '../shared/changed'; import { changed } from '../shared/changed';
import { Literal } from 'estree';
export default class AttributeWrapper { export default class AttributeWrapper {
node: Attribute; node: Attribute;
@ -72,89 +71,81 @@ export default class AttributeWrapper {
const is_legacy_input_type = element.renderer.component.compile_options.legacy && name === 'type' && this.parent.node.name === 'input'; const is_legacy_input_type = element.renderer.component.compile_options.legacy && name === 'type' && this.parent.node.name === 'input';
const dependencies = this.node.get_dependencies(); const dependencies = this.node.get_dependencies();
if (dependencies.length > 0) { const value = this.get_value(block);
let value;
// TODO some of this code is repeated in Tag.ts — would be good to
// DRY it out if that's possible without introducing crazy indirection
if (this.node.chunks.length === 1) {
// single {tag} — may be a non-string
value = (this.node.chunks[0] as Expression).manipulate(block);
} else {
value = this.node.name === 'class'
? this.get_class_name_text()
: this.render_chunks().reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
// '{foo} {bar}' — treat as string concatenation
if (this.node.chunks[0].type !== 'Text') {
value = x`"" + ${value}`;
}
}
const is_select_value_attribute = const is_src = this.node.name === 'src'; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750
name === 'value' && element.node.name === 'select'; const is_select_value_attribute =
name === 'value' && element.node.name === 'select';
const should_cache = is_select_value_attribute; // TODO is this necessary? const should_cache = is_src || this.node.should_cache() || is_select_value_attribute; // TODO is this necessary?
const last = should_cache && block.get_unique_name( const last = should_cache && block.get_unique_name(
`${element.var.name}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value` `${element.var.name}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value`
); );
if (should_cache) block.add_variable(last); if (should_cache) block.add_variable(last);
let updater; let updater;
const init = should_cache ? x`${last} = ${value}` : value; const init = should_cache ? x`${last} = ${value}` : value;
if (is_legacy_input_type) { if (is_legacy_input_type) {
block.chunks.hydrate.push( block.chunks.hydrate.push(
b`@set_input_type(${element.var}, ${init});` b`@set_input_type(${element.var}, ${init});`
); );
updater = b`@set_input_type(${element.var}, ${should_cache ? last : value});`; updater = b`@set_input_type(${element.var}, ${should_cache ? last : value});`;
} else if (is_select_value_attribute) { } else if (is_select_value_attribute) {
// annoying special case // annoying special case
const is_multiple_select = element.node.get_static_attribute_value('multiple'); const is_multiple_select = element.node.get_static_attribute_value('multiple');
const i = block.get_unique_name('i'); const i = block.get_unique_name('i');
const option = block.get_unique_name('option'); const option = block.get_unique_name('option');
const if_statement = is_multiple_select const if_statement = is_multiple_select
? b` ? b`
${option}.selected = ~${last}.indexOf(${option}.__value);` ${option}.selected = ~${last}.indexOf(${option}.__value);`
: b` : b`
if (${option}.__value === ${last}) { if (${option}.__value === ${last}) {
${option}.selected = true; ${option}.selected = true;
${{ type: 'BreakStatement' }}; ${{ type: 'BreakStatement' }};
}`; // TODO the BreakStatement is gross, but it's unsyntactic otherwise... }`; // TODO the BreakStatement is gross, but it's unsyntactic otherwise...
updater = b` updater = b`
for (var ${i} = 0; ${i} < ${element.var}.options.length; ${i} += 1) { for (var ${i} = 0; ${i} < ${element.var}.options.length; ${i} += 1) {
var ${option} = ${element.var}.options[${i}]; var ${option} = ${element.var}.options[${i}];
${if_statement} ${if_statement}
} }
`; `;
block.chunks.mount.push(b` block.chunks.mount.push(b`
${last} = ${value}; ${last} = ${value};
${updater} ${updater}
`); `);
} else if (property_name) { } else if (is_src) {
block.chunks.hydrate.push( block.chunks.hydrate.push(
b`${element.var}.${property_name} = ${init};` b`if (${element.var}.src !== ${init}) ${method}(${element.var}, "${name}", ${last});`
); );
updater = block.renderer.options.dev updater = b`${method}(${element.var}, "${name}", ${should_cache ? last : value});`;
? b`@prop_dev(${element.var}, "${property_name}", ${should_cache ? last : value});` } else if (property_name) {
: b`${element.var}.${property_name} = ${should_cache ? last : value};`; block.chunks.hydrate.push(
} else { b`${element.var}.${property_name} = ${init};`
block.chunks.hydrate.push( );
b`${method}(${element.var}, "${name}", ${init});` updater = block.renderer.options.dev
); ? b`@prop_dev(${element.var}, "${property_name}", ${should_cache ? last : value});`
updater = b`${method}(${element.var}, "${name}", ${should_cache ? last : value});`; : b`${element.var}.${property_name} = ${should_cache ? last : value};`;
} } else {
block.chunks.hydrate.push(
b`${method}(${element.var}, "${name}", ${init});`
);
updater = b`${method}(${element.var}, "${name}", ${should_cache ? last : value});`;
}
if (dependencies.length > 0) {
let condition = changed(dependencies); let condition = changed(dependencies);
if (should_cache) { if (should_cache) {
condition = x`${condition} && (${last} !== (${last} = ${value}))`; condition = is_src
? x`${condition} && (${element.var}.src !== (${last} = ${value}))`
: x`${condition} && (${last} !== (${last} = ${value}))`;
} }
if (block.has_outros) { if (block.has_outros) {
@ -165,23 +156,11 @@ export default class AttributeWrapper {
if (${condition}) { if (${condition}) {
${updater} ${updater}
}`); }`);
} else { }
const value = this.node.get_value(block);
const statement = (
is_legacy_input_type
? b`@set_input_type(${element.var}, ${value});`
: property_name
? b`${element.var}.${property_name} = ${value};`
: b`${method}(${element.var}, "${name}", ${value.type === 'Literal' && (value as Literal).value === true ? x`""` : value});`
);
block.chunks.hydrate.push(statement);
// special case autofocus. has to be handled in a bit of a weird way // special case autofocus. has to be handled in a bit of a weird way
if (this.node.is_true && name === 'autofocus') { if (this.node.is_true && name === 'autofocus') {
block.autofocus = element.var; block.autofocus = element.var;
}
} }
if (is_indirectly_bound_value) { if (is_indirectly_bound_value) {
@ -199,6 +178,36 @@ export default class AttributeWrapper {
return metadata; return metadata;
} }
get_value(block) {
if (this.node.is_true) {
const metadata = this.get_metadata();
if (metadata && boolean_attribute.has(metadata.property_name.toLowerCase())) {
return x`true`;
}
return x`""`;
}
if (this.node.chunks.length === 0) return x`""`;
// TODO some of this code is repeated in Tag.ts — would be good to
// DRY it out if that's possible without introducing crazy indirection
if (this.node.chunks.length === 1) {
return this.node.chunks[0].type === 'Text'
? string_literal((this.node.chunks[0] as Text).data)
: (this.node.chunks[0] as Expression).manipulate(block);
}
let value = this.node.name === 'class'
? this.get_class_name_text()
: this.render_chunks().reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
// '{foo} {bar}' — treat as string concatenation
if (this.node.chunks[0].type !== 'Text') {
value = x`"" + ${value}`;
}
return value;
}
get_class_name_text() { get_class_name_text() {
const scoped_css = this.node.chunks.some((chunk: Text) => chunk.synthetic); const scoped_css = this.node.chunks.some((chunk: Text) => chunk.synthetic);
const rendered = this.render_chunks(); const rendered = this.render_chunks();
@ -292,3 +301,32 @@ Object.keys(attribute_lookup).forEach(name => {
const metadata = attribute_lookup[name]; const metadata = attribute_lookup[name];
if (!metadata.property_name) metadata.property_name = name; if (!metadata.property_name) metadata.property_name = name;
}); });
// source: https://html.spec.whatwg.org/multipage/indices.html
const boolean_attribute = new Set([
'allowfullscreen',
'allowpaymentrequest',
'async',
'autofocus',
'autoplay',
'checked',
'controls',
'default',
'defer',
'disabled',
'formnovalidate',
'hidden',
'ismap',
'itemscope',
'loop',
'multiple',
'muted',
'nomodule',
'novalidate',
'open',
'playsinline',
'readonly',
'required',
'reversed',
'selected'
]);

@ -0,0 +1,69 @@
import EventHandler from '../../../nodes/EventHandler';
import Wrapper from '../shared/Wrapper';
import Block from '../../Block';
import { b, x, p } from 'code-red';
const TRUE = x`true`;
const FALSE = x`false`;
export default class EventHandlerWrapper {
node: EventHandler;
parent: Wrapper;
constructor(node: EventHandler, parent: Wrapper) {
this.node = node;
this.parent = parent;
if (!node.expression) {
this.parent.renderer.component.add_var({
name: node.handler_name.name,
internal: true,
referenced: true,
});
this.parent.renderer.component.partly_hoisted.push(b`
function ${node.handler_name.name}(event) {
@bubble($$self, event);
}
`);
}
}
get_snippet(block) {
const snippet = this.node.expression ? this.node.expression.manipulate(block) : x`#ctx.${this.node.handler_name}`;
if (this.node.reassigned) {
block.maintain_context = true;
return x`function () { ${snippet}.apply(this, arguments); }`;
}
return snippet;
}
render(block: Block, target: string) {
let snippet = this.get_snippet(block);
if (this.node.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`;
if (this.node.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`;
if (this.node.modifiers.has('self')) snippet = x`@self(${snippet})`;
const args = [];
const opts = ['passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod));
if (opts.length) {
args.push((opts.length === 1 && opts[0] === 'capture')
? TRUE
: x`{ ${opts.map(opt => p`${opt}: true`)} }`);
} else if (block.renderer.options.dev) {
args.push(FALSE);
}
if (block.renderer.options.dev) {
args.push(this.node.modifiers.has('stopPropagation') ? TRUE : FALSE);
args.push(this.node.modifiers.has('preventDefault') ? TRUE : FALSE);
}
block.event_listeners.push(
x`@listen(${target}, "${this.node.name}", ${snippet}, ${args})`
);
}
}

@ -24,6 +24,7 @@ import bind_this from '../shared/bind_this';
import { changed } from '../shared/changed'; import { changed } from '../shared/changed';
import { is_head } from '../shared/is_head'; import { is_head } from '../shared/is_head';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
import EventHandler from './EventHandler';
const events = [ const events = [
{ {
@ -113,12 +114,14 @@ export default class ElementWrapper extends Wrapper {
fragment: FragmentWrapper; fragment: FragmentWrapper;
attributes: AttributeWrapper[]; attributes: AttributeWrapper[];
bindings: Binding[]; bindings: Binding[];
event_handlers: EventHandler[];
class_dependencies: string[]; class_dependencies: string[];
slot_block: Block; slot_block: Block;
select_binding_dependencies?: Set<string>; select_binding_dependencies?: Set<string>;
var: any; var: any;
void: boolean;
constructor( constructor(
renderer: Renderer, renderer: Renderer,
@ -134,6 +137,8 @@ export default class ElementWrapper extends Wrapper {
name: node.name.replace(/[^a-zA-Z0-9_$]/g, '_') name: node.name.replace(/[^a-zA-Z0-9_$]/g, '_')
}; };
this.void = is_void(node.name);
this.class_dependencies = []; this.class_dependencies = [];
this.attributes = this.node.attributes.map(attribute => { this.attributes = this.node.attributes.map(attribute => {
@ -194,6 +199,8 @@ export default class ElementWrapper extends Wrapper {
// e.g. <audio bind:paused bind:currentTime> // e.g. <audio bind:paused bind:currentTime>
this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this)); this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this));
this.event_handlers = this.node.handlers.map(event_handler => new EventHandler(event_handler, this));
if (node.intro || node.outro) { if (node.intro || node.outro) {
if (node.intro) block.add_intro(node.intro.is_local); if (node.intro) block.add_intro(node.intro.is_local);
if (node.outro) block.add_outro(node.outro.is_local); if (node.outro) block.add_outro(node.outro.is_local);
@ -254,6 +261,7 @@ export default class ElementWrapper extends Wrapper {
const node = this.var; const node = this.var;
const nodes = parent_nodes && block.get_unique_name(`${this.var.name}_nodes`); // if we're in unclaimable territory, i.e. <head>, parent_nodes is null const nodes = parent_nodes && block.get_unique_name(`${this.var.name}_nodes`); // if we're in unclaimable territory, i.e. <head>, parent_nodes is null
const children = x`@children(${this.node.name === 'template' ? x`${node}.content` : node})`;
block.add_variable(node); block.add_variable(node);
const render_statement = this.get_render_statement(); const render_statement = this.get_render_statement();
@ -265,8 +273,13 @@ export default class ElementWrapper extends Wrapper {
if (parent_nodes) { if (parent_nodes) {
block.chunks.claim.push(b` block.chunks.claim.push(b`
${node} = ${this.get_claim_statement(parent_nodes)}; ${node} = ${this.get_claim_statement(parent_nodes)};
var ${nodes} = @children(${this.node.name === 'template' ? x`${node}.content` : node});
`); `);
if (!this.void && this.node.children.length > 0) {
block.chunks.claim.push(b`
var ${nodes} = ${children};
`);
}
} else { } else {
block.chunks.claim.push( block.chunks.claim.push(
b`${node} = ${render_statement};` b`${node} = ${render_statement};`
@ -291,7 +304,8 @@ export default class ElementWrapper extends Wrapper {
} }
// insert static children with textContent or innerHTML // insert static children with textContent or innerHTML
if (!this.node.namespace && (this.can_use_innerhtml || this.can_use_textcontent()) && this.fragment.nodes.length > 0) { const can_use_textcontent = this.can_use_textcontent();
if (!this.node.namespace && (this.can_use_innerhtml || can_use_textcontent) && this.fragment.nodes.length > 0) {
if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') { if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
block.chunks.create.push( block.chunks.create.push(
// @ts-ignore todo: should it be this.fragment.nodes[0].node.data instead? // @ts-ignore todo: should it be this.fragment.nodes[0].node.data instead?
@ -318,7 +332,8 @@ export default class ElementWrapper extends Wrapper {
quasis: [] quasis: []
}; };
to_html((this.fragment.nodes as unknown as Array<ElementWrapper | TextWrapper>), block, literal, state); const can_use_raw_text = !this.can_use_innerhtml && can_use_textcontent;
to_html((this.fragment.nodes as unknown as Array<ElementWrapper | TextWrapper>), block, literal, state, can_use_raw_text);
literal.quasis.push(state.quasi); literal.quasis.push(state.quasi);
block.chunks.create.push( block.chunks.create.push(
@ -345,18 +360,18 @@ export default class ElementWrapper extends Wrapper {
block.maintain_context = true; block.maintain_context = true;
} }
this.add_attributes(block);
this.add_bindings(block); this.add_bindings(block);
this.add_event_handlers(block); this.add_event_handlers(block);
this.add_attributes(block);
this.add_transitions(block); this.add_transitions(block);
this.add_animation(block); this.add_animation(block);
this.add_actions(block); this.add_actions(block);
this.add_classes(block); this.add_classes(block);
this.add_manual_style_scoping(block); this.add_manual_style_scoping(block);
if (nodes && this.renderer.options.hydratable) { if (nodes && this.renderer.options.hydratable && !this.void) {
block.chunks.claim.push( block.chunks.claim.push(
b`${nodes}.forEach(@detach);` b`${this.node.children.length > 0 ? nodes : children}.forEach(@detach);`
); );
} }
@ -620,7 +635,7 @@ export default class ElementWrapper extends Wrapper {
const snippet = x`{ ${ const snippet = x`{ ${
(metadata && metadata.property_name) || (metadata && metadata.property_name) ||
fix_attribute_casing(attr.node.name) fix_attribute_casing(attr.node.name)
}: ${attr.node.get_value(block)} }`; }: ${attr.get_value(block)} }`;
initial_props.push(snippet); initial_props.push(snippet);
updates.push(condition ? x`${condition} && ${snippet}` : snippet); updates.push(condition ? x`${condition} && ${snippet}` : snippet);
@ -628,10 +643,10 @@ export default class ElementWrapper extends Wrapper {
}); });
block.chunks.init.push(b` block.chunks.init.push(b`
var ${levels} = [${initial_props}]; let ${levels} = [${initial_props}];
var ${data} = {}; let ${data} = {};
for (var #i = 0; #i < ${levels}.length; #i += 1) { for (let #i = 0; #i < ${levels}.length; #i += 1) {
${data} = @assign(${data}, ${levels}[#i]); ${data} = @assign(${data}, ${levels}[#i]);
} }
`); `);
@ -650,7 +665,7 @@ export default class ElementWrapper extends Wrapper {
} }
add_event_handlers(block: Block) { add_event_handlers(block: Block) {
add_event_handlers(block, this.var, this.node.handlers); add_event_handlers(block, this.var, this.event_handlers);
} }
add_transitions( add_transitions(
@ -861,7 +876,7 @@ export default class ElementWrapper extends Wrapper {
} }
} }
function to_html(wrappers: Array<ElementWrapper | TextWrapper | TagWrapper>, block: Block, literal: any, state: any) { function to_html(wrappers: Array<ElementWrapper | TextWrapper | TagWrapper>, block: Block, literal: any, state: any, can_use_raw_text?: boolean) {
wrappers.forEach(wrapper => { wrappers.forEach(wrapper => {
if (wrapper.node.type === 'Text') { if (wrapper.node.type === 'Text') {
if ((wrapper as TextWrapper).use_space()) state.quasi.value.raw += ' '; if ((wrapper as TextWrapper).use_space()) state.quasi.value.raw += ' ';
@ -870,7 +885,8 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | TagWrapper>, blo
const raw = parent && ( const raw = parent && (
parent.name === 'script' || parent.name === 'script' ||
parent.name === 'style' parent.name === 'style' ||
can_use_raw_text
); );
state.quasi.value.raw += (raw ? wrapper.node.data : escape_html(wrapper.node.data)) state.quasi.value.raw += (raw ? wrapper.node.data : escape_html(wrapper.node.data))
@ -901,7 +917,7 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | TagWrapper>, blo
attr.node.chunks.forEach(chunk => { attr.node.chunks.forEach(chunk => {
if (chunk.type === 'Text') { if (chunk.type === 'Text') {
state.quasi.value.raw += chunk.data; state.quasi.value.raw += escape_html(chunk.data);
} else { } else {
literal.quasis.push(state.quasi); literal.quasis.push(state.quasi);
literal.expressions.push(chunk.manipulate(block)); literal.expressions.push(chunk.manipulate(block));
@ -918,11 +934,11 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | TagWrapper>, blo
state.quasi.value.raw += '>'; state.quasi.value.raw += '>';
if (!is_void(wrapper.node.name)) { if (!(wrapper as ElementWrapper).void) {
to_html((wrapper as ElementWrapper).fragment.nodes as Array<ElementWrapper | TextWrapper>, block, literal, state); to_html((wrapper as ElementWrapper).fragment.nodes as Array<ElementWrapper | TextWrapper>, block, literal, state);
state.quasi.value.raw += `</${wrapper.node.name}>`; state.quasi.value.raw += `</${wrapper.node.name}>`;
} }
} }
}); });
} }

@ -16,6 +16,7 @@ import is_dynamic from '../shared/is_dynamic';
import bind_this from '../shared/bind_this'; import bind_this from '../shared/bind_this';
import { changed } from '../shared/changed'; import { changed } from '../shared/changed';
import { Node, Identifier, ObjectExpression } from 'estree'; import { Node, Identifier, ObjectExpression } from 'estree';
import EventHandler from '../Element/EventHandler';
export default class InlineComponentWrapper extends Wrapper { export default class InlineComponentWrapper extends Wrapper {
var: Identifier; var: Identifier;
@ -299,7 +300,9 @@ export default class InlineComponentWrapper extends Wrapper {
updates.push(b` updates.push(b`
if (!${updating} && ${changed(Array.from(binding.expression.dependencies))}) { if (!${updating} && ${changed(Array.from(binding.expression.dependencies))}) {
${updating} = true;
${name_changes}.${binding.name} = ${snippet}; ${name_changes}.${binding.name} = ${snippet};
@add_flush_callback(() => ${updating} = false);
} }
`); `);
@ -336,8 +339,6 @@ export default class InlineComponentWrapper extends Wrapper {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${id}(${value}) { function ${id}(${value}) {
#ctx.${id}.call(null, ${value}, #ctx); #ctx.${id}.call(null, ${value}, #ctx);
${updating} = true;
@add_flush_callback(() => ${updating} = false);
} }
`); `);
@ -346,8 +347,6 @@ export default class InlineComponentWrapper extends Wrapper {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${id}(${value}) { function ${id}(${value}) {
#ctx.${id}.call(null, ${value}); #ctx.${id}.call(null, ${value});
${updating} = true;
@add_flush_callback(() => ${updating} = false);
} }
`); `);
} }
@ -365,7 +364,8 @@ export default class InlineComponentWrapper extends Wrapper {
}); });
const munged_handlers = this.node.handlers.map(handler => { const munged_handlers = this.node.handlers.map(handler => {
let snippet = handler.render(block); const event_handler = new EventHandler(handler, this);
let snippet = event_handler.get_snippet(block);
if (handler.modifiers.has('once')) snippet = x`@once(${snippet})`; if (handler.modifiers.has('once')) snippet = x`@once(${snippet})`;
return b`${name}.$on("${handler.name}", ${snippet});`; return b`${name}.$on("${handler.name}", ${snippet});`;
@ -396,12 +396,12 @@ export default class InlineComponentWrapper extends Wrapper {
`); `);
block.chunks.create.push( block.chunks.create.push(
b`if (${name}) ${name}.$$.fragment.c();` b`if (${name}) @create_component(${name}.$$.fragment);`
); );
if (parent_nodes && this.renderer.options.hydratable) { if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(
b`if (${name}) ${name}.$$.fragment.l(${parent_nodes});` b`if (${name}) @claim_component(${name}.$$.fragment, ${parent_nodes});`
); );
} }
@ -437,7 +437,7 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_bindings} ${munged_bindings}
${munged_handlers} ${munged_handlers}
${name}.$$.fragment.c(); @create_component(${name}.$$.fragment);
@transition_in(${name}.$$.fragment, 1); @transition_in(${name}.$$.fragment, 1);
@mount_component(${name}, ${update_mount_node}, ${anchor}); @mount_component(${name}, ${update_mount_node}, ${anchor});
} else { } else {
@ -472,11 +472,11 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_handlers} ${munged_handlers}
`); `);
block.chunks.create.push(b`${name}.$$.fragment.c();`); block.chunks.create.push(b`@create_component(${name}.$$.fragment);`);
if (parent_nodes && this.renderer.options.hydratable) { if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push( block.chunks.claim.push(
b`${name}.$$.fragment.l(${parent_nodes});` b`@claim_component(${name}.$$.fragment, ${parent_nodes});`
); );
} }

@ -8,6 +8,7 @@ import add_actions from './shared/add_actions';
import { changed } from './shared/changed'; import { changed } from './shared/changed';
import { Identifier } from 'estree'; import { Identifier } from 'estree';
import { TemplateNode } from '../../../interfaces'; import { TemplateNode } from '../../../interfaces';
import EventHandler from './Element/EventHandler';
const associated_events = { const associated_events = {
innerWidth: 'resize', innerWidth: 'resize',
@ -34,9 +35,11 @@ const readonly = new Set([
export default class WindowWrapper extends Wrapper { export default class WindowWrapper extends Wrapper {
node: Window; node: Window;
handlers: EventHandler[];
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) { constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) {
super(renderer, block, parent, node); super(renderer, block, parent, node);
this.handlers = this.node.handlers.map(handler => new EventHandler(handler, this));
} }
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
@ -47,7 +50,7 @@ export default class WindowWrapper extends Wrapper {
const bindings: Record<string, string> = {}; const bindings: Record<string, string> = {};
add_actions(component, block, '@_window', this.node.actions); add_actions(component, block, '@_window', this.node.actions);
add_event_handlers(block, '@_window', this.node.handlers); add_event_handlers(block, '@_window', this.handlers);
this.node.bindings.forEach(binding => { this.node.bindings.forEach(binding => {
// in dev mode, throw if read-only values are written to // in dev mode, throw if read-only values are written to
@ -127,7 +130,7 @@ export default class WindowWrapper extends Wrapper {
component.partly_hoisted.push(b` component.partly_hoisted.push(b`
function ${id}() { function ${id}() {
${props.map(prop => b`$$invalidate('${prop.name}', ${prop.name} = @_window.${prop.value});`)} ${props.map(prop => component.invalidate(prop.name, x`${prop.name} = @_window.${prop.value}`))}
} }
`); `);
@ -167,7 +170,7 @@ export default class WindowWrapper extends Wrapper {
component.partly_hoisted.push(b` component.partly_hoisted.push(b`
function ${id}() { function ${id}() {
$$invalidate('${name}', ${name} = @_navigator.onLine); ${component.invalidate(name, x`${name} = @_navigator.onLine`)}
} }
`); `);

@ -6,6 +6,7 @@ import MustacheTag from '../../../nodes/MustacheTag';
import RawMustacheTag from '../../../nodes/RawMustacheTag'; import RawMustacheTag from '../../../nodes/RawMustacheTag';
import { is_string } from './is_string'; import { is_string } from './is_string';
import { Node } from 'estree'; import { Node } from 'estree';
import { changed } from './changed';
export default class Tag extends Wrapper { export default class Tag extends Wrapper {
node: MustacheTag | RawMustacheTag; node: MustacheTag | RawMustacheTag;
@ -46,10 +47,7 @@ export default class Tag extends Wrapper {
if (this.node.should_cache) block.add_variable(value, snippet); if (this.node.should_cache) block.add_variable(value, snippet);
if (dependencies.length > 0) { if (dependencies.length > 0) {
let condition = x`#changed.${dependencies[0]}`; let condition = changed(dependencies);
for (let i = 1; i < dependencies.length; i += 1) {
condition = x`${condition} || #changed.${dependencies[i]}`;
}
if (block.has_outros) { if (block.has_outros) {
condition = x`!#current || ${condition}`; condition = x`!#current || ${condition}`;

@ -1,39 +1,10 @@
import Block from '../../Block'; import Block from '../../Block';
import EventHandler from '../../../nodes/EventHandler'; import EventHandler from '../Element/EventHandler';
import { x, p } from 'code-red';
const TRUE = x`true`;
const FALSE = x`false`;
export default function add_event_handlers( export default function add_event_handlers(
block: Block, block: Block,
target: string, target: string,
handlers: EventHandler[] handlers: EventHandler[]
) { ) {
handlers.forEach(handler => { handlers.forEach(handler => handler.render(block, target));
let snippet = handler.render(block);
if (handler.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`;
if (handler.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`;
if (handler.modifiers.has('self')) snippet = x`@self(${snippet})`;
const args = [];
const opts = ['passive', 'once', 'capture'].filter(mod => handler.modifiers.has(mod));
if (opts.length) {
args.push((opts.length === 1 && opts[0] === 'capture')
? TRUE
: x`{ ${opts.map(opt => p`${opt}: true`)} }`);
} else if (block.renderer.options.dev) {
args.push(FALSE);
}
if (block.renderer.options.dev) {
args.push(handler.modifiers.has('stopPropagation') ? TRUE : FALSE);
args.push(handler.modifiers.has('preventDefault') ? TRUE : FALSE);
}
block.event_listeners.push(
x`@listen(${target}, "${handler.name}", ${snippet}, ${args})`
);
});
} }

@ -1,7 +1,7 @@
export default function get_name_from_filename(filename: string) { export default function get_name_from_filename(filename: string) {
if (!filename) return null; if (!filename) return null;
// eslint-disable-next-line no-useless-escape
const parts = filename.split(/[\/\\]/); const parts = filename.split(/[/\\]/).map(encodeURI);
if (parts.length > 1) { if (parts.length > 1) {
const index_match = parts[parts.length - 1].match(/^index(\.\w+)/); const index_match = parts[parts.length - 1].match(/^index(\.\w+)/);
@ -12,6 +12,7 @@ export default function get_name_from_filename(filename: string) {
} }
const base = parts.pop() const base = parts.pop()
.replace(/%/g, 'u')
.replace(/\.[^.]+$/, "") .replace(/\.[^.]+$/, "")
.replace(/[^a-zA-Z_$0-9]+/g, '_') .replace(/[^a-zA-Z_$0-9]+/g, '_')
.replace(/^_/, '') .replace(/^_/, '')

@ -1,156 +1,8 @@
import { walk } from 'estree-walker'; import { Node } from 'estree';
import is_reference from 'is-reference'; import { analyze, Scope, extract_names, extract_identifiers } from 'periscopic';
import { Node, VariableDeclaration, ClassDeclaration, VariableDeclarator, ObjectPattern, Property, RestElement, ArrayPattern, Identifier } from 'estree';
import get_object from './get_object';
// TODO replace this with periscopic?
export function create_scopes(expression: Node) { export function create_scopes(expression: Node) {
const map = new WeakMap(); return analyze(expression);
const globals: Map<string, Node> = new Map();
let scope = new Scope(null, false);
walk(expression, {
enter(node, parent) {
if (node.type === 'ImportDeclaration') {
node.specifiers.forEach(specifier => {
scope.declarations.set(specifier.local.name, specifier);
});
} else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
if (node.type === 'FunctionDeclaration') {
scope.declarations.set(node.id.name, node);
scope = new Scope(scope, false);
map.set(node, scope);
} else {
scope = new Scope(scope, false);
map.set(node, scope);
if (node.type === 'FunctionExpression' && node.id) {
scope.declarations.set(node.id.name, node);
}
}
node.params.forEach((param) => {
extract_names(param).forEach(name => {
scope.declarations.set(name, node);
});
});
} else if (/For(?:In|Of)?Statement/.test(node.type)) {
scope = new Scope(scope, true);
map.set(node, scope);
} else if (node.type === 'BlockStatement') {
scope = new Scope(scope, true);
map.set(node, scope);
} else if (node.type === 'ClassDeclaration' || node.type === 'VariableDeclaration') {
scope.add_declaration(node);
} else if (node.type === 'CatchClause') {
scope = new Scope(scope, true);
map.set(node, scope);
extract_names(node.param).forEach(name => {
scope.declarations.set(name, node.param);
});
} else if (node.type === 'Identifier' && is_reference(node as Node, parent as Node)) {
if (!scope.has(node.name) && !globals.has(node.name)) {
globals.set(node.name, node);
}
}
},
leave(node: Node) {
if (map.has(node)) {
scope = scope.parent;
}
}
});
scope.declarations.forEach((_node, name) => {
globals.delete(name);
});
return { map, scope, globals };
} }
export class Scope { export { Scope, extract_names, extract_identifiers };
parent: Scope;
block: boolean;
declarations: Map<string, Node> = new Map();
initialised_declarations: Set<string> = new Set();
constructor(parent: Scope, block: boolean) {
this.parent = parent;
this.block = block;
}
add_declaration(node: VariableDeclaration | ClassDeclaration) {
if (node.type === 'VariableDeclaration') {
if (node.kind === 'var' && this.block && this.parent) {
this.parent.add_declaration(node);
} else {
node.declarations.forEach((declarator: VariableDeclarator) => {
extract_names(declarator.id).forEach(name => {
this.declarations.set(name, node);
if (declarator.init) this.initialised_declarations.add(name);
});
});
}
} else {
this.declarations.set(node.id.name, node);
}
}
find_owner(name: string): Scope {
if (this.declarations.has(name)) return this;
return this.parent && this.parent.find_owner(name);
}
has(name: string): boolean {
return (
this.declarations.has(name) || (this.parent && this.parent.has(name))
);
}
}
export function extract_names(param: Node): string[] {
return extract_identifiers(param).map((node: any) => node.name);
}
export function extract_identifiers(param: Node): Identifier[] {
const nodes: Identifier[] = [];
extractors[param.type] && extractors[param.type](nodes, param);
return nodes;
}
const extractors = {
Identifier(nodes: Node[], param: Node) {
nodes.push(param);
},
MemberExpression(nodes: Node[], param: Node) {
nodes.push(get_object(param));
},
ObjectPattern(nodes: Node[], param: ObjectPattern) {
param.properties.forEach((prop: Property | RestElement) => {
if (prop.type === 'RestElement') {
nodes.push(prop.argument);
} else {
extractors[prop.value.type](nodes, prop.value);
}
});
},
ArrayPattern(nodes: Node[], param: ArrayPattern) {
param.elements.forEach((element: Node) => {
if (element) extractors[element.type](nodes, element);
});
},
RestElement(nodes: Node[], param: any) {
extractors[param.argument.type](nodes, param.argument);
},
AssignmentPattern(nodes: Node[], param: any) {
extractors[param.left.type](nodes, param.left);
}
};

@ -12,13 +12,15 @@ export function escape(data: string, { only_escape_at_symbol = false } = {}) {
} }
const escaped = { const escaped = {
'"': '&quot;',
"'": '&#39;',
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;', '>': '&gt;',
}; };
export function escape_html(html) { export function escape_html(html) {
return String(html).replace(/[&<>]/g, match => escaped[match]); return String(html).replace(/["'&<>]/g, match => escaped[match]);
} }
export function escape_template(str) { export function escape_template(str) {

@ -121,6 +121,7 @@ export interface CompileOptions {
customElement?: boolean; customElement?: boolean;
tag?: string; tag?: string;
css?: boolean; css?: boolean;
loopGuardTimeout?: number;
preserveComments?: boolean; preserveComments?: boolean;
preserveWhitespace?: boolean; preserveWhitespace?: boolean;

@ -94,8 +94,12 @@ export default async function preprocess(
for (const fn of script) { for (const fn of script) {
source = await replace_async( source = await replace_async(
source, source,
/<script(\s[^]*?)?>([^]*?)<\/script>/gi, /<!--[^]*?-->|<script(\s[^]*?)?>([^]*?)<\/script>/gi,
async (match, attributes = '', content) => { async (match, attributes = '', content) => {
if (!attributes && !content) {
return match;
}
attributes = attributes || '';
const processed = await fn({ const processed = await fn({
content, content,
attributes: parse_attributes(attributes), attributes: parse_attributes(attributes),
@ -110,8 +114,11 @@ export default async function preprocess(
for (const fn of style) { for (const fn of style) {
source = await replace_async( source = await replace_async(
source, source,
/<style(\s[^]*?)?>([^]*?)<\/style>/gi, /<!--[^]*?-->|<style(\s[^]*?)?>([^]*?)<\/style>/gi,
async (match, attributes = '', content) => { async (match, attributes = '', content) => {
if (!attributes && !content) {
return match;
}
const processed: Processed = await fn({ const processed: Processed = await fn({
content, content,
attributes: parse_attributes(attributes), attributes: parse_attributes(attributes),

@ -1,19 +1,3 @@
declare module '*.svelte' { declare module '*.svelte' {
type Props = Record<string, any>; export { SvelteComponentDev as default } from 'svelte/internal';
export default class {
constructor(options: {
target: Element;
anchor?: Element;
props?: Props;
hydrate?: boolean;
intro?: boolean;
});
$set(props: Props): void;
$on<T = any>(event: string, callback: (event: CustomEvent<T>) => void): () => void;
$destroy(): void;
[accessor: string]: any;
}
} }

@ -8,5 +8,6 @@ export {
setContext, setContext,
getContext, getContext,
tick, tick,
createEventDispatcher createEventDispatcher,
SvelteComponentDev as SvelteComponent
} from 'svelte/internal'; } from 'svelte/internal';

@ -4,6 +4,21 @@ import { blank_object, is_function, run, run_all, noop, has_prop } from './utils
import { children } from './dom'; import { children } from './dom';
import { transition_in } from './transitions'; import { transition_in } from './transitions';
interface Fragment {
key: string|null;
first: null;
/* create */ c: () => void;
/* claim */ l: (nodes: any) => void;
/* hydrate */ h: () => void;
/* mount */ m: (target: HTMLElement, anchor: any) => void;
/* update */ p: (changed: any, ctx: any) => void;
/* measure */ r: () => void;
/* fix */ f: () => void;
/* animate */ a: () => void;
/* intro */ i: (local: any) => void;
/* outro */ o: (local: any) => void;
/* destroy */ d: (detaching: 0|1) => void;
}
// eslint-disable-next-line @typescript-eslint/class-name-casing // eslint-disable-next-line @typescript-eslint/class-name-casing
interface T$$ { interface T$$ {
dirty: null; dirty: null;
@ -13,7 +28,7 @@ interface T$$ {
callbacks: any; callbacks: any;
after_update: any[]; after_update: any[];
props: Record<string, 0 | string>; props: Record<string, 0 | string>;
fragment: null|any; fragment: null|false|Fragment;
not_equal: any; not_equal: any;
before_update: any[]; before_update: any[];
context: Map<any, any>; context: Map<any, any>;
@ -29,10 +44,18 @@ export function bind(component, name, callback) {
} }
} }
export function create_component(block) {
block && block.c();
}
export function claim_component(block, parent_nodes) {
block && block.l(parent_nodes);
}
export function mount_component(component, target, anchor) { export function mount_component(component, target, anchor) {
const { fragment, on_mount, on_destroy, after_update } = component.$$; const { fragment, on_mount, on_destroy, after_update } = component.$$;
fragment.m(target, anchor); fragment && fragment.m(target, anchor);
// onMount happens before the initial afterUpdate // onMount happens before the initial afterUpdate
add_render_callback(() => { add_render_callback(() => {
@ -51,15 +74,16 @@ export function mount_component(component, target, anchor) {
} }
export function destroy_component(component, detaching) { export function destroy_component(component, detaching) {
if (component.$$.fragment) { const $$ = component.$$;
run_all(component.$$.on_destroy); if ($$.fragment !== null) {
run_all($$.on_destroy);
component.$$.fragment.d(detaching); $$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to // TODO null out other refs, including component.$$ (but need to
// preserve final state?) // preserve final state?)
component.$$.on_destroy = component.$$.fragment = null; $$.on_destroy = $$.fragment = null;
component.$$.ctx = {}; $$.ctx = {};
} }
} }
@ -115,15 +139,17 @@ export function init(component, options, instance, create_fragment, not_equal, p
$$.update(); $$.update();
ready = true; ready = true;
run_all($$.before_update); run_all($$.before_update);
$$.fragment = create_fragment($$.ctx);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) { if (options.target) {
if (options.hydrate) { if (options.hydrate) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment!.l(children(options.target)); $$.fragment && $$.fragment!.l(children(options.target));
} else { } else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment!.c(); $$.fragment && $$.fragment!.c();
} }
if (options.intro) transition_in(component.$$.fragment); if (options.intro) transition_in(component.$$.fragment);

@ -79,8 +79,24 @@ export function set_data_dev(text, data) {
text.data = data; text.data = data;
} }
type Props = Record<string, any>;
export interface SvelteComponentDev {
$set(props?: Props): void;
$on<T = any>(event: string, callback: (event: CustomEvent<T>) => void): () => void;
$destroy(): void;
[accessor: string]: any;
}
export class SvelteComponentDev extends SvelteComponent { export class SvelteComponentDev extends SvelteComponent {
constructor(options) { constructor(options: {
target: Element;
anchor?: Element;
props?: Props;
hydrate?: boolean;
intro?: boolean;
$$inline?: boolean;
}) {
if (!options || (!options.target && !options.$$inline)) { if (!options || (!options.target && !options.$$inline)) {
throw new Error(`'target' is a required option`); throw new Error(`'target' is a required option`);
} }
@ -95,3 +111,12 @@ export class SvelteComponentDev extends SvelteComponent {
}; };
} }
} }
export function loop_guard(timeout) {
const start = Date.now();
return () => {
if (Date.now() - start > timeout) {
throw new Error(`Infinite loop detected`);
}
};
}

@ -30,7 +30,7 @@ export function onDestroy(fn) {
export function createEventDispatcher() { export function createEventDispatcher() {
const component = get_current_component(); const component = get_current_component();
return (type, detail) => { return (type: string, detail?: any) => {
const callbacks = component.$$.callbacks[type]; const callbacks = component.$$.callbacks[type];
if (callbacks) { if (callbacks) {

@ -70,10 +70,10 @@ export function flush() {
} }
function update($$) { function update($$) {
if ($$.fragment) { if ($$.fragment !== null) {
$$.update($$.dirty); $$.update($$.dirty);
run_all($$.before_update); run_all($$.before_update);
$$.fragment.p($$.dirty, $$.ctx); $$.fragment && $$.fragment.p($$.dirty, $$.ctx);
$$.dirty = null; $$.dirty = null;
$$.after_update.forEach(add_render_callback); $$.after_update.forEach(add_render_callback);

@ -1,4 +1,4 @@
import { cubicOut, cubicInOut } from 'svelte/easing'; import { cubicOut, cubicInOut, linear } from 'svelte/easing';
import { assign, is_function } from 'svelte/internal'; import { assign, is_function } from 'svelte/internal';
type EasingFunction = (t: number) => number; type EasingFunction = (t: number) => number;
@ -43,17 +43,20 @@ export function blur(node: Element, {
interface FadeParams { interface FadeParams {
delay: number; delay: number;
duration: number; duration: number;
easing: EasingFunction;
} }
export function fade(node: Element, { export function fade(node: Element, {
delay = 0, delay = 0,
duration = 400 duration = 400,
easing = linear
}: FadeParams): TransitionConfig { }: FadeParams): TransitionConfig {
const o = +getComputedStyle(node).opacity; const o = +getComputedStyle(node).opacity;
return { return {
delay, delay,
duration, duration,
easing,
css: t => `opacity: ${t * o}` css: t => `opacity: ${t * o}`
}; };
} }

@ -1,6 +1,6 @@
import * as assert from 'assert'; import * as assert from 'assert';
import * as fs from 'fs'; import * as fs from 'fs';
import { env, normalizeHtml, svelte } from '../helpers.js'; import { env, svelte, setupHtmlEqual, shouldUpdateExpected } from '../helpers.js';
function try_require(file) { function try_require(file) {
try { try {
@ -37,6 +37,10 @@ function create(code) {
} }
describe('css', () => { describe('css', () => {
before(() => {
setupHtmlEqual();
});
fs.readdirSync(`${__dirname}/samples`).forEach(dir => { fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
if (dir[0] === '.') return; if (dir[0] === '.') return;
@ -80,7 +84,17 @@ describe('css', () => {
css: read(`${__dirname}/samples/${dir}/expected.css`) css: read(`${__dirname}/samples/${dir}/expected.css`)
}; };
assert.equal(dom.css.code.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz'), expected.css); const actual_css = dom.css.code.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz');
try {
assert.equal(actual_css, expected.css);
} catch (error) {
if (shouldUpdateExpected()) {
fs.writeFileSync(`${__dirname}/samples/${dir}/expected.css`, actual_css);
console.log(`Updated ${dir}/expected.css.`);
} else {
throw error;
}
}
let ClientComponent; let ClientComponent;
let ServerComponent; let ServerComponent;
@ -114,10 +128,8 @@ describe('css', () => {
fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, html); fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, html);
assert.equal( const actual_html = html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz');
normalizeHtml(window, html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz')), assert.htmlEqual(actual_html, expected.html);
normalizeHtml(window, expected.html)
);
window.document.head.innerHTML = ''; // remove added styles window.document.head.innerHTML = ''; // remove added styles
} catch (err) { } catch (err) {
@ -127,13 +139,8 @@ describe('css', () => {
// ssr // ssr
try { try {
assert.equal( const actual_ssr = ServerComponent.render(config.props).html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz');
normalizeHtml( assert.htmlEqual(actual_ssr, expected.html);
window,
ServerComponent.render(config.props).html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz')
),
normalizeHtml(window, expected.html)
);
} catch (err) { } catch (err) {
console.log(ssr.js.code); console.log(ssr.js.code);
throw err; throw err;

@ -0,0 +1 @@
<p class=" svelte-xyz">Foo</p>

@ -0,0 +1,3 @@
<style>p { color: red; }</style>
<p class={undefined}>Foo</p>

@ -0,0 +1,143 @@
export default {
warnings: [
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
` 9: <style>
10: .foo {color: red;}
11: .fooaa {color: red;}
^
12: .foobb {color: red;}
13: .foocc {color: red;}`,
start: { line: 11, column: 2, character: 206 },
end: { line: 11, column: 8, character: 212 },
pos: 206,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`10: .foo {color: red;}
11: .fooaa {color: red;}
12: .foobb {color: red;}
^
13: .foocc {color: red;}
14: .foodd {color: red;}`,
start: { line: 12, column: 2, character: 229 },
end: { line: 12, column: 8, character: 235 },
pos: 229,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`12: .foobb {color: red;}
13: .foocc {color: red;}
14: .foodd {color: red;}
^
15: .aa {color: red;}
16: .bb {color: red;}`,
start: { line: 14, column: 2, character: 275 },
end: { line: 14, column: 8, character: 281 },
pos: 275,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`18: .dd {color: red;}
19: .aabar {color: red;}
20: .bbbar {color: red;}
^
21: .ccbar {color: red;}
22: .ddbar {color: red;}`,
start: { line: 20, column: 2, character: 401 },
end: { line: 20, column: 8, character: 407 },
pos: 401,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`19: .aabar {color: red;}
20: .bbbar {color: red;}
21: .ccbar {color: red;}
^
22: .ddbar {color: red;}
23: .fooaabar {color: red;}`,
start: { line: 21, column: 2, character: 424 },
end: { line: 21, column: 8, character: 430 },
pos: 424,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`20: .bbbar {color: red;}
21: .ccbar {color: red;}
22: .ddbar {color: red;}
^
23: .fooaabar {color: red;}
24: .foobbbar {color: red;}`,
start: { line: 22, column: 2, character: 447 },
end: { line: 22, column: 8, character: 453 },
pos: 447,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`21: .ccbar {color: red;}
22: .ddbar {color: red;}
23: .fooaabar {color: red;}
^
24: .foobbbar {color: red;}
25: .fooccbar {color: red;}`,
start: { line: 23, column: 2, character: 470 },
end: { line: 23, column: 11, character: 479 },
pos: 470,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`22: .ddbar {color: red;}
23: .fooaabar {color: red;}
24: .foobbbar {color: red;}
^
25: .fooccbar {color: red;}
26: .fooddbar {color: red;}`,
start: { line: 24, column: 2, character: 496 },
end: { line: 24, column: 11, character: 505 },
pos: 496,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`23: .fooaabar {color: red;}
24: .foobbbar {color: red;}
25: .fooccbar {color: red;}
^
26: .fooddbar {color: red;}
27: .baz {color: red;}`,
start: { line: 25, column: 2, character: 522 },
end: { line: 25, column: 11, character: 531 },
pos: 522,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame:
`26: .fooddbar {color: red;}
27: .baz {color: red;}
28: .unused {color: red;}
^
29: </style>`,
start: { line: 28, column: 2, character: 595 },
end: { line: 28, column: 9, character: 602 },
pos: 595,
},
],
};

@ -0,0 +1 @@
.foo.svelte-xyz{color:red}.foocc.svelte-xyz{color:red}.aa.svelte-xyz{color:red}.bb.svelte-xyz{color:red}.cc.svelte-xyz{color:red}.dd.svelte-xyz{color:red}.aabar.svelte-xyz{color:red}.fooddbar.svelte-xyz{color:red}.baz.svelte-xyz{color:red}

@ -0,0 +1,29 @@
<script>
export let a, b, c;
</script>
<div class="foo{a ? ' aa' : b ? ' bb ' : c ? 'cc ' : 'dd'}bar baz {a ? ' aa' : b ? ' bb ' : c ? 'cc ' : 'dd'}">
some stuff
</div>
<style>
.foo {color: red;}
.fooaa {color: red;}
.foobb {color: red;}
.foocc {color: red;}
.foodd {color: red;}
.aa {color: red;}
.bb {color: red;}
.cc {color: red;}
.dd {color: red;}
.aabar {color: red;}
.bbbar {color: red;}
.ccbar {color: red;}
.ddbar {color: red;}
.fooaabar {color: red;}
.foobbbar {color: red;}
.fooccbar {color: red;}
.fooddbar {color: red;}
.baz {color: red;}
.unused {color: red;}
</style>

@ -0,0 +1,3 @@
export default {
warnings: [],
};

@ -0,0 +1 @@
.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue}.hover.svelte-xyz{color:blue}.hover.unused.svelte-xyz{color:blue}.unused.svelte-xyz{color:blue}

@ -0,0 +1,18 @@
<script>
export let active;
export let hover;
</script>
<div class="thing {active ? 'active' : hover}">
some stuff
</div>
<style>
.thing {color: blue;}
.active {color: blue;}
.thing.active {color: blue;}
.hover { color: blue; }
.hover.unused { color: blue; }
.unused {color: blue;}
</style>

@ -0,0 +1,25 @@
export default {
warnings: [
{
code: 'css-unused-selector',
end: {
character: 205,
column: 9,
line: 14,
},
frame: `
12: .thing.active {color: blue;}
13:
14: .unused {color: blue;}
^
15: </style>`,
message: 'Unused CSS selector',
pos: 198,
start: {
character: 198,
column: 2,
line: 14,
},
},
],
};

@ -0,0 +1 @@
.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue}

@ -0,0 +1,15 @@
<script>
export let active;
</script>
<div class="thing {active ? 'active' : ''}">
some stuff
</div>
<style>
.thing {color: blue;}
.active {color: blue;}
.thing.active {color: blue;}
.unused {color: blue;}
</style>

@ -0,0 +1,31 @@
export default {
warnings: [
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame: `
13: .thing.active {color: blue;}
14: .hover { color: blue; }
15: .hover.unused { color: blue; }
^
16:
17: .unused {color: blue;}`,
start: { line: 15, column: 2, character: 261 },
end: { line: 15, column: 15, character: 274 },
pos: 261,
},
{
code: 'css-unused-selector',
message: 'Unused CSS selector',
frame: `
15: .hover.unused { color: blue; }
16:
17: .unused {color: blue;}
^
18: </style>`,
start: { line: 17, column: 2, character: 295 },
end: { line: 17, column: 9, character: 302 },
pos: 295,
},
],
};

@ -0,0 +1 @@
.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue}.hover.svelte-xyz{color:blue}

@ -0,0 +1,18 @@
<script>
export let active;
export let hover;
</script>
<div class="thing {active ? 'active' : hover ? 'hover' : ''}">
some stuff
</div>
<style>
.thing {color: blue;}
.active {color: blue;}
.thing.active {color: blue;}
.hover { color: blue; }
.hover.unused { color: blue; }
.unused {color: blue;}
</style>

@ -52,12 +52,19 @@ global.document = window.document;
global.navigator = window.navigator; global.navigator = window.navigator;
global.getComputedStyle = window.getComputedStyle; global.getComputedStyle = window.getComputedStyle;
global.requestAnimationFrame = null; // placeholder, filled in using set_raf global.requestAnimationFrame = null; // placeholder, filled in using set_raf
global.window = window;
// add missing ecmascript globals to window // add missing ecmascript globals to window
for (const key of Object.getOwnPropertyNames(global)) { for (const key of Object.getOwnPropertyNames(global)) {
window[key] = window[key] || global[key]; window[key] = window[key] || global[key];
} }
// implement mock scroll
window.scrollTo = function(pageXOffset, pageYOffset) {
window.pageXOffset = pageXOffset;
window.pageYOffset = pageYOffset;
};
export function env() { export function env() {
window.document.title = ''; window.document.title = '';
window.document.body.innerHTML = '<main></main>'; window.document.body.innerHTML = '<main></main>';
@ -199,8 +206,34 @@ export function showOutput(cwd, options = {}, compile = svelte.compile) {
}); });
} }
export function shouldUpdateExpected() {
return process.argv.includes('--update');
}
export function spaces(i) { export function spaces(i) {
let result = ''; let result = '';
while (i--) result += ' '; while (i--) result += ' ';
return result; return result;
} }
// fake timers
const original_set_timeout = global.setTimeout;
export function useFakeTimers() {
const callbacks = [];
global.setTimeout = function(fn) {
callbacks.push(fn);
};
return {
flush() {
callbacks.forEach(fn => fn());
callbacks.splice(0, callbacks.length);
},
removeFakeTimers() {
callbacks.splice(0, callbacks.length);
global.setTimeout = original_set_timeout;
}
};
}

@ -7,7 +7,8 @@ import {
loadConfig, loadConfig,
loadSvelte, loadSvelte,
env, env,
setupHtmlEqual setupHtmlEqual,
shouldUpdateExpected
} from '../helpers.js'; } from '../helpers.js';
let compileOptions = null; let compileOptions = null;
@ -76,7 +77,16 @@ describe('hydration', () => {
props: config.props props: config.props
}); });
assert.htmlEqual(target.innerHTML, fs.readFileSync(`${cwd}/_after.html`, 'utf-8')); try {
assert.htmlEqual(target.innerHTML, fs.readFileSync(`${cwd}/_after.html`, 'utf-8'));
} catch (error) {
if (shouldUpdateExpected()) {
fs.writeFileSync(`${cwd}/_after.html`, target.innerHTML);
console.log(`Updated ${cwd}/_after.html.`);
} else {
throw error;
}
}
if (config.test) { if (config.test) {
config.test(assert, target, snapshot, component, window); config.test(assert, target, snapshot, component, window);

@ -1,7 +1,8 @@
import * as assert from "assert"; import * as assert from "assert";
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import { loadConfig, svelte } from "../helpers.js"; import * as colors from "kleur";
import { loadConfig, svelte, shouldUpdateExpected } from "../helpers.js";
describe("js", () => { describe("js", () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => { fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
@ -14,8 +15,14 @@ describe("js", () => {
throw new Error("Forgot to remove `solo: true` from test"); throw new Error("Forgot to remove `solo: true` from test");
} }
dir = path.resolve(`${__dirname}/samples`, dir);
if (!fs.existsSync(`${dir}/input.svelte`)) {
console.log(colors.red().bold(`Missing file ${dir}/input.svelte. If you recently switched branches you may need to delete this directory`));
return;
}
(solo ? it.only : it)(dir, () => { (solo ? it.only : it)(dir, () => {
dir = path.resolve(`${__dirname}/samples`, dir);
const config = loadConfig(`${dir}/_config.js`); const config = loadConfig(`${dir}/_config.js`);
const input = fs.readFileSync(`${dir}/input.svelte`, "utf-8").replace(/\s+$/, ""); const input = fs.readFileSync(`${dir}/input.svelte`, "utf-8").replace(/\s+$/, "");
@ -34,16 +41,32 @@ describe("js", () => {
const output = `${dir}/_actual.js`; const output = `${dir}/_actual.js`;
fs.writeFileSync(output, actual); fs.writeFileSync(output, actual);
const expected = fs.readFileSync(`${dir}/expected.js`, "utf-8"); const expectedPath = `${dir}/expected.js`;
if (process.env.UPDATE_EXPECTED) { let expected = '';
fs.writeFileSync(`${dir}/expected.js`, actual); try {
expected = fs.readFileSync(expectedPath, "utf-8");
} catch (error) {
console.log(error);
if (error.code === 'ENOENT') {
// missing expected.js
fs.writeFileSync(expectedPath, actual);
}
} }
assert.equal( try {
actual.trim().replace(/^[ \t]+$/gm, ""), assert.equal(
expected.trim().replace(/^[ \t]+$/gm, "") actual.trim().replace(/^[ \t]+$/gm, ""),
); expected.trim().replace(/^[ \t]+$/gm, "")
);
} catch (error) {
if (shouldUpdateExpected()) {
fs.writeFileSync(expectedPath, actual);
console.log(`Updated ${expectedPath}.`);
} else {
throw error;
}
}
}); });
}); });
}); });

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
detach, detach,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
attr, attr,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
add_render_callback, add_render_callback,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
detach, detach,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
add_render_callback, add_render_callback,

@ -0,0 +1,83 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
attr,
detach,
element,
init,
insert,
listen,
noop,
run_all,
safe_not_equal,
space
} from "svelte/internal";
function create_fragment(ctx) {
let input0;
let t;
let input1;
let dispose;
return {
c() {
input0 = element("input");
t = space();
input1 = element("input");
attr(input0, "type", "file");
attr(input1, "type", "file");
dispose = [
listen(input0, "change", ctx.input0_change_handler),
listen(input1, "change", ctx.input1_change_handler)
];
},
m(target, anchor) {
insert(target, input0, anchor);
insert(target, t, anchor);
insert(target, input1, anchor);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(input0);
if (detaching) detach(t);
if (detaching) detach(input1);
run_all(dispose);
}
};
}
function instance($$self, $$props, $$invalidate) {
let { files } = $$props;
function input0_change_handler() {
files = this.files;
$$invalidate("files", files);
}
function input1_change_handler() {
files = this.files;
$$invalidate("files", files);
}
$$self.$set = $$props => {
if ("files" in $$props) $$invalidate("files", files = $$props.files);
};
return {
files,
input0_change_handler,
input1_change_handler
};
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, { files: 0 });
}
}
export default Component;

@ -0,0 +1,6 @@
<script>
export let files;
</script>
<input type="file" bind:files>
<input bind:files type="file">

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
append, append,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
append, append,

@ -1,5 +1,7 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -15,7 +17,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
nested.$$.fragment.c(); create_component(nested.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(nested, target, anchor); mount_component(nested, target, anchor);

@ -1,5 +1,7 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -15,7 +17,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
nested.$$.fragment.c(); create_component(nested.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(nested, target, anchor); mount_component(nested, target, anchor);

@ -1,5 +1,7 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -15,7 +17,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
nested.$$.fragment.c(); create_component(nested.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(nested, target, anchor); mount_component(nested, target, anchor);

@ -1,5 +1,7 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
detach, detach,
element, element,
@ -28,9 +30,9 @@ function create_fragment(ctx) {
return { return {
c() { c() {
foo.$$.fragment.c(); create_component(foo.$$.fragment);
t0 = space(); t0 = space();
bar.$$.fragment.c(); create_component(bar.$$.fragment);
t1 = space(); t1 = space();
input = element("input"); input = element("input");
dispose = listen(input, "input", ctx.input_input_handler); dispose = listen(input, "input", ctx.input_input_handler);

@ -1,5 +1,7 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
create_component,
destroy_component, destroy_component,
init, init,
mount_component, mount_component,
@ -15,7 +17,7 @@ function create_fragment(ctx) {
return { return {
c() { c() {
nested.$$.fragment.c(); create_component(nested.$$.fragment);
}, },
m(target, anchor) { m(target, anchor) {
mount_component(nested, target, anchor); mount_component(nested, target, anchor);

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
append, append,

@ -1,25 +1,14 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
component_subscribe, component_subscribe,
init, init,
noop,
safe_not_equal, safe_not_equal,
set_store_value set_store_value
} from "svelte/internal"; } from "svelte/internal";
import { count } from "./store.js"; import { count } from "./store.js";
function create_fragment(ctx) {
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let $count; let $count;
component_subscribe($$self, count, $$value => $$invalidate("$count", $count = $$value)); component_subscribe($$self, count, $$value => $$invalidate("$count", $count = $$value));
@ -34,7 +23,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, instance, create_fragment, safe_not_equal, { increment: 0 }); init(this, options, instance, null, safe_not_equal, { increment: 0 });
} }
get increment() { get increment() {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
append, append,

@ -1,15 +1,5 @@
import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; /* generated by Svelte vX.Y.Z */
import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function create_fragment(ctx) {
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let { x } = $$props; let { x } = $$props;
@ -32,7 +22,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, instance, create_fragment, safe_not_equal, { x: 0, a: 0, b: 0 }); init(this, options, instance, null, safe_not_equal, { x: 0, a: 0, b: 0 });
} }
get a() { get a() {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
append, append,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteElement, SvelteElement,
detach, detach,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
attr, attr,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponentDev, SvelteComponentDev,
add_location, add_location,
@ -67,7 +68,7 @@ function instance($$self, $$props, $$invalidate) {
const writable_props = ["name"]; const writable_props = ["name"];
Object.keys($$props).forEach(key => { Object.keys($$props).forEach(key => {
if (!writable_props.includes(key) && !key.startsWith("$$")) console.warn(`<Component> was created with unknown prop '${key}'`); if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
}); });
$$self.$set = $$props => { $$self.$set = $$props => {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponentDev, SvelteComponentDev,
add_location, add_location,
@ -23,6 +24,7 @@ function get_each_context(ctx, list, i) {
return child_ctx; return child_ctx;
} }
// (8:0) {#each things as thing}
function create_each_block(ctx) { function create_each_block(ctx) {
let span; let span;
let t0_fn = ctx => ctx.thing.name + ""; let t0_fn = ctx => ctx.thing.name + "";
@ -166,7 +168,7 @@ function instance($$self, $$props, $$invalidate) {
const writable_props = ["things", "foo", "bar", "baz"]; const writable_props = ["things", "foo", "bar", "baz"];
Object.keys($$props).forEach(key => { Object.keys($$props).forEach(key => {
if (!writable_props.includes(key) && !key.startsWith("$$")) console.warn(`<Component> was created with unknown prop '${key}'`); if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
}); });
$$self.$set = $$props => { $$self.$set = $$props => {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponentDev, SvelteComponentDev,
add_location, add_location,
@ -23,6 +24,7 @@ function get_each_context(ctx, list, i) {
return child_ctx; return child_ctx;
} }
// (6:0) {#each things as thing}
function create_each_block(ctx) { function create_each_block(ctx) {
let span; let span;
let t0_fn = ctx => ctx.thing.name + ""; let t0_fn = ctx => ctx.thing.name + "";
@ -164,7 +166,7 @@ function instance($$self, $$props, $$invalidate) {
const writable_props = ["things", "foo"]; const writable_props = ["things", "foo"];
Object.keys($$props).forEach(key => { Object.keys($$props).forEach(key => {
if (!writable_props.includes(key) && !key.startsWith("$$")) console.warn(`<Component> was created with unknown prop '${key}'`); if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
}); });
$$self.$set = $$props => { $$self.$set = $$props => {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponentDev, SvelteComponentDev,
dispatch_dev, dispatch_dev,

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponentDev, SvelteComponentDev,
destroy_each, destroy_each,
@ -21,6 +22,7 @@ function get_each_context(ctx, list, i) {
return child_ctx; return child_ctx;
} }
// (4:0) {#each things as thing, index}
function create_each_block(ctx) { function create_each_block(ctx) {
let t0; let t0;
let t1_value = ctx.thing + ""; let t1_value = ctx.thing + "";

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { create_ssr_component, debug, each, escape } from "svelte/internal"; import { create_ssr_component, debug, each, escape } from "svelte/internal";
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => { const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
append, append,
@ -19,6 +20,7 @@ function get_each_context(ctx, list, i) {
return child_ctx; return child_ctx;
} }
// (5:0) {#each createElement as node}
function create_each_block(ctx) { function create_each_block(ctx) {
let span; let span;
let t_fn = ctx => ctx.node + ""; let t_fn = ctx => ctx.node + "";

@ -1,16 +1,7 @@
import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; /* generated by Svelte vX.Y.Z */
import { onMount } from "svelte"; import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
function create_fragment(ctx) { import { onMount } from "svelte";
return {
c: noop,
m: noop,
p: noop,
i: noop,
o: noop,
d: noop
};
}
function instance($$self, $$props, $$invalidate) { function instance($$self, $$props, $$invalidate) {
let { foo = "bar" } = $$props; let { foo = "bar" } = $$props;
@ -29,7 +20,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent { class Component extends SvelteComponent {
constructor(options) { constructor(options) {
super(); super();
init(this, options, instance, create_fragment, safe_not_equal, { foo: 0 }); init(this, options, instance, null, safe_not_equal, { foo: 0 });
} }
} }

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponentDev, SvelteComponentDev,
add_location, add_location,
@ -66,7 +67,7 @@ function instance($$self, $$props, $$invalidate) {
const writable_props = ["foo"]; const writable_props = ["foo"];
Object.keys($$props).forEach(key => { Object.keys($$props).forEach(key => {
if (!writable_props.includes(key) && !key.startsWith("$$")) console.warn(`<Component> was created with unknown prop '${key}'`); if (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console.warn(`<Component> was created with unknown prop '${key}'`);
}); });
$$self.$set = $$props => { $$self.$set = $$props => {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import { import {
SvelteComponent, SvelteComponent,
detach, detach,

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

Loading…
Cancel
Save