merge master -> optimise-build

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

@ -1,6 +1,20 @@
# 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))
@ -24,6 +38,24 @@ Also:
* 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))
* 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

@ -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 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
When adding a new breaking change, follow this template in your pull request:

36
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.13.0-alpha.2",
"version": "3.14.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -30,6 +30,16 @@
"integrity": "sha512-KioOCsSvSvXx6xUNLiJz+P+VMb7NRcePjoefOr74Y5P6lEKsiOn35eZyZzgpK4XCNJdXTDR7+zykj0lwxRvZ2g==",
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@ -490,14 +500,14 @@
"dev": true
},
"code-red": {
"version": "0.0.18",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.18.tgz",
"integrity": "sha512-g7W6RwRqBbQTtMaUqrNWDyyl2GK0Uulk/uZPzGdgTXpOGX/LA8bW67EKQLdQgpYfd6APhZVwoX2lrL7mnJOWkA==",
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.21.tgz",
"integrity": "sha512-luUVxyGNwTU/lG+lY+pitXHJzgDvFa0WfxA4Nsd7uk/S366mHY8B86pwAcyRcKY9BE/HvryVNXCR691Q5ry+Ww==",
"dev": true,
"requires": {
"acorn": "^7.1.0",
"is-reference": "^1.1.4",
"periscopic": "^1.0.2",
"periscopic": "^2.0.1",
"sourcemap-codec": "^1.4.6"
}
},
@ -2713,9 +2723,9 @@
"dev": true
},
"periscopic": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-1.0.2.tgz",
"integrity": "sha512-KpKBKadLf8THXOxswQBhOY8E1lVVhfUidacPtQBrq7KDXaNkQLUPiTmXagzqpJGECP3/0gDXYFO6CZHVbGvOSw==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-2.0.1.tgz",
"integrity": "sha512-twJ8e4RatllMAcbmBqKj8cvZ94HtqSzbb8hJoGj4iSCcCHXxKb06HRxOq4heyq2x/6mKynJDvTTreHCz+m6lJw==",
"dev": true,
"requires": {
"is-reference": "^1.1.4"
@ -3109,16 +3119,6 @@
"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": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-sucrase/-/rollup-plugin-sucrase-2.1.0.tgz",

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

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

@ -6,7 +6,9 @@ authorURL: https://twitter.com/Rich_Harris
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
* svelte-vscode
@ -14,7 +16,7 @@ draft: true
## 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
"*":
@ -45,3 +47,23 @@ To set the filetype for a single file, use a [modeline](https://vim.fandom.com/w
```
<!-- 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
<Keypad bind:value={pin}/>

@ -53,6 +53,7 @@ The following options can be passed to the compiler. None are required:
| `tag` | string | null
| `accessors` | boolean | `false`
| `css` | boolean | `true`
| `loopGuardTimeout` | number | 0
| `preserveComments` | boolean | `false`
| `preserveWhitespace` | boolean | `false`
| `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.
| `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.
| `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.
| `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.

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

@ -33,7 +33,7 @@
font-family: 'Overpass';
letter-spacing: 0.12em;
color: #676778;
font-weight: 100;
font-weight: 400;
}
.centered span {
@ -71,4 +71,4 @@
toggle me
</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 -->
<button autofocus on:click={close}>close modal</button>
<a href="argh">argh</a>
</div>
<style>
@ -80,4 +78,4 @@
button {
display: block;
}
</style>
</style>

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

@ -30,3 +30,12 @@ function addNumber() {
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%;
height: 100%;
background-color: #666;
-webkit-mask: url(logo-mask.svg) 50% 50% no-repeat;
mask: url(logo-mask.svg) 50% 50% no-repeat;
-webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
}
</style>

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

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

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

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

@ -1291,17 +1291,23 @@
}
},
"@sveltejs/svelte-repl": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@sveltejs/svelte-repl/-/svelte-repl-0.1.9.tgz",
"integrity": "sha512-OXDfHwT5O7UXVYnf4ndTk3dKMITTmWcMty4/lOFte80ui01i47QiVy3GEe9G8FkcU1YBe+c06MMnIgm7j0Ln7Q==",
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/@sveltejs/svelte-repl/-/svelte-repl-0.1.13.tgz",
"integrity": "sha512-griMkrRRzAQq22awKaBN5cewGly4xeeo8qpDs8ZhQxmEk5TcX3dMhIGMLuPTSv3Yr0s2c6TDJXcN+ORJn//fpQ==",
"dev": true,
"requires": {
"codemirror": "^5.48.4",
"estree-walker": "^0.6.1",
"codemirror": "^5.49.2",
"estree-walker": "^0.9.0",
"sourcemap-codec": "^1.4.6",
"yootils": "0.0.16"
},
"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": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz",
@ -1587,9 +1593,9 @@
"dev": true
},
"codemirror": {
"version": "5.48.4",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.48.4.tgz",
"integrity": "sha512-pUhZXDQ6qXSpWdwlgAwHEkd4imA0kf83hINmUEzJpmG80T/XLtDDEzZo8f6PQLuRCcUQhmzqqIo3ZPTRaWByRA==",
"version": "5.49.2",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.49.2.tgz",
"integrity": "sha512-dwJ2HRPHm8w51WB5YTF9J7m6Z5dtkqbU9ntMZ1dqXyFB9IpjoUFDj80ahRVEoVanfIp6pfASJbOlbWdEf8FOzQ==",
"dev": true
},
"color-convert": {
@ -3368,12 +3374,6 @@
"requires": {
"@babel/helper-module-imports": "^7.0.0",
"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": {
@ -3411,10 +3411,6 @@
"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=="
},
"resolve": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
@ -3458,12 +3454,6 @@
"rollup-pluginutils": "^2.8.1",
"serialize-javascript": "^1.7.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": {
@ -3473,14 +3463,6 @@
"dev": true,
"requires": {
"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": {

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

@ -13,6 +13,9 @@ const mode = process.env.NODE_ENV;
const dev = mode === 'development';
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 {
client: {
input: config.client.input(),
@ -28,7 +31,10 @@ export default {
hydratable: true,
emitCss: true
}),
resolve(),
resolve({
browser: true,
dedupe
}),
commonjs(),
json(),
@ -53,6 +59,7 @@ export default {
module: true
})
],
onwarn
},
server: {
@ -67,7 +74,9 @@ export default {
generate: 'ssr',
dev
}),
resolve(),
resolve({
dedupe
}),
commonjs(),
json()
],
@ -78,6 +87,7 @@ export default {
require('module').builtinModules || Object.keys(process.binding('natives'))
)
],
onwarn
},
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 add_to_set from './utils/add_to_set';
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 {
namespace?: string;
@ -240,8 +240,7 @@ export default class Component {
const { compile_options, name } = this;
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 };
@ -718,11 +717,13 @@ export default class Component {
let scope = instance_scope;
const toRemove = [];
const to_remove = [];
const remove = (parent, prop, index) => {
toRemove.unshift([parent, prop, index]);
to_remove.unshift([parent, prop, index]);
};
const to_insert = new Map();
walk(content, {
enter(node, parent, prop, index) {
if (map.has(node)) {
@ -748,16 +749,41 @@ export default class Component {
}
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) {
if (map.has(node)) {
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 (index !== null) {
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?) {
const variable = this.var_lookup.get(name);

@ -44,7 +44,7 @@ function edit_source(source, sveltePath) {
function esm(
program: any,
name: Identifier,
_banner: string,
banner: string,
sveltePath: string,
internal_path: string,
helpers: Array<{ name: string; alias: Identifier }>,
@ -98,6 +98,8 @@ function esm(
};
program.body = b`
/* ${banner} */
${import_declaration}
${internal_globals}
${imports}
@ -112,7 +114,7 @@ function esm(
function cjs(
program: any,
name: Identifier,
_banner: string,
banner: string,
sveltePath: string,
internal_path: string,
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 }};`);
program.body = b`
/* ${banner} */
"use strict";
${internal_requires}
${internal_globals}

@ -3,6 +3,7 @@ import Stylesheet from './Stylesheet';
import { gather_possible_values, UNKNOWN } from './gather_possible_values';
import { CssNode } from './interfaces';
import Component from '../Component';
import Element from '../nodes/Element';
enum BlockAppliesToNode {
NotPossible,
@ -34,8 +35,8 @@ export default class Selector {
this.used = this.blocks[0].global;
}
apply(node: CssNode, stack: CssNode[]) {
const to_encapsulate: CssNode[] = [];
apply(node: Element, stack: Element[]) {
const to_encapsulate: any[] = [];
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();
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);
if (!attr) return false;
if (attr.is_true) return operator === null;
if (attr.chunks.length > 1) return true;
if (!expected_value) return true;
const value = attr.chunks[0];
if (!value) return false;
if (value.type === 'Text') return test_attribute(operator, expected_value, case_insensitive, value.data);
if (attr.chunks.length === 1) {
const value = attr.chunks[0];
if (!value) return false;
if (value.type === 'Text') return test_attribute(operator, expected_value, case_insensitive, value.data);
}
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;
for (const value of possible_values) {

@ -25,13 +25,14 @@ const valid_options = [
'customElement',
'tag',
'css',
'loopGuardTimeout',
'preserveComments',
'preserveWhitespace',
'optimiseAst',
];
function validate_options(options: CompileOptions, warnings: Warning[]) {
const { name, filename } = options;
const { name, filename, loopGuardTimeout, dev } = options;
Object.keys(options).forEach(key => {
if (valid_options.indexOf(key) === -1) {
@ -56,6 +57,16 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
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 = {}) {

@ -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 => {
switch (node.type) {
case 'Action':

@ -1,8 +1,6 @@
import Node from './shared/Node';
import Expression from './shared/Expression';
import Component from '../Component';
import { b, x } from 'code-red';
import Block from '../render_dom/Block';
import { sanitize } from '../../utils/names';
import { Identifier } from 'estree';
@ -14,6 +12,7 @@ export default class EventHandler extends Node {
handler_name: Identifier;
uses_context = false;
can_make_passive = false;
reassigned?: boolean;
constructor(component: 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);
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;
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) {
this.can_make_passive = true;
}
this.reassigned = component.var_lookup.get(info.expression.name).reassigned;
}
}
} else {
const id = 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;
this.handler_name = component.get_unique_name(`${sanitize(this.name)}_handler`);
}
}
// 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) {
this.variables.forEach(v => {
if (v.id.name === id.name) {
throw new Error(
`Variable '${id.name}' already initialised with a different value`
);
}
});
if (this.variables.has(id.name)) {
throw new Error(
`Variable '${id.name}' already initialised with a different value`
);
}
this.variables.set(id.name, { id, init });
}
@ -268,7 +266,7 @@ export default class Block {
: this.chunks.hydrate
);
properties.create = x`function create() {
properties.create = x`function #create() {
${this.chunks.create}
${hydrate}
}`;
@ -278,7 +276,7 @@ export default class Block {
if (this.chunks.claim.length === 0 && this.chunks.hydrate.length === 0) {
properties.claim = noop;
} else {
properties.claim = x`function claim(#nodes) {
properties.claim = x`function #claim(#nodes) {
${this.chunks.claim}
${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) {
properties.hydrate = x`function hydrate() {
properties.hydrate = x`function #hydrate() {
${this.chunks.hydrate}
}`;
}
@ -294,7 +292,7 @@ export default class Block {
if (this.chunks.mount.length === 0) {
properties.mount = noop;
} else {
properties.mount = x`function mount(#target, anchor) {
properties.mount = x`function #mount(#target, anchor) {
${this.chunks.mount}
}`;
}
@ -304,7 +302,7 @@ export default class Block {
properties.update = noop;
} else {
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.chunks.update}
}`;
@ -312,15 +310,15 @@ export default class Block {
}
if (this.has_animation) {
properties.measure = x`function measure() {
properties.measure = x`function #measure() {
${this.chunks.measure}
}`;
properties.fix = x`function fix() {
properties.fix = x`function #fix() {
${this.chunks.fix}
}`;
properties.animate = x`function animate() {
properties.animate = x`function #animate() {
${this.chunks.animate}
}`;
}
@ -329,7 +327,7 @@ export default class Block {
if (this.chunks.intro.length === 0) {
properties.intro = noop;
} else {
properties.intro = x`function intro(#local) {
properties.intro = x`function #intro(#local) {
${this.has_outros && b`if (#current) return;`}
${this.chunks.intro}
}`;
@ -338,7 +336,7 @@ export default class Block {
if (this.chunks.outro.length === 0) {
properties.outro = noop;
} else {
properties.outro = x`function outro(#local) {
properties.outro = x`function #outro(#local) {
${this.chunks.outro}
}`;
}
@ -347,7 +345,7 @@ export default class Block {
if (this.chunks.destroy.length === 0) {
properties.destroy = noop;
} else {
properties.destroy = x`function destroy(detaching) {
properties.destroy = x`function #destroy(detaching) {
${this.chunks.destroy}
}`;
}
@ -376,6 +374,8 @@ export default class Block {
d: ${properties.destroy}
}`;
const block = dev && this.get_unique_name('block');
const body = b`
${Array.from(this.variables.values()).map(({ id, init }) => {
return init
@ -387,9 +387,15 @@ export default class Block {
${dev
? b`
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 });
return block;`
const ${block} = ${return_value};
@dispatch_dev("SvelteRegisterBlock", {
block: ${block},
id: ${this.name || 'create_fragment'}.name,
type: "${this.type}",
source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}",
ctx: #ctx
});
return ${block};`
: b`
return ${return_value};`
}
@ -398,21 +404,36 @@ export default class Block {
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() {
const key = this.key && this.get_unique_name('key');
const args: any[] = [x`#ctx`];
if (key) args.unshift(key);
// TODO include this.comment
// ${this.comment && `// ${escape(this.comment, { only_escape_at_symbol: true })}`}
const fn = b`function ${this.name}(${args}) {
${this.get_contents(key)}
}`;
return b`
function ${this.name}(${args}) {
${this.get_contents(key)}
}
`;
return this.comment
? b`
// ${this.comment}
${fn}`
: fn;
}
render_listeners(chunk: string = '') {

@ -241,11 +241,16 @@ export default function dom(
args.push(x`$$props`, x`$$invalidate`);
}
body.push(b`
function create_fragment(#ctx) {
${block.get_contents()}
}
const has_create_fragment = block.has_content();
if (has_create_fragment) {
body.push(b`
function create_fragment(#ctx) {
${block.get_contents()}
}
`);
}
body.push(b`
${component.extract_javascript(component.ast.module)}
${component.fully_hoisted}
@ -364,7 +369,7 @@ export default function dom(
unknown_props_check = b`
const writable_props = [${writable_props.map(prop => x`'${prop.export_name}'`)}];
@_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>\`;`}
@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}
@ -489,7 +494,7 @@ export default function dom(
constructor(options) {
super(${options.dev && `options`});
${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 });`}
${dev_props_check}

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

@ -7,7 +7,6 @@ import { b, x } from 'code-red';
import Expression from '../../../nodes/shared/Expression';
import Text from '../../../nodes/Text';
import { changed } from '../shared/changed';
import { Literal } from 'estree';
export default class AttributeWrapper {
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 dependencies = this.node.get_dependencies();
if (dependencies.length > 0) {
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 value = this.get_value(block);
const is_select_value_attribute =
name === 'value' && element.node.name === 'select';
const is_src = this.node.name === 'src'; // TODO retire this exception in favour of https://github.com/sveltejs/svelte/issues/3750
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(
`${element.var.name}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value`
);
const last = should_cache && block.get_unique_name(
`${element.var.name}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value`
);
if (should_cache) block.add_variable(last);
let updater;
const init = should_cache ? x`${last} = ${value}` : value;
if (is_legacy_input_type) {
block.chunks.hydrate.push(
b`@set_input_type(${element.var}, ${init});`
);
updater = b`@set_input_type(${element.var}, ${should_cache ? last : value});`;
} else if (is_select_value_attribute) {
// annoying special case
const is_multiple_select = element.node.get_static_attribute_value('multiple');
const i = block.get_unique_name('i');
const option = block.get_unique_name('option');
const if_statement = is_multiple_select
? b`
${option}.selected = ~${last}.indexOf(${option}.__value);`
: b`
if (${option}.__value === ${last}) {
${option}.selected = true;
${{ type: 'BreakStatement' }};
}`; // TODO the BreakStatement is gross, but it's unsyntactic otherwise...
updater = b`
for (var ${i} = 0; ${i} < ${element.var}.options.length; ${i} += 1) {
var ${option} = ${element.var}.options[${i}];
${if_statement}
}
`;
block.chunks.mount.push(b`
${last} = ${value};
${updater}
`);
} else if (property_name) {
block.chunks.hydrate.push(
b`${element.var}.${property_name} = ${init};`
);
updater = block.renderer.options.dev
? b`@prop_dev(${element.var}, "${property_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 (should_cache) block.add_variable(last);
let updater;
const init = should_cache ? x`${last} = ${value}` : value;
if (is_legacy_input_type) {
block.chunks.hydrate.push(
b`@set_input_type(${element.var}, ${init});`
);
updater = b`@set_input_type(${element.var}, ${should_cache ? last : value});`;
} else if (is_select_value_attribute) {
// annoying special case
const is_multiple_select = element.node.get_static_attribute_value('multiple');
const i = block.get_unique_name('i');
const option = block.get_unique_name('option');
const if_statement = is_multiple_select
? b`
${option}.selected = ~${last}.indexOf(${option}.__value);`
: b`
if (${option}.__value === ${last}) {
${option}.selected = true;
${{ type: 'BreakStatement' }};
}`; // TODO the BreakStatement is gross, but it's unsyntactic otherwise...
updater = b`
for (var ${i} = 0; ${i} < ${element.var}.options.length; ${i} += 1) {
var ${option} = ${element.var}.options[${i}];
${if_statement}
}
`;
block.chunks.mount.push(b`
${last} = ${value};
${updater}
`);
} else if (is_src) {
block.chunks.hydrate.push(
b`if (${element.var}.src !== ${init}) ${method}(${element.var}, "${name}", ${last});`
);
updater = b`${method}(${element.var}, "${name}", ${should_cache ? last : value});`;
} else if (property_name) {
block.chunks.hydrate.push(
b`${element.var}.${property_name} = ${init};`
);
updater = block.renderer.options.dev
? b`@prop_dev(${element.var}, "${property_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);
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) {
@ -165,23 +156,11 @@ export default class AttributeWrapper {
if (${condition}) {
${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
if (this.node.is_true && name === 'autofocus') {
block.autofocus = element.var;
}
// special case autofocus. has to be handled in a bit of a weird way
if (this.node.is_true && name === 'autofocus') {
block.autofocus = element.var;
}
if (is_indirectly_bound_value) {
@ -199,6 +178,36 @@ export default class AttributeWrapper {
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() {
const scoped_css = this.node.chunks.some((chunk: Text) => chunk.synthetic);
const rendered = this.render_chunks();
@ -292,3 +301,32 @@ Object.keys(attribute_lookup).forEach(name => {
const metadata = attribute_lookup[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 { is_head } from '../shared/is_head';
import { Identifier } from 'estree';
import EventHandler from './EventHandler';
const events = [
{
@ -113,12 +114,14 @@ export default class ElementWrapper extends Wrapper {
fragment: FragmentWrapper;
attributes: AttributeWrapper[];
bindings: Binding[];
event_handlers: EventHandler[];
class_dependencies: string[];
slot_block: Block;
select_binding_dependencies?: Set<string>;
var: any;
void: boolean;
constructor(
renderer: Renderer,
@ -134,6 +137,8 @@ export default class ElementWrapper extends Wrapper {
name: node.name.replace(/[^a-zA-Z0-9_$]/g, '_')
};
this.void = is_void(node.name);
this.class_dependencies = [];
this.attributes = this.node.attributes.map(attribute => {
@ -194,6 +199,8 @@ export default class ElementWrapper extends Wrapper {
// e.g. <audio bind:paused bind:currentTime>
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) block.add_intro(node.intro.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 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);
const render_statement = this.get_render_statement();
@ -265,8 +273,13 @@ export default class ElementWrapper extends Wrapper {
if (parent_nodes) {
block.chunks.claim.push(b`
${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 {
block.chunks.claim.push(
b`${node} = ${render_statement};`
@ -291,7 +304,8 @@ export default class ElementWrapper extends Wrapper {
}
// 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') {
block.chunks.create.push(
// @ts-ignore todo: should it be this.fragment.nodes[0].node.data instead?
@ -318,7 +332,8 @@ export default class ElementWrapper extends Wrapper {
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);
block.chunks.create.push(
@ -345,18 +360,18 @@ export default class ElementWrapper extends Wrapper {
block.maintain_context = true;
}
this.add_attributes(block);
this.add_bindings(block);
this.add_event_handlers(block);
this.add_attributes(block);
this.add_transitions(block);
this.add_animation(block);
this.add_actions(block);
this.add_classes(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(
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`{ ${
(metadata && metadata.property_name) ||
fix_attribute_casing(attr.node.name)
}: ${attr.node.get_value(block)} }`;
}: ${attr.get_value(block)} }`;
initial_props.push(snippet);
updates.push(condition ? x`${condition} && ${snippet}` : snippet);
@ -628,10 +643,10 @@ export default class ElementWrapper extends Wrapper {
});
block.chunks.init.push(b`
var ${levels} = [${initial_props}];
let ${levels} = [${initial_props}];
var ${data} = {};
for (var #i = 0; #i < ${levels}.length; #i += 1) {
let ${data} = {};
for (let #i = 0; #i < ${levels}.length; #i += 1) {
${data} = @assign(${data}, ${levels}[#i]);
}
`);
@ -650,7 +665,7 @@ export default class ElementWrapper extends Wrapper {
}
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(
@ -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 => {
if (wrapper.node.type === 'Text') {
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 && (
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))
@ -901,7 +917,7 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | TagWrapper>, blo
attr.node.chunks.forEach(chunk => {
if (chunk.type === 'Text') {
state.quasi.value.raw += chunk.data;
state.quasi.value.raw += escape_html(chunk.data);
} else {
literal.quasis.push(state.quasi);
literal.expressions.push(chunk.manipulate(block));
@ -918,11 +934,11 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | TagWrapper>, blo
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);
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 { changed } from '../shared/changed';
import { Node, Identifier, ObjectExpression } from 'estree';
import EventHandler from '../Element/EventHandler';
export default class InlineComponentWrapper extends Wrapper {
var: Identifier;
@ -299,7 +300,9 @@ export default class InlineComponentWrapper extends Wrapper {
updates.push(b`
if (!${updating} && ${changed(Array.from(binding.expression.dependencies))}) {
${updating} = true;
${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`
function ${id}(${value}) {
#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`
function ${id}(${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 => {
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})`;
return b`${name}.$on("${handler.name}", ${snippet});`;
@ -396,12 +396,12 @@ export default class InlineComponentWrapper extends Wrapper {
`);
block.chunks.create.push(
b`if (${name}) ${name}.$$.fragment.c();`
b`if (${name}) @create_component(${name}.$$.fragment);`
);
if (parent_nodes && this.renderer.options.hydratable) {
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_handlers}
${name}.$$.fragment.c();
@create_component(${name}.$$.fragment);
@transition_in(${name}.$$.fragment, 1);
@mount_component(${name}, ${update_mount_node}, ${anchor});
} else {
@ -472,11 +472,11 @@ export default class InlineComponentWrapper extends Wrapper {
${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) {
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 { Identifier } from 'estree';
import { TemplateNode } from '../../../interfaces';
import EventHandler from './Element/EventHandler';
const associated_events = {
innerWidth: 'resize',
@ -34,9 +35,11 @@ const readonly = new Set([
export default class WindowWrapper extends Wrapper {
node: Window;
handlers: EventHandler[];
constructor(renderer: Renderer, block: Block, parent: Wrapper, node: TemplateNode) {
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) {
@ -47,7 +50,7 @@ export default class WindowWrapper extends Wrapper {
const bindings: Record<string, string> = {};
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 => {
// 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`
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`
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 { is_string } from './is_string';
import { Node } from 'estree';
import { changed } from './changed';
export default class Tag extends Wrapper {
node: MustacheTag | RawMustacheTag;
@ -46,10 +47,7 @@ export default class Tag extends Wrapper {
if (this.node.should_cache) block.add_variable(value, snippet);
if (dependencies.length > 0) {
let condition = x`#changed.${dependencies[0]}`;
for (let i = 1; i < dependencies.length; i += 1) {
condition = x`${condition} || #changed.${dependencies[i]}`;
}
let condition = changed(dependencies);
if (block.has_outros) {
condition = x`!#current || ${condition}`;

@ -1,39 +1,10 @@
import Block from '../../Block';
import EventHandler from '../../../nodes/EventHandler';
import { x, p } from 'code-red';
const TRUE = x`true`;
const FALSE = x`false`;
import EventHandler from '../Element/EventHandler';
export default function add_event_handlers(
block: Block,
target: string,
handlers: EventHandler[]
) {
handlers.forEach(handler => {
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})`
);
});
handlers.forEach(handler => handler.render(block, target));
}

@ -1,7 +1,7 @@
export default function get_name_from_filename(filename: string) {
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) {
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()
.replace(/%/g, 'u')
.replace(/\.[^.]+$/, "")
.replace(/[^a-zA-Z_$0-9]+/g, '_')
.replace(/^_/, '')

@ -1,156 +1,8 @@
import { walk } from 'estree-walker';
import is_reference from 'is-reference';
import { Node, VariableDeclaration, ClassDeclaration, VariableDeclarator, ObjectPattern, Property, RestElement, ArrayPattern, Identifier } from 'estree';
import get_object from './get_object';
import { Node } from 'estree';
import { analyze, Scope, extract_names, extract_identifiers } from 'periscopic';
// TODO replace this with periscopic?
export function create_scopes(expression: Node) {
const map = new WeakMap();
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 };
return analyze(expression);
}
export class Scope {
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);
}
};
export { Scope, extract_names, extract_identifiers };

@ -12,13 +12,15 @@ export function escape(data: string, { only_escape_at_symbol = false } = {}) {
}
const escaped = {
'"': '&quot;',
"'": '&#39;',
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
};
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) {

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

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

@ -1,19 +1,3 @@
declare module '*.svelte' {
type Props = Record<string, any>;
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;
}
export { SvelteComponentDev as default } from 'svelte/internal';
}

@ -8,5 +8,6 @@ export {
setContext,
getContext,
tick,
createEventDispatcher
createEventDispatcher,
SvelteComponentDev as SvelteComponent
} 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 { 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
interface T$$ {
dirty: null;
@ -13,7 +28,7 @@ interface T$$ {
callbacks: any;
after_update: any[];
props: Record<string, 0 | string>;
fragment: null|any;
fragment: null|false|Fragment;
not_equal: any;
before_update: 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) {
const { fragment, on_mount, on_destroy, after_update } = component.$$;
fragment.m(target, anchor);
fragment && fragment.m(target, anchor);
// onMount happens before the initial afterUpdate
add_render_callback(() => {
@ -51,15 +74,16 @@ export function mount_component(component, target, anchor) {
}
export function destroy_component(component, detaching) {
if (component.$$.fragment) {
run_all(component.$$.on_destroy);
const $$ = component.$$;
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
// preserve final state?)
component.$$.on_destroy = component.$$.fragment = null;
component.$$.ctx = {};
$$.on_destroy = $$.fragment = null;
$$.ctx = {};
}
}
@ -115,15 +139,17 @@ export function init(component, options, instance, create_fragment, not_equal, p
$$.update();
ready = true;
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.hydrate) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment!.l(children(options.target));
$$.fragment && $$.fragment!.l(children(options.target));
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment!.c();
$$.fragment && $$.fragment!.c();
}
if (options.intro) transition_in(component.$$.fragment);

@ -79,8 +79,24 @@ export function set_data_dev(text, 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 {
constructor(options) {
constructor(options: {
target: Element;
anchor?: Element;
props?: Props;
hydrate?: boolean;
intro?: boolean;
$$inline?: boolean;
}) {
if (!options || (!options.target && !options.$$inline)) {
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() {
const component = get_current_component();
return (type, detail) => {
return (type: string, detail?: any) => {
const callbacks = component.$$.callbacks[type];
if (callbacks) {

@ -70,10 +70,10 @@ export function flush() {
}
function update($$) {
if ($$.fragment) {
if ($$.fragment !== null) {
$$.update($$.dirty);
run_all($$.before_update);
$$.fragment.p($$.dirty, $$.ctx);
$$.fragment && $$.fragment.p($$.dirty, $$.ctx);
$$.dirty = null;
$$.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';
type EasingFunction = (t: number) => number;
@ -43,17 +43,20 @@ export function blur(node: Element, {
interface FadeParams {
delay: number;
duration: number;
easing: EasingFunction;
}
export function fade(node: Element, {
delay = 0,
duration = 400
duration = 400,
easing = linear
}: FadeParams): TransitionConfig {
const o = +getComputedStyle(node).opacity;
return {
delay,
duration,
easing,
css: t => `opacity: ${t * o}`
};
}

@ -1,6 +1,6 @@
import * as assert from 'assert';
import * as fs from 'fs';
import { env, normalizeHtml, svelte } from '../helpers.js';
import { env, svelte, setupHtmlEqual, shouldUpdateExpected } from '../helpers.js';
function try_require(file) {
try {
@ -37,6 +37,10 @@ function create(code) {
}
describe('css', () => {
before(() => {
setupHtmlEqual();
});
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
if (dir[0] === '.') return;
@ -80,7 +84,17 @@ describe('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 ServerComponent;
@ -114,10 +128,8 @@ describe('css', () => {
fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, html);
assert.equal(
normalizeHtml(window, html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz')),
normalizeHtml(window, expected.html)
);
const actual_html = html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz');
assert.htmlEqual(actual_html, expected.html);
window.document.head.innerHTML = ''; // remove added styles
} catch (err) {
@ -127,13 +139,8 @@ describe('css', () => {
// ssr
try {
assert.equal(
normalizeHtml(
window,
ServerComponent.render(config.props).html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz')
),
normalizeHtml(window, expected.html)
);
const actual_ssr = ServerComponent.render(config.props).html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz');
assert.htmlEqual(actual_ssr, expected.html);
} catch (err) {
console.log(ssr.js.code);
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.getComputedStyle = window.getComputedStyle;
global.requestAnimationFrame = null; // placeholder, filled in using set_raf
global.window = window;
// add missing ecmascript globals to window
for (const key of Object.getOwnPropertyNames(global)) {
window[key] = window[key] || global[key];
}
// implement mock scroll
window.scrollTo = function(pageXOffset, pageYOffset) {
window.pageXOffset = pageXOffset;
window.pageYOffset = pageYOffset;
};
export function env() {
window.document.title = '';
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) {
let result = '';
while (i--) 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,
loadSvelte,
env,
setupHtmlEqual
setupHtmlEqual,
shouldUpdateExpected
} from '../helpers.js';
let compileOptions = null;
@ -76,7 +77,16 @@ describe('hydration', () => {
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) {
config.test(assert, target, snapshot, component, window);

@ -1,7 +1,8 @@
import * as assert from "assert";
import * as fs from "fs";
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", () => {
fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
@ -14,8 +15,14 @@ describe("js", () => {
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, () => {
dir = path.resolve(`${__dirname}/samples`, dir);
const config = loadConfig(`${dir}/_config.js`);
const input = fs.readFileSync(`${dir}/input.svelte`, "utf-8").replace(/\s+$/, "");
@ -34,16 +41,32 @@ describe("js", () => {
const output = `${dir}/_actual.js`;
fs.writeFileSync(output, actual);
const expected = fs.readFileSync(`${dir}/expected.js`, "utf-8");
const expectedPath = `${dir}/expected.js`;
if (process.env.UPDATE_EXPECTED) {
fs.writeFileSync(`${dir}/expected.js`, actual);
let expected = '';
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(
actual.trim().replace(/^[ \t]+$/gm, ""),
expected.trim().replace(/^[ \t]+$/gm, "")
);
try {
assert.equal(
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 {
SvelteComponent,
detach,

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

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

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

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
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 {
SvelteComponent,
append,

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

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

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

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

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

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

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

@ -1,25 +1,14 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
component_subscribe,
init,
noop,
safe_not_equal,
set_store_value
} from "svelte/internal";
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) {
let $count;
component_subscribe($$self, count, $$value => $$invalidate("$count", $count = $$value));
@ -34,7 +23,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, { increment: 0 });
init(this, options, instance, null, safe_not_equal, { increment: 0 });
}
get increment() {

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

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

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

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

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

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponentDev,
add_location,
@ -67,7 +68,7 @@ function instance($$self, $$props, $$invalidate) {
const writable_props = ["name"];
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 => {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponentDev,
add_location,
@ -23,6 +24,7 @@ function get_each_context(ctx, list, i) {
return child_ctx;
}
// (8:0) {#each things as thing}
function create_each_block(ctx) {
let span;
let t0_fn = ctx => ctx.thing.name + "";
@ -166,7 +168,7 @@ function instance($$self, $$props, $$invalidate) {
const writable_props = ["things", "foo", "bar", "baz"];
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 => {

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponentDev,
add_location,
@ -23,6 +24,7 @@ function get_each_context(ctx, list, i) {
return child_ctx;
}
// (6:0) {#each things as thing}
function create_each_block(ctx) {
let span;
let t0_fn = ctx => ctx.thing.name + "";
@ -164,7 +166,7 @@ function instance($$self, $$props, $$invalidate) {
const writable_props = ["things", "foo"];
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 => {

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

@ -1,3 +1,4 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponentDev,
destroy_each,
@ -21,6 +22,7 @@ function get_each_context(ctx, list, i) {
return child_ctx;
}
// (4:0) {#each things as thing, index}
function create_each_block(ctx) {
let t0;
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";
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {

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

@ -1,16 +1,7 @@
import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal";
import { onMount } from "svelte";
/* 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
};
}
import { onMount } from "svelte";
function instance($$self, $$props, $$invalidate) {
let { foo = "bar" } = $$props;
@ -29,7 +20,7 @@ function instance($$self, $$props, $$invalidate) {
class Component extends SvelteComponent {
constructor(options) {
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 {
SvelteComponentDev,
add_location,
@ -66,7 +67,7 @@ function instance($$self, $$props, $$invalidate) {
const writable_props = ["foo"];
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 => {

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

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

Loading…
Cancel
Save