diff --git a/documentation/docs/02-runes/05-$props.md b/documentation/docs/02-runes/05-$props.md index f300fb239d77..222b4831b65a 100644 --- a/documentation/docs/02-runes/05-$props.md +++ b/documentation/docs/02-runes/05-$props.md @@ -37,7 +37,7 @@ On the other side, inside `MyComponent.svelte`, we can receive props with the `$ ## Fallback values -Destructuring allows us to declare fallback values, which are used if the parent component does not set a given prop: +Destructuring allows us to declare fallback values, which are used if the parent component does not set a given prop (or the value is `undefined`): ```js let { adjective = 'happy' } = $props(); @@ -219,4 +219,4 @@ This is useful for linking elements via attributes like `for` and `aria-labelled -``` \ No newline at end of file +``` diff --git a/documentation/docs/03-template-syntax/01-basic-markup.md b/documentation/docs/03-template-syntax/01-basic-markup.md index 5e8b4342d3c5..fe5f8b02aae0 100644 --- a/documentation/docs/03-template-syntax/01-basic-markup.md +++ b/documentation/docs/03-template-syntax/01-basic-markup.md @@ -154,6 +154,8 @@ A JavaScript expression can be included as text by surrounding it with curly bra {expression} ``` +Expressions that are `null` or `undefined` will be omitted; all others are [coerced to strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion). + Curly braces can be included in a Svelte template by using their [HTML entity](https://developer.mozilla.org/docs/Glossary/Entity) strings: `{`, `{`, or `{` for `{` and `}`, `}`, or `}` for `}`. If you're using a regular expression (`RegExp`) [literal notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#literal_notation_and_constructor), you'll need to wrap it in parentheses. diff --git a/documentation/docs/03-template-syntax/06-snippet.md b/documentation/docs/03-template-syntax/06-snippet.md index c9951d3f3414..ab536c6e5ced 100644 --- a/documentation/docs/03-template-syntax/06-snippet.md +++ b/documentation/docs/03-template-syntax/06-snippet.md @@ -112,6 +112,8 @@ Snippets can reference themselves and each other ([demo](/playground/untitled#H4 ## Passing snippets to components +### Explicit props + Within the template, snippets are values just like any other. As such, they can be passed to components as props ([demo](/playground/untitled#H4sIAAAAAAAAE3VS247aMBD9lZGpBGwDASRegonaPvQL2qdlH5zYEKvBNvbQLbL875VzAcKyj3PmzJnLGU8UOwqSkd8KJdaCk4TsZS0cyV49wYuJuQiQpGd-N2bu_ooaI1YwJ57hpVYoFDqSEepKKw3mO7VDeTTaIvxiRS1gb_URxvO0ibrS8WanIrHUyiHs7Vmigy28RmyHHmKvDMbMmFq4cQInvGSwTsBYWYoMVhCSB2rBFFPsyl0uruTlR3JZCWvlTXl1Yy_mawiR_rbZKZrellJ-5JQ0RiBUgnFhJ9OGR7HKmwVoilXeIye8DOJGfYCgRlZ3iE876TBsZPX7hPdteO75PC4QaIo8vwNPePmANQ2fMeEFHrLD7rR1jTNkW986E8C3KwfwVr8HSHOSEBT_kGRozyIkn_zQveXDL3rIfPJHtUDwzShJd_Qk3gQCbOGLsdq4yfTRJopRuin3I7nv6kL7ARRjmLdBDG3uv1mhuLA3V2mKtqNEf_oCn8p9aN-WYqH5peP4kWBl1UwJzAEPT9U7K--0fRrrWnPTXpCm1_EVdXjpNmlA8G1hPPyM1fKgMqjFHjctXGjLhZ05w0qpDhksGrybuNEHtJnCalZWsuaTlfq6nPaaBSv_HKw-K57BjzOiVj9ZKQYKzQjZodYFqydYTRN4gPhVzTDO2xnma3HsVWjaLjT8nbfwHy7Q5f2dBAAA)): ```svelte @@ -144,6 +146,8 @@ Within the template, snippets are values just like any other. As such, they can Think about it like passing content instead of data to a component. The concept is similar to slots in web components. +### Implicit props + As an authoring convenience, snippets declared directly _inside_ a component implicitly become props _on_ the component ([demo](/playground/untitled#H4sIAAAAAAAAE3VSTa_aMBD8Kyu_SkAbCA-JSzBR20N_QXt6vIMTO8SqsY29tI2s_PcqTiB8vaPHs7MzuxuIZgdBMvJLo0QlOElIJZXwJHsLBBvb_XUASc7Mb9Yu_B-hsMMK5sUzvDQahUZPMkJ96aTFfKd3KA_WOISfrFACKmcOMFmk8TWUTjY73RFLoz1C5U4SPWzhrcN2GKDrlcGEWauEnyRwxCaDdQLWyVJksII2uaMWTDPNLtzX5YX8-kgua-GcHJVXI3u5WEPb0d83O03TMZSmfRzOkG1Db7mNacOL19JagVALxoWbztq-H8U6j0SaYp2P2BGbOyQ2v8PQIFMXLKRDk177pq0zf6d8bMrzwBdd0pamyPMb-IjNEzS2f86Gz_Dwf-2F9nvNSUJQ_EOSoTuJNvngqK5v4Pas7n4-OCwlEEJcQTIMO-nSQwtb-GSdsX46e9gbRoP9yGQ11I0rEuycunu6PHx1QnPhxm3SFN15MOlYEFJZtf0dUywMbwZOeBGsrKNLYB54-1R9WNqVdki7usim6VmQphf7mnpshiQRhNAXdoOfMyX3OgMlKtz0cGEcF27uLSul3mewjPjgOOoDukxjPS9rqfh0pb-8zs6aBSt_7505aZ7B9xOi0T9YKW4UooVsr0zB1BTrWQJ3EL-oWcZ572GxFoezCk37QLe3897-B2i2U62uBAAA)): ```svelte @@ -165,6 +169,8 @@ As an authoring convenience, snippets declared directly _inside_ a component imp ``` +### Implicit `children` snippet + Any content inside the component tags that is _not_ a snippet declaration implicitly becomes part of the `children` snippet ([demo](/playground/untitled#H4sIAAAAAAAAE3WOQQrCMBBFrzIMggql3ddY1Du4si5sOmIwnYRkFKX07lKqglqX8_7_w2uRDw1hjlsWI5ZqTPBoLEXMdy3K3fdZDzB5Ndfep_FKVnpWHSKNce1YiCVijirqYLwUJQOYxrsgsLmIOIZjcA1M02w4n-PpomSVvTclqyEutDX6DA2pZ7_ABIVugrmEC3XJH92P55_G39GodCmWBFrQJ2PrQAwdLGHig_NxNv9xrQa1dhWIawrv1Wzeqawa8953D-8QOmaEAQAA)): ```svelte @@ -184,6 +190,8 @@ Any content inside the component tags that is _not_ a snippet declaration implic > [!NOTE] Note that you cannot have a prop called `children` if you also have content inside the component — for this reason, you should avoid having props with that name +### Optional snippet props + You can declare snippet props as being optional. You can either use optional chaining to not render anything if the snippet isn't set... ```svelte diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3c876880b8e2..58f2317796c8 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.26.3 + +### Patch Changes + +- fix: correctly validate head snippets on the server ([#15755](https://github.com/sveltejs/svelte/pull/15755)) + +- fix: ignore mutation validation for props that are not proxies in more cases ([#15759](https://github.com/sveltejs/svelte/pull/15759)) + +- fix: allow self-closing tags within math namespace ([#15761](https://github.com/sveltejs/svelte/pull/15761)) + ## 5.26.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 75568f4a7723..b8654671ec97 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.26.2", + "version": "5.26.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js index 03dfaebcb793..d5689e5d5592 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -173,7 +173,8 @@ export function RegularElement(node, context) { if ( context.state.analysis.source[node.end - 2] === '/' && !is_void(node_name) && - !is_svg(node_name) + !is_svg(node_name) && + !is_mathml(node_name) ) { w.element_invalid_self_closing_tag(node, node.name); } diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 108c7adf9282..5a8af6d52299 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -31,13 +31,14 @@ export function create_ownership_validator(props) { return result; } - let value = props[name]; + /** @type {any} */ + let value = props; - for (let i = 1; i < path.length - 1; i++) { + for (let i = 0; i < path.length - 1; i++) { + value = value[path[i]]; if (!value?.[STATE_SYMBOL]) { return result; } - value = value[path[i]]; } const location = sanitize_location(`${component[FILENAME]}:${line}:${column}`); diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 34849196b7b6..157f22f929c9 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -6,7 +6,7 @@ import { } from '../../html-tree-validation.js'; import { current_component } from './context.js'; import { invalid_snippet_arguments } from '../shared/errors.js'; -import { Payload } from './payload.js'; +import { HeadPayload, Payload } from './payload.js'; /** * @typedef {{ @@ -105,7 +105,11 @@ export function pop_element() { * @param {Payload} payload */ export function validate_snippet_args(payload) { - if (typeof payload !== 'object' || !(payload instanceof Payload)) { + if ( + typeof payload !== 'object' || + // for some reason typescript consider the type of payload as never after the first instanceof + !(payload instanceof Payload || /** @type {any} */ (payload) instanceof HeadPayload) + ) { invalid_snippet_arguments(); } } diff --git a/packages/svelte/src/internal/server/payload.js b/packages/svelte/src/internal/server/payload.js index 03bcaf492ea9..8df5787ba4a7 100644 --- a/packages/svelte/src/internal/server/payload.js +++ b/packages/svelte/src/internal/server/payload.js @@ -1,16 +1,25 @@ +export class HeadPayload { + /** @type {Set<{ hash: string; code: string }>} */ + css = new Set(); + out = ''; + uid = () => ''; + title = ''; + + constructor(css = new Set(), out = '', title = '', uid = () => '') { + this.css = css; + this.out = out; + this.title = title; + this.uid = uid; + } +} + export class Payload { /** @type {Set<{ hash: string; code: string }>} */ css = new Set(); out = ''; uid = () => ''; - head = { - /** @type {Set<{ hash: string; code: string }>} */ - css: new Set(), - title: '', - out: '', - uid: () => '' - }; + head = new HeadPayload(); constructor(id_prefix = '') { this.uid = props_id_generator(id_prefix); @@ -30,12 +39,11 @@ export function copy_payload({ out, css, head, uid }) { payload.css = new Set(css); payload.uid = uid; - payload.head = { - title: head.title, - out: head.out, - css: new Set(head.css), - uid: head.uid - }; + payload.head = new HeadPayload(); + payload.head.out = head.out; + payload.head.css = new Set(head.css); + payload.head.title = head.title; + payload.head.uid = head.uid; return payload; } diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 2dc75f7e595b..190958814669 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.26.2'; +export const VERSION = '5.26.3'; export const PUBLIC_VERSION = '5'; diff --git a/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js new file mode 100644 index 000000000000..7c609205df9b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + mode: ['server'], + async test({ errors, assert }) { + assert.equal(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte new file mode 100644 index 000000000000..7eb31d3a9ee9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte @@ -0,0 +1,7 @@ +{#snippet head()} + Cool +{/snippet} + + + {@render head()} + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/_config.js new file mode 100644 index 000000000000..437385e185c8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/_config.js @@ -0,0 +1,18 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + test({ assert, target, warnings }) { + const btn = target.querySelector('button'); + btn?.click(); + flushSync(); + + assert.deepEqual(warnings, []); + }, + + warnings: [] +}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/child.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/child.svelte new file mode 100644 index 000000000000..0243a6c7d1fa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/child.svelte @@ -0,0 +1,8 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/main.svelte new file mode 100644 index 000000000000..8685664ab164 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/main.svelte @@ -0,0 +1,21 @@ + + + diff --git a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte index 376c9f79bd84..07582e834341 100644 --- a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte +++ b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte @@ -1,6 +1,7 @@ + diff --git a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json index 40b87ec7c8f4..3e45bca5ad15 100644 --- a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json +++ b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json @@ -3,11 +3,11 @@ "code": "element_invalid_self_closing_tag", "message": "Self-closing HTML tags for non-void elements are ambiguous — use `
` rather than `
`", "start": { - "line": 8, + "line": 9, "column": 0 }, "end": { - "line": 8, + "line": 9, "column": 7 } }, @@ -15,11 +15,11 @@ "code": "element_invalid_self_closing_tag", "message": "Self-closing HTML tags for non-void elements are ambiguous — use `` rather than ``", "start": { - "line": 9, + "line": 10, "column": 0 }, "end": { - "line": 9, + "line": 10, "column": 12 } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy