From 78131b9f98bf00f078dda3ab2ceb85a0135ed0f3 Mon Sep 17 00:00:00 2001 From: Ignatius Bagus Date: Sun, 27 Feb 2022 01:37:12 +0700 Subject: [PATCH 01/89] [feat] TS interfaces for typing actions (#7121) Fixes #6538 --- .gitignore | 1 + package.json | 3 +++ src/runtime/action/index.ts | 42 +++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/runtime/action/index.ts diff --git a/.gitignore b/.gitignore index ee1daa2c03..22389f683c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ node_modules /compiler.*js /index.*js /ssr.*js +/action /internal /store /easing diff --git a/package.json b/package.json index 2ca79c5fa8..c58f999bd3 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,9 @@ "import": "./compiler.mjs", "require": "./compiler.js" }, + "./action": { + "types": "./types/runtime/action/index.d.ts" + }, "./animate": { "types": "./types/runtime/animate/index.d.ts", "import": "./animate/index.mjs", diff --git a/src/runtime/action/index.ts b/src/runtime/action/index.ts new file mode 100644 index 0000000000..6d1d394139 --- /dev/null +++ b/src/runtime/action/index.ts @@ -0,0 +1,42 @@ +/** + * Actions can return an object containing the two properties defined in this interface. Both are optional. + * - update: An action can have a parameter. This method will be called whenever that parameter changes, + * immediately after Svelte has applied updates to the markup. + * - destroy: Method that is called after the element is unmounted + * + * Example usage: + * ```ts + * export function myAction(node: HTMLElement, paramater: Parameter): ActionReturn { + * // ... + * return { + * update: (updatedParameter) => {...}, + * destroy: () => {...} + * }; + * } + * ``` + * + * Docs: https://svelte.dev/docs#template-syntax-element-directives-use-action + */ +export interface ActionReturn { + update?: (parameter: Parameter) => void; + destroy?: () => void; +} + +/** + * Actions are functions that are called when an element is created. + * You can use this interface to type such actions. + * The following example defines an action that only works on `
` elements + * and optionally accepts a parameter which it has a default value for: + * ```ts + * export const myAction: Action = (node, param = { someProperty: true }) => { + * // ... + * } + * ``` + * You can return an object with methods `update` and `destroy` from the function. + * See interface `ActionReturn` for more details. + * + * Docs: https://svelte.dev/docs#template-syntax-element-directives-use-action + */ +export interface Action { + (node: Node, parameter?: Parameter): void | ActionReturn; +} From 8073768f6505c913c92fd99e1fa4529129a65eb5 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sat, 26 Feb 2022 19:37:53 +0100 Subject: [PATCH 02/89] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c2add8c5a..5df6ab56f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Add `aria-description` to the list of allowed ARIA attributes ([#7301](https://github.com/sveltejs/svelte/issues/7301)) +- Add TypeScript interfaces for typing actions ([#6538](https://github.com/sveltejs/svelte/issues/6538)) ## 3.46.4 From 555cdf3da21fe6dc84c236d8e93b027e44a22808 Mon Sep 17 00:00:00 2001 From: Frozen FIsh <76603360+sudongyuer@users.noreply.github.com> Date: Tue, 1 Mar 2022 00:51:26 +0800 Subject: [PATCH 03/89] [docs] fix typo (#7323) --- CONTRIBUTING.md | 2 +- src/runtime/action/index.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48aa238f89..c7989caeea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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'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 diff --git a/src/runtime/action/index.ts b/src/runtime/action/index.ts index 6d1d394139..d7cbf04b12 100644 --- a/src/runtime/action/index.ts +++ b/src/runtime/action/index.ts @@ -3,10 +3,10 @@ * - update: An action can have a parameter. This method will be called whenever that parameter changes, * immediately after Svelte has applied updates to the markup. * - destroy: Method that is called after the element is unmounted - * + * * Example usage: * ```ts - * export function myAction(node: HTMLElement, paramater: Parameter): ActionReturn { + * export function myAction(node: HTMLElement, parameter: Parameter): ActionReturn { * // ... * return { * update: (updatedParameter) => {...}, @@ -14,7 +14,7 @@ * }; * } * ``` - * + * * Docs: https://svelte.dev/docs#template-syntax-element-directives-use-action */ export interface ActionReturn { @@ -34,7 +34,7 @@ export interface ActionReturn { * ``` * You can return an object with methods `update` and `destroy` from the function. * See interface `ActionReturn` for more details. - * + * * Docs: https://svelte.dev/docs#template-syntax-element-directives-use-action */ export interface Action { From a9e6087113d92207d39f11b13b0d11f335fdcba4 Mon Sep 17 00:00:00 2001 From: Daniel Sandoval Date: Tue, 1 Mar 2022 07:16:20 -0700 Subject: [PATCH 04/89] [docs] "What's new in Svelte" March newsletter (#7293) --- ...22-03-01-whats-new-in-svelte-march-2022.md | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md diff --git a/site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md b/site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md new file mode 100644 index 0000000000..472edc495a --- /dev/null +++ b/site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md @@ -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! From 9b7b8149e4be41816712aff2b9f34bc0dba23a94 Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Wed, 2 Mar 2022 12:21:24 +0900 Subject: [PATCH 05/89] [docs] add `-` to the beginning of the line (#7328) --- site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md b/site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md index 472edc495a..ceaed511bc 100644 --- a/site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md +++ b/site/content/blog/2022-03-01-whats-new-in-svelte-march-2022.md @@ -80,7 +80,7 @@ _To Watch_ - [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 +- [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. From e2adf6a211ed7993a79c4745bc1724a6e57aac51 Mon Sep 17 00:00:00 2001 From: rgossiaux Date: Wed, 2 Mar 2022 16:55:02 -0800 Subject: [PATCH 06/89] fix: do not generate unused-export-let inside + -{$q} +{$q2} diff --git a/test/validator/samples/unreferenced-variables/warnings.json b/test/validator/samples/unreferenced-variables/warnings.json index dfac58ebdb..4097ba023b 100644 --- a/test/validator/samples/unreferenced-variables/warnings.json +++ b/test/validator/samples/unreferenced-variables/warnings.json @@ -2,76 +2,76 @@ { "code": "unused-export-let", "end": { - "character": 103, - "column": 12, - "line": 8 + "character": 519, + "column": 13, + "line": 31 }, - "message": "Component has unused export property 'd'. If it is for external reference only, please consider using `export const d`", - "pos": 102, + "message": "Component has unused export property 'd2'. If it is for external reference only, please consider using `export const d2`", + "pos": 517, "start": { - "character": 102, + "character": 517, "column": 11, - "line": 8 + "line": 31 } }, { "code": "unused-export-let", "end": { - "character": 106, - "column": 15, - "line": 8 + "character": 523, + "column": 17, + "line": 31 }, - "message": "Component has unused export property 'e'. If it is for external reference only, please consider using `export const e`", - "pos": 105, + "message": "Component has unused export property 'e2'. If it is for external reference only, please consider using `export const e2`", + "pos": 521, "start": { - "character": 105, - "column": 14, - "line": 8 + "character": 521, + "column": 15, + "line": 31 } }, { "code": "unused-export-let", "end": { - "character": 130, - "column": 18, - "line": 9 + "character": 549, + "column": 19, + "line": 32 }, - "message": "Component has unused export property 'g'. If it is for external reference only, please consider using `export const g`", - "pos": 125, + "message": "Component has unused export property 'g2'. If it is for external reference only, please consider using `export const g2`", + "pos": 543, "start": { - "character": 125, + "character": 543, "column": 13, - "line": 9 + "line": 32 } }, { "code": "unused-export-let", "end": { - "character": 150, - "column": 18, - "line": 10 + "character": 570, + "column": 19, + "line": 33 }, - "message": "Component has unused export property 'h'. If it is for external reference only, please consider using `export const h`", - "pos": 145, + "message": "Component has unused export property 'h2'. If it is for external reference only, please consider using `export const h2`", + "pos": 564, "start": { - "character": 145, + "character": 564, "column": 13, - "line": 10 + "line": 33 } }, { "code": "unused-export-let", "end": { - "character": 199, - "column": 25, - "line": 12 + "character": 621, + "column": 26, + "line": 35 }, - "message": "Component has unused export property 'j'. If it is for external reference only, please consider using `export const j`", - "pos": 187, + "message": "Component has unused export property 'j2'. If it is for external reference only, please consider using `export const j2`", + "pos": 608, "start": { - "character": 187, + "character": 608, "column": 13, - "line": 12 + "line": 35 } } ] From 2c629bcc0c9eec64ad55d1359e75815c6e12cf14 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 2 Mar 2022 16:57:26 -0800 Subject: [PATCH 07/89] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df6ab56f3..cd8f5933bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Add `aria-description` to the list of allowed ARIA attributes ([#7301](https://github.com/sveltejs/svelte/issues/7301)) - Add TypeScript interfaces for typing actions ([#6538](https://github.com/sveltejs/svelte/issues/6538)) +- Do not generate `unused-export-let` warning inside `" ->
\ No newline at end of file + foo="">\" + bar="">\" +> diff --git a/test/server-side-rendering/samples/attribute-escaped-quotes/main.svelte b/test/server-side-rendering/samples/attribute-escaped-quotes/main.svelte index ed34f4d129..aeed0f20d1 100644 --- a/test/server-side-rendering/samples/attribute-escaped-quotes/main.svelte +++ b/test/server-side-rendering/samples/attribute-escaped-quotes/main.svelte @@ -1,5 +1,6 @@ -
\ No newline at end of file +
From 1632bca34e4803d6b0e0b0abd652ab5968181860 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 3 Mar 2022 12:24:04 +0100 Subject: [PATCH 11/89] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f0304be85..ff085942d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ * Add `aria-description` to the list of allowed ARIA attributes ([#7301](https://github.com/sveltejs/svelte/issues/7301)) * Add TypeScript interfaces for typing actions ([#6538](https://github.com/sveltejs/svelte/issues/6538)) * Do not generate `unused-export-let` warning inside ` ``` -...we can then import it from `App.svelte`... +...we can then import it in `App.svelte`... ```html -

+

+

+

diff --git a/test/runtime/samples/inline-style/_config.js b/test/runtime/samples/inline-style/_config.js index 3e984d4c69..192659346c 100644 --- a/test/runtime/samples/inline-style/_config.js +++ b/test/runtime/samples/inline-style/_config.js @@ -4,9 +4,9 @@ export default { `, test({ assert, component, target, window }) { - const p = target.querySelector('div'); + const div = target.querySelector('div'); - const styles = window.getComputedStyle(p); + const styles = window.getComputedStyle(div); assert.equal(styles.color, 'red'); } }; From 428023ea58fc373630ffa48bd6e49bbe454008f5 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 30 Mar 2022 10:50:54 -0400 Subject: [PATCH 16/89] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff085942d7..93b077a331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,12 @@ ## Unreleased -* Add `aria-description` to the list of allowed ARIA attributes ([#7301](https://github.com/sveltejs/svelte/issues/7301)) * Add TypeScript interfaces for typing actions ([#6538](https://github.com/sveltejs/svelte/issues/6538)) * Do not generate `unused-export-let` warning inside ` diff --git a/test/runtime/samples/key-block-component-slot/_config.js b/test/runtime/samples/key-block-component-slot/_config.js new file mode 100644 index 0000000000..8fbbeaec64 --- /dev/null +++ b/test/runtime/samples/key-block-component-slot/_config.js @@ -0,0 +1,18 @@ +const logs = []; + +export default { + html: '', + props: { + logs + }, + async test({ assert, component, target, raf }) { + assert.deepEqual(logs, ['mount']); + + const button = target.querySelector('button'); + + const click = new window.MouseEvent('click'); + await button.dispatchEvent(click); + + assert.deepEqual(logs, ['mount', 'unmount', 'mount']); + } +}; diff --git a/test/runtime/samples/key-block-component-slot/main.svelte b/test/runtime/samples/key-block-component-slot/main.svelte new file mode 100644 index 0000000000..77fde32d9c --- /dev/null +++ b/test/runtime/samples/key-block-component-slot/main.svelte @@ -0,0 +1,17 @@ + + + + {#key reset} + + {/key} + + + \ No newline at end of file From 54197c5a1f02b01291e6ee062fb51d377bcd99a6 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 8 Apr 2022 22:26:32 +0800 Subject: [PATCH 33/89] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46b871cfb4..da5eb22c40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Add `Symbol` as a known global ([#7419](https://github.com/sveltejs/svelte/pull/7419)) +* Fix `{#key}` block not reactive when the key variable is not being used ([#7422](https://github.com/sveltejs/svelte/pull/7422)) ## 3.46.6 From e0d93254fd9db02cf7fd1b9f35238bdcdbe04a81 Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Fri, 8 Apr 2022 23:54:55 +0900 Subject: [PATCH 34/89] [feature] Dynamic elements implementation (#6898) Closes #2324 Co-authored-by: Alfred Ringstad Co-authored-by: Simon Holthausen Co-authored-by: tanhauhau --- site/content/docs/02-template-syntax.md | 22 +++ .../03-svelte-element/app-a/App.svelte | 18 ++ .../03-svelte-element/app-b/App.svelte | 12 ++ .../03-svelte-element/text.md | 23 +++ .../app-a/App.svelte | 0 .../app-b/App.svelte | 0 .../text.md | 0 .../app-a/App.svelte | 0 .../app-b/App.svelte | 0 .../text.md | 0 .../app-a/App.svelte | 0 .../app-b/App.svelte | 0 .../text.md | 0 .../app-a/App.svelte | 0 .../app-b/App.svelte | 0 .../text.md | 0 .../app-a/App.svelte | 0 .../app-a/Todo.svelte | 0 .../app-a/flash.js | 0 .../app-b/App.svelte | 0 .../app-b/Todo.svelte | 0 .../app-b/flash.js | 0 .../text.md | 0 .../app-a/App.svelte | 0 .../app-a/Box.svelte | 0 .../app-b/App.svelte | 0 .../app-b/Box.svelte | 0 .../text.md | 0 src/compiler/compile/compiler_errors.ts | 4 + src/compiler/compile/nodes/Element.ts | 18 ++ src/compiler/compile/render_dom/Block.ts | 9 + .../render_dom/wrappers/Element/index.ts | 168 ++++++++++++++++-- .../compile/render_ssr/handlers/Element.ts | 25 ++- src/compiler/parse/errors.ts | 8 + src/compiler/parse/state/tag.ts | 23 ++- src/runtime/internal/dev.ts | 6 + test/css/samples/dynamic-element/_config.js | 21 +++ test/css/samples/dynamic-element/expected.css | 1 + .../css/samples/dynamic-element/expected.html | 1 + test/css/samples/dynamic-element/input.svelte | 10 ++ test/js/samples/debug-ssr-foo/expected.js | 2 +- .../dynamic-element-string/input.svelte | 2 + .../dynamic-element-string/output.json | 50 ++++++ .../dynamic-element-variable/input.svelte | 2 + .../dynamic-element-variable/output.json | 80 +++++++++ .../error-svelte-selfdestructive/error.json | 2 +- .../dynamic-element-action-update/_config.js | 45 +++++ .../dynamic-element-action-update/main.svelte | 14 ++ .../dynamic-element-animation-2/_config.js | 105 +++++++++++ .../dynamic-element-animation-2/main.svelte | 26 +++ .../dynamic-element-animation/_config.js | 62 +++++++ .../dynamic-element-animation/main.svelte | 18 ++ .../dynamic-element-attribute/_config.js | 17 ++ .../dynamic-element-attribute/main.svelte | 5 + .../_config.js | 3 + .../main.svelte | 6 + .../dynamic-element-binding-this/_config.js | 8 + .../dynamic-element-binding-this/main.svelte | 6 + .../dynamic-element-change-tag/_config.js | 17 ++ .../dynamic-element-change-tag/main.svelte | 5 + .../dynamic-element-empty-tag/_config.js | 3 + .../dynamic-element-empty-tag/main.svelte | 5 + .../dynamic-element-event-handler1/_config.js | 21 +++ .../main.svelte | 6 + .../dynamic-element-event-handler2/_config.js | 23 +++ .../main.svelte | 6 + .../dynamic-element-expression/_config.js | 3 + .../dynamic-element-expression/main.svelte | 1 + .../dynamic-element-invalid-this/_config.js | 9 + .../dynamic-element-invalid-this/main.svelte | 5 + .../dynamic-element-null-tag/_config.js | 3 + .../dynamic-element-null-tag/main.svelte | 5 + .../dynamic-element-pass-props/_config.js | 16 ++ .../dynamic-element-pass-props/main.svelte | 6 + .../samples/dynamic-element-slot/Foo.svelte | 7 + .../samples/dynamic-element-slot/_config.js | 29 +++ .../samples/dynamic-element-slot/main.svelte | 10 ++ .../samples/dynamic-element-store/_config.js | 3 + .../samples/dynamic-element-store/main.svelte | 6 + .../samples/dynamic-element-string/_config.js | 3 + .../dynamic-element-string/main.svelte | 1 + .../_config.js | 21 +++ .../main.svelte | 5 + .../dynamic-element-transition/_config.js | 17 ++ .../dynamic-element-transition/main.svelte | 17 ++ .../dynamic-element-undefined-tag/_config.js | 19 ++ .../dynamic-element-undefined-tag/main.svelte | 5 + .../dynamic-element-variable/_config.js | 20 +++ .../dynamic-element-variable/main.svelte | 6 + .../dynamic-element-string/_expected.html | 1 + .../dynamic-element-string/main.svelte | 1 + .../dynamic-element-variable/_expected.html | 2 + .../dynamic-element-variable/main.svelte | 7 + .../dynamic-element-invalid-tag/errors.json | 17 ++ .../dynamic-element-invalid-tag/input.svelte | 3 + .../dynamic-element-missing-tag/errors.json | 15 ++ .../dynamic-element-missing-tag/input.svelte | 3 + .../samples/dynamic-element-this/errors.json | 15 ++ .../samples/dynamic-element-this/input.svelte | 3 + 99 files changed, 1170 insertions(+), 22 deletions(-) create mode 100644 site/content/tutorial/16-special-elements/03-svelte-element/app-a/App.svelte create mode 100644 site/content/tutorial/16-special-elements/03-svelte-element/app-b/App.svelte create mode 100644 site/content/tutorial/16-special-elements/03-svelte-element/text.md rename site/content/tutorial/16-special-elements/{03-svelte-window => 04-svelte-window}/app-a/App.svelte (100%) rename site/content/tutorial/16-special-elements/{03-svelte-window => 04-svelte-window}/app-b/App.svelte (100%) rename site/content/tutorial/16-special-elements/{03-svelte-window => 04-svelte-window}/text.md (100%) rename site/content/tutorial/16-special-elements/{04-svelte-window-bindings => 05-svelte-window-bindings}/app-a/App.svelte (100%) rename site/content/tutorial/16-special-elements/{04-svelte-window-bindings => 05-svelte-window-bindings}/app-b/App.svelte (100%) rename site/content/tutorial/16-special-elements/{04-svelte-window-bindings => 05-svelte-window-bindings}/text.md (100%) rename site/content/tutorial/16-special-elements/{05-svelte-body => 06-svelte-body}/app-a/App.svelte (100%) rename site/content/tutorial/16-special-elements/{05-svelte-body => 06-svelte-body}/app-b/App.svelte (100%) rename site/content/tutorial/16-special-elements/{05-svelte-body => 06-svelte-body}/text.md (100%) rename site/content/tutorial/16-special-elements/{06-svelte-head => 07-svelte-head}/app-a/App.svelte (100%) rename site/content/tutorial/16-special-elements/{06-svelte-head => 07-svelte-head}/app-b/App.svelte (100%) rename site/content/tutorial/16-special-elements/{06-svelte-head => 07-svelte-head}/text.md (100%) rename site/content/tutorial/16-special-elements/{07-svelte-options => 08-svelte-options}/app-a/App.svelte (100%) rename site/content/tutorial/16-special-elements/{07-svelte-options => 08-svelte-options}/app-a/Todo.svelte (100%) rename site/content/tutorial/16-special-elements/{07-svelte-options => 08-svelte-options}/app-a/flash.js (100%) rename site/content/tutorial/16-special-elements/{07-svelte-options => 08-svelte-options}/app-b/App.svelte (100%) rename site/content/tutorial/16-special-elements/{07-svelte-options => 08-svelte-options}/app-b/Todo.svelte (100%) rename site/content/tutorial/16-special-elements/{07-svelte-options => 08-svelte-options}/app-b/flash.js (100%) rename site/content/tutorial/16-special-elements/{07-svelte-options => 08-svelte-options}/text.md (100%) rename site/content/tutorial/16-special-elements/{08-svelte-fragment => 09-svelte-fragment}/app-a/App.svelte (100%) rename site/content/tutorial/16-special-elements/{08-svelte-fragment => 09-svelte-fragment}/app-a/Box.svelte (100%) rename site/content/tutorial/16-special-elements/{08-svelte-fragment => 09-svelte-fragment}/app-b/App.svelte (100%) rename site/content/tutorial/16-special-elements/{08-svelte-fragment => 09-svelte-fragment}/app-b/Box.svelte (100%) rename site/content/tutorial/16-special-elements/{08-svelte-fragment => 09-svelte-fragment}/text.md (100%) create mode 100644 test/css/samples/dynamic-element/_config.js create mode 100644 test/css/samples/dynamic-element/expected.css create mode 100644 test/css/samples/dynamic-element/expected.html create mode 100644 test/css/samples/dynamic-element/input.svelte create mode 100644 test/parser/samples/dynamic-element-string/input.svelte create mode 100644 test/parser/samples/dynamic-element-string/output.json create mode 100644 test/parser/samples/dynamic-element-variable/input.svelte create mode 100644 test/parser/samples/dynamic-element-variable/output.json create mode 100644 test/runtime/samples/dynamic-element-action-update/_config.js create mode 100644 test/runtime/samples/dynamic-element-action-update/main.svelte create mode 100644 test/runtime/samples/dynamic-element-animation-2/_config.js create mode 100644 test/runtime/samples/dynamic-element-animation-2/main.svelte create mode 100644 test/runtime/samples/dynamic-element-animation/_config.js create mode 100644 test/runtime/samples/dynamic-element-animation/main.svelte create mode 100644 test/runtime/samples/dynamic-element-attribute/_config.js create mode 100644 test/runtime/samples/dynamic-element-attribute/main.svelte create mode 100644 test/runtime/samples/dynamic-element-binding-invalid/_config.js create mode 100644 test/runtime/samples/dynamic-element-binding-invalid/main.svelte create mode 100644 test/runtime/samples/dynamic-element-binding-this/_config.js create mode 100644 test/runtime/samples/dynamic-element-binding-this/main.svelte create mode 100644 test/runtime/samples/dynamic-element-change-tag/_config.js create mode 100644 test/runtime/samples/dynamic-element-change-tag/main.svelte create mode 100644 test/runtime/samples/dynamic-element-empty-tag/_config.js create mode 100644 test/runtime/samples/dynamic-element-empty-tag/main.svelte create mode 100644 test/runtime/samples/dynamic-element-event-handler1/_config.js create mode 100644 test/runtime/samples/dynamic-element-event-handler1/main.svelte create mode 100644 test/runtime/samples/dynamic-element-event-handler2/_config.js create mode 100644 test/runtime/samples/dynamic-element-event-handler2/main.svelte create mode 100644 test/runtime/samples/dynamic-element-expression/_config.js create mode 100644 test/runtime/samples/dynamic-element-expression/main.svelte create mode 100644 test/runtime/samples/dynamic-element-invalid-this/_config.js create mode 100644 test/runtime/samples/dynamic-element-invalid-this/main.svelte create mode 100644 test/runtime/samples/dynamic-element-null-tag/_config.js create mode 100644 test/runtime/samples/dynamic-element-null-tag/main.svelte create mode 100644 test/runtime/samples/dynamic-element-pass-props/_config.js create mode 100644 test/runtime/samples/dynamic-element-pass-props/main.svelte create mode 100644 test/runtime/samples/dynamic-element-slot/Foo.svelte create mode 100644 test/runtime/samples/dynamic-element-slot/_config.js create mode 100644 test/runtime/samples/dynamic-element-slot/main.svelte create mode 100644 test/runtime/samples/dynamic-element-store/_config.js create mode 100644 test/runtime/samples/dynamic-element-store/main.svelte create mode 100644 test/runtime/samples/dynamic-element-string/_config.js create mode 100644 test/runtime/samples/dynamic-element-string/main.svelte create mode 100644 test/runtime/samples/dynamic-element-template-literals/_config.js create mode 100644 test/runtime/samples/dynamic-element-template-literals/main.svelte create mode 100644 test/runtime/samples/dynamic-element-transition/_config.js create mode 100644 test/runtime/samples/dynamic-element-transition/main.svelte create mode 100644 test/runtime/samples/dynamic-element-undefined-tag/_config.js create mode 100644 test/runtime/samples/dynamic-element-undefined-tag/main.svelte create mode 100644 test/runtime/samples/dynamic-element-variable/_config.js create mode 100644 test/runtime/samples/dynamic-element-variable/main.svelte create mode 100644 test/server-side-rendering/samples/dynamic-element-string/_expected.html create mode 100644 test/server-side-rendering/samples/dynamic-element-string/main.svelte create mode 100644 test/server-side-rendering/samples/dynamic-element-variable/_expected.html create mode 100644 test/server-side-rendering/samples/dynamic-element-variable/main.svelte create mode 100644 test/validator/samples/dynamic-element-invalid-tag/errors.json create mode 100644 test/validator/samples/dynamic-element-invalid-tag/input.svelte create mode 100644 test/validator/samples/dynamic-element-missing-tag/errors.json create mode 100644 test/validator/samples/dynamic-element-missing-tag/input.svelte create mode 100644 test/validator/samples/dynamic-element-this/errors.json create mode 100644 test/validator/samples/dynamic-element-this/input.svelte diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index e42ef23b71..00070e563c 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -1627,6 +1627,28 @@ If `this` is falsy, no component is rendered. ``` +### `` + +```sv + +``` + +--- + +The `` element lets you render an element of a dynamically specified type. This is useful for example when rich text content from a CMS. If the tag is changed, the children will be preserved unless there's a transition attached to the element. 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, a warning will be logged in development mode. + +```sv + + +Foo +``` ### `` diff --git a/site/content/tutorial/16-special-elements/03-svelte-element/app-a/App.svelte b/site/content/tutorial/16-special-elements/03-svelte-element/app-a/App.svelte new file mode 100644 index 0000000000..0718efd949 --- /dev/null +++ b/site/content/tutorial/16-special-elements/03-svelte-element/app-a/App.svelte @@ -0,0 +1,18 @@ + + + + +{#if selected === 'h1'} +

I'm a h1 tag

+{:else if selected === 'h3'} +

I'm a h3 tag

+{:else if selected === 'p'} +

I'm a p tag

+{/if} diff --git a/site/content/tutorial/16-special-elements/03-svelte-element/app-b/App.svelte b/site/content/tutorial/16-special-elements/03-svelte-element/app-b/App.svelte new file mode 100644 index 0000000000..068f7b3c4f --- /dev/null +++ b/site/content/tutorial/16-special-elements/03-svelte-element/app-b/App.svelte @@ -0,0 +1,12 @@ + + + + +I'm a {selected} tag diff --git a/site/content/tutorial/16-special-elements/03-svelte-element/text.md b/site/content/tutorial/16-special-elements/03-svelte-element/text.md new file mode 100644 index 0000000000..ace344db42 --- /dev/null +++ b/site/content/tutorial/16-special-elements/03-svelte-element/text.md @@ -0,0 +1,23 @@ +--- +title: +--- + +Sometimes we don't know in advance what kind of DOM element to render. `` comes in handy here. Instead of a sequence of `if` blocks... + +```html +{#if selected === 'h1'} +

I'm a h1 tag

+{:else if selected === 'h3'} +

I'm a h3 tag

+{:else if selected === 'p'} +

I'm a p tag

+{/if} +``` + +...we can have a single dynamic component: + +```html +I'm a {selected} tag +``` + +The `this` value can be any string, or a falsy value — if it's falsy, no element is rendered. \ No newline at end of file diff --git a/site/content/tutorial/16-special-elements/03-svelte-window/app-a/App.svelte b/site/content/tutorial/16-special-elements/04-svelte-window/app-a/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/03-svelte-window/app-a/App.svelte rename to site/content/tutorial/16-special-elements/04-svelte-window/app-a/App.svelte diff --git a/site/content/tutorial/16-special-elements/03-svelte-window/app-b/App.svelte b/site/content/tutorial/16-special-elements/04-svelte-window/app-b/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/03-svelte-window/app-b/App.svelte rename to site/content/tutorial/16-special-elements/04-svelte-window/app-b/App.svelte diff --git a/site/content/tutorial/16-special-elements/03-svelte-window/text.md b/site/content/tutorial/16-special-elements/04-svelte-window/text.md similarity index 100% rename from site/content/tutorial/16-special-elements/03-svelte-window/text.md rename to site/content/tutorial/16-special-elements/04-svelte-window/text.md diff --git a/site/content/tutorial/16-special-elements/04-svelte-window-bindings/app-a/App.svelte b/site/content/tutorial/16-special-elements/05-svelte-window-bindings/app-a/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/04-svelte-window-bindings/app-a/App.svelte rename to site/content/tutorial/16-special-elements/05-svelte-window-bindings/app-a/App.svelte diff --git a/site/content/tutorial/16-special-elements/04-svelte-window-bindings/app-b/App.svelte b/site/content/tutorial/16-special-elements/05-svelte-window-bindings/app-b/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/04-svelte-window-bindings/app-b/App.svelte rename to site/content/tutorial/16-special-elements/05-svelte-window-bindings/app-b/App.svelte diff --git a/site/content/tutorial/16-special-elements/04-svelte-window-bindings/text.md b/site/content/tutorial/16-special-elements/05-svelte-window-bindings/text.md similarity index 100% rename from site/content/tutorial/16-special-elements/04-svelte-window-bindings/text.md rename to site/content/tutorial/16-special-elements/05-svelte-window-bindings/text.md diff --git a/site/content/tutorial/16-special-elements/05-svelte-body/app-a/App.svelte b/site/content/tutorial/16-special-elements/06-svelte-body/app-a/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/05-svelte-body/app-a/App.svelte rename to site/content/tutorial/16-special-elements/06-svelte-body/app-a/App.svelte diff --git a/site/content/tutorial/16-special-elements/05-svelte-body/app-b/App.svelte b/site/content/tutorial/16-special-elements/06-svelte-body/app-b/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/05-svelte-body/app-b/App.svelte rename to site/content/tutorial/16-special-elements/06-svelte-body/app-b/App.svelte diff --git a/site/content/tutorial/16-special-elements/05-svelte-body/text.md b/site/content/tutorial/16-special-elements/06-svelte-body/text.md similarity index 100% rename from site/content/tutorial/16-special-elements/05-svelte-body/text.md rename to site/content/tutorial/16-special-elements/06-svelte-body/text.md diff --git a/site/content/tutorial/16-special-elements/06-svelte-head/app-a/App.svelte b/site/content/tutorial/16-special-elements/07-svelte-head/app-a/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/06-svelte-head/app-a/App.svelte rename to site/content/tutorial/16-special-elements/07-svelte-head/app-a/App.svelte diff --git a/site/content/tutorial/16-special-elements/06-svelte-head/app-b/App.svelte b/site/content/tutorial/16-special-elements/07-svelte-head/app-b/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/06-svelte-head/app-b/App.svelte rename to site/content/tutorial/16-special-elements/07-svelte-head/app-b/App.svelte diff --git a/site/content/tutorial/16-special-elements/06-svelte-head/text.md b/site/content/tutorial/16-special-elements/07-svelte-head/text.md similarity index 100% rename from site/content/tutorial/16-special-elements/06-svelte-head/text.md rename to site/content/tutorial/16-special-elements/07-svelte-head/text.md diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/app-a/App.svelte b/site/content/tutorial/16-special-elements/08-svelte-options/app-a/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/07-svelte-options/app-a/App.svelte rename to site/content/tutorial/16-special-elements/08-svelte-options/app-a/App.svelte diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/app-a/Todo.svelte b/site/content/tutorial/16-special-elements/08-svelte-options/app-a/Todo.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/07-svelte-options/app-a/Todo.svelte rename to site/content/tutorial/16-special-elements/08-svelte-options/app-a/Todo.svelte diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/app-a/flash.js b/site/content/tutorial/16-special-elements/08-svelte-options/app-a/flash.js similarity index 100% rename from site/content/tutorial/16-special-elements/07-svelte-options/app-a/flash.js rename to site/content/tutorial/16-special-elements/08-svelte-options/app-a/flash.js diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/app-b/App.svelte b/site/content/tutorial/16-special-elements/08-svelte-options/app-b/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/07-svelte-options/app-b/App.svelte rename to site/content/tutorial/16-special-elements/08-svelte-options/app-b/App.svelte diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/app-b/Todo.svelte b/site/content/tutorial/16-special-elements/08-svelte-options/app-b/Todo.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/07-svelte-options/app-b/Todo.svelte rename to site/content/tutorial/16-special-elements/08-svelte-options/app-b/Todo.svelte diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/app-b/flash.js b/site/content/tutorial/16-special-elements/08-svelte-options/app-b/flash.js similarity index 100% rename from site/content/tutorial/16-special-elements/07-svelte-options/app-b/flash.js rename to site/content/tutorial/16-special-elements/08-svelte-options/app-b/flash.js diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/text.md b/site/content/tutorial/16-special-elements/08-svelte-options/text.md similarity index 100% rename from site/content/tutorial/16-special-elements/07-svelte-options/text.md rename to site/content/tutorial/16-special-elements/08-svelte-options/text.md diff --git a/site/content/tutorial/16-special-elements/08-svelte-fragment/app-a/App.svelte b/site/content/tutorial/16-special-elements/09-svelte-fragment/app-a/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/08-svelte-fragment/app-a/App.svelte rename to site/content/tutorial/16-special-elements/09-svelte-fragment/app-a/App.svelte diff --git a/site/content/tutorial/16-special-elements/08-svelte-fragment/app-a/Box.svelte b/site/content/tutorial/16-special-elements/09-svelte-fragment/app-a/Box.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/08-svelte-fragment/app-a/Box.svelte rename to site/content/tutorial/16-special-elements/09-svelte-fragment/app-a/Box.svelte diff --git a/site/content/tutorial/16-special-elements/08-svelte-fragment/app-b/App.svelte b/site/content/tutorial/16-special-elements/09-svelte-fragment/app-b/App.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/08-svelte-fragment/app-b/App.svelte rename to site/content/tutorial/16-special-elements/09-svelte-fragment/app-b/App.svelte diff --git a/site/content/tutorial/16-special-elements/08-svelte-fragment/app-b/Box.svelte b/site/content/tutorial/16-special-elements/09-svelte-fragment/app-b/Box.svelte similarity index 100% rename from site/content/tutorial/16-special-elements/08-svelte-fragment/app-b/Box.svelte rename to site/content/tutorial/16-special-elements/09-svelte-fragment/app-b/Box.svelte diff --git a/site/content/tutorial/16-special-elements/08-svelte-fragment/text.md b/site/content/tutorial/16-special-elements/09-svelte-fragment/text.md similarity index 100% rename from site/content/tutorial/16-special-elements/08-svelte-fragment/text.md rename to site/content/tutorial/16-special-elements/09-svelte-fragment/text.md diff --git a/src/compiler/compile/compiler_errors.ts b/src/compiler/compile/compiler_errors.ts index df832373fc..a04780e375 100644 --- a/src/compiler/compile/compiler_errors.ts +++ b/src/compiler/compile/compiler_errors.ts @@ -246,6 +246,10 @@ export default { code: 'invalid-animation', 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: ' cannot have a animate directive' + }, 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]`)' diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 78d6c9188f..00a9b6b22f 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -18,6 +18,9 @@ import Let from './Let'; import TemplateScope from './shared/TemplateScope'; import { INode } from './interfaces'; 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_errors from '../compiler_errors'; @@ -190,11 +193,26 @@ export default class Element extends Node { children: INode[]; namespace: string; 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) { super(component, parent, scope, info); 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); if (this.namespace !== namespaces.foreign) { diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index cad7e22170..34c4774804 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -48,6 +48,7 @@ export default class Block { hydrate: Array; mount: Array; measure: Array; + restore_measurements: Array; fix: Array; animate: Array; intro: Array; @@ -96,6 +97,7 @@ export default class Block { hydrate: [], mount: [], measure: [], + restore_measurements: [], fix: [], animate: [], intro: [], @@ -326,6 +328,12 @@ export default class Block { ${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() { ${this.chunks.fix} }`; @@ -379,6 +387,7 @@ export default class Block { m: ${properties.mount}, p: ${properties.update}, r: ${properties.measure}, + s: ${properties.restore_measurements}, f: ${properties.fix}, a: ${properties.animate}, i: ${properties.intro}, diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 989e7cb82b..b66c8938fc 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -26,6 +26,7 @@ import Action from '../../../nodes/Action'; import MustacheTagWrapper from '../MustacheTag'; import RawMustacheTagWrapper from '../RawMustacheTag'; import is_dynamic from '../shared/is_dynamic'; +import create_debugging_comment from '../shared/create_debugging_comment'; import { push_array } from '../../../../utils/push_array'; interface BindingGroup { @@ -134,6 +135,8 @@ const events = [ } ]; +const CHILD_DYNAMIC_ELEMENT_BLOCK = 'child_dynamic_element'; + export default class ElementWrapper extends Wrapper { node: Element; fragment: FragmentWrapper; @@ -147,6 +150,9 @@ export default class ElementWrapper extends Wrapper { var: any; void: boolean; + child_dynamic_element_block?: Block = null; + child_dynamic_element?: ElementWrapper = null; + constructor( renderer: Renderer, block: Block, @@ -156,6 +162,24 @@ export default class ElementWrapper extends Wrapper { next_sibling: Wrapper ) { 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 = { type: 'Identifier', name: node.name.replace(/[^a-zA-Z0-9_$]/g, '_') @@ -199,6 +223,8 @@ export default class ElementWrapper extends Wrapper { block.add_animation(); } + block.add_dependencies(node.tag_expr.dependencies); + // add directive and handler dependencies [node.animation, node.outro, ...node.actions, ...node.classes, ...node.styles].forEach(directive => { if (directive && directive.expression) { @@ -221,6 +247,7 @@ export default class ElementWrapper extends Wrapper { node.handlers.length > 0 || node.styles.length > 0 || this.node.name === 'option' || + node.tag_expr.dynamic_dependencies().length || renderer.options.dev ) { this.parent.cannot_use_innerhtml(); // need to use add_location @@ -232,6 +259,110 @@ export default class ElementWrapper extends Wrapper { } 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});`} + 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.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; if (this.node.name === 'noscript') return; @@ -249,7 +380,7 @@ export default class ElementWrapper extends Wrapper { if (renderer.options.hydratable) { if (parent_nodes) { 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) { @@ -357,6 +488,8 @@ export default class ElementWrapper extends Wrapper { 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() { @@ -364,7 +497,7 @@ export default class ElementWrapper extends Wrapper { } get_render_statement(block: Block) { - const { name, namespace } = this.node; + const { name, namespace, tag_expr } = this.node; if (namespace === namespaces.svg) { return x`@svg_element("${name}")`; @@ -379,22 +512,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("${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 .filter((attr) => !(attr instanceof SpreadAttributeWrapper) && !attr.property_name) .map((attr) => p`${(attr as StyleAttributeWrapper | AttributeWrapper).name}: true`); - const name = this.node.namespace - ? this.node.name - : this.node.name.toUpperCase(); + let reference; + if (this.node.tag_expr.node.type === 'Literal') { + 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) { - return x`@claim_svg_element(${nodes}, "${name}", { ${attributes} })`; + return x`@claim_svg_element(${nodes}, ${reference}, { ${attributes} })`; } else { - return x`@claim_element(${nodes}, "${name}", { ${attributes} })`; + return x`@claim_element(${nodes}, ${reference}, { ${attributes} })`; } } @@ -847,6 +990,11 @@ export default class ElementWrapper extends Wrapper { ${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` @fix_position(${this.var}); ${stop_animation}(); @@ -940,7 +1088,7 @@ export default class ElementWrapper extends Wrapper { if (should_cache) { block.chunks.update.push(b` if (${block.renderer.dirty(dependencies)} && (${cached_snippet} !== (${cached_snippet} = ${snippet}))) { - ${updater} + ${updater} } `); } else { diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index dd096ae79b..7c4c5ba765 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -8,8 +8,9 @@ import Expression from '../../nodes/shared/Expression'; import remove_whitespace_children from './utils/remove_whitespace_children'; import fix_attribute_casing from '../../render_dom/wrappers/Element/fix_attribute_casing'; import { namespaces } from '../../../utils/namespaces'; +import { Expression as ESExpression } from 'estree'; -export default function(node: Element, renderer: Renderer, options: RenderOptions) { +export default function (node: Element, renderer: Renderer, options: RenderOptions) { const children = remove_whitespace_children(node.children, node.next); @@ -22,7 +23,8 @@ export default function(node: Element, renderer: Renderer, options: RenderOption node.attributes.some((attribute) => attribute.name === 'contenteditable') ); - renderer.add_string(`<${node.name}`); + renderer.add_string('<'); + add_tag_name(); const class_expression_list = node.classes.map(class_directive => { const { expression, name } = class_directive; @@ -167,14 +169,25 @@ export default function(node: Element, renderer: Renderer, options: RenderOption renderer.add_expression(node_contents); } - if (!is_void(node.name)) { - renderer.add_string(``); - } + add_close_tag(); } else { renderer.render(children, options); + add_close_tag(); + } + function add_close_tag() { if (!is_void(node.name)) { - renderer.add_string(``); + renderer.add_string(''); + } + } + + function add_tag_name() { + if (node.tag_expr.node.type === 'Literal') { + renderer.add_string(node.tag_expr.node.value as string); + } else { + renderer.add_expression(node.tag_expr.node as ESExpression); } } } diff --git a/src/compiler/parse/errors.ts b/src/compiler/parse/errors.ts index ef1f72a8be..63bd5b0919 100644 --- a/src/compiler/parse/errors.ts +++ b/src/compiler/parse/errors.ts @@ -99,6 +99,10 @@ export default { code: `invalid-${slug}-content`, message: `<${name}> cannot have children` }), + invalid_element_definition: { + code: 'invalid-element-definition', + message: 'Invalid element definition' + }, invalid_element_placement: (slug: string, name: string) => ({ code: `invalid-${slug}-placement`, message: `<${name}> tags cannot be inside elements or blocks` @@ -161,6 +165,10 @@ export default { code: 'missing-attribute-value', message: 'Expected value for the attribute' }, + missing_element_definition: { + code: 'missing-element-definition', + message: ' must have a \'this\' attribute' + }, unclosed_script: { code: 'unclosed-script', message: ' + +tag is {tag}. diff --git a/test/runtime/samples/dynamic-element-animation-2/_config.js b/test/runtime/samples/dynamic-element-animation-2/_config.js new file mode 100644 index 0000000000..27b28bb349 --- /dev/null +++ b/test/runtime/samples/dynamic-element-animation-2/_config.js @@ -0,0 +1,105 @@ +let originalDivGetBoundingClientRect; +let originalSpanGetBoundingClientRect; +let originalParagraphGetBoundingClientRect; + +export default { + skip_if_ssr: true, + props: { + things: [ + { id: 1, name: 'a' }, + { id: 2, name: 'b' }, + { id: 3, name: 'c' }, + { id: 4, name: 'd' }, + { id: 5, name: 'e' } + ], + tag: 'div' + }, + + html: ` +
a
+
b
+
c
+
d
+
e
+ `, + + before_test() { + originalDivGetBoundingClientRect = + window.HTMLDivElement.prototype.getBoundingClientRect; + originalSpanGetBoundingClientRect = + window.HTMLSpanElement.prototype.getBoundingClientRect; + originalParagraphGetBoundingClientRect = + window.HTMLParagraphElement.prototype.getBoundingClientRect; + + window.HTMLDivElement.prototype.getBoundingClientRect = + fakeGetBoundingClientRect; + window.HTMLSpanElement.prototype.getBoundingClientRect = + fakeGetBoundingClientRect; + window.HTMLParagraphElement.prototype.getBoundingClientRect = + fakeGetBoundingClientRect; + + function fakeGetBoundingClientRect() { + const index = [...this.parentNode.children].indexOf(this); + const top = index * 30; + + return { + left: 0, + right: 100, + top, + bottom: top + 20 + }; + } + }, + after_test() { + window.HTMLDivElement.prototype.getBoundingClientRect = + originalDivGetBoundingClientRect; + window.HTMLSpanElement.prototype.getBoundingClientRect = + originalSpanGetBoundingClientRect; + window.HTMLParagraphElement.prototype.getBoundingClientRect = + originalParagraphGetBoundingClientRect; + }, + + async test({ assert, component, target, raf }) { + // switch tag and things at the same time + await component.update('p', [ + { id: 5, name: 'e' }, + { id: 2, name: 'b' }, + { id: 3, name: 'c' }, + { id: 4, name: 'd' }, + { id: 1, name: 'a' } + ]); + + const ps = document.querySelectorAll('p'); + assert.equal(ps[0].dy, 120); + assert.equal(ps[4].dy, -120); + + raf.tick(50); + assert.equal(ps[0].dy, 60); + assert.equal(ps[4].dy, -60); + + raf.tick(100); + assert.equal(ps[0].dy, 0); + assert.equal(ps[4].dy, 0); + + await component.update('span', [ + { id: 1, name: 'a' }, + { id: 2, name: 'b' }, + { id: 3, name: 'c' }, + { id: 4, name: 'd' }, + { id: 5, name: 'e' } + ]); + + const spans = document.querySelectorAll('span'); + + assert.equal(spans[0].dy, 120); + assert.equal(spans[4].dy, -120); + + raf.tick(150); + assert.equal(spans[0].dy, 60); + assert.equal(spans[4].dy, -60); + + raf.tick(200); + assert.equal(spans[0].dy, 0); + assert.equal(spans[4].dy, 0); + } +}; diff --git a/test/runtime/samples/dynamic-element-animation-2/main.svelte b/test/runtime/samples/dynamic-element-animation-2/main.svelte new file mode 100644 index 0000000000..f655a40af9 --- /dev/null +++ b/test/runtime/samples/dynamic-element-animation-2/main.svelte @@ -0,0 +1,26 @@ + + +{#each things as thing (thing.id)} + {thing.name} +{/each} \ No newline at end of file diff --git a/test/runtime/samples/dynamic-element-animation/_config.js b/test/runtime/samples/dynamic-element-animation/_config.js new file mode 100644 index 0000000000..e3c57a868e --- /dev/null +++ b/test/runtime/samples/dynamic-element-animation/_config.js @@ -0,0 +1,62 @@ +export default { + props: { + things: [ + { id: 1, name: 'a' }, + { id: 2, name: 'b' }, + { id: 3, name: 'c' }, + { id: 4, name: 'd' }, + { id: 5, name: 'e' } + ], + tag: 'div' + }, + + html: ` +
a
+
b
+
c
+
d
+
e
+ `, + + test({ assert, component, target, raf }) { + component.tag = 'p'; + assert.equal(target.querySelectorAll('p').length, 5); + + component.tag = 'div'; + let divs = target.querySelectorAll('div'); + divs.forEach(div => { + div.getBoundingClientRect = function() { + const index = [...this.parentNode.children].indexOf(this); + const top = index * 30; + + return { + left: 0, + right: 100, + top, + bottom: top + 20 + }; + }; + }); + + component.things = [ + { id: 5, name: 'e' }, + { id: 2, name: 'b' }, + { id: 3, name: 'c' }, + { id: 4, name: 'd' }, + { id: 1, name: 'a' } + ]; + + divs = target.querySelectorAll('div'); + assert.ok(~divs[0].style.animation.indexOf('__svelte')); + assert.equal(divs[1].style.animation, ''); + assert.equal(divs[2].style.animation, ''); + assert.equal(divs[3].style.animation, ''); + assert.ok(~divs[4].style.animation.indexOf('__svelte')); + + raf.tick(100); + assert.deepEqual([ + divs[0].style.animation, + divs[4].style.animation + ], ['', '']); + } +}; diff --git a/test/runtime/samples/dynamic-element-animation/main.svelte b/test/runtime/samples/dynamic-element-animation/main.svelte new file mode 100644 index 0000000000..596d12c77a --- /dev/null +++ b/test/runtime/samples/dynamic-element-animation/main.svelte @@ -0,0 +1,18 @@ + + +{#each things as thing (thing.id)} + {thing.name} +{/each} \ No newline at end of file diff --git a/test/runtime/samples/dynamic-element-attribute/_config.js b/test/runtime/samples/dynamic-element-attribute/_config.js new file mode 100644 index 0000000000..6e7c340437 --- /dev/null +++ b/test/runtime/samples/dynamic-element-attribute/_config.js @@ -0,0 +1,17 @@ +export default { + props: { + tag: 'div' + }, + html: '
Foo
', + + test({ assert, component, target }) { + component.tag = 'h1'; + + assert.htmlEqual( + target.innerHTML, + ` +

Foo

+ ` + ); + } +}; diff --git a/test/runtime/samples/dynamic-element-attribute/main.svelte b/test/runtime/samples/dynamic-element-attribute/main.svelte new file mode 100644 index 0000000000..2498e06de9 --- /dev/null +++ b/test/runtime/samples/dynamic-element-attribute/main.svelte @@ -0,0 +1,5 @@ + + +Foo diff --git a/test/runtime/samples/dynamic-element-binding-invalid/_config.js b/test/runtime/samples/dynamic-element-binding-invalid/_config.js new file mode 100644 index 0000000000..14c6d775dc --- /dev/null +++ b/test/runtime/samples/dynamic-element-binding-invalid/_config.js @@ -0,0 +1,3 @@ +export default { + error: "'value' is not a valid binding on elements" +}; diff --git a/test/runtime/samples/dynamic-element-binding-invalid/main.svelte b/test/runtime/samples/dynamic-element-binding-invalid/main.svelte new file mode 100644 index 0000000000..45f8f96061 --- /dev/null +++ b/test/runtime/samples/dynamic-element-binding-invalid/main.svelte @@ -0,0 +1,6 @@ + + + diff --git a/test/runtime/samples/dynamic-element-binding-this/_config.js b/test/runtime/samples/dynamic-element-binding-this/_config.js new file mode 100644 index 0000000000..e0722c9375 --- /dev/null +++ b/test/runtime/samples/dynamic-element-binding-this/_config.js @@ -0,0 +1,8 @@ +export default { + html: '
', + + test({ assert, component, target }) { + const div = target.querySelector('div'); + assert.equal(div, component.foo); + } +}; diff --git a/test/runtime/samples/dynamic-element-binding-this/main.svelte b/test/runtime/samples/dynamic-element-binding-this/main.svelte new file mode 100644 index 0000000000..75e8b02ce1 --- /dev/null +++ b/test/runtime/samples/dynamic-element-binding-this/main.svelte @@ -0,0 +1,6 @@ + + + diff --git a/test/runtime/samples/dynamic-element-change-tag/_config.js b/test/runtime/samples/dynamic-element-change-tag/_config.js new file mode 100644 index 0000000000..9e4bf6fd32 --- /dev/null +++ b/test/runtime/samples/dynamic-element-change-tag/_config.js @@ -0,0 +1,17 @@ +export default { + props: { + tag: 'div' + }, + html: '
Foo
', + + test({ assert, component, target }) { + component.tag = 'h1'; + + assert.htmlEqual( + target.innerHTML, + ` +

Foo

+ ` + ); + } +}; diff --git a/test/runtime/samples/dynamic-element-change-tag/main.svelte b/test/runtime/samples/dynamic-element-change-tag/main.svelte new file mode 100644 index 0000000000..a9c4d5c00c --- /dev/null +++ b/test/runtime/samples/dynamic-element-change-tag/main.svelte @@ -0,0 +1,5 @@ + + +Foo \ No newline at end of file diff --git a/test/runtime/samples/dynamic-element-empty-tag/_config.js b/test/runtime/samples/dynamic-element-empty-tag/_config.js new file mode 100644 index 0000000000..22dc25b41e --- /dev/null +++ b/test/runtime/samples/dynamic-element-empty-tag/_config.js @@ -0,0 +1,3 @@ +export default { + html: '' +}; diff --git a/test/runtime/samples/dynamic-element-empty-tag/main.svelte b/test/runtime/samples/dynamic-element-empty-tag/main.svelte new file mode 100644 index 0000000000..e3889ce0f5 --- /dev/null +++ b/test/runtime/samples/dynamic-element-empty-tag/main.svelte @@ -0,0 +1,5 @@ + + +Foo diff --git a/test/runtime/samples/dynamic-element-event-handler1/_config.js b/test/runtime/samples/dynamic-element-event-handler1/_config.js new file mode 100644 index 0000000000..03b8f7879d --- /dev/null +++ b/test/runtime/samples/dynamic-element-event-handler1/_config.js @@ -0,0 +1,21 @@ +let clicked = false; +function handler() { + clicked = true; +} + +export default { + props: { + handler + }, + html: '', + + test({ assert, target }) { + assert.equal(clicked, false); + + const button = target.querySelector('button'); + const click = new window.MouseEvent('click'); + button.dispatchEvent(click); + + assert.equal(clicked, true); + } +}; diff --git a/test/runtime/samples/dynamic-element-event-handler1/main.svelte b/test/runtime/samples/dynamic-element-event-handler1/main.svelte new file mode 100644 index 0000000000..7a7fef9c22 --- /dev/null +++ b/test/runtime/samples/dynamic-element-event-handler1/main.svelte @@ -0,0 +1,6 @@ + + +Foo \ No newline at end of file diff --git a/test/runtime/samples/dynamic-element-event-handler2/_config.js b/test/runtime/samples/dynamic-element-event-handler2/_config.js new file mode 100644 index 0000000000..22cbe735a7 --- /dev/null +++ b/test/runtime/samples/dynamic-element-event-handler2/_config.js @@ -0,0 +1,23 @@ +let clicked = false; +function handler() { + clicked = true; +} + +export default { + props: { + tag: 'div', + handler + }, + html: '
Foo
', + + test({ assert, component, target }) { + assert.equal(clicked, false); + + component.tag = 'button'; + const button = target.querySelector('button'); + const click = new window.MouseEvent('click'); + button.dispatchEvent(click); + + assert.equal(clicked, true); + } +}; diff --git a/test/runtime/samples/dynamic-element-event-handler2/main.svelte b/test/runtime/samples/dynamic-element-event-handler2/main.svelte new file mode 100644 index 0000000000..f2534c1f62 --- /dev/null +++ b/test/runtime/samples/dynamic-element-event-handler2/main.svelte @@ -0,0 +1,6 @@ + + +Foo diff --git a/test/runtime/samples/dynamic-element-expression/_config.js b/test/runtime/samples/dynamic-element-expression/_config.js new file mode 100644 index 0000000000..acad91c901 --- /dev/null +++ b/test/runtime/samples/dynamic-element-expression/_config.js @@ -0,0 +1,3 @@ +export default { + html: '
Foo
' +}; diff --git a/test/runtime/samples/dynamic-element-expression/main.svelte b/test/runtime/samples/dynamic-element-expression/main.svelte new file mode 100644 index 0000000000..7ec11e4ef6 --- /dev/null +++ b/test/runtime/samples/dynamic-element-expression/main.svelte @@ -0,0 +1 @@ +Foo \ No newline at end of file diff --git a/test/runtime/samples/dynamic-element-invalid-this/_config.js b/test/runtime/samples/dynamic-element-invalid-this/_config.js new file mode 100644 index 0000000000..3aaa554991 --- /dev/null +++ b/test/runtime/samples/dynamic-element-invalid-this/_config.js @@ -0,0 +1,9 @@ +export default { + compileOptions: { + dev: true + }, + props: { + tag: 123 + }, + error: ' expects "this" attribute to be a string.' +}; diff --git a/test/runtime/samples/dynamic-element-invalid-this/main.svelte b/test/runtime/samples/dynamic-element-invalid-this/main.svelte new file mode 100644 index 0000000000..bcc2c293bb --- /dev/null +++ b/test/runtime/samples/dynamic-element-invalid-this/main.svelte @@ -0,0 +1,5 @@ + + + diff --git a/test/runtime/samples/dynamic-element-null-tag/_config.js b/test/runtime/samples/dynamic-element-null-tag/_config.js new file mode 100644 index 0000000000..22dc25b41e --- /dev/null +++ b/test/runtime/samples/dynamic-element-null-tag/_config.js @@ -0,0 +1,3 @@ +export default { + html: '' +}; diff --git a/test/runtime/samples/dynamic-element-null-tag/main.svelte b/test/runtime/samples/dynamic-element-null-tag/main.svelte new file mode 100644 index 0000000000..58dc96ff2a --- /dev/null +++ b/test/runtime/samples/dynamic-element-null-tag/main.svelte @@ -0,0 +1,5 @@ + + +Foo diff --git a/test/runtime/samples/dynamic-element-pass-props/_config.js b/test/runtime/samples/dynamic-element-pass-props/_config.js new file mode 100644 index 0000000000..35f8b7abdf --- /dev/null +++ b/test/runtime/samples/dynamic-element-pass-props/_config.js @@ -0,0 +1,16 @@ +let clicked = false; + +export default { + props: { + tag: 'div', + onClick: () => clicked = true + }, + html: '
Foo
', + + async test({ assert, target, window }) { + const div = target.querySelector('div'); + await div.dispatchEvent(new window.MouseEvent('click')); + + assert.equal(clicked, true); + } +}; diff --git a/test/runtime/samples/dynamic-element-pass-props/main.svelte b/test/runtime/samples/dynamic-element-pass-props/main.svelte new file mode 100644 index 0000000000..6a54a93f27 --- /dev/null +++ b/test/runtime/samples/dynamic-element-pass-props/main.svelte @@ -0,0 +1,6 @@ + + +Foo \ No newline at end of file diff --git a/test/runtime/samples/dynamic-element-slot/Foo.svelte b/test/runtime/samples/dynamic-element-slot/Foo.svelte new file mode 100644 index 0000000000..51dda4b7e3 --- /dev/null +++ b/test/runtime/samples/dynamic-element-slot/Foo.svelte @@ -0,0 +1,7 @@ +

Foo

+
+ +
+
+ +
diff --git a/test/runtime/samples/dynamic-element-slot/_config.js b/test/runtime/samples/dynamic-element-slot/_config.js new file mode 100644 index 0000000000..aa9da522a3 --- /dev/null +++ b/test/runtime/samples/dynamic-element-slot/_config.js @@ -0,0 +1,29 @@ +export default { + props: { + x: true + }, + + html: ` +

Foo

+
+

This is default slot

+
+
+

This is other slot

+
+ `, + + test({ assert, component, target }) { + component.tag = 'h2'; + + assert.htmlEqual(target.innerHTML, ` +

Foo

+
+

This is default slot

+
+
+

This is other slot

+
+ `); + } +}; diff --git a/test/runtime/samples/dynamic-element-slot/main.svelte b/test/runtime/samples/dynamic-element-slot/main.svelte new file mode 100644 index 0000000000..4b1cd81969 --- /dev/null +++ b/test/runtime/samples/dynamic-element-slot/main.svelte @@ -0,0 +1,10 @@ + + + + This is default slot + This is other slot + + diff --git a/test/runtime/samples/dynamic-element-store/_config.js b/test/runtime/samples/dynamic-element-store/_config.js new file mode 100644 index 0000000000..ded19eef79 --- /dev/null +++ b/test/runtime/samples/dynamic-element-store/_config.js @@ -0,0 +1,3 @@ +export default { + html: '
' +}; diff --git a/test/runtime/samples/dynamic-element-store/main.svelte b/test/runtime/samples/dynamic-element-store/main.svelte new file mode 100644 index 0000000000..84a577ecee --- /dev/null +++ b/test/runtime/samples/dynamic-element-store/main.svelte @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/dynamic-element-string/_config.js b/test/runtime/samples/dynamic-element-string/_config.js new file mode 100644 index 0000000000..acad91c901 --- /dev/null +++ b/test/runtime/samples/dynamic-element-string/_config.js @@ -0,0 +1,3 @@ +export default { + html: '
Foo
' +}; diff --git a/test/runtime/samples/dynamic-element-string/main.svelte b/test/runtime/samples/dynamic-element-string/main.svelte new file mode 100644 index 0000000000..62d65d5f20 --- /dev/null +++ b/test/runtime/samples/dynamic-element-string/main.svelte @@ -0,0 +1 @@ +Foo \ No newline at end of file diff --git a/test/runtime/samples/dynamic-element-template-literals/_config.js b/test/runtime/samples/dynamic-element-template-literals/_config.js new file mode 100644 index 0000000000..d37164b4d8 --- /dev/null +++ b/test/runtime/samples/dynamic-element-template-literals/_config.js @@ -0,0 +1,21 @@ +export default { + props: { + size: 1 + }, + html: '

This is h1 tag

', + + test({ assert, component, target }) { + const h1 = target.firstChild; + component.size = 2; + + assert.htmlEqual( + target.innerHTML, + ` +

This is h2 tag

+ ` + ); + + const h2 = target.firstChild; + assert.notEqual(h1, h2); + } +}; diff --git a/test/runtime/samples/dynamic-element-template-literals/main.svelte b/test/runtime/samples/dynamic-element-template-literals/main.svelte new file mode 100644 index 0000000000..84b63dcca7 --- /dev/null +++ b/test/runtime/samples/dynamic-element-template-literals/main.svelte @@ -0,0 +1,5 @@ + + +This is h{size} tag diff --git a/test/runtime/samples/dynamic-element-transition/_config.js b/test/runtime/samples/dynamic-element-transition/_config.js new file mode 100644 index 0000000000..cb8474afc1 --- /dev/null +++ b/test/runtime/samples/dynamic-element-transition/_config.js @@ -0,0 +1,17 @@ +export default { + test({ assert, component, target, raf }) { + component.visible = true; + const h1 = target.querySelector('h1'); + assert.equal(h1.style.animation, '__svelte_3809512021_0 100ms linear 0ms 1 both'); + + raf.tick(150); + component.tag = 'h2'; + const h2 = target.querySelector('h2'); + assert.equal(h1.style.animation, ''); + assert.equal(h2.style.animation, ''); + + raf.tick(50); + component.visible = false; + assert.equal(h2.style.animation, '__svelte_3750847757_0 100ms linear 0ms 1 both'); + } +}; diff --git a/test/runtime/samples/dynamic-element-transition/main.svelte b/test/runtime/samples/dynamic-element-transition/main.svelte new file mode 100644 index 0000000000..b8c0eff0bd --- /dev/null +++ b/test/runtime/samples/dynamic-element-transition/main.svelte @@ -0,0 +1,17 @@ + + +{#if visible} + +{/if} diff --git a/test/runtime/samples/dynamic-element-undefined-tag/_config.js b/test/runtime/samples/dynamic-element-undefined-tag/_config.js new file mode 100644 index 0000000000..d0bd665d3d --- /dev/null +++ b/test/runtime/samples/dynamic-element-undefined-tag/_config.js @@ -0,0 +1,19 @@ +export default { + html: '', + test({ component, target, assert }) { + component.tag = 'h1'; + assert.htmlEqual(target.innerHTML, '

Foo

'); + + component.tag = null; + assert.htmlEqual(target.innerHTML, ''); + + component.tag = 'div'; + assert.htmlEqual(target.innerHTML, '
Foo
'); + + component.tag = false; + assert.htmlEqual(target.innerHTML, ''); + + component.tag = 'span'; + assert.htmlEqual(target.innerHTML, 'Foo'); + } +}; diff --git a/test/runtime/samples/dynamic-element-undefined-tag/main.svelte b/test/runtime/samples/dynamic-element-undefined-tag/main.svelte new file mode 100644 index 0000000000..6aca93ca13 --- /dev/null +++ b/test/runtime/samples/dynamic-element-undefined-tag/main.svelte @@ -0,0 +1,5 @@ + + +Foo diff --git a/test/runtime/samples/dynamic-element-variable/_config.js b/test/runtime/samples/dynamic-element-variable/_config.js new file mode 100644 index 0000000000..20e0fa9418 --- /dev/null +++ b/test/runtime/samples/dynamic-element-variable/_config.js @@ -0,0 +1,20 @@ +export default { + props: { + tag: 'div', + text: 'Foo' + }, + html: '
Foo
', + + test({ assert, component, target }) { + const div = target.firstChild; + component.tag = 'nav'; + component.text = 'Bar'; + + assert.htmlEqual(target.innerHTML, ` + + `); + + const h1 = target.firstChild; + assert.notEqual(div, h1); + } +}; diff --git a/test/runtime/samples/dynamic-element-variable/main.svelte b/test/runtime/samples/dynamic-element-variable/main.svelte new file mode 100644 index 0000000000..d60953bba5 --- /dev/null +++ b/test/runtime/samples/dynamic-element-variable/main.svelte @@ -0,0 +1,6 @@ + + +{text} \ No newline at end of file diff --git a/test/server-side-rendering/samples/dynamic-element-string/_expected.html b/test/server-side-rendering/samples/dynamic-element-string/_expected.html new file mode 100644 index 0000000000..cb98432e14 --- /dev/null +++ b/test/server-side-rendering/samples/dynamic-element-string/_expected.html @@ -0,0 +1 @@ +
Foo
diff --git a/test/server-side-rendering/samples/dynamic-element-string/main.svelte b/test/server-side-rendering/samples/dynamic-element-string/main.svelte new file mode 100644 index 0000000000..62d65d5f20 --- /dev/null +++ b/test/server-side-rendering/samples/dynamic-element-string/main.svelte @@ -0,0 +1 @@ +Foo \ No newline at end of file diff --git a/test/server-side-rendering/samples/dynamic-element-variable/_expected.html b/test/server-side-rendering/samples/dynamic-element-variable/_expected.html new file mode 100644 index 0000000000..3ae445f54c --- /dev/null +++ b/test/server-side-rendering/samples/dynamic-element-variable/_expected.html @@ -0,0 +1,2 @@ +

Foo

+
Bar
\ No newline at end of file diff --git a/test/server-side-rendering/samples/dynamic-element-variable/main.svelte b/test/server-side-rendering/samples/dynamic-element-variable/main.svelte new file mode 100644 index 0000000000..9aaba18bf1 --- /dev/null +++ b/test/server-side-rendering/samples/dynamic-element-variable/main.svelte @@ -0,0 +1,7 @@ + + +Foo +Bar \ No newline at end of file diff --git a/test/validator/samples/dynamic-element-invalid-tag/errors.json b/test/validator/samples/dynamic-element-invalid-tag/errors.json new file mode 100644 index 0000000000..c8d3c52490 --- /dev/null +++ b/test/validator/samples/dynamic-element-invalid-tag/errors.json @@ -0,0 +1,17 @@ +[ + { + "message": "Invalid element definition", + "code": "invalid-element-definition", + "start": { + "line": 2, + "column": 17, + "character": 23 + }, + "end": { + "line": 2, + "column": 17, + "character": 23 + }, + "pos": 23 + } +] diff --git a/test/validator/samples/dynamic-element-invalid-tag/input.svelte b/test/validator/samples/dynamic-element-invalid-tag/input.svelte new file mode 100644 index 0000000000..bc6b8be822 --- /dev/null +++ b/test/validator/samples/dynamic-element-invalid-tag/input.svelte @@ -0,0 +1,3 @@ +
+ foo +
diff --git a/test/validator/samples/dynamic-element-missing-tag/errors.json b/test/validator/samples/dynamic-element-missing-tag/errors.json new file mode 100644 index 0000000000..8243deeb80 --- /dev/null +++ b/test/validator/samples/dynamic-element-missing-tag/errors.json @@ -0,0 +1,15 @@ +[{ + "code": "missing-element-definition", + "message": " must have a 'this' attribute", + "start": { + "line": 2, + "column": 1, + "character": 7 + }, + "end": { + "line": 2, + "column": 1, + "character": 7 + }, + "pos": 7 +}] diff --git a/test/validator/samples/dynamic-element-missing-tag/input.svelte b/test/validator/samples/dynamic-element-missing-tag/input.svelte new file mode 100644 index 0000000000..4b645d25a1 --- /dev/null +++ b/test/validator/samples/dynamic-element-missing-tag/input.svelte @@ -0,0 +1,3 @@ +
+ foo +
diff --git a/test/validator/samples/dynamic-element-this/errors.json b/test/validator/samples/dynamic-element-this/errors.json new file mode 100644 index 0000000000..7d61c20ee2 --- /dev/null +++ b/test/validator/samples/dynamic-element-this/errors.json @@ -0,0 +1,15 @@ +[{ + "code": "unexpected-reserved-word", + "message": "'this' is a reserved word in JavaScript and cannot be used here", + "start": { + "line": 2, + "column": 18, + "character": 24 + }, + "end": { + "line": 2, + "column": 18, + "character": 24 + }, + "pos": 24 + }] diff --git a/test/validator/samples/dynamic-element-this/input.svelte b/test/validator/samples/dynamic-element-this/input.svelte new file mode 100644 index 0000000000..1a5079b924 --- /dev/null +++ b/test/validator/samples/dynamic-element-this/input.svelte @@ -0,0 +1,3 @@ +
+ foo +
From f6fd8e1ec835601634c4f8e0d4278dbc7a36ec9e Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 8 Apr 2022 16:59:03 +0200 Subject: [PATCH 35/89] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da5eb22c40..af883f9697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Svelte changelog -## Unreleased +## 3.47.0 (Unreleased) * Add `Symbol` as a known global ([#7419](https://github.com/sveltejs/svelte/pull/7419)) * Fix `{#key}` block not reactive when the key variable is not being used ([#7422](https://github.com/sveltejs/svelte/pull/7422)) +* Add support for dynamic elements through `` ([#2324](https://github.com/sveltejs/svelte/issues/2324)) ## 3.46.6 From eca1a652fb3939be477a194fe2aeaefec5cae207 Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Sat, 9 Apr 2022 02:30:49 +0900 Subject: [PATCH 36/89] [fix] better context checks for identifiers in const tags (#7222) Fixes #7423 Fixes #7431 Fixes #7206 Fixes #7431 Fixes #7221 Co-authored-by: tanhauhau --- .../compile/nodes/shared/Expression.ts | 10 ++++- .../samples/const-tag-each-const/_config.js | 29 +++++++++++++++ .../samples/const-tag-each-const/main.svelte | 26 +++++++++++++ .../_config.js | 29 +++++++++++++++ .../main.svelte | 19 ++++++++++ .../_config.js | 32 ++++++++++++++++ .../main.svelte | 33 +++++++++++++++++ .../_config.js | 29 +++++++++++++++ .../main.svelte | 25 +++++++++++++ .../const-tag-each-function/_config.js | 29 +++++++++++++++ .../const-tag-each-function/main.svelte | 26 +++++++++++++ .../samples/const-tag-shadow-2/_config.js | 37 +++++++++++++++++++ .../samples/const-tag-shadow-2/main.svelte | 15 ++++++++ 13 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 test/runtime/samples/const-tag-each-const/_config.js create mode 100644 test/runtime/samples/const-tag-each-const/main.svelte create mode 100644 test/runtime/samples/const-tag-each-duplicated-variable1/_config.js create mode 100644 test/runtime/samples/const-tag-each-duplicated-variable1/main.svelte create mode 100644 test/runtime/samples/const-tag-each-duplicated-variable2/_config.js create mode 100644 test/runtime/samples/const-tag-each-duplicated-variable2/main.svelte create mode 100644 test/runtime/samples/const-tag-each-duplicated-variable3/_config.js create mode 100644 test/runtime/samples/const-tag-each-duplicated-variable3/main.svelte create mode 100644 test/runtime/samples/const-tag-each-function/_config.js create mode 100644 test/runtime/samples/const-tag-each-function/main.svelte create mode 100644 test/runtime/samples/const-tag-shadow-2/_config.js create mode 100644 test/runtime/samples/const-tag-shadow-2/main.svelte diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts index 98fb2f1e3a..751b739564 100644 --- a/src/compiler/compile/nodes/shared/Expression.ts +++ b/src/compiler/compile/nodes/shared/Expression.ts @@ -254,11 +254,17 @@ export default class Expression { const declaration = b`const ${id} = ${node}`; if (owner.type === 'ConstTag') { + let child_scope = scope; walk(node, { - enter(node: Node) { - if (node.type === 'Identifier') { + enter(node: Node, parent: any) { + 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)); } + }, + leave(node: Node) { + if (map.has(node)) child_scope = child_scope.parent; } }); } else if (dependencies.size === 0 && contextual_dependencies.size === 0) { diff --git a/test/runtime/samples/const-tag-each-const/_config.js b/test/runtime/samples/const-tag-each-const/_config.js new file mode 100644 index 0000000000..cde4226a25 --- /dev/null +++ b/test/runtime/samples/const-tag-each-const/_config.js @@ -0,0 +1,29 @@ +export default { + html: ` +

0

+

bar: 1,2,3,1,1,2,3,2, num: 1

+

bar: 0,2,4,1,0,2,4,2, num: 2

+ `, + async test({ component, target, assert }) { + assert.htmlEqual( + target.innerHTML, + ` +

0

+

bar: 1,2,3,1,1,2,3,2, num: 1

+

bar: 0,2,4,1,0,2,4,2, num: 2

+ ` + ); + + component.nums = [1, 2, 3]; + + assert.htmlEqual( + target.innerHTML, + ` +

0

+

bar: 1,2,3,1,1,2,3,2,1,2,3,3, num: 1

+

bar: 0,2,4,1,0,2,4,2,0,2,4,3, num: 2

+

bar: -100,0,100,1,-100,0,100,2,-100,0,100,3, num: 3

+ ` + ); + } +}; diff --git a/test/runtime/samples/const-tag-each-const/main.svelte b/test/runtime/samples/const-tag-each-const/main.svelte new file mode 100644 index 0000000000..182ea3cfc6 --- /dev/null +++ b/test/runtime/samples/const-tag-each-const/main.svelte @@ -0,0 +1,26 @@ + + +

{foo}

+{#each nums as num, index} + {@const bar = nums.map((num) => { + const func = (foos, num) => { + return [...foos.map((foo) => foo), num]; + } + return func(foos[index].nums, num); + })} +

bar: {bar}, num: {num}

+{/each} diff --git a/test/runtime/samples/const-tag-each-duplicated-variable1/_config.js b/test/runtime/samples/const-tag-each-duplicated-variable1/_config.js new file mode 100644 index 0000000000..1f3e5856f2 --- /dev/null +++ b/test/runtime/samples/const-tag-each-duplicated-variable1/_config.js @@ -0,0 +1,29 @@ +export default { + html: ` +

bar: 1,2,3,0,2,4,-100,0,100, num: 1

+

bar: 1,2,3,0,2,4,-100,0,100, num: 2

+

bar: 1,2,3,0,2,4,-100,0,100, num: 3

+ `, + async test({ component, target, assert }) { + assert.htmlEqual( + target.innerHTML, + ` +

bar: 1,2,3,0,2,4,-100,0,100, num: 1

+

bar: 1,2,3,0,2,4,-100,0,100, num: 2

+

bar: 1,2,3,0,2,4,-100,0,100, num: 3

+ ` + ); + + component.nums = [1, 2, 3, 4]; + + assert.htmlEqual( + target.innerHTML, + ` +

bar: 1,2,3,0,2,4,-100,0,100, num: 1

+

bar: 1,2,3,0,2,4,-100,0,100, num: 2

+

bar: 1,2,3,0,2,4,-100,0,100, num: 3

+

bar: 1,2,3,0,2,4,-100,0,100, num: 4

+ ` + ); + } +}; diff --git a/test/runtime/samples/const-tag-each-duplicated-variable1/main.svelte b/test/runtime/samples/const-tag-each-duplicated-variable1/main.svelte new file mode 100644 index 0000000000..00c53dd4ff --- /dev/null +++ b/test/runtime/samples/const-tag-each-duplicated-variable1/main.svelte @@ -0,0 +1,19 @@ + + +{#each nums as num} + {@const bar = foos.map((foos) => foos.nums)} +

bar: {bar}, num: {num}

+{/each} diff --git a/test/runtime/samples/const-tag-each-duplicated-variable2/_config.js b/test/runtime/samples/const-tag-each-duplicated-variable2/_config.js new file mode 100644 index 0000000000..16f48218f1 --- /dev/null +++ b/test/runtime/samples/const-tag-each-duplicated-variable2/_config.js @@ -0,0 +1,32 @@ +export default { + html: ` +

foo: dummy-foo, num: dummy-num

+

bar: 1,2,3,2,, num: 1

+

bar: 1,2,3,2,, num: 2

+

bar: 1,2,3,2,, num: 3

+ `, + async test({ component, target, assert }) { + assert.htmlEqual( + target.innerHTML, + ` +

foo: dummy-foo, num: dummy-num

+

bar: 1,2,3,2,, num: 1

+

bar: 1,2,3,2,, num: 2

+

bar: 1,2,3,2,, num: 3

+ ` + ); + + component.nums = [1, 2, 3, 4]; + + assert.htmlEqual( + target.innerHTML, + ` +

foo: dummy-foo, num: dummy-num

+

bar: 1,2,3,2,4,, num: 1

+

bar: 1,2,3,2,4,, num: 2

+

bar: 1,2,3,2,4,, num: 3

+

bar: 1,2,3,2,4,, num: 4

+ ` + ); + } +}; diff --git a/test/runtime/samples/const-tag-each-duplicated-variable2/main.svelte b/test/runtime/samples/const-tag-each-duplicated-variable2/main.svelte new file mode 100644 index 0000000000..1f7afd92fd --- /dev/null +++ b/test/runtime/samples/const-tag-each-duplicated-variable2/main.svelte @@ -0,0 +1,33 @@ + + +

foo: {foo}, num: {num}

+{#each nums as num} + {@const bar = foos.map((foo) => + foo.nums.filter((num) => { + if (Object.keys($$slots).length) { + return false; + } else if (Object.keys(foo).length) { + return nums.includes(num) || default_nums.includes(num); + } else { + return false; + } + }) || num + )} +

bar: {bar}, num: {num}

+{/each} diff --git a/test/runtime/samples/const-tag-each-duplicated-variable3/_config.js b/test/runtime/samples/const-tag-each-duplicated-variable3/_config.js new file mode 100644 index 0000000000..cde4226a25 --- /dev/null +++ b/test/runtime/samples/const-tag-each-duplicated-variable3/_config.js @@ -0,0 +1,29 @@ +export default { + html: ` +

0

+

bar: 1,2,3,1,1,2,3,2, num: 1

+

bar: 0,2,4,1,0,2,4,2, num: 2

+ `, + async test({ component, target, assert }) { + assert.htmlEqual( + target.innerHTML, + ` +

0

+

bar: 1,2,3,1,1,2,3,2, num: 1

+

bar: 0,2,4,1,0,2,4,2, num: 2

+ ` + ); + + component.nums = [1, 2, 3]; + + assert.htmlEqual( + target.innerHTML, + ` +

0

+

bar: 1,2,3,1,1,2,3,2,1,2,3,3, num: 1

+

bar: 0,2,4,1,0,2,4,2,0,2,4,3, num: 2

+

bar: -100,0,100,1,-100,0,100,2,-100,0,100,3, num: 3

+ ` + ); + } +}; diff --git a/test/runtime/samples/const-tag-each-duplicated-variable3/main.svelte b/test/runtime/samples/const-tag-each-duplicated-variable3/main.svelte new file mode 100644 index 0000000000..f559627d23 --- /dev/null +++ b/test/runtime/samples/const-tag-each-duplicated-variable3/main.svelte @@ -0,0 +1,25 @@ + + +

{foo}

+{#each nums as num, index} + {@const bar = nums.map((num) => { + return (function (foos, num) { + return [...foos.map((foo) => foo), num]; + })(foos[index].nums, num); + })} +

bar: {bar}, num: {num}

+{/each} diff --git a/test/runtime/samples/const-tag-each-function/_config.js b/test/runtime/samples/const-tag-each-function/_config.js new file mode 100644 index 0000000000..cde4226a25 --- /dev/null +++ b/test/runtime/samples/const-tag-each-function/_config.js @@ -0,0 +1,29 @@ +export default { + html: ` +

0

+

bar: 1,2,3,1,1,2,3,2, num: 1

+

bar: 0,2,4,1,0,2,4,2, num: 2

+ `, + async test({ component, target, assert }) { + assert.htmlEqual( + target.innerHTML, + ` +

0

+

bar: 1,2,3,1,1,2,3,2, num: 1

+

bar: 0,2,4,1,0,2,4,2, num: 2

+ ` + ); + + component.nums = [1, 2, 3]; + + assert.htmlEqual( + target.innerHTML, + ` +

0

+

bar: 1,2,3,1,1,2,3,2,1,2,3,3, num: 1

+

bar: 0,2,4,1,0,2,4,2,0,2,4,3, num: 2

+

bar: -100,0,100,1,-100,0,100,2,-100,0,100,3, num: 3

+ ` + ); + } +}; diff --git a/test/runtime/samples/const-tag-each-function/main.svelte b/test/runtime/samples/const-tag-each-function/main.svelte new file mode 100644 index 0000000000..2e027c6620 --- /dev/null +++ b/test/runtime/samples/const-tag-each-function/main.svelte @@ -0,0 +1,26 @@ + + +

{foo}

+{#each nums as num, index} + {@const bar = nums.map((num) => { + function func(foos, num) { + return [...foos.map((foo) => foo), num]; + } + return func(foos[index].nums, num); + })} +

bar: {bar}, num: {num}

+{/each} diff --git a/test/runtime/samples/const-tag-shadow-2/_config.js b/test/runtime/samples/const-tag-shadow-2/_config.js new file mode 100644 index 0000000000..9ad4aed48c --- /dev/null +++ b/test/runtime/samples/const-tag-shadow-2/_config.js @@ -0,0 +1,37 @@ +export default { + html: ` +

1

+

3,6,9

+

2

+

3,6,9

+

3

+

3,6,9

+ `, + test({ component, target, assert }) { + component.baz = 5; + assert.htmlEqual( + target.innerHTML, + ` +

1

+

5,10,15

+

2

+

5,10,15

+

3

+

5,10,15

+ ` + ); + + component.array = [3, 4, 5]; + assert.htmlEqual( + target.innerHTML, + ` +

3

+

15,20,25

+

4

+

15,20,25

+

5

+

15,20,25

+ ` + ); + } +}; diff --git a/test/runtime/samples/const-tag-shadow-2/main.svelte b/test/runtime/samples/const-tag-shadow-2/main.svelte new file mode 100644 index 0000000000..c3bcb2f605 --- /dev/null +++ b/test/runtime/samples/const-tag-shadow-2/main.svelte @@ -0,0 +1,15 @@ + + +{#each array as item} +

{foo(item)}

+ {@const bar = array.map((item) => { + const bar = baz; + const foo = (item) => item * bar; + return foo(item); + })} +

{bar}

+{/each} From cd3bb35342492918f48e7aeecedfd9397d173f98 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 8 Apr 2022 19:32:35 +0200 Subject: [PATCH 37/89] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af883f9697..b343675fbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Add `Symbol` as a known global ([#7419](https://github.com/sveltejs/svelte/pull/7419)) * Fix `{#key}` block not reactive when the key variable is not being used ([#7422](https://github.com/sveltejs/svelte/pull/7422)) * Add support for dynamic elements through `` ([#2324](https://github.com/sveltejs/svelte/issues/2324)) +* Better context checks for identifiers in `{@const ..}` tags ([#7222](https://github.com/sveltejs/svelte/pull/7222)) ## 3.46.6 From 467ba0a920d9b9902a2059085bac2662c6813b9a Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 8 Apr 2022 14:24:31 -0400 Subject: [PATCH 38/89] -> v3.47.0 --- CHANGELOG.md | 8 ++++---- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b343675fbe..64b25a217d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ # Svelte changelog -## 3.47.0 (Unreleased) +## 3.47.0 -* Add `Symbol` as a known global ([#7419](https://github.com/sveltejs/svelte/pull/7419)) -* Fix `{#key}` block not reactive when the key variable is not being used ([#7422](https://github.com/sveltejs/svelte/pull/7422)) * Add support for dynamic elements through `` ([#2324](https://github.com/sveltejs/svelte/issues/2324)) -* Better context checks for identifiers in `{@const ..}` tags ([#7222](https://github.com/sveltejs/svelte/pull/7222)) +* 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 diff --git a/package-lock.json b/package-lock.json index 5f9fdfc5de..f42a96a32c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "svelte", - "version": "3.46.6", + "version": "3.47.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "3.46.6", + "version": "3.47.0", "license": "MIT", "devDependencies": { "@ampproject/remapping": "^0.3.0", diff --git a/package.json b/package.json index 82dc1e84c7..2237ef40f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.46.6", + "version": "3.47.0", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 707455fa8b29acd092c8b44382a89c347326b646 Mon Sep 17 00:00:00 2001 From: Kazuma Oe Date: Mon, 11 Apr 2022 00:50:15 +0900 Subject: [PATCH 39/89] [docs] add `` example (#7439) --- .../17-special-elements/02-svelte-element/App.svelte | 12 ++++++++++++ .../17-special-elements/02-svelte-element/meta.json | 3 +++ .../App.svelte | 0 .../{02-svelte-window => 03-svelte-window}/meta.json | 0 .../App.svelte | 0 .../meta.json | 0 .../{04-svelte-body => 05-svelte-body}/App.svelte | 0 .../{04-svelte-body => 05-svelte-body}/meta.json | 0 .../{05-svelte-head => 06-svelte-head}/App.svelte | 0 .../{05-svelte-head => 06-svelte-head}/meta.json | 0 10 files changed, 15 insertions(+) create mode 100644 site/content/examples/17-special-elements/02-svelte-element/App.svelte create mode 100644 site/content/examples/17-special-elements/02-svelte-element/meta.json rename site/content/examples/17-special-elements/{02-svelte-window => 03-svelte-window}/App.svelte (100%) rename site/content/examples/17-special-elements/{02-svelte-window => 03-svelte-window}/meta.json (100%) rename site/content/examples/17-special-elements/{03-svelte-window-bindings => 04-svelte-window-bindings}/App.svelte (100%) rename site/content/examples/17-special-elements/{03-svelte-window-bindings => 04-svelte-window-bindings}/meta.json (100%) rename site/content/examples/17-special-elements/{04-svelte-body => 05-svelte-body}/App.svelte (100%) rename site/content/examples/17-special-elements/{04-svelte-body => 05-svelte-body}/meta.json (100%) rename site/content/examples/17-special-elements/{05-svelte-head => 06-svelte-head}/App.svelte (100%) rename site/content/examples/17-special-elements/{05-svelte-head => 06-svelte-head}/meta.json (100%) diff --git a/site/content/examples/17-special-elements/02-svelte-element/App.svelte b/site/content/examples/17-special-elements/02-svelte-element/App.svelte new file mode 100644 index 0000000000..068f7b3c4f --- /dev/null +++ b/site/content/examples/17-special-elements/02-svelte-element/App.svelte @@ -0,0 +1,12 @@ + + + + +I'm a {selected} tag diff --git a/site/content/examples/17-special-elements/02-svelte-element/meta.json b/site/content/examples/17-special-elements/02-svelte-element/meta.json new file mode 100644 index 0000000000..4ff15b716f --- /dev/null +++ b/site/content/examples/17-special-elements/02-svelte-element/meta.json @@ -0,0 +1,3 @@ +{ + "title": "" +} \ No newline at end of file diff --git a/site/content/examples/17-special-elements/02-svelte-window/App.svelte b/site/content/examples/17-special-elements/03-svelte-window/App.svelte similarity index 100% rename from site/content/examples/17-special-elements/02-svelte-window/App.svelte rename to site/content/examples/17-special-elements/03-svelte-window/App.svelte diff --git a/site/content/examples/17-special-elements/02-svelte-window/meta.json b/site/content/examples/17-special-elements/03-svelte-window/meta.json similarity index 100% rename from site/content/examples/17-special-elements/02-svelte-window/meta.json rename to site/content/examples/17-special-elements/03-svelte-window/meta.json diff --git a/site/content/examples/17-special-elements/03-svelte-window-bindings/App.svelte b/site/content/examples/17-special-elements/04-svelte-window-bindings/App.svelte similarity index 100% rename from site/content/examples/17-special-elements/03-svelte-window-bindings/App.svelte rename to site/content/examples/17-special-elements/04-svelte-window-bindings/App.svelte diff --git a/site/content/examples/17-special-elements/03-svelte-window-bindings/meta.json b/site/content/examples/17-special-elements/04-svelte-window-bindings/meta.json similarity index 100% rename from site/content/examples/17-special-elements/03-svelte-window-bindings/meta.json rename to site/content/examples/17-special-elements/04-svelte-window-bindings/meta.json diff --git a/site/content/examples/17-special-elements/04-svelte-body/App.svelte b/site/content/examples/17-special-elements/05-svelte-body/App.svelte similarity index 100% rename from site/content/examples/17-special-elements/04-svelte-body/App.svelte rename to site/content/examples/17-special-elements/05-svelte-body/App.svelte diff --git a/site/content/examples/17-special-elements/04-svelte-body/meta.json b/site/content/examples/17-special-elements/05-svelte-body/meta.json similarity index 100% rename from site/content/examples/17-special-elements/04-svelte-body/meta.json rename to site/content/examples/17-special-elements/05-svelte-body/meta.json diff --git a/site/content/examples/17-special-elements/05-svelte-head/App.svelte b/site/content/examples/17-special-elements/06-svelte-head/App.svelte similarity index 100% rename from site/content/examples/17-special-elements/05-svelte-head/App.svelte rename to site/content/examples/17-special-elements/06-svelte-head/App.svelte diff --git a/site/content/examples/17-special-elements/05-svelte-head/meta.json b/site/content/examples/17-special-elements/06-svelte-head/meta.json similarity index 100% rename from site/content/examples/17-special-elements/05-svelte-head/meta.json rename to site/content/examples/17-special-elements/06-svelte-head/meta.json From afd3f4e5a9c279555db977bc9839ef662c9d5a0a Mon Sep 17 00:00:00 2001 From: gtmnayan <50981692+gtm-nayan@users.noreply.github.com> Date: Mon, 11 Apr 2022 06:35:28 +0545 Subject: [PATCH 40/89] [feat] Make setContext return the value that was passed in (#7432) * return value from setContext * update docs * Add test * eof new line * pacify the linter * const and tabs --- site/content/docs/03-run-time.md | 2 +- src/runtime/internal/lifecycle.ts | 3 ++- test/runtime/samples/context-setcontext-return/_config.js | 5 +++++ test/runtime/samples/context-setcontext-return/main.svelte | 7 +++++++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 test/runtime/samples/context-setcontext-return/_config.js create mode 100644 test/runtime/samples/context-setcontext-return/main.svelte diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index 636a98834d..a0d3f4148c 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -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. diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index bb3df3d295..3b3c2f5f71 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -46,8 +46,9 @@ export function createEventDispatcher< }; } -export function setContext(key, context: T) { +export function setContext(key, context: T): T { get_current_component().$$.context.set(key, context); + return context; } export function getContext(key): T { diff --git a/test/runtime/samples/context-setcontext-return/_config.js b/test/runtime/samples/context-setcontext-return/_config.js new file mode 100644 index 0000000000..28a78f1359 --- /dev/null +++ b/test/runtime/samples/context-setcontext-return/_config.js @@ -0,0 +1,5 @@ +export default { + html: ` +
true
+ ` +}; diff --git a/test/runtime/samples/context-setcontext-return/main.svelte b/test/runtime/samples/context-setcontext-return/main.svelte new file mode 100644 index 0000000000..87153ee846 --- /dev/null +++ b/test/runtime/samples/context-setcontext-return/main.svelte @@ -0,0 +1,7 @@ + + +
{a === b}
From c36f1c066ef2dbfd1a414d358151a09d0fe7ae4c Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Mon, 11 Apr 2022 08:51:37 +0800 Subject: [PATCH 41/89] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64b25a217d..1fa6ec5796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Return the context object in `setContext` [#7427](https://github.com/sveltejs/svelte/issues/7427) + ## 3.47.0 * Add support for dynamic elements through `` ([#2324](https://github.com/sveltejs/svelte/issues/2324)) From 1e0c10bebcf57b9eca6bb60fe213853c792322df Mon Sep 17 00:00:00 2001 From: josh-brainbox <71662743+josh-brainbox@users.noreply.github.com> Date: Mon, 11 Apr 2022 03:36:17 -0400 Subject: [PATCH 42/89] [docs] fix tutorial/keyed-each-blocks (#7218) --- site/content/tutorial/04-logic/05-keyed-each-blocks/text.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/tutorial/04-logic/05-keyed-each-blocks/text.md b/site/content/tutorial/04-logic/05-keyed-each-blocks/text.md index b990dead95..76d8322fb0 100644 --- a/site/content/tutorial/04-logic/05-keyed-each-blocks/text.md +++ b/site/content/tutorial/04-logic/05-keyed-each-blocks/text.md @@ -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. -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 `` 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 `` 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 `` component and its DOM node, and leave the others unaffected. From e50be669cc73063eb9f66d0c2c4b7d3b61d0488d Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 12 Apr 2022 16:38:08 +0800 Subject: [PATCH 43/89] fix const declared without let: binding get ignored (#7434) --- src/compiler/compile/nodes/SlotTemplate.ts | 5 +--- .../Component.svelte | 2 ++ .../_config.js | 15 ++++++++++++ .../main.svelte | 24 +++++++++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 test/runtime/samples/const-tag-component-without-let/Component.svelte create mode 100644 test/runtime/samples/const-tag-component-without-let/_config.js create mode 100644 test/runtime/samples/const-tag-component-without-let/main.svelte diff --git a/src/compiler/compile/nodes/SlotTemplate.ts b/src/compiler/compile/nodes/SlotTemplate.ts index cd18859603..90299c8e39 100644 --- a/src/compiler/compile/nodes/SlotTemplate.ts +++ b/src/compiler/compile/nodes/SlotTemplate.ts @@ -27,10 +27,7 @@ export default class SlotTemplate extends Node { this.validate_slot_template_placement(); - const has_let = info.attributes.some((node) => node.type === 'Let'); - if (has_let) { - scope = scope.child(); - } + scope = scope.child(); info.attributes.forEach((node) => { switch (node.type) { diff --git a/test/runtime/samples/const-tag-component-without-let/Component.svelte b/test/runtime/samples/const-tag-component-without-let/Component.svelte new file mode 100644 index 0000000000..e9e76f19b9 --- /dev/null +++ b/test/runtime/samples/const-tag-component-without-let/Component.svelte @@ -0,0 +1,2 @@ + + diff --git a/test/runtime/samples/const-tag-component-without-let/_config.js b/test/runtime/samples/const-tag-component-without-let/_config.js new file mode 100644 index 0000000000..047e034ce9 --- /dev/null +++ b/test/runtime/samples/const-tag-component-without-let/_config.js @@ -0,0 +1,15 @@ +export default { + html: ` +
static dynamic
+
static dynamic
+
static dynamic
+ `, + async test({ component, target, assert }) { + component.props = 'xxx'; + assert.htmlEqual(target.innerHTML, ` +
static xxx
+
static xxx
+
static xxx
+ `); + } +}; diff --git a/test/runtime/samples/const-tag-component-without-let/main.svelte b/test/runtime/samples/const-tag-component-without-let/main.svelte new file mode 100644 index 0000000000..9bee015c98 --- /dev/null +++ b/test/runtime/samples/const-tag-component-without-let/main.svelte @@ -0,0 +1,24 @@ + + + + + {@const foo = "static"} + {@const bar = props} +
{foo} {bar}
+
+ + + {@const foo = "static"} + {@const bar = props} +
{foo} {bar}
+
+
+ + + {@const foo = "static"} + {@const bar = props} +
{foo} {bar}
+
From fc19537995c8d2563345a4570e65ec6f08eb872d Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 12 Apr 2022 16:40:34 +0800 Subject: [PATCH 44/89] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fa6ec5796..2a7b5ee0a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Return the context object in `setContext` [#7427](https://github.com/sveltejs/svelte/issues/7427) +* Fix `{@const}` tag not working inside Component when there's no `let:` [#7189](https://github.com/sveltejs/svelte/issues/7189) ## 3.47.0 From d9687da63202633845e60e0538b3e075c5636ff8 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 12 Apr 2022 16:48:09 +0800 Subject: [PATCH 45/89] each animate allow having comment siblings (#7435) Co-authored-by: GHOST Co-authored-by: GHOST --- src/compiler/compile/nodes/EachBlock.ts | 5 ++++- .../samples/animation-comment-siblings/errors.json | 1 + .../samples/animation-comment-siblings/input.svelte | 9 +++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/validator/samples/animation-comment-siblings/errors.json create mode 100644 test/validator/samples/animation-comment-siblings/input.svelte diff --git a/src/compiler/compile/nodes/EachBlock.ts b/src/compiler/compile/nodes/EachBlock.ts index bb78845dd9..bea6fb7910 100644 --- a/src/compiler/compile/nodes/EachBlock.ts +++ b/src/compiler/compile/nodes/EachBlock.ts @@ -63,7 +63,7 @@ export default class EachBlock extends AbstractBlock { ([this.const_tags, this.children] = get_const_tags(info.children, component, this, this)); 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) { const child = this.children.find(child => !!(child as Element).animation); @@ -83,3 +83,6 @@ export default class EachBlock extends AbstractBlock { function isEmptyNode(node: INode) { return node.type === 'Text' && node.data.trim() === ''; } +function isCommentNode(node: INode) { + return node.type === 'Comment'; +} diff --git a/test/validator/samples/animation-comment-siblings/errors.json b/test/validator/samples/animation-comment-siblings/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/test/validator/samples/animation-comment-siblings/errors.json @@ -0,0 +1 @@ +[] diff --git a/test/validator/samples/animation-comment-siblings/input.svelte b/test/validator/samples/animation-comment-siblings/input.svelte new file mode 100644 index 0000000000..1c8170950b --- /dev/null +++ b/test/validator/samples/animation-comment-siblings/input.svelte @@ -0,0 +1,9 @@ + + +{#each things as thing (thing)} + +
+{/each} \ No newline at end of file From f100d96898b4845ebef666c8c887ae6add3b0952 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 12 Apr 2022 16:49:59 +0800 Subject: [PATCH 46/89] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7b5ee0a2..947173fd4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Return the context object in `setContext` [#7427](https://github.com/sveltejs/svelte/issues/7427) * Fix `{@const}` tag not working inside Component when there's no `let:` [#7189](https://github.com/sveltejs/svelte/issues/7189) +* Ignore comments in `{#each}` blocks when containing elements with `animate:` ([#3999](https://github.com/sveltejs/svelte/issues/3999)) ## 3.47.0 From c371c3fd3c70f35ef5b7e9b65d3539f6ef205767 Mon Sep 17 00:00:00 2001 From: tarunama Date: Tue, 12 Apr 2022 17:57:11 +0900 Subject: [PATCH 47/89] execute `npm audit fix` (#7421) --- package-lock.json | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index f42a96a32c..0b5a130992 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2989,9 +2989,9 @@ "dev": true }, "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "node_modules/json-schema-traverse": { @@ -3025,18 +3025,18 @@ } }, "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "dev": true, - "engines": [ - "node >=0.6.0" - ], "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", - "json-schema": "0.2.3", + "json-schema": "0.4.0", "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" } }, "node_modules/kleur": { @@ -7426,9 +7426,9 @@ "dev": true }, "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "json-schema-traverse": { @@ -7459,14 +7459,14 @@ } }, "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", - "json-schema": "0.2.3", + "json-schema": "0.4.0", "verror": "1.10.0" } }, From 0f94c890f5fde899c40f2be05bce8e87579f26f3 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Tue, 12 Apr 2022 22:52:22 +0800 Subject: [PATCH 48/89] [feat] dispatch cancelable custom events (#7064) --- CHANGELOG.md | 1 + site/content/docs/03-run-time.md | 25 +++++++++++++++++++++++-- src/runtime/internal/dev.ts | 2 +- src/runtime/internal/dom.ts | 4 ++-- src/runtime/internal/lifecycle.ts | 21 ++++++++++++++++----- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 947173fd4d..0b2bb5d7fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Return the context object in `setContext` [#7427](https://github.com/sveltejs/svelte/issues/7427) * Fix `{@const}` tag not working inside Component when there's no `let:` [#7189](https://github.com/sveltejs/svelte/issues/7189) * Ignore comments in `{#each}` blocks when containing elements with `animate:` ([#3999](https://github.com/sveltejs/svelte/issues/3999)) +* Add a third parameter to the returned function of `createEventDispatcher` that allows passing an object of `{ cancelable: true }` to create a cancelable custom event. The returned function when called will also return a boolean depending on whether the event is cancelled ([#7064](https://github.com/sveltejs/svelte/pull/7064)) ## 3.47.0 diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index a0d3f4148c..c6423f408c 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -221,14 +221,14 @@ Retrieves the whole context map that belongs to the closest parent component. Mu #### `createEventDispatcher` ```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`. -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 +``` + ### `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. diff --git a/src/runtime/internal/dev.ts b/src/runtime/internal/dev.ts index 4501771c0a..0ba47f7e86 100644 --- a/src/runtime/internal/dev.ts +++ b/src/runtime/internal/dev.ts @@ -2,7 +2,7 @@ import { custom_event, append, append_hydration, insert, insert_hydration, detac import { SvelteComponent } from './Component'; export function dispatch_dev(type: string, detail?: T) { - document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }, true)); + document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }, { bubbles: true })); } export function append_dev(target: Node, node: Node) { diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 287c16b5fc..e2a7420236 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -634,9 +634,9 @@ export function toggle_class(element, name, toggle) { element.classList[toggle ? 'add' : 'remove'](name); } -export function custom_event(type: string, detail?: T, bubbles: boolean = false) { +export function custom_event(type: string, detail?: T, { bubbles = false, cancelable = false } = {}): CustomEvent { const e: CustomEvent = document.createEvent('CustomEvent'); - e.initCustomEvent(type, bubbles, false, detail); + e.initCustomEvent(type, bubbles, cancelable, detail); return e; } diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index 3b3c2f5f71..fbbeca9a67 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -27,22 +27,33 @@ export function onDestroy(fn: () => any) { get_current_component().$$.on_destroy.push(fn); } -export function createEventDispatcher< - EventMap extends {} = any ->(): >(type: EventKey, detail?: EventMap[EventKey]) => void { +export interface DispatchOptions { + cancelable?: boolean; +} + +export function createEventDispatcher(): < + EventKey extends Extract +>( + type: EventKey, + detail?: EventMap[EventKey], + options?: DispatchOptions +) => boolean { const component = get_current_component(); - return (type: string, detail?: any) => { + return (type: string, detail?: any, { cancelable = false } = {}): boolean => { const callbacks = component.$$.callbacks[type]; if (callbacks) { // TODO are there situations where events could be dispatched // in a server (non-DOM) environment? - const event = custom_event(type, detail); + const event = custom_event(type, detail, { cancelable }); callbacks.slice().forEach(fn => { fn.call(component, event); }); + return !event.defaultPrevented; } + + return true; }; } From 9276f85768337c8eb4ba91c02a55271f24cb99a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20Gro=C3=9F?= <57948036+henninggross@users.noreply.github.com> Date: Tue, 12 Apr 2022 18:22:11 +0200 Subject: [PATCH 49/89] [feat] improve error message for animate used in a non-keyed each block (#6838) * introdcued new compiler error * making use of newly created compiler error * updated test for animation not in keyed each * removed unneeded conditions --- src/compiler/compile/compiler_errors.ts | 4 ++++ src/compiler/compile/nodes/Animation.ts | 7 ++++++- .../samples/animation-not-in-keyed-each/errors.json | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/compiler/compile/compiler_errors.ts b/src/compiler/compile/compiler_errors.ts index a04780e375..3099e6cca1 100644 --- a/src/compiler/compile/compiler_errors.ts +++ b/src/compiler/compile/compiler_errors.ts @@ -242,6 +242,10 @@ export default { code: 'invalid-animation', 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: { code: 'invalid-animation', message: 'An element that uses the animate directive must be the sole child of a keyed each block' diff --git a/src/compiler/compile/nodes/Animation.ts b/src/compiler/compile/nodes/Animation.ts index 507aa1df04..c05a2d8669 100644 --- a/src/compiler/compile/nodes/Animation.ts +++ b/src/compiler/compile/nodes/Animation.ts @@ -26,12 +26,17 @@ export default class Animation extends Node { } const block = parent.parent; - if (!block || block.type !== 'EachBlock' || !block.key) { + if (!block || block.type !== 'EachBlock') { // TODO can we relax the 'immediate child' rule? component.error(this, compiler_errors.invalid_animation_immediate); return; } + if (!block.key) { + component.error(this, compiler_errors.invalid_animation_key); + return; + } + (block as EachBlock).has_animation = true; this.expression = info.expression diff --git a/test/validator/samples/animation-not-in-keyed-each/errors.json b/test/validator/samples/animation-not-in-keyed-each/errors.json index 3e0b2d3c0c..a21579ce4f 100644 --- a/test/validator/samples/animation-not-in-keyed-each/errors.json +++ b/test/validator/samples/animation-not-in-keyed-each/errors.json @@ -1,6 +1,6 @@ [{ "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 used inside a keyed each block. Did you forget to add a key to your each block?", "start": { "line": 6, "column": 6, From 39d2dfcbcdbd1bb28748f826635a1617987f7bec Mon Sep 17 00:00:00 2001 From: gtmnayan <50981692+gtm-nayan@users.noreply.github.com> Date: Tue, 12 Apr 2022 23:46:32 +0545 Subject: [PATCH 50/89] [docs] style directive tutorial (#7161) --- .../01-classes/app-a/App.svelte | 0 .../01-classes/app-b/App.svelte | 0 .../01-classes/text.md | 0 .../02-class-shorthand/app-a/App.svelte | 0 .../02-class-shorthand/app-b/App.svelte | 0 .../02-class-shorthand/text.md | 0 .../03-inline-styles/app-a/App.svelte | 15 +++++++++++++++ .../03-inline-styles/app-b/App.svelte | 15 +++++++++++++++ .../03-inline-styles/text.md | 10 ++++++++++ .../04-style-directive/app-a/App.svelte | 15 +++++++++++++++ .../04-style-directive/app-b/App.svelte | 15 +++++++++++++++ .../04-style-directive/text.md | 18 ++++++++++++++++++ .../tutorial/13-advanced-styling/meta.json | 3 +++ site/content/tutorial/13-classes/meta.json | 3 --- 14 files changed, 91 insertions(+), 3 deletions(-) rename site/content/tutorial/{13-classes => 13-advanced-styling}/01-classes/app-a/App.svelte (100%) rename site/content/tutorial/{13-classes => 13-advanced-styling}/01-classes/app-b/App.svelte (100%) rename site/content/tutorial/{13-classes => 13-advanced-styling}/01-classes/text.md (100%) rename site/content/tutorial/{13-classes => 13-advanced-styling}/02-class-shorthand/app-a/App.svelte (100%) rename site/content/tutorial/{13-classes => 13-advanced-styling}/02-class-shorthand/app-b/App.svelte (100%) rename site/content/tutorial/{13-classes => 13-advanced-styling}/02-class-shorthand/text.md (100%) create mode 100644 site/content/tutorial/13-advanced-styling/03-inline-styles/app-a/App.svelte create mode 100644 site/content/tutorial/13-advanced-styling/03-inline-styles/app-b/App.svelte create mode 100644 site/content/tutorial/13-advanced-styling/03-inline-styles/text.md create mode 100644 site/content/tutorial/13-advanced-styling/04-style-directive/app-a/App.svelte create mode 100644 site/content/tutorial/13-advanced-styling/04-style-directive/app-b/App.svelte create mode 100644 site/content/tutorial/13-advanced-styling/04-style-directive/text.md create mode 100644 site/content/tutorial/13-advanced-styling/meta.json delete mode 100644 site/content/tutorial/13-classes/meta.json diff --git a/site/content/tutorial/13-classes/01-classes/app-a/App.svelte b/site/content/tutorial/13-advanced-styling/01-classes/app-a/App.svelte similarity index 100% rename from site/content/tutorial/13-classes/01-classes/app-a/App.svelte rename to site/content/tutorial/13-advanced-styling/01-classes/app-a/App.svelte diff --git a/site/content/tutorial/13-classes/01-classes/app-b/App.svelte b/site/content/tutorial/13-advanced-styling/01-classes/app-b/App.svelte similarity index 100% rename from site/content/tutorial/13-classes/01-classes/app-b/App.svelte rename to site/content/tutorial/13-advanced-styling/01-classes/app-b/App.svelte diff --git a/site/content/tutorial/13-classes/01-classes/text.md b/site/content/tutorial/13-advanced-styling/01-classes/text.md similarity index 100% rename from site/content/tutorial/13-classes/01-classes/text.md rename to site/content/tutorial/13-advanced-styling/01-classes/text.md diff --git a/site/content/tutorial/13-classes/02-class-shorthand/app-a/App.svelte b/site/content/tutorial/13-advanced-styling/02-class-shorthand/app-a/App.svelte similarity index 100% rename from site/content/tutorial/13-classes/02-class-shorthand/app-a/App.svelte rename to site/content/tutorial/13-advanced-styling/02-class-shorthand/app-a/App.svelte diff --git a/site/content/tutorial/13-classes/02-class-shorthand/app-b/App.svelte b/site/content/tutorial/13-advanced-styling/02-class-shorthand/app-b/App.svelte similarity index 100% rename from site/content/tutorial/13-classes/02-class-shorthand/app-b/App.svelte rename to site/content/tutorial/13-advanced-styling/02-class-shorthand/app-b/App.svelte diff --git a/site/content/tutorial/13-classes/02-class-shorthand/text.md b/site/content/tutorial/13-advanced-styling/02-class-shorthand/text.md similarity index 100% rename from site/content/tutorial/13-classes/02-class-shorthand/text.md rename to site/content/tutorial/13-advanced-styling/02-class-shorthand/text.md diff --git a/site/content/tutorial/13-advanced-styling/03-inline-styles/app-a/App.svelte b/site/content/tutorial/13-advanced-styling/03-inline-styles/app-a/App.svelte new file mode 100644 index 0000000000..b86665ce55 --- /dev/null +++ b/site/content/tutorial/13-advanced-styling/03-inline-styles/app-a/App.svelte @@ -0,0 +1,15 @@ + + + + +

This is a paragraph.

+ + diff --git a/site/content/tutorial/13-advanced-styling/03-inline-styles/app-b/App.svelte b/site/content/tutorial/13-advanced-styling/03-inline-styles/app-b/App.svelte new file mode 100644 index 0000000000..58280f71ad --- /dev/null +++ b/site/content/tutorial/13-advanced-styling/03-inline-styles/app-b/App.svelte @@ -0,0 +1,15 @@ + + + + +

This is a paragraph.

+ + diff --git a/site/content/tutorial/13-advanced-styling/03-inline-styles/text.md b/site/content/tutorial/13-advanced-styling/03-inline-styles/text.md new file mode 100644 index 0000000000..f558958358 --- /dev/null +++ b/site/content/tutorial/13-advanced-styling/03-inline-styles/text.md @@ -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. diff --git a/site/content/tutorial/13-advanced-styling/04-style-directive/app-a/App.svelte b/site/content/tutorial/13-advanced-styling/04-style-directive/app-a/App.svelte new file mode 100644 index 0000000000..58280f71ad --- /dev/null +++ b/site/content/tutorial/13-advanced-styling/04-style-directive/app-a/App.svelte @@ -0,0 +1,15 @@ + + + + +

This is a paragraph.

+ + diff --git a/site/content/tutorial/13-advanced-styling/04-style-directive/app-b/App.svelte b/site/content/tutorial/13-advanced-styling/04-style-directive/app-b/App.svelte new file mode 100644 index 0000000000..aade861b43 --- /dev/null +++ b/site/content/tutorial/13-advanced-styling/04-style-directive/app-b/App.svelte @@ -0,0 +1,15 @@ + + + + +

This is a paragraph.

+ + diff --git a/site/content/tutorial/13-advanced-styling/04-style-directive/text.md b/site/content/tutorial/13-advanced-styling/04-style-directive/text.md new file mode 100644 index 0000000000..88b4e05885 --- /dev/null +++ b/site/content/tutorial/13-advanced-styling/04-style-directive/text.md @@ -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 +

+``` + +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. diff --git a/site/content/tutorial/13-advanced-styling/meta.json b/site/content/tutorial/13-advanced-styling/meta.json new file mode 100644 index 0000000000..2b76d024e4 --- /dev/null +++ b/site/content/tutorial/13-advanced-styling/meta.json @@ -0,0 +1,3 @@ +{ + "title": "Advanced styling" +} \ No newline at end of file diff --git a/site/content/tutorial/13-classes/meta.json b/site/content/tutorial/13-classes/meta.json deleted file mode 100644 index 9c2b8a41f6..0000000000 --- a/site/content/tutorial/13-classes/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "title": "Classes" -} \ No newline at end of file From 4aff59b08034e1ce10944a7d8946fdd652d89218 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 13 Apr 2022 23:34:40 +0800 Subject: [PATCH 51/89] [fix] correctly track dependencies of let: bindings (#7448) Fixes #7440 --- CHANGELOG.md | 1 + .../compile/nodes/shared/Expression.ts | 2 +- .../component-slot-let-in-slot-2/Inner.svelte | 1 + .../component-slot-let-in-slot-2/Outer.svelte | 5 ++++ .../component-slot-let-in-slot-2/_config.js | 25 +++++++++++++++++++ .../component-slot-let-in-slot-2/main.svelte | 11 ++++++++ 6 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/runtime/samples/component-slot-let-in-slot-2/Inner.svelte create mode 100644 test/runtime/samples/component-slot-let-in-slot-2/Outer.svelte create mode 100644 test/runtime/samples/component-slot-let-in-slot-2/_config.js create mode 100644 test/runtime/samples/component-slot-let-in-slot-2/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b2bb5d7fb..904c927a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Fix `{@const}` tag not working inside Component when there's no `let:` [#7189](https://github.com/sveltejs/svelte/issues/7189) * Ignore comments in `{#each}` blocks when containing elements with `animate:` ([#3999](https://github.com/sveltejs/svelte/issues/3999)) * Add a third parameter to the returned function of `createEventDispatcher` that allows passing an object of `{ cancelable: true }` to create a cancelable custom event. The returned function when called will also return a boolean depending on whether the event is cancelled ([#7064](https://github.com/sveltejs/svelte/pull/7064)) +* Fix value of `let:` bindings not updating in certain cases ([#7440](https://github.com/sveltejs/svelte/issues/7440)) ## 3.47.0 diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts index 751b739564..a773355e31 100644 --- a/src/compiler/compile/nodes/shared/Expression.ts +++ b/src/compiler/compile/nodes/shared/Expression.ts @@ -90,7 +90,7 @@ export default class Expression { } if (template_scope.is_let(name)) { - if (!function_expression) { // TODO should this be `!lazy` ? + if (!lazy) { contextual_dependencies.add(name); dependencies.add(name); } diff --git a/test/runtime/samples/component-slot-let-in-slot-2/Inner.svelte b/test/runtime/samples/component-slot-let-in-slot-2/Inner.svelte new file mode 100644 index 0000000000..d0ea817d54 --- /dev/null +++ b/test/runtime/samples/component-slot-let-in-slot-2/Inner.svelte @@ -0,0 +1 @@ + diff --git a/test/runtime/samples/component-slot-let-in-slot-2/Outer.svelte b/test/runtime/samples/component-slot-let-in-slot-2/Outer.svelte new file mode 100644 index 0000000000..590a70564a --- /dev/null +++ b/test/runtime/samples/component-slot-let-in-slot-2/Outer.svelte @@ -0,0 +1,5 @@ + + + diff --git a/test/runtime/samples/component-slot-let-in-slot-2/_config.js b/test/runtime/samples/component-slot-let-in-slot-2/_config.js new file mode 100644 index 0000000000..96c43497db --- /dev/null +++ b/test/runtime/samples/component-slot-let-in-slot-2/_config.js @@ -0,0 +1,25 @@ +let logs; +function log(value) { + logs.push(value); +} + +export default { + props: { + prop: 'a', + log + }, + html: '', + before_test() { + logs = []; + }, + async test({ assert, component, target, window }) { + const button = target.querySelector('button'); + await button.dispatchEvent(new window.MouseEvent('click')); + + assert.deepEqual(logs, ['a']); + + component.prop = 'b'; + await button.dispatchEvent(new window.MouseEvent('click')); + assert.deepEqual(logs, ['a', 'b']); + } +}; diff --git a/test/runtime/samples/component-slot-let-in-slot-2/main.svelte b/test/runtime/samples/component-slot-let-in-slot-2/main.svelte new file mode 100644 index 0000000000..214b0895ae --- /dev/null +++ b/test/runtime/samples/component-slot-let-in-slot-2/main.svelte @@ -0,0 +1,11 @@ + + + +