Merge branch 'sveltejs:master' into master

pull/6928/head
andirady 4 years ago committed by GitHub
commit 48b60bafe8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

1
.gitignore vendored

@ -9,6 +9,7 @@ node_modules
/compiler.*js /compiler.*js
/index.*js /index.*js
/ssr.*js /ssr.*js
/action
/internal /internal
/store /store
/easing /easing

@ -2,7 +2,44 @@
## Unreleased ## Unreleased
- Add `aria-description` to the list of allowed ARIA attributes ([#7301](https://github.com/sveltejs/svelte/issues/7301)) * Faster SSR ([#5701](https://github.com/sveltejs/svelte/pull/5701))
* Fix `class:` directive updates with `<svelte:element>` ([#7521](https://github.com/sveltejs/svelte/issues/7521))
## 3.48.0
* Allow creating cancelable custom events with `createEventDispatcher` ([#4623](https://github.com/sveltejs/svelte/issues/4623))
* Support `{@const}` tag in `{#if}` blocks [#7241](https://github.com/sveltejs/svelte/issues/7241)
* Return the context object in `setContext` [#7427](https://github.com/sveltejs/svelte/issues/7427)
* Allow comments inside `{#each}` blocks when using `animate:` ([#3999](https://github.com/sveltejs/svelte/issues/3999))
* Fix `|local` transitions in `{#key}` blocks ([#5950](https://github.com/sveltejs/svelte/issues/5950))
* Support svg namespace for `{@html}` ([#7002](https://github.com/sveltejs/svelte/issues/7002), [#7450](https://github.com/sveltejs/svelte/issues/7450))
* Fix `{@const}` tag not working inside a component when there's no `let:` [#7189](https://github.com/sveltejs/svelte/issues/7189)
* Remove extraneous leading newline inside `<pre>` and `<textarea>` ([#7264](https://github.com/sveltejs/svelte/issues/7264))
* Fix erroneous setting of `textContent` for `<template>` elements ([#7297](https://github.com/sveltejs/svelte/pull/7297))
* Fix value of `let:` bindings not updating in certain cases ([#7440](https://github.com/sveltejs/svelte/issues/7440))
* Fix handling of void tags in `<svelte:element>` ([#7449](https://github.com/sveltejs/svelte/issues/7449))
* Fix handling of boolean attributes in `<svelte:element>` ([#7478](https://github.com/sveltejs/svelte/issues/7478))
* Add special style scoping handling of `[open]` selectors on `<dialog>` elements ([#7495](https://github.com/sveltejs/svelte/issues/7494))
## 3.47.0
* Add support for dynamic elements through `<svelte:element>` ([#2324](https://github.com/sveltejs/svelte/issues/2324))
* Miscellaneous variable context fixes in `{@const}` ([#7222](https://github.com/sveltejs/svelte/pull/7222))
* Fix `{#key}` block not being reactive when the key variable is not otherwise used ([#7408](https://github.com/sveltejs/svelte/issues/7408))
* Add `Symbol` as a known global ([#7418](https://github.com/sveltejs/svelte/issues/7418))
## 3.46.6
* Actually include action TypeScript interface in published package ([#7407](https://github.com/sveltejs/svelte/pull/7407))
## 3.46.5
* Add TypeScript interfaces for typing actions ([#6538](https://github.com/sveltejs/svelte/issues/6538))
* Do not generate `unused-export-let` warning inside `<script context="module">` blocks ([#7055](https://github.com/sveltejs/svelte/issues/7055))
* Do not collapse whitespace-only CSS vars ([#7152](https://github.com/sveltejs/svelte/issues/7152))
* Add `aria-description` to the list of allowed ARIA attributes ([#7301](https://github.com/sveltejs/svelte/issues/7301))
* Fix attribute escaping during SSR ([#7327](https://github.com/sveltejs/svelte/issues/7327))
* Prevent `.innerHTML` optimization from being used when `style:` directive is present ([#7386](https://github.com/sveltejs/svelte/issues/7386))
## 3.46.4 ## 3.46.4

@ -67,7 +67,7 @@ Working on your first Pull Request? You can learn how from this free video serie
If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, you can also file an issue with [feature template](https://github.com/sveltejs/svelte/issues/new?template=feature_request.md). If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, you can also file an issue with [feature template](https://github.com/sveltejs/svelte/issues/new?template=feature_request.md).
If you're only fixing a bug, it's fine to submit a pull request right away but we still recommend that you file an issue detailing what you're fixing. This is helpful in case we don't accept that specific fix but want to keep track of the issue. If you're only fixing a bug, it's fine to submit a pull request right away, but we still recommend that you file an issue detailing what you're fixing. This is helpful in case we don't accept that specific fix but want to keep track of the issue.
### Sending a pull request ### Sending a pull request

2703
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.46.4", "version": "3.48.0",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"module": "index.mjs", "module": "index.mjs",
"main": "index", "main": "index",
@ -16,7 +16,7 @@
"transition", "transition",
"easing", "easing",
"motion", "motion",
"svelte", "action",
"README.md" "README.md"
], ],
"exports": { "exports": {
@ -39,6 +39,9 @@
"import": "./compiler.mjs", "import": "./compiler.mjs",
"require": "./compiler.js" "require": "./compiler.js"
}, },
"./action": {
"types": "./types/runtime/action/index.d.ts"
},
"./animate": { "./animate": {
"types": "./types/runtime/animate/index.d.ts", "types": "./types/runtime/animate/index.d.ts",
"import": "./animate/index.mjs", "import": "./animate/index.mjs",
@ -110,7 +113,7 @@
"bugs": { "bugs": {
"url": "https://github.com/sveltejs/svelte/issues" "url": "https://github.com/sveltejs/svelte/issues"
}, },
"homepage": "https://github.com/sveltejs/svelte#README", "homepage": "https://svelte.dev",
"devDependencies": { "devDependencies": {
"@ampproject/remapping": "^0.3.0", "@ampproject/remapping": "^0.3.0",
"@rollup/plugin-commonjs": "^11.0.0", "@rollup/plugin-commonjs": "^11.0.0",
@ -123,15 +126,15 @@
"@sveltejs/eslint-config": "github:sveltejs/eslint-config#v5.8.0", "@sveltejs/eslint-config": "github:sveltejs/eslint-config#v5.8.0",
"@types/mocha": "^7.0.0", "@types/mocha": "^7.0.0",
"@types/node": "^8.10.53", "@types/node": "^8.10.53",
"@typescript-eslint/eslint-plugin": "^4.31.2", "@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^4.31.2", "@typescript-eslint/parser": "^5.22.0",
"acorn": "^8.4.1", "acorn": "^8.4.1",
"agadoo": "^1.1.0", "agadoo": "^1.1.0",
"code-red": "^0.2.5", "code-red": "^0.2.5",
"css-tree": "^1.1.2", "css-tree": "^1.1.2",
"eslint": "^7.32.0", "eslint": "^8.0.0",
"eslint-plugin-import": "^2.24.2", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-svelte3": "^3.2.1", "eslint-plugin-svelte3": "^4.0.0",
"estree-walker": "^3.0.0", "estree-walker": "^3.0.0",
"is-reference": "^3.0.0", "is-reference": "^3.0.0",
"jsdom": "^15.2.1", "jsdom": "^15.2.1",

@ -12,7 +12,9 @@ Because of that, getting started with it can be a little bit confusing at first.
## 1. Use the REPL ## 1. Use the REPL
The [Svelte REPL](/repl) is the easiest way to begin. You can choose from a list of examples to get you started, and tweak them until they do what you want. The [Svelte REPL](/repl) (Read-Eval-Print Loop) is the easiest way to begin. This is an interactive environment that allows you to modify code and instantly see the result.
You can choose from a list of [examples](/examples/), click the [REPL](/repl) link, and then tweak them until they do what you want.
<aside><p>You'll need to have <a href="https://nodejs.org/">Node.js</a> installed, and know how to use the terminal</p></aside> <aside><p>You'll need to have <a href="https://nodejs.org/">Node.js</a> installed, and know how to use the terminal</p></aside>
@ -31,8 +33,7 @@ npm install
npm run dev npm run dev
``` ```
This will serve your app on [localhost:5000](http://localhost:5000) and rebuild it with [Rollup](https://rollupjs.org) every time you make a change to the files in `svelte-app/src`. This will serve your app on [localhost:8080](http://localhost:8080) and rebuild it with [Rollup](https://rollupjs.org) every time you make a change to the files in `svelte-app/src`.
## 2. Use degit ## 2. Use degit
@ -50,7 +51,7 @@ npm install
npm run dev npm run dev
``` ```
This will create a new project in the `my-svelte-project` directory, install its dependencies, and start a server on http://localhost:5000. This will create a new project in the `my-svelte-project` directory, install its dependencies, and start a server on http://localhost:8080.
You can find more information about using TypeScript [here](/blog/svelte-and-typescript). You can find more information about using TypeScript [here](/blog/svelte-and-typescript).

@ -8,7 +8,7 @@ draft: true
*__Coming soon__* *__Coming soon__*
This post will walk you through setting up your editor so that recognises Svelte files: This post will walk you through setting up your editor so that it recognises Svelte files:
* eslint-plugin-svelte3 * eslint-plugin-svelte3
* svelte-vscode * svelte-vscode

@ -78,7 +78,7 @@ npm run dev
Running the `dev` script starts a program called [Rollup](https://rollupjs.org/guide/en/). Rollup's job is to take your application's source files (so far, just `src/main.js` and `src/App.svelte`), pass them to other programs (including Svelte, in our case) and convert them into the code that will actually run when you open the application in a browser. Running the `dev` script starts a program called [Rollup](https://rollupjs.org/guide/en/). Rollup's job is to take your application's source files (so far, just `src/main.js` and `src/App.svelte`), pass them to other programs (including Svelte, in our case) and convert them into the code that will actually run when you open the application in a browser.
Speaking of which, open a browser and navigate to http://localhost:5000. This is your application running on a local *web server* (hence 'localhost') on port 5000. Speaking of which, open a browser and navigate to http://localhost:8080. This is your application running on a local *web server* (hence 'localhost') on port 8080.
Try changing `src/App.svelte` and saving it. The application will reload with your changes. Try changing `src/App.svelte` and saving it. The application will reload with your changes.
@ -99,7 +99,7 @@ Your `public` directory now contains a compressed `bundle.js` file containing yo
npm run start npm run start
``` ```
This will run the app on http://localhost:5000. This will run the app on http://localhost:8080.
## Next steps ## Next steps

@ -0,0 +1,87 @@
---
title: "What's new in Svelte: March 2022"
description: "Svelte Summit Spring is coming... and page endpoints are here!"
author: Daniel Sandoval
authorURL: https://desandoval.net
---
Just announced: [Svelte Summit Spring](https://www.sveltesummit.com/) will be taking place on April 30, 2022. The 5th Virtual Svelte Conference is [looking for speakers](https://www.sveltesummit.com/#speakers) and [sponsors](https://www.sveltesummit.com/sponsors)... so it's time to dust off those proposals!
Also, some long-requested features were added to SvelteKit this month... including page endpoints! This change in how the `load` function works makes it easier to fetch data required for basic pages, redirect from POST responses and handle 404s and other errors.
More on that and other new features and fixes below!
## What's new in SvelteKit
- The docs are now searchable and multipage with type definitions and hoverable code examples - Check them out at [kit.svelte.dev/docs](https://kit.svelte.dev/docs/)
- Page endpoints significantly decrease the boilerplate needed when loading a page ([Issue](https://github.com/sveltejs/kit/issues/3532), [PR](https://github.com/sveltejs/kit/pull/3679), [Docs](https://kit.svelte.dev/docs/routing#endpoints-page-endpoints))
- Application versioning and update detection support lets you determine what to do when a route fails to load after an app update ([Issue](https://github.com/sveltejs/kit/issues/87), [PR](https://github.com/sveltejs/kit/pull/3412), [Docs](https://kit.svelte.dev/docs/configuration#version))
- A new option in `npm init svelte@next` will now set up Playwright automatically for testing ([PR](https://github.com/sveltejs/kit/pull/4056))
**Breaking Changes**
- The `target` option is no longer available. Instead, the `init` script hydrates its `parentNode` ([#3674](https://github.com/sveltejs/kit/pull/3674))
- App-level types now live in the `App` namespace which allows you to type global types like `Stuff` or `Session` ([#3670](https://github.com/sveltejs/kit/pull/3670))
- `JSONString` is now `JSONValue` ([#3683](https://github.com/sveltejs/kit/pull/3683))
- `createIndexFiles` has been removed — it is now controlled by the `trailingSlash` option ([#3801](https://github.com/sveltejs/kit/pull/3801))
- SvelteKit will no longer exclude root-relative external links from prerendering, which will cause 404s if these URLs are intended to be served by a separate app. Use a custom [`prerender.onError`](https://kit.svelte.dev/docs/configuration#prerender) handler if you need to ignore them ([#3826](https://github.com/sveltejs/kit/pull/3826))
## New in Language Tools
- Accessing properties in markups has been improved in the Svelte language tools ([105.12.0](https://github.com/sveltejs/language-tools/releases/tag/extensions-105.12.0)) - working around some known issues with autocomplete ([#538](https://github.com/sveltejs/language-tools/issues/538) / [#1302](https://github.com/sveltejs/language-tools/issues/1302))
---
## Community Showcase
**Apps & Sites**
- [SvelteStorm](https://github.com/open-source-labs/SvelteStorm) is specifically tailored to provide all of the essential tools a Svelte developer needs to build a Svelte application
- [Supachat](https://github.com/Lleweraf/supachat) is a real-time chat app using Svelte and Supabase
- [Radicle](https://radicle.xyz/) is a peer-to-peer stack for building software together
- [The Making Known](https://the-making-known.com/) is a narrated encounter with posters designed by the Nazi German government to communicate with the occupied nations of Belgium, France, and Luxembourg during the Second World War
- [Svelte Kanban](https://github.com/V-Py/svelte-kanban) is a simple Svelte Kanban made in pure CSS
- [fngrng](https://github.com/nvlgzr/fngrng) is a typing trainer focussed on accuracy over speed
- [Generative grids](https://svelte.dev/repl/873988ce33db43f097c0ca69df57b3ac?version=3.46.4) is a neat little generative SVG grid in a Svelte REPL, with randomly generated color palettes and shapes
- [LifeHash](https://github.com/BlockchainCommons/lifehash.info) is a method of hash visualization that creates beautiful, deterministic icons
- [TypedWebhook.tools](https://typedwebhook.tools/) is a webhook testing tool for checking payloads, with automatic type generation
- [Speedskating](https://github.com/spiegelgraphics/speedskating) is an animation widget to show olympic speedskating runs. Built with Svelte, D3 and regl
- [Web tail](https://github.com/mishankov/web-tail) is a web application to view lines from file on local system or on remote server
Want to work on a SvelteKit site with others? [Contribute to the Svelte Society site](https://github.com/svelte-society/sveltesociety.dev/issues)!
**Learning Resources**
_To Read_
- [Svelte Components as Web Components](https://medium.com/@yesmeno/svelte-components-as-web-components-b400d1253504) by Matias Meno
- [Simple Svelte Routing with Reactive URLs](https://bjornlu.com/blog/simple-svelte-routing-with-reactive-urls) by Bjorn Lu
- [Leveling Up my Sveltekit / Sanity.io Blog Content with Featured Videos and Syntax Highlighting](https://ryanboddy.net/level-up-blog) by Ryan Boddy
- [How This Blog Makes the Most of GitHub](https://paullj.github.io/posts/how-this-blog-makes-the-most-of-github/) by paullj
- [FullStack JWT Auth: Introducing SvelteKit](https://dev.to/sirneij/fullstack-jwt-introducing-sveltekit-3jcn) by John Idogun
- [Svelte-Cubed: Adding Motion to 3D Scenes](https://dev.to/alexwarnes/svelte-cubed-adding-motion-to-3d-scenes-51lo) by Alex Warnes
- [Creating a RSS feed with Sanity and Svelte Kit](https://ghostdev.xyz/posts/creating-a-rss-feed-with-sanity-and-svelte-kit) by GHOST
- [How to use Svelte's style directive](https://geoffrich.net/posts/style-directives/) by Geoff Rich
- [SvelteKit and the "Client pattern"](https://retro.cloud/sveltekit-and-the-client-pattern/) by Julian Laubstein
_To Watch_
- [~~Shadow~~ Page Endpoints In Svelte Kit - Weekly Svelte](https://www.youtube.com/watch?v=PoYPZT7ruqI) by LevelUpTuts
- [Testing For Beginners (Playlist)](https://www.youtube.com/watch?v=y53wwdBr5AI&list=PLA9WiRZ-IS_z7KpqhPELfEMbhAGRwZrzn) by Joy of Code
- [KitQL - The native SvelteKit library for GraphQL](https://www.youtube.com/watch?v=6pH4fnFN70w) by Jean-Yves COUËT
**Libraries, Tools & Components**
- [gosvelte](https://github.com/sachinbhutani/gosvelte) is a proof of concept to serve Svelte-generated pages on GoLang HTTP server with server data being sent as props to svelte components
- [svelte-ethers-store](https://www.npmjs.com/package/svelte-ethers-store) uses the ethers.js library as a collection of readable Svelte stores for Svelte, Sapper or SvelteKit
- [Fluid Grid](https://fluid-grid.com/) is a CSS grid system for future web
- [stirstack](https://github.com/seeReadCode/stirstack) is an opinionated framework that combines Svelte.js, TailwindCSS, InertiaJS and Ruby on Rails
- [OATHqr](https://codeberg.org/vhs/oathqr) helps users create security credentials for use with 2FA/MFA and other OATH-enabled apps. Use it to generate scannable QR codes for one-time password authenticator apps such as Aegis or YubiKey
- [svelte-GridTiles](https://github.com/honeybeeSunshine/svelte-GridTiles) is a drag and drop resizable tiles library built on a responsive grid
- [Miscellaneous Svelte Components](https://github.com/alex-knyaz/Miscellaneous-svelte-components/) is a collection of miscellaneous svelte components alex-knyaz often use in my projects
- [walk-and-graph-svelte-components](https://github.com/j2l/walk-and-graph-svelte-components) is a CLI node script to walk svelte and js files, to draw a beautiful JPG of your dependencies aka "imports"
- [Felte](https://www.npmjs.com/package/felte) is a simple to use form library for Svelte
- [svelte-use-tooltip](https://github.com/untemps/svelte-use-tooltip) is a Svelte action to display a tooltip
- [persistent-svelte-store](https://github.com/omer-g/persistent-svelte-store) is a generic persistent writable store, built from scratch in TypeScript according to the Svelte store contract
What'd we miss? Join us on [Reddit](https://www.reddit.com/r/sveltejs/) or [Discord](https://discord.com/invite/yy75DKs) to continue the conversation.
See y'all next month!

@ -0,0 +1,104 @@
---
title: "What's new in Svelte: April 2022"
description: "Goodbye fallthrough routes, hello param validators!"
author: Daniel Sandoval
authorURL: https://desandoval.net
---
This month, we felt a shift in the way SvelteKit handles page properties. The last holdout of the use-cases that required fallthrough routes, validating parameter properties, has been replaced by a more specific solution.
More on that, and what else is new in Svelte, as we dive in...
## What's new in SvelteKit
- Param matchers allow you to check if a url parameter matches before rendering a page - replacing the need for fallthrough routes for this purpose ([Docs](https://kit.svelte.dev/docs/routing#advanced-routing-matching), [#4334](https://github.com/sveltejs/kit/pull/4334))
- Explicit redirects can now be handled directly from endpoints ([#4260](https://github.com/sveltejs/kit/pull/4260))
- `svelte-kit sync` ([#4182](https://github.com/sveltejs/kit/pull/4182)), TypeScript 4.6 ([#4190](https://github.com/sveltejs/kit/pull/4190)) and Vite 2.9 were released - adding non-blocking dependency optimization and experimental CSS source maps in dev mode as well as a number of bug fixes contributed by the SvelteKit team ([#4468](https://github.com/sveltejs/kit/pull/4468))
**New Config Options**
- `outDir` fixes path issues in monorepos and other situations where the desired output directory is outside the project directory ([Docs](https://kit.svelte.dev/docs/configuration#outdir), [#4176](https://github.com/sveltejs/kit/pull/4176))
- `endpointExtensions` prevents files other than .js and .ts files from being treated as endpoints, unless you specify endpointExtensions ([Docs](https://kit.svelte.dev/docs/configuration#endpointextensions), [#4197](https://github.com/sveltejs/kit/pull/4197))
- `prerender.default` lets you prerender every page without having to write `export const prerender = true` in every page file ([Docs](https://kit.svelte.dev/docs/configuration#prerender), [#4192](https://github.com/sveltejs/kit/pull/4192))
**Breaking Changes**
- Fallthrough routes have been removed. For migration tips, check out the PR ([#4330](https://github.com/sveltejs/kit/pull/4330))
- `tabindex="-1"` is only added to `<body>` during navigation ([#4140](https://github.com/sveltejs/kit/pull/4140) and [#4184](https://github.com/sveltejs/kit/pull/4184))
- Adapters are now required to supply a `getClientAddress` function ([#4289](https://github.com/sveltejs/kit/pull/4289))
- `InputProps` and `OutputProps` can now be typed separately in generated `Load` ([#4305](https://github.com/sveltejs/kit/pull/4305))
- The `\$` character is no longer allowed in dynamic parameters ([#4334](https://github.com/sveltejs/kit/pull/4334))
- `svelte-kit package` has been marked as experimental so changes to it after Kit 1.0 will not be considered breaking ([#4164](https://github.com/sveltejs/kit/pull/4164))
## New across the Svelte ecosystem
- Svelte: Lots of new types for TypeScript and Svelte plugin users - including `style:` directives and Svelte Actions (**3.46.4** and **3.46.5**)
- Language Tools: Svelte project files are now importable/findable through references without having them imported in a TS file ([105.13.0](https://github.com/sveltejs/language-tools/releases/tag/extensions-105.13.0))
- Language Tools: Region folding is now supported in html with `<!--#region-->`/`<!--#endregion-->` ([105.13.0](https://github.com/sveltejs/language-tools/releases/tag/extensions-105.13.0))
---
## Community Showcase
**Apps & Sites built with Svelte**
- [Launcher](https://launcher.team/) is an open-source app launcher powered by SvelteKit, Prisma, and Tailwind
- [Paaster](https://paaster.io/) is a secure by default end to end encrypted pastebin built with Svelte, Vite, Typescript, Python, Starlette, rclone & Docker.
- [Simple AF Video Converter](https://github.com/berlyozzy/Simple-AF-Video-Converter) is an Electron wrapper around ffmpeg.wasm to make converting videos between formats easier
- [Streamchaser](https://github.com/streamchaser/streamchaser) seeks to simplify movie, series and documentary search through a centralized entertainment technology platform
- [Svelte Color Picker](https://github.com/V-Py/svelte-material-color-picker) is a simple color picker made with Svelte
- [ConcertMash](https://github.com/mcmxcdev/ConcertMash) is a small website that interacts with the Spotify API and generates new playlists based on the upcoming concerts you're attending
- [Modulus](https://modulus.vision/) is a Design+Code Think Tank conceived with the main mission to evolve design and technology
- [Multiply](https://www.multiply.us/) is an integrated PR and Social agency moving at the speed of culture
- [yia!](https://www.yia.co.nz/) is a Young Innovator Award competition in New Zealand
- [Write to Russia](https://www.writetorussia.org/index) is a community email writing platform to communicate with public `.ru` email addresses
- [Markdown Playground](https://github.com/Petros-K/markdown-playground) is an online playground dedicated for your markdown experiments.
- [RatherMisty](https://rathermisty.com/) is a no frills weather app with weather data from Open-Meteo
- [Minecraft Profile Pic (MCPFP)](https://github.com/MauritsWilke/mcpfp) is a site to generate Minecraft profile pictures with ease
- [WebGL Fluid Simulation](https://github.com/jpaquim/svelte-webgl-fluid-simulation) is a configurable fluid simulation built with Svelte and WebGL
- [This @NobelPeaceOslo exhibition](https://twitter.com/perbyhring/status/1504754949791621120) was built using printed graphics, projected motion graphics, particle animations and generative sound design
Itching to contribute to a modern SvelteKit website? [Help build the Svelte Society site](https://github.com/svelte-society/sveltesociety.dev/issues)!
**Learning Resources**
_To Attend_
- [Svelte Summit: Spring](https://www.sveltesummit.com/) will take place on April 30, 2022! Join us for the 5th virtual Svelte conference on [YouTube](https://www.sveltesummit.com/) and Discord 🍾
_To Read_
- [Svelte(Kit) TypeScript Showcase + general TypeScript tips](https://github.com/ivanhofer/sveltekit-typescript-showcase) by Hofer Ivan
- [Local constants in Svelte with the @const tag](https://geoffrich.net/posts/local-constants/) by Geoff Rich
- [Design Patterns for Building Reusable Svelte Components](https://render.com/blog/svelte-design-patterns) by Eric Liu
- [Svelte is better than React](https://labs.hamy.xyz/posts/svelte-is-better-than-react/) by Hamilton Greene
- [Making Visualizations Literally with Svelte and D3](https://www.connorrothschild.com/post/svelte-and-d3) by Connor Rothschild
- [Coordinating Multiple Elements with Svelte Deferred Transitions](https://imfeld.dev/writing/svelte_deferred_transitions) by Daniel Imfeld
- [Animate on scroll with Svelte Inview - Little Bits](https://dev.to/maciekgrzybek/animate-on-scroll-with-svelte-inview-266f) by Maciek Grzybek
- [Lazy-Loading Firebase with SvelteKit](https://www.captaincodeman.com/lazy-loading-firebase-with-sveltekit) and [HeadlessUI Components with Svelte](https://www.captaincodeman.com/headlessui-components-with-svelte) by Captain Codeman
- [SvelteKit Accessibility Testing: Automated CI A11y Tests](https://rodneylab.com/sveltekit-accessibility-testing/) by Rodney Lab
- [Getting Started with KitQL and GraphCMS](https://scottspence.com/posts/getting-started-with-kitql-and-graphcms) by Scott Spence
- [React ⇆ Svelte Cheatsheet](https://dev.to/joshnuss/react-to-svelte-cheatsheet-1a2a) lists the similarities and differences between the two libraries - by Joshua Nussbaum
_To Watch_
- [Svelte Extravaganza | Async](https://www.youtube.com/watch?v=mT4CLVHgtSg) by pngwn
- [6 Svelte Packages You Should Know](https://www.youtube.com/watch?v=y5SrUKcX_Co) and [Basic React To Svelte Conversion](https://www.youtube.com/watch?v=DiSuwLlhOxs) by LevelUpTuts
- [Page/Shadow Endpoint in SvelteKit](https://www.youtube.com/watch?v=j-9D5UDyVOM) by WebJeda
- [Custom Svelte Store: Higher Order Store](https://www.youtube.com/watch?v=p1aPfVyZ1IY) by lihautan
- [SvelteKit For Beginners (Playlist)](https://www.youtube.com/watch?v=bLBHecY4-ak&list=PLA9WiRZ-IS_zXZZyW4qfj0akvOAtk6MFS) by Joy of Code - follow along with the [blog guide](https://joyofcode.xyz/sveltekit-for-beginners)
- [Fullstack SvelteKit Auth 🔐 with Firebase & Magic Links! 🪄](https://www.youtube.com/watch?v=MAHE4iQgh5Q) by Johnny Magrippis
- [Firebase Authentication in SvelteKit! Full Stack App](https://www.youtube.com/watch?v=N6Y3hqhZvNI) by Ryan Boddy
**Libraries, Tools & Components**
- [SvelTable](https://sveltable.io/) is a feature rich, data table component built with Svelte
- [svelte-cyberComp](https://github.com/Cybersteam00/svelte-cyberComp) is a powerful, lightweight component library written in Svelte and Typescript
- [Flowbite Svelte](https://github.com/shinokada/flowbite-svelte) is an unofficial Flowbite component library for Svelte
- [Svelte-Tide-Project](https://github.com/jbertovic/svelte-tide-project) is a starter template for Svelte frontend apps with Rust Tide backend server
- [Fetch Inject](https://github.com/vhscom/fetch-inject#sveltekit) implements a performance optimization technique for managing asynchronous JavaScript dependencies - now with Svelte support
- [svelte-utterances](https://github.com/shinokada/svelte-utterances) is a lightweight comments widget built on GitHub issues
- [Liquivelte](https://github.com/malipetek/liquivelte-vscode) allows you to create your Shopify theme with Svelte-like components
- [@storyblok/svelte](https://github.com/storyblok/storyblok-svelte) is the Svelte SDK you need to interact with Storyblok API and enable the Real-time Visual Editing Experience
- [@svelte-on-solana/wallet-adapter](https://github.com/svelte-on-solana/wallet-adapter) is a modular TypeScript wallet adapter and UI components for Solana/Anchor applications using SvelteJS as framework
- [svelte-lookat](https://www.npmjs.com/package/svelte-lookat) creates a div which makes all its children follow the mouse cursor or the user's face when using a mobile phone
Join us on [Reddit](https://www.reddit.com/r/sveltejs/) or [Discord](https://discord.com/invite/yy75DKs) to continue the conversation.
See y'all next month!

@ -0,0 +1,89 @@
---
title: "What's new in Svelte: May 2022"
description: "Dynamically switch between HTML element types with `<svelte:element>`"
author: Daniel Sandoval
authorURL: https://desandoval.net
---
With yesterday's Svelte Summit behind us, we've got a lot of news to share! Check out all of the recordings on the [Svelte Society YouTube Channel](https://www.youtube.com/sveltesociety) and the rest of this month's updates below...
## What's new in Svelte
- The `<svelte:element>` element lets you render an element of a dynamically specified type. This is useful, for example, when rendering rich text content from a CMS. Check out the [docs](https://svelte.dev/docs#template-syntax-svelte-element) or the [tutorial](https://svelte.dev/tutorial/svelte-element) for more info (**3.47.0**)!
## Language Tools updates
- `svelte:element` and `sveltekit:reload` are now supported
- Invalid Svelte import paths will now be automatically detected - see PR for getting back the old behavior ([#1448](https://github.com/sveltejs/language-tools/pull/1448))
- `source.sortImports` lets you sort imports without deleting unused imports ([#1338](https://github.com/sveltejs/language-tools/issues/1338))
- Hovering over HTML attributes will now show HTML hover info instead of the TS hover info - resulting in much more useful information ([#1447](https://github.com/sveltejs/language-tools/pull/1447))
- In VS Code, you can now wrap existing blocks of code in control flow tags using the `Insert Snippet` command ([#1373](https://github.com/sveltejs/language-tools/pull/1373))
## What's new in SvelteKit
- Files and directories can now be named `__tests__` and `__test__` in the routes directory ([#4438](https://github.com/sveltejs/kit/pull/4438))
- Netlify Edge Functions ([#4657](https://github.com/sveltejs/kit/pull/4657)) and the Vercel build output API ([#4663](https://github.com/sveltejs/kit/pull/4663)) are now supported
- Custom `load` dependencies, array of strings representing URLs the page depends on, are now available when loading routes ([Docs](https://kit.svelte.dev/docs/loading#output-dependencies), [#4536](https://github.com/sveltejs/kit/pull/4536))
**Breaking Changes**
- Validators are now called "matchers" ([Docs](https://kit.svelte.dev/docs/routing#advanced-routing-matching), [#4358](https://github.com/sveltejs/kit/pull/4358))
- `__layout.reset` has been replaced by named layouts - which have much configurability for shared layout elements ([Docs](https://kit.svelte.dev/docs/layouts#named-layouts), [#4388](https://github.com/sveltejs/kit/pull/4388))
- Prerendering is now skipped for `rel="external"` links ([#4545](https://github.com/sveltejs/kit/pull/4545))
- `maxage` is now `cache` in `LoadOutput` ([#4690](https://github.com/sveltejs/kit/pull/4690))
---
## Community Showcase
**Apps & Sites built with Svelte**
- [polySpectra AR](https://ar.polyspectra.com/) lets you prototype faster 3D Printing with seamless AR file handoffs ([video demo](https://www.youtube.com/watch?v=VhYCeVGcG3E))
- [Pixel Art Together](https://github.com/liveblocks/pixel-art-together) is a free multiplayer pixel art editor powered by Liveblocks
- [Tooling Manager](https://tooling-manager.netlify.app/) lets you compare your JavaScript tech stack against industry standard boilerplates
- [Easy Portfolio](https://easy-portfolio.com/) generates a portfolio based on your GitHub profile
- [FLOAT](https://github.com/muttoni/float) is an attendance tracking program for events
- [The Coin Perspective](https://thecoinperspective.com/) is a cryptocurrency price tracker and portfolio management tool
- [Locutionis](https://github.com/pbouillon/locutionis) is a small, online reference of figures of speech (en français)
- [ASM Editor](https://asm-editor.specy.app/) is an all in one web editor for M68K and MIPS
- [Otium](https://github.com/alombi/otium) is a free and open source book manager and bookshelf organiser, that helps you managing your books and the ones you would like to read
- [Sinwaver](https://github.com/Hugo-Dz/Sinwaver) is an SVG sine wave generator
Want to contribute to a modern SvelteKit website? [Help build the Svelte Society site](https://github.com/svelte-society/sveltesociety.dev/issues)!
**Learning Resources**
_To Read_
- [4 tips for cleaner Svelte components](https://geoffrich.net/posts/clean-component-tips/) by Geoff Rich
- [Building a Clubhouse clone with Svelte and 100ms](https://www.100ms.live/blog/clubhouse-clone-with-svelte) By Seun Taiwo
- [SvelteKit uvu Testing: Fast Component Unit Tests](https://rodneylab.com/sveltekit-uvu-testing/) by Rodney Lab
- [SvelteKit JWT authentication tutorial](https://dev.to/pilcrowonpaper/sveltekit-jwt-authentication-tutorial-2m34) by pilcrowOnPaper
- [Converting a Rollup-based Svelte SPA to SvelteKit](https://github.com/sveltejs/kit/discussions/4595) by Simon H
- [Add Commitint, Commitizen, Standard Version, and Husky to SvelteKit Project](https://davipon.hashnode.dev/add-commitint-commitizen-standard-version-and-husky-to-sveltekit-project) by David Peng
_To Watch or Hear_
- [Rich Harris - The Road to SvelteKit 1.0 (Svelte Society NYC)](https://www.youtube.com/watch?v=s6a1pbTVcUs) by Svelte Society
- [Svelte Fundamentals - Intro to Svelte](https://codingcat.dev/course/intro-to-svelte) by Coding Cat
- [Svelte Components Using Custom Markdown Renderers - Weekly Svelte](https://www.youtube.com/watch?v=ZiEROAqobwM) by LevelUpTuts
- [Implementing {@const} in if block](https://www.youtube.com/watch?v=f5iReGqjmG0) by lihautan
- [Svelte and Contributing to Open-Source with Geoff Rich](https://podcast.20minjs.com/1952066/10417700-episode-6-svelte-and-contributing-to-open-source-with-geoff-rich) by 20minJS
**Libraries, Tools & Components**
- [KitDocs](https://github.com/svelteness/kit-docs) is a documentation integration for SvelteKit - like VitePress for Svelte.
- [Svelte Copy](https://github.com/ghostdevv/svelte-copy) is a click/tap-to-copy library that makes it easy to copy to the clipboard
- [Svend3r](https://github.com/oslabs-beta/svend3r) provides beautiful visualizations that harness the power of D3 to bring your data to life while abstracting away its imperative-style code
- [Svelte Hamburgers](https://github.com/ghostdevv/svelte-hamburgers) is the easy to use Hamburger menu component for Svelte
- [Svelte Droplet](https://github.com/probablykasper/svelte-droplet) is a file dropzone for Svelte
- [Svelte MP3](https://www.npmjs.com/package/svelte-mp3) is a light blazingly fast yet simple minimalistic audio player for Svelte
- [SvelteUI](https://github.com/Brisklemonade/svelteui) is a component library for building fully functional & accessible web applications faster than ever
- [svelte-spotlight](https://github.com/beynar/svelte-spotlight) is a headless spotlight component to help you build your site's global search box in minutes
- [svelte-pdf-simple](https://github.com/gspasov/svelte-pdf-simple) is a simple svelte library for displaying PDFs and giving you all the control
- [persistent-svelte-store](https://github.com/omer-g/persistent-svelte-store) is a generic persistent writable store, built from scratch in TypeScript according to the Svelte store contract
- [svelte-exmarkdown](https://github.com/ssssota/svelte-exmarkdown) is Svelte component to render markdown dynamically
- [Bookit](https://github.com/leveluptuts/bookit) is a storybook-like component rendering environment - finely tuned to work directly within your SvelteKit projects
Join us on [Reddit](https://www.reddit.com/r/sveltejs/) or [Discord](https://discord.com/invite/yy75DKs) to continue the conversation.
If you'd prefer to join us in person, Svelte Summit is finally transitioning properly into the real world. Come join us for two days of awesome Svelte content! [Get your tickets now!](https://ti.to/svelte/svelte-summit-fall-edition)
See y'all next month!

@ -465,16 +465,16 @@ The `{@const ...}` tag defines a local constant.
```sv ```sv
<script> <script>
export let boxes; export let boxes;
</script> </script>
{#each boxes as box} {#each boxes as box}
{@const area = box.width * box.height} {@const area = box.width * box.height}
{box.width} * {box.height} = {area} {box.width} * {box.height} = {area}
{/each} {/each}
``` ```
`{@const}` is only allowed as direct child of `{#each}`, `{:then}`, `{:catch}`, `<Component />` or `<svelte:fragment />`. `{@const}` is only allowed as direct child of `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<Component />` or `<svelte:fragment />`.
### Element directives ### Element directives
@ -627,7 +627,7 @@ On `<input>` elements with `type="file"`, you can use `bind:files` to get the [`
--- ---
`bind:` can be used together with `on:` directives. The order that they are defined in determines the value of the bound variable when the event handler is called. If you're using `bind:` directives together with `on:` directives, the order that they're defined in affects the value of the bound variable when the event handler is called.
```sv ```sv
<script> <script>
@ -641,6 +641,7 @@ On `<input>` elements with `type="file"`, you can use `bind:files` to get the [`
/> />
``` ```
Here we were binding to the value of a text input, which uses the `input` event. Bindings on other elements may use different events such as `change`.
##### Binding `<select>` value ##### Binding `<select>` value
@ -1627,6 +1628,30 @@ If `this` is falsy, no component is rendered.
<svelte:component this={currentSelection.component} foo={bar}/> <svelte:component this={currentSelection.component} foo={bar}/>
``` ```
### `<svelte:element>`
```sv
<svelte:element this={expression}/>
```
---
The `<svelte:element>` element lets you render an element of a dynamically specified type. This is useful for example when displaying rich text content from a CMS. Any properties and event listeners present will be applied to the element.
The only supported binding is `bind:this`, since the element type specific bindings that Svelte does at build time (e.g. `bind:value` for input elements) does not work with a dynamic tag type.
If `this` has a nullish value, the element and its children will not be rendered.
If `this` is the name of a void tag (e.g., `br`) and `<svelte:element>` has child elements, a runtime error will be thrown in development mode.
```sv
<script>
let tag = 'div';
export let handler;
</script>
<svelte:element this={tag} on:click={handler}>Foo</svelte:element>
```
### `<svelte:window>` ### `<svelte:window>`
@ -1683,7 +1708,7 @@ All except `scrollX` and `scrollY` are readonly.
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#template-syntax-element-directives-use-action) on the `<body>` element. 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#template-syntax-element-directives-use-action) on the `<body>` element.
`<svelte:body>` also has to appear at the top level of your component. As with `<svelte:window>`, this element may only appear the top level of your component and must never be inside a block or element.
```sv ```sv
<svelte:body <svelte:body
@ -1704,7 +1729,7 @@ Similarly to `<svelte:window>`, this element allows you to add listeners to even
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:body>`, 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 may only appear at the top level of your component and must never be inside a block or element.
```sv ```sv
<svelte:head> <svelte:head>

@ -148,7 +148,7 @@ setContext(key: any, context: any)
--- ---
Associates an arbitrary `context` object with the current component and the specified `key`. The context is then available to children of the component (including slotted content) with `getContext`. Associates an arbitrary `context` object with the current component and the specified `key` and returns that object. The context is then available to children of the component (including slotted content) with `getContext`.
Like lifecycle functions, this must be called during component initialisation. Like lifecycle functions, this must be called during component initialisation.
@ -221,14 +221,14 @@ Retrieves the whole context map that belongs to the closest parent component. Mu
#### `createEventDispatcher` #### `createEventDispatcher`
```js ```js
dispatch: ((name: string, detail?: any) => void) = createEventDispatcher(); dispatch: ((name: string, detail?: any, options?: DispatchOptions) => boolean) = createEventDispatcher();
``` ```
--- ---
Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). Event dispatchers are functions that can take two arguments: `name` and `detail`. Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). Event dispatchers are functions that can take two arguments: `name` and `detail`.
Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture) and are not cancellable with `event.preventDefault()`. The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data. Component events created with `createEventDispatcher` create a [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property and can contain any type of data.
```sv ```sv
<script> <script>
@ -254,6 +254,27 @@ Events dispatched from child components can be listened to in their parent. Any
<Child on:notify="{callbackFunction}"/> <Child on:notify="{callbackFunction}"/>
``` ```
---
Events can be cancelable by passing a third parameter to the dispatch function. The function returns `false` if the event is cancelled with `event.preventDefault()`, otherwise it returns `true`.
```sv
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function notify() {
const shouldContinue = dispatch('notify', 'detail value', { cancelable: true });
if (shouldContinue) {
// no one called preventDefault
} else {
// a listener called preventDefault
}
}
</script>
```
### `svelte/store` ### `svelte/store`
The `svelte/store` module exports functions for creating [readable](/docs#run-time-svelte-store-readable), [writable](/docs#run-time-svelte-store-writable) and [derived](/docs#run-time-svelte-store-derived) stores. The `svelte/store` module exports functions for creating [readable](/docs#run-time-svelte-store-readable), [writable](/docs#run-time-svelte-store-writable) and [derived](/docs#run-time-svelte-store-derived) stores.
@ -537,10 +558,34 @@ A `spring` store gradually changes to its target value based on its `stiffness`
--- ---
As with [`tweened`](/docs#run-time-svelte-motion-tweened) stores, `set` and `update` return a Promise that resolves if the spring settles. The `store.stiffness` and `store.damping` properties can be changed while the spring is in motion, and will take immediate effect. All of the options above can be changed while the spring is in motion, and will take immediate effect.
```js
const size = spring(100);
size.stiffness = 0.3;
size.damping = 0.4;
size.precision = 0.005;
```
---
As with [`tweened`](/docs#run-time-svelte-motion-tweened) stores, `set` and `update` return a Promise that resolves if the spring settles.
Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`. Both `set` and `update` can take a second argument — an object with `hard` or `soft` properties. `{ hard: true }` sets the target value immediately; `{ soft: n }` preserves existing momentum for `n` seconds before settling. `{ soft: true }` is equivalent to `{ soft: 0.5 }`.
```js
const coords = spring({ x: 50, y: 50 });
// updates the value immediately
coords.set({ x: 100, y: 200 }, { hard: true });
// preserves existing momentum for 1s
coords.update(
(target_coords, coords) => {
return { x: target_coords.x, y: coords.y };
},
{ soft: 1 }
);
```
[See a full example on the spring tutorial.](/tutorial/spring) [See a full example on the spring tutorial.](/tutorial/spring)
```sv ```sv

@ -2,11 +2,7 @@
title: Compile time title: Compile time
--- ---
Typically, you won't interact with the Svelte compiler directly, but will instead integrate it into your build system using a bundler plugin: Typically, you won't interact with the Svelte compiler directly, but will instead integrate it into your build system using a bundler plugin. The bundler plugin that the Svelte team most recommends and invests in is [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte). The [SvelteKit](https://kit.svelte.dev/) framework provides a setup leveraging `vite-plugin-svelte` to build applications as well as a [tool for packaging Svelte component libraries](https://kit.svelte.dev/docs/packaging). Svelte Society maintains a list of [other bundler plugins](https://sveltesociety.dev/tools/#bundling) for additional tools like Rollup and Webpack.
* [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)
* or one of the [community-maintained plugins](https://sveltesociety.dev/tools)
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.
@ -77,7 +73,7 @@ The following options can be passed to the compiler. None are required:
| `accessors` | `false` | If `true`, getters and setters will be created for the component's props. If `false`, they will only be created for readonly exported values (i.e. those declared with `const`, `class` and `function`). If compiling with `customElement: true` this option defaults to `true`. | `accessors` | `false` | If `true`, getters and setters will be created for the component's props. If `false`, they will only be created for readonly exported values (i.e. those declared with `const`, `class` and `function`). If compiling with `customElement: true` this option defaults to `true`.
| `customElement` | `false` | If `true`, tells the compiler to generate a custom element constructor instead of a regular Svelte component. | `customElement` | `false` | If `true`, tells the compiler to generate a custom element constructor instead of a regular Svelte component.
| `tag` | `null` | A `string` that tells Svelte what tag name to register the custom element with. It must be a lowercase alphanumeric string with at least one hyphen, e.g. `"my-element"`. | `tag` | `null` | A `string` that tells Svelte what tag name to register the custom element with. It must be a lowercase alphanumeric string with at least one hyphen, e.g. `"my-element"`.
| `css` | `true` | If `true`, styles will be included in the JavaScript class and injected at runtime. It's recommended that you set this to `false` and use the CSS that is statically generated, as it will result in smaller JavaScript bundles and better performance. | `css` | `true` | If `true`, styles will be included in the JavaScript class and injected at runtime for the components actually rendered. If `false`, the CSS will be returned in the `css` field of the compilation result. Most Svelte bundler plugins will set this to `false` and use the CSS that is statically generated for better performance, as it will result in smaller JavaScript bundles and the output can be served as cacheable `.css` files.
| `cssHash` | See right | A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. It defaults to returning `svelte-${hash(css)}` | `cssHash` | See right | A function that takes a `{ hash, css, name, filename }` argument and returns the string that is used as a classname for scoped CSS. It defaults to returning `svelte-${hash(css)}`
| `loopGuardTimeout` | 0 | A `number` that tells Svelte to break the loop if it blocks the thread for more than `loopGuardTimeout` ms. This is useful to prevent infinite loops. **Only available when `dev: true`** | `loopGuardTimeout` | 0 | A `number` that tells Svelte to break the loop if it blocks the thread for more than `loopGuardTimeout` ms. This is useful to prevent infinite loops. **Only available when `dev: true`**
| `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out. | `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out.

@ -4,7 +4,7 @@
let photos = []; let photos = [];
onMount(async () => { onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`); const res = await fetch(`/tutorial/api/album`);
photos = await res.json(); photos = await res.json();
}); });
</script> </script>

@ -0,0 +1,12 @@
<script>
const options = ['h1', 'h3', 'p'];
let selected = options[0];
</script>
<select bind:value={selected}>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</select>
<svelte:element this={selected}>I'm a {selected} tag</svelte:element>

@ -2,4 +2,4 @@
question: How do I do hot module reloading? question: How do I do hot module reloading?
--- ---
We recommend using [SvelteKit](https://kit.svelte.dev/), which supports HMR out of the box and is built on top of Vite and svelte-hmr. There are also community plugins for [rollup](https://github.com/rixo/rollup-plugin-svelte-hot) and [webpack](https://github.com/rixo/svelte-loader-hot). We recommend using [SvelteKit](https://kit.svelte.dev/), which supports HMR out of the box and is built on top of [Vite](https://vitejs.dev/) and [svelte-hmr](https://github.com/sveltejs/svelte-hmr). There are also community plugins for [rollup](https://github.com/rixo/rollup-plugin-svelte-hot) and [webpack](https://github.com/sveltejs/svelte-loader).

@ -0,0 +1,5 @@
---
question: Are there any third-party resources?
---
Svelte Society maintains a [list of books and videos](https://sveltesociety.dev/resources).

@ -1,18 +0,0 @@
---
question: Are there any video courses?
---
Rich Harris, the creator of Svelte, taught a course:
- [Frontend Masters](https://frontendmasters.com/courses/svelte/)
There are also a number of third-party courses:
- [Egghead](https://egghead.io/browse/frameworks/svelte)
- [Udemy](https://www.udemy.com/courses/search/?q=sveltejs+svelte) (Note: Udemy frequently has discounts over 90%)
- [Pluralsight](https://www.pluralsight.com/search?q=svelte)
Finally, there are also YouTube channels and playlists that teach Svelte:
- [Svelte Master](https://www.youtube.com/channel/UCg6SQd5jnWo5Y70rZD9SQFA)
- [Svelte Tutorial for Beginners](https://www.youtube.com/watch?v=zojEMeQGGHs&list=PL4cUxeGkcC9hlbrVO_2QFVqVPhlZmz7tO) by The Net Ninja

@ -1,9 +0,0 @@
---
question: Are there any books?
---
There are a few books:
- [Svelte Handbook](https://flaviocopes.com/page/svelte-handbook/) by Flavio Copes
- [Svelte 3 Up and Running](https://www.amazon.com/dp/B08D6T6BKS/) by Alessandro Segala
- [Svelte and Sapper in Action](https://www.manning.com/books/svelte-and-sapper-in-action) by R. Mark Volkmann

@ -8,4 +8,5 @@ There are a few approaches that people take when testing, but it generally invol
Some resources for getting started with unit testing: Some resources for getting started with unit testing:
- [Svelte Testing Library](https://testing-library.com/docs/svelte-testing-library/example/) - [Svelte Testing Library](https://testing-library.com/docs/svelte-testing-library/example/)
- [Example using vitest](https://github.com/vitest-dev/vitest/tree/main/examples/svelte)
- [Example using uvu test runner with JSDOM](https://github.com/lukeed/uvu/tree/master/examples/svelte) - [Example using uvu test runner with JSDOM](https://github.com/lukeed/uvu/tree/master/examples/svelte)

@ -2,9 +2,9 @@
title: Dynamic attributes title: Dynamic attributes
--- ---
Just like you can use curly braces to control text, you can use them to control element attributes. You can use curly braces to control element attributes, just like you use them to control text.
Our image is missing a `src` — let's add one: Our image is missing a `src` attribute — let's add one:
```html ```html
<img src={src}> <img src={src}>
@ -24,7 +24,6 @@ In this case, we're missing the `alt` attribute that describes the image for peo
We can use curly braces *inside* attributes. Try changing it to `"{name} dances."` — remember to declare a `name` variable in the `<script>` block. We can use curly braces *inside* attributes. Try changing it to `"{name} dances."` — remember to declare a `name` variable in the `<script>` block.
## Shorthand attributes ## Shorthand attributes
It's not uncommon to have an attribute where the name and value are the same, like `src={src}`. Svelte gives us a convenient shorthand for these cases: It's not uncommon to have an attribute where the name and value are the same, like `src={src}`. Svelte gives us a convenient shorthand for these cases:
@ -32,4 +31,3 @@ It's not uncommon to have an attribute where the name and value are the same, li
```html ```html
<img {src} alt="A man dances."> <img {src} alt="A man dances.">
``` ```

@ -2,9 +2,13 @@
title: Nested components title: Nested components
--- ---
It would be impractical to put your entire app in a single component. Instead, we can import components from other files and include them as though we were including elements. It would be impractical to put your entire app in a single component. Instead, we can import components from other files and then use them as though we were including elements.
Add a `<script>` tag that imports `Nested.svelte`... We now present you 2 files in the editor on the right (upper bar) to click on, `App.svelte` and `Nested.svelte`.
Each `.svelte` file holds a component that is a reusable self-contained block of code that encapsulates HTML, CSS, and JavaScript that belong together.
Let's add a `<script>` tag to `App.svelte` that imports the file (our component) `Nested.svelte` into our app...
```html ```html
<script> <script>
@ -12,7 +16,7 @@ Add a `<script>` tag that imports `Nested.svelte`...
</script> </script>
``` ```
...then add it to the markup: ...then use component `Nested` in the app markup:
```html ```html
<p>This is a paragraph.</p> <p>This is a paragraph.</p>

@ -14,7 +14,11 @@ First, you'll need to integrate Svelte with a build tool. There are officially m
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.
You'll also want to configure your text editor. There are [plugins](https://sveltesociety.dev/tools#editor-support) for many popular editors as well as an official [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If your editor does not have a Svelte plugin then you can follow [this guide](/blog/setting-up-your-editor) to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting. You'll also want to configure your text editor. There are [plugins](https://sveltesociety.dev/tools#editor-support) for many popular editors as well as an official [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
<!--
NOTE: Removed until we have better place for setting-up-your-editor guide. See https://github.com/sveltejs/svelte/pull/7310#issuecomment-1049923609
If your editor does not have a Svelte plugin then you can follow [this guide](/blog/setting-up-your-editor) to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting. -->
Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`: Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`:

@ -2,9 +2,11 @@
title: Updating arrays and objects title: Updating arrays and objects
--- ---
Because Svelte's reactivity is triggered by assignments, using array methods like `push` and `splice` won't automatically cause updates. For example, clicking the button doesn't do anything. Svelte's reactivity is triggered by assignments. Methods that mutate arrays or objects will not trigger updates by themselves.
One way to fix that is to add an assignment that would otherwise be redundant: In this example, clicking the "Add a number" button calls the `addNumber` function, which appends a number to the array but doesn't trigger the recalculation of `sum`.
One way to fix that is to assign `numbers` to itself to tell the compiler it has changed:
```js ```js
function addNumber() { function addNumber() {
@ -13,7 +15,7 @@ function addNumber() {
} }
``` ```
But there's a more idiomatic solution: You could also write this more concisely using the ES6 spread syntax:
```js ```js
function addNumber() { function addNumber() {
@ -21,7 +23,7 @@ function addNumber() {
} }
``` ```
You can use similar patterns to replace `pop`, `shift`, `unshift` and `splice`. The same rule applies to array methods such as `pop`, `shift`, and `splice` and to objects methods such as `Map.set`, `Set.add`, etc.
Assignments to *properties* of arrays and objects — e.g. `obj.foo += 1` or `array[i] = x` — work the same way as assignments to the values themselves. Assignments to *properties* of arrays and objects — e.g. `obj.foo += 1` or `array[i] = x` — work the same way as assignments to the values themselves.
@ -31,11 +33,22 @@ function addNumber() {
} }
``` ```
A simple rule of thumb: the name of the updated variable must appear on the left hand side of the assignment. For example this... However, indirect assignments to references such as this...
```js ```js
const foo = obj.foo; const foo = obj.foo;
foo.bar = 'baz'; foo.bar = 'baz';
``` ```
or
```js
function quox(thing) {
thing.foo.bar = 'baz';
}
quox(obj);
```
...won't trigger reactivity on `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`.
A simple rule of thumb: the updated variable must directly appear on the left hand side of the assignment.

@ -4,7 +4,7 @@ title: Keyed each blocks
By default, when you modify the value of an `each` block, it will add and remove items at the *end* of the block, and update any values that have changed. That might not be what you want. By default, when you modify the value of an `each` block, it will add and remove items at the *end* of the block, and update any values that have changed. That might not be what you want.
It's easier to show why than to explain. Click the 'Remove first thing' button a few times, and notice what happens: It removes the first `<Thing>` component, but the *last* DOM node. Then it updates the `name` value in the remaining DOM nodes, but not the emoji. It's easier to show why than to explain. Click the 'Remove first thing' button a few times, and notice what happens: it does not remove the first `<Thing>` component, but rather the *last* DOM node. Then it updates the `name` value in the remaining DOM nodes, but not the emoji.
Instead, we'd like to remove only the first `<Thing>` component and its DOM node, and leave the others unaffected. Instead, we'd like to remove only the first `<Thing>` component and its DOM node, and leave the others unaffected.

@ -10,4 +10,4 @@ We can also use `bind:value` with `<select>` elements. Update line 20:
Note that the `<option>` values are objects rather than strings. Svelte doesn't mind. Note that the `<option>` values are objects rather than strings. Svelte doesn't mind.
> Because we haven't set an initial value of `selected`, the binding will set it to the default value (the first in the list) automatically. Be careful though — until the binding is initialised, `selected` remains undefined, so we can't blindly reference e.g. `selected.id` in the template. > Because we haven't set an initial value of `selected`, the binding will set it to the default value (the first in the list) automatically. Be careful though — until the binding is initialised, `selected` remains undefined, so we can't blindly reference e.g. `selected.id` in the template. If your use case allows it, you could also set an initial value to bypass this problem.

@ -4,7 +4,7 @@
let photos = []; let photos = [];
onMount(async () => { onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`); const res = await fetch(`/tutorial/api/album`);
photos = await res.json(); photos = await res.json();
}); });
</script> </script>

@ -15,7 +15,7 @@ We'll add an `onMount` handler that loads some data over the network:
let photos = []; let photos = [];
onMount(async () => { onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`); const res = await fetch(`/tutorial/api/album`);
photos = await res.json(); photos = await res.json();
}); });
</script> </script>

@ -25,3 +25,5 @@ let coords = spring({ x: 50, y: 50 }, {
``` ```
Waggle your mouse around, and try dragging the sliders to get a feel for how they affect the spring's behaviour. Notice that you can adjust the values while the spring is still in motion. Waggle your mouse around, and try dragging the sliders to get a feel for how they affect the spring's behaviour. Notice that you can adjust the values while the spring is still in motion.
Consult the [API reference](/docs#run-time-svelte-motion-spring) for more information.

@ -0,0 +1,15 @@
<script>
let bgOpacity = 0.5;
$: color = bgOpacity < 0.6 ? '#000' : '#fff';
</script>
<input type="range" min="0" max="1" step="0.1" bind:value={bgOpacity} />
<p>This is a paragraph.</p>
<style>
p {
font-family: "Comic Sans MS", cursive;
background: rgba(255, 62, 0, var(--opacity));
}
</style>

@ -0,0 +1,15 @@
<script>
let bgOpacity = 0.5;
$: color = bgOpacity < 0.6 ? '#000' : '#fff';
</script>
<input type="range" min="0" max="1" step="0.1" bind:value={bgOpacity} />
<p style="color: {color}; --opacity: {bgOpacity};">This is a paragraph.</p>
<style>
p {
font-family: "Comic Sans MS", cursive;
background: rgba(255, 62, 0, var(--opacity));
}
</style>

@ -0,0 +1,10 @@
---
title: Inline styles
---
Apart from adding styles inside style tags, you can also add styles to individual elements using the style attribute. Usually you will want to do styling through CSS, but this can come in handy for dynamic styles, especially when combined with CSS custom properties.
Add the following style attribute to the paragraph element:
`style="color: {color}; --opacity: {bgOpacity};"`
Great, now you can style the paragraph using variables that change based on your input without having to make a class for every possible value.

@ -0,0 +1,15 @@
<script>
let bgOpacity = 0.5;
$: color = bgOpacity < 0.6 ? '#000' : '#fff';
</script>
<input type="range" min="0" max="1" step="0.1" bind:value={bgOpacity} />
<p style="color: {color}; --opacity: {bgOpacity};">This is a paragraph.</p>
<style>
p {
font-family: "Comic Sans MS", cursive;
background: rgba(255, 62, 0, var(--opacity));
}
</style>

@ -0,0 +1,15 @@
<script>
let bgOpacity = 0.5;
$: color = bgOpacity < 0.6 ? "#000" : "#fff";
</script>
<input type="range" min="0" max="1" step="0.1" bind:value={bgOpacity} />
<p style:color style:--opacity={bgOpacity}>This is a paragraph.</p>
<style>
p {
font-family: "Comic Sans MS", cursive;
background: rgba(255, 62, 0, var(--opacity));
}
</style>

@ -0,0 +1,18 @@
---
title: The style directive
---
Being able to set CSS properties dynamically is nice. However, this can get unwieldy if you have to write a long string. Mistakes like missing any of the semicolons could make the whole string invalid. Therefore, Svelte provides a nicer way to write inline styles with the style directive.
Change the style attribute of the paragraph to the following:
```html
<p
style:color
style:--opacity="{bgOpacity}"
>
```
The style directive shares a few qualities with the class directive. You can use a shorthand when the name of the property and the variable are the same. So `style:color="{color}"` can be written as just `style:color`.
Similar to the class directive, the style directive will take precedence if you try to set the same property through a style attribute.

@ -0,0 +1,3 @@
{
"title": "Advanced styling"
}

@ -1,3 +0,0 @@
{
"title": "Classes"
}

@ -0,0 +1,18 @@
<script>
const options = ['h1', 'h3', 'p'];
let selected = options[0];
</script>
<select bind:value={selected}>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</select>
{#if selected === 'h1'}
<h1>I'm a h1 tag</h1>
{:else if selected === 'h3'}
<h3>I'm a h3 tag</h3>
{:else if selected === 'p'}
<p>I'm a p tag</p>
{/if}

@ -0,0 +1,12 @@
<script>
const options = ['h1', 'h3', 'p'];
let selected = options[0];
</script>
<select bind:value={selected}>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</select>
<svelte:element this={selected}>I'm a {selected} tag</svelte:element>

@ -0,0 +1,23 @@
---
title: <svelte:element>
---
Sometimes we don't know in advance what kind of DOM element to render. `<svelte:element>` comes in handy here. Instead of a sequence of `if` blocks...
```html
{#if selected === 'h1'}
<h1>I'm a h1 tag</h1>
{:else if selected === 'h3'}
<h3>I'm a h3 tag</h3>
{:else if selected === 'p'}
<p>I'm a p tag</p>
{/if}
```
...we can have a single dynamic component:
```html
<svelte:element this={selected}>I'm a {selected} tag</svelte:element>
```
The `this` value can be any string, or a falsy value — if it's falsy, no element is rendered.

@ -16,7 +16,7 @@ Anything exported from a `context="module"` script block becomes an export from
</script> </script>
``` ```
...we can then import it from `App.svelte`... ...we can then import it in `App.svelte`...
```html ```html
<script> <script>

@ -519,7 +519,7 @@ export default class Component {
return result; return result;
} }
private _extract_exports(node: ExportDefaultDeclaration | ExportNamedDeclaration | ExportAllDeclaration, module_script) { private _extract_exports(node: ExportDefaultDeclaration | ExportNamedDeclaration | ExportAllDeclaration, module_script: boolean) {
if (node.type === 'ExportDefaultDeclaration') { if (node.type === 'ExportDefaultDeclaration') {
return this.error(node as any, compiler_errors.default_export); return this.error(node as any, compiler_errors.default_export);
} }
@ -539,7 +539,7 @@ export default class Component {
extract_names(declarator.id).forEach(name => { extract_names(declarator.id).forEach(name => {
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 (!module_script && variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(declarator as any, compiler_warnings.unused_export_let(this.name.name, name)); this.warn(declarator as any, compiler_warnings.unused_export_let(this.name.name, name));
} }
}); });
@ -559,7 +559,7 @@ export default class Component {
if (variable) { if (variable) {
variable.export_name = specifier.exported.name; variable.export_name = specifier.exported.name;
if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) { if (!module_script && variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(specifier as any, 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));
} }
} }

@ -242,17 +242,25 @@ export default {
code: 'invalid-animation', code: 'invalid-animation',
message: 'An element that uses the animate directive must be the immediate child of a keyed each block' message: 'An element that uses the animate directive must be the immediate child of a keyed each block'
}, },
invalid_animation_key: {
code: 'invalid-animation',
message: 'An element that uses the animate directive must be used inside a keyed each block. Did you forget to add a key to your each block?'
},
invalid_animation_sole: { invalid_animation_sole: {
code: 'invalid-animation', code: 'invalid-animation',
message: 'An element that uses the animate directive must be the sole child of a keyed each block' message: 'An element that uses the animate directive must be the sole child of a keyed each block'
}, },
invalid_animation_dynamic_element: {
code: 'invalid-animation',
message: '<svelte:element> cannot have a animate directive'
},
invalid_directive_value: { invalid_directive_value: {
code: 'invalid-directive-value', code: 'invalid-directive-value',
message: 'Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)' message: 'Can only bind to an identifier (e.g. `foo`) or a member expression (e.g. `foo.bar` or `foo[baz]`)'
}, },
invalid_const_placement: { invalid_const_placement: {
code: 'invalid-const-placement', code: 'invalid-const-placement',
message: '{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>' message: '{@const} must be the immediate child of {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>'
}, },
invalid_const_declaration: (name: string) => ({ invalid_const_declaration: (name: string) => ({
code: 'invalid-const-declaration', code: 'invalid-const-declaration',

@ -21,7 +21,8 @@ enum NodeExist {
} }
const whitelist_attribute_selector = new Map([ const whitelist_attribute_selector = new Map([
['details', new Set(['open'])] ['details', new Set(['open'])],
['dialog', new Set(['open'])]
]); ]);
export default class Selector { export default class Selector {
@ -584,7 +585,7 @@ class Block {
host: boolean; host: boolean;
root: boolean; root: boolean;
combinator: CssNode; combinator: CssNode;
selectors: CssNode[] selectors: CssNode[];
start: number; start: number;
end: number; end: number;
should_encapsulate: boolean; should_encapsulate: boolean;

@ -145,6 +145,10 @@ class Declaration {
? this.node.value.children[0] ? this.node.value.children[0]
: this.node.value; : this.node.value;
// Don't minify whitespace in custom properties, since some browsers (Chromium < 99)
// treat --foo: ; and --foo:; differently
if (first.type === 'Raw' && /^\s+$/.test(first.value)) return;
let start = first.start; let start = first.start;
while (/\s/.test(code.original[start])) start += 1; while (/\s/.test(code.original[start])) start += 1;

@ -26,12 +26,17 @@ export default class Animation extends Node {
} }
const block = parent.parent; const block = parent.parent;
if (!block || block.type !== 'EachBlock' || !block.key) { if (!block || block.type !== 'EachBlock') {
// TODO can we relax the 'immediate child' rule? // TODO can we relax the 'immediate child' rule?
component.error(this, compiler_errors.invalid_animation_immediate); component.error(this, compiler_errors.invalid_animation_immediate);
return; return;
} }
if (!block.key) {
component.error(this, compiler_errors.invalid_animation_key);
return;
}
(block as EachBlock).has_animation = true; (block as EachBlock).has_animation = true;
this.expression = info.expression this.expression = info.expression

@ -11,7 +11,7 @@ import is_reference, { NodeWithPropertyDefinition } from 'is-reference';
import get_object from '../utils/get_object'; import get_object from '../utils/get_object';
import compiler_errors from '../compiler_errors'; import compiler_errors from '../compiler_errors';
const allowed_parents = new Set(['EachBlock', 'CatchBlock', 'ThenBlock', 'InlineComponent', 'SlotTemplate']); const allowed_parents = new Set(['EachBlock', 'CatchBlock', 'ThenBlock', 'InlineComponent', 'SlotTemplate', 'IfBlock', 'ElseBlock']);
export default class ConstTag extends Node { export default class ConstTag extends Node {
type: 'ConstTag'; type: 'ConstTag';

@ -63,7 +63,7 @@ export default class EachBlock extends AbstractBlock {
([this.const_tags, this.children] = get_const_tags(info.children, component, this, this)); ([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
if (this.has_animation) { if (this.has_animation) {
this.children = this.children.filter(child => !isEmptyNode(child)); this.children = this.children.filter(child => !isEmptyNode(child) && !isCommentNode(child));
if (this.children.length !== 1) { if (this.children.length !== 1) {
const child = this.children.find(child => !!(child as Element).animation); const child = this.children.find(child => !!(child as Element).animation);
@ -83,3 +83,6 @@ export default class EachBlock extends AbstractBlock {
function isEmptyNode(node: INode) { function isEmptyNode(node: INode) {
return node.type === 'Text' && node.data.trim() === ''; return node.type === 'Text' && node.data.trim() === '';
} }
function isCommentNode(node: INode) {
return node.type === 'Comment';
}

@ -1,4 +1,4 @@
import { is_void } from '../../utils/names'; import { is_void } from '../../../shared/utils/names';
import Node from './shared/Node'; import Node from './shared/Node';
import Attribute from './Attribute'; import Attribute from './Attribute';
import Binding from './Binding'; import Binding from './Binding';
@ -11,13 +11,16 @@ import StyleDirective from './StyleDirective';
import Text from './Text'; import Text from './Text';
import { namespaces } from '../../utils/namespaces'; import { namespaces } from '../../utils/namespaces';
import map_children from './shared/map_children'; import map_children from './shared/map_children';
import { dimensions } from '../../utils/patterns'; import { dimensions, start_newline } from '../../utils/patterns';
import fuzzymatch from '../../utils/fuzzymatch'; import fuzzymatch from '../../utils/fuzzymatch';
import list from '../../utils/list'; import list from '../../utils/list';
import Let from './Let'; import Let from './Let';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces'; import { INode } from './interfaces';
import Component from '../Component'; import Component from '../Component';
import Expression from './shared/Expression';
import { string_literal } from '../utils/stringify';
import { Literal } from 'estree';
import compiler_warnings from '../compiler_warnings'; import compiler_warnings from '../compiler_warnings';
import compiler_errors from '../compiler_errors'; import compiler_errors from '../compiler_errors';
@ -190,14 +193,43 @@ export default class Element extends Node {
children: INode[]; children: INode[];
namespace: string; namespace: string;
needs_manual_style_scoping: boolean; needs_manual_style_scoping: boolean;
tag_expr: Expression;
get is_dynamic_element() {
return this.name === 'svelte:element';
}
constructor(component: Component, parent: Node, scope: TemplateScope, info: any) { constructor(component: Component, parent: Node, scope: TemplateScope, info: any) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.name = info.name; this.name = info.name;
if (info.name === 'svelte:element') {
if (typeof info.tag !== 'string') {
this.tag_expr = new Expression(component, this, scope, info.tag);
} else {
this.tag_expr = new Expression(component, this, scope, string_literal(info.tag) as Literal);
}
} else {
this.tag_expr = new Expression(component, this, scope, string_literal(this.name) as Literal);
}
this.namespace = get_namespace(parent as Element, this, component.namespace); this.namespace = get_namespace(parent as Element, this, component.namespace);
if (this.namespace !== namespaces.foreign) { if (this.namespace !== namespaces.foreign) {
if (this.name === 'pre' || this.name === 'textarea') {
const first = info.children[0];
if (first && first.type === 'Text') {
// The leading newline character needs to be stripped because of a qirk,
// it is ignored by browsers if the tag and its contents are set through
// innerHTML (NOT if set through the innerHTML of the tag or dynamically).
// Therefore strip it here but add it back in the appropriate
// places if there's another newline afterwards.
// see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions
// see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
first.data = first.data.replace(start_newline, '');
}
}
if (this.name === 'textarea') { if (this.name === 'textarea') {
if (info.children.length > 0) { if (info.children.length > 0) {
const value_attribute = info.attributes.find(node => node.name === 'value'); const value_attribute = info.attributes.find(node => node.name === 'value');

@ -1,16 +1,21 @@
import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock'; import AbstractBlock from './shared/AbstractBlock';
import Component from '../Component'; import Component from '../Component';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces'; import { TemplateNode } from '../../interfaces';
import Node from './shared/Node'; import Node from './shared/Node';
import ConstTag from './ConstTag';
import get_const_tags from './shared/get_const_tags';
export default class ElseBlock extends AbstractBlock { export default class ElseBlock extends AbstractBlock {
type: 'ElseBlock'; type: 'ElseBlock';
scope: TemplateScope;
const_tags: ConstTag[];
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.children = map_children(component, this, scope, info.children);
this.scope = scope.child();
([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
this.warn_if_empty_block(); this.warn_if_empty_block();
} }

@ -1,22 +1,26 @@
import ElseBlock from './ElseBlock'; import ElseBlock from './ElseBlock';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import map_children from './shared/map_children';
import AbstractBlock from './shared/AbstractBlock'; import AbstractBlock from './shared/AbstractBlock';
import Component from '../Component'; import Component from '../Component';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces'; import { TemplateNode } from '../../interfaces';
import Node from './shared/Node'; import Node from './shared/Node';
import ConstTag from './ConstTag';
import get_const_tags from './shared/get_const_tags';
export default class IfBlock extends AbstractBlock { export default class IfBlock extends AbstractBlock {
type: 'IfBlock'; type: 'IfBlock';
expression: Expression; expression: Expression;
else: ElseBlock; else: ElseBlock;
scope: TemplateScope;
const_tags: ConstTag[];
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info); super(component, parent, scope, info);
this.scope = scope.child();
this.expression = new Expression(component, this, scope, info.expression); this.expression = new Expression(component, this, this.scope, info.expression);
this.children = map_children(component, this, scope, info.children); ([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
this.else = info.else this.else = info.else
? new ElseBlock(component, this, scope, info.else) ? new ElseBlock(component, this, scope, info.else)

@ -1,5 +1,5 @@
import Tag from './shared/Tag'; import Tag from './shared/Tag';
export default class RawMustacheTag extends Tag { export default class RawMustacheTag extends Tag {
type: 'RawMustacheTag' type: 'RawMustacheTag';
} }

@ -27,10 +27,7 @@ export default class SlotTemplate extends Node {
this.validate_slot_template_placement(); this.validate_slot_template_placement();
const has_let = info.attributes.some((node) => node.type === 'Let'); scope = scope.child();
if (has_let) {
scope = scope.child();
}
info.attributes.forEach((node) => { info.attributes.forEach((node) => {
switch (node.type) { switch (node.type) {

@ -72,6 +72,8 @@ export type INode = Action
| Window; | Window;
export type INodeAllowConstTag = export type INodeAllowConstTag =
| IfBlock
| ElseBlock
| EachBlock | EachBlock
| CatchBlock | CatchBlock
| ThenBlock | ThenBlock

@ -90,7 +90,7 @@ export default class Expression {
} }
if (template_scope.is_let(name)) { if (template_scope.is_let(name)) {
if (!function_expression) { // TODO should this be `!lazy` ? if (!lazy) {
contextual_dependencies.add(name); contextual_dependencies.add(name);
dependencies.add(name); dependencies.add(name);
} }
@ -254,11 +254,17 @@ export default class Expression {
const declaration = b`const ${id} = ${node}`; const declaration = b`const ${id} = ${node}`;
if (owner.type === 'ConstTag') { if (owner.type === 'ConstTag') {
let child_scope = scope;
walk(node, { walk(node, {
enter(node: Node) { enter(node: Node, parent: any) {
if (node.type === 'Identifier') { if (map.has(node)) child_scope = map.get(node);
if (node.type === 'Identifier' && is_reference(node, parent)) {
if (child_scope.has(node.name)) return;
this.replace(block.renderer.reference(node, ctx)); this.replace(block.renderer.reference(node, ctx));
} }
},
leave(node: Node) {
if (map.has(node)) child_scope = child_scope.parent;
} }
}); });
} else if (dependencies.size === 0 && contextual_dependencies.size === 0) { } else if (dependencies.size === 0 && contextual_dependencies.size === 0) {

@ -48,6 +48,7 @@ export default class Block {
hydrate: Array<Node | Node[]>; hydrate: Array<Node | Node[]>;
mount: Array<Node | Node[]>; mount: Array<Node | Node[]>;
measure: Array<Node | Node[]>; measure: Array<Node | Node[]>;
restore_measurements: Array<Node | Node[]>;
fix: Array<Node | Node[]>; fix: Array<Node | Node[]>;
animate: Array<Node | Node[]>; animate: Array<Node | Node[]>;
intro: Array<Node | Node[]>; intro: Array<Node | Node[]>;
@ -96,6 +97,7 @@ export default class Block {
hydrate: [], hydrate: [],
mount: [], mount: [],
measure: [], measure: [],
restore_measurements: [],
fix: [], fix: [],
animate: [], animate: [],
intro: [], intro: [],
@ -326,6 +328,12 @@ export default class Block {
${this.chunks.measure} ${this.chunks.measure}
}`; }`;
if (this.chunks.restore_measurements.length) {
properties.restore_measurements = x`function #restore_measurements(#measurement) {
${this.chunks.restore_measurements}
}`;
}
properties.fix = x`function #fix() { properties.fix = x`function #fix() {
${this.chunks.fix} ${this.chunks.fix}
}`; }`;
@ -379,6 +387,7 @@ export default class Block {
m: ${properties.mount}, m: ${properties.mount},
p: ${properties.update}, p: ${properties.update},
r: ${properties.measure}, r: ${properties.measure},
s: ${properties.restore_measurements},
f: ${properties.fix}, f: ${properties.fix},
a: ${properties.animate}, a: ${properties.animate},
i: ${properties.intro}, i: ${properties.intro},

@ -27,6 +27,7 @@ export class ElseBlockWrapper extends Wrapper {
next_sibling: Wrapper next_sibling: Wrapper
) { ) {
super(renderer, block, parent, node); super(renderer, block, parent, node);
add_const_tags_context(renderer, this.node.const_tags);
this.block = block.child({ this.block = block.child({
comment: create_debugging_comment(node, this.renderer.component), comment: create_debugging_comment(node, this.renderer.component),
@ -60,7 +61,7 @@ export default class EachBlockWrapper extends Wrapper {
fixed_length: number; fixed_length: number;
data_length: Node | number; data_length: Node | number;
view_length: Node | number; view_length: Node | number;
} };
context_props: Array<Node | Node[]>; context_props: Array<Node | Node[]>;
index_name: Identifier; index_name: Identifier;
@ -257,6 +258,18 @@ export default class EachBlockWrapper extends Wrapper {
} }
if (this.else) { if (this.else) {
let else_ctx = x`#ctx`;
if (this.else.node.const_tags.length > 0) {
const get_ctx_name = this.renderer.component.get_unique_name('get_else_ctx');
this.renderer.blocks.push(b`
function ${get_ctx_name}(#ctx) {
const child_ctx = #ctx.slice();
${add_const_tags(block, this.else.node.const_tags, 'child_ctx')}
return child_ctx;
}
`);
else_ctx = x`${get_ctx_name}(#ctx)`;
}
const each_block_else = component.get_unique_name(`${this.var.name}_else`); const each_block_else = component.get_unique_name(`${this.var.name}_else`);
block.chunks.init.push(b`let ${each_block_else} = null;`); block.chunks.init.push(b`let ${each_block_else} = null;`);
@ -264,7 +277,7 @@ export default class EachBlockWrapper extends Wrapper {
// TODO neaten this up... will end up with an empty line in the block // TODO neaten this up... will end up with an empty line in the block
block.chunks.init.push(b` block.chunks.init.push(b`
if (!${this.vars.data_length}) { if (!${this.vars.data_length}) {
${each_block_else} = ${this.else.block.name}(#ctx); ${each_block_else} = ${this.else.block.name}(${else_ctx});
} }
`); `);
@ -304,9 +317,9 @@ export default class EachBlockWrapper extends Wrapper {
if (this.else.block.has_update_method) { if (this.else.block.has_update_method) {
this.updates.push(b` this.updates.push(b`
if (!${this.vars.data_length} && ${each_block_else}) { if (!${this.vars.data_length} && ${each_block_else}) {
${each_block_else}.p(#ctx, #dirty); ${each_block_else}.p(${else_ctx}, #dirty);
} else if (!${this.vars.data_length}) { } else if (!${this.vars.data_length}) {
${each_block_else} = ${this.else.block.name}(#ctx); ${each_block_else} = ${this.else.block.name}(${else_ctx});
${each_block_else}.c(); ${each_block_else}.c();
${has_transitions && b`@transition_in(${each_block_else}, 1);`} ${has_transitions && b`@transition_in(${each_block_else}, 1);`}
${each_block_else}.m(${update_mount_node}, ${update_anchor_node}); ${each_block_else}.m(${update_mount_node}, ${update_anchor_node});
@ -321,7 +334,7 @@ export default class EachBlockWrapper extends Wrapper {
${destroy_block_else}; ${destroy_block_else};
} }
} else if (!${each_block_else}) { } else if (!${each_block_else}) {
${each_block_else} = ${this.else.block.name}(#ctx); ${each_block_else} = ${this.else.block.name}(${else_ctx});
${each_block_else}.c(); ${each_block_else}.c();
${has_transitions && b`@transition_in(${each_block_else}, 1);`} ${has_transitions && b`@transition_in(${each_block_else}, 1);`}
${each_block_else}.m(${update_mount_node}, ${update_anchor_node}); ${each_block_else}.m(${update_mount_node}, ${update_anchor_node});

@ -2,7 +2,7 @@ import Renderer from '../../Renderer';
import Element from '../../../nodes/Element'; import Element from '../../../nodes/Element';
import Wrapper from '../shared/Wrapper'; import Wrapper from '../shared/Wrapper';
import Block from '../../Block'; import Block from '../../Block';
import { is_void } from '../../../../utils/names'; import { is_void } from '../../../../../shared/utils/names';
import FragmentWrapper from '../Fragment'; import FragmentWrapper from '../Fragment';
import { escape_html, string_literal } from '../../../utils/stringify'; import { escape_html, string_literal } from '../../../utils/stringify';
import TextWrapper from '../Text'; import TextWrapper from '../Text';
@ -12,7 +12,7 @@ import { namespaces } from '../../../../utils/namespaces';
import AttributeWrapper from './Attribute'; import AttributeWrapper from './Attribute';
import StyleAttributeWrapper from './StyleAttribute'; import StyleAttributeWrapper from './StyleAttribute';
import SpreadAttributeWrapper from './SpreadAttribute'; import SpreadAttributeWrapper from './SpreadAttribute';
import { dimensions } from '../../../../utils/patterns'; import { dimensions, start_newline } from '../../../../utils/patterns';
import Binding from './Binding'; import Binding from './Binding';
import add_to_set from '../../../utils/add_to_set'; import add_to_set from '../../../utils/add_to_set';
import { add_event_handler } from '../shared/add_event_handlers'; import { add_event_handler } from '../shared/add_event_handlers';
@ -26,6 +26,7 @@ import Action from '../../../nodes/Action';
import MustacheTagWrapper from '../MustacheTag'; import MustacheTagWrapper from '../MustacheTag';
import RawMustacheTagWrapper from '../RawMustacheTag'; import RawMustacheTagWrapper from '../RawMustacheTag';
import is_dynamic from '../shared/is_dynamic'; import is_dynamic from '../shared/is_dynamic';
import create_debugging_comment from '../shared/create_debugging_comment';
import { push_array } from '../../../../utils/push_array'; import { push_array } from '../../../../utils/push_array';
interface BindingGroup { interface BindingGroup {
@ -134,6 +135,8 @@ const events = [
} }
]; ];
const CHILD_DYNAMIC_ELEMENT_BLOCK = 'child_dynamic_element';
export default class ElementWrapper extends Wrapper { export default class ElementWrapper extends Wrapper {
node: Element; node: Element;
fragment: FragmentWrapper; fragment: FragmentWrapper;
@ -147,6 +150,9 @@ export default class ElementWrapper extends Wrapper {
var: any; var: any;
void: boolean; void: boolean;
child_dynamic_element_block?: Block = null;
child_dynamic_element?: ElementWrapper = null;
constructor( constructor(
renderer: Renderer, renderer: Renderer,
block: Block, block: Block,
@ -156,6 +162,24 @@ export default class ElementWrapper extends Wrapper {
next_sibling: Wrapper next_sibling: Wrapper
) { ) {
super(renderer, block, parent, node); super(renderer, block, parent, node);
if (node.is_dynamic_element && block.type !== CHILD_DYNAMIC_ELEMENT_BLOCK) {
this.child_dynamic_element_block = block.child({
comment: create_debugging_comment(node, renderer.component),
name: renderer.component.get_unique_name('create_dynamic_element'),
type: CHILD_DYNAMIC_ELEMENT_BLOCK
});
renderer.blocks.push(this.child_dynamic_element_block);
this.child_dynamic_element = new ElementWrapper(
renderer,
this.child_dynamic_element_block,
parent,
node,
strip_whitespace,
next_sibling
);
}
this.var = { this.var = {
type: 'Identifier', type: 'Identifier',
name: node.name.replace(/[^a-zA-Z0-9_$]/g, '_') name: node.name.replace(/[^a-zA-Z0-9_$]/g, '_')
@ -199,6 +223,8 @@ export default class ElementWrapper extends Wrapper {
block.add_animation(); block.add_animation();
} }
block.add_dependencies(node.tag_expr.dependencies);
// add directive and handler dependencies // add directive and handler dependencies
[node.animation, node.outro, ...node.actions, ...node.classes, ...node.styles].forEach(directive => { [node.animation, node.outro, ...node.actions, ...node.classes, ...node.styles].forEach(directive => {
if (directive && directive.expression) { if (directive && directive.expression) {
@ -219,7 +245,9 @@ export default class ElementWrapper extends Wrapper {
node.classes.length > 0 || node.classes.length > 0 ||
node.intro || node.outro || node.intro || node.outro ||
node.handlers.length > 0 || node.handlers.length > 0 ||
node.styles.length > 0 ||
this.node.name === 'option' || this.node.name === 'option' ||
node.tag_expr.dynamic_dependencies().length ||
renderer.options.dev renderer.options.dev
) { ) {
this.parent.cannot_use_innerhtml(); // need to use add_location this.parent.cannot_use_innerhtml(); // need to use add_location
@ -231,6 +259,112 @@ export default class ElementWrapper extends Wrapper {
} }
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
if (this.child_dynamic_element) {
this.render_dynamic_element(block, parent_node, parent_nodes);
} else {
this.render_element(block, parent_node, parent_nodes);
}
}
render_dynamic_element(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
this.child_dynamic_element.render(
this.child_dynamic_element_block,
null,
(x`#nodes` as unknown) as Identifier
);
const previous_tag = block.get_unique_name('previous_tag');
const tag = this.node.tag_expr.manipulate(block);
block.add_variable(previous_tag, tag);
block.chunks.init.push(b`
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
${this.renderer.options.dev && this.node.children.length > 0 && b`@validate_void_dynamic_element(${tag});`}
let ${this.var} = ${tag} && ${this.child_dynamic_element_block.name}(#ctx);
`);
block.chunks.create.push(b`
if (${this.var}) ${this.var}.c();
`);
if (this.renderer.options.hydratable) {
block.chunks.claim.push(b`
if (${this.var}) ${this.var}.l(${parent_nodes});
`);
}
block.chunks.mount.push(b`
if (${this.var}) ${this.var}.m(${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});
`);
const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const has_transitions = !!(this.node.intro || this.node.outro);
const not_equal = this.renderer.component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;
block.chunks.update.push(b`
if (${tag}) {
if (!${previous_tag}) {
${this.var} = ${this.child_dynamic_element_block.name}(#ctx);
${this.var}.c();
${has_transitions && b`@transition_in(${this.var})`}
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
} else if (${not_equal}(${previous_tag}, ${tag})) {
${this.var}.d(1);
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
${this.renderer.options.dev && this.node.children.length > 0 && b`@validate_void_dynamic_element(${tag});`}
${this.var} = ${this.child_dynamic_element_block.name}(#ctx);
${this.var}.c();
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
} else {
${this.var}.p(#ctx, #dirty);
}
} else if (${previous_tag}) {
${
has_transitions
? b`
@group_outros();
@transition_out(${this.var}, 1, 1, () => {
${this.var} = null;
});
@check_outros();
`
: b`
${this.var}.d(1);
${this.var} = null;
`
}
}
${previous_tag} = ${tag};
`);
if (this.child_dynamic_element_block.has_intros) {
block.chunks.intro.push(b`@transition_in(${this.var});`);
}
if (this.child_dynamic_element_block.has_outros) {
block.chunks.outro.push(b`@transition_out(${this.var});`);
}
block.chunks.destroy.push(b`if (${this.var}) ${this.var}.d(detaching)`);
if (this.node.animation) {
const measurements = block.get_unique_name('measurements');
block.add_variable(measurements);
block.chunks.measure.push(b`${measurements} = ${this.var}.r()`);
block.chunks.fix.push(b`${this.var}.f();`);
block.chunks.animate.push(b`
${this.var}.s(${measurements});
${this.var}.a()
`);
}
}
is_dom_node() {
return super.is_dom_node() && !this.child_dynamic_element;
}
render_element(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
const { renderer } = this; const { renderer } = this;
if (this.node.name === 'noscript') return; if (this.node.name === 'noscript') return;
@ -248,7 +382,7 @@ export default class ElementWrapper extends Wrapper {
if (renderer.options.hydratable) { if (renderer.options.hydratable) {
if (parent_nodes) { if (parent_nodes) {
block.chunks.claim.push(b` block.chunks.claim.push(b`
${node} = ${this.get_claim_statement(parent_nodes)}; ${node} = ${this.get_claim_statement(block, parent_nodes)};
`); `);
if (!this.void && this.node.children.length > 0) { if (!this.void && this.node.children.length > 0) {
@ -288,8 +422,12 @@ export default class ElementWrapper extends Wrapper {
} }
// insert static children with textContent or innerHTML // insert static children with textContent or innerHTML
// skip textcontent for <template>. append nodes to TemplateElement.content instead
const can_use_textcontent = this.can_use_textcontent(); const can_use_textcontent = this.can_use_textcontent();
if (!this.node.namespace && (this.can_use_innerhtml || can_use_textcontent) && this.fragment.nodes.length > 0) { const is_template = this.node.name === 'template';
const is_template_with_text_content = is_template && can_use_textcontent;
if (!is_template_with_text_content && !this.node.namespace && (this.can_use_innerhtml || can_use_textcontent) && this.fragment.nodes.length > 0) {
if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') { if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
block.chunks.create.push( block.chunks.create.push(
b`${node}.textContent = ${string_literal((this.fragment.nodes[0] as TextWrapper).data)};` b`${node}.textContent = ${string_literal((this.fragment.nodes[0] as TextWrapper).data)};`
@ -320,7 +458,7 @@ export default class ElementWrapper extends Wrapper {
this.fragment.nodes.forEach((child: Wrapper) => { this.fragment.nodes.forEach((child: Wrapper) => {
child.render( child.render(
block, block,
this.node.name === 'template' ? x`${node}.content` : node, is_template ? x`${node}.content` : node,
nodes nodes
); );
}); });
@ -356,6 +494,8 @@ export default class ElementWrapper extends Wrapper {
b`@add_location(${this.var}, ${renderer.file_var}, ${loc.line - 1}, ${loc.column}, ${this.node.start});` b`@add_location(${this.var}, ${renderer.file_var}, ${loc.line - 1}, ${loc.column}, ${this.node.start});`
); );
} }
block.renderer.dirty(this.node.tag_expr.dynamic_dependencies());
} }
can_use_textcontent() { can_use_textcontent() {
@ -363,7 +503,7 @@ export default class ElementWrapper extends Wrapper {
} }
get_render_statement(block: Block) { get_render_statement(block: Block) {
const { name, namespace } = this.node; const { name, namespace, tag_expr } = this.node;
if (namespace === namespaces.svg) { if (namespace === namespaces.svg) {
return x`@svg_element("${name}")`; return x`@svg_element("${name}")`;
@ -378,22 +518,32 @@ export default class ElementWrapper extends Wrapper {
return x`@element_is("${name}", ${is.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`)})`; return x`@element_is("${name}", ${is.render_chunks(block).reduce((lhs, rhs) => x`${lhs} + ${rhs}`)})`;
} }
return x`@element("${name}")`; const reference = tag_expr.manipulate(block);
return x`@element(${reference})`;
} }
get_claim_statement(nodes: Identifier) { get_claim_statement(block: Block, nodes: Identifier) {
const attributes = this.attributes const attributes = this.attributes
.filter((attr) => !(attr instanceof SpreadAttributeWrapper) && !attr.property_name) .filter((attr) => !(attr instanceof SpreadAttributeWrapper) && !attr.property_name)
.map((attr) => p`${(attr as StyleAttributeWrapper | AttributeWrapper).name}: true`); .map((attr) => p`${(attr as StyleAttributeWrapper | AttributeWrapper).name}: true`);
const name = this.node.namespace let reference;
? this.node.name if (this.node.tag_expr.node.type === 'Literal') {
: this.node.name.toUpperCase(); if (this.node.namespace) {
reference = `"${this.node.tag_expr.node.value}"`;
} else {
reference = `"${(this.node.tag_expr.node.value as String || '').toUpperCase()}"`;
}
} else if (this.node.namespace) {
reference = x`${this.node.tag_expr.manipulate(block)}`;
} else {
reference = x`(${this.node.tag_expr.manipulate(block)} || 'null').toUpperCase()`;
}
if (this.node.namespace === namespaces.svg) { if (this.node.namespace === namespaces.svg) {
return x`@claim_svg_element(${nodes}, "${name}", { ${attributes} })`; return x`@claim_svg_element(${nodes}, ${reference}, { ${attributes} })`;
} else { } else {
return x`@claim_element(${nodes}, "${name}", { ${attributes} })`; return x`@claim_element(${nodes}, ${reference}, { ${attributes} })`;
} }
} }
@ -602,7 +752,7 @@ export default class ElementWrapper extends Wrapper {
} }
}); });
if (this.node.attributes.some(attr => attr.is_spread)) { if (this.node.attributes.some(attr => attr.is_spread) || this.node.is_dynamic_element) {
this.add_spread_attributes(block); this.add_spread_attributes(block);
return; return;
} }
@ -676,7 +826,7 @@ export default class ElementWrapper extends Wrapper {
(${data}.multiple ? @select_options : @select_option)(${this.var}, ${data}.value); (${data}.multiple ? @select_options : @select_option)(${this.var}, ${data}.value);
`); `);
block.chunks.update.push(b` block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(dependencies))} && 'value' in ${data}) (${data}.multiple ? @select_options : @select_option)(${this.var}, ${data}.value);; if (${block.renderer.dirty(Array.from(dependencies))} && 'value' in ${data}) (${data}.multiple ? @select_options : @select_option)(${this.var}, ${data}.value);
`); `);
} else if (this.node.name === 'input' && this.attributes.find(attr => attr.node.name === 'value')) { } else if (this.node.name === 'input' && this.attributes.find(attr => attr.node.name === 'value')) {
const type = this.node.get_static_attribute_value('type'); const type = this.node.get_static_attribute_value('type');
@ -846,6 +996,11 @@ export default class ElementWrapper extends Wrapper {
${rect} = ${this.var}.getBoundingClientRect(); ${rect} = ${this.var}.getBoundingClientRect();
`); `);
if (block.type === CHILD_DYNAMIC_ELEMENT_BLOCK) {
block.chunks.measure.push(b`return ${rect}`);
block.chunks.restore_measurements.push(b`${rect} = #measurement;`);
}
block.chunks.fix.push(b` block.chunks.fix.push(b`
@fix_position(${this.var}); @fix_position(${this.var});
${stop_animation}(); ${stop_animation}();
@ -893,7 +1048,7 @@ export default class ElementWrapper extends Wrapper {
block.chunks.hydrate.push(updater); block.chunks.hydrate.push(updater);
if (has_spread) { if (has_spread || this.node.is_dynamic_element) {
block.chunks.update.push(updater); block.chunks.update.push(updater);
} else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) { } else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
const all_dependencies = this.class_dependencies.concat(...dependencies); const all_dependencies = this.class_dependencies.concat(...dependencies);
@ -965,6 +1120,9 @@ export default class ElementWrapper extends Wrapper {
function to_html(wrappers: Array<ElementWrapper | TextWrapper | MustacheTagWrapper | RawMustacheTagWrapper>, block: Block, literal: any, state: any, can_use_raw_text?: boolean) { function to_html(wrappers: Array<ElementWrapper | TextWrapper | MustacheTagWrapper | RawMustacheTagWrapper>, block: Block, literal: any, state: any, can_use_raw_text?: boolean) {
wrappers.forEach(wrapper => { wrappers.forEach(wrapper => {
if (wrapper instanceof TextWrapper) { if (wrapper instanceof TextWrapper) {
// Don't add the <pre>/<textare> newline logic here because pre/textarea.innerHTML
// would keep the leading newline, too, only someParent.innerHTML = '..<pre/textarea>..' won't
if ((wrapper as TextWrapper).use_space()) state.quasi.value.raw += ' '; if ((wrapper as TextWrapper).use_space()) state.quasi.value.raw += ' ';
const parent = wrapper.node.parent as Element; const parent = wrapper.node.parent as Element;
@ -992,22 +1150,16 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | MustacheTagWrapp
// element // element
state.quasi.value.raw += `<${wrapper.node.name}`; state.quasi.value.raw += `<${wrapper.node.name}`;
const is_empty_textarea = wrapper.node.name === 'textarea' && wrapper.fragment.nodes.length === 0;
(wrapper as ElementWrapper).attributes.forEach((attr: AttributeWrapper) => { (wrapper as ElementWrapper).attributes.forEach((attr: AttributeWrapper) => {
if (is_empty_textarea && attr.node.name === 'value') {
// The value attribute of <textarea> renders as content.
return;
}
state.quasi.value.raw += ` ${fix_attribute_casing(attr.node.name)}="`; state.quasi.value.raw += ` ${fix_attribute_casing(attr.node.name)}="`;
attr.node.chunks.forEach(chunk => { to_html_for_attr_value(attr, block, literal, state);
if (chunk.type === 'Text') {
state.quasi.value.raw += escape_html(chunk.data);
} else {
literal.quasis.push(state.quasi);
literal.expressions.push(chunk.manipulate(block));
state.quasi = {
type: 'TemplateElement',
value: { raw: '' }
};
}
});
state.quasi.value.raw += '"'; state.quasi.value.raw += '"';
}); });
@ -1015,6 +1167,29 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | MustacheTagWrapp
if (!wrapper.void) { if (!wrapper.void) {
state.quasi.value.raw += '>'; state.quasi.value.raw += '>';
if (wrapper.node.name === 'pre') {
// Two or more leading newlines are required to restore the leading newline immediately after `<pre>`.
// see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
const first = wrapper.fragment.nodes[0];
if (first && first.node.type === 'Text' && start_newline.test(first.node.data)) {
state.quasi.value.raw += '\n';
}
}
if (is_empty_textarea) {
// The <textarea> renders the value attribute as content because the content is stored in the value attribute.
const value_attribute = wrapper.attributes.find(attr => attr.node.name === 'value');
if (value_attribute) {
// Two or more leading newlines are required to restore the leading newline immediately after `<textarea>`.
// see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions
const first = value_attribute.node.chunks[0];
if (first && first.type === 'Text' && start_newline.test(first.data)) {
state.quasi.value.raw += '\n';
}
to_html_for_attr_value(value_attribute, block, literal, state);
}
}
to_html(wrapper.fragment.nodes as Array<ElementWrapper | TextWrapper>, block, literal, state); to_html(wrapper.fragment.nodes as Array<ElementWrapper | TextWrapper>, block, literal, state);
state.quasi.value.raw += `</${wrapper.node.name}>`; state.quasi.value.raw += `</${wrapper.node.name}>`;
@ -1024,3 +1199,19 @@ function to_html(wrappers: Array<ElementWrapper | TextWrapper | MustacheTagWrapp
} }
}); });
} }
function to_html_for_attr_value(attr: AttributeWrapper | StyleAttributeWrapper | SpreadAttributeWrapper, block: Block, literal: any, state: any) {
attr.node.chunks.forEach(chunk => {
if (chunk.type === 'Text') {
state.quasi.value.raw += escape_html(chunk.data);
} else {
literal.quasis.push(state.quasi);
literal.expressions.push(chunk.manipulate(block));
state.quasi = {
type: 'TemplateElement',
value: { raw: '' }
};
}
});
}

@ -11,6 +11,7 @@ import { walk } from 'estree-walker';
import { is_head } from './shared/is_head'; import { is_head } from './shared/is_head';
import { Identifier, Node } from 'estree'; import { Identifier, Node } from 'estree';
import { push_array } from '../../../utils/push_array'; import { push_array } from '../../../utils/push_array';
import { add_const_tags, add_const_tags_context } from './shared/add_const_tags';
function is_else_if(node: ElseBlock) { function is_else_if(node: ElseBlock) {
return ( return (
@ -25,8 +26,10 @@ class IfBlockBranch extends Wrapper {
condition?: any; condition?: any;
snippet?: Node; snippet?: Node;
is_dynamic: boolean; is_dynamic: boolean;
node: IfBlock | ElseBlock;
var = null; var = null;
get_ctx_name: Node | undefined;
constructor( constructor(
renderer: Renderer, renderer: Renderer,
@ -63,6 +66,8 @@ class IfBlockBranch extends Wrapper {
} }
} }
add_const_tags_context(renderer, this.node.const_tags);
this.block = block.child({ this.block = block.child({
comment: create_debugging_comment(node, parent.renderer.component), comment: create_debugging_comment(node, parent.renderer.component),
name: parent.renderer.component.get_unique_name( name: parent.renderer.component.get_unique_name(
@ -74,6 +79,10 @@ class IfBlockBranch extends Wrapper {
this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent, strip_whitespace, next_sibling); this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent, strip_whitespace, next_sibling);
this.is_dynamic = this.block.dependencies.size > 0; this.is_dynamic = this.block.dependencies.size > 0;
if (node.const_tags.length > 0) {
this.get_ctx_name = parent.renderer.component.get_unique_name(is_else ? 'get_else_ctx' : 'get_if_ctx');
}
} }
} }
@ -190,6 +199,18 @@ export default class IfBlockWrapper extends Wrapper {
const has_outros = this.branches[0].block.has_outro_method; const has_outros = this.branches[0].block.has_outro_method;
const has_transitions = has_intros || has_outros; const has_transitions = has_intros || has_outros;
this.branches.forEach(branch => {
if (branch.get_ctx_name) {
this.renderer.blocks.push(b`
function ${branch.get_ctx_name}(#ctx) {
const child_ctx = #ctx.slice();
${add_const_tags(block, branch.node.const_tags, 'child_ctx')}
return child_ctx;
}
`);
}
});
const vars = { name, anchor, if_exists_condition, has_else, has_transitions }; const vars = { name, anchor, if_exists_condition, has_else, has_transitions };
const detaching = parent_node && !is_head(parent_node) ? null : 'detaching'; const detaching = parent_node && !is_head(parent_node) ? null : 'detaching';
@ -260,9 +281,13 @@ export default class IfBlockWrapper extends Wrapper {
) { ) {
const select_block_type = this.renderer.component.get_unique_name('select_block_type'); const select_block_type = this.renderer.component.get_unique_name('select_block_type');
const current_block_type = block.get_unique_name('current_block_type'); const current_block_type = block.get_unique_name('current_block_type');
const need_select_block_ctx = this.branches.some(branch => branch.get_ctx_name);
const select_block_ctx = need_select_block_ctx ? block.get_unique_name('select_block_ctx') : null;
const if_ctx = select_block_ctx ? x`${select_block_ctx}(#ctx, ${current_block_type})` : x`#ctx`;
const get_block = has_else const get_block = has_else
? x`${current_block_type}(#ctx)` ? x`${current_block_type}(${if_ctx})`
: x`${current_block_type} && ${current_block_type}(#ctx)`; : x`${current_block_type} && ${current_block_type}(${if_ctx})`;
if (this.needs_update) { if (this.needs_update) {
block.chunks.init.push(b` block.chunks.init.push(b`
@ -271,10 +296,10 @@ export default class IfBlockWrapper extends Wrapper {
return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`; return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`;
})} })}
${this.branches.map(({ condition, snippet, block }) => condition ${this.branches.map(({ condition, snippet, block }) => condition
? b` ? b`
${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`} ${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
if (${condition}) return ${block.name};` if (${condition}) return ${block.name};`
: b`return ${block.name};` : b`return ${block.name};`
)} )}
} }
`); `);
@ -282,12 +307,40 @@ export default class IfBlockWrapper extends Wrapper {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${select_block_type}(#ctx, #dirty) { function ${select_block_type}(#ctx, #dirty) {
${this.branches.map(({ condition, snippet, block }) => condition ${this.branches.map(({ condition, snippet, block }) => condition
? b`if (${snippet || condition}) return ${block.name};` ? b`if (${snippet || condition}) return ${block.name};`
: b`return ${block.name};`)} : b`return ${block.name};`)}
} }
`); `);
} }
if (need_select_block_ctx) {
// if all branches needs create a context
if (this.branches.every(branch => branch.get_ctx_name)) {
block.chunks.init.push(b`
function ${select_block_ctx}(#ctx, #type) {
${this.branches.map(({ condition, get_ctx_name, block }) => {
return condition
? b`if (#type === ${block.name}) return ${get_ctx_name}(#ctx);`
: b`return ${get_ctx_name}(#ctx);`;
}).filter(Boolean)}
}
`);
} else {
// when not all branches need to create a new context,
// this code is simpler
block.chunks.init.push(b`
function ${select_block_ctx}(#ctx, #type) {
${this.branches.map(({ get_ctx_name, block }) => {
return get_ctx_name
? b`if (#type === ${block.name}) return ${get_ctx_name}(#ctx);`
: null;
}).filter(Boolean)}
return #ctx;
}
`);
}
}
block.chunks.init.push(b` block.chunks.init.push(b`
let ${current_block_type} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()}); let ${current_block_type} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()});
let ${name} = ${get_block}; let ${name} = ${get_block};
@ -322,7 +375,7 @@ export default class IfBlockWrapper extends Wrapper {
if (dynamic) { if (dynamic) {
block.chunks.update.push(b` block.chunks.update.push(b`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(#ctx, #dirty)) && ${name}) { if (${current_block_type} === (${current_block_type} = ${select_block_type}(#ctx, #dirty)) && ${name}) {
${name}.p(#ctx, #dirty); ${name}.p(${if_ctx}, #dirty);
} else { } else {
${change_block} ${change_block}
} }
@ -336,9 +389,9 @@ export default class IfBlockWrapper extends Wrapper {
} }
} else if (dynamic) { } else if (dynamic) {
if (if_exists_condition) { if (if_exists_condition) {
block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(#ctx, #dirty);`); block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(${if_ctx}, #dirty);`);
} else { } else {
block.chunks.update.push(b`${name}.p(#ctx, #dirty);`); block.chunks.update.push(b`${name}.p(${if_ctx}, #dirty);`);
} }
} }
@ -370,6 +423,9 @@ export default class IfBlockWrapper extends Wrapper {
const previous_block_index = block.get_unique_name('previous_block_index'); const previous_block_index = block.get_unique_name('previous_block_index');
const if_block_creators = block.get_unique_name('if_block_creators'); const if_block_creators = block.get_unique_name('if_block_creators');
const if_blocks = block.get_unique_name('if_blocks'); const if_blocks = block.get_unique_name('if_blocks');
const need_select_block_ctx = this.branches.some(branch => branch.get_ctx_name);
const select_block_ctx = need_select_block_ctx ? block.get_unique_name('select_block_ctx') : null;
const if_ctx = select_block_ctx ? x`${select_block_ctx}(#ctx, ${current_block_type_index})` : x`#ctx`;
const if_current_block_type_index = has_else const if_current_block_type_index = has_else
? nodes => nodes ? nodes => nodes
@ -392,32 +448,60 @@ export default class IfBlockWrapper extends Wrapper {
return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`; return b`${snippet && dependencies.length > 0 ? b`if (${block.renderer.dirty(dependencies)}) ${condition} = null;` : null}`;
})} })}
${this.branches.map(({ condition, snippet }, i) => condition ${this.branches.map(({ condition, snippet }, i) => condition
? b` ? b`
${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`} ${snippet && b`if (${condition} == null) ${condition} = !!${snippet}`}
if (${condition}) return ${i};` if (${condition}) return ${i};`
: b`return ${i};`)} : b`return ${i};`)}
${!has_else && b`return -1;`} ${!has_else && b`return -1;`}
} }
` `
: b` : b`
function ${select_block_type}(#ctx, #dirty) { function ${select_block_type}(#ctx, #dirty) {
${this.branches.map(({ condition, snippet }, i) => condition ${this.branches.map(({ condition, snippet }, i) => condition
? b`if (${snippet || condition}) return ${i};` ? b`if (${snippet || condition}) return ${i};`
: b`return ${i};`)} : b`return ${i};`)}
${!has_else && b`return -1;`} ${!has_else && b`return -1;`}
} }
`} `}
`); `);
if (need_select_block_ctx) {
// if all branches needs create a context
if (this.branches.every(branch => branch.get_ctx_name)) {
block.chunks.init.push(b`
function ${select_block_ctx}(#ctx, #index) {
${this.branches.map(({ condition, get_ctx_name }, i) => {
return condition
? b`if (#index === ${i}) return ${get_ctx_name}(#ctx);`
: b`return ${get_ctx_name}(#ctx);`;
}).filter(Boolean)}
}
`);
} else {
// when not all branches need to create a new context,
// this code is simpler
block.chunks.init.push(b`
function ${select_block_ctx}(#ctx, #index) {
${this.branches.map(({ get_ctx_name }, i) => {
return get_ctx_name
? b`if (#index === ${i}) return ${get_ctx_name}(#ctx);`
: null;
}).filter(Boolean)}
return #ctx;
}
`);
}
}
if (has_else) { if (has_else) {
block.chunks.init.push(b` block.chunks.init.push(b`
${current_block_type_index} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()}); ${current_block_type_index} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()});
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#ctx); ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
`); `);
} else { } else {
block.chunks.init.push(b` block.chunks.init.push(b`
if (~(${current_block_type_index} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()}))) { if (~(${current_block_type_index} = ${select_block_type}(#ctx, ${this.renderer.get_initial_dirty()}))) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#ctx); ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
} }
`); `);
} }
@ -445,10 +529,10 @@ export default class IfBlockWrapper extends Wrapper {
const create_new_block = b` const create_new_block = b`
${name} = ${if_blocks}[${current_block_type_index}]; ${name} = ${if_blocks}[${current_block_type_index}];
if (!${name}) { if (!${name}) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#ctx); ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](${if_ctx});
${name}.c(); ${name}.c();
} else { } else {
${dynamic && b`${name}.p(#ctx, #dirty);`} ${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
} }
${has_transitions && b`@transition_in(${name}, 1);`} ${has_transitions && b`@transition_in(${name}, 1);`}
${name}.m(${update_mount_node}, ${anchor}); ${name}.m(${update_mount_node}, ${anchor});
@ -480,7 +564,7 @@ export default class IfBlockWrapper extends Wrapper {
if (dynamic) { if (dynamic) {
block.chunks.update.push(b` block.chunks.update.push(b`
if (${current_block_type_index} === ${previous_block_index}) { if (${current_block_type_index} === ${previous_block_index}) {
${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(#ctx, #dirty);`)} ${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(${if_ctx}, #dirty);`)}
} else { } else {
${change_block} ${change_block}
} }
@ -494,9 +578,9 @@ export default class IfBlockWrapper extends Wrapper {
} }
} else if (dynamic) { } else if (dynamic) {
if (if_exists_condition) { if (if_exists_condition) {
block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(#ctx, #dirty);`); block.chunks.update.push(b`if (${if_exists_condition}) ${name}.p(${if_ctx}, #dirty);`);
} else { } else {
block.chunks.update.push(b`${name}.p(#ctx, #dirty);`); block.chunks.update.push(b`${name}.p(${if_ctx}, #dirty);`);
} }
} }
@ -514,11 +598,12 @@ export default class IfBlockWrapper extends Wrapper {
detaching detaching
) { ) {
const branch = this.branches[0]; const branch = this.branches[0];
const if_ctx = branch.get_ctx_name ? x`${branch.get_ctx_name}(#ctx)` : x`#ctx`;
if (branch.snippet) block.add_variable(branch.condition, branch.snippet); if (branch.snippet) block.add_variable(branch.condition, branch.snippet);
block.chunks.init.push(b` block.chunks.init.push(b`
let ${name} = ${branch.condition} && ${branch.block.name}(#ctx); let ${name} = ${branch.condition} && ${branch.block.name}(${if_ctx});
`); `);
const initial_mount_node = parent_node || '#target'; const initial_mount_node = parent_node || '#target';
@ -533,15 +618,14 @@ export default class IfBlockWrapper extends Wrapper {
const enter = b` const enter = b`
if (${name}) { if (${name}) {
${dynamic && b`${name}.p(#ctx, #dirty);`} ${dynamic && b`${name}.p(${if_ctx}, #dirty);`}
${ ${has_transitions &&
has_transitions &&
b`if (${block.renderer.dirty(branch.dependencies)}) { b`if (${block.renderer.dirty(branch.dependencies)}) {
@transition_in(${name}, 1); @transition_in(${name}, 1);
}` }`
} }
} else { } else {
${name} = ${branch.block.name}(#ctx); ${name} = ${branch.block.name}(${if_ctx});
${name}.c(); ${name}.c();
${has_transitions && b`@transition_in(${name}, 1);`} ${has_transitions && b`@transition_in(${name}, 1);`}
${name}.m(${update_mount_node}, ${anchor}); ${name}.m(${update_mount_node}, ${anchor});
@ -578,7 +662,7 @@ export default class IfBlockWrapper extends Wrapper {
} }
} else if (dynamic) { } else if (dynamic) {
block.chunks.update.push(b` block.chunks.update.push(b`
if (${branch.condition}) ${name}.p(#ctx, #dirty); if (${branch.condition}) ${name}.p(${if_ctx}, #dirty);
`); `);
} }

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

Loading…
Cancel
Save