merge with updates from master

pull/6568/head
Your Name 5 years ago
commit 4b77652127

@ -0,0 +1,28 @@
name: Docs
on:
push:
branches:
- master
paths:
- site/content/**
jobs:
release:
name: Deploy docs
runs-on: ubuntu-latest
steps:
- name: my-app-install token
id: github-app
uses: getsentry/action-github-app-token@v1
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: run deploy docs workflow
uses: 'sveltejs/action-deploy-docs/dispatch@main'
with:
repo: 'svelte'
branch: 'master'
docs_path: 'site/content'
token: ${{ steps.github-app.outputs.token }}

@ -1,5 +1,38 @@
# Svelte changelog # Svelte changelog
## 3.42.2
* Collapse whitespace in `class` and `style` attributes ([#6004](https://github.com/sveltejs/svelte/issues/6004))
* Deselect all `<option>`s in a `<select>` where the bound `value` doesn't match any of them ([#6126](https://github.com/sveltejs/svelte/issues/6126))
* In hydrated components, only rely on helpers for creating the types of elements present in the component ([#6555](https://github.com/sveltejs/svelte/issues/6555))
* Add `HTMLElement` and `SVGElement` as known globals ([#6643](https://github.com/sveltejs/svelte/issues/6643))
* Account for scaling in `flip` animations ([#6657](https://github.com/sveltejs/svelte/issues/6657))
## 3.42.1
* Fix regression with reordering keyed `{#each}` blocks when compiling with hydration enabled ([#6561](https://github.com/sveltejs/svelte/issues/6561))
## 3.42.0
* Allow `use:actions` to be used on `<svelte:body>` ([#3163](https://github.com/sveltejs/svelte/issues/3163))
* Improve parser errors for certain invalid components ([#6259](https://github.com/sveltejs/svelte/issues/6259), [#6288](https://github.com/sveltejs/svelte/issues/6288))
* Fix paths in generator JS sourcemaps to be relative ([#6598](https://github.com/sveltejs/svelte/pull/6598))
* Fix overzealous warnings about `context="module"` variables not being reactive ([#6606](https://github.com/sveltejs/svelte/issues/6606))
## 3.41.0
* Support `export { ... } from` syntax in components ([#2214](https://github.com/sveltejs/svelte/issues/2214))
* Support `export let { ... } =` syntax in components ([#5612](https://github.com/sveltejs/svelte/issues/5612))
* Support `{#await ... then/catch}` without a variable for the resolved/rejected value ([#6270](https://github.com/sveltejs/svelte/issues/6270))
## 3.40.3
* Fix `<slot>` data when a transition is cancelled before completing ([#5394](https://github.com/sveltejs/svelte/issues/5394))
* Fix destructuring into variables beginning with `$` so that they result in store updates ([#5653](https://github.com/sveltejs/svelte/issues/5653))
* Fix `in:` transition configuration not properly updating when it's changed after its initial creation ([#6505](https://github.com/sveltejs/svelte/issues/6505))
* Fix applying `:global()` for `>` selector combinator ([#6550](https://github.com/sveltejs/svelte/issues/6550))
* Fix mounting component at detached DOM node ([#6567](https://github.com/sveltejs/svelte/issues/6567))
## 3.40.2 ## 3.40.2
* Fix dynamic `autofocus={...}` attribute handling ([#4995](https://github.com/sveltejs/svelte/issues/4995)) * Fix dynamic `autofocus={...}` attribute handling ([#4995](https://github.com/sveltejs/svelte/issues/4995))
@ -1981,7 +2014,7 @@ Also:
## 1.10.3 ## 1.10.3
* Prevent `'</script>'` string occurence breaking pages ([#349](https://github.com/sveltejs/svelte/pull/349)) * Prevent `'</script>'` string occurrence breaking pages ([#349](https://github.com/sveltejs/svelte/pull/349))
* Allow reference to whitelisted globals without properties ([#333](https://github.com/sveltejs/svelte/pull/333)) * Allow reference to whitelisted globals without properties ([#333](https://github.com/sveltejs/svelte/pull/333))
* Don't remove `&nbsp;` incorrectly ([#348](https://github.com/sveltejs/svelte/issues/348)) * Don't remove `&nbsp;` incorrectly ([#348](https://github.com/sveltejs/svelte/issues/348))
* `let` -> `var` in `addCss` block ([#351](https://github.com/sveltejs/svelte/pull/351)) * `let` -> `var` in `addCss` block ([#351](https://github.com/sveltejs/svelte/pull/351))

5711
package-lock.json generated

File diff suppressed because it is too large Load Diff

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

@ -41,7 +41,7 @@ When we say that Svelte now supports TypeScript, we mean a few different things:
* You get autocompletion hints and type-checking as you're writing components, even in expressions inside markup * You get autocompletion hints and type-checking as you're writing components, even in expressions inside markup
* TypeScript files understand the Svelte component API — no more red squiggles when you import a `.svelte` file into a `.ts` module * TypeScript files understand the Svelte component API — no more red squiggles when you import a `.svelte` file into a `.ts` module
#### How does it work? ### How does it work?
To understand the two main parts of TypeScript support, we'll compare it to the technique TypeScript uses to provide dev tools. There is a compiler `tsc` which you run on the command-line to convert `*.ts` to `*.js`, then there is a `TSServer` which is a node API that responds to requests from text editors. The `TSServer` is what provides all the JavaScript and TypeScript realtime introspection for editors while coding, and it has most of the compiler's code inside it. To understand the two main parts of TypeScript support, we'll compare it to the technique TypeScript uses to provide dev tools. There is a compiler `tsc` which you run on the command-line to convert `*.ts` to `*.js`, then there is a `TSServer` which is a node API that responds to requests from text editors. The `TSServer` is what provides all the JavaScript and TypeScript realtime introspection for editors while coding, and it has most of the compiler's code inside it.
@ -52,7 +52,7 @@ The Svelte compiler support for TypeScript is handled by [Christian Kaisermann](
For the editor level, we took inspiration from [Pine's](https://github.com/octref) work in the [Vue](https://vuejs.org) ecosystem via [Vetur](https://github.com/vuejs/vetur). Vetur provides an [LSP](https://github.com/vuejs/vetur/blob/master/server), a VS Code extension and a [CLI](https://github.com/vuejs/vetur/blob/master/vti). Svelte now also has an [LSP](https://github.com/sveltejs/language-tools/blob/master/packages/language-server), a [VS Code extension](https://github.com/sveltejs/language-tools/blob/master/packages/svelte-vscode) and a [CLI](https://github.com/sveltejs/language-tools/blob/master/packages/svelte-check). For the editor level, we took inspiration from [Pine's](https://github.com/octref) work in the [Vue](https://vuejs.org) ecosystem via [Vetur](https://github.com/vuejs/vetur). Vetur provides an [LSP](https://github.com/vuejs/vetur/blob/master/server), a VS Code extension and a [CLI](https://github.com/vuejs/vetur/blob/master/vti). Svelte now also has an [LSP](https://github.com/sveltejs/language-tools/blob/master/packages/language-server), a [VS Code extension](https://github.com/sveltejs/language-tools/blob/master/packages/svelte-vscode) and a [CLI](https://github.com/sveltejs/language-tools/blob/master/packages/svelte-check).
#### `*.svelte` Introspection ### `*.svelte` Introspection
For the official Svelte VS Code extension, we built off the foundations which [James Birtles](https://github.com/UnwrittenFun) has created in [`UnwrittenFun/svelte-vscode`](https://github.com/UnwrittenFun/svelte-vscode) and [`UnwrittenFun/svelte-language-server`](https://github.com/UnwrittenFun/svelte-language-server/). For the official Svelte VS Code extension, we built off the foundations which [James Birtles](https://github.com/UnwrittenFun) has created in [`UnwrittenFun/svelte-vscode`](https://github.com/UnwrittenFun/svelte-vscode) and [`UnwrittenFun/svelte-language-server`](https://github.com/UnwrittenFun/svelte-language-server/).
@ -67,7 +67,7 @@ Before getting started, add the dependencies:
npm install --save-dev @tsconfig/svelte typescript svelte-preprocess svelte-check npm install --save-dev @tsconfig/svelte typescript svelte-preprocess svelte-check
``` ```
##### 1. Compiling TypeScript ### 1. Compiling TypeScript
You first need to set up [`svelte-preprocess`](https://github.com/sveltejs/svelte-preprocess#svelte-preprocess), which passes the contents of your `<script lang="ts">` blocks through the TypeScript compiler. You first need to set up [`svelte-preprocess`](https://github.com/sveltejs/svelte-preprocess#svelte-preprocess), which passes the contents of your `<script lang="ts">` blocks through the TypeScript compiler.
@ -103,7 +103,7 @@ To configure TypeScript, you will need to create a `tsconfig.json` in the root o
Your `include`/`exclude` may differ per project — these are defaults that should work across most Svelte projects. Your `include`/`exclude` may differ per project — these are defaults that should work across most Svelte projects.
##### 2. Editor Support ### 2. Editor Support
Any editor [using an LSP](https://langserver.org/#implementations-client) can be supported. The [VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) extension has been our primary focus, but there is work in progress [on Atom](https://github.com/sveltejs/language-tools/pull/160), and Vim via [coc-svelte](https://github.com/coc-extensions/coc-svelte) has been updated with the latest LSP. Any editor [using an LSP](https://langserver.org/#implementations-client) can be supported. The [VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) extension has been our primary focus, but there is work in progress [on Atom](https://github.com/sveltejs/language-tools/pull/160), and Vim via [coc-svelte](https://github.com/coc-extensions/coc-svelte) has been updated with the latest LSP.
@ -111,7 +111,7 @@ These editor extensions will improve your coding experience even if you only use
To switch a `<script>` to use TypeScript, use `<script lang="ts">` and that should be it. Hopefully you won't be seeing an ocean of red squiggles. To switch a `<script>` to use TypeScript, use `<script lang="ts">` and that should be it. Hopefully you won't be seeing an ocean of red squiggles.
##### 3. CI Checks ### 3. CI Checks
Having red squiggles is great, well, kinda. On the long run though, you want to be able to verify that there are no errors in your code. To verify your project is error free, you can use the CLI tool [`svelte-check`](https://www.npmjs.com/package/svelte-check). It acts like an editor asking for errors against all of your `.svelte` files. Having red squiggles is great, well, kinda. On the long run though, you want to be able to verify that there are no errors in your code. To verify your project is error free, you can use the CLI tool [`svelte-check`](https://www.npmjs.com/package/svelte-check). It acts like an editor asking for errors against all of your `.svelte` files.

@ -57,18 +57,18 @@ For all the features and bugfixes see the CHANGELOG for [Svelte](https://github.
- [Who are my representatives?](https://whoaremyrepresentatives.us/) is a website built with Svelte to help US residents get more info on their congressional representatives - [Who are my representatives?](https://whoaremyrepresentatives.us/) is a website built with Svelte to help US residents get more info on their congressional representatives
- [Pick Palette](https://github.com/bluwy/pick-palette) is a color palette manager made with Svelte! - [Pick Palette](https://github.com/bluwy/pick-palette) is a color palette manager made with Svelte!
#### In-depth learning: ### In-depth learning:
- [Svelte 3 Up and Running](https://www.amazon.com/dp/B08D6T6BKS/ref=cm_sw_r_tw_dp_x_OQMtFb3GPQCB2) is a new book about building production-ready static web apps with Svelte 3 - [Svelte 3 Up and Running](https://www.amazon.com/dp/B08D6T6BKS/ref=cm_sw_r_tw_dp_x_OQMtFb3GPQCB2) is a new book about building production-ready static web apps with Svelte 3
- [Sapper Tutorial (Crash Course)](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gdr4Qhx83gBBcID-KMe-PQ) walks through the ins-and-outs of Sapper, the Svelte-powered application framework - [Sapper Tutorial (Crash Course)](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gdr4Qhx83gBBcID-KMe-PQ) walks through the ins-and-outs of Sapper, the Svelte-powered application framework
- [Svelte Society Day France](https://france.sveltesociety.dev/) happened September 27th featuring a wide variety of topics all in French! You can find the full recording [here](https://www.youtube.com/watch?v=aS1TQ155JK4). - [Svelte Society Day France](https://france.sveltesociety.dev/) happened September 27th featuring a wide variety of topics all in French! You can find the full recording [here](https://www.youtube.com/watch?v=aS1TQ155JK4).
#### Plug-and-play components: ### Plug-and-play components:
- [svelte-zoom](https://github.com/vaheqelyan/svelte-zoom) brings "nearly native" pan-and-zoom to images on desktop and mobile - [svelte-zoom](https://github.com/vaheqelyan/svelte-zoom) brings "nearly native" pan-and-zoom to images on desktop and mobile
- [svelte-materialify](https://github.com/TheComputerM/svelte-materialify) is a Material component library for Svelte with over 50 components - [svelte-materialify](https://github.com/TheComputerM/svelte-materialify) is a Material component library for Svelte with over 50 components
- [svelte-undoable](https://github.com/macfja/svelte-undoable) makes it easy to introduce undo and redo functionality using `bind:` - [svelte-undoable](https://github.com/macfja/svelte-undoable) makes it easy to introduce undo and redo functionality using `bind:`
- [This Tilt component](https://svelte.dev/repl/7b23ad9d2693424482cd411b0378b55b?version=3.24.1) implements a common UX pattern where the hovered element tilts to follow the mouse - [This Tilt component](https://svelte.dev/repl/7b23ad9d2693424482cd411b0378b55b?version=3.24.1) implements a common UX pattern where the hovered element tilts to follow the mouse
#### Lots of examples of how use JS tech came out this month: ### Lots of examples of how use JS tech came out this month:
- [Sapper with PostCSS and Tailwind](https://codechips.me/sapper-with-postcss-and-tailwind/) - [Sapper with PostCSS and Tailwind](https://codechips.me/sapper-with-postcss-and-tailwind/)
- [PrismJS (Code block syntax highlighting)](https://github.com/phptuts/Svelte-PrismJS) - [PrismJS (Code block syntax highlighting)](https://github.com/phptuts/Svelte-PrismJS)
- [Filepond (Drag-and-drop file upload)](https://github.com/pqina/svelte-filepond) - [Filepond (Drag-and-drop file upload)](https://github.com/pqina/svelte-filepond)

@ -62,7 +62,7 @@ For all the features and bugfixes see the CHANGELOGs for [Svelte](https://github
- [svelte-store-router](https://github.com/zyxd/svelte-store-router) is a store-based router for Svelte that suggests that routing is just another global state and History API changes are just an optional side-effects of this state. - [svelte-store-router](https://github.com/zyxd/svelte-store-router) is a store-based router for Svelte that suggests that routing is just another global state and History API changes are just an optional side-effects of this state.
- [Routify](https://routify.dev/blog/routify-2-released) just released version 2 of its Svelte router. - [Routify](https://routify.dev/blog/routify-2-released) just released version 2 of its Svelte router.
- [svelte-error-boundary](https://www.npmjs.com/package/@crownframework/svelte-error-boundary) provides a simple error boundary component for Svelte that can be can be used with both DOM and SSR targets. - [svelte-error-boundary](https://www.npmjs.com/package/@crownframework/svelte-error-boundary) provides a simple error boundary component for Svelte that can be can be used with both DOM and SSR targets.
- [svelte2dts](https://www.npmjs.com/package/svelte2dts) generates d.ts files from svelte files, creating truly sharable and well typed components. - [svelte2dts](https://www.npmjs.com/package/svelte2dts) generates d.ts files from svelte files, creating truly shareable and well typed components.
## See you next month! ## See you next month!

@ -54,7 +54,7 @@ Want to learn more about how to get started, what's different compared to Sapper
- [Sapper Netlify](https://www.npmjs.com/package/sapper-netlify) is a Sapper project that can run on a Netlify function. - [Sapper Netlify](https://www.npmjs.com/package/sapper-netlify) is a Sapper project that can run on a Netlify function.
**Looking for a particular starter?** Check out [svelte-adders](https://github.com/svelte-add/svelte-adders) and a number of other integration examples at [sveltejs/integrations](https://github.com/sveltejs/integrations) **Looking for a particular starter?** Check out [svelte-adders](https://github.com/svelte-add/svelte-adders) and a number of other template examples at the community site [sveltesociety.dev](https://sveltesociety.dev/templates/)
**Learning Resources** **Learning Resources**
- [How to Build a Website with Svelte and SvelteKit](https://prismic.io/blog/svelte-sveltekit-tutorial) is a step-by-step tutorial walking through the new SvelteKit setup. - [How to Build a Website with Svelte and SvelteKit](https://prismic.io/blog/svelte-sveltekit-tutorial) is a step-by-step tutorial walking through the new SvelteKit setup.

@ -57,7 +57,7 @@ Last week, Svelte Summit blew us away with a mountain of content! [Check out the
- [Adds Supabase to Svelte](https://github.com/joshnuss/svelte-supabase) is an experimental command to run to add Supabase to your SvelteKit project - [Adds Supabase to Svelte](https://github.com/joshnuss/svelte-supabase) is an experimental command to run to add Supabase to your SvelteKit project
- [svelte-babylon](https://github.com/SectorXUSA/svelte-babylon) lets you use BabylonJS like A-Frame through reactive Svelte Components - [svelte-babylon](https://github.com/SectorXUSA/svelte-babylon) lets you use BabylonJS like A-Frame through reactive Svelte Components
**Looking for a starter or integration?** Check out [svelte-adders](https://github.com/svelte-add/svelte-adders) and a number of other integration examples at [sveltejs/integrations](https://github.com/sveltejs/integrations) **Looking for a starter or integration?** Check out [svelte-adders](https://github.com/svelte-add/svelte-adders) and a number of other template examples at the community site [sveltesociety.dev](https://sveltesociety.dev/templates)
**Learning Resources** **Learning Resources**

@ -0,0 +1,71 @@
---
title: What's new in Svelte: August 2021
description: Shadow DOM, export and await - oh my!
author: Daniel Sandoval
authorURL: https://desandoval.net
---
From The Changelog ([JS Party Ep. 182](https://changelog.com/jsparty/182)) to Svelte Radio (Episodes [29](https://share.transistor.fm/s/adc23e84) and [30](https://share.transistor.fm/s/6316622d)), it seems that folks couldn't help but talk about Svelte, this month! Also, shadow DOM support and new export and await functionality are new in Svelte.
## New in Svelte
July was the most active month for the Svelte core repo since late 2019 as we really worked to reduce the number of outstanding PRs and saw the release of Svelte 3.39.0, 3.40.0, and 3.41.0. Tons of bug fixes were added as well as the following new features:
- The `|trusted` event modifier allows you to check if an event is trusted before it's called ([#6137](https://github.com/sveltejs/svelte/issues/6137))
- The new `svelte/ssr` package to support work on improving SvelteKit SSR ([#6416](https://github.com/sveltejs/svelte/pull/6416))
- A new `errorMode` compiler option to support improved preprocessing of TypeScript files ([#6194](https://github.com/sveltejs/svelte/pull/6194))
- You can now specify a `ShadowRoot` as the `target` when creating a component - making it possible to render Svelte components inside the shadow DOM ([#5869](https://github.com/sveltejs/svelte/issues/5869))
- The `export { ... } from` ([#2214](https://github.com/sveltejs/svelte/issues/2214)), `export let { ... } =` ([#5612](https://github.com/sveltejs/svelte/issues/5612)) and `{#await ... then/catch}` ([#6270](https://github.com/sveltejs/svelte/issues/6270)) syntaxes are all now supported in Svelte components
For a full list of features and bug fixes, check out the [Svelte changelog](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md).
## SvelteKit Updates
- `prerender.force` is now `prerender.onError` which lets you fine-tune which errors fail the build and which do not ([#2007](https://github.com/sveltejs/kit/pull/2007))
- esbuild's configuration is now exposed for use with SvelteKit adapters ([#1914](https://github.com/sveltejs/kit/pull/1914))
- Error messages are friendlier now for common config errors ([#1910](https://github.com/sveltejs/kit/pull/1910)) and compiler errors ([#1827](https://github.com/sveltejs/kit/pull/1827))
- Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it ([#1847](https://github.com/sveltejs/kit/pull/1847))
- index.js exports will now be changed to directory exports when packaging - making for nicer imports ([#1905](https://github.com/sveltejs/kit/pull/1905))
- Vite.js's `mode` is now exposed from `$app/env` ([#1789](https://github.com/sveltejs/kit/pull/1789))
- Better types across the board ([#1778](https://github.com/sveltejs/kit/pull/1778), [#1791](https://github.com/sveltejs/kit/pull/1791), [#1646](https://github.com/sveltejs/kit/pull/1646))
To see all updates to SvelteKit, check out the [SvelteKit changelog](https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md).
## Features & bug fixes from around svelte/*
- Language Tools now better support the "Workplace Trust" functionality (used in VS Code)
- In svelte2tsx, ambient type declarations are now renamed to avoid conflicting declarations in the future. Users are now expected to provide the ambient type definitions themselves - fixing JS output
- Sapper released v0.29.2 which fixes regex routes, status codes when requesting a directory, and exports when a user has not provided a `base` tag ([changelog](https://github.com/sveltejs/sapper/blob/master/CHANGELOG.md))
---
## Community Showcase
**Apps & Sites**
- [Parsnip](https://www.parsnip.ai/) is a mobile-first, progressive-web-app that helps you to learn to cook at home. Check out the [conversation on Reddit](https://www.reddit.com/r/sveltejs/comments/oearb9/learning_to_cook_at_home_with_parsnip_built/) for all the geeky details.
- [Central Bank Digital Currency (CBDC) tracker](https://www.atlanticcouncil.org/cbdctracker/) is a site that keeps track of how countries around the world are adopting digital currencies.
- [Svelte Commerce](https://github.com/itswadesh/svelte-commerce) is an advanced frontend platform for eCommerce based on Sveltekit.
- [neovimcraft](https://neovimcraft.com/) is a SvelteKit site dedicated to neovim plugins
**Looking for a Svelte project to work on? Interested in helping make Svelte's presence on the web better?** Check out [the list of open issues](https://github.com/svelte-society/sveltesociety-2021/issues) if you'd like to contribute to the Svelte Society rewrite in SvelteKit.
**Educational Content**
- [How I Built a Cross-Platform Desktop Application with Svelte, Redis, and Rust](https://css-tricks.com/how-i-built-a-cross-platform-desktop-application-with-svelte-redis-and-rust/) is a blog post by Luke Edwards, Svelte maintainer and Developer Advocate from Cloudflare.
- [How to Create a Blog with SvelteKit and Strapi](https://strapi.io/blog/how-to-create-a-blog-with-svelte-kit-strapi) is a step-by-step tutorial by Aarnav Pai from Strapi
- [Sveltekit Markdown Blog](https://www.youtube.com/watch?v=sKKgT0SEioI&list=PLm_Qt4aKpfKgonq1zwaCS6kOD-nbOKx7V) is a YouTube tutorial series by WebJeda.
- [Using Custom Elements in Svelte](https://css-tricks.com/using-custom-elements-in-svelte/) is a deep dive into custom elements by Geoff Rich.
- [learn / graphql / svelte](https://hasura.io/learn/graphql/svelte-apollo/introduction/) is a free 2-hour GraphQL course course from Hasura.
- [How to add Magic Link to a SvelteKit application](https://magic.link/posts/magic-svelte) is a guide to the popular password-less login pattern.
**Libraries, Tools & Components**
- [Svelte-Capacitor](https://github.com/drannex42/svelte-capacitor/) just released v2.0.0 - making it even easier to build hybrid mobile apps for iOS and Android using Svelte and Capacitor with near native performance.
- [svelte-remixicon](https://github.com/ABarnob/svelte-remixicon) is an icon library for Svelte based on Remix Icon, consisting of more than 2000 icons.
- [SveltePress](https://github.com/GeopJr/SveltePress) is a documentation tool built on top of SvelteKit.
- [Svelte Starter Kit](https://github.com/one-aalam/svelte-starter-kit/tree/auth-supabase) is a boilerplate to quckly get up and running with Svelte, with Auth and User Profiles powered by Supabase.
- [Kahi UI](https://github.com/novacbn/kahi-ui) is a Svelte-first UI kit with Dark Mode built-in.
- [typesafe-i18n](https://github.com/ivanhofer/typesafe-i18n) is an opinionated, fully type-safe, lightweight localization library for TypeScript and JavaScript projects with no external dependencies.
Check out the community site [sveltesociety.dev](https://sveltesociety.dev/templates/) for more templates, adders and adapters from across the Svelte ecosystem.
## See you next month!
Want more updates? Join us on [Reddit](https://www.reddit.com/r/sveltejs/) or [Discord](https://discord.com/invite/yy75DKs)!

@ -350,7 +350,7 @@ If you don't care about the pending state, you can also omit the initial block.
--- ---
If conversely you only want to show the error state, you can omit the `then` block. Similarly, if you only want to show the error state, you can omit the `then` block.
```sv ```sv
{#await promise catch error} {#await promise catch error}
@ -515,7 +515,8 @@ The following modifiers are available:
* `nonpassive` — explicitly set `passive: false` * `nonpassive` — explicitly set `passive: false`
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase
* `once` — remove the handler after the first time it runs * `once` — remove the handler after the first time it runs
* `self` — only trigger handler if event.target is the element itself * `self` — only trigger handler if `event.target` is the element itself
* `trusted` — only trigger handler if `event.isTrusted` is `true`. I.e. if the event is triggered by a user action.
Modifiers can be chained together, e.g. `on:click|once|capture={...}`. Modifiers can be chained together, e.g. `on:click|once|capture={...}`.
@ -669,7 +670,7 @@ Media elements (`<audio>` and `<video>`) have their own set of bindings — six
* `playbackRate` — how fast or slow to play the video, where 1 is 'normal' * `playbackRate` — how fast or slow to play the video, where 1 is 'normal'
* `paused` — this one should be self-explanatory * `paused` — this one should be self-explanatory
* `volume` — a value between 0 and 1 * `volume` — a value between 0 and 1
* `muted` — a boolean value where `true` is muted * `muted` — a boolean value indicating whether the player is muted
Videos additionally have readonly `videoWidth` and `videoHeight` bindings. Videos additionally have readonly `videoWidth` and `videoHeight` bindings.
@ -1249,7 +1250,7 @@ As with DOM events, if the `on:` directive is used without a value, the componen
--- ---
As of [Svelte 3.38](https://github.com/sveltejs/svelte/issues/6268) ([RFC](https://github.com/sveltejs/rfcs/pull/13)), you can pass styles as props to components for the purposes of theming, using CSS custom properties. You can also pass styles as props to components for the purposes of theming, using CSS custom properties.
Svelte's implementation is essentially syntactic sugar for adding a wrapper element. This example: Svelte's implementation is essentially syntactic sugar for adding a wrapper element. This example:
@ -1276,7 +1277,7 @@ Desugars to this:
</div> </div>
``` ```
**Note**: Since this is an extra div, beware that your CSS structure might accidentally target this. Be mindful of this added wrapper element when using this feature. Also note that not all browsers support `display: contents`: https://caniuse.com/css-display-contents **Note**: Since this is an extra `<div>`, beware that your CSS structure might accidentally target this. Be mindful of this added wrapper element when using this feature.
--- ---
@ -1548,7 +1549,7 @@ If `this` is falsy, no component is rendered.
The `<svelte:window>` element allows you to add event listeners to the `window` object without worrying about removing them when the component is destroyed, or checking for the existence of `window` when server-side rendering. The `<svelte:window>` element allows you to add event listeners to the `window` object without worrying about removing them when the component is destroyed, or checking for the existence of `window` when server-side rendering.
Contrary to `<svelte:self>` this element can only be at the top level of your component and must never be inside a block or element. Unlike `<svelte:self>`, this element may only appear the top level of your component and must never be inside a block or element.
```sv ```sv
<script> <script>
@ -1587,12 +1588,15 @@ All except `scrollX` and `scrollY` are readonly.
--- ---
As with `<svelte:window>`, this element allows you to add listeners to events on `document.body`, such as `mouseenter` and `mouseleave` which don't fire on `window`; and it has to appear at the top level of your component. Similarly to `<svelte:window>`, this element allows you to add listeners to events on `document.body`, such as `mouseenter` and `mouseleave`, which don't fire on `window`. It also lets you use [actions](docs#use_action) on the `<body>` element.
`<svelte:body>` also has to appear at the top level of your component.
```sv ```sv
<svelte:body <svelte:body
on:mouseenter={handleMouseenter} on:mouseenter={handleMouseenter}
on:mouseleave={handleMouseleave} on:mouseleave={handleMouseleave}
use:someAction
/> />
``` ```
@ -1607,7 +1611,7 @@ As with `<svelte:window>`, this element allows you to add listeners to events on
This element makes it possible to insert elements into `document.head`. During server-side rendering, `head` content is exposed separately to the main `html` content. This element makes it possible to insert elements into `document.head`. During server-side rendering, `head` content is exposed separately to the main `html` content.
As with `<svelte:window>` and `<svelte:head>` this element has to appear at the top level of your component and cannot be inside a block or other element. As with `<svelte:window>` and `<svelte:body>`, this element has to appear at the top level of your component and cannot be inside a block or other element.
```sv ```sv
<svelte:head> <svelte:head>

@ -6,7 +6,7 @@ Typically, you won't interact with the Svelte compiler directly, but will instea
* [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) for users of [Rollup](https://rollupjs.org) * [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) for users of [Rollup](https://rollupjs.org)
* [svelte-loader](https://github.com/sveltejs/svelte-loader) for users of [webpack](https://webpack.js.org) * [svelte-loader](https://github.com/sveltejs/svelte-loader) for users of [webpack](https://webpack.js.org)
* or one of the [community-maintained plugins](https://github.com/sveltejs/integrations#bundler-plugins) * or one of the [community-maintained plugins](https://sveltesociety.dev/tooling)
Nonetheless, it's useful to understand how to use the compiler, since bundler plugins generally expose compiler options to you. Nonetheless, it's useful to understand how to use the compiler, since bundler plugins generally expose compiler options to you.
@ -68,7 +68,7 @@ The following options can be passed to the compiler. None are required:
| `name` | `"Component"` | `string` that sets the name of the resulting JavaScript class (though the compiler will rename it if it would otherwise conflict with other variables in scope). It will normally be inferred from `filename`. | `name` | `"Component"` | `string` that sets the name of the resulting JavaScript class (though the compiler will rename it if it would otherwise conflict with other variables in scope). It will normally be inferred from `filename`.
| `format` | `"esm"` | If `"esm"`, creates a JavaScript module (with `import` and `export`). If `"cjs"`, creates a CommonJS module (with `require` and `module.exports`), which is useful in some server-side rendering situations or for testing. | `format` | `"esm"` | If `"esm"`, creates a JavaScript module (with `import` and `export`). If `"cjs"`, creates a CommonJS module (with `require` and `module.exports`), which is useful in some server-side rendering situations or for testing.
| `generate` | `"dom"` | If `"dom"`, Svelte emits a JavaScript class for mounting to the DOM. If `"ssr"`, Svelte emits an object with a `render` method suitable for server-side rendering. If `false`, no JavaScript or CSS is returned; just metadata. | `generate` | `"dom"` | If `"dom"`, Svelte emits a JavaScript class for mounting to the DOM. If `"ssr"`, Svelte emits an object with a `render` method suitable for server-side rendering. If `false`, no JavaScript or CSS is returned; just metadata.
| `errorMode` | `"throw"` | If `"throw"`, Svelte throws when a compilation error occured. If `"warn"`, Svelte will treat errors as warnings and add them to the warning report. | `errorMode` | `"throw"` | If `"throw"`, Svelte throws when a compilation error occurred. If `"warn"`, Svelte will treat errors as warnings and add them to the warning report.
| `varsReport` | `"strict"` | If `"strict"`, Svelte returns a variables report with only variables that are not globals nor internals. If `"full"`, Svelte returns a variables report with all detected variables. If `false`, no variables report is returned. | `varsReport` | `"strict"` | If `"strict"`, Svelte returns a variables report with only variables that are not globals nor internals. If `"full"`, Svelte returns a variables report with all detected variables. If `false`, no variables report is returned.
| `dev` | `false` | If `true`, causes extra code to be added to components that will perform runtime checks and provide debugging information during development. | `dev` | `false` | If `true`, causes extra code to be added to components that will perform runtime checks and provide debugging information during development.
| `immutable` | `false` | If `true`, tells the compiler that you promise not to mutate any objects. This allows it to be less conservative about checking whether values have changed. | `immutable` | `false` | If `true`, tells the compiler that you promise not to mutate any objects. This allows it to be less conservative about checking whether values have changed.

@ -1,4 +1,4 @@
import emotion from 'emotion/dist/emotion.umd.min.js'; import emotion from '@emotion/css@11.1.3/dist/emotion-css.umd.min.js';
const { css } = emotion; const { css } = emotion;

@ -10,7 +10,7 @@ First, you'll need to integrate Svelte with a build tool. There are officially m
* [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) * [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte)
* [svelte-loader](https://github.com/sveltejs/svelte-loader) * [svelte-loader](https://github.com/sveltejs/svelte-loader)
...and a variety of [community-maintained ones](https://github.com/sveltejs/integrations#bundler-plugins). ...and a variety of [community-maintained ones](https://sveltesociety.dev/tooling).
Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process. Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process.

@ -38,4 +38,4 @@ const foo = obj.foo;
foo.bar = 'baz'; foo.bar = 'baz';
``` ```
...won't update references to `obj.foo.bar`, unless you follow it up with `obj = obj`. ...won't trigger reactivity on `obj.foo.bar`, unless you follow it up with `obj = obj`.

@ -25,5 +25,6 @@ The full list of modifiers:
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase ([MDN docs](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture)) * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase ([MDN docs](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture))
* `once` — remove the handler after the first time it runs * `once` — remove the handler after the first time it runs
* `self` — only trigger handler if event.target is the element itself * `self` — only trigger handler if event.target is the element itself
* `trusted` — only trigger handler if `event.isTrusted` is `true`. I.e. if the event is triggered by a user action.
You can chain modifiers together, e.g. `on:click|once|capture={...}`. You can chain modifiers together, e.g. `on:click|once|capture={...}`.

@ -18,4 +18,4 @@ Returning to our [earlier ice cream example](tutorial/group-inputs), we can repl
</select> </select>
``` ```
> Press and hold the `shift` key for selecting multiple options. > Press and hold the `control` key (or the `command` key on MacOS) for selecting multiple options.

@ -45,5 +45,6 @@
span { span {
position: absolute; position: absolute;
font-size: 5vw; font-size: 5vw;
user-select: none;
} }
</style> </style>

@ -2917,9 +2917,9 @@
"dev": true "dev": true
}, },
"path-parse": { "path-parse": {
"version": "1.0.6", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true "dev": true
}, },
"pg": { "pg": {

@ -1,10 +1,30 @@
<script> <script>
import { companies } from './WhosUsingSvelte.js'; import { companies } from './WhosUsingSvelte.js';
const randomizer = ({prominent}) => Math.random(); const randomizer = ({ prominent }) => Math.random();
const doSort = (a, b) => randomizer(b) - randomizer(a); const doSort = (a, b) => randomizer(b) - randomizer(a);
const sortedCompanies = companies.sort(doSort); const sortedCompanies = companies.sort(doSort);
</script> </script>
<div class="logos">
{#each sortedCompanies as { href, filename, alt, style, picture, span }, index}
<a target="_blank" rel="noopener" {href} style={style || ""}>
{#if picture}
<picture>
{#each picture as { type, srcset }}
<source {type} {srcset} />
{/each}
<img src="/whos-using-svelte/{filename}" {alt} loading="lazy" />
</picture>
{:else}
<img src="/whos-using-svelte/{filename}" {alt} loading="lazy" />
{#if span}
<span>{span}</span>
{/if}
{/if}
</a>
{/each}
</div>
<style> <style>
.logos { .logos {
margin: 1em 0 0 0; margin: 1em 0 0 0;
@ -17,44 +37,29 @@
display: flex; display: flex;
align-items: center; align-items: center;
border: 2px solid var(--second); border: 2px solid var(--second);
padding: 5px 10px; padding: 0;
border-radius: 20px; border-radius: 20px;
color: var(--text); color: var(--text);
} }
picture, picture,
img { img {
height: 100%; height: 100%;
padding: 5px 10px;
transition: transform 0.2s;
}
picture:hover,
img:hover {
transform: scale(1.2);
} }
@media (min-width: 540px) { @media (min-width: 540px) {
a { a {
height: 60px; height: 60px;
padding: 10px 20px;
border-radius: 30px; border-radius: 30px;
} }
picture,
img {
padding: 10px 20px;
}
} }
</style> </style>
<div class="logos">
{#each sortedCompanies as {href, filename, alt, style, picture, span}, index}
<a
target="_blank"
rel="noopener"
{href}
style="{style || ''}"
>
{#if picture}
<picture>
{#each picture as {type, srcset}}
<source {type} {srcset}>
{/each}
<img src="/whos-using-svelte/{filename}" {alt} loading="lazy">
</picture>
{:else}
<img src="/whos-using-svelte/{filename}" {alt} loading="lazy">
{#if span}
<span>{span}</span>
{/if}
{/if}
</a>
{/each}
</div>

@ -24,10 +24,10 @@ import TemplateScope from './nodes/shared/TemplateScope';
import fuzzymatch from '../utils/fuzzymatch'; import fuzzymatch from '../utils/fuzzymatch';
import get_object from './utils/get_object'; import get_object from './utils/get_object';
import Slot from './nodes/Slot'; import Slot from './nodes/Slot';
import { Node, ImportDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal } from 'estree'; import { Node, ImportDeclaration, ExportNamedDeclaration, Identifier, ExpressionStatement, AssignmentExpression, Literal, Property, RestElement, ExportDefaultDeclaration, ExportAllDeclaration } from 'estree';
import add_to_set from './utils/add_to_set'; import add_to_set from './utils/add_to_set';
import check_graph_for_cycles from './utils/check_graph_for_cycles'; import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x, b } from 'code-red'; import { print, b } from 'code-red';
import { is_reserved_keyword } from './utils/reserved_keywords'; import { is_reserved_keyword } from './utils/reserved_keywords';
import { apply_preprocessor_sourcemap } from '../utils/mapped_code'; import { apply_preprocessor_sourcemap } from '../utils/mapped_code';
import Element from './nodes/Element'; import Element from './nodes/Element';
@ -70,6 +70,8 @@ export default class Component {
var_lookup: Map<string, Var> = new Map(); var_lookup: Map<string, Var> = new Map();
imports: ImportDeclaration[] = []; imports: ImportDeclaration[] = [];
exports_from: ExportNamedDeclaration[] = [];
instance_exports_from: ExportNamedDeclaration[] = [];
hoistable_nodes: Set<Node> = new Set(); hoistable_nodes: Set<Node> = new Set();
node_for_declaration: Map<string, Node> = new Map(); node_for_declaration: Map<string, Node> = new Map();
@ -333,26 +335,29 @@ export default class Component {
.map(variable => ({ .map(variable => ({
name: variable.name, name: variable.name,
as: variable.export_name as: variable.export_name
})) })),
this.exports_from
); );
css = compile_options.customElement css = compile_options.customElement
? { code: null, map: null } ? { code: null, map: null }
: result.css; : result.css;
const sourcemap_source_filename = get_sourcemap_source_filename(compile_options);
js = print(program, { js = print(program, {
sourceMapSource: compile_options.filename sourceMapSource: sourcemap_source_filename
}); });
js.map.sources = [ js.map.sources = [
compile_options.filename ? get_relative_path(compile_options.outputFilename || '', compile_options.filename) : null sourcemap_source_filename
]; ];
js.map.sourcesContent = [ js.map.sourcesContent = [
this.source this.source
]; ];
js.map = apply_preprocessor_sourcemap(this.file, js.map, compile_options.sourcemap as (string | RawSourceMap | DecodedSourceMap)); js.map = apply_preprocessor_sourcemap(sourcemap_source_filename, js.map, compile_options.sourcemap as (string | RawSourceMap | DecodedSourceMap));
} }
return { return {
@ -492,22 +497,27 @@ export default class Component {
this.imports.push(node); this.imports.push(node);
} }
extract_exports(node) { extract_exports(node, module_script = false) {
const ignores = extract_svelte_ignore_from_comments(node); const ignores = extract_svelte_ignore_from_comments(node);
if (ignores.length) this.push_ignores(ignores); if (ignores.length) this.push_ignores(ignores);
const result = this._extract_exports(node); const result = this._extract_exports(node, module_script);
if (ignores.length) this.pop_ignores(); if (ignores.length) this.pop_ignores();
return result; return result;
} }
private _extract_exports(node) { private _extract_exports(node: ExportDefaultDeclaration | ExportNamedDeclaration | ExportAllDeclaration, module_script) {
if (node.type === 'ExportDefaultDeclaration') { if (node.type === 'ExportDefaultDeclaration') {
return this.error(node, compiler_errors.default_export); return this.error(node as any, compiler_errors.default_export);
} }
if (node.type === 'ExportNamedDeclaration') { if (node.type === 'ExportNamedDeclaration') {
if (node.source) { if (node.source) {
return this.error(node, compiler_errors.not_implemented); if (module_script) {
this.exports_from.push(node);
} else {
this.instance_exports_from.push(node);
}
return null;
} }
if (node.declaration) { if (node.declaration) {
if (node.declaration.type === 'VariableDeclaration') { if (node.declaration.type === 'VariableDeclaration') {
@ -516,7 +526,7 @@ export default class Component {
const variable = this.var_lookup.get(name); const variable = this.var_lookup.get(name);
variable.export_name = name; variable.export_name = name;
if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) { if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(declarator, compiler_warnings.unused_export_let(this.name.name, name)); this.warn(declarator as any, compiler_warnings.unused_export_let(this.name.name, name));
} }
}); });
}); });
@ -536,7 +546,7 @@ export default class Component {
variable.export_name = specifier.exported.name; variable.export_name = specifier.exported.name;
if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) { if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(specifier, compiler_warnings.unused_export_let(this.name.name, specifier.exported.name)); this.warn(specifier as any, compiler_warnings.unused_export_let(this.name.name, specifier.exported.name));
} }
} }
}); });
@ -612,7 +622,7 @@ export default class Component {
} }
if (/^Export/.test(node.type)) { if (/^Export/.test(node.type)) {
const replacement = this.extract_exports(node); const replacement = this.extract_exports(node, true);
if (replacement) { if (replacement) {
body[i] = replacement; body[i] = replacement;
} else { } else {
@ -935,7 +945,7 @@ export default class Component {
let scope = instance_scope; let scope = instance_scope;
walk(this.ast.instance.content, { walk(this.ast.instance.content, {
enter(node: Node, parent, key, index) { enter(node: Node) {
if (/Function/.test(node.type)) { if (/Function/.test(node.type)) {
return this.skip(); return this.skip();
} }
@ -944,75 +954,130 @@ export default class Component {
scope = map.get(node); scope = map.get(node);
} }
if (node.type === 'ExportNamedDeclaration' && node.declaration) {
return this.replace(node.declaration);
}
if (node.type === 'VariableDeclaration') { if (node.type === 'VariableDeclaration') {
// NOTE: `var` does not follow block scoping
if (node.kind === 'var' || scope === instance_scope) { if (node.kind === 'var' || scope === instance_scope) {
node.declarations.forEach(declarator => {
if (declarator.id.type !== 'Identifier') {
const inserts = []; const inserts = [];
const props = [];
extract_names(declarator.id).forEach(name => { function add_new_props(exported, local, default_value) {
const variable = component.var_lookup.get(name); props.push({
type: 'Property',
if (variable.export_name) { method: false,
// TODO is this still true post-#3539? shorthand: false,
return component.error(declarator as any, compiler_errors.destructured_prop); computed: false,
kind: 'init',
key: exported,
value: default_value
? {
type: 'AssignmentPattern',
left: local,
right: default_value
}
: local
});
} }
// transform
// ```
// export let { x, y = 123 } = OBJ, z = 456
// ```
// into
// ```
// let { x: x$, y: y$ = 123 } = OBJ;
// let { x = x$, y = y$, z = 456 } = $$props;
// ```
for (let index = 0; index < node.declarations.length; index++) {
const declarator = node.declarations[index];
if (declarator.id.type !== 'Identifier') {
function get_new_name(local) {
const variable = component.var_lookup.get(local.name);
if (variable.subscribable) { if (variable.subscribable) {
inserts.push(get_insert(variable)); inserts.push(get_insert(variable));
} }
});
if (inserts.length) { if (variable.export_name && variable.writable) {
parent[key].splice(index + 1, 0, ...inserts); const alias_name = component.get_unique_name(local.name);
add_new_props({ type: 'Identifier', name: variable.export_name }, local, alias_name);
return alias_name;
} }
return local;
return;
} }
const { name } = declarator.id; function rename_identifiers(param: Node) {
const variable = component.var_lookup.get(name); switch (param.type) {
case 'ObjectPattern': {
const handle_prop = (prop: Property | RestElement) => {
if (prop.type === 'RestElement') {
rename_identifiers(prop);
} else if (prop.value.type === 'Identifier') {
prop.value = get_new_name(prop.value);
} else {
rename_identifiers(prop.value);
}
};
if (variable.export_name && variable.writable) { param.properties.forEach(handle_prop);
declarator.id = { break;
type: 'ObjectPattern', }
properties: [{ case 'ArrayPattern': {
type: 'Property', const handle_element = (element: Node, index: number, array: Node[]) => {
method: false, if (element) {
shorthand: false, if (element.type === 'Identifier') {
computed: false, array[index] = get_new_name(element);
kind: 'init', } else {
key: { type: 'Identifier', name: variable.export_name }, rename_identifiers(element);
value: declarator.init }
? {
type: 'AssignmentPattern',
left: declarator.id,
right: declarator.init
} }
: declarator.id
}]
}; };
declarator.init = x`$$props`; param.elements.forEach(handle_element);
break;
} }
if (variable.subscribable && declarator.init) { case 'RestElement':
const insert = get_insert(variable); param.argument = get_new_name(param.argument);
parent[key].splice(index + 1, 0, ...insert); break;
case 'AssignmentPattern':
param.left = get_new_name(param.left);
break;
} }
}); }
rename_identifiers(declarator.id);
} else {
const { name } = declarator.id;
const variable = component.var_lookup.get(name);
const is_props = variable.export_name && variable.writable;
if (is_props) {
add_new_props({ type: 'Identifier', name: variable.export_name }, declarator.id, declarator.init);
node.declarations.splice(index--, 1);
}
if (variable.subscribable && (is_props || declarator.init)) {
inserts.push(get_insert(variable));
}
}
}
this.replace(b`
${node.declarations.length ? node : null}
${ props.length > 0 && b`let { ${ props } } = $$props;`}
${inserts}
` as any);
return this.skip();
} }
} }
}, },
leave(node: Node, parent, _key, index) { leave(node: Node) {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
if (node.type === 'ExportNamedDeclaration' && node.declaration) {
(parent as Program).body[index] = node.declaration;
}
} }
}); });
} }
@ -1247,7 +1312,7 @@ export default class Component {
if (variable) { if (variable) {
variable.is_reactive_dependency = true; variable.is_reactive_dependency = true;
if (variable.module) { if (variable.module && variable.writable) {
should_add_as_dependency = false; should_add_as_dependency = false;
module_dependencies.add(name); module_dependencies.add(name);
} }
@ -1488,3 +1553,15 @@ function get_relative_path(from: string, to: string) {
return from_parts.concat(to_parts).join('/'); return from_parts.concat(to_parts).join('/');
} }
function get_basename(filename: string) {
return filename.split(/[/\\]/).pop();
}
function get_sourcemap_source_filename(compile_options: CompileOptions) {
if (!compile_options.filename) return null;
return compile_options.outputFilename
? get_relative_path(compile_options.outputFilename, compile_options.filename)
: get_basename(compile_options.filename);
}

@ -174,10 +174,6 @@ export default {
code: 'default-export', code: 'default-export',
message: 'A component cannot have a default export' message: 'A component cannot have a default export'
}, },
not_implemented: {
code: 'not-implemented',
message: 'A component currently cannot have an export ... from'
},
illegal_declaration: { illegal_declaration: {
code: 'illegal-declaration', code: 'illegal-declaration',
message: 'The $ prefix is reserved, and cannot be used for variable and import names' message: 'The $ prefix is reserved, and cannot be used for variable and import names'
@ -190,10 +186,6 @@ export default {
code: 'illegal-global', code: 'illegal-global',
message: `${name} is an illegal variable name` message: `${name} is an illegal variable name`
}), }),
destructured_prop: {
code: 'destructured-prop',
message: 'Cannot declare props in destructured declaration'
},
cyclical_reactive_declaration: (cycle: string[]) => ({ cyclical_reactive_declaration: (cycle: string[]) => ({
code: 'cyclical-reactive-declaration', code: 'cyclical-reactive-declaration',
message: `Cyclical dependency detected: ${cycle.join(' → ')}` message: `Cyclical dependency detected: ${cycle.join(' → ')}`

@ -1,7 +1,7 @@
import list from '../utils/list'; import list from '../utils/list';
import { ModuleFormat } from '../interfaces'; import { ModuleFormat } from '../interfaces';
import { b, x } from 'code-red'; import { b, x } from 'code-red';
import { Identifier, ImportDeclaration } from 'estree'; import { Identifier, ImportDeclaration, ExportNamedDeclaration } from 'estree';
const wrappers = { esm, cjs }; const wrappers = { esm, cjs };
@ -19,20 +19,21 @@ export default function create_module(
helpers: Array<{ name: string; alias: Identifier }>, helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>, globals: Array<{ name: string; alias: Identifier }>,
imports: ImportDeclaration[], imports: ImportDeclaration[],
module_exports: Export[] module_exports: Export[],
exports_from: ExportNamedDeclaration[]
) { ) {
const internal_path = `${sveltePath}/internal`; const internal_path = `${sveltePath}/internal`;
helpers.sort((a, b) => (a.name < b.name) ? -1 : 1); helpers.sort((a, b) => (a.name < b.name) ? -1 : 1);
globals.sort((a, b) => (a.name < b.name) ? -1 : 1); globals.sort((a, b) => (a.name < b.name) ? -1 : 1);
if (format === 'esm') { const formatter = wrappers[format];
return esm(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports);
}
if (format === 'cjs') return cjs(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports);
if (!formatter) {
throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`); throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`);
}
return formatter(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports, exports_from);
} }
function edit_source(source, sveltePath) { function edit_source(source, sveltePath) {
@ -76,7 +77,8 @@ function esm(
helpers: Array<{ name: string; alias: Identifier }>, helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>, globals: Array<{ name: string; alias: Identifier }>,
imports: ImportDeclaration[], imports: ImportDeclaration[],
module_exports: Export[] module_exports: Export[],
exports_from: ExportNamedDeclaration[]
) { ) {
const import_declaration = { const import_declaration = {
type: 'ImportDeclaration', type: 'ImportDeclaration',
@ -94,6 +96,9 @@ function esm(
imports.forEach(node => { imports.forEach(node => {
node.source.value = edit_source(node.source.value, sveltePath); node.source.value = edit_source(node.source.value, sveltePath);
}); });
exports_from.forEach(node => {
node.source!.value = edit_source(node.source!.value, sveltePath);
});
const exports = module_exports.length > 0 && { const exports = module_exports.length > 0 && {
type: 'ExportNamedDeclaration', type: 'ExportNamedDeclaration',
@ -110,6 +115,7 @@ function esm(
${import_declaration} ${import_declaration}
${internal_globals} ${internal_globals}
${imports} ${imports}
${exports_from}
${program.body} ${program.body}
@ -127,7 +133,8 @@ function cjs(
helpers: Array<{ name: string; alias: Identifier }>, helpers: Array<{ name: string; alias: Identifier }>,
globals: Array<{ name: string; alias: Identifier }>, globals: Array<{ name: string; alias: Identifier }>,
imports: ImportDeclaration[], imports: ImportDeclaration[],
module_exports: Export[] module_exports: Export[],
exports_from: ExportNamedDeclaration[]
) { ) {
const internal_requires = { const internal_requires = {
type: 'VariableDeclaration', type: 'VariableDeclaration',
@ -183,6 +190,13 @@ function cjs(
const exports = module_exports.map(x => b`exports.${{ type: 'Identifier', name: x.as }} = ${{ type: 'Identifier', name: x.name }};`); const exports = module_exports.map(x => b`exports.${{ type: 'Identifier', name: x.as }} = ${{ type: 'Identifier', name: x.name }};`);
const user_exports_from = exports_from.map(node => {
const init = x`require("${edit_source(node.source.value, sveltePath)}")`;
return node.specifiers.map(specifier => {
return b`exports.${specifier.exported} = ${init}.${specifier.local};`;
});
});
program.body = b` program.body = b`
/* ${banner} */ /* ${banner} */
@ -190,6 +204,7 @@ function cjs(
${internal_requires} ${internal_requires}
${internal_globals} ${internal_globals}
${user_requires} ${user_requires}
${user_exports_from}
${program.body} ${program.body}

@ -227,7 +227,8 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: Array<{
return false; return false;
} else if (block.combinator.name === '>') { } else if (block.combinator.name === '>') {
if (apply_selector(blocks, get_element_parent(node), to_encapsulate)) { const has_global_parent = blocks.every(block => block.global);
if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
to_encapsulate.push({ node, block }); to_encapsulate.push({ node, block });
return true; return true;
} }

@ -1,21 +1,23 @@
import Node from './shared/Node'; import Node from './shared/Node';
import EventHandler from './EventHandler'; import EventHandler from './EventHandler';
import Action from './Action';
import Component from '../Component'; import Component from '../Component';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces'; import { Element } from '../../interfaces';
export default class Body extends Node { export default class Body extends Node {
type: 'Body'; type: 'Body';
handlers: EventHandler[]; handlers: EventHandler[] = [];
actions: Action[] = [];
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { constructor(component: Component, parent: Node, scope: TemplateScope, info: Element) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.handlers = []; info.attributes.forEach((node) => {
info.attributes.forEach((node: Node) => {
if (node.type === 'EventHandler') { if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node)); this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else { } else {
// TODO there shouldn't be anything else here... // TODO there shouldn't be anything else here...
} }

@ -96,6 +96,8 @@ const react_attributes = new Map([
['htmlFor', 'for'] ['htmlFor', 'for']
]); ]);
const attributes_to_compact_whitespace = ['class', 'style'];
function get_namespace(parent: Element, element: Element, explicit_namespace: string) { function get_namespace(parent: Element, element: Element, explicit_namespace: string) {
const parent_element = parent.find_nearest(/^Element/); const parent_element = parent.find_nearest(/^Element/);
@ -241,6 +243,8 @@ export default class Element extends Node {
this.validate(); this.validate();
this.optimise();
component.apply_stylesheet(this); component.apply_stylesheet(this);
} }
@ -750,6 +754,25 @@ export default class Element extends Node {
get slot_template_name() { get slot_template_name() {
return this.attributes.find(attribute => attribute.name === 'slot').get_static_value() as string; return this.attributes.find(attribute => attribute.name === 'slot').get_static_value() as string;
} }
optimise() {
attributes_to_compact_whitespace.forEach(attribute_name => {
const attribute = this.attributes.find(a => a.name === attribute_name);
if (attribute && !attribute.is_true) {
attribute.chunks.forEach((chunk, index) => {
if (chunk.type === 'Text') {
let data = chunk.data.replace(/[\s\n\t]+/g, ' ');
if (index === 0) {
data = data.trimLeft();
} else if (index === attribute.chunks.length - 1) {
data = data.trimRight();
}
chunk.data = data;
}
});
}
});
}
} }
function should_have_attribute( function should_have_attribute(

@ -6,7 +6,7 @@ import { walk } from 'estree-walker';
import { extract_names, Scope } from 'periscopic'; import { extract_names, Scope } from 'periscopic';
import { invalidate } from './invalidate'; import { invalidate } from './invalidate';
import Block from './Block'; import Block from './Block';
import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree'; import { ImportDeclaration, ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree';
import { apply_preprocessor_sourcemap } from '../../utils/mapped_code'; import { apply_preprocessor_sourcemap } from '../../utils/mapped_code';
import { RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types'; import { RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types';
import { flatten } from '../../utils/flatten'; import { flatten } from '../../utils/flatten';
@ -174,6 +174,46 @@ export default function dom(
} }
}); });
component.instance_exports_from.forEach(exports_from => {
const import_declaration = {
...exports_from,
type: 'ImportDeclaration',
specifiers: [],
source: exports_from.source
};
component.imports.push(import_declaration as ImportDeclaration);
exports_from.specifiers.forEach(specifier => {
if (component.component_options.accessors) {
const name = component.get_unique_name(specifier.exported.name);
import_declaration.specifiers.push({
...specifier,
type: 'ImportSpecifier',
imported: specifier.local,
local: name
});
accessors.push({
type: 'MethodDefinition',
kind: 'get',
key: { type: 'Identifier', name: specifier.exported.name },
value: x`function() {
return ${name}
}`
});
} else if (component.compile_options.dev) {
accessors.push({
type: 'MethodDefinition',
kind: 'get',
key: { type: 'Identifier', name: specifier.exported.name },
value: x`function() {
throw new @_Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'");
}`
});
}
});
});
if (component.compile_options.dev) { if (component.compile_options.dev) {
// checking that expected ones were passed // checking that expected ones were passed
const expected = props.filter(prop => prop.writable && !prop.initialised); const expected = props.filter(prop => prop.writable && !prop.initialised);

@ -7,6 +7,7 @@ import EventHandler from './Element/EventHandler';
import add_event_handlers from './shared/add_event_handlers'; import add_event_handlers from './shared/add_event_handlers';
import { TemplateNode } from '../../../interfaces'; import { TemplateNode } from '../../../interfaces';
import Renderer from '../Renderer'; import Renderer from '../Renderer';
import add_actions from './shared/add_actions';
export default class BodyWrapper extends Wrapper { export default class BodyWrapper extends Wrapper {
node: Body; node: Body;
@ -19,5 +20,6 @@ export default class BodyWrapper extends Wrapper {
render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) {
add_event_handlers(block, x`@_document.body`, this.handlers); add_event_handlers(block, x`@_document.body`, this.handlers);
add_actions(block, x`@_document.body`, this.node.actions);
} }
} }

@ -388,9 +388,11 @@ export default class ElementWrapper extends Wrapper {
? this.node.name ? this.node.name
: this.node.name.toUpperCase(); : this.node.name.toUpperCase();
const svg = this.node.namespace === namespaces.svg ? 1 : null; if (this.node.namespace === namespaces.svg) {
return x`@claim_svg_element(${nodes}, "${name}", { ${attributes} })`;
return x`@claim_element(${nodes}, "${name}", { ${attributes} }, ${svg})`; } else {
return x`@claim_element(${nodes}, "${name}", { ${attributes} })`;
}
} }
add_directives_in_order (block: Block) { add_directives_in_order (block: Block) {
@ -760,7 +762,7 @@ export default class ElementWrapper extends Wrapper {
intro_block = b` intro_block = b`
@add_render_callback(() => { @add_render_callback(() => {
if (${outro_name}) ${outro_name}.end(1); if (${outro_name}) ${outro_name}.end(1);
if (!${intro_name}) ${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet}); ${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet});
${intro_name}.start(); ${intro_name}.start();
}); });
`; `;

@ -107,7 +107,7 @@ export default class SlotWrapper extends Wrapper {
if (spread_dynamic_dependencies.size) { if (spread_dynamic_dependencies.size) {
get_slot_spread_changes_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_spread_changes`); get_slot_spread_changes_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_spread_changes`);
renderer.blocks.push(b` renderer.blocks.push(b`
const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(Array.from(spread_dynamic_dependencies))} > 0 ? -1 : 0; const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(Array.from(spread_dynamic_dependencies))};
`); `);
} }
} else { } else {
@ -168,27 +168,41 @@ export default class SlotWrapper extends Wrapper {
if (block.has_outros) { if (block.has_outros) {
condition = x`!#current || ${condition}`; condition = x`!#current || ${condition}`;
} }
let dirty = x`#dirty`;
if (block.has_outros) {
dirty = x`!#current ? ${renderer.get_initial_dirty()} : ${dirty}`;
}
const slot_update = get_slot_spread_changes_fn ? b` // conditions to treat everything as dirty
const all_dirty_conditions = [
get_slot_spread_changes_fn ? x`${get_slot_spread_changes_fn}(#dirty)` : null,
block.has_outros ? x`!#current` : null
].filter(Boolean);
const all_dirty_condition = all_dirty_conditions.length ? all_dirty_conditions.reduce((condition1, condition2) => x`${condition1} || ${condition2}`) : null;
let slot_update;
if (all_dirty_condition) {
const dirty = x`${all_dirty_condition} ? @get_all_dirty_from_scope(${renderer.reference('$$scope')}) : @get_slot_changes(${slot_definition}, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn})`;
slot_update = b`
if (${slot}.p && ${condition}) { if (${slot}.p && ${condition}) {
@update_slot_spread(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_changes_fn}, ${get_slot_spread_changes_fn}, ${get_slot_context_fn}); @update_slot_base(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_context_fn});
} }
` : b` `;
} else {
slot_update = b`
if (${slot}.p && ${condition}) { if (${slot}.p && ${condition}) {
@update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_changes_fn}, ${get_slot_context_fn}); @update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_context_fn});
} }
`; `;
}
let fallback_condition = renderer.dirty(fallback_dynamic_dependencies); let fallback_condition = renderer.dirty(fallback_dynamic_dependencies);
let fallback_dirty = x`#dirty`;
if (block.has_outros) { if (block.has_outros) {
fallback_condition = x`!#current || ${fallback_condition}`; fallback_condition = x`!#current || ${fallback_condition}`;
fallback_dirty = x`!#current ? ${renderer.get_initial_dirty()} : ${fallback_dirty}`;
} }
const fallback_update = has_fallback && fallback_dynamic_dependencies.length > 0 && b` const fallback_update = has_fallback && fallback_dynamic_dependencies.length > 0 && b`
if (${slot_or_fallback} && ${slot_or_fallback}.p && ${fallback_condition}) { if (${slot_or_fallback} && ${slot_or_fallback}.p && ${fallback_condition}) {
${slot_or_fallback}.p(#ctx, ${dirty}); ${slot_or_fallback}.p(#ctx, ${fallback_dirty});
} }
`; `;

@ -1,17 +1,18 @@
import { b, x } from 'code-red'; import { b, x } from 'code-red';
import Block from '../../Block'; import Block from '../../Block';
import Action from '../../../nodes/Action'; import Action from '../../../nodes/Action';
import { Expression } from 'estree';
import is_contextual from '../../../nodes/shared/is_contextual'; import is_contextual from '../../../nodes/shared/is_contextual';
export default function add_actions( export default function add_actions(
block: Block, block: Block,
target: string, target: string | Expression,
actions: Action[] actions: Action[]
) { ) {
actions.forEach(action => add_action(block, target, action)); actions.forEach(action => add_action(block, target, action));
} }
export function add_action(block: Block, target: string, action: Action) { export function add_action(block: Block, target: string | Expression, action: Action) {
const { expression, template_scope } = action; const { expression, template_scope } = action;
let snippet; let snippet;
let dependencies; let dependencies;

@ -46,7 +46,24 @@ interface BaseDirective extends BaseNode {
modifiers: string[]; modifiers: string[];
} }
export interface Transition extends BaseDirective{ export interface Element extends BaseNode {
type: 'InlineComponent' | 'SlotTemplate' | 'Title' | 'Slot' | 'Element' | 'Head' | 'Options' | 'Window' | 'Body';
attributes: Array<BaseDirective | Attribute | SpreadAttribute>;
name: string;
}
export interface Attribute extends BaseNode {
type: 'Attribute';
name: string;
value: any[];
}
export interface SpreadAttribute extends BaseNode {
type: 'Spread';
expression: Node;
}
export interface Transition extends BaseDirective {
type: 'Transition'; type: 'Transition';
intro: boolean; intro: boolean;
outro: boolean; outro: boolean;
@ -57,6 +74,9 @@ export type Directive = BaseDirective | Transition;
export type TemplateNode = Text export type TemplateNode = Text
| MustacheTag | MustacheTag
| BaseNode | BaseNode
| Element
| Attribute
| SpreadAttribute
| Directive | Directive
| Transition | Transition
| Comment; | Comment;

@ -157,6 +157,10 @@ export default {
code: 'missing-component-definition', code: 'missing-component-definition',
message: '<svelte:component> must have a \'this\' attribute' message: '<svelte:component> must have a \'this\' attribute'
}, },
missing_attribute_value: {
code: 'missing-attribute-value',
message: 'Expected value for the attribute'
},
unclosed_script: { unclosed_script: {
code: 'unclosed-script', code: 'unclosed-script',
message: '<script> must have a closing tag' message: '<script> must have a closing tag'
@ -169,6 +173,10 @@ export default {
code: 'unclosed-comment', code: 'unclosed-comment',
message: 'comment was left open, expected -->' message: 'comment was left open, expected -->'
}, },
unclosed_attribute_value: (token: string) => ({
code: 'unclosed-attribute-value',
message: `Expected to close the attribute value with ${token}`
}),
unexpected_block_close: { unexpected_block_close: {
code: 'unexpected-block-close', code: 'unexpected-block-close',
message: 'Unexpected block closing tag' message: 'Unexpected block closing tag'

@ -290,17 +290,25 @@ export default function mustache(parser: Parser) {
const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then'); const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then');
if (await_block_shorthand) { if (await_block_shorthand) {
if (parser.match_regex(/\s*}/)) {
parser.allow_whitespace();
} else {
parser.require_whitespace(); parser.require_whitespace();
block.value = read_context(parser); block.value = read_context(parser);
parser.allow_whitespace(); parser.allow_whitespace();
} }
}
const await_block_catch_shorthand = !await_block_shorthand && type === 'AwaitBlock' && parser.eat('catch'); const await_block_catch_shorthand = !await_block_shorthand && type === 'AwaitBlock' && parser.eat('catch');
if (await_block_catch_shorthand) { if (await_block_catch_shorthand) {
if (parser.match_regex(/\s*}/)) {
parser.allow_whitespace();
} else {
parser.require_whitespace(); parser.require_whitespace();
block.error = read_context(parser); block.error = read_context(parser);
parser.allow_whitespace(); parser.allow_whitespace();
} }
}
parser.eat('}', true); parser.eat('}', true);

@ -438,7 +438,25 @@ function read_attribute_value(parser: Parser) {
/(\/>|[\s"'=<>`])/ /(\/>|[\s"'=<>`])/
); );
const value = read_sequence(parser, () => !!parser.match_regex(regex)); let value;
try {
value = read_sequence(parser, () => !!parser.match_regex(regex));
} catch (error) {
if (error.code === 'parse-error') {
// if the attribute value didn't close + self-closing tag
// eg: `<Component test={{a:1} />`
// acorn may throw a `Unterminated regular expression` because of `/>`
if (parser.template.slice(error.pos - 1, error.pos + 1) === '/>') {
parser.index = error.pos;
parser.error(parser_errors.unclosed_attribute_value(quote_mark || '}'));
}
}
throw error;
}
if (value.length === 0 && !quote_mark) {
parser.error(parser_errors.missing_attribute_value);
}
if (quote_mark) parser.index += 1; if (quote_mark) parser.index += 1;
return value; return value;

@ -24,6 +24,7 @@ export const globals = new Set([
'global', 'global',
'globalThis', 'globalThis',
'history', 'history',
'HTMLElement',
'Infinity', 'Infinity',
'InternalError', 'InternalError',
'Intl', 'Intl',
@ -36,8 +37,8 @@ export const globals = new Set([
'Math', 'Math',
'NaN', 'NaN',
'navigator', 'navigator',
'Number',
'Node', 'Node',
'Number',
'Object', 'Object',
'parseFloat', 'parseFloat',
'parseInt', 'parseInt',
@ -52,6 +53,7 @@ export const globals = new Set([
'setInterval', 'setInterval',
'setTimeout', 'setTimeout',
'String', 'String',
'SVGElement',
'SyntaxError', 'SyntaxError',
'TypeError', 'TypeError',
'undefined', 'undefined',

@ -16,27 +16,31 @@ interface FlipParams {
easing?: (t: number) => number; easing?: (t: number) => number;
} }
export function flip(node: Element, animation: { from: DOMRect; to: DOMRect }, params: FlipParams = {}): AnimationConfig { export function flip(node: Element, { from, to }: { from: DOMRect; to: DOMRect }, params: FlipParams = {}): AnimationConfig {
const style = getComputedStyle(node); const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform; const transform = style.transform === 'none' ? '' : style.transform;
const scaleX = animation.from.width / node.clientWidth;
const scaleY = animation.from.height / node.clientHeight;
const dx = (animation.from.left - animation.to.left) / scaleX; const [ox, oy] = style.transformOrigin.split(' ').map(parseFloat);
const dy = (animation.from.top - animation.to.top) / scaleY; const dx = (from.left + from.width * ox / to.width) - (to.left + ox);
const dy = (from.top + from.height * oy / to.height) - (to.top + oy);
const d = Math.sqrt(dx * dx + dy * dy);
const { const {
delay = 0, delay = 0,
duration = (d: number) => Math.sqrt(d) * 120, duration = (d) => Math.sqrt(d) * 120,
easing = cubicOut easing = cubicOut
} = params; } = params;
return { return {
delay, delay,
duration: is_function(duration) ? duration(d) : duration, duration: is_function(duration) ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
easing, easing,
css: (_t, u) => `transform: ${transform} translate(${u * dx}px, ${u * dy}px);` css: (t, u) => {
const x = u * dx;
const y = u * dy;
const sx = t + u * from.width / to.width;
const sy = t + u * from.height / to.height;
return `transform: ${transform} translate(${x}px, ${y}px) scale(${sx}, ${sy});`;
}
}; };
} }

@ -134,9 +134,9 @@ export function append_styles(
style_sheet_id: string, style_sheet_id: string,
styles: string styles: string
) { ) {
const append_styles_to = get_root_for_styles(target); const append_styles_to = get_root_for_style(target);
if (!append_styles_to?.getElementById(style_sheet_id)) { if (!append_styles_to.getElementById(style_sheet_id)) {
const style = element('style'); const style = element('style');
style.id = style_sheet_id; style.id = style_sheet_id;
style.textContent = styles; style.textContent = styles;
@ -144,20 +144,19 @@ export function append_styles(
} }
} }
export function get_root_for_node(node: Node) { export function get_root_for_style(node: Node): ShadowRoot | Document {
if (!node) return document; if (!node) return document;
return (node.getRootNode ? node.getRootNode() : node.ownerDocument); // check for getRootNode because IE is still supported const root = node.getRootNode ? node.getRootNode() : node.ownerDocument;
} if ((root as ShadowRoot).host) {
return root as ShadowRoot;
function get_root_for_styles(node: Node) { }
const root = get_root_for_node(node); return document;
return (root as ShadowRoot).host ? root as ShadowRoot : root as Document;
} }
export function append_empty_stylesheet(node: Node) { export function append_empty_stylesheet(node: Node) {
const style_element = element('style') as HTMLStyleElement; const style_element = element('style') as HTMLStyleElement;
append_stylesheet(get_root_for_styles(node), style_element); append_stylesheet(get_root_for_style(node), style_element);
return style_element; return style_element;
} }
@ -186,7 +185,7 @@ export function append_hydration(target: NodeEx, node: NodeEx) {
} else { } else {
target.actual_end_child = node.nextSibling; target.actual_end_child = node.nextSibling;
} }
} else if (node.parentNode !== target) { } else if (node.parentNode !== target || node.nextSibling !== null) {
target.appendChild(node); target.appendChild(node);
} }
} }
@ -433,7 +432,7 @@ function claim_node<R extends ChildNodeEx>(nodes: ChildNodeArray, predicate: (no
return resultNode; return resultNode;
} }
export function claim_element(nodes: ChildNodeArray, name: string, attributes: {[key: string]: boolean}, svg) { function claim_element_base(nodes: ChildNodeArray, name: string, attributes: { [key: string]: boolean }, create_element: (name: string) => Element | SVGElement) {
return claim_node<Element | SVGElement>( return claim_node<Element | SVGElement>(
nodes, nodes,
(node: ChildNode): node is Element | SVGElement => node.nodeName === name, (node: ChildNode): node is Element | SVGElement => node.nodeName === name,
@ -448,10 +447,18 @@ export function claim_element(nodes: ChildNodeArray, name: string, attributes: {
remove.forEach(v => node.removeAttribute(v)); remove.forEach(v => node.removeAttribute(v));
return undefined; return undefined;
}, },
() => svg ? svg_element(name as keyof SVGElementTagNameMap) : element(name as keyof HTMLElementTagNameMap) () => create_element(name)
); );
} }
export function claim_element(nodes: ChildNodeArray, name: string, attributes: { [key: string]: boolean }) {
return claim_element_base(nodes, name, attributes, element);
}
export function claim_svg_element(nodes: ChildNodeArray, name: string, attributes: { [key: string]: boolean }) {
return claim_element_base(nodes, name, attributes, svg_element);
}
export function claim_text(nodes: ChildNodeArray, data) { export function claim_text(nodes: ChildNodeArray, data) {
return claim_node<Text>( return claim_node<Text>(
nodes, nodes,
@ -535,6 +542,8 @@ export function select_option(select, value) {
return; return;
} }
} }
select.selectedIndex = -1; // no option should be selected
} }
export function select_options(select, value) { export function select_options(select, value) {

@ -1,4 +1,4 @@
import { append_empty_stylesheet, get_root_for_node } from './dom'; import { append_empty_stylesheet, get_root_for_style } from './dom';
import { raf } from './environment'; import { raf } from './environment';
interface ExtendedDoc extends Document { interface ExtendedDoc extends Document {
@ -29,7 +29,7 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b:
const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`;
const name = `__svelte_${hash(rule)}_${uid}`; const name = `__svelte_${hash(rule)}_${uid}`;
const doc = get_root_for_node(node) as unknown as ExtendedDoc; const doc = get_root_for_style(node) as ExtendedDoc;
active_docs.add(doc); active_docs.add(doc);
const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = append_empty_stylesheet(node).sheet as CSSStyleSheet); const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = append_empty_stylesheet(node).sheet as CSSStyleSheet);
const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {}); const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {});

@ -119,20 +119,28 @@ export function get_slot_changes(definition, $$scope, dirty, fn) {
return $$scope.dirty; return $$scope.dirty;
} }
export function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) { export function update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn) {
const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
if (slot_changes) { if (slot_changes) {
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
slot.p(slot_context, slot_changes); slot.p(slot_context, slot_changes);
} }
} }
export function update_slot_spread(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_spread_changes_fn, get_slot_context_fn) { export function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) {
const slot_changes = get_slot_spread_changes_fn(dirty) | get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn); const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
if (slot_changes) { update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn);
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); }
slot.p(slot_context, slot_changes);
export function get_all_dirty_from_scope($$scope) {
if ($$scope.ctx.length > 32) {
const dirty = [];
const length = $$scope.ctx.length / 32;
for (let i = 0; i < length; i++) {
dirty[i] = -1;
}
return dirty;
} }
return -1;
} }
export function exclude_internal_props(props) { export function exclude_internal_props(props) {
@ -169,7 +177,7 @@ export function null_to_empty(value) {
return value == null ? '' : value; return value == null ? '' : value;
} }
export function set_store_value(store, ret, value = ret) { export function set_store_value(store, ret, value) {
store.set(value); store.set(value);
return ret; return ret;
} }

@ -0,0 +1,27 @@
export default {
warnings: [
{
code: 'css-unused-selector',
end: {
character: 111,
column: 21,
line: 8
},
frame: `
6: color: red;
7: }
8: a:global(.foo) > div {
^
9: color: red;
10: }
`,
message: 'Unused CSS selector "a:global(.foo) > div"',
pos: 91,
start: {
character: 91,
column: 1,
line: 8
}
}
]
};

@ -0,0 +1 @@
div>div.svelte-xyz.svelte-xyz{color:red}div.svelte-xyz.foo>div.svelte-xyz{color:red}

@ -0,0 +1,3 @@
<div class="svelte-xyz">
<div class="svelte-xyz"></div>
</div>

@ -0,0 +1,15 @@
<style>
:global(div) > div {
color: red;
}
div:global(.foo) > div {
color: red;
}
a:global(.foo) > div {
color: red;
}
</style>
<div>
<div />
</div>

@ -0,0 +1,3 @@
<div class="svelte-xyz">
<div class="svelte-xyz"></div>
</div>

@ -0,0 +1,9 @@
<style>
:global(a) > :global(b) > div {
color: red;
}
</style>
<div>
<div />
</div>

@ -0,0 +1,3 @@
<div class="svelte-xyz">
<div class="svelte-xyz"></div>
</div>

@ -0,0 +1,114 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
assign,
attr,
compute_rest_props,
create_component,
destroy_component,
detach,
element,
exclude_internal_props,
init,
insert,
mount_component,
safe_not_equal,
space,
transition_in,
transition_out
} from "svelte/internal";
import Component from "./Component.svelte";
function create_fragment(ctx) {
let div;
let div_class_value;
let div_style_value;
let div_other_value;
let t;
let component;
let current;
component = new Component({
props: {
class: "\n\t\tbutton\n\t\tbutton--size--" + /*size*/ ctx[0] + "\n\t\tbutton--theme--" + /*theme*/ ctx[1] + "\n \t" + (/*$$restProps*/ ctx[2].class || ''),
style: "\n\t\tcolor: green;\n\t\tbackground: white;\n\t\tfont-size: " + /*size*/ ctx[0] + ";\n \ttransform: " + /*$$restProps*/ ctx[2].scale + " " + /*$$restProps*/ ctx[2].rotate + ";\n\t\t" + /*$$restProps*/ ctx[2].styles,
other: "\n\t\tbutton\n\t\tbutton--size--" + /*size*/ ctx[0] + "\n\t\tbutton--theme--" + /*theme*/ ctx[1] + "\n \t" + (/*$$restProps*/ ctx[2].class || '')
}
});
return {
c() {
div = element("div");
t = space();
create_component(component.$$.fragment);
attr(div, "class", div_class_value = "button button--size--" + /*size*/ ctx[0] + " button--theme--" + /*theme*/ ctx[1] + " " + (/*$$restProps*/ ctx[2].class || ''));
attr(div, "style", div_style_value = "color: green; background: white; font-size: " + /*size*/ ctx[0] + "; transform: " + /*$$restProps*/ ctx[2].scale + " " + /*$$restProps*/ ctx[2].rotate + "; " + /*$$restProps*/ ctx[2].styles);
attr(div, "other", div_other_value = "\n\t\tbutton\n\t\tbutton--size--" + /*size*/ ctx[0] + "\n\t\tbutton--theme--" + /*theme*/ ctx[1] + "\n \t" + (/*$$restProps*/ ctx[2].class || ''));
},
m(target, anchor) {
insert(target, div, anchor);
insert(target, t, anchor);
mount_component(component, target, anchor);
current = true;
},
p(ctx, [dirty]) {
if (!current || dirty & /*size, theme, $$restProps*/ 7 && div_class_value !== (div_class_value = "button button--size--" + /*size*/ ctx[0] + " button--theme--" + /*theme*/ ctx[1] + " " + (/*$$restProps*/ ctx[2].class || ''))) {
attr(div, "class", div_class_value);
}
if (!current || dirty & /*size, $$restProps*/ 5 && div_style_value !== (div_style_value = "color: green; background: white; font-size: " + /*size*/ ctx[0] + "; transform: " + /*$$restProps*/ ctx[2].scale + " " + /*$$restProps*/ ctx[2].rotate + "; " + /*$$restProps*/ ctx[2].styles)) {
attr(div, "style", div_style_value);
}
if (!current || dirty & /*size, theme, $$restProps*/ 7 && div_other_value !== (div_other_value = "\n\t\tbutton\n\t\tbutton--size--" + /*size*/ ctx[0] + "\n\t\tbutton--theme--" + /*theme*/ ctx[1] + "\n \t" + (/*$$restProps*/ ctx[2].class || ''))) {
attr(div, "other", div_other_value);
}
const component_changes = {};
if (dirty & /*size, theme, $$restProps*/ 7) component_changes.class = "\n\t\tbutton\n\t\tbutton--size--" + /*size*/ ctx[0] + "\n\t\tbutton--theme--" + /*theme*/ ctx[1] + "\n \t" + (/*$$restProps*/ ctx[2].class || '');
if (dirty & /*size, $$restProps*/ 5) component_changes.style = "\n\t\tcolor: green;\n\t\tbackground: white;\n\t\tfont-size: " + /*size*/ ctx[0] + ";\n \ttransform: " + /*$$restProps*/ ctx[2].scale + " " + /*$$restProps*/ ctx[2].rotate + ";\n\t\t" + /*$$restProps*/ ctx[2].styles;
if (dirty & /*size, theme, $$restProps*/ 7) component_changes.other = "\n\t\tbutton\n\t\tbutton--size--" + /*size*/ ctx[0] + "\n\t\tbutton--theme--" + /*theme*/ ctx[1] + "\n \t" + (/*$$restProps*/ ctx[2].class || '');
component.$set(component_changes);
},
i(local) {
if (current) return;
transition_in(component.$$.fragment, local);
current = true;
},
o(local) {
transition_out(component.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching) detach(div);
if (detaching) detach(t);
destroy_component(component, detaching);
}
};
}
function instance($$self, $$props, $$invalidate) {
const omit_props_names = ["size","theme"];
let $$restProps = compute_rest_props($$props, omit_props_names);
let { size } = $$props;
let { theme } = $$props;
$$self.$$set = $$new_props => {
$$props = assign(assign({}, $$props), exclude_internal_props($$new_props));
$$invalidate(2, $$restProps = compute_rest_props($$props, omit_props_names));
if ('size' in $$new_props) $$invalidate(0, size = $$new_props.size);
if ('theme' in $$new_props) $$invalidate(1, theme = $$new_props.theme);
};
return [size, theme, $$restProps];
}
class Component_1 extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, { size: 0, theme: 1 });
}
}
export default Component_1;

@ -0,0 +1,41 @@
<script>
import Component from "./Component.svelte";
export let size;
export let theme;
</script>
<div
class="
button
button--size--{size}
button--theme--{theme}
{$$restProps.class || ''}"
style="
color: green;
background: white;
font-size: {size};
transform: {$$restProps.scale} {$$restProps.rotate};
{$$restProps.styles}"
other="
button
button--size--{size}
button--theme--{theme}
{$$restProps.class || ''}" />
<Component
class="
button
button--size--{size}
button--theme--{theme}
{$$restProps.class || ''}"
style="
color: green;
background: white;
font-size: {size};
transform: {$$restProps.scale} {$$restProps.rotate};
{$$restProps.styles}"
other="
button
button--size--{size}
button--theme--{theme}
{$$restProps.class || ''}" />

@ -0,0 +1,5 @@
export default {
options: {
accessors: true
}
};

@ -0,0 +1,34 @@
/* generated by Svelte vX.Y.Z */
import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
import { f as f_1, g as g_1 } from './d';
import { h as h_1 } from './e';
import { i as j } from './f';
export { d as e } from './c';
export { c } from './b';
export { a, b } from './a';
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, null, safe_not_equal, {});
}
get f() {
return f_1;
}
get g() {
return g_1;
}
get h() {
return h_1;
}
get j() {
return j;
}
}
export default Component;

@ -0,0 +1,11 @@
<script context="module">
export { a, b } from './a';
export { c } from './b';
export { d as e } from './c';
</script>
<script>
export { f, g } from './d';
export { h } from './e';
export { i as j } from './f';
</script>

@ -0,0 +1,6 @@
export default {
options: {
accessors: true,
format: 'cjs'
}
};

@ -0,0 +1,36 @@
/* generated by Svelte vX.Y.Z */
"use strict";
const { SvelteComponent, init, safe_not_equal } = require("svelte/internal");
const { f: f_1, g: g_1 } = require("./d");
const { h: h_1 } = require("./e");
const { i: j } = require("./f");
exports.e = require("./c").d;
exports.c = require("./b").c;
exports.a = require("./a").a;
exports.b = require("./a").b;
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, null, safe_not_equal, {});
}
get f() {
return f_1;
}
get g() {
return g_1;
}
get h() {
return h_1;
}
get j() {
return j;
}
}
exports.default = Component;

@ -0,0 +1,11 @@
<script context="module">
export { a, b } from './a';
export { c } from './b';
export { d as e } from './c';
</script>
<script>
export { f, g } from './d';
export { h } from './e';
export { i as j } from './f';
</script>

@ -0,0 +1,18 @@
/* generated by Svelte vX.Y.Z */
import { SvelteComponent, init, safe_not_equal } from "svelte/internal";
import './d';
import './e';
import './f';
export { d as e } from './c';
export { c } from './b';
export { a, b } from './a';
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, null, safe_not_equal, {});
}
}
export default Component;

@ -0,0 +1,11 @@
<script context="module">
export { a, b } from './a';
export { c } from './b';
export { d as e } from './c';
</script>
<script>
export { f, g } from './d';
export { h } from './e';
export { i as j } from './f';
</script>

@ -0,0 +1,5 @@
export default {
options: {
hydratable: true
}
};

@ -0,0 +1,58 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
append_hydration,
children,
claim_svg_element,
claim_text,
detach,
init,
insert_hydration,
noop,
safe_not_equal,
svg_element,
text
} from "svelte/internal";
function create_fragment(ctx) {
let svg;
let title;
let t;
return {
c() {
svg = svg_element("svg");
title = svg_element("title");
t = text("a title");
},
l(nodes) {
svg = claim_svg_element(nodes, "svg", {});
var svg_nodes = children(svg);
title = claim_svg_element(svg_nodes, "title", {});
var title_nodes = children(title);
t = claim_text(title_nodes, "a title");
title_nodes.forEach(detach);
svg_nodes.forEach(detach);
},
m(target, anchor) {
insert_hydration(target, svg, anchor);
append_hydration(svg, title);
append_hydration(title, t);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(svg);
}
};
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
export default Component;

@ -0,0 +1,3 @@
<svg>
<title>a title</title>
</svg>

After

Width:  |  Height:  |  Size: 36 B

@ -0,0 +1,10 @@
{
"code": "missing-attribute-value",
"message": "Expected value for the attribute",
"pos": 12,
"start": {
"character": 12,
"column": 12,
"line": 1
}
}

@ -0,0 +1 @@
<div a="" b={''} c='' d="{''}" ></div>

@ -0,0 +1,108 @@
{
"html": {
"start": 0,
"end": 38,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 38,
"type": "Element",
"name": "div",
"attributes": [
{
"start": 5,
"end": 9,
"type": "Attribute",
"name": "a",
"value": [
{
"start": 8,
"end": 8,
"type": "Text",
"raw": "",
"data": ""
}
]
},
{
"start": 10,
"end": 16,
"type": "Attribute",
"name": "b",
"value": [
{
"start": 12,
"end": 16,
"type": "MustacheTag",
"expression": {
"type": "Literal",
"start": 13,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 13
},
"end": {
"line": 1,
"column": 15
}
},
"value": "",
"raw": "''"
}
}
]
},
{
"start": 17,
"end": 21,
"type": "Attribute",
"name": "c",
"value": [
{
"start": 20,
"end": 20,
"type": "Text",
"raw": "",
"data": ""
}
]
},
{
"start": 22,
"end": 30,
"type": "Attribute",
"name": "d",
"value": [
{
"start": 25,
"end": 29,
"type": "MustacheTag",
"expression": {
"type": "Literal",
"start": 26,
"end": 28,
"loc": {
"start": {
"line": 1,
"column": 26
},
"end": {
"line": 1,
"column": 28
}
},
"value": "",
"raw": "''"
}
}
]
}
],
"children": []
}
]
}
}

@ -0,0 +1,10 @@
{
"code": "unclosed-attribute-value",
"message": "Expected to close the attribute value with }",
"pos": 25,
"start": {
"character": 25,
"column": 25,
"line": 1
}
}

@ -0,0 +1,18 @@
export default {
html: '<div></div>',
async test({ assert, target, window }) {
const enter = new window.MouseEvent('mouseenter');
const leave = new window.MouseEvent('mouseleave');
await window.document.body.dispatchEvent(enter);
assert.htmlEqual(target.innerHTML, `
<div>
<div class="tooltip">Perform an Action</div>
</div>
`);
await window.document.body.dispatchEvent(leave);
assert.htmlEqual(target.innerHTML, '<div></div>');
}
};

@ -0,0 +1,32 @@
<script>
let container;
function tooltip(node, text) {
let tooltip = null;
function onMouseEnter() {
tooltip = document.createElement('div');
tooltip.classList.add('tooltip');
tooltip.textContent = text;
container.appendChild(tooltip);
}
function onMouseLeave() {
if (!tooltip) return;
tooltip.remove();
tooltip = null;
}
node.addEventListener('mouseenter', onMouseEnter);
node.addEventListener('mouseleave', onMouseLeave);
return {
destroy() {
node.removeEventListener('mouseenter', onMouseEnter);
node.removeEventListener('mouseleave', onMouseLeave);
}
}
}
</script>
<svelte:body use:tooltip="{'Perform an Action'}" />
<div bind:this={container} />

@ -0,0 +1,47 @@
let fulfil;
let thePromise = new Promise(f => {
fulfil = f;
});
export default {
props: {
thePromise
},
html: `
<br />
<p>the promise is pending</p>
`,
async test({ assert, component, target }) {
fulfil(42);
await thePromise;
assert.htmlEqual(target.innerHTML, '<br />');
let reject;
thePromise = new Promise((f, r) => {
reject = r;
});
component.thePromise = thePromise;
assert.htmlEqual(target.innerHTML, `
<br />
<p>the promise is pending</p>
`);
reject(new Error());
await thePromise.catch(() => {});
assert.htmlEqual(target.innerHTML, `
<p>oh no! Something broke!</p>
<br />
<p>oh no! Something broke!</p>
`);
}
};

@ -0,0 +1,15 @@
<script>
export let thePromise;
</script>
{#await thePromise catch}
<p>oh no! Something broke!</p>
{/await}
<br />
{#await thePromise}
<p>the promise is pending</p>
{:catch}
<p>oh no! Something broke!</p>
{/await}

@ -0,0 +1,55 @@
let fulfil;
let thePromise = new Promise(f => {
fulfil = f;
});
export default {
props: {
thePromise
},
html: `
<br>
<br>
<p>the promise is pending</p>
`,
async test({ assert, component, target }) {
fulfil();
await thePromise;
assert.htmlEqual(target.innerHTML, `
<p>the promise is resolved</p>
<br>
<p>the promise is resolved</p>
<br>
<p>the promise is resolved</p>
`);
let reject;
thePromise = new Promise((f, r) => {
reject = r;
});
component.thePromise = thePromise;
assert.htmlEqual(target.innerHTML, `
<br>
<br>
<p>the promise is pending</p>
`);
reject(new Error('something broke'));
await thePromise.catch(() => {});
assert.htmlEqual(target.innerHTML, `
<p>oh no! something broke</p>
<br>
<br>
`);
}
};

@ -0,0 +1,23 @@
<script>
export let thePromise;
</script>
{#await thePromise then}
<p>the promise is resolved</p>
{:catch theError}
<p>oh no! {theError.message}</p>
{/await}
<br />
{#await thePromise then}
<p>the promise is resolved</p>
{/await}
<br />
{#await thePromise}
<p>the promise is pending</p>
{:then}
<p>the promise is resolved</p>
{/await}

@ -14,6 +14,7 @@ export default {
`, `,
test({ assert, component, target }) { test({ assert, component, target }) {
assert.equal(component.selected, 'a');
const select = target.querySelector('select'); const select = target.querySelector('select');
const options = [...target.querySelectorAll('option')]; const options = [...target.querySelectorAll('option')];

@ -0,0 +1,60 @@
export default {
html: `
<p>selected: null</p>
<select>
<option value='a'>a</option>
<option value='b'>b</option>
<option value='c'>c</option>
</select>
<p>selected: null</p>
`,
async test({ assert, component, target }) {
const select = target.querySelector('select');
const options = [...target.querySelectorAll('option')];
assert.equal(component.selected, null);
// no option should be selected since none of the options matches the bound value
assert.equal(select.value, '');
assert.equal(select.selectedIndex, -1);
assert.ok(!options[0].selected);
component.selected = 'a'; // first option should now be selected
assert.equal(select.value, 'a');
assert.ok(options[0].selected);
assert.htmlEqual(target.innerHTML, `
<p>selected: a</p>
<select>
<option value='a'>a</option>
<option value='b'>b</option>
<option value='c'>c</option>
</select>
<p>selected: a</p>
`);
component.selected = 'd'; // doesn't match an option
// now no option should be selected again
assert.equal(select.value, '');
assert.equal(select.selectedIndex, -1);
assert.ok(!options[0].selected);
assert.htmlEqual(target.innerHTML, `
<p>selected: d</p>
<select>
<option value='a'>a</option>
<option value='b'>b</option>
<option value='c'>c</option>
</select>
<p>selected: d</p>
`);
}
};

@ -0,0 +1,14 @@
<script>
// set as null so no option will be selected by default
export let selected = null;
</script>
<p>selected: {selected}</p>
<select bind:value={selected}>
<option>a</option>
<option>b</option>
<option>c</option>
</select>
<p>selected: {selected}</p>

@ -0,0 +1,24 @@
<script>
import { writable } from 'svelte/store';
const THING = { a: 1, b: { c: 2, d: [3, 4, writable(5)] }, e: [6], h: 8 };
const default_g = 9;
export let { a, b: { c, d: [d_one,,d_three], f }, e: [e_one], g = default_g } = THING;
export const { a: A, b: { c: C } } = THING;
</script>
<div>
a: {a},
b: {typeof b},
c: {c},
d_one: {d_one},
d_three: {$d_three},
f: {f},
g: {g},
e: {typeof e},
e_one: {e_one},
A: {A},
C: {C}
</div>
<div>{JSON.stringify(THING)}</div>

@ -0,0 +1,9 @@
export default {
html: `
<div>a: 1, b: undefined, c: 2, d_one: 3, d_three: 5, f: undefined, g: 9, e: undefined, e_one: 6, A: 1, C: 2</div>
<div>{"a":1,"b":{"c":2,"d":[3,4,{}]},"e":[6],"h":8}</div>
<br>
<div>a: a, b: undefined, c: 2, d_one: d_one, d_three: 5, f: f, g: g, e: undefined, e_one: 6, A: 1, C: 2</div>
<div>{"a":1,"b":{"c":2,"d":[3,4,{}]},"e":[6],"h":8}</div>
`
};

@ -0,0 +1,7 @@
<script>
import A from './A.svelte';
</script>
<A />
<br />
<A a="a" d_one="d_one" list_one="list_one" f="f" list_two_b="list_two_b" g="g" A="A" C="C" />

@ -0,0 +1,21 @@
<script>
import { writable } from 'svelte/store';
let default_b = 5;
const LIST = [1, { a: 2 }, [3, writable(4)]];
export const [x, { a: list_two_a, b: list_two_b = default_b }, [, y]] = LIST;
export let [m, { a: n, b: o = default_b }, [p, q]] = LIST;
</script>
<div>
x: {x},
list_two_a: {list_two_a},
list_two_b: {list_two_b},
y: {$y},
m: {m},
n: {n},
o: {o},
p: {p},
q: {$q}
</div>
<div>{JSON.stringify(LIST)}</div>

@ -0,0 +1,19 @@
export default {
html: `
<div>x: 1, list_two_a: 2, list_two_b: 5, y: 4, m: 1, n: 2, o: 5, p: 3, q: 4</div>
<div>[1,{"a":2},[3,{}]]</div>
<br><div>x: 1, list_two_a: 2, list_two_b: 5, y: 4, m: m, n: n, o: o, p: p, q: q</div>
<div>[1,{"a":2},[3,{}]]</div>
`,
async test({ component, assert, target }) {
await component.update();
assert.htmlEqual(target.innerHTML, `
<div>x: 1, list_two_a: 2, list_two_b: 5, y: 4, m: 1, n: 2, o: 5, p: 3, q: 4</div>
<div>[1,{"a":2},[3,{}]]</div>
<br><div>x: 1, list_two_a: 2, list_two_b: 5, y: 4, m: MM, n: NN, o: OO, p: PP, q: QQ</div>
<div>[1,{"a":2},[3,{}]]</div>
`);
}
};

@ -0,0 +1,30 @@
<script>
import A from './A.svelte';
import { writable } from 'svelte/store';
let x = 'x',
list_two_a = 'list_two_a',
list_two_b = 'list_two_b',
y = writable('y'),
m = 'm',
n = 'n',
o = 'o',
p = 'p',
q = writable('q');
export function update() {
x = 'XX';
list_two_a = 'LIST_TWO_A';
list_two_b = 'LIST_TWO_B';
y = writable('YY');
m = 'MM';
n = 'NN';
o = 'OO';
p = 'PP';
q = writable('QQ');
}
</script>
<A />
<br />
<A {x} {list_two_a} {list_two_b} {y} {m} {n} {o} {p} {q} />

@ -0,0 +1,10 @@
<script>
import { writable } from 'svelte/store';
const { i, j, k } = { i: 9, j: 10, k: writable(11) }, l = 12, m = 13, n = writable(14);
let { a, b, c } = { a: 9, b: 10, c: writable(11) }, d = 12, e = 13, f = writable(14);
export { i, k, l, n, a, c, d, f };
</script>
<div>i: {i}, j: {j}, k: {$k}, l: {l}, m: {m}, n: {$n}, a: {a}, b: {b}, c: {$c}, d: {d}, e: {e}, f: {$f}</div>

@ -0,0 +1,15 @@
export default {
html: `
<div>i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, a: 9, b: 10, c: 11, d: 12, e: 13, f: 14</div>
<br>
<div>i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, a: a, b: 10, c: c, d: d, e: 13, f: f</div>
`,
async test({ component, target, assert }) {
await component.update();
assert.htmlEqual(target.innerHTML, `
<div>i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, a: 9, b: 10, c: 11, d: 12, e: 13, f: 14</div>
<br>
<div>i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, a: aa, b: 10, c: cc, d: dd, e: 13, f: ff</div>
`);
}
};

@ -0,0 +1,29 @@
<script>
import A from './A.svelte';
import { writable } from 'svelte/store';
let i = 'i',
k = writable('k'),
l = 'l',
n = writable('n'),
a = 'a',
c = writable('c'),
d = 'd',
f = writable('f');
export function update() {
i = 'ii';
k = writable('kk');
l = 'll';
n = writable('nn');
a = 'aa';
c = writable('cc');
d = 'dd';
f = writable('ff');
}
</script>
<A />
<br />
<A {i} {k} {l} {n} {a} {c} {d} {f} />

@ -0,0 +1,23 @@
<script context="module">
export { a, b } from './B.svelte';
export { c as d } from './B.svelte';
</script>
<script>
export { d, e } from './B.svelte';
export { f as g } from './B.svelte';
let e = 123;
let b = 234;
function foo() {
e = 456;
b = 567;
}
</script>
a: {typeof a}<br />
b: {typeof b}<br />
c: {typeof c}<br />
d: {typeof d}<br />
e: {typeof e}<br />
f: {typeof f}<br />
g: {typeof g}<br />

@ -0,0 +1,8 @@
<script context="module">
export const a = 'a';
export const b = 'b';
export const c = 'c';
export const d = 'd';
export const e = 'e';
export const f = 'f';
</script>

@ -0,0 +1,28 @@
export default {
html: `
a,b,undefined,c
<br />
a: undefined<br />
b: number<br />
c: undefined<br />
d: undefined<br />
e: number<br />
f: undefined<br />
g: undefined<br />
<br />
{"d":"d","e":"e","g":"f"}
`,
ssrHtml: `
a,b,undefined,c
<br />
a: undefined<br />
b: number<br />
c: undefined<br />
d: undefined<br />
e: number<br />
f: undefined<br />
g: undefined<br />
<br />
{}
`
};

@ -0,0 +1,21 @@
<script>
import A, { a, b, c, d } from './A.svelte';
import {onMount} from 'svelte';
let component;
let props = {};
onMount(() => {
props = {
d: component.d,
e: component.e,
f: component.f,
g: component.g,
};
});
</script>
{a},{b},{c},{d}
<br />
<A bind:this={component} />
<br />
{JSON.stringify(props)}

@ -0,0 +1,19 @@
export default {
html: `
<div>
<div><span class="name">item 1</span><span>something</span></div>
<div><span class="name">item 2</span><span>something</span></div>
<div><span class="name">item 3</span><span>something</span></div>
</div>
`,
test({ assert, component, target }) {
component.sortById = false;
assert.htmlEqual( target.innerHTML, `
<div>
<div><span class="name">item 3</span><span>something</span></div>
<div><span class="name">item 2</span><span>something</span></div>
<div><span class="name">item 1</span><span>something</span></div>
</div>
`);
}
};

@ -0,0 +1,23 @@
<script>
export let sortById = true;
let items = [
{ id: 1, name: "item 1", value: 3 },
{ id: 2, name: "item 2", value: 2 },
{ id: 3, name: "item 3", value: 1 },
];
$: items = items.sort((a, b) => { return sortById ? a.id - b.id : a.value - b.value; });
</script>
<div>
{#each items as item (item.id)}
<div>
{#if item.name}
<span class="name">
{item.name}
</span>
{/if}
<span>something</span>
</div>
{/each}
</div>

@ -0,0 +1,11 @@
export default {
html: `
<div>$userName1: user1</div>
<div>$userName2: undefined</div>
<div>$userName3: undefined</div>
<div>$userName4: user4</div>
<div>$userName5: undefined</div>
<div>$userName6: user6</div>
<div>$userName7: undefined</div>
`
};

@ -0,0 +1,34 @@
<script>
import { writable } from 'svelte/store';
let userName1 = writable('init1');
let userName2 = writable('init2');
let userName3 = writable('init3');
let userName4 = writable('init4');
let userName5 = writable('init5');
let userName6 = writable('init6');
let userName7 = writable('init7');
let obj = {
userName1: 'user1',
userName2: 'user2',
userName3: 'user3',
$userName4: 'user4',
userName5: 'user5',
$userName6: 'user6',
userName7: 'user7',
};
({userName1: $userName1, $userName2 } = obj);
({$userName3} = obj);
({$userName4} = obj);
({$userName5, $userName6, $userName7} = obj);
</script>
<div>$userName1: {$userName1}</div>
<div>$userName2: {$userName2}</div>
<div>$userName3: {$userName3}</div>
<div>$userName4: {$userName4}</div>
<div>$userName5: {$userName5}</div>
<div>$userName6: {$userName6}</div>
<div>$userName7: {$userName7}</div>

@ -0,0 +1,11 @@
<script>
let name = 'World';
</script>
<div>Hello {name}</div>
<style>
div {
color: red;
}
</style>

@ -0,0 +1,16 @@
export default {
skip_if_ssr: true,
compileOptions: {
cssHash: () => 'svelte-xyz'
},
async test({ assert, component, target, window }) {
assert.htmlEqual(
window.document.head.innerHTML,
'<style id="svelte-xyz">div.svelte-xyz{color:red}</style>'
);
assert.htmlEqual(
component.div.innerHTML,
'<div class="svelte-xyz">Hello World</div>'
);
}
};

@ -0,0 +1,18 @@
<script>
import App from './App.svelte';
import { onMount } from 'svelte';
export let div;
onMount(() => {
div = document.createElement('div');
const app = new App({
target: div
});
return () => {
app.$destroy();
}
});
</script>

@ -0,0 +1,11 @@
<script>
let name = 'World';
</script>
<div>Hello {name}</div>
<style>
div {
color: red;
}
</style>

@ -0,0 +1,16 @@
export default {
skip_if_ssr: true,
compileOptions: {
cssHash: () => 'svelte-xyz'
},
async test({ assert, component, target, window }) {
assert.htmlEqual(
window.document.head.innerHTML,
'<style id="svelte-xyz">div.svelte-xyz{color:red}</style>'
);
assert.htmlEqual(
component.div.innerHTML,
'<div class="svelte-xyz">Hello World</div>'
);
}
};

@ -0,0 +1,18 @@
<script>
import App from './App.svelte';
import { onMount } from 'svelte';
export let div;
onMount(() => {
const app = new App({
target: div
});
return () => {
app.$destroy();
}
});
</script>
<div bind:this={div} />

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

Loading…
Cancel
Save