From d4fb9f4b100683cafc1f8d558c059c8691aab250 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Mon, 12 May 2025 20:00:13 -0600 Subject: [PATCH 1/6] fix: remove unncessary guards that require CSP privilege when removing event attributes (#15846) * fix: remove unncessary guards that cause CSP violations when removing event attributes * add changeset --- .changeset/hot-seals-hang.md | 5 +++++ .../svelte/src/internal/client/dom/elements/events.js | 8 ++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 .changeset/hot-seals-hang.md diff --git a/.changeset/hot-seals-hang.md b/.changeset/hot-seals-hang.md new file mode 100644 index 000000000000..184b89d506c8 --- /dev/null +++ b/.changeset/hot-seals-hang.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove unncessary guards that require CSP privilege when removing event attributes diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 3374fe713ff8..c2b7fc7d838d 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -26,12 +26,8 @@ export const root_event_handles = new Set(); export function replay_events(dom) { if (!hydrating) return; - if (dom.onload) { - dom.removeAttribute('onload'); - } - if (dom.onerror) { - dom.removeAttribute('onerror'); - } + dom.removeAttribute('onload'); + dom.removeAttribute('onerror'); // @ts-expect-error const event = dom.__e; if (event !== undefined) { From 8866851e2c3254d650732ee04c9cceb33f5a552c Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 12 May 2025 19:42:13 -0700 Subject: [PATCH 2/6] docs: add links to new CLI add-on documentation (#15835) Co-authored-by: Rich Harris --- documentation/docs/07-misc/02-testing.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/docs/07-misc/02-testing.md b/documentation/docs/07-misc/02-testing.md index 08420190392b..64bf49d77a27 100644 --- a/documentation/docs/07-misc/02-testing.md +++ b/documentation/docs/07-misc/02-testing.md @@ -6,9 +6,9 @@ Testing helps you write and maintain your code and guard against regressions. Te ## Unit and integration testing using Vitest -Unit tests allow you to test small isolated parts of your code. Integration tests allow you to test parts of your application to see if they work together. If you're using Vite (including via SvelteKit), we recommend using [Vitest](https://vitest.dev/). +Unit tests allow you to test small isolated parts of your code. Integration tests allow you to test parts of your application to see if they work together. If you're using Vite (including via SvelteKit), we recommend using [Vitest](https://vitest.dev/). You can use the Svelte CLI to [setup Vitest](/docs/cli/vitest) either during project creation or later on. -To get started, install Vitest: +To setup Vitest manually, first install it: ```bash npm install -D vitest @@ -254,9 +254,9 @@ When writing component tests that involve two-way bindings, context or snippet p E2E (short for 'end to end') tests allow you to test your full application through the eyes of the user. This section uses [Playwright](https://playwright.dev/) as an example, but you can also use other solutions like [Cypress](https://www.cypress.io/) or [NightwatchJS](https://nightwatchjs.org/). -To get started with Playwright, either install it via [the VS Code extension](https://playwright.dev/docs/getting-started-vscode), or install it from the command line using `npm init playwright`. It is also part of the setup CLI when you run `npx sv create`. +You can use the Svelte CLI to [setup Playwright](/docs/cli/playwright) either during project creation or later on. You can also [set it up with `npm init playwright`](https://playwright.dev/docs/intro). Additionally, you may also want to install an IDE plugin such as [the VS Code extension](https://playwright.dev/docs/getting-started-vscode) to be able to execute tests from inside your IDE. -After you've done that, you should have a `tests` folder and a Playwright config. You may need to adjust that config to tell Playwright what to do before running the tests - mainly starting your application at a certain port: +If you've run `npm init playwright` or are not using Vite, you may need to adjust the Playwright config to tell Playwright what to do before running the tests - mainly starting your application at a certain port. For example: ```js /// file: playwright.config.js From b6fe1cca3ce6170ce92c6e09d78c43f6be17f9fb Mon Sep 17 00:00:00 2001 From: Tristan <62366897+TitanCmd@users.noreply.github.com> Date: Tue, 13 May 2025 22:51:57 +1000 Subject: [PATCH 3/6] docs: remove dead link in 07-misc/99-faq.md (#15906) remove dead link from the docs in favor of pointing to our own docs --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- documentation/docs/07-misc/99-faq.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/99-faq.md b/documentation/docs/07-misc/99-faq.md index ed5c6277c099..cf98cdd3c3f1 100644 --- a/documentation/docs/07-misc/99-faq.md +++ b/documentation/docs/07-misc/99-faq.md @@ -81,9 +81,10 @@ _End-to-End Tests_: To ensure your users are able to interact with your applicat Some resources for getting started with testing: +- [Svelte docs on testing](/docs/svelte/testing) +- [Setup Vitest using the Svelte CLI](/docs/cli/vitest) - [Svelte Testing Library](https://testing-library.com/docs/svelte-testing-library/example/) - [Svelte Component Testing in Cypress](https://docs.cypress.io/guides/component-testing/svelte/overview) -- [Example using vitest](https://github.com/vitest-dev/vitest/tree/main/examples/sveltekit) - [Example using uvu test runner with JSDOM](https://github.com/lukeed/uvu/tree/master/examples/svelte) - [Test Svelte components using Vitest & Playwright](https://davipon.hashnode.dev/test-svelte-component-using-vitest-playwright) - [Component testing with WebdriverIO](https://webdriver.io/docs/component-testing/svelte) From 8fc8bc79d1bd0b1c44b40135faf4211a5e648e5d Mon Sep 17 00:00:00 2001 From: 7nik Date: Tue, 13 May 2025 17:16:37 +0300 Subject: [PATCH 4/6] docs: add missing bindings (#15834) * add missing bindings docs * tweak * tweak --------- Co-authored-by: 7nik Co-authored-by: Rich Harris --- .../docs/03-template-syntax/11-bind.md | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/11-bind.md index c23f3b52327c..0970ee384eba 100644 --- a/documentation/docs/03-template-syntax/11-bind.md +++ b/documentation/docs/03-template-syntax/11-bind.md @@ -117,6 +117,29 @@ Since 5.6.0, if an `` has a `defaultChecked` attribute and is part of a f ``` +## `` + +Checkboxes can be in an [indeterminate](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/indeterminate) state, independently of whether they are checked or unchecked: + +```svelte + + +
+ + + {#if indeterminate} + waiting... + {:else if checked} + checked + {:else} + unchecked + {/if} +
+``` + ## `` Inputs that work together can use `bind:group`. @@ -227,6 +250,7 @@ You can give the `` a default value by adding a `selected` attribute to ``` +## `window` and `document` + +To bind to properties of `window` and `document`, see [``](svelte-window) and [``](svelte-document). + ## Contenteditable bindings Elements with the `contenteditable` attribute support the following bindings: @@ -278,6 +306,10 @@ All visible elements have the following readonly bindings, measured with a `Resi - [`clientHeight`](https://developer.mozilla.org/en-US/docs/Web/API/Element/clientHeight) - [`offsetWidth`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth) - [`offsetHeight`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetHeight) +- [`contentRect`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentRect) +- [`contentBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentBoxSize) +- [`borderBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/borderBoxSize) +- [`devicePixelContentBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/devicePixelContentBoxSize) ```svelte
@@ -285,7 +317,7 @@ All visible elements have the following readonly bindings, measured with a `Resi
``` -> [!NOTE] `display: inline` elements do not have a width or height (except for elements with 'intrinsic' dimensions, like `` and ``), and cannot be observed with a `ResizeObserver`. You will need to change the `display` style of these elements to something else, such as `inline-block`. +> [!NOTE] `display: inline` elements do not have a width or height (except for elements with 'intrinsic' dimensions, like `` and ``), and cannot be observed with a `ResizeObserver`. You will need to change the `display` style of these elements to something else, such as `inline-block`. Note that CSS transformations do not trigger `ResizeObserver` callbacks. ## bind:this From 7826ebaccceeee5edda77b5d900d2c204590c6ff Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Tue, 13 May 2025 09:06:12 -0700 Subject: [PATCH 5/6] fix: rewrite destructuring logic to handle iterators (#15813) * fix: wrap array destructuring in spread to avoid iterator edge case * spread at `tmp` declaration * completely rewrite destructuring handling * only wrap in iife if necessary * oops * minor style tweaks * separate visitors * tweak --------- Co-authored-by: Rich Harris --- .changeset/serious-moles-yell.md | 5 ++ .../client/visitors/VariableDeclaration.js | 69 ++++++++++--------- .../server/visitors/VariableDeclaration.js | 13 ++-- .../phases/3-transform/shared/assignments.js | 67 ++++++++++-------- packages/svelte/src/compiler/phases/scope.js | 5 +- packages/svelte/src/compiler/utils/ast.js | 46 ++++++++++++- .../_expected/client/index.svelte.js | 10 +-- 7 files changed, 141 insertions(+), 74 deletions(-) create mode 100644 .changeset/serious-moles-yell.md diff --git a/.changeset/serious-moles-yell.md b/.changeset/serious-moles-yell.md new file mode 100644 index 000000000000..08f33dc05975 --- /dev/null +++ b/.changeset/serious-moles-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: rewrite destructuring logic to handle iterators diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js index 84044e4dedca..84c205d16325 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js @@ -2,7 +2,7 @@ /** @import { Binding } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; -import { extract_paths } from '../../../../utils/ast.js'; +import { build_pattern, extract_paths } from '../../../../utils/ast.js'; import * as b from '#compiler/builders'; import * as assert from '../../../../utils/assert.js'; import { get_rune } from '../../../scope.js'; @@ -141,20 +141,20 @@ export function VariableDeclaration(node, context) { b.declarator(declarator.id, create_state_declarator(declarator.id, value)) ); } else { - const tmp = context.state.scope.generate('tmp'); - const paths = extract_paths(declarator.id); + const [pattern, replacements] = build_pattern(declarator.id, context.state.scope); declarations.push( - b.declarator(b.id(tmp), value), - ...paths.map((path) => { - const value = path.expression?.(b.id(tmp)); - const binding = context.state.scope.get(/** @type {Identifier} */ (path.node).name); - return b.declarator( - path.node, - binding?.kind === 'state' || binding?.kind === 'raw_state' - ? create_state_declarator(binding.node, value) - : value - ); - }) + b.declarator(pattern, value), + .../** @type {[Identifier, Identifier][]} */ ([...replacements]).map( + ([original, replacement]) => { + const binding = context.state.scope.get(original.name); + return b.declarator( + original, + binding?.kind === 'state' || binding?.kind === 'raw_state' + ? create_state_declarator(binding.node, replacement) + : replacement + ); + } + ) ); } @@ -170,8 +170,7 @@ export function VariableDeclaration(node, context) { ) ); } else { - const bindings = extract_paths(declarator.id); - + const [pattern, replacements] = build_pattern(declarator.id, context.state.scope); const init = /** @type {CallExpression} */ (declarator.init); /** @type {Identifier} */ @@ -189,10 +188,16 @@ export function VariableDeclaration(node, context) { ); } - for (let i = 0; i < bindings.length; i++) { - const binding = bindings[i]; + for (let i = 0; i < replacements.size; i++) { + const [original, replacement] = [...replacements][i]; declarations.push( - b.declarator(binding.node, b.call('$.derived', b.thunk(binding.expression(rhs)))) + b.declarator( + original, + b.call( + '$.derived', + b.arrow([], b.block([b.let(pattern, rhs), b.return(replacement)])) + ) + ) ); } } @@ -304,19 +309,19 @@ function create_state_declarators(declarator, { scope, analysis }, value) { ]; } - const tmp = scope.generate('tmp'); - const paths = extract_paths(declarator.id); + const [pattern, replacements] = build_pattern(declarator.id, scope); return [ - b.declarator(b.id(tmp), value), - ...paths.map((path) => { - const value = path.expression?.(b.id(tmp)); - const binding = scope.get(/** @type {Identifier} */ (path.node).name); - return b.declarator( - path.node, - binding?.kind === 'state' - ? b.call('$.mutable_source', value, analysis.immutable ? b.true : undefined) - : value - ); - }) + b.declarator(pattern, value), + .../** @type {[Identifier, Identifier][]} */ ([...replacements]).map( + ([original, replacement]) => { + const binding = scope.get(original.name); + return b.declarator( + original, + binding?.kind === 'state' + ? b.call('$.mutable_source', replacement, analysis.immutable ? b.true : undefined) + : replacement + ); + } + ) ]; } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index b76455b5c149..17bf516a2232 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -3,7 +3,7 @@ /** @import { Context } from '../types.js' */ /** @import { ComponentAnalysis } from '../../../types.js' */ /** @import { Scope } from '../../../scope.js' */ -import { build_fallback, extract_paths } from '../../../../utils/ast.js'; +import { build_pattern, build_fallback, extract_paths } from '../../../../utils/ast.js'; import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; import { walk } from 'zimmerframe'; @@ -188,13 +188,10 @@ function create_state_declarators(declarator, scope, value) { return [b.declarator(declarator.id, value)]; } - const tmp = scope.generate('tmp'); - const paths = extract_paths(declarator.id); + const [pattern, replacements] = build_pattern(declarator.id, scope); return [ - b.declarator(b.id(tmp), value), // TODO inject declarator for opts, so we can use it below - ...paths.map((path) => { - const value = path.expression?.(b.id(tmp)); - return b.declarator(path.node, value); - }) + b.declarator(pattern, value), + // TODO inject declarator for opts, so we can use it below + ...[...replacements].map(([original, replacement]) => b.declarator(original, replacement)) ]; } diff --git a/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js b/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js index 3e6bb0c4c605..85b0869a1539 100644 --- a/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js +++ b/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js @@ -1,7 +1,7 @@ -/** @import { AssignmentExpression, AssignmentOperator, Expression, Node, Pattern } from 'estree' */ +/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Node, Pattern } from 'estree' */ /** @import { Context as ClientContext } from '../client/types.js' */ /** @import { Context as ServerContext } from '../server/types.js' */ -import { extract_paths, is_expression_async } from '../../../utils/ast.js'; +import { build_pattern, is_expression_async } from '../../../utils/ast.js'; import * as b from '#compiler/builders'; /** @@ -23,21 +23,23 @@ export function visit_assignment_expression(node, context, build_assignment) { let changed = false; - const assignments = extract_paths(node.left).map((path) => { - const value = path.expression?.(rhs); + const [pattern, replacements] = build_pattern(node.left, context.state.scope); - let assignment = build_assignment('=', path.node, value, context); - if (assignment !== null) changed = true; - - return ( - assignment ?? - b.assignment( - '=', - /** @type {Pattern} */ (context.visit(path.node)), - /** @type {Expression} */ (context.visit(value)) - ) - ); - }); + const assignments = [ + b.let(pattern, rhs), + ...[...replacements].map(([original, replacement]) => { + let assignment = build_assignment(node.operator, original, replacement, context); + if (assignment !== null) changed = true; + return b.stmt( + assignment ?? + b.assignment( + node.operator, + /** @type {Identifier} */ (context.visit(original)), + /** @type {Expression} */ (context.visit(replacement)) + ) + ); + }) + ]; if (!changed) { // No change to output -> nothing to transform -> we can keep the original assignment @@ -45,25 +47,36 @@ export function visit_assignment_expression(node, context, build_assignment) { } const is_standalone = /** @type {Node} */ (context.path.at(-1)).type.endsWith('Statement'); - const sequence = b.sequence(assignments); + const block = b.block(assignments); if (!is_standalone) { // this is part of an expression, we need the sequence to end with the value - sequence.expressions.push(rhs); + block.body.push(b.return(rhs)); } - if (should_cache) { - // the right hand side is a complex expression, wrap in an IIFE to cache it - const iife = b.arrow([rhs], sequence); + if (is_standalone && !should_cache) { + return block; + } - const iife_is_async = - is_expression_async(value) || - assignments.some((assignment) => is_expression_async(assignment)); + const iife = b.arrow(should_cache ? [rhs] : [], block); - return iife_is_async ? b.await(b.call(b.async(iife), value)) : b.call(iife, value); - } + const iife_is_async = + is_expression_async(value) || + assignments.some( + (assignment) => + (assignment.type === 'ExpressionStatement' && + is_expression_async(assignment.expression)) || + (assignment.type === 'VariableDeclaration' && + assignment.declarations.some( + (declaration) => + is_expression_async(declaration.id) || + (declaration.init && is_expression_async(declaration.init)) + )) + ); - return sequence; + return iife_is_async + ? b.await(b.call(b.async(iife), should_cache ? value : undefined)) + : b.call(iife, should_cache ? value : undefined); } if (node.left.type !== 'Identifier' && node.left.type !== 'MemberExpression') { diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 75a26d487b78..f37dfab8d1da 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -630,9 +630,10 @@ export class Scope { /** * @param {string} preferred_name + * @param {(name: string, counter: number) => string} [generator] * @returns {string} */ - generate(preferred_name) { + generate(preferred_name, generator = (name, counter) => `${name}_${counter}`) { if (this.#porous) { return /** @type {Scope} */ (this.parent).generate(preferred_name); } @@ -647,7 +648,7 @@ export class Scope { this.root.conflicts.has(name) || is_reserved(name) ) { - name = `${preferred_name}_${n++}`; + name = generator(preferred_name, n++); } this.references.set(name, []); diff --git a/packages/svelte/src/compiler/utils/ast.js b/packages/svelte/src/compiler/utils/ast.js index 23a95a102650..32ff5a37b35d 100644 --- a/packages/svelte/src/compiler/utils/ast.js +++ b/packages/svelte/src/compiler/utils/ast.js @@ -1,7 +1,8 @@ -/** @import { AST } from '#compiler' */ +/** @import { AST, Scope } from '#compiler' */ /** @import * as ESTree from 'estree' */ import { walk } from 'zimmerframe'; import * as b from '#compiler/builders'; +import is_reference from 'is-reference'; /** * Gets the left-most identifier of a member expression or identifier. @@ -128,6 +129,49 @@ export function unwrap_pattern(pattern, nodes = []) { return nodes; } +/** + * @param {ESTree.Pattern} id + * @param {Scope} scope + * @returns {[ESTree.Pattern, Map]} + */ +export function build_pattern(id, scope) { + /** @type {Map} */ + const map = new Map(); + + /** @type {Map} */ + const names = new Map(); + + let counter = 0; + + for (const node of unwrap_pattern(id)) { + const name = scope.generate(`$$${++counter}`, (_, counter) => `$$${counter}`); + + map.set(node, b.id(name)); + + if (node.type === 'Identifier') { + names.set(node.name, name); + } + } + + const pattern = walk(id, null, { + Identifier(node, context) { + if (is_reference(node, /** @type {ESTree.Pattern} */ (context.path.at(-1)))) { + const name = names.get(node.name); + if (name) return b.id(name); + } + }, + + MemberExpression(node, context) { + const n = map.get(node); + if (n) return n; + + context.next(); + } + }); + + return [pattern, map]; +} + /** * Extracts all identifiers from a pattern. * @param {ESTree.Pattern} pattern diff --git a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js index 47f297bce9c7..b2ef29ccafb5 100644 --- a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js @@ -7,10 +7,12 @@ let c = 3; let d = 4; export function update(array) { - ( - $.set(a, array[0], true), - $.set(b, array[1], true) - ); + { + let [$$1, $$2] = array; + + $.set(a, $$1, true); + $.set(b, $$2, true); + }; [c, d] = array; } \ No newline at end of file From 7636031b0d73ec350e0addb2dbfa7a1e3ee413df Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 13:22:08 -0400 Subject: [PATCH 6/6] Version Packages (#15905) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/hot-seals-hang.md | 5 ----- .changeset/serious-moles-yell.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/hot-seals-hang.md delete mode 100644 .changeset/serious-moles-yell.md diff --git a/.changeset/hot-seals-hang.md b/.changeset/hot-seals-hang.md deleted file mode 100644 index 184b89d506c8..000000000000 --- a/.changeset/hot-seals-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: remove unncessary guards that require CSP privilege when removing event attributes diff --git a/.changeset/serious-moles-yell.md b/.changeset/serious-moles-yell.md deleted file mode 100644 index 08f33dc05975..000000000000 --- a/.changeset/serious-moles-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: rewrite destructuring logic to handle iterators diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8df324d64988..935d2b7cc611 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.28.7 + +### Patch Changes + +- fix: remove unncessary guards that require CSP privilege when removing event attributes ([#15846](https://github.com/sveltejs/svelte/pull/15846)) + +- fix: rewrite destructuring logic to handle iterators ([#15813](https://github.com/sveltejs/svelte/pull/15813)) + ## 5.28.6 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index dadba9d50ce2..ce257c687a64 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.28.6", + "version": "5.28.7", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a77849804cd4..d7c041762f64 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.28.6'; +export const VERSION = '5.28.7'; export const PUBLIC_VERSION = '5'; 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