diff --git a/benchmarking/compare/index.js b/benchmarking/compare/index.js index a5fc6d10a9a1..9d8d279c353a 100644 --- a/benchmarking/compare/index.js +++ b/benchmarking/compare/index.js @@ -2,7 +2,6 @@ import fs from 'node:fs'; import path from 'node:path'; import { execSync, fork } from 'node:child_process'; import { fileURLToPath } from 'node:url'; -import { benchmarks } from '../benchmarks.js'; // if (execSync('git status --porcelain').toString().trim()) { // console.error('Working directory is not clean'); diff --git a/benchmarking/compare/runner.js b/benchmarking/compare/runner.js index 6fa58e2bacf3..a2e864637969 100644 --- a/benchmarking/compare/runner.js +++ b/benchmarking/compare/runner.js @@ -1,7 +1,7 @@ -import { benchmarks } from '../benchmarks.js'; +import { reactivity_benchmarks } from '../benchmarks/reactivity/index.js'; const results = []; -for (const benchmark of benchmarks) { +for (const benchmark of reactivity_benchmarks) { const result = await benchmark(); console.error(result.benchmark); results.push(result); diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index e346bceba81c..6a2b565aeaa2 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -280,7 +280,7 @@ You might be tempted to do something convoluted with effects to link one value t ``` -Instead, use callbacks where possible ([demo](/playground/untitled#H4sIAAAAAAAACo1SMW6EMBD8imWluFMSIEUaDiKlvy5lSOHjlhOSMRZeTiDkv8deMEEJRcqdmZ1ZjzzxqpZgePo5cRw18JQA_sSVaPz0rnVk7iDRYxdhYA8vW4Wg0NnwzJRdrfGtUAVKQIYtCsly9pIkp4AZ7cQOezAoEA7JcWUkVBuCdol0dNWrEutWsV5fHfnhPQ5wZJMnCwyejxCh6G6A0V3IHk4zu_jOxzzPBxBld83PTr7xXrb3rUNw8PbiYJ3FP22oTIoLSComq5XuXTeu8LzgnVA3KDgj13wiQ8taRaJ82rzXskYM-URRlsXktejjgNLoo9e4fyf70_8EnwncySX1GuunX6kGRwnzR_BgaPNaGy3FmLJKwrCUeBM6ZUn0Cs2mOlp3vwthQJ5i14P9st9vZqQlsQIAAA==)): +Instead, use `oninput` callbacks or — better still — [function bindings](bind#Function-bindings) where possible ([demo](/playground/untitled#H4sIAAAAAAAAE51SsW6DMBT8FcvqABINdOhCIFKXTt06lg4GHpElYyz8iECIf69tcIIipo6-u3f3fPZMJWuBpvRzkBXyTpKSy5rLq6YRbbgATdOfmeKkrMgCBt9GPpQ66RsItFjJNBzhVScRJBobmumq5wovhSxQABLskAmSk7ckOXtMKyM22ItGhhAk4Z0R0OwIN-tIQzd-90HVhvy2HsGNiQFCMltBgd7XoecV2xzXNV7XaEcth7ZfRv7kujnsTX2Qd7USb5rFjwZkJlgJwpWRcakG04cpOS9oz-QVCuoeInXW-RyEJL-sG0b7Wy6kZWM-u7CFxM5tdrIl9qg72vB74H-y7T2iXROHyVb0CLanp1yNk4D1A1jQ91hzrQSbUtIIGLcir0ylJDm9Q7urz42bX4UwIk2xH2D5Xf4A7SeMcMQCAAA=)): ```svelte ``` -If you need to use bindings, for whatever reason (for example when you want some kind of "writable `$derived`"), consider using getters and setters to synchronise state ([demo](/playground/untitled#H4sIAAAAAAAACpWRwW6DMBBEf8WyekikFOihFwcq9TvqHkyyQUjGsfCCQMj_XnvBNKpy6Qn2DTOD1wu_tRocF18Lx9kCFwT4iRvVxenT2syNoDGyWjl4xi93g2AwxPDSXfrW4oc0EjUgwzsqzSr2VhTnxJwNHwf24lAhHIpjVDZNwy1KS5wlNoGMSg9wOCYksQccerMlv65p51X0p_Xpdt_4YEy9yTkmV3z4MJT579-bUqsaNB2kbI0dwlnCgirJe2UakJzVrbkKaqkWivasU1O1ULxnOVk3JU-Uxti0p_-vKO4no_enbQ_yXhnZn0aHs4b1jiJMK7q2zmo1C3bTMG3LaZQVrMjeoSPgaUtkDxePMCEX2Ie6b_8D4WyJJEwCAAA=)): - -```svelte - - - - - -``` - If you absolutely have to update `$state` within an effect and run into an infinite loop because you read and write to the same `$state`, use [untrack](svelte#untrack). diff --git a/documentation/docs/06-runtime/02-context.md b/documentation/docs/06-runtime/02-context.md index 87b93a92b269..b698323a04ee 100644 --- a/documentation/docs/06-runtime/02-context.md +++ b/documentation/docs/06-runtime/02-context.md @@ -2,129 +2,137 @@ title: Context --- - +Context allows components to access values owned by parent components without passing them down as props (potentially through many layers of intermediate components, known as 'prop-drilling'). The parent component sets context with `setContext(key, value)`... -Most state is component-level state that lives as long as its component lives. There's also section-wide or app-wide state however, which also needs to be handled somehow. - -The easiest way to do that is to create global state and just import that. +```svelte + + ``` +...and the child retrieves it with `getContext`: + ```svelte - + + +

{message}, inside Child.svelte

``` -This has a few drawbacks though: +This is particularly useful when `Parent.svelte` is not directly aware of `Child.svelte`, but instead renders it as part of a `children` [snippet](snippet) ([demo](/playground/untitled#H4sIAAAAAAAAE42Q3W6DMAyFX8WyJgESK-oto6hTX2D3YxcM3IIUQpR40yqUd58CrCXsp7tL7HNsf2dAWXaEKR56yfTBGOOxFWQwfR6Qz8q1XAHjL-GjUhvzToJd7bU09FO9ctMkG0wxM5VuFeeFLLjtVK8ZnkpNkuGo-w6CTTJ9Z3PwsBAemlbUF934W8iy5DpaZtOUcU02-ZLcaS51jHEkTFm_kY1_wfOO8QnXrb8hBzDEc6pgZ4gFoyz4KgiD7nxfTe8ghqAhIfrJ46cTzVZBbkPlODVJsLCDO6V7ZcJoncyw1yRr0hd1GNn_ZbEM3I9i1bmVxOlWElUvDUNHxpQngt3C4CXzjS1rtvkw22wMrTRtTbC8Lkuabe7jvthPPe3DofYCAAA=)): + +```svelte + + + +``` -- it only safely works when your global state is only used client-side - for example, when you're building a single page application that does not render any of your components on the server. If your state ends up being managed and updated on the server, it could end up being shared between sessions and/or users, causing bugs -- it may give the false impression that certain state is global when in reality it should only be used in a certain part of your app +The key (`'my-context'`, in the example above) and the context itself can be any JavaScript value. -To solve these drawbacks, Svelte provides a few `context` primitives which alleviate these problems. +In addition to [`setContext`](svelte#setContext) and [`getContext`](svelte#getContext), Svelte exposes [`hasContext`](svelte#hasContext) and [`getAllContexts`](svelte#getAllContexts) functions. -## Setting and getting context +## Using context with state -To associate an arbitrary object with the current component, use `setContext`. +You can store reactive state in context ([demo](/playground/untitled#H4sIAAAAAAAAE41R0W6DMAz8FSuaBNUQdK8MkKZ-wh7HHihzu6hgosRMm1D-fUpSVNq12x4iEvvOx_kmQU2PIhfP3DCCJGgHYvxkkYid7NCI_GUS_KUcxhVEMjOelErNB3bsatvG4LW6n0ZsRC4K02qpuKqpZtmrQTNMYJA3QRAs7PTQQxS40eMCt3mX3duxnWb-lS5h7nTI0A4jMWoo4c44P_Hku-zrOazdy64chWo-ScfRkRgl8wgHKrLTH1OxHZkHgoHaTraHcopXUFYzPPVfuC_hwQaD1GrskdiNCdQwJljJqlvXfyqVsA5CGg0uRUQifHw56xFtciO75QrP07vo_JXf_tf8yK2ezDKY_ZWt_1y2qqYzv7bI1IW1V_sN19m-07wCAAA=))... ```svelte + + + + + + ``` -The context is then available to children of the component (including slotted content) with `getContext`. +...though note that if you _reassign_ `counter` instead of updating it, you will 'break the link' — in other words instead of this... ```svelte - + ``` -`setContext` and `getContext` solve the above problems: +...you must do this: -- the state is not global, it's scoped to the component. That way it's safe to render your components on the server and not leak state -- it's clear that the state is not global but rather scoped to a specific component tree and therefore can't be used in other parts of your app +```svelte + +``` -> [!NOTE] `setContext`/`getContext` must be called during component initialisation. +Svelte will warn you if you get it wrong. -Context is not inherently reactive. If you need reactive values in context then you can pass a `$state` object into context, whose properties _will_ be reactive. +## Type-safe context -```svelte - - +```js +/// file: context.js +// @filename: ambient.d.ts +interface User {} - -``` +// @filename: index.js +// ---cut--- +import { getContext, setContext } from 'svelte'; -```svelte - - +/** @param {User} user */ +export function setUserContext(user) { + setContext(key, user); +} -

Count is {value.count}

+export function getUserContext() { + return /** @type {User} */ (getContext(key)); +} ``` -To check whether a given `key` has been set in the context of a parent component, use `hasContext`. +## Replacing global state -```svelte - + // ... +}); ``` -You can also retrieve the whole context map that belongs to the closest parent component using `getAllContexts`. This is useful, for example, if you programmatically create a component and want to pass the existing context to it. +In many cases this is perfectly fine, but there is a risk: if you mutate the state during server-side rendering (which is discouraged, but entirely possible!)... ```svelte + ``` -## Encapsulating context interactions - -The above methods are very unopinionated about how to use them. When your app grows in scale, it's worthwhile to encapsulate setting and getting the context into functions and properly type them. - -```ts -// @errors: 2304 -import { getContext, setContext } from 'svelte'; - -let userKey = Symbol('user'); - -export function setUserContext(user: User) { - setContext(userKey, user); -} - -export function getUserContext(): User { - return getContext(userKey) as User; -} -``` +...then the data may be accessible by the _next_ user. Context solves this problem because it is not shared between requests. diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 0beb3cb9a96c..62d9c3302a3c 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -122,12 +122,6 @@ Property descriptors defined on `$state` objects must contain `value` and always Cannot set prototype of `$state` object ``` -### state_unsafe_local_read - -``` -Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state -``` - ### state_unsafe_mutation ``` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6461df1d25df..8cb7efd4ef14 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,21 @@ # svelte +## 5.24.0 + +### Minor Changes + +- feat: allow state created in deriveds/effects to be written/read locally without self-invalidation ([#15553](https://github.com/sveltejs/svelte/pull/15553)) + +### Patch Changes + +- fix: check if DOM prototypes are extensible ([#15569](https://github.com/sveltejs/svelte/pull/15569)) + +- Keep inlined trailing JSDoc comments of properties when running svelte-migrate ([#15567](https://github.com/sveltejs/svelte/pull/15567)) + +- fix: simplify set calls for proxyable values ([#15548](https://github.com/sveltejs/svelte/pull/15548)) + +- fix: don't depend on deriveds created inside the current reaction ([#15564](https://github.com/sveltejs/svelte/pull/15564)) + ## 5.23.2 ### Patch Changes diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index ab4d1519c18c..bc8ec3625605 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -80,10 +80,6 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long > Cannot set prototype of `$state` object -## state_unsafe_local_read - -> Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state - ## state_unsafe_mutation > Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` diff --git a/packages/svelte/package.json b/packages/svelte/package.json index d005eca0b9c8..0aa6b2984123 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.23.2", + "version": "5.24.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 1bb7a69a20f9..02bb5b144385 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1592,7 +1592,6 @@ function extract_type_and_comment(declarator, state, path) { const comment_start = /** @type {any} */ (comment_node)?.start; const comment_end = /** @type {any} */ (comment_node)?.end; let comment = comment_node && str.original.substring(comment_start, comment_end); - if (comment_node) { str.update(comment_start, comment_end, ''); } @@ -1673,6 +1672,11 @@ function extract_type_and_comment(declarator, state, path) { state.has_type_or_fallback = true; const match = /@type {(.+)}/.exec(comment_node.value); if (match) { + // try to find JSDoc comments after a hyphen `-` + const jsdocComment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value); + if (jsdocComment) { + cleaned_comment += jsdocComment[1]?.trim(); + } return { type: match[1], comment: cleaned_comment, @@ -1693,7 +1697,6 @@ function extract_type_and_comment(declarator, state, path) { }; } } - return { type: 'any', comment: state.uses_ts ? comment : cleaned_comment, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index ac8263b91669..0bdfbae746d0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -219,7 +219,10 @@ export function client_component(analysis, options) { for (const [name, binding] of analysis.instance.scope.declarations) { if (binding.kind === 'legacy_reactive') { legacy_reactive_declarations.push( - b.const(name, b.call('$.mutable_state', undefined, analysis.immutable ? b.true : undefined)) + b.const( + name, + b.call('$.mutable_source', undefined, analysis.immutable ? b.true : undefined) + ) ); } if (binding.kind === 'store_sub') { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts index 63fe3223cf7d..243e1c64a33c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts @@ -30,7 +30,7 @@ export interface ClientTransformState extends TransformState { /** turn `foo` into e.g. `$.get(foo)` */ read: (id: Identifier) => Expression; /** turn `foo = bar` into e.g. `$.set(foo, bar)` */ - assign?: (node: Identifier, value: Expression) => Expression; + assign?: (node: Identifier, value: Expression, proxy?: boolean) => Expression; /** turn `foo.bar = baz` into e.g. `$.mutate(foo, $.get(foo).bar = baz);` */ mutate?: (node: Identifier, mutation: AssignmentExpression | UpdateExpression) => Expression; /** turn `foo++` into e.g. `$.update(foo)` */ diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 421118cf680b..28e3fabb1990 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -45,14 +45,6 @@ export function build_getter(node, state) { return node; } -/** - * @param {Expression} value - * @param {Expression} previous - */ -export function build_proxy_reassignment(value, previous) { - return dev ? b.call('$.proxy', value, b.null, previous) : b.call('$.proxy', value); -} - /** * @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node * @param {ComponentContext} context diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js index a8c615af936f..150c56e166c1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js @@ -1,5 +1,4 @@ -/** @import { Location } from 'locate-character' */ -/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Literal, MemberExpression, Pattern } from 'estree' */ +/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Pattern } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types.js' */ import * as b from '../../../../utils/builders.js'; @@ -8,8 +7,8 @@ import { get_attribute_expression, is_event_attribute } from '../../../../utils/ast.js'; -import { dev, filename, is_ignored, locate_node, locator } from '../../../../state.js'; -import { build_proxy_reassignment, should_proxy } from '../utils.js'; +import { dev, is_ignored, locate_node } from '../../../../state.js'; +import { should_proxy } from '../utils.js'; import { visit_assignment_expression } from '../../shared/assignments.js'; /** @@ -65,21 +64,12 @@ function build_assignment(operator, left, right, context) { context.visit(build_assignment_value(operator, left, right)) ); - if ( + const needs_proxy = private_state.kind === 'state' && is_non_coercive_operator(operator) && - should_proxy(value, context.state.scope) - ) { - value = build_proxy_reassignment(value, b.member(b.this, private_state.id)); - } - - if (context.state.in_constructor) { - // inside the constructor, we can assign to `this.#foo.v` rather than using `$.set`, - // since nothing is tracking the signal at this point - return b.assignment(operator, /** @type {Pattern} */ (context.visit(left)), value); - } + should_proxy(value, context.state.scope); - return b.call('$.set', left, value); + return b.call('$.set', left, value, needs_proxy && b.true); } } @@ -113,20 +103,18 @@ function build_assignment(operator, left, right, context) { context.visit(build_assignment_value(operator, left, right)) ); - if ( + return transform.assign( + object, + value, !is_primitive && - binding.kind !== 'prop' && - binding.kind !== 'bindable_prop' && - binding.kind !== 'raw_state' && - binding.kind !== 'store_sub' && - context.state.analysis.runes && - should_proxy(right, context.state.scope) && - is_non_coercive_operator(operator) - ) { - value = build_proxy_reassignment(value, object); - } - - return transform.assign(object, value); + binding.kind !== 'prop' && + binding.kind !== 'bindable_prop' && + binding.kind !== 'raw_state' && + binding.kind !== 'store_sub' && + context.state.analysis.runes && + should_proxy(right, context.state.scope) && + is_non_coercive_operator(operator) + ); } // mutation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index ed800e5226ce..5787b590a8f9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -5,7 +5,7 @@ import { dev, is_ignored } from '../../../../state.js'; import * as b from '../../../../utils/builders.js'; import { regex_invalid_identifier_chars } from '../../../patterns.js'; import { get_rune } from '../../../scope.js'; -import { build_proxy_reassignment, should_proxy } from '../utils.js'; +import { should_proxy } from '../utils.js'; /** * @param {ClassBody} node @@ -142,29 +142,20 @@ export function ClassBody(node, context) { // get foo() { return this.#foo; } body.push(b.method('get', definition.key, [], [b.return(b.call('$.get', member))])); - if (field.kind === 'state') { + if (field.kind === 'state' || field.kind === 'raw_state') { // set foo(value) { this.#foo = value; } const value = b.id('value'); - const prev = b.member(b.this, field.id); body.push( b.method( 'set', definition.key, [value], - [b.stmt(b.call('$.set', member, build_proxy_reassignment(value, prev)))] + [b.stmt(b.call('$.set', member, value, field.kind === 'state' && b.true))] ) ); } - if (field.kind === 'raw_state') { - // set foo(value) { this.#foo = value; } - const value = b.id('value'); - body.push( - b.method('set', definition.key, [value], [b.stmt(b.call('$.set', member, value))]) - ); - } - if (dev && (field.kind === 'derived' || field.kind === 'derived_by')) { body.push( b.method( 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 baffc5dec374..3a914fb56099 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 @@ -299,7 +299,7 @@ function create_state_declarators(declarator, { scope, analysis }, value) { return [ b.declarator( declarator.id, - b.call('$.mutable_state', value, analysis.immutable ? b.true : undefined) + b.call('$.mutable_source', value, analysis.immutable ? b.true : undefined) ) ]; } @@ -314,7 +314,7 @@ function create_state_declarators(declarator, { scope, analysis }, value) { return b.declarator( path.node, binding?.kind === 'state' - ? b.call('$.mutable_state', value, analysis.immutable ? b.true : undefined) + ? b.call('$.mutable_source', value, analysis.immutable ? b.true : undefined) : value ); }) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js index 0bd8c352f6a9..a13ecfed2ce5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js @@ -24,8 +24,8 @@ export function add_state_transformers(context) { ) { context.state.transform[name] = { read: binding.declaration_kind === 'var' ? (node) => b.call('$.safe_get', node) : get_value, - assign: (node, value) => { - let call = b.call('$.set', node, value); + assign: (node, value, proxy = false) => { + let call = b.call('$.set', node, value, proxy && b.true); if (context.state.scope.get(`$${node.name}`)?.kind === 'store_sub') { call = b.call('$.store_unsub', call, b.literal(`$${node.name}`), b.id('$$stores')); diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index a4840ce4ebd0..21377c1cc85f 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -20,6 +20,7 @@ export const LEGACY_DERIVED_PROP = 1 << 17; export const INSPECT_EFFECT = 1 << 18; export const HEAD_EFFECT = 1 << 19; export const EFFECT_HAS_DERIVED = 1 << 20; +export const EFFECT_IS_UPDATING = 1 << 21; export const STATE_SYMBOL = Symbol('$state'); export const STATE_SYMBOL_METADATA = Symbol('$state metadata'); diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 0ad9045b2062..aae44d4b3989 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -2,7 +2,7 @@ import { hydrate_node, hydrating, set_hydrate_node } from './hydration.js'; import { DEV } from 'esm-env'; import { init_array_prototype_warnings } from '../dev/equality.js'; -import { get_descriptor } from '../../shared/utils.js'; +import { get_descriptor, is_extensible } from '../../shared/utils.js'; // export these for reference in the compiled code, making global name deduplication unnecessary /** @type {Window} */ @@ -34,26 +34,31 @@ export function init_operations() { var element_prototype = Element.prototype; var node_prototype = Node.prototype; + var text_prototype = Text.prototype; // @ts-ignore first_child_getter = get_descriptor(node_prototype, 'firstChild').get; // @ts-ignore next_sibling_getter = get_descriptor(node_prototype, 'nextSibling').get; - // the following assignments improve perf of lookups on DOM nodes - // @ts-expect-error - element_prototype.__click = undefined; - // @ts-expect-error - element_prototype.__className = undefined; - // @ts-expect-error - element_prototype.__attributes = null; - // @ts-expect-error - element_prototype.__style = undefined; - // @ts-expect-error - element_prototype.__e = undefined; - - // @ts-expect-error - Text.prototype.__t = undefined; + if (is_extensible(element_prototype)) { + // the following assignments improve perf of lookups on DOM nodes + // @ts-expect-error + element_prototype.__click = undefined; + // @ts-expect-error + element_prototype.__className = undefined; + // @ts-expect-error + element_prototype.__attributes = null; + // @ts-expect-error + element_prototype.__style = undefined; + // @ts-expect-error + element_prototype.__e = undefined; + } + + if (is_extensible(text_prototype)) { + // @ts-expect-error + text_prototype.__t = undefined; + } if (DEV) { // @ts-expect-error diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 682816e1d64b..8a5b5033a78c 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -307,21 +307,6 @@ export function state_prototype_fixed() { } } -/** - * Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state - * @returns {never} - */ -export function state_unsafe_local_read() { - if (DEV) { - const error = new Error(`state_unsafe_local_read\nReading state that was created inside the same derived is forbidden. Consider using \`untrack\` to read locally created state\nhttps://svelte.dev/e/state_unsafe_local_read`); - - error.name = 'Svelte error'; - throw error; - } else { - throw new Error(`https://svelte.dev/e/state_unsafe_local_read`); - } -} - /** * Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` * @returns {never} diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 31da00dbb448..a5f93e8b171b 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -101,7 +101,7 @@ export { text, props_id } from './dom/template.js'; -export { derived, derived_safe_equal } from './reactivity/deriveds.js'; +export { user_derived as derived, derived_safe_equal } from './reactivity/deriveds.js'; export { effect_tracking, effect_root, @@ -113,7 +113,7 @@ export { user_effect, user_pre_effect } from './reactivity/effects.js'; -export { mutable_state, mutate, set, state, update, update_pre } from './reactivity/sources.js'; +export { mutable_source, mutate, set, state, update, update_pre } from './reactivity/sources.js'; export { prop, rest_props, diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 4c262880f1cd..ffe63f4b77a8 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -1,6 +1,6 @@ /** @import { ProxyMetadata, Source } from '#client' */ import { DEV } from 'esm-env'; -import { get, active_effect } from './runtime.js'; +import { get, active_effect, active_reaction, set_active_reaction } from './runtime.js'; import { component_context } from './context.js'; import { array_prototype, @@ -10,26 +10,23 @@ import { object_prototype } from '../shared/utils.js'; import { check_ownership, widen_ownership } from './dev/ownership.js'; -import { source, set } from './reactivity/sources.js'; +import { state as source, set } from './reactivity/sources.js'; import { STATE_SYMBOL, STATE_SYMBOL_METADATA } from './constants.js'; import { UNINITIALIZED } from '../../constants.js'; import * as e from './errors.js'; import { get_stack } from './dev/tracing.js'; import { tracing_mode_flag } from '../flags/index.js'; +/** @type {ProxyMetadata | null} */ +var parent_metadata = null; + /** * @template T * @param {T} value - * @param {ProxyMetadata | null} [parent] * @param {Source} [prev] dev mode only * @returns {T} */ -export function proxy(value, parent = null, prev) { - /** @type {Error | null} */ - var stack = null; - if (DEV && tracing_mode_flag) { - stack = get_stack('CreatedAt'); - } +export function proxy(value, prev) { // if non-proxyable, or is already a proxy, return `value` if (typeof value !== 'object' || value === null || STATE_SYMBOL in value) { return value; @@ -46,6 +43,33 @@ export function proxy(value, parent = null, prev) { var is_proxied_array = is_array(value); var version = source(0); + var stack = DEV && tracing_mode_flag ? get_stack('CreatedAt') : null; + var reaction = active_reaction; + + /** + * @template T + * @param {() => T} fn + */ + var with_parent = (fn) => { + var previous_reaction = active_reaction; + set_active_reaction(reaction); + + /** @type {T} */ + var result; + + if (DEV) { + var previous_metadata = parent_metadata; + parent_metadata = metadata; + result = fn(); + parent_metadata = previous_metadata; + } else { + result = fn(); + } + + set_active_reaction(previous_reaction); + return result; + }; + if (is_proxied_array) { // We need to create the length source eagerly to ensure that // mutations to the array are properly synced with our proxy @@ -57,7 +81,7 @@ export function proxy(value, parent = null, prev) { if (DEV) { metadata = { - parent, + parent: parent_metadata, owners: null }; @@ -69,7 +93,7 @@ export function proxy(value, parent = null, prev) { metadata.owners = prev_owners ? new Set(prev_owners) : null; } else { metadata.owners = - parent === null + parent_metadata === null ? component_context !== null ? new Set([component_context.function]) : null @@ -95,10 +119,13 @@ export function proxy(value, parent = null, prev) { var s = sources.get(prop); if (s === undefined) { - s = source(descriptor.value, stack); + s = with_parent(() => source(descriptor.value, stack)); sources.set(prop, s); } else { - set(s, proxy(descriptor.value, metadata)); + set( + s, + with_parent(() => proxy(descriptor.value)) + ); } return true; @@ -109,7 +136,10 @@ export function proxy(value, parent = null, prev) { if (s === undefined) { if (prop in target) { - sources.set(prop, source(UNINITIALIZED, stack)); + sources.set( + prop, + with_parent(() => source(UNINITIALIZED, stack)) + ); } } else { // When working with arrays, we need to also ensure we update the length when removing @@ -143,7 +173,7 @@ export function proxy(value, parent = null, prev) { // create a source, but only if it's an own property and not a prototype property if (s === undefined && (!exists || get_descriptor(target, prop)?.writable)) { - s = source(proxy(exists ? target[prop] : UNINITIALIZED, metadata), stack); + s = with_parent(() => source(proxy(exists ? target[prop] : UNINITIALIZED), stack)); sources.set(prop, s); } @@ -211,7 +241,7 @@ export function proxy(value, parent = null, prev) { (active_effect !== null && (!has || get_descriptor(target, prop)?.writable)) ) { if (s === undefined) { - s = source(has ? proxy(target[prop], metadata) : UNINITIALIZED, stack); + s = with_parent(() => source(has ? proxy(target[prop]) : UNINITIALIZED, stack)); sources.set(prop, s); } @@ -238,7 +268,7 @@ export function proxy(value, parent = null, prev) { // If the item exists in the original, we need to create a uninitialized source, // else a later read of the property would result in a source being created with // the value of the original item at that index. - other_s = source(UNINITIALIZED, stack); + other_s = with_parent(() => source(UNINITIALIZED, stack)); sources.set(i + '', other_s); } } @@ -250,13 +280,19 @@ export function proxy(value, parent = null, prev) { // object property before writing to that property. if (s === undefined) { if (!has || get_descriptor(target, prop)?.writable) { - s = source(undefined, stack); - set(s, proxy(value, metadata)); + s = with_parent(() => source(undefined, stack)); + set( + s, + with_parent(() => proxy(value)) + ); sources.set(prop, s); } } else { has = s.v !== UNINITIALIZED; - set(s, proxy(value, metadata)); + set( + s, + with_parent(() => proxy(value)) + ); } if (DEV) { diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 795417cc0fdb..cd7bbba02f91 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -8,7 +8,8 @@ import { skip_reaction, update_reaction, increment_write_version, - set_active_effect + set_active_effect, + push_reaction_value } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; import * as e from '../errors.js'; @@ -61,6 +62,19 @@ export function derived(fn) { return signal; } +/** + * @template V + * @param {() => V} fn + * @returns {Derived} + */ +export function user_derived(fn) { + const d = derived(fn); + + push_reaction_value(d); + + return d; +} + /** * @template V * @param {() => V} fn diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 49584e862624..e4834902fe3f 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -11,11 +11,12 @@ import { untrack, increment_write_version, update_effect, - derived_sources, - set_derived_sources, + reaction_sources, + set_reaction_sources, check_dirtiness, untracking, - is_destroying_effect + is_destroying_effect, + push_reaction_value } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; import { @@ -27,12 +28,14 @@ import { UNOWNED, MAYBE_DIRTY, BLOCK_EFFECT, - ROOT_EFFECT + ROOT_EFFECT, + EFFECT_IS_UPDATING } from '../constants.js'; import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; import { component_context, is_runes } from '../context.js'; +import { proxy } from '../proxy.js'; export let inspect_effects = new Set(); export const old_values = new Map(); @@ -50,6 +53,7 @@ export function set_inspect_effects(v) { * @param {Error | null} [stack] * @returns {Source} */ +// TODO rename this to `state` throughout the codebase export function source(v, stack) { /** @type {Value} */ var signal = { @@ -72,9 +76,14 @@ export function source(v, stack) { /** * @template V * @param {V} v + * @param {Error | null} [stack] */ -export function state(v) { - return push_derived_source(source(v)); +export function state(v, stack) { + const s = source(v, stack); + + push_reaction_value(s); + + return s; } /** @@ -99,33 +108,6 @@ export function mutable_source(initial_value, immutable = false) { return s; } -/** - * @template V - * @param {V} v - * @param {boolean} [immutable] - * @returns {Source} - */ -export function mutable_state(v, immutable = false) { - return push_derived_source(mutable_source(v, immutable)); -} - -/** - * @template V - * @param {Source} source - */ -/*#__NO_SIDE_EFFECTS__*/ -function push_derived_source(source) { - if (active_reaction !== null && !untracking && (active_reaction.f & DERIVED) !== 0) { - if (derived_sources === null) { - set_derived_sources([source]); - } else { - derived_sources.push(source); - } - } - - return source; -} - /** * @template V * @param {Value} source @@ -143,22 +125,23 @@ export function mutate(source, value) { * @template V * @param {Source} source * @param {V} value + * @param {boolean} [should_proxy] * @returns {V} */ -export function set(source, value) { +export function set(source, value, should_proxy = false) { if ( active_reaction !== null && !untracking && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT)) !== 0 && - // If the source was created locally within the current derived, then - // we allow the mutation. - (derived_sources === null || !derived_sources.includes(source)) + !reaction_sources?.includes(source) ) { e.state_unsafe_mutation(); } - return internal_set(source, value); + let new_value = should_proxy ? proxy(value, source) : value; + + return internal_set(source, new_value); } /** diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 0a65c6e45a13..a5d26412a4e6 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -22,7 +22,8 @@ import { ROOT_EFFECT, LEGACY_DERIVED_PROP, DISCONNECTED, - BOUNDARY_EFFECT + BOUNDARY_EFFECT, + EFFECT_IS_UPDATING } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { internal_set, old_values } from './reactivity/sources.js'; @@ -87,17 +88,28 @@ export function set_active_effect(effect) { } /** - * When sources are created within a derived, we record them so that we can safely allow - * local mutations to these sources without the side-effect error being invoked unnecessarily. + * When sources are created within a reaction, reading and writing + * them should not cause a re-run * @type {null | Source[]} */ -export let derived_sources = null; +export let reaction_sources = null; /** * @param {Source[] | null} sources */ -export function set_derived_sources(sources) { - derived_sources = sources; +export function set_reaction_sources(sources) { + reaction_sources = sources; +} + +/** @param {Value} value */ +export function push_reaction_value(value) { + if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) { + if (reaction_sources === null) { + set_reaction_sources([value]); + } else { + reaction_sources.push(value); + } + } } /** @@ -367,6 +379,9 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true) for (var i = 0; i < reactions.length; i++) { var reaction = reactions[i]; + + if (reaction_sources?.includes(signal)) continue; + if ((reaction.f & DERIVED) !== 0) { schedule_possible_effect_self_invalidation(/** @type {Derived} */ (reaction), effect, false); } else if (effect === reaction) { @@ -391,9 +406,10 @@ export function update_reaction(reaction) { var previous_untracked_writes = untracked_writes; var previous_reaction = active_reaction; var previous_skip_reaction = skip_reaction; - var prev_derived_sources = derived_sources; + var previous_reaction_sources = reaction_sources; var previous_component_context = component_context; var previous_untracking = untracking; + var flags = reaction.f; new_deps = /** @type {null | Value[]} */ (null); @@ -403,11 +419,13 @@ export function update_reaction(reaction) { (flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null); active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; - derived_sources = null; + reaction_sources = null; set_component_context(reaction.ctx); untracking = false; read_version++; + reaction.f |= EFFECT_IS_UPDATING; + try { var result = /** @type {Function} */ (0, reaction.fn)(); var deps = reaction.deps; @@ -477,9 +495,11 @@ export function update_reaction(reaction) { untracked_writes = previous_untracked_writes; active_reaction = previous_reaction; skip_reaction = previous_skip_reaction; - derived_sources = prev_derived_sources; + reaction_sources = previous_reaction_sources; set_component_context(previous_component_context); untracking = previous_untracking; + + reaction.f ^= EFFECT_IS_UPDATING; } } @@ -866,24 +886,23 @@ export function get(signal) { // Register the dependency on the current reaction signal. if (active_reaction !== null && !untracking) { - if (derived_sources !== null && derived_sources.includes(signal)) { - e.state_unsafe_local_read(); - } - var deps = active_reaction.deps; - if (signal.rv < read_version) { - signal.rv = read_version; - // If the signal is accessing the same dependencies in the same - // order as it did last time, increment `skipped_deps` - // rather than updating `new_deps`, which creates GC cost - if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { - skipped_deps++; - } else if (new_deps === null) { - new_deps = [signal]; - } else if (!skip_reaction || !new_deps.includes(signal)) { - // Normally we can push duplicated dependencies to `new_deps`, but if we're inside - // an unowned derived because skip_reaction is true, then we need to ensure that - // we don't have duplicates - new_deps.push(signal); + if (!reaction_sources?.includes(signal)) { + var deps = active_reaction.deps; + if (signal.rv < read_version) { + signal.rv = read_version; + // If the signal is accessing the same dependencies in the same + // order as it did last time, increment `skipped_deps` + // rather than updating `new_deps`, which creates GC cost + if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { + skipped_deps++; + } else if (new_deps === null) { + new_deps = [signal]; + } else if (!skip_reaction || !new_deps.includes(signal)) { + // Normally we can push duplicated dependencies to `new_deps`, but if we're inside + // an unowned derived because skip_reaction is true, then we need to ensure that + // we don't have duplicates + new_deps.push(signal); + } } } } else if ( diff --git a/packages/svelte/src/internal/shared/utils.js b/packages/svelte/src/internal/shared/utils.js index f9d52cb065ac..5e7f3152d80b 100644 --- a/packages/svelte/src/internal/shared/utils.js +++ b/packages/svelte/src/internal/shared/utils.js @@ -10,6 +10,7 @@ export var get_descriptors = Object.getOwnPropertyDescriptors; export var object_prototype = Object.prototype; export var array_prototype = Array.prototype; export var get_prototype_of = Object.getPrototypeOf; +export var is_extensible = Object.isExtensible; /** * @param {any} thing diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 191b52ecef42..7cd43e74cb7d 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.23.2'; +export const VERSION = '5.24.0'; export const PUBLIC_VERSION = '5'; diff --git a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte index f2efb1db804c..f138c3a0707d 100644 --- a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte +++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte @@ -21,6 +21,9 @@ */ export let type_no_comment; + /** @type {boolean} type_with_comment - One-line declaration with comment */ + export let type_with_comment; + /** * This is optional */ @@ -40,4 +43,10 @@ export let inline_multiline_trailing_comment = 'world'; /* * this is a same-line trailing multiline comment **/ + + /** @type {number} [default_value=1] */ + export let default_value = 1; + + /** @type {number} [comment_default_value=1] - This has a comment and an optional value. */ + export let comment_default_value = 1; \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte index 19fbe38b5093..32133ccd4c85 100644 --- a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte +++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte @@ -9,12 +9,18 @@ + + + + + + /** * @typedef {Object} Props * @property {string} comment - My wonderful comment @@ -22,11 +28,14 @@ * @property {any} one_line - one line comment * @property {any} no_comment * @property {boolean} type_no_comment + * @property {boolean} type_with_comment - One-line declaration with comment * @property {any} [optional] - This is optional * @property {any} inline_commented - this should stay a comment * @property {any} inline_commented_merged - This comment should be merged - with this inline comment * @property {string} [inline_multiline_leading_comment] - this is a same-line leading multiline comment * @property {string} [inline_multiline_trailing_comment] - this is a same-line trailing multiline comment + * @property {number} [default_value] + * @property {number} [comment_default_value] - This has a comment and an optional value. */ /** @type {Props} */ @@ -36,10 +45,13 @@ one_line, no_comment, type_no_comment, + type_with_comment, optional = {stuff: true}, inline_commented, inline_commented_merged, inline_multiline_leading_comment = 'world', - inline_multiline_trailing_comment = 'world' + inline_multiline_trailing_comment = 'world', + default_value = 1, + comment_default_value = 1 } = $props(); \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js index 6a3d9eef7702..e55733c14810 100644 --- a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js @@ -10,6 +10,6 @@ export default test({ flushSync(() => { b1.click(); }); - assert.deepEqual(logs, ['init 0', 'cleanup 2', null, 'init 2', 'cleanup 4', null, 'init 4']); + assert.deepEqual(logs, ['init 0']); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js new file mode 100644 index 000000000000..18062b86fb43 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target, logs }) { + const button = target.querySelector('button'); + + flushSync(() => button?.click()); + + assert.htmlEqual( + target.innerHTML, + ` + +

1/2

+ class Foo { + value = $state(0); + double = $derived(this.value * 2); + + constructor() { + console.log(this.value, this.double); + } + + increment() { + this.value++; + } + } + + let foo = $state(); + + $effect(() => { + foo = new Foo(); + }); + + + + +{#if foo} +

{foo.value}/{foo.double}

+{/if} diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index ef4cf16d3b6c..3977caae36ad 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -487,6 +487,26 @@ describe('signals', () => { }; }); + test('schedules rerun when updating deeply nested value', (runes) => { + if (!runes) return () => {}; + + const value = proxy({ a: { b: { c: 0 } } }); + user_effect(() => { + value.a.b.c += 1; + }); + + return () => { + let errored = false; + try { + flushSync(); + } catch (e: any) { + assert.include(e.message, 'effect_update_depth_exceeded'); + errored = true; + } + assert.equal(errored, true); + }; + }); + test('schedules rerun when writing to signal before reading it', (runes) => { if (!runes) return () => {}; @@ -958,14 +978,30 @@ describe('signals', () => { }; }); - test('deriveds cannot depend on state they own', () => { + test('deriveds do not depend on state they own', () => { return () => { + let s; + const d = derived(() => { - const s = state(0); + s = state(0); return $.get(s); }); - assert.throws(() => $.get(d), 'state_unsafe_local_read'); + assert.equal($.get(d), 0); + + set(s!, 1); + assert.equal($.get(d), 0); + }; + }); + + test('effects do not depend on state they own', () => { + user_effect(() => { + const value = state(0); + set(value, $.get(value) + 1); + }); + + return () => { + flushSync(); }; }); diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js index fa990b33ee56..390e86a3510a 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js @@ -23,7 +23,7 @@ export default function Bind_component_snippet($$anchor) { return $.get(value); }, set value($$value) { - $.set(value, $.proxy($$value)); + $.set(value, $$value, true); } }); diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js index 2898f31a6fb5..21339741761f 100644 --- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js @@ -12,14 +12,14 @@ export default function Class_state_field_constructor_assignment($$anchor, $$pro } set a(value) { - $.set(this.#a, $.proxy(value)); + $.set(this.#a, value, true); } #b = $.state(); constructor() { this.a = 1; - this.#b.v = 2; + $.set(this.#b, 2); } } 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 9651713c52f5..47f297bce9c7 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 @@ -8,8 +8,8 @@ let d = 4; export function update(array) { ( - $.set(a, $.proxy(array[0])), - $.set(b, $.proxy(array[1])) + $.set(a, array[0], true), + $.set(b, array[1], true) ); [c, d] = array; diff --git a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js index c545608bcacf..762a23754c9b 100644 --- a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js @@ -13,7 +13,7 @@ export default function Function_prop_no_getter($$anchor) { Button($$anchor, { onmousedown: () => $.set(count, $.get(count) + 1), onmouseup, - onmouseenter: () => $.set(count, $.proxy(plusOne($.get(count)))), + onmouseenter: () => $.set(count, plusOne($.get(count)), true), children: ($$anchor, $$slotProps) => { $.next(); 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