merge master -> database

pull/2572/head
Richard Harris 6 years ago
commit 2e6f9c4214

@ -1,5 +1,22 @@
# Svelte changelog # Svelte changelog
## 3.2.0
* Improve `spring` animations, and add `hard`/`soft` options ([#2627](https://github.com/sveltejs/svelte/pull/2627))
* Expose `parse` and `walk` functions ([#2661](https://github.com/sveltejs/svelte/issues/2661), [#2534](https://github.com/sveltejs/svelte/pull/2534))
* Support array/object rest in `each` block destructuring patterns ([#2647](https://github.com/sveltejs/svelte/issues/2647), [#2658](https://github.com/sveltejs/svelte/pull/2658))
* Use `setAttribute` to change `form` property on form elements ([#1742](https://github.com/sveltejs/svelte/issues/1742))
* Fix a11y warning when `<figcaption>` is non-direct descendant of `<figure>` ([#2582](https://github.com/sveltejs/svelte/issues/2582))
* Squelch erroneous 'empty block' warnings ([#1716](https://github.com/sveltejs/svelte/issues/1716))
* Fix IE9/10 error with `insertBefore` ([#2573](https://github.com/sveltejs/svelte/issues/2573))
* Prevent `$$scope` from being spread onto an element ([#2520](https://github.com/sveltejs/svelte/issues/2520))
* Resubscribe to stores that are assigned to in `<script>` ([#2435](https://github.com/sveltejs/svelte/issues/2435))
* Allow reactive declarations to depend on `const` variables ([#2285](https://github.com/sveltejs/svelte/issues/2285))
* Trigger store changes on UpdateExpression ([#2625](https://github.com/sveltejs/svelte/issues/2625))
* Squelch missing prop warning if variable is initialised ([#2635](https://github.com/sveltejs/svelte/issues/2635))
* Add `alert`, `confirm` and `prompt` to known globals ([#2648](https://github.com/sveltejs/svelte/issues/2648))
## 3.1.0 ## 3.1.0
* Allow store subscribe functions to return an object with an `unsubscribe` method, providing native RxJS support ([#2549](https://github.com/sveltejs/svelte/issues/2549)) * Allow store subscribe functions to return an object with an `unsubscribe` method, providing native RxJS support ([#2549](https://github.com/sveltejs/svelte/issues/2549))

@ -54,7 +54,7 @@ npm run test -- -g transition
## svelte.dev ## svelte.dev
The source code for https://svelte.dev, including all the documentation, lives in the [site](site) directory. The site is built with [Sapper](https://sapper.svelte.technology). To develop locally: The source code for https://svelte.dev, including all the documentation, lives in the [site](site) directory. The site is built with [Sapper](https://sapper.svelte.dev). To develop locally:
```bash ```bash
cd site cd site

2
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.0.0-beta.25", "version": "3.1.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.1.0", "version": "3.2.0",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"module": "index.mjs", "module": "index.mjs",
"main": "index", "main": "index",
@ -92,5 +92,6 @@
], ],
"sourceMap": true, "sourceMap": true,
"instrument": true "instrument": true
} },
"dependencies": {}
} }

@ -92,6 +92,6 @@ We don't take this lightly: hopefully once you've experienced Svelte 3 you'll un
## Still to come ## Still to come
As grueling as this release has been, we're nowhere near finished. We have a ton of ideas for generating smarter, more compact code, and a long feature wish-list. [Sapper](https://sapper.svelte.technology), our Next.js-style app framework, is still in the middle of being updated to use Svelte 3. The [Svelte Native](https://svelte-native.technology/) community project, which allows you to write Android and iOS apps in Svelte, is making solid progress but deserves more complete support from core. We don't yet have the bounty of editor extensions, syntax highlighters, component kits, devtools and so on that other frameworks have, and we should fix that. We *really* want to add first-class TypeScript support. As grueling as this release has been, we're nowhere near finished. We have a ton of ideas for generating smarter, more compact code, and a long feature wish-list. [Sapper](https://sapper.svelte.dev), our Next.js-style app framework, is still in the middle of being updated to use Svelte 3. The [Svelte Native](https://svelte-native.technology/) community project, which allows you to write Android and iOS apps in Svelte, is making solid progress but deserves more complete support from core. We don't yet have the bounty of editor extensions, syntax highlighters, component kits, devtools and so on that other frameworks have, and we should fix that. We *really* want to add first-class TypeScript support.
But in the meantime we think Svelte 3 is the best way to build web apps yet. Take an hour to go through the [tutorial](tutorial) and we hope to convince you of the same. Either way, we'd love to see you in our [Discord chatroom](https://discord.gg/yy75DKs) and on [GitHub](https://github.com/sveltejs/svelte) — everyone is welcome, especially you. But in the meantime we think Svelte 3 is the best way to build web apps yet. Take an hour to go through the [tutorial](tutorial) and we hope to convince you of the same. Either way, we'd love to see you in our [Discord chatroom](https://discord.gg/yy75DKs) and on [GitHub](https://github.com/sveltejs/svelte) — everyone is welcome, especially you.

@ -36,6 +36,9 @@ Svelte uses the `export` keyword to mark a variable declaration as a *property*
export let foo; export let foo;
export let bar = 'optional default value'; export let bar = 'optional default value';
// this property is readonly externally
export const buzz = 'buzz';
// Values that are passed in as props // Values that are passed in as props
// are immediately available // are immediately available
console.log(foo, bar); console.log(foo, bar);
@ -56,6 +59,8 @@ To change component state and trigger a re-render, just assign to a locally decl
Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect. Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect.
Because Svelte's reactivity is based on assignments, using array methods like `.push()` and `.splice()` won't automatically trigger updates. Options for getting around this can be found in the [tutorial](tutorial/updating-arrays-and-objects).
```html ```html
<script> <script>
let count = 0; let count = 0;

@ -755,6 +755,8 @@ The `in:` and `out:` directives are not bidirectional. An in transition will con
{/if} {/if}
``` ```
> By default intro transitions will not play on first render. You can modify this behaviour by setting `intro: true` when you [create a component](docs#Client-side_component_API).
#### Transition parameters #### Transition parameters
--- ---

@ -486,6 +486,8 @@ A `spring` store gradually changes to its target value based on its `stiffness`
As with [`tweened`](#tweened) stores, `set` and `update` return a Promise that resolves if the spring settles. The `store.stiffness` and `store.damping` properties can be changed while the spring is in motion, and will take immediate effect. As with [`tweened`](#tweened) stores, `set` and `update` return a Promise that resolves if the spring settles. The `store.stiffness` and `store.damping` properties can be changed while the spring is in motion, and will take immediate effect.
Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`.
[See a full example on the spring tutorial.](tutorial/spring) [See a full example on the spring tutorial.](tutorial/spring)
```html ```html
@ -556,9 +558,31 @@ You can see a full example on the [animations tutorial](tutorial/animate)
* TODO could have nice little interactive widgets showing the different functions, maybe * TODO could have nice little interactive widgets showing the different functions, maybe
### `svelte/register` ### `svelte/register`
TODO To render Svelte components in Node.js without bundling, use `require('svelte/register')`. After that, you can use `require` to include any `.svelte` file.
```js
require('svelte/register');
const App = require('./App.svelte').default;
...
const { html, css, head } = App.render({ answer: 42 });
```
> The `.default` is necessary because we're converting from native JavaScript modules to the CommonJS modules recognised by Node. Note that if your component imports JavaScript modules, they will fail to load in Node and you will need to use a bundler instead.
To set compile options, or to use a custom file extension, call the `register` hook as a function:
```js
require('svelte/register')({
extensions: ['.customextension'], // defaults to ['.html', '.svelte']
preserveComments: true
});
```
### Client-side component API ### Client-side component API
@ -569,8 +593,6 @@ TODO
const component = new Component(options) const component = new Component(options)
``` ```
---
A client-side component — that is, a component compiled with `generate: 'dom'` (or the `generate` option left unspecified) is a JavaScript class. A client-side component — that is, a component compiled with `generate: 'dom'` (or the `generate` option left unspecified) is a JavaScript class.
```js ```js

@ -153,6 +153,30 @@ compiled: {
--> -->
### `svelte.parse`
```js
ast: object = svelte.parse(
source: string,
options?: {
filename?: string,
customElement?: boolean
}
)
```
---
The `parse` function parses a component, returning only its abstract syntax tree. Unlike compiling with the `generate: false` option, this will not perform any validation or other analysis of the component beyond parsing it.
```js
const svelte = require('svelte/compiler');
const ast = svelte.parse(source, { filename: 'App.svelte' });
```
### `svelte.preprocess` ### `svelte.preprocess`
```js ```js
@ -280,6 +304,38 @@ const { code } = svelte.preprocess(source, [
``` ```
### `svelte.walk`
```js
walk(ast: Node, {
enter(node: Node, parent: Node)?: void,
leave(node: Node, parent: Node)?: void
})
```
---
The `walk` function provides a way to walk to abstract syntax trees generated by the parser, using the compiler's own built-in instance of [estree-walker](https://github.com/Rich-Harris/estree-walker).
The walker takes an abstract syntax tree to walk and an object with two optional methods: `enter` and `leave`. For each node, `enter` is called (if present). Then, unless `this.skip()` is called during `enter`, each of the children are traversed, and then `leave` is called on the node.
```js
const svelte = require('svelte/compiler');
svelte.walk(ast, {
enter(node, parent) {
do_something(node);
if (should_skip_children(node)) {
this.skip();
}
},
leave(node, parent) {
do_something_else(node);
}
});
```
### `svelte.VERSION` ### `svelte.VERSION`
--- ---

@ -29,4 +29,4 @@ Each tutorial chapter will have a 'Show me' button that you can click if you get
## Understanding components ## Understanding components
In Svelte, an application is composed from one or more *components*. A component is a reusable self-contained block of code that encapsulates HTML, CSS and JavaScript that belong together, written into a `.svelte` file. The 'hello world' example on the right is a simple component. In Svelte, an application is composed from one or more *components*. A component is a reusable self-contained block of code that encapsulates HTML, CSS and JavaScript that belong together, written into a `.svelte` file. The 'hello world' example in the code editor is a simple component.

@ -12,4 +12,4 @@ In Svelte, you do this with the special `{@html ...}` tag:
<p>{@html string}</p> <p>{@html string}</p>
``` ```
> Svelte doesn't perform any sanitization of the data before it gets inserted into the DOM. In other words, it's critical that you manually escape HTML that comes from sources you don't trust, otherwise you risk exposing your users to XSS attacks. > Svelte doesn't perform any sanitization of the expression inside `{@html ...}` before it gets inserted into the DOM. In other words, if you use this feature it's critical that you manually escape HTML that comes from sources you don't trust, otherwise you risk exposing your users to XSS attacks.

@ -39,6 +39,8 @@ It starts to get a bit boilerplatey though, especially if your component subscri
<h1>The count is {$count}</h1> <h1>The count is {$count}</h1>
``` ```
> Auto-subscription only works with store variables that are declared (or imported) at the top-level scope of a component.
You're not limited to using `$count` inside the markup, either — you can use it anywhere in the `<script>` as well, such as in event handlers or reactive declarations. You're not limited to using `$count` inside the markup, either — you can use it anywhere in the `<script>` as well, such as in event handlers or reactive declarations.
> Any name beginning with `$` is assumed to refer to a store value. It's effectively a reserved character — Svelte will prevent you from declaring your own variables with a `$` prefix. > Any name beginning with `$` is assumed to refer to a store value. It's effectively a reserved character — Svelte will prevent you from declaring your own variables with a `$` prefix.

@ -6,6 +6,6 @@ You've now finished the Svelte tutorial and are ready to start building apps. Yo
To get set up in your local development environment, check out [the quickstart guide](blog/the-easiest-way-to-get-started). To get set up in your local development environment, check out [the quickstart guide](blog/the-easiest-way-to-get-started).
If you're looking for a more expansive framework that includes routing, server-side rendering and everything else, take a look at [Sapper](https://sapper.svelte.technology). If you're looking for a more expansive framework that includes routing, server-side rendering and everything else, take a look at [Sapper](https://sapper.svelte.dev).
Most importantly: since you're now a member of the Svelte community, you should [join our friendly Discord chatroom](https://discord.gg/yy75DKs). That's where you'll find fellow Svelte users, and it's where we plan the future of the framework. Most importantly: since you're now a member of the Svelte community, you should [join our friendly Discord chatroom](https://discord.gg/yy75DKs). That's where you'll find fellow Svelte users, and it's where we plan the future of the framework.

1066
site/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -5,7 +5,7 @@
"scripts": { "scripts": {
"dev": "sapper dev", "dev": "sapper dev",
"migrate": "node-pg-migrate -r dotenv/config", "migrate": "node-pg-migrate -r dotenv/config",
"sapper": "sapper build --legacy && npm run update_shimport", "sapper": "sapper build --legacy",
"update_shimport": "cp node_modules/shimport/index.js __sapper__/build/client/shimport@0.0.14.js", "update_shimport": "cp node_modules/shimport/index.js __sapper__/build/client/shimport@0.0.14.js",
"update": "node scripts/update_template.js && node scripts/get-contributors.js", "update": "node scripts/update_template.js && node scripts/get-contributors.js",
"start": "node __sapper__/build", "start": "node __sapper__/build",
@ -28,35 +28,35 @@
"sirv": "^0.4.0" "sirv": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.3.3", "@babel/core": "^7.4.4",
"@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.2.0", "@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.3.1", "@babel/preset-env": "^7.4.4",
"@babel/runtime": "^7.3.1", "@babel/runtime": "^7.4.4",
"@sindresorhus/slugify": "^0.9.1", "@sindresorhus/slugify": "^0.9.1",
"@sveltejs/site-kit": "^1.0.3",
"@sveltejs/svelte-repl": "0.0.10", "@sveltejs/svelte-repl": "0.0.10",
"chokidar": "^2.1.2", "chokidar": "^3.0.0",
"degit": "^2.1.3", "degit": "^2.1.3",
"dotenv": "^7.0.0", "dotenv": "^8.0.0",
"eslint-plugin-svelte3": "^0.4.4", "eslint-plugin-svelte3": "^1.0.0",
"esm": "^3.2.22", "esm": "^3.2.22",
"jimp": "^0.6.0", "jimp": "^0.6.0",
"mocha": "^6.1.3", "mocha": "^6.1.3",
"node-fetch": "^2.3.0", "node-fetch": "^2.3.0",
"node-pg-migrate": "^3.18.1", "node-pg-migrate": "^3.18.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"rollup": "^1.2.2", "rollup": "^1.11.2",
"rollup-plugin-babel": "^4.3.2", "rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.2.0", "rollup-plugin-commonjs": "^9.3.4",
"rollup-plugin-json": "^3.1.0", "rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^4.0.1", "rollup-plugin-node-resolve": "^4.2.3",
"rollup-plugin-replace": "^2.1.0", "rollup-plugin-replace": "^2.2.0",
"rollup-plugin-svelte": "^5.0.3", "rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-terser": "^4.0.4", "rollup-plugin-terser": "^4.0.4",
"sapper": "^0.26.0-alpha.12", "sapper": "^0.26.0",
"shelljs": "^0.8.3", "shelljs": "^0.8.3",
"shimport": "0.0.16", "svelte": "^3.0.0"
"svelte": "^3.0.0-beta.28"
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"

@ -5,15 +5,26 @@ const Jimp = require('jimp');
process.chdir(__dirname); process.chdir(__dirname);
const base = `https://api.github.com/repos/sveltejs/svelte/contributors`;
const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } = process.env;
const SIZE = 64; const SIZE = 64;
async function main() { async function main() {
const res = await fetch(`https://api.github.com/repos/sveltejs/svelte/stats/contributors?client_id=${process.env.GITHUB_CLIENT_ID}&client_secret=${process.env.GITHUB_CLIENT_SECRET}`); const contributors = [];
const contributors = await res.json(); let page = 1;
while (true) {
const res = await fetch(`${base}?client_id=${GITHUB_CLIENT_ID}&client_secret=${GITHUB_CLIENT_SECRET}&per_page=100&page=${page++}`);
const list = await res.json();
if (list.length === 0) break;
contributors.push(...list);
}
const authors = contributors const authors = contributors
.sort((a, b) => b.total - a.total) .sort((a, b) => b.contributions - a.contributions);
.map(({ author }) => author);
const sprite = new Jimp(SIZE * authors.length, SIZE); const sprite = new Jimp(SIZE * authors.length, SIZE);

@ -1,3 +1,4 @@
import '@sveltejs/site-kit/base.css';
import * as sapper from '@sapper/app'; import * as sapper from '@sapper/app';
sapper.start({ sapper.start({

@ -1,39 +0,0 @@
<!--
-----------------------------------------------
svg icon
- https://github.com/jacobmischka/svelte-feather-icon
- https://feathericons.com/
-----------------------------------------------
-->
<script>
export let clas = '';
export let name;
export let size = 20;
</script>
<!-- style="--color: {color ? color : 'currentColor'}" -->
<svg class="{'icon ' + clas}" width={size} height={size}>
<use xlink:href='#{name}' />
</svg>
<style>
.icon {
position: relative;
/* width: 1.2em;
height: 1.2em;
top: -0.123em; */
overflow: hidden;
vertical-align: middle;
-o-object-fit: contain;
object-fit: contain;
-webkit-transform-origin: center center;
transform-origin: center center;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}
</style>

Before

Width:  |  Height:  |  Size: 828 B

@ -1,115 +0,0 @@
<!--
-----------------------------------------------
inline-svg sprite
- common used svg-stuff (feather-icons)
- advantage of css-styling
- https://github.com/jacobmischka/svelte-feather-icon
- https://feathericons.com/
- if required we can split out app-controls to REPL only
-----------------------------------------------
-->
<svg style='display:none'>
<symbol id='arrow-left' class='icon' viewBox='0 0 24 24'>
<line x1='19' y1='12' x2='5' y2='12' />
<polyline points='12 19 5 12 12 5' />
</symbol>
<symbol id='arrow-right' class='icon' viewBox='0 0 24 24'>
<line x1='5' y1='12' x2='19' y2='12' />
<polyline points='12 5 19 12 12 19' />
</symbol>
<symbol id='arrow-up' class='icon' viewBox='0 0 24 24'>
<line x1='12' y1='19' x2='12' y2='5' />
<polyline points='5 12 12 5 19 12' />
</symbol>
<symbol id='arrow-down' class='icon' viewBox='0 0 24 24'>
<line x1='12' y1='5' x2='12' y2='19' />
<polyline points='19 12 12 19 5 12' />
</symbol>
<symbol id='check' class='icon' viewBox='0 0 24 24'>
<polyline points='20 6 9 17 4 12' />
</symbol>
<symbol id='close' class='icon' viewBox='0 0 24 24'>
<line x1='18' y1='6' x2='6' y2='18' />
<line x1='6' y1='6' x2='18' y2='18' />
</symbol>
<symbol id='download' class='icon' viewBox='0 0 24 24'>
<path d='M21 15V19A2 2 0 0 1 19 21H5A2 2 0 0 1 3 19V15' />
<polyline points='7 10 12 15 17 10' />
<line x1='12' y1='15' x2='12' y2='3' />
</symbol>
<symbol id='edit' class='icon' viewBox='0 0 24 24'>
<path d='M20 14.66V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5.34' />
<polygon points='18 2 22 6 12 16 8 16 8 12 18 2' />
</symbol>
<symbol id='github' class='icon' viewBox='0 0 24 24'>
<path d='M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22' />
</symbol>
<symbol id='git-branch' class='icon' viewBox='0 0 24 24'>
<line x1='6' y1='3' x2='6' y2='15' />
<circle cx='18' cy='6' r='3' />
<circle cx='6' cy='18' r='3' />
<path d='M18 9a9 9 0 0 1-9 9' />
</symbol>
<symbol id='log-in' class='icon' viewBox='0 0 24 24'>
<path d='M15 3H19A2 2 0 0 1 21 5V19A2 2 0 0 1 19 21H15' />
<polyline points='10 17 15 12 10 7' />
<line x1='15' y1='12' x2='3' y2='12' />
</symbol>
<symbol id='maximize' class='icon' viewBox='0 0 24 24'>
<path d='M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3' />
</symbol>
<symbol id='maximize-2' class='icon' viewBox='0 0 24 24'>
<polyline points='15 3 21 3 21 9' />
<polyline points='9 21 3 21 3 15' />
<line x1='21' y1='3' x2='14' y2='10' />
<line x1='3' y1='21' x2='10' y2='14' />
</symbol>
<symbol id='menu' class='icon' viewBox='0 0 24 24'>
<line x1='3' y1='12' x2='21' y2='12' />
<line x1='3' y1='6' x2='21' y2='6' />
<line x1='3' y1='18' x2='21' y2='18' />
</symbol>
<symbol id='message-square' class='icon' viewBox='0 0 24 24'>
<path d='M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z' />
</symbol>
<symbol id='minus' class='icon' viewBox='0 0 24 24'>
<line x1='5' y1='12' x2='19' y2='12' />
</symbol>
<symbol id='plus' class='icon' viewBox='0 0 24 24'>
<line x1='12' y1='5' x2='12' y2='19' />
<line x1='5' y1='12' x2='19' y2='12' />
</symbol>
<symbol id='save' class='icon' viewBox='0 0 24 24'>
<path d='M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z' />
<polyline points='17 21 17 13 7 13 7 21' />
<polyline points='7 3 7 8 15 8' />
</symbol>
<symbol id="link" class="icon" viewBox="0 0 24 24">
<path d="M9,7L6,7A2 2 0 0 0 6,17L9,17"/>
<path d="M15,7L18,7A2 2 0 0 1 18,17L15,17"/>
<path d="M7,12L17,12"/>
</symbol>
<symbol id="chevron" class="icon" viewBox="0 0 24 24">
<path d="M2,7 L12,17 L20,7"/>
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

@ -1,277 +0,0 @@
<script>
import { onMount } from 'svelte';
import Icon from './Icon.svelte';
import { page } from '@sapper/app';
export let segment;
let open = false;
let visible = true;
// hide nav whenever we navigate
page.subscribe(() => {
open = false;
});
// TODO remove this post-https://github.com/sveltejs/svelte/issues/1914
let ul;
onMount(() => {
function handler(event) {
if (!open) {
event.preventDefault();
event.stopPropagation();
open = true;
}
}
ul.addEventListener('touchstart', handler, {
capture: true
});
return () => {
ul.removeEventListener('touchstart', handler, {
capture: true
});
};
});
// Prevents navbar to show/hide when clicking in docs sidebar
let hash_changed = false;
function handle_hashchange() {
hash_changed = true;
}
let last_scroll = 0;
function handle_scroll() {
const scroll = window.pageYOffset;
if (!hash_changed) {
visible = (scroll < 50 || scroll < last_scroll);
}
last_scroll = scroll;
hash_changed = false;
}
</script>
<style>
header {
position: fixed;
display: flex;
align-items: center;
justify-content: space-between;
width: 100vw;
height: var(--nav-h);
padding: 0 var(--side-nav);
margin: 0 auto;
background-color: white;
box-shadow: 0 -0.4rem 0.9rem 0.2rem rgba(0,0,0,.5);
font-family: var(--font);
z-index: 100;
user-select: none;
transform: translate(0,calc(-100% - 1rem));
transition: transform 0.2s;
}
header.visible {
transform: none;
}
nav {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: var(--nav-h);
padding: 0 var(--side-nav) 0 var(--side-nav);
display: flex;
align-items: center;
justify-content: space-between;
background-color: transparent;
transform: none;
transition: none;
box-shadow: none;
}
.primary {
list-style: none;
font-family: var(--font);
margin: 0;
line-height: 1;
}
li {
display: block;
display: none;
}
li.active {
display: block;
}
ul {
position: relative;
padding: 0 3rem 0 0;
background: url(/icons/chevron.svg) calc(100% - 1em) 0.05em no-repeat;
background-size: 1em 1em;
}
ul::after {
/* prevent clicks from registering if nav is closed */
position: absolute;
content: '';
width: 100%;
height: 100%;
left: 0;
top: 0;
}
ul.open {
padding: 0 0 1em 0;
background: white;
border-left: 1px solid #eee;
border-right: 1px solid #eee;
border-bottom: 1px solid #eee;
border-radius: 0 0 var(--border-r) var(--border-r);
align-self: start;
}
ul.open li {
display: block;
text-align: right
}
ul.open::after {
display: none;
}
ul li a {
font-size: var(--h5);
padding: 0 .8rem;
}
ul.open li a {
padding: 1.5rem 3.7rem 1.5rem 4rem;
display: block;
}
ul.open li:first-child a {
padding-top: 2.3rem;
}
.primary :global(svg) {
width: 2rem;
height: 2rem;
}
.home {
position: relative;
top: -.1rem;
width: 18rem;
height: 4.2rem;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
background: url(/svelte-logo-horizontal.svg) 0 50% no-repeat;
background-size: auto 100%;
/* z-index: 11; */
}
.active {
color: var(--prime)
}
.modal-background {
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
background-color: rgba(255, 255, 255, 0.9);
}
a {
color: inherit;
border-bottom: none;
transition: none;
}
li:not(.active) a:hover {
color: var(--flash);
}
@media (min-width: 840px) {
ul {
padding: 0;
background: none;
}
ul.open {
padding: 0;
background: white;
border: none;
align-self: initial;
}
ul.open li {
display: inline;
text-align: left;
}
ul.open li a {
font-size: var(--h5);
padding: 0 .8rem;
display: inline;
}
ul::after {
display: none;
}
li {
display: inline !important;
}
.hide-if-desktop {
display: none !important;
}
}
</style>
<svelte:window on:hashchange={handle_hashchange} on:scroll={handle_scroll} />
<header class:visible="{visible || open}">
<nav>
<a rel="prefetch" href='.' class="home" title='Homepage'></a>
{#if open}
<div class="modal-background hide-if-desktop" on:click="{() => open = false}"></div>
{/if}
<ul
bind:this={ul}
class="primary"
class:open
on:mouseenter="{() => open = true}"
on:mouseleave="{() => open = false}"
>
<li class="hide-if-desktop" class:active="{!segment}"><a rel="prefetch" href=".">Home</a></li>
<li class:active="{segment === 'tutorial'}"><a rel="prefetch" href="tutorial">Tutorial</a></li>
<li class:active="{segment === 'docs'}"><a rel="prefetch" href="docs">API Docs</a></li>
<li class:active="{segment === 'examples'}"><a rel="prefetch" href="examples">Examples</a></li>
<li class:active="{segment === 'repl'}"><a rel="prefetch" href="repl">REPL</a></li>
<li class:active="{segment === 'blog'}"><a rel="prefetch" href="blog">Blog</a></li>
<li><a href="https://sapper.svelte.technology">Sapper</a></li>
<li>
<a href="https://discord.gg/yy75DKs" title="Discord Chat">
<Icon name="message-square" />
</a>
</li>
<li>
<a href="https://github.com/sveltejs/svelte" title="Github Repo">
<Icon name="github" />
</a>
</li>
</ul>
</nav>
</header>

@ -1,28 +0,0 @@
<!--
-----------------------------------------------
svg svelte-logo 1.0
- style doesn't like var(--xxx)???
-----------------------------------------------
-->
<script>
export let outer = '';
export let inner = '';
export let size;
</script>
<svg class='logo' viewBox='0 0 103 124' width={size} height={size}>
<path style='{outer === "none" ? "fill:none; stroke:none;" : outer || "fill:var(--prime)"}' 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' />
<path style='{(inner === "none") ? "fill:none; stroke:none;" : inner || "fill:white"}' d='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>
<style>
.logo {
position: relative;
vertical-align: middle;
stroke-miterlimit: 10;
stroke-width: 3.5;
stroke-linecap: round;
stroke-linejoin: round;
}
</style>

@ -1,68 +0,0 @@
<script>
export let style;
let show = false;
$: if (typeof document !== 'undefined') {
if (show) document.body.classList.add('css-debug')
else document.body.classList.remove('css-debug')
}
</script>
<!--
-----------------------------------------------
simple css-debug
-----------------------------------------------
-->
<label {style}>
<input type="checkbox" bind:checked={show}> grid
</label>
{#if show}
<div class='guide horizontal' style='top: calc(var(--nav-h) + var(--top-offset))' />
<div class='guide vertical' style='left: var(--side-page)' />
<div class='guide vertical' style='right: var(--side-page)' />
<div class='guide vertical' style='left: var(--side-nav)' />
<div class='guide vertical' style='right: var(--side-nav)' />
{/if}
<style>
label {
position: fixed;
padding: 0.3rem 1rem;
font-size: 1.2rem;
cursor: pointer;
user-select: none;
opacity: 0.5;
font: 300 1rem/1.7 var(--font-ui);
z-index: 30;
}
label:hover {
opacity: 1
}
input {
display: none
}
.guide {
position: fixed;
opacity: .5;
z-index: 30
}
.horizontal {
width: 100%;
height: 0
}
.vertical {
width: 0;
height: 100vh
}
:global(.css-debug *) {
outline: 1px solid rgba(73, 177, 238, 0.25);
}
</style>

@ -1,28 +0,0 @@
<!--
-----------------------------------------------
svg svelte-isometry 1.0
- first simple try to simplify iso-grafics
-----------------------------------------------
-->
<script>
export let style = '';
export let size = '';
</script>
<svg class='svelte-iso' viewBox='0 0 128 128' width={size} height={size}>
<polygon {style} points="107.94 92 107.94 36 64.01 8 20.06 35.99 20.06 92.01 64 120 107.94 92" />
<polyline {style} points="107.94 36 64.01 64 64 120" />
<line {style} x1="64.01" y1="64" x2="20.06" y2="35.99" />
</svg>
<style>
.svelte-iso {
position: relative;
vertical-align: middle;
stroke-miterlimit: 10;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
</style>

@ -1,81 +0,0 @@
<!--
NOTE
from time to time <input type="color"> is crashing on chrome!
no idea why?
even with all extensions off
-->
{#if color}
<input ref:prop class="color" on:input="change(this)" type="color" name={color} /> {/if}
{#if text}
<input ref:prop on:input="change(this)" type="text" name={text} />
{/if}
<style>
input,
input:active,
input:focus {
border: none;
outline: none;
cursor: pointer;
}
input[type=text] {
border: .1rem solid var(--text);
padding: .8rem 1.4rem;
font: 300 var(--code-fs)/1.7 var(--font-mono);
color: var(--second);
text-indent: 0;
transition: all .2s var(--out-back);
}
input[type=text]:focus {
border-color: var(--flash);
color: var(--flash);
text-indent: .8rem;
}
.color {
position: relative;
top: 0;
width: 100%;
height: 100%;
}
/* input-color-reset */
::-webkit-color-swatch {
border: none
}
::-webkit-color-swatch-wrapper {
padding: 0
}
::-moz-color-swatch,
::-moz-focus-inner {
border: none
}
::-moz-focus-inner {
padding: 0
}
</style>
<script>
import CustomProps from '../utils/css-custom-properties.js';
export default {
oncreate() {
this.cssprop = new CustomProps();
let prop = this.refs.prop;
prop.value = this.cssprop.get(prop.name);
},
methods: {
change(prop) {
return this.cssprop.set(prop.name, prop.value)
}
}
};
</script>

@ -1,49 +0,0 @@
<button class='toast' on:click='reload()'>
Site has been updated — tap to reload
</button>
<style>
.toast {
position: fixed;
left: 0;
bottom: 0;
margin: 0;
border-radius: 0;
width: 100%;
background-color: var(--prime);
cursor: pointer;
color: white;
padding: 3.2rem 4rem;
text-align: left;
font: 300 var(--h6) var(--font-ui);
z-index: 99;
animation: slide-up 0.4s ease-out;
}
@keyframes slide-up {
0% {
transform: translate(0, 100%);
}
100% {
transform: translate(0, 0);
}
}
</style>
<script>
export default {
methods: {
reload() {
window.location.reload();
}
},
setup(Toast) {
Toast.show = () => {
const target = document.createDocumentFragment();
new Toast({ target });
document.body.appendChild(target);
Toast.show = () => {};
};
}
};
</script>

@ -0,0 +1,52 @@
<script>
import { Section } from '@sveltejs/site-kit';
import IntersectionObserver from '../../components/IntersectionObserver.svelte';
import ReplWidget from '../../components/Repl/ReplWidget.svelte';
export let id;
</script>
<style>
.example {
width: 100%;
}
.example :global(a) {
color: inherit;
}
.example > :global(p) {
margin: 4.4rem 2.4rem 2.4rem 0;
}
.repl-container {
width: 100%;
height: 420px;
border-radius: var(--border-r);
overflow: hidden;
}
@media (min-width: 920px) {
.example {
display: grid;
grid-template-columns: 1fr 3fr;
grid-gap: 0.5em;
align-items: start;
}
}
</style>
<Section>
<div class="example">
<slot></slot>
<div class="repl-container">
<IntersectionObserver once let:intersecting top={400}>
{#if intersecting}
<!-- <Lazy this={loadReplWidget} example={id}/> -->
<ReplWidget example={id}/>
{/if}
</IntersectionObserver>
</div>
</div>
</Section>

@ -1,19 +1,37 @@
<script> <script>
import { page, preloading } from '@sapper/app'; import { stores } from '@sapper/app';
import InlineSvg from '../components/InlineSvg.svelte'; import { Icon, Icons, Nav, NavItem } from '@sveltejs/site-kit';
import PreloadingIndicator from '../components/PreloadingIndicator.svelte'; import PreloadingIndicator from '../components/PreloadingIndicator.svelte';
import Nav from '../components/TopNav.svelte';
const { page, preloading } = stores();
export let segment; export let segment;
</script> </script>
<Icons/>
{#if $preloading} {#if $preloading}
<PreloadingIndicator/> <PreloadingIndicator/>
{/if} {/if}
<InlineSvg />
{#if $page.path !== '/repl/embed'} {#if $page.path !== '/repl/embed'}
<Nav {segment}/> <Nav {segment} {page} logo="svelte-logo-horizontal.svg">
<NavItem segment="tutorial">Tutorial</NavItem>
<NavItem segment="docs">API</NavItem>
<NavItem segment="examples">Examples</NavItem>
<NavItem segment="repl">REPL</NavItem>
<NavItem segment="blog">Blog</NavItem>
<NavItem external="https://sapper.svelte.dev">Sapper</NavItem>
<NavItem external="https://discord.gg/yy75DKs" title="Discord Chat">
<Icon name="message-square"/>
</NavItem>
<NavItem external="https://github.com/sveltejs/svelte" title="GitHub Repo">
<Icon name="github"/>
</NavItem>
</Nav>
{/if} {/if}
<main> <main>

@ -133,6 +133,48 @@
border: 0.8rem solid var(--second); border: 0.8rem solid var(--second);
} }
/* headers anchors */
.post :global(.offset-anchor) {
position: relative;
display: block;
top: calc(-1 * (var(--nav-h) + var(--top-offset) - 1rem));
width: 0;
height: 0;
}
.post :global(.anchor) {
position: absolute;
display: block;
background: url(/icons/link.svg) 0 50% no-repeat;
background-size: 1em 1em;
width: 1.4em;
height: 1em;
top: calc((var(--h3) - 24px) / 2);
left: -1.4em;
opacity: 0;
transition: opacity 0.2s;
border: none !important; /* TODO get rid of linkify */
}
.post :global(h2):hover :global(.anchor),
.post :global(h3):hover :global(.anchor),
.post :global(h4):hover :global(.anchor),
.post :global(h5):hover :global(.anchor),
.post :global(h6):hover :global(.anchor) {
opacity: 1;
}
@media (max-width: 768px) {
.post :global(.anchor) {
transform: scale(0.6);
opacity: 1;
top: calc((1em - 0.6 * 24px) / 2);
left: -1.0em;
}
}
@media (min-width: 910px) { @media (min-width: 910px) {
.post :global(.max) { .post :global(.max) {
width: calc(100vw - 2 * var(--side-nav)); width: calc(100vw - 2 * var(--side-nav));

@ -1,10 +1,14 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { extract_frontmatter, langs, link_renderer } from '../../utils/markdown.js'; import { extract_frontmatter, langs, link_renderer } from '@sveltejs/site-kit/utils/markdown.js';
import marked from 'marked'; import marked from 'marked';
import { makeSlugProcessor } from '../../utils/slug';
import { SLUG_PRESERVE_UNICODE } from '../../../config';
import PrismJS from 'prismjs'; import PrismJS from 'prismjs';
import 'prismjs/components/prism-bash'; import 'prismjs/components/prism-bash';
const makeSlug = makeSlugProcessor(SLUG_PRESERVE_UNICODE);
export default function get_posts() { export default function get_posts() {
return fs return fs
.readdirSync('content/blog') .readdirSync('content/blog')
@ -39,6 +43,17 @@ export default function get_posts() {
return `<pre class='language-${plang}'><code>${highlighted}</code></pre>`; return `<pre class='language-${plang}'><code>${highlighted}</code></pre>`;
}; };
renderer.heading = (text, level, rawtext) => {
const fragment = makeSlug(rawtext);
return `
<h${level}>
<span id="${fragment}" class="offset-anchor"></span>
<a href="blog/${slug}#${fragment}" class="anchor" aria-hidden="true"></a>
${text}
</h${level}>`;
};
const html = marked( const html = marked(
content.replace(/^\t+/gm, match => match.split('\t').join(' ')), content.replace(/^\t+/gm, match => match.split('\t').join(' ')),
{ renderer } { renderer }

@ -48,19 +48,12 @@
a { a {
position: relative; position: relative;
opacity: 0.75; transition: color 0.2s;
transition: opacity 0.2s;
border-bottom: none; border-bottom: none;
padding: 0; padding: 0;
color: var(--second); color: var(--second);
} }
@media (min-width: 832px) {
a {
color: white;
}
}
.section { .section {
display: block; display: block;
padding: 0 0 .8rem 0; padding: 0 0 .8rem 0;
@ -78,22 +71,33 @@
} }
.section:hover, .section:hover,
.subsection:hover { .subsection:hover,
.active {
color: var(--flash); color: var(--flash);
opacity: 1
} }
.subsection[data-level="4"] { .subsection[data-level="4"] {
padding-left: 1.2rem; padding-left: 1.2rem;
} }
.active { opacity: 1 }
.icon-container { .icon-container {
position: absolute; position: absolute;
top: -.2rem; top: -.2rem;
right: 2.4rem; right: 2.4rem;
} }
@media (min-width: 832px) {
a {
color: var(--sidebar-text);
}
a:hover,
.section:hover,
.subsection:hover,
.active {
color: white
}
}
</style> </style>
<ul <ul

@ -1,8 +1,8 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { SLUG_PRESERVE_UNICODE } from '../../../config'; import { SLUG_PRESERVE_UNICODE, SLUG_SEPARATOR } from '../../../config';
import { extract_frontmatter, extract_metadata, langs, link_renderer } from '../../utils/markdown.js'; import { extract_frontmatter, extract_metadata, langs, link_renderer } from '@sveltejs/site-kit/utils/markdown.js';
import { makeSessionSlugProcessor } from '../../utils/slug'; import { make_session_slug_processor } from '@sveltejs/site-kit/utils/slug';
import marked from 'marked'; import marked from 'marked';
import PrismJS from 'prismjs'; import PrismJS from 'prismjs';
import 'prismjs/components/prism-bash'; import 'prismjs/components/prism-bash';
@ -38,7 +38,10 @@ const blockTypes = [
]; ];
export default function() { export default function() {
const makeSlug = makeSessionSlugProcessor(SLUG_PRESERVE_UNICODE); const makeSlug = make_session_slug_processor({
preserve_unicode: SLUG_PRESERVE_UNICODE,
separator: SLUG_SEPARATOR
});
return fs return fs
.readdirSync(`content/docs`) .readdirSync(`content/docs`)

@ -6,359 +6,11 @@
</script> </script>
<script> <script>
import { onMount } from 'svelte'; import { Docs } from '@sveltejs/site-kit'
import GuideContents from './_GuideContents.svelte';
import Icon from '../../components/Icon.svelte';
import { getFragment } from '../../utils/navigation';
export let sections; export let sections;
let active_section;
let container;
let aside;
let show_contents = false;
onMount(() => {
// don't update `active_section` for headings above level 4, see _sections.js
const anchors = container.querySelectorAll('[id]:not([data-scrollignore])');
let positions;
const onresize = () => {
const { top } = container.getBoundingClientRect();
positions = [].map.call(anchors, anchor => {
return anchor.getBoundingClientRect().top - top;
});
}
let last_id = getFragment();
const onscroll = () => {
const top = -window.scrollY;
let i = anchors.length;
while (i--) {
if (positions[i] + top < 40) {
const anchor = anchors[i];
const { id } = anchor;
if (id !== last_id) {
active_section = id;
last_id = id;
}
return;
}
}
};
window.addEventListener('scroll', onscroll, true);
window.addEventListener('resize', onresize, true);
// wait for fonts to load...
const timeouts = [
setTimeout(onresize, 1000),
setTimeout(onscroll, 5000)
];
onresize();
onscroll();
return () => {
window.removeEventListener('scroll', onscroll, true);
window.removeEventListener('resize', onresize, true);
timeouts.forEach(timeout => clearTimeout(timeout));
};
});
</script> </script>
<style>
aside {
position: fixed;
background-color: white;
left: 0.8rem;
bottom: 0.8rem;
width: 2em;
height: 2em;
overflow: hidden;
border: 1px solid #eee;
box-shadow: 1px 1px 6px rgba(0,0,0,0.1);
transition: width 0.2s, height 0.2s;
}
aside button {
position: absolute;
bottom: 0;
left: 0;
width: 3.4rem;
height: 3.4rem;
}
aside.open {
width: calc(100vw - 3rem);
height: calc(100vh - var(--nav-h));
}
aside.open::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: calc(100% - 2rem);
height: 2em;
background: linear-gradient(to top, rgba(255,255,255,0) 0%, rgba(255,255,255,0.7) 50%, rgba(255,255,255,1) 100%);
pointer-events: none;
z-index: 2;
}
aside::after {
content: '';
position: absolute;
left: 0;
bottom: 1.9em;
width: calc(100% - 2rem);
height: 2em;
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,0.7) 50%, rgba(255,255,255,1) 100%);
pointer-events: none;
}
.sidebar {
position: absolute;
font-family: var(--font);
overflow-y: auto;
width: 100%;
height: 100%;
padding: 4em 1.6rem 2em 3.2rem;
bottom: 2em;
}
.content {
width: 100%;
margin: 0;
padding: var(--top-offset) var(--side-nav);
tab-size: 2;
-moz-tab-size: 2;
}
@media (min-width: 832px) { /* can't use vars in @media :( */
aside {
display: block;
width: var(--sidebar-w);
height: 100vh;
top: 0;
left: 0;
overflow: hidden;
box-shadow: none;
border: none;
overflow: hidden;
background-color: var(--second);
color: white;
}
aside.open::before {
display: none;
}
aside::after {
content: '';
bottom: 0;
height: var(--top-offset);
background: linear-gradient(to bottom, rgba(103,103,120,0) 0%, rgba(103,103,120,0.7) 50%, rgba(103,103,120,1) 100%);
}
aside button {
display: none;
}
.sidebar {
padding: var(--top-offset) 0 6.4rem 3.2rem;
font-family: var(--font);
overflow-y: auto;
height: 100%;
bottom: auto;
width: 100%;
}
.content {
padding-left: calc(var(--sidebar-w) + var(--side-nav));
}
.content :global(.side-by-side) {
display: grid;
grid-template-columns: calc(50% - 0.5em) calc(50% - 0.5em);
grid-gap: 1em;
}
}
.content h2 {
margin-top: 8rem;
padding: 2rem 1.6rem 4rem 0.2rem;
border-top: var(--border-w) solid #6767785b; /* based on --second */
color: var(--text);
line-height: 1;
font-size: var(--h3);
letter-spacing: .05em;
text-transform: uppercase;
}
.content section:first-of-type > h2 {
margin-top: 0;
}
.content :global(h4) {
margin: 2em 0 1em 0;
}
.content :global(.offset-anchor) {
position: relative;
display: block;
top: calc(-1 * (var(--nav-h) + var(--top-offset) - 1rem));
width: 0;
height: 0;
}
.content :global(.anchor) {
position: absolute;
display: block;
background: url(/icons/link.svg) 0 50% no-repeat;
background-size: 1em 1em;
width: 1.4em;
height: 1em;
left: -1.3em;
opacity: 0;
transition: opacity 0.2s;
border: none !important; /* TODO get rid of linkify */
}
@media (min-width: 768px) {
.content :global(h2):hover :global(.anchor),
.content :global(h3):hover :global(.anchor),
.content :global(h4):hover :global(.anchor),
.content :global(h5):hover :global(.anchor),
.content :global(h6):hover :global(.anchor) {
opacity: 1;
}
.content :global(h5) :global(.anchor),
.content :global(h6) :global(.anchor) {
top: 0.5em;
}
}
.content :global(h3),
.content :global(h3 > code) {
margin: 6.4rem 0 0 0;
padding: 2rem 1.6rem 5.6rem .2rem;
color: var(--text);
border-top: var(--border-w) solid #6767781f; /* based on --second */
background: transparent;
line-height: 1;
}
.content :global(h3):first-of-type {
border: none;
margin: 0;
}
/* avoid doubled border-top */
.content :global(h3 > code) {
border-radius: 0 0 0 0;
border: none;
font-size: var(--h4);
line-height: 1.2;
}
.content :global(h4),
.content :global(h4 > code) {
font-weight: 600;
font-size: var(--h4);
color: var(--second);
margin: 6.4rem 0 1.6rem 0;
padding-left: 0;
background: transparent;
line-height: 1;
padding: 0;
}
.content :global(h5) {
font-size: 2rem;
}
.content :global(code) {
padding: .3rem .8rem .3rem;
margin: 0 0.2rem;
top: -.1rem;
background: var(--back-api);
}
.content :global(pre) :global(code) {
padding: 0;
margin: 0;
top: 0;
background: transparent;
}
.content :global(pre) {
margin: 0 0 2em 0;
}
.content :global(.icon) {
width: 2rem;
height: 2rem;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}
.content :global(table) {
margin: 0 0 2em 0;
}
section > :global(.code-block) > :global(pre) {
display: inline-block;
background: var(--back-api);
color: white;
padding: .3rem .8rem;
margin: 0;
max-width: 100%;
}
section > :global(.code-block)> :global(pre.language-markup) {
padding: .3rem .8rem .2rem;
background: var(--back-api);
}
section > :global(p) {
max-width: var(--linemax)
}
small {
font-size: var(--h5);
float: right;
pointer-events: all;
color: var(--prime);
cursor: pointer;
}
/* no linkify on these */
small a { all: unset }
small a:before { all: unset }
section :global(blockquote) {
color: hsl(204, 100%, 50%);
border: 2px solid var(--flash);
}
section :global(blockquote) :global(code) {
background: hsl(204, 100%, 95%) !important;
color: hsl(204, 100%, 50%);
}
</style>
<svelte:head> <svelte:head>
<title>API Docs • Svelte</title> <title>API Docs • Svelte</title>
@ -367,31 +19,4 @@
<meta name="Description" content="Cybernetically enhanced web apps"> <meta name="Description" content="Cybernetically enhanced web apps">
</svelte:head> </svelte:head>
<div bind:this={container} class='content listify'> <Docs {sections}/>
{#each sections as section}
<section data-id={section.slug}>
<h2>
<span class="offset-anchor" id={section.slug}></span>
<a href="docs#{section.slug}" class="anchor" aria-hidden></a>
{section.metadata.title}
<small>
<a href='https://github.com/sveltejs/svelte/edit/master/site/content/docs/{section.file}' title='edit this section'>
<Icon name='edit' /></a>
</small>
</h2>
{@html section.html}
</section>
{/each}
</div>
<aside bind:this={aside} class="sidebar-container" class:open={show_contents}>
<div class="sidebar" on:click="{() => show_contents = false}"> <!-- scroll container -->
<GuideContents {sections} {active_section} {show_contents} />
</div>
<button on:click="{() => show_contents = !show_contents}">
<Icon name="{show_contents? 'close' : 'menu'}"/>
</button>
</aside>

@ -22,7 +22,7 @@
.section-title { .section-title {
display: block; display: block;
padding: 0 0 .8rem 0; padding: 0 0 0.8rem 0;
font: 400 var(--h6) var(--font); font: 400 var(--h6) var(--font);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.12em; letter-spacing: 0.12em;
@ -32,7 +32,7 @@
a { a {
display: flex; display: flex;
position: relative; position: relative;
color: white; color: var(--sidebar-text);
border-bottom: none; border-bottom: none;
padding: 0.2rem 3rem; padding: 0.2rem 3rem;
margin: 0 -3rem; margin: 0 -3rem;
@ -42,17 +42,19 @@
} }
a:hover { a:hover {
color: var(--flash); color: white;
} }
a.active { a.active {
background: rgba(255, 255, 255, 0.1) calc(100% - 3rem) 50% no-repeat url(/icons/arrow-right.svg); background: rgba(0, 0, 0, 0.15) calc(100% - 3rem) 50% no-repeat
url(/icons/arrow-right.svg);
background-size: 1em 1em; background-size: 1em 1em;
color: white; color: white;
} }
a.active.loading { a.active.loading {
background: rgba(255, 255, 255, 0.1) calc(100% - 3rem) 50% no-repeat url(/icons/loading.svg); background: rgba(0, 0, 0, 0.1) calc(100% - 3rem) 50% no-repeat
url(/icons/loading.svg);
background-size: 1em 1em; background-size: 1em 1em;
color: white; color: white;
} }
@ -86,7 +88,7 @@
class="thumbnail" class="thumbnail"
alt="{example.title} thumbnail" alt="{example.title} thumbnail"
src="examples/thumbnails/{example.slug}.jpg" src="examples/thumbnails/{example.slug}.jpg"
> />
<span>{example.title}</span> <span>{example.title}</span>
</a> </a>

@ -26,7 +26,7 @@
svelteUrl svelteUrl
} from '../../config'; } from '../../config';
import { process_example } from '../../utils/examples'; import { process_example } from '../../utils/examples';
import { getFragment } from '../../utils/navigation'; import { getFragment } from '@sveltejs/site-kit/utils/navigation';
import TableOfContents from './_TableOfContents.svelte'; import TableOfContents from './_TableOfContents.svelte';
export let sections; export let sections;

@ -1,13 +1,11 @@
<script> <script>
import Blurb from './_components/Blurb.svelte'; import { Blurb, Hero, Section } from '@sveltejs/site-kit';
// import Blurb from './_components/Blurb.svelte';
import Example from './_components/Example.svelte';
import WhosUsingSvelte from './_components/WhosUsingSvelte.svelte'; import WhosUsingSvelte from './_components/WhosUsingSvelte.svelte';
import IntersectionObserver from '../components/IntersectionObserver.svelte';
// import Lazy from '../components/Lazy.svelte'; // import Lazy from '../components/Lazy.svelte';
import ReplWidget from '../components/Repl/ReplWidget.svelte';
import contributors from './_contributors.js'; import contributors from './_contributors.js';
let sy = 0;
// TODO this causes a Sapper CSS bug... // TODO this causes a Sapper CSS bug...
// function loadReplWidget() { // function loadReplWidget() {
// console.log('lazy loading'); // console.log('lazy loading');
@ -16,77 +14,18 @@
</script> </script>
<style> <style>
.container {
position: relative;
margin: 10rem auto;
padding: 0 var(--side-nav);
max-width: 120rem;
}
.container h3 { color: var(--text) }
.container ul { list-style: none }
/* max line-length ~60 chars */
li:not(.box) > p {
max-width: var(--linemax)
}
/* darken text for accesibility */ /* darken text for accesibility */
/* TODO does this belong elsewhere? */
:global(.back-light) { :global(.back-light) {
--text: hsl(36, 3%, 44%); --text: hsl(36, 3%, 44%);
} }
.hero {
margin: 10rem auto;
}
.hero h3, .logotype {
position: relative;
left: 1.6rem;
}
.hero h3 {
font-size: 2rem;
}
.logotype {
height: 4rem;
}
.logo {
position: absolute;
top: -4rem;
right: 0rem;
width: 52rem;
will-change: transform;
display: none;
}
.examples { .examples {
background: var(--second); background: var(--second);
color: white; color: white;
overflow: hidden; overflow: hidden;
} }
.example {
width: 100%;
}
.example a {
color: inherit;
}
.example > p {
margin: 4.4rem 2.4rem 2.4rem 0;
}
.repl-container {
width: 100%;
height: 420px;
border-radius: var(--border-r);
overflow: hidden;
}
.contributor { .contributor {
width: 2.4em; width: 2.4em;
height: 2.4em; height: 2.4em;
@ -98,45 +37,6 @@
margin: 0 0.5em 0.5em 0; margin: 0 0.5em 0.5em 0;
border: 2px solid var(--second); border: 2px solid var(--second);
} }
@media (min-width: 640px) {
.logotype {
height: 6rem;
}
.hero h3 {
font-size: var(--h3);
}
}
@media (min-width: 800px) {
.logo {
display: block;
}
.hero {
margin: 15rem auto;
}
.hero h3, .logotype {
left: 3rem;
}
}
@media (min-width: 920px) {
.example {
display: grid;
grid-template-columns: 1fr 3fr;
grid-gap: 0.5em;
align-items: start;
}
}
@media (min-width: 1200px) {
.logo {
right: calc(50vw - 60rem);
}
}
</style> </style>
<svelte:head> <svelte:head>
@ -147,78 +47,83 @@
<meta name="Description" content="Cybernetically enhanced web apps"> <meta name="Description" content="Cybernetically enhanced web apps">
</svelte:head> </svelte:head>
<svelte:window bind:scrollY={sy}/> <Hero
title="Svelte"
tagline="Cybernetically enhanced web apps"
outline="svelte-logo-outline.svg"
logotype="svelte-logotype.svg"
/>
<img alt="Svelte logo" class="logo" src="svelte-logo-outline.svg" style="transform: translate(0, {sy * .2}px)"> <Blurb>
<a href="blog/write-less-code" slot="one">
<h2>Write less code</h2>
<p>Build boilerplate-free components using languages you already know — HTML, CSS and JavaScript</p>
<section class="hero container"> <span class="learn-more">learn more</span>
<img alt="Svelte logotype" class="logotype" src="svelte-logotype.svg"> </a>
<h3>Cybernetically enhanced web apps</h3>
</section>
<Blurb/> <a href="blog/virtual-dom-is-pure-overhead" slot="two">
<h2>No virtual DOM</h2>
<p>Svelte compiles your code to tiny, framework-less vanilla JS — your app starts fast and stays fast</p>
<div class="examples"> <span class="learn-more">learn more</span>
<section class="container example"> </a>
<p>Svelte components are built on top of HTML. Just add data.</p>
<div class="repl-container"> <a href="blog/svelte-3-rethinking-reactivity" slot="three">
<IntersectionObserver once let:intersecting top={400}> <h2>Truly reactive</h2>
{#if intersecting} <p>No more complex state management libraries — Svelte brings reactivity to JavaScript itself</p>
<!-- <Lazy this={loadReplWidget} example="hello-world"/> -->
<ReplWidget example="hello-world"/>
{/if}
</IntersectionObserver>
</div>
</section>
<section class="container example"> <span class="learn-more">learn more</span>
<p>CSS is component-scoped by default — no more style collisions or specificity wars. Or you can <a href="/blog/svelte-css-in-js">use your favourite CSS-in-JS library</a>.</p> </a>
<div class="repl-container"> <div class="description" slot="what">
<IntersectionObserver once let:intersecting top={400}> <p>Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the <em>browser</em>, Svelte shifts that work into a <em>compile step</em> that happens when you build your app.</p>
{#if intersecting}
<!-- <Lazy this={loadReplWidget} example="nested-components"/> -->
<ReplWidget example="nested-components"/>
{/if}
</IntersectionObserver>
</div>
</section>
<section class="container example"> <p>Instead of using techniques like virtual DOM diffing, Svelte writes code that surgically updates the DOM when the state of your app changes.</p>
<p>Trigger efficient, granular updates by assigning to local variables. The compiler does the rest.</p>
<div class="repl-container"> <p><a href="blog/svelte-3-rethinking-reactivity">Read the introductory blog post</a> to learn more.</p>
<IntersectionObserver once let:intersecting top={400}>
{#if intersecting}
<!-- <Lazy this={loadReplWidget} example="reactive-assignments"/> -->
<ReplWidget example="reactive-assignments"/>
{/if}
</IntersectionObserver>
</div> </div>
</section>
<section class="container example"> <div style="grid-area: start; display: flex; flex-direction: column; min-width: 0" slot="how">
<p>Build beautiful UIs with a powerful, performant transition engine built right into the framework.</p> <pre class="language-bash" style="margin: 0 0 1em 0; min-width: 0; min-height: 0">
npx degit sveltejs/template my-svelte-project
cd my-svelte-project
<div class="repl-container"> npm install
<IntersectionObserver once let:intersecting top={400}> npm run dev & open http://localhost:5000
{#if intersecting} </pre>
<!-- <Lazy this={loadReplWidget} example="svg-transitions"/> -->
<ReplWidget example="svg-transitions"/> <p style="flex: 1">See the <a href="blog/the-easiest-way-to-get-started">quickstart guide</a> for more information.</p>
{/if}
</IntersectionObserver> <p class="cta"><a rel="prefetch" href="tutorial">Learn Svelte</a></p>
</div> </div>
</section> </Blurb>
<div class="examples">
<Example id="hello-world">
<p>Svelte components are built on top of HTML. Just add data.</p>
</Example>
<Example id="nested-components">
<p>CSS is component-scoped by default — no more style collisions or specificity wars. Or you can <a href="/blog/svelte-css-in-js">use your favourite CSS-in-JS library</a>.</p>
</Example>
<Example id="reactive-assignments">
<p>Trigger efficient, granular updates by assigning to local variables. The compiler does the rest.</p>
</Example>
<Example id="svg-transitions">
<p>Build beautiful UIs with a powerful, performant transition engine built right into the framework.</p>
</Example>
</div> </div>
<section class="container"> <Section>
<h3>Who's using Svelte?</h3> <h3>Who's using Svelte?</h3>
<WhosUsingSvelte/> <WhosUsingSvelte/>
</section> </Section>
<section class="container"> <Section>
<h3>Contributors</h3> <h3>Contributors</h3>
<p>Svelte is free and open source software, made possible by the work of dozens of volunteers. <a href="https://github.com/sveltejs/svelte">Join us!</a></p> <p>Svelte is free and open source software, made possible by the work of dozens of volunteers. <a href="https://github.com/sveltejs/svelte">Join us!</a></p>
@ -230,4 +135,4 @@
href="https://github.com/{contributor}" href="https://github.com/{contributor}"
>{contributor}</a> >{contributor}</a>
{/each} {/each}
</section> </Section>

@ -1,7 +1,7 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import UserMenu from './UserMenu.svelte'; import UserMenu from './UserMenu.svelte';
import Icon from '../../../../components/Icon.svelte'; import { Icon } from '@sveltejs/site-kit';
import * as doNotZip from 'do-not-zip'; import * as doNotZip from 'do-not-zip';
import downloadBlob from '../../_utils/downloadBlob.js'; import downloadBlob from '../../_utils/downloadBlob.js';
import { user } from '../../../../user.js'; import { user } from '../../../../user.js';

@ -1,6 +1,6 @@
<script> <script>
import { goto } from '@sapper/app'; import { goto } from '@sapper/app';
import Icon from '../../../components/Icon.svelte'; import { Icon } from '@sveltejs/site-kit';
export let sections; export let sections;
export let slug; export let slug;
@ -30,13 +30,17 @@
display: block; display: block;
padding: 0.7em 0; padding: 0.7em 0;
text-align: center; text-align: center;
opacity: 0.7; opacity: 0.75;
color: white; color: white;
} }
a:hover {
opacity: 1;
}
a.disabled, a.disabled:hover, a.disabled:active { a.disabled, a.disabled:hover, a.disabled:active {
color: white; color: white;
opacity: 0.4; opacity: 0.3;
} }
span { span {

@ -3,7 +3,7 @@ import * as path from 'path';
import marked from 'marked'; import marked from 'marked';
import PrismJS from 'prismjs'; import PrismJS from 'prismjs';
import send from '@polka/send'; import send from '@polka/send';
import { extract_frontmatter, extract_metadata, langs, link_renderer } from '../../../utils/markdown'; import { extract_frontmatter, extract_metadata, langs, link_renderer } from '@sveltejs/site-kit/utils/markdown';
const cache = new Map(); const cache = new Map();

@ -18,7 +18,7 @@
import { getContext } from 'svelte'; import { getContext } from 'svelte';
import ScreenToggle from '../../../components/ScreenToggle.svelte'; import ScreenToggle from '../../../components/ScreenToggle.svelte';
import Icon from '../../../components/Icon.svelte'; import { Icon } from '@sveltejs/site-kit';
import TableOfContents from './_TableOfContents.svelte'; import TableOfContents from './_TableOfContents.svelte';
import { import {
@ -151,7 +151,7 @@
height: 100%; height: 100%;
border-right: 1px solid var(--second); border-right: 1px solid var(--second);
background-color: var(--second); background-color: var(--second);
color: white; color: var(--sidebar-text);
} }
.chapter-markup { .chapter-markup {
@ -165,6 +165,7 @@
margin: 4rem 0 1.6rem 0; margin: 4rem 0 1.6rem 0;
font-size: var(--h3); font-size: var(--h3);
line-height: 1; line-height: 1;
font-weight: 400;
color: white; color: white;
} }
@ -173,16 +174,21 @@
} }
.chapter-markup :global(a) { .chapter-markup :global(a) {
color: var(--sidebar-text);
}
.chapter-markup :global(a:hover) {
color: white; color: white;
} }
.chapter-markup :global(ul) { .chapter-markup :global(ul) {
padding: 0 0 0 2em; padding: 0 0 0 2em;
} }
.chapter-markup :global(blockquote) { .chapter-markup :global(blockquote) {
background-color: rgba(255,255,255,.1); background-color: rgba(0,0,0,.17);
color: white; color: var(--sidebar-text);
} }
.chapter-markup::-webkit-scrollbar { .chapter-markup::-webkit-scrollbar {
@ -198,22 +204,22 @@
.chapter-markup :global(p) > :global(code), .chapter-markup :global(p) > :global(code),
.chapter-markup :global(ul) :global(code) { .chapter-markup :global(ul) :global(code) {
color: white; color: var(--sidebar-text);
background: rgba(255,255,255,.1); background: rgba(0,0,0,.12);
padding: .2em .4em; padding: .2em .4em .3em;
white-space: nowrap; white-space: nowrap;
position: relative; position: relative;
top: -0.1em; top: -0.1em;
} }
.controls { .controls {
border-top: 1px solid rgba(255,255,255,.1); border-top: 1px solid rgba(255,255,255,.15);
padding: 1em 0 0 0; padding: 1em 0 0 0;
display: flex; display: flex;
} }
.show { .show {
background: rgba(255,255,255,.1); background: rgba(0,0,0,.4);
padding: .2em .7em .3em; padding: .2em .7em .3em;
border-radius: var(--border-r); border-radius: var(--border-r);
top: .1em; top: .1em;
@ -223,22 +229,17 @@
} }
.show:hover { .show:hover {
background: rgba(255,255,255,.2); background: rgba(0,0,0,.65);
color: white;
} }
a.next { a.next {
/* border-bottom: none; */
padding-right: 1.2em; padding-right: 1.2em;
background: no-repeat 100% 50% url(/icons/arrow-right.svg); background: no-repeat 100% 50% url(/icons/arrow-right.svg);
background-size: 1em 1em; background-size: 1em 1em;
margin-left: auto; margin-left: auto;
} }
a.next:hover {
/* border-bottom: 2px solid currentColor; */
/* text-decoration: underline; */
}
.improve-chapter { .improve-chapter {
padding: 1em 0 .5em 0; padding: 1em 0 .5em 0;
} }

@ -1,6 +1,6 @@
import * as fs from 'fs'; import * as fs from 'fs';
import send from '@polka/send'; import send from '@polka/send';
import { extract_frontmatter } from '../../utils/markdown'; import { extract_frontmatter } from '@sveltejs/site-kit/utils/markdown';
let json; let json;

@ -7,7 +7,6 @@
%sapper.base% %sapper.base%
<link rel='stylesheet' href='global.css'>
<link href=prism.css rel=stylesheet> <link href=prism.css rel=stylesheet>
<link rel='manifest' href='manifest.json'> <link rel='manifest' href='manifest.json'>
<link rel='icon' type='image/png' href='favicon.png'> <link rel='icon' type='image/png' href='favicon.png'>

@ -1,61 +0,0 @@
import * as fleece from 'golden-fleece';
export function extract_frontmatter(markdown) {
const match = /---\r?\n([\s\S]+?)\r?\n---/.exec(markdown);
const frontMatter = match[1];
const content = markdown.slice(match[0].length);
const metadata = {};
frontMatter.split('\n').forEach(pair => {
const colonIndex = pair.indexOf(':');
metadata[pair.slice(0, colonIndex).trim()] = pair
.slice(colonIndex + 1)
.trim();
});
return { metadata, content };
}
export function extract_metadata(line, lang) {
try {
if (lang === 'html' && line.startsWith('<!--') && line.endsWith('-->')) {
return fleece.evaluate(line.slice(4, -3).trim());
}
if (
lang === 'js' ||
(lang === 'json' && line.startsWith('/*') && line.endsWith('*/'))
) {
return fleece.evaluate(line.slice(2, -2).trim());
}
} catch (err) {
// TODO report these errors, don't just squelch them
return null;
}
}
// map lang to prism-language-attr
export const langs = {
bash: 'bash',
html: 'markup',
sv: 'markup',
js: 'javascript',
css: 'css'
};
// links renderer
export function link_renderer (href, title, text) {
let target_attr = '';
let title_attr = '';
if (href.startsWith("http")) {
target_attr = ' target="_blank"';
}
if (title !== null) {
title_attr = ` title="${title}"`;
}
return `<a href="${href}"${target_attr}${title_attr}>${text}</a>`;
}

@ -1 +0,0 @@
export const getFragment = () => window.location.hash.slice(1);

@ -55,10 +55,16 @@ export const unicodeSafeProcessor = string =>
}, []) }, [])
.join(SLUG_SEPARATOR); .join(SLUG_SEPARATOR);
/* processor */
export const makeSlugProcessor = (preserveUnicode = false) => preserveUnicode
? unicodeSafeProcessor
: urlsafeSlugProcessor;
/* session processor */ /* session processor */
export const makeSessionSlugProcessor = (preserveUnicode = false) => { export const makeSessionSlugProcessor = (preserveUnicode = false) => {
const processor = preserveUnicode ? unicodeSafeProcessor : urlsafeSlugProcessor; const processor = makeSlugProcessor(preserveUnicode);
const seen = new Set(); const seen = new Set();
return string => { return string => {

@ -46,6 +46,7 @@
--flash: #40b3ff; --flash: #40b3ff;
--heading: var(--second); --heading: var(--second);
--text: #444; --text: #444;
--sidebar-text: rgba(255, 255, 255, .75);
--border-w: .3rem; /* border-width */ --border-w: .3rem; /* border-width */
--border-r: .4rem; /* border-radius */ --border-r: .4rem; /* border-radius */
} }

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

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

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

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

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

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -1,12 +1,9 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
export default class CatchBlock extends Node { export default class CatchBlock extends AbstractBlock {
block: Block;
scope: TemplateScope; scope: TemplateScope;
children: Node[];
constructor(component, parent, scope, info) { constructor(component, parent, scope, info) {
super(component, parent, scope, info); super(component, parent, scope, info);
@ -15,6 +12,8 @@ export default class CatchBlock extends Node {
this.scope.add(parent.error, parent.expression.dependencies, this); this.scope.add(parent.error, parent.expression.dependencies, this);
this.children = map_children(component, parent, this.scope, info.children); this.children = map_children(component, parent, this.scope, info.children);
if (!info.skip) {
this.warn_if_empty_block(); this.warn_if_empty_block();
} }
} }
}

@ -1,34 +1,50 @@
import Node from './shared/Node'; import Node from './shared/Node';
import ElseBlock from './ElseBlock'; import ElseBlock from './ElseBlock';
import Block from '../render-dom/Block';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
import { Node as INode } from '../../interfaces'; import { Node as INode } from '../../interfaces';
import { new_tail } from '../utils/tail';
function unpack_destructuring(contexts: Array<{ name: string, tail: string }>, node: INode, tail: string) { function unpack_destructuring(contexts: Array<{ name: string, tail: string }>, node: INode, tail: string) {
if (!node) return; if (!node) return;
if (node.type === 'Identifier') { if (node.type === 'Identifier' || node.type === 'RestIdentifier') {
contexts.push({ contexts.push({
key: node, key: node,
tail tail
}); });
} else if (node.type === 'ArrayPattern') { } else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => { node.elements.forEach((element, i) => {
if (element && element.type === 'RestIdentifier') {
unpack_destructuring(contexts, element, `${tail}.slice(${i})`)
} else {
unpack_destructuring(contexts, element, `${tail}[${i}]`); unpack_destructuring(contexts, element, `${tail}[${i}]`);
}
}); });
} else if (node.type === 'ObjectPattern') { } else if (node.type === 'ObjectPattern') {
const used_properties = [];
node.properties.forEach((property) => { node.properties.forEach((property) => {
if (property.kind === 'rest') {
unpack_destructuring(
contexts,
property.value,
`@object_without_properties(${tail}, ${JSON.stringify(used_properties)})`
);
} else {
used_properties.push(property.key.name);
unpack_destructuring(contexts, property.value,`${tail}.${property.key.name}`); unpack_destructuring(contexts, property.value,`${tail}.${property.key.name}`);
}
}); });
} }
} }
export default class EachBlock extends Node { export default class EachBlock extends AbstractBlock {
type: 'EachBlock'; type: 'EachBlock';
block: Block;
expression: Expression; expression: Expression;
context_node: Node; context_node: Node;
@ -41,7 +57,6 @@ export default class EachBlock extends Node {
has_animation: boolean; has_animation: boolean;
has_binding = false; has_binding = false;
children: Node[];
else?: ElseBlock; else?: ElseBlock;
constructor(component, parent, scope, info) { constructor(component, parent, scope, info) {
@ -55,7 +70,7 @@ export default class EachBlock extends Node {
this.scope = scope.child(); this.scope = scope.child();
this.contexts = []; this.contexts = [];
unpack_destructuring(this.contexts, info.context, ''); unpack_destructuring(this.contexts, info.context, new_tail());
this.contexts.forEach(context => { this.contexts.forEach(context => {
this.scope.add(context.key.name, this.expression.dependencies, this); this.scope.add(context.key.name, this.expression.dependencies, this);
@ -85,7 +100,7 @@ export default class EachBlock extends Node {
} }
} }
this.warn_if_empty_block(); // TODO would be better if EachBlock, IfBlock etc extended an abstract Block class this.warn_if_empty_block();
this.else = info.else this.else = info.else
? new ElseBlock(component, this, this.scope, info.else) ? new ElseBlock(component, this, this.scope, info.else)

@ -224,7 +224,21 @@ export default class Element extends Node {
} }
if (this.name === 'figcaption') { if (this.name === 'figcaption') {
if (this.parent.name !== 'figure') { let { parent } = this;
let is_figure_parent = false;
while (parent) {
if (parent.name === 'figure') {
is_figure_parent = true;
break;
}
if (parent.type === 'Element') {
break;
}
parent = parent.parent;
}
if (!is_figure_parent) {
this.component.warn(this, { this.component.warn(this, {
code: `a11y-structure`, code: `a11y-structure`,
message: `A11y: <figcaption> must be an immediate child of <figure>` message: `A11y: <figcaption> must be an immediate child of <figure>`

@ -1,11 +1,8 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
export default class ElseBlock extends Node { export default class ElseBlock extends AbstractBlock {
type: 'ElseBlock'; type: 'ElseBlock';
children: Node[];
block: Block;
constructor(component, parent, scope, info) { constructor(component, parent, scope, info) {
super(component, parent, scope, info); super(component, parent, scope, info);

@ -1,17 +1,13 @@
import Node from './shared/Node';
import ElseBlock from './ElseBlock'; import ElseBlock from './ElseBlock';
import Block from '../render-dom/Block';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
export default class IfBlock extends Node { export default class IfBlock extends AbstractBlock {
type: 'IfBlock'; type: 'IfBlock';
expression: Expression; expression: Expression;
children: any[];
else: ElseBlock; else: ElseBlock;
block: Block;
constructor(component, parent, scope, info) { constructor(component, parent, scope, info) {
super(component, parent, scope, info); super(component, parent, scope, info);

@ -1,15 +1,14 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock';
export default class PendingBlock extends Node { export default class PendingBlock extends AbstractBlock {
block: Block;
children: Node[];
constructor(component, parent, scope, info) { constructor(component, parent, scope, info) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.children = map_children(component, parent, scope, info.children); this.children = map_children(component, parent, scope, info.children);
if (!info.skip) {
this.warn_if_empty_block(); this.warn_if_empty_block();
} }
} }
}

@ -1,12 +1,9 @@
import Node from './shared/Node';
import Block from '../render-dom/Block';
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
export default class ThenBlock extends Node { export default class ThenBlock extends AbstractBlock {
block: Block;
scope: TemplateScope; scope: TemplateScope;
children: Node[];
constructor(component, parent, scope, info) { constructor(component, parent, scope, info) {
super(component, parent, scope, info); super(component, parent, scope, info);
@ -15,6 +12,8 @@ export default class ThenBlock extends Node {
this.scope.add(parent.value, parent.expression.dependencies, this); this.scope.add(parent.value, parent.expression.dependencies, this);
this.children = map_children(component, parent, this.scope, info.children); this.children = map_children(component, parent, this.scope, info.children);
if (!info.skip) {
this.warn_if_empty_block(); this.warn_if_empty_block();
} }
} }
}

@ -0,0 +1,25 @@
import Block from '../../render-dom/Block';
import Component from './../../Component';
import Node from './Node';
export default class AbstractBlock extends Node {
block: Block;
children: Node[];
constructor(component: Component, parent, scope, info: any) {
super(component, parent, scope, info);
}
warn_if_empty_block() {
if (!this.children || this.children.length > 1) return;
const child = this.children[0];
if (!child || (child.type === 'Text' && !/[^ \r\n\f\v\t]/.test(child.data))) {
this.component.warn(this, {
code: 'empty-block',
message: 'Empty block'
});
}
}
}

@ -1,3 +1,4 @@
import Attribute from './../Attribute';
import Component from './../../Component'; import Component from './../../Component';
export default class Node { export default class Node {
@ -12,6 +13,7 @@ export default class Node {
can_use_innerhtml: boolean; can_use_innerhtml: boolean;
var: string; var: string;
attributes: Attribute[];
constructor(component: Component, parent, scope, info: any) { constructor(component: Component, parent, scope, info: any) {
this.start = info.start; this.start = info.start;
@ -64,18 +66,4 @@ export default class Node {
this.parent.type === type || this.parent.has_ancestor(type) : this.parent.type === type || this.parent.has_ancestor(type) :
false; false;
} }
warn_if_empty_block() {
if (!/Block$/.test(this.type) || !this.children) return;
if (this.children.length > 1) return;
const child = this.children[0];
if (!child || (child.type === 'Text' && !/[^ \r\n\f\v\t]/.test(child.data))) {
this.component.warn(this, {
code: 'empty-block',
message: 'Empty block'
});
}
}
} }

@ -169,14 +169,15 @@ export default function dom(
scope = scope.parent; scope = scope.parent;
} }
if (node.type === 'AssignmentExpression') { if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
let names = []; let names = [];
if (node.left.type === 'MemberExpression') { if (assignee.type === 'MemberExpression') {
const left_object_name = get_object(node.left).name; const left_object_name = get_object(assignee).name;
left_object_name && (names = [left_object_name]); left_object_name && (names = [left_object_name]);
} else { } else {
names = extract_names(node.left); names = extract_names(assignee);
} }
if (node.operator === '=' && nodes_match(node.left, node.right)) { if (node.operator === '=' && nodes_match(node.left, node.right)) {
@ -189,9 +190,10 @@ export default function dom(
code.overwrite(node.start, node.end, dirty.map(n => component.invalidate(n)).join('; ')); code.overwrite(node.start, node.end, dirty.map(n => component.invalidate(n)).join('; '));
} else { } else {
const single = ( const single = (
node.left.type === 'Identifier' && node.type === 'AssignmentExpression' &&
assignee.type === 'Identifier' &&
parent.type === 'ExpressionStatement' && parent.type === 'ExpressionStatement' &&
node.left.name[0] !== '$' assignee.name[0] !== '$'
); );
names.forEach(name => { names.forEach(name => {
@ -201,7 +203,7 @@ export default function dom(
const variable = component.var_lookup.get(name); const variable = component.var_lookup.get(name);
if (variable && (variable.hoistable || variable.global || variable.module)) return; if (variable && (variable.hoistable || variable.global || variable.module)) return;
if (single) { if (single && !(variable.subscribable && variable.reassigned)) {
code.prependRight(node.start, `$$invalidate('${name}', `); code.prependRight(node.start, `$$invalidate('${name}', `);
code.appendLeft(node.end, `)`); code.appendLeft(node.end, `)`);
} else { } else {
@ -213,18 +215,6 @@ export default function dom(
} }
} }
else if (node.type === 'UpdateExpression') {
const { name } = get_object(node.argument);
if (scope.find_owner(name) !== component.instance_scope) return;
const variable = component.var_lookup.get(name);
if (variable && variable.hoistable) return;
pending_assignments.add(name);
component.has_reactive_assignments = true;
}
if (pending_assignments.size > 0) { if (pending_assignments.size > 0) {
if (node.type === 'ArrowFunctionExpression') { if (node.type === 'ArrowFunctionExpression') {
const insert = Array.from(pending_assignments).map(name => component.invalidate(name)).join('; '); const insert = Array.from(pending_assignments).map(name => component.invalidate(name)).join('; ');
@ -423,14 +413,15 @@ export default function dom(
${set && `$$self.$set = ${set};`} ${set && `$$self.$set = ${set};`}
${reactive_declarations.length > 0 && deindent`
${injected.length && `let ${injected.join(', ')};`} ${injected.length && `let ${injected.join(', ')};`}
${reactive_declarations.length > 0 && deindent`
$$self.$$.update = ($$dirty = { ${Array.from(all_reactive_dependencies).map(n => `${n}: 1`).join(', ')} }) => { $$self.$$.update = ($$dirty = { ${Array.from(all_reactive_dependencies).map(n => `${n}: 1`).join(', ')} }) => {
${reactive_declarations} ${reactive_declarations}
}; };
`}
${fixed_reactive_declarations} ${fixed_reactive_declarations}
`}
return ${stringify_props(filtered_declarations)}; return ${stringify_props(filtered_declarations)};
} }

@ -6,6 +6,7 @@ import EachBlock from '../../nodes/EachBlock';
import FragmentWrapper from './Fragment'; import FragmentWrapper from './Fragment';
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import ElseBlock from '../../nodes/ElseBlock'; import ElseBlock from '../../nodes/ElseBlock';
import { attach_head } from '../../utils/tail';
class ElseBlockWrapper extends Wrapper { class ElseBlockWrapper extends Wrapper {
node: ElseBlock; node: ElseBlock;
@ -127,7 +128,7 @@ export default class EachBlockWrapper extends Wrapper {
this.block.bindings.set(prop.key.name, { this.block.bindings.set(prop.key.name, {
object: this.vars.each_block_value, object: this.vars.each_block_value,
property: this.index_name, property: this.index_name,
snippet: `${this.vars.each_block_value}[${this.index_name}]${prop.tail}` snippet: attach_head(`${this.vars.each_block_value}[${this.index_name}]`, prop.tail)
}); });
}); });
@ -177,7 +178,7 @@ export default class EachBlockWrapper extends Wrapper {
? block.get_unique_name(`${this.var}_anchor`) ? block.get_unique_name(`${this.var}_anchor`)
: (this.next && this.next.var) || 'null'; : (this.next && this.next.var) || 'null';
this.context_props = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`); this.context_props = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = ${attach_head('list[i]', prop.tail)};`);
if (this.node.has_binding) this.context_props.push(`child_ctx.${this.vars.each_block_value} = list;`); if (this.node.has_binding) this.context_props.push(`child_ctx.${this.vars.each_block_value} = list;`);
if (this.node.has_binding || this.node.index) this.context_props.push(`child_ctx.${this.index_name} = i;`); if (this.node.has_binding || this.node.index) this.context_props.push(`child_ctx.${this.index_name} = i;`);

@ -308,21 +308,6 @@ const attribute_lookup = {
dropzone: {}, dropzone: {},
enctype: { applies_to: ['form'] }, enctype: { applies_to: ['form'] },
for: { property_name: 'htmlFor', applies_to: ['label', 'output'] }, for: { property_name: 'htmlFor', applies_to: ['label', 'output'] },
form: {
applies_to: [
'button',
'fieldset',
'input',
'keygen',
'label',
'meter',
'object',
'output',
'progress',
'select',
'textarea',
],
},
formaction: { applies_to: ['input', 'button'] }, formaction: { applies_to: ['input', 'button'] },
headers: { applies_to: ['td', 'th'] }, headers: { applies_to: ['td', 'th'] },
height: { height: {

@ -75,12 +75,10 @@ export class Scope {
if (node.kind === 'var' && this.block && this.parent) { if (node.kind === 'var' && this.block && this.parent) {
this.parent.add_declaration(node); this.parent.add_declaration(node);
} else if (node.type === 'VariableDeclaration') { } else if (node.type === 'VariableDeclaration') {
const initialised = !!node.init;
node.declarations.forEach((declarator: Node) => { node.declarations.forEach((declarator: Node) => {
extract_names(declarator.id).forEach(name => { extract_names(declarator.id).forEach(name => {
this.declarations.set(name, node); this.declarations.set(name, node);
if (initialised) this.initialised_declarations.add(name); if (declarator.init) this.initialised_declarations.add(name);
}); });
}); });
} else { } else {

@ -0,0 +1,7 @@
export function new_tail(): string {
return '%%tail_head%%';
}
export function attach_head(head: string, tail: string): string {
return tail.replace('%%tail_head%%', head);
}

@ -1,4 +1,6 @@
export { default as compile } from './compile/index'; export { default as compile } from './compile/index';
export { default as parse } from './parse/index';
export { default as preprocess } from './preprocess/index'; export { default as preprocess } from './preprocess/index';
export { walk } from 'estree-walker';
export const VERSION = '__VERSION__'; export const VERSION = '__VERSION__';

@ -61,6 +61,11 @@ export interface CompileOptions {
preserveWhitespace?: boolean; preserveWhitespace?: boolean;
} }
export interface ParserOptions {
filename?: string;
customElement?: boolean;
}
export interface Visitor { export interface Visitor {
enter: (node: Node) => void; enter: (node: Node) => void;
leave?: (node: Node) => void; leave?: (node: Node) => void;

@ -3,7 +3,7 @@ export function append(target, node) {
} }
export function insert(target, node, anchor) { export function insert(target, node, anchor) {
target.insertBefore(node, anchor); target.insertBefore(node, anchor || null);
} }
export function detach(node) { export function detach(node) {
@ -38,6 +38,16 @@ export function element(name) {
return document.createElement(name); return document.createElement(name);
} }
export function object_without_properties(obj, exclude) {
const target = {};
for (const k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) {
target[k] = obj[k];
}
}
return target;
}
export function svg_element(name) { export function svg_element(name) {
return document.createElementNS('http://www.w3.org/2000/svg', name); return document.createElementNS('http://www.w3.org/2000/svg', name);
} }

@ -2,7 +2,7 @@ export function get_spread_update(levels, updates) {
const update = {}; const update = {};
const to_null_out = {}; const to_null_out = {};
const accounted_for = {}; const accounted_for = { $$scope: 1 };
let i = levels.length; let i = levels.length;
while (i--) { while (i--) {

@ -2,144 +2,94 @@ import { writable } from 'svelte/store'; // eslint-disable-line import/no-unreso
import { loop } from 'svelte/internal'; // eslint-disable-line import/no-unresolved import { loop } from 'svelte/internal'; // eslint-disable-line import/no-unresolved
import { is_date } from './utils.js'; import { is_date } from './utils.js';
function get_initial_velocity(value) { function tick_spring(ctx, last_value, current_value, target_value) {
if (typeof value === 'number' || is_date(value)) return 0;
if (Array.isArray(value)) return value.map(get_initial_velocity);
if (value && typeof value === 'object') {
const velocities = {};
for (const k in value) velocities[k] = get_initial_velocity(value[k]);
return velocities;
}
throw new Error(`Cannot spring ${typeof value} values`);
}
function get_threshold(value, target_value, precision) {
if (typeof value === 'number' || is_date(value)) return precision * Math.abs((target_value - value));
if (Array.isArray(value)) return value.map((v, i) => get_threshold(v, target_value[i], precision));
if (value && typeof value === 'object') {
const threshold = {};
for (const k in value) threshold[k] = get_threshold(value[k], target_value[k], precision);
return threshold;
}
throw new Error(`Cannot spring ${typeof value} values`);
}
function tick_spring(velocity, current_value, target_value, stiffness, damping, multiplier, threshold) {
let settled = true;
let value;
if (typeof current_value === 'number' || is_date(current_value)) { if (typeof current_value === 'number' || is_date(current_value)) {
const delta = target_value - current_value; const delta = target_value - current_value;
const spring = stiffness * delta; const velocity = (current_value - last_value) / (ctx.dt||1/60); // guard div by 0
const damper = damping * velocity; const spring = ctx.opts.stiffness * delta;
const damper = ctx.opts.damping * velocity;
const acceleration = spring - damper; const acceleration = (spring - damper) * ctx.inv_mass;
const d = (velocity + acceleration) * ctx.dt;
velocity += acceleration;
const d = velocity * multiplier; if (Math.abs(d) < ctx.opts.precision && Math.abs(delta) < ctx.opts.precision) {
return target_value; // settled
if (is_date(current_value)) {
value = new Date(current_value.getTime() + d);
} else { } else {
value = current_value + d; ctx.settled = false; // signal loop to keep ticking
return is_date(current_value) ?
new Date(current_value.getTime() + d) : current_value + d;
} }
} else if (Array.isArray(current_value)) {
if (Math.abs(d) > threshold || Math.abs(delta) > threshold) settled = false; return current_value.map((_, i) =>
} tick_spring(ctx, last_value[i], current_value[i], target_value[i]));
} else if (typeof current_value === 'object') {
else if (Array.isArray(current_value)) { const next_value = {};
value = current_value.map((v, i) => { for (const k in current_value)
const result = tick_spring( next_value[k] = tick_spring(ctx, last_value[k], current_value[k], target_value[k]);
velocity[i], return next_value;
v, } else {
target_value[i],
stiffness,
damping,
multiplier,
threshold[i]
);
velocity[i] = result.velocity;
if (!result.settled) settled = false;
return result.value;
});
}
else if (typeof current_value === 'object') {
value = {};
for (const k in current_value) {
const result = tick_spring(
velocity[k],
current_value[k],
target_value[k],
stiffness,
damping,
multiplier,
threshold[k]
);
velocity[k] = result.velocity;
if (!result.settled) settled = false;
value[k] = result.value;
}
}
else {
throw new Error(`Cannot spring ${typeof value} values`); throw new Error(`Cannot spring ${typeof value} values`);
} }
return { velocity, value, settled };
} }
export function spring(value, opts = {}) { export function spring(value, opts = {}) {
const store = writable(value); const store = writable(value);
const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = opts;
const { stiffness = 0.15, damping = 0.8, precision = 0.001 } = opts;
const velocity = get_initial_velocity(value);
let task;
let target_value = value;
let last_time; let last_time;
let settled; let task;
let threshold;
let current_token; let current_token;
let last_value = value;
let target_value = value;
function set(new_value) { let inv_mass = 1;
target_value = new_value; let inv_mass_recovery_rate = 0;
threshold = get_threshold(value, target_value, spring.precision); let cancel_task = false;
function set(new_value, opts = {}) {
target_value = new_value;
const token = current_token = {}; const token = current_token = {};
if (opts.hard || (spring.stiffness >= 1 && spring.damping >= 1)) {
cancel_task = true; // cancel any running animation
last_time = window.performance.now();
last_value = value;
store.set(value = target_value);
return new Promise(f => f()); // fulfil immediately
} else if (opts.soft) {
const rate = opts.soft === true ? .5 : +opts.soft;
inv_mass_recovery_rate = 1 / (rate * 60);
inv_mass = 0; // infinite mass, unaffected by spring forces
}
if (!task) { if (!task) {
last_time = window.performance.now(); last_time = window.performance.now();
settled = false; cancel_task = false;
task = loop(now => { task = loop(now => {
({ value, settled } = tick_spring(
velocity,
value,
target_value,
spring.stiffness,
spring.damping,
(now - last_time) * 60 / 1000,
threshold
));
last_time = now;
if (settled) { if (cancel_task) {
value = target_value; cancel_task = false;
task = null; task = null;
return false;
} }
store.set(value); inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1);
return !settled;
const ctx = {
inv_mass,
opts: spring,
settled: true, // tick_spring may signal false
dt: (now - last_time) * 60 / 1000
};
const next_value = tick_spring(ctx, last_value, value, target_value);
last_time = now;
last_value = value;
store.set(value = next_value);
if (ctx.settled)
task = null;
return !ctx.settled;
}); });
} }
@ -152,7 +102,7 @@ export function spring(value, opts = {}) {
const spring = { const spring = {
set, set,
update: fn => set(fn(target_value, value)), update: (fn, opts) => set(fn(target_value, value), opts),
subscribe: store.subscribe, subscribe: store.subscribe,
stiffness, stiffness,
damping, damping,

@ -3,14 +3,9 @@ import fragment from './state/fragment';
import { whitespace } from '../utils/patterns'; import { whitespace } from '../utils/patterns';
import { reserved } from '../utils/names'; import { reserved } from '../utils/names';
import full_char_code_at from '../utils/full_char_code_at'; import full_char_code_at from '../utils/full_char_code_at';
import { Node, Ast } from '../interfaces'; import { Node, Ast, ParserOptions } from '../interfaces';
import error from '../utils/error'; import error from '../utils/error';
interface ParserOptions {
filename?: string;
customElement?: boolean;
}
type ParserState = (parser: Parser) => (ParserState | void); type ParserState = (parser: Parser) => (ParserState | void);
export class Parser { export class Parser {

@ -11,7 +11,7 @@ type Property = {
start: number; start: number;
end: number; end: number;
type: 'Property'; type: 'Property';
kind: string; kind: 'init' | 'rest';
shorthand: boolean; shorthand: boolean;
key: Identifier; key: Identifier;
value: Context; value: Context;
@ -20,7 +20,7 @@ type Property = {
type Context = { type Context = {
start: number; start: number;
end: number; end: number;
type: 'Identifier' | 'ArrayPattern' | 'ObjectPattern'; type: 'Identifier' | 'ArrayPattern' | 'ObjectPattern' | 'RestIdentifier';
name?: string; name?: string;
elements?: Context[]; elements?: Context[];
properties?: Property[]; properties?: Property[];
@ -35,6 +35,13 @@ function error_on_assignment_pattern(parser: Parser) {
} }
} }
function error_on_rest_pattern_not_last(parser: Parser) {
parser.error({
code: 'rest-pattern-not-last',
message: 'Rest destructuring expected to be last'
}, parser.index);
}
export default function read_context(parser: Parser) { export default function read_context(parser: Parser) {
const context: Context = { const context: Context = {
start: parser.index, start: parser.index,
@ -49,6 +56,11 @@ export default function read_context(parser: Parser) {
do { do {
parser.allow_whitespace(); parser.allow_whitespace();
const lastContext = context.elements[context.elements.length - 1];
if (lastContext && lastContext.type === 'RestIdentifier') {
error_on_rest_pattern_not_last(parser);
}
if (parser.template[parser.index] === ',') { if (parser.template[parser.index] === ',') {
context.elements.push(null); context.elements.push(null);
} else { } else {
@ -69,6 +81,41 @@ export default function read_context(parser: Parser) {
do { do {
parser.allow_whitespace(); parser.allow_whitespace();
if (parser.eat('...')) {
parser.allow_whitespace();
const start = parser.index;
const name = parser.read_identifier();
const key: Identifier = {
start,
end: parser.index,
type: 'Identifier',
name
}
const property: Property = {
start,
end: parser.index,
type: 'Property',
kind: 'rest',
shorthand: true,
key,
value: key
}
context.properties.push(property);
parser.allow_whitespace();
if (parser.eat(',')) {
parser.error({
code: `comma-after-rest`,
message: `Comma is not permitted after the rest element`
}, parser.index - 1);
}
break;
}
const start = parser.index; const start = parser.index;
const name = parser.read_identifier(); const name = parser.read_identifier();
const key: Identifier = { const key: Identifier = {
@ -103,6 +150,22 @@ export default function read_context(parser: Parser) {
context.end = parser.index; context.end = parser.index;
} }
else if (parser.eat('...')) {
const name = parser.read_identifier();
if (name) {
context.type = 'RestIdentifier';
context.end = parser.index;
context.name = name;
}
else {
parser.error({
code: 'invalid-context',
message: 'Expected a rest pattern'
});
}
}
else { else {
const name = parser.read_identifier(); const name = parser.read_identifier();
if (name) { if (name) {

@ -36,7 +36,7 @@ export default function mustache(parser: Parser) {
parser.allow_whitespace(); parser.allow_whitespace();
// {/if} or {/each} // {/if}, {/each} or {/await}
if (parser.eat('/')) { if (parser.eat('/')) {
let block = parser.current(); let block = parser.current();
let expected; let expected;
@ -172,7 +172,8 @@ export default function mustache(parser: Parser) {
start, start,
end: null, end: null,
type: 'ThenBlock', type: 'ThenBlock',
children: [] children: [],
skip: false
}; };
await_block.then = then_block; await_block.then = then_block;
@ -196,7 +197,8 @@ export default function mustache(parser: Parser) {
start, start,
end: null, end: null,
type: 'CatchBlock', type: 'CatchBlock',
children: [] children: [],
skip: false
}; };
await_block.catch = catch_block; await_block.catch = catch_block;
@ -235,19 +237,22 @@ export default function mustache(parser: Parser) {
start: null, start: null,
end: null, end: null,
type: 'PendingBlock', type: 'PendingBlock',
children: [] children: [],
skip: true
}, },
then: { then: {
start: null, start: null,
end: null, end: null,
type: 'ThenBlock', type: 'ThenBlock',
children: [] children: [],
skip: true
}, },
catch: { catch: {
start: null, start: null,
end: null, end: null,
type: 'CatchBlock', type: 'CatchBlock',
children: [] children: [],
skip: true
}, },
} : } :
{ {
@ -287,13 +292,6 @@ export default function mustache(parser: Parser) {
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat(')', true); parser.eat(')', true);
parser.allow_whitespace(); parser.allow_whitespace();
} else if (parser.eat('@')) {
block.key = parser.read_identifier();
if (!block.key) parser.error({
code: `expected-name`,
message: `Expected name`
});
parser.allow_whitespace();
} }
} }
@ -310,7 +308,15 @@ export default function mustache(parser: Parser) {
parser.stack.push(block); parser.stack.push(block);
if (type === 'AwaitBlock') { if (type === 'AwaitBlock') {
const child_block = await_block_shorthand ? block.then : block.pending; let child_block;
if (await_block_shorthand) {
block.then.skip = false;
child_block = block.then;
} else {
block.pending.skip = false;
child_block = block.pending;
}
child_block.start = parser.index; child_block.start = parser.index;
parser.stack.push(child_block); parser.stack.push(child_block);
} }

@ -2,8 +2,10 @@ import { isIdentifierStart, isIdentifierChar } from 'acorn';
import full_char_code_at from './full_char_code_at'; import full_char_code_at from './full_char_code_at';
export const globals = new Set([ export const globals = new Set([
'alert',
'Array', 'Array',
'Boolean', 'Boolean',
'confirm',
'console', 'console',
'Date', 'Date',
'decodeURI', 'decodeURI',
@ -24,6 +26,7 @@ export const globals = new Set([
'parseInt', 'parseInt',
'process', 'process',
'Promise', 'Promise',
'prompt',
'RegExp', 'RegExp',
'Set', 'Set',
'String', 'String',

@ -19,6 +19,7 @@
"pending": { "pending": {
"start": 19, "start": 19,
"end": 39, "end": 39,
"skip": false,
"type": "PendingBlock", "type": "PendingBlock",
"children": [ "children": [
{ {
@ -53,6 +54,7 @@
"then": { "then": {
"start": 39, "start": 39,
"end": 88, "end": 88,
"skip": false,
"type": "ThenBlock", "type": "ThenBlock",
"children": [ "children": [
{ {
@ -98,6 +100,7 @@
"catch": { "catch": {
"start": 88, "start": 88,
"end": 140, "end": 140,
"skip": false,
"type": "CatchBlock", "type": "CatchBlock",
"children": [ "children": [
{ {

@ -1,3 +1,3 @@
{#each animals as [key, value]} {#each animals as [key, value, ...rest]}
<p>{key}: {value}</p> <p>{key}: {value}</p>
{/each} {/each}

@ -1,12 +1,12 @@
{ {
"html": { "html": {
"start": 0, "start": 0,
"end": 62, "end": 71,
"type": "Fragment", "type": "Fragment",
"children": [ "children": [
{ {
"start": 0, "start": 0,
"end": 62, "end": 71,
"type": "EachBlock", "type": "EachBlock",
"expression": { "expression": {
"type": "Identifier", "type": "Identifier",
@ -16,37 +16,37 @@
}, },
"children": [ "children": [
{ {
"start": 33, "start": 42,
"end": 54, "end": 63,
"type": "Element", "type": "Element",
"name": "p", "name": "p",
"attributes": [], "attributes": [],
"children": [ "children": [
{ {
"start": 36, "start": 45,
"end": 41, "end": 50,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier", "type": "Identifier",
"start": 37, "start": 46,
"end": 40, "end": 49,
"name": "key" "name": "key"
} }
}, },
{ {
"start": 41, "start": 50,
"end": 43, "end": 52,
"type": "Text", "type": "Text",
"data": ": " "data": ": "
}, },
{ {
"start": 43, "start": 52,
"end": 50, "end": 59,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier", "type": "Identifier",
"start": 44, "start": 53,
"end": 49, "end": 58,
"name": "value" "name": "value"
} }
} }
@ -55,7 +55,7 @@
], ],
"context": { "context": {
"start": 18, "start": 18,
"end": 30, "end": 39,
"type": "ArrayPattern", "type": "ArrayPattern",
"elements": [ "elements": [
{ {
@ -69,6 +69,12 @@
"end": 29, "end": 29,
"type": "Identifier", "type": "Identifier",
"name": "value" "name": "value"
},
{
"start": 31,
"end": 38,
"type": "RestIdentifier",
"name": "rest"
} }
] ]
} }

@ -0,0 +1,4 @@
<div>
<slot />
<div {...$$props}></div>
</div>

@ -0,0 +1,19 @@
export default {
html: `
<div>
<input />
<div class="foo"></div>
</div>
`,
async test({ assert, component, target }) {
component.value = 'foo';
assert.htmlEqual(target.innerHTML, `
<div>
<input />
<div class="foo"></div>
</div>
`);
}
};

@ -0,0 +1,8 @@
<script>
import Nested from './Nested.svelte';
export let value = '';
</script>
<Nested class="foo">
<input bind:value />
</Nested>

@ -1,5 +1,6 @@
<script> <script>
export let value; export let value;
export let value_with_default = undefined;
</script> </script>
<input bind:value> <input bind:value>

@ -1,6 +1,7 @@
<script> <script>
export let x; export let x;
export let y; export let y;
export let z = undefined;
</script> </script>
<div>{x} {y}</div> <div>{x} {y}</div>

@ -0,0 +1,20 @@
export default {
props: {
animalEntries: [
{ animal: 'raccoon', class: 'mammal' },
{ animal: 'eagle', class: 'bird' }
]
},
html: `
<p class="mammal">raccoon</p>
<p class="bird">eagle</p>
`,
test({ assert, component, target }) {
component.animalEntries = [{ animal: 'cow', class: 'mammal' }];
assert.htmlEqual(target.innerHTML, `
<p class="mammal">cow</p>
`);
},
};

@ -0,0 +1,7 @@
<script>
export let animalEntries;
</script>
{#each animalEntries as { animal, ...props } }
<p {...props}>{animal}</p>
{/each}

@ -0,0 +1,11 @@
export default {
html: `
<p>4</p>
`,
test({ assert, component, target }) {
assert.htmlEqual(target.innerHTML, `
<p>4</p>
`);
}
};

@ -0,0 +1,6 @@
<script>
const num = 2;
$: squared = num * num;
</script>
<p>{squared}</p>

@ -0,0 +1,8 @@
export default {
html: `0`,
async test({ assert, component, target }) {
await component.increment();
assert.htmlEqual(target.innerHTML, `1`);
}
};

@ -0,0 +1,11 @@
<script>
import { writable } from '../../../../store.js';
const foo = writable(0);
export function increment() {
$foo++;
}
</script>
{$foo}

@ -0,0 +1,3 @@
export default {
html: `42`,
};

@ -0,0 +1,7 @@
<script>
import { writable } from 'svelte/store';
let foo = writable(0);
foo = writable(42);
</script>
{$foo}

@ -0,0 +1,10 @@
<script>
let caption = 'a foo in its natural habitat';
</script>
<figure>
<img src='foo.jpg' alt='a picture of a foo'>
{#if caption}
<figcaption>{caption}</figcaption>
{/if}
</figure>

@ -0,0 +1,9 @@
<script>
let promise;
</script>
{#await promise}
<p>Loading</p>
{:then data}
<p>Data: {data}</p>
{/await}

@ -0,0 +1,7 @@
<script>
let promise;
</script>
{#await promise then data}
<p>Data: {data}</p>
{/await}

@ -0,0 +1,15 @@
[{
"code": "comma-after-rest",
"message": "Comma is not permitted after the rest element",
"pos": 100,
"start": {
"line": 5,
"column": 53,
"character": 100
},
"end": {
"line": 5,
"column": 53,
"character": 100
}
}]

@ -0,0 +1,7 @@
<script>
export let animalEntries;
</script>
{#each animalEntries as { animal, features: { ...rest, eyes } } }
<p {...rest}>{animal} {eyes}</p>
{/each}
Loading…
Cancel
Save