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>

@ -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`:

@ -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,7 +934,7 @@ 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