From 6f0aec527177d4c3ec56b94d1e8b21d28266bbb7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 10 Jul 2025 12:36:10 -0400 Subject: [PATCH 01/13] chore: simplify source ownership (#16333) * simplify source ownership * rename * changeset * make it unnecessary to hand onto `current_sources` past the initial update --- .changeset/eight-walls-mate.md | 5 ++++ packages/svelte/src/internal/client/proxy.js | 29 +++++++++++++++---- .../src/internal/client/reactivity/sources.js | 4 +-- .../svelte/src/internal/client/runtime.js | 28 +++++++++--------- 4 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 .changeset/eight-walls-mate.md diff --git a/.changeset/eight-walls-mate.md b/.changeset/eight-walls-mate.md new file mode 100644 index 0000000000..a7de4e6278 --- /dev/null +++ b/.changeset/eight-walls-mate.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: simplify reaction/source ownership tracking diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 97c8da9d33..5da1b7e188 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -1,6 +1,13 @@ /** @import { Source } from '#client' */ import { DEV } from 'esm-env'; -import { get, active_effect, active_reaction, set_active_reaction } from './runtime.js'; +import { + get, + active_effect, + update_version, + active_reaction, + set_update_version, + set_active_reaction +} from './runtime.js'; import { array_prototype, get_descriptor, @@ -41,7 +48,7 @@ export function proxy(value) { var version = source(0); var stack = DEV && tracing_mode_flag ? get_stack('CreatedAt') : null; - var reaction = active_reaction; + var parent_version = update_version; /** * Executes the proxy in the context of the reaction it was originally created in, if any @@ -49,13 +56,23 @@ export function proxy(value) { * @param {() => T} fn */ var with_parent = (fn) => { - var previous_reaction = active_reaction; - set_active_reaction(reaction); + if (update_version === parent_version) { + return fn(); + } + + // child source is being created after the initial proxy — + // prevent it from being associated with the current reaction + var reaction = active_reaction; + var version = update_version; + + set_active_reaction(null); + set_update_version(parent_version); - /** @type {T} */ var result = fn(); - set_active_reaction(previous_reaction); + set_active_reaction(reaction); + set_update_version(version); + return result; }; diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 9f08354cc0..f84312e31c 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -11,7 +11,7 @@ import { untrack, increment_write_version, update_effect, - source_ownership, + current_sources, check_dirtiness, untracking, is_destroying_effect, @@ -140,7 +140,7 @@ export function set(source, value, should_proxy = false) { (!untracking || (active_reaction.f & INSPECT_EFFECT) !== 0) && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT | INSPECT_EFFECT)) !== 0 && - !(source_ownership?.reaction === active_reaction && source_ownership.sources.includes(source)) + !current_sources?.includes(source) ) { e.state_unsafe_mutation(); } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d6e7325ba3..6477e2942a 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -88,17 +88,17 @@ export function set_active_effect(effect) { /** * When sources are created within a reaction, reading and writing * them within that reaction should not cause a re-run - * @type {null | { reaction: Reaction, sources: Source[] }} + * @type {null | Source[]} */ -export let source_ownership = null; +export let current_sources = null; /** @param {Value} value */ export function push_reaction_value(value) { if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) { - if (source_ownership === null) { - source_ownership = { reaction: active_reaction, sources: [value] }; + if (current_sources === null) { + current_sources = [value]; } else { - source_ownership.sources.push(value); + current_sources.push(value); } } } @@ -136,6 +136,11 @@ let read_version = 0; export let update_version = read_version; +/** @param {number} value */ +export function set_update_version(value) { + update_version = value; +} + // If we are working with a get() chain that has no active container, // to prevent memory leaks, we skip adding the reaction. export let skip_reaction = false; @@ -236,7 +241,7 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true) var reactions = signal.reactions; if (reactions === null) return; - if (source_ownership?.reaction === active_reaction && source_ownership.sources.includes(signal)) { + if (current_sources?.includes(signal)) { return; } @@ -263,7 +268,7 @@ export function update_reaction(reaction) { var previous_untracked_writes = untracked_writes; var previous_reaction = active_reaction; var previous_skip_reaction = skip_reaction; - var previous_reaction_sources = source_ownership; + var previous_sources = current_sources; var previous_component_context = component_context; var previous_untracking = untracking; var previous_update_version = update_version; @@ -277,7 +282,7 @@ 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; - source_ownership = null; + current_sources = null; set_component_context(reaction.ctx); untracking = false; update_version = ++read_version; @@ -365,7 +370,7 @@ export function update_reaction(reaction) { untracked_writes = previous_untracked_writes; active_reaction = previous_reaction; skip_reaction = previous_skip_reaction; - source_ownership = previous_reaction_sources; + current_sources = previous_sources; set_component_context(previous_component_context); untracking = previous_untracking; update_version = previous_update_version; @@ -759,10 +764,7 @@ export function get(signal) { // Register the dependency on the current reaction signal. if (active_reaction !== null && !untracking) { - if ( - source_ownership?.reaction !== active_reaction || - !source_ownership?.sources.includes(signal) - ) { + if (!current_sources?.includes(signal)) { var deps = active_reaction.deps; if (signal.rv < read_version) { signal.rv = read_version; From ca1eb55e970243dbed1c032e038860218325d63a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 20:41:02 -0400 Subject: [PATCH 02/13] Version Packages (#16334) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/eight-walls-mate.md | 5 ----- .changeset/new-candles-marry.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/eight-walls-mate.md delete mode 100644 .changeset/new-candles-marry.md diff --git a/.changeset/eight-walls-mate.md b/.changeset/eight-walls-mate.md deleted file mode 100644 index a7de4e6278..0000000000 --- a/.changeset/eight-walls-mate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: simplify reaction/source ownership tracking diff --git a/.changeset/new-candles-marry.md b/.changeset/new-candles-marry.md deleted file mode 100644 index 4d55980c72..0000000000 --- a/.changeset/new-candles-marry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: simplify internal component `pop()` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 19aa1466c0..8f158c528f 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.35.6 + +### Patch Changes + +- chore: simplify reaction/source ownership tracking ([#16333](https://github.com/sveltejs/svelte/pull/16333)) + +- chore: simplify internal component `pop()` ([#16331](https://github.com/sveltejs/svelte/pull/16331)) + ## 5.35.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 4ac497ed41..4eed145f3f 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.35.5", + "version": "5.35.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index eb68753d71..266b0b9491 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.35.5'; +export const VERSION = '5.35.6'; export const PUBLIC_VERSION = '5'; From e802d3b2cc0b6b5f96f853833fc6852705554a6c Mon Sep 17 00:00:00 2001 From: "Ahmad S." Date: Fri, 11 Jul 2025 20:31:22 +0300 Subject: [PATCH 03/13] chore: replace inline regex with variable (#16340) * chore: replace inline regex with variable * Update packages/svelte/src/compiler/phases/patterns.js Co-authored-by: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> * Update a11y.js * Update a11y.js --------- Co-authored-by: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> --- .changeset/tiny-news-whisper.md | 5 +++++ .../src/compiler/phases/2-analyze/visitors/shared/a11y.js | 6 ++++-- packages/svelte/src/compiler/phases/patterns.js | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .changeset/tiny-news-whisper.md diff --git a/.changeset/tiny-news-whisper.md b/.changeset/tiny-news-whisper.md new file mode 100644 index 0000000000..8bf877085d --- /dev/null +++ b/.changeset/tiny-news-whisper.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: replace inline regex with variable diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js index 1f58a28cad..e103e3eb80 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js @@ -6,7 +6,9 @@ import { roles as roles_map, aria, elementRoles } from 'aria-query'; import { AXObjects, AXObjectRoles, elementAXObjects } from 'axobject-query'; import { regex_heading_tags, + regex_js_prefix, regex_not_whitespace, + regex_redundant_img_alt, regex_starts_with_vowel, regex_whitespaces } from '../../../patterns.js'; @@ -1011,7 +1013,7 @@ export function check_element(node, context) { if (href) { const href_value = get_static_text_value(href); if (href_value !== null) { - if (href_value === '' || href_value === '#' || /^\W*javascript:/i.test(href_value)) { + if (href_value === '' || href_value === '#' || regex_js_prefix.test(href_value)) { w.a11y_invalid_attribute(href, href_value, href.name); } } @@ -1061,7 +1063,7 @@ export function check_element(node, context) { const alt_attribute = get_static_text_value(attribute_map.get('alt')); const aria_hidden = get_static_value(attribute_map.get('aria-hidden')); if (alt_attribute && !aria_hidden && !has_spread) { - if (/\b(image|picture|photo)\b/i.test(alt_attribute)) { + if (regex_redundant_img_alt.test(alt_attribute)) { w.a11y_img_redundant_alt(node); } } diff --git a/packages/svelte/src/compiler/phases/patterns.js b/packages/svelte/src/compiler/phases/patterns.js index 2bee717131..448be7f949 100644 --- a/packages/svelte/src/compiler/phases/patterns.js +++ b/packages/svelte/src/compiler/phases/patterns.js @@ -23,3 +23,5 @@ export const regex_heading_tags = /^h[1-6]$/; export const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/; export const regex_bidirectional_control_characters = /[\u202a\u202b\u202c\u202d\u202e\u2066\u2067\u2068\u2069]+/g; +export const regex_js_prefix = /^\W*javascript:/i; +export const regex_redundant_img_alt = /\b(image|picture|photo)\b/i; From 96c1a10042475c4ef8b55dfdca8538a25e792626 Mon Sep 17 00:00:00 2001 From: "Ahmad S." Date: Fri, 11 Jul 2025 20:41:59 +0300 Subject: [PATCH 04/13] fix: silence autofocus a11y warning inside `` (#16341) --- .changeset/cuddly-humans-end.md | 5 +++++ .../src/compiler/phases/2-analyze/visitors/shared/a11y.js | 2 +- .../tests/validator/samples/a11y-no-autofocus/input.svelte | 7 ++++++- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 .changeset/cuddly-humans-end.md diff --git a/.changeset/cuddly-humans-end.md b/.changeset/cuddly-humans-end.md new file mode 100644 index 0000000000..d02e08de7a --- /dev/null +++ b/.changeset/cuddly-humans-end.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: silence autofocus a11y warning inside `` diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js index e103e3eb80..152e679bf5 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js @@ -877,7 +877,7 @@ export function check_element(node, context) { } // no-autofocus - if (name === 'autofocus') { + if (name === 'autofocus' && node.name !== 'dialog' && !is_parent(context.path, ['dialog'])) { w.a11y_autofocus(attribute); } diff --git a/packages/svelte/tests/validator/samples/a11y-no-autofocus/input.svelte b/packages/svelte/tests/validator/samples/a11y-no-autofocus/input.svelte index 769dbe8c5b..7b1dccd1e8 100644 --- a/packages/svelte/tests/validator/samples/a11y-no-autofocus/input.svelte +++ b/packages/svelte/tests/validator/samples/a11y-no-autofocus/input.svelte @@ -1 +1,6 @@ -
\ No newline at end of file +
+ + + + + From 58baf80a70433a27a58bf5a642b4ccfd0c23349b Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Sun, 13 Jul 2025 02:40:26 -0700 Subject: [PATCH 05/13] docs: add note about proxying state proxies (#16354) --- documentation/docs/02-runes/02-$state.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md index 8e6c91fad7..7c4571e575 100644 --- a/documentation/docs/02-runes/02-$state.md +++ b/documentation/docs/02-runes/02-$state.md @@ -50,7 +50,7 @@ todos.push({ }); ``` -> [!NOTE] When you update properties of proxies, the original object is _not_ mutated. +> [!NOTE] When you update properties of proxies, the original object is _not_ mutated. If you desire to use your own proxy handlers in a state proxy, [you should wrap the object _after_ wrapping it in `$state`](https://svelte.dev/playground/hello-world?version=latest#H4sIAAAAAAAACpWR3WoDIRCFX2UqhWyIJL3erAulL9C7XnQLMe5ksbUqOpsfln33YuyGFNJC8UKdc2bOhw7Myk9kJXsJ0nttO9jcR5KEG9AWJDwHdzwxznbaYGTl68Do5JM_FRifuh-9X8Y9Gkq1rYx4q66cJbQUWcmqqIL2VDe2IYMEbvuOikBADi-GJDSkXG-phId0G-frye2DO2psQYDFQ0Ys8gQO350dUkEydEg82T0GOs0nsSG9g2IqgxACZueo2ZUlpdvoDC6N64qsg1QKY8T2bpZp8gpIfbCQ85Zn50Ud82HkeY83uDjspenxv3jXcSDyjPWf9L1vJf0GH666J-jLu1ery4dV257IWXBWGa0-xFDMQdTTn2ScxWKsn86ROsLwQxqrVR5QM84Ij8TKFD2-cUZSm4O2LSt30kQcvwCgCmfZnAIAAA==). Note that if you destructure a reactive value, the references are not reactive — as in normal JavaScript, they are evaluated at the point of destructuring: From 61f75651d6a4f1106a55c2cd357751d473729e55 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 13 Jul 2025 12:48:08 -0400 Subject: [PATCH 06/13] chore: re-export shared errors (#16356) --- .../process-messages/templates/client-errors.js | 2 ++ .../process-messages/templates/server-errors.js | 2 ++ packages/svelte/src/index-client.js | 11 +++++------ packages/svelte/src/internal/client/context.js | 4 ++-- packages/svelte/src/internal/client/dev/validation.js | 7 ++++--- packages/svelte/src/internal/client/errors.js | 2 ++ packages/svelte/src/internal/server/context.js | 2 +- packages/svelte/src/internal/server/dev.js | 4 ++-- packages/svelte/src/internal/server/errors.js | 2 +- packages/svelte/src/legacy/legacy-client.js | 4 ++-- 10 files changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/svelte/scripts/process-messages/templates/client-errors.js b/packages/svelte/scripts/process-messages/templates/client-errors.js index c72e9f9d5e..ef749b4ba3 100644 --- a/packages/svelte/scripts/process-messages/templates/client-errors.js +++ b/packages/svelte/scripts/process-messages/templates/client-errors.js @@ -1,5 +1,7 @@ import { DEV } from 'esm-env'; +export * from '../shared/errors.js'; + /** * MESSAGE * @param {string} PARAMETER diff --git a/packages/svelte/scripts/process-messages/templates/server-errors.js b/packages/svelte/scripts/process-messages/templates/server-errors.js index 6fb7924564..0bbe801abc 100644 --- a/packages/svelte/scripts/process-messages/templates/server-errors.js +++ b/packages/svelte/scripts/process-messages/templates/server-errors.js @@ -1,3 +1,5 @@ +export * from '../shared/errors.js'; + /** * MESSAGE * @param {string} PARAMETER diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index 0d962aacd1..ae1caf16d9 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -5,7 +5,6 @@ import { active_reaction, untrack } from './internal/client/runtime.js'; import { is_array } from './internal/shared/utils.js'; import { user_effect } from './internal/client/index.js'; import * as e from './internal/client/errors.js'; -import { lifecycle_outside_component } from './internal/shared/errors.js'; import { legacy_mode_flag } from './internal/flags/index.js'; import { component_context } from './internal/client/context.js'; import { DEV } from 'esm-env'; @@ -91,7 +90,7 @@ export function getAbortSignal() { */ export function onMount(fn) { if (component_context === null) { - lifecycle_outside_component('onMount'); + e.lifecycle_outside_component('onMount'); } if (legacy_mode_flag && component_context.l !== null) { @@ -115,7 +114,7 @@ export function onMount(fn) { */ export function onDestroy(fn) { if (component_context === null) { - lifecycle_outside_component('onDestroy'); + e.lifecycle_outside_component('onDestroy'); } onMount(() => () => untrack(fn)); @@ -158,7 +157,7 @@ function create_custom_event(type, detail, { bubbles = false, cancelable = false export function createEventDispatcher() { const active_component_context = component_context; if (active_component_context === null) { - lifecycle_outside_component('createEventDispatcher'); + e.lifecycle_outside_component('createEventDispatcher'); } return (type, detail, options) => { @@ -196,7 +195,7 @@ export function createEventDispatcher() { */ export function beforeUpdate(fn) { if (component_context === null) { - lifecycle_outside_component('beforeUpdate'); + e.lifecycle_outside_component('beforeUpdate'); } if (component_context.l === null) { @@ -219,7 +218,7 @@ export function beforeUpdate(fn) { */ export function afterUpdate(fn) { if (component_context === null) { - lifecycle_outside_component('afterUpdate'); + e.lifecycle_outside_component('afterUpdate'); } if (component_context.l === null) { diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index 6876a89f57..eae326f9bb 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -1,7 +1,7 @@ /** @import { ComponentContext, DevStackEntry } from '#client' */ import { DEV } from 'esm-env'; -import { lifecycle_outside_component } from '../shared/errors.js'; +import * as e from './errors.js'; import { source } from './reactivity/sources.js'; import { active_effect, @@ -205,7 +205,7 @@ export function is_runes() { */ function get_or_init_context_map(name) { if (component_context === null) { - lifecycle_outside_component(name); + e.lifecycle_outside_component(name); } return (component_context.c ??= new Map(get_parent_context(component_context) || undefined)); diff --git a/packages/svelte/src/internal/client/dev/validation.js b/packages/svelte/src/internal/client/dev/validation.js index e41e4c4628..60d140c718 100644 --- a/packages/svelte/src/internal/client/dev/validation.js +++ b/packages/svelte/src/internal/client/dev/validation.js @@ -1,15 +1,16 @@ -import { invalid_snippet_arguments } from '../../shared/errors.js'; +import * as e from '../errors.js'; /** * @param {Node} anchor * @param {...(()=>any)[]} args */ export function validate_snippet_args(anchor, ...args) { if (typeof anchor !== 'object' || !(anchor instanceof Node)) { - invalid_snippet_arguments(); + e.invalid_snippet_arguments(); } + for (let arg of args) { if (typeof arg !== 'function') { - invalid_snippet_arguments(); + e.invalid_snippet_arguments(); } } } diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 5c3f5340e1..64dc34e8d0 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -2,6 +2,8 @@ import { DEV } from 'esm-env'; +export * from '../shared/errors.js'; + /** * Using `bind:value` together with a checkbox input is not allowed. Use `bind:checked` instead * @returns {never} diff --git a/packages/svelte/src/internal/server/context.js b/packages/svelte/src/internal/server/context.js index 4e547f48cb..bae93beb53 100644 --- a/packages/svelte/src/internal/server/context.js +++ b/packages/svelte/src/internal/server/context.js @@ -1,7 +1,7 @@ /** @import { Component } from '#server' */ import { DEV } from 'esm-env'; import { on_destroy } from './index.js'; -import * as e from '../shared/errors.js'; +import * as e from './errors.js'; /** @type {Component | null} */ export var current_component = null; diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index efc761d7c5..3c320f9698 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -5,7 +5,7 @@ import { is_tag_valid_with_parent } from '../../html-tree-validation.js'; import { current_component } from './context.js'; -import { invalid_snippet_arguments } from '../shared/errors.js'; +import * as e from './errors.js'; import { HeadPayload, Payload } from './payload.js'; /** @@ -102,6 +102,6 @@ export function validate_snippet_args(payload) { // 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(); + e.invalid_snippet_arguments(); } } diff --git a/packages/svelte/src/internal/server/errors.js b/packages/svelte/src/internal/server/errors.js index e47530c9aa..458937218f 100644 --- a/packages/svelte/src/internal/server/errors.js +++ b/packages/svelte/src/internal/server/errors.js @@ -1,6 +1,6 @@ /* This file is generated by scripts/process-messages/index.js. Do not edit! */ - +export * from '../shared/errors.js'; /** * `%name%(...)` is not available on the server diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index 45c478ecab..61acbeaa28 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -4,8 +4,8 @@ import { user_pre_effect } from '../internal/client/reactivity/effects.js'; import { mutable_source, set } from '../internal/client/reactivity/sources.js'; import { hydrate, mount, unmount } from '../internal/client/render.js'; import { active_effect, flushSync, get, set_signal_status } from '../internal/client/runtime.js'; -import { lifecycle_outside_component } from '../internal/shared/errors.js'; import { define_property, is_array } from '../internal/shared/utils.js'; +import * as e from '../internal/client/errors.js'; import * as w from '../internal/client/warnings.js'; import { DEV } from 'esm-env'; import { FILENAME } from '../constants.js'; @@ -245,7 +245,7 @@ export function handlers(...handlers) { export function createBubbler() { const active_component_context = component_context; if (active_component_context === null) { - lifecycle_outside_component('createBubbler'); + e.lifecycle_outside_component('createBubbler'); } return (/**@type {string}*/ type) => (/**@type {Event}*/ event) => { From 4ef53a75a9f7428efe169ab2e037e87508c3ac87 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 13 Jul 2025 15:15:02 -0400 Subject: [PATCH 07/13] chore: tidy up some stuff (#16357) * chore: tidy up some stuff * shut up dumbass --- packages/svelte/knip.json | 4 ---- packages/svelte/src/compiler/phases/scope.js | 6 +++--- packages/svelte/src/internal/client/context.js | 9 +-------- packages/svelte/src/internal/server/abort-signal.js | 2 +- packages/svelte/src/internal/shared/validate.js | 2 -- packages/svelte/src/utils.js | 2 +- 6 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/svelte/knip.json b/packages/svelte/knip.json index 7a27a64a91..0d1bf17e9f 100644 --- a/packages/svelte/knip.json +++ b/packages/svelte/knip.json @@ -1,10 +1,6 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", "entry": [ - "src/*/index.js", - "src/index-client.ts", - "src/index-server.ts", - "src/index.d.ts", "tests/**/*.js", "tests/**/*.ts", "!tests/**/*.svelte", diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 8a2cc39ba7..662924b52c 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -18,9 +18,9 @@ import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js'; const UNKNOWN = Symbol('unknown'); /** Includes `BigInt` */ -export const NUMBER = Symbol('number'); -export const STRING = Symbol('string'); -export const FUNCTION = Symbol('string'); +const NUMBER = Symbol('number'); +const STRING = Symbol('string'); +const FUNCTION = Symbol('string'); /** @type {Record} */ const globals = { diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index eae326f9bb..a9ceafcd11 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -1,15 +1,8 @@ /** @import { ComponentContext, DevStackEntry } from '#client' */ - import { DEV } from 'esm-env'; import * as e from './errors.js'; import { source } from './reactivity/sources.js'; -import { - active_effect, - active_reaction, - set_active_effect, - set_active_reaction -} from './runtime.js'; -import { create_user_effect, teardown } from './reactivity/effects.js'; +import { create_user_effect } from './reactivity/effects.js'; import { legacy_mode_flag } from '../flags/index.js'; import { FILENAME } from '../../constants.js'; diff --git a/packages/svelte/src/internal/server/abort-signal.js b/packages/svelte/src/internal/server/abort-signal.js index da579b2592..a769a46e3d 100644 --- a/packages/svelte/src/internal/server/abort-signal.js +++ b/packages/svelte/src/internal/server/abort-signal.js @@ -1,7 +1,7 @@ import { STALE_REACTION } from '#client/constants'; /** @type {AbortController | null} */ -export let controller = null; +let controller = null; export function abort() { controller?.abort(STALE_REACTION); diff --git a/packages/svelte/src/internal/shared/validate.js b/packages/svelte/src/internal/shared/validate.js index bbb237594b..8f3e2807e7 100644 --- a/packages/svelte/src/internal/shared/validate.js +++ b/packages/svelte/src/internal/shared/validate.js @@ -1,5 +1,3 @@ -/** @import { TemplateNode } from '#client' */ -/** @import { Getters } from '#shared' */ import { is_void } from '../../utils.js'; import * as w from './warnings.js'; import * as e from './errors.js'; diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index 921eaec57c..2fc21220f0 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -428,7 +428,7 @@ export function is_mathml(name) { return MATHML_ELEMENTS.includes(name); } -export const STATE_CREATION_RUNES = /** @type {const} */ ([ +const STATE_CREATION_RUNES = /** @type {const} */ ([ '$state', '$state.raw', '$derived', From a23599a196380ce7fc3fb374dac4daee173776b7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 13 Jul 2025 18:20:29 -0400 Subject: [PATCH 08/13] fix: don't show adjusted error messages in boundaries (#16360) --- .changeset/large-balloons-agree.md | 5 ++ .../src/internal/client/error-handling.js | 47 ++++++++++++------- .../samples/error-boundary-3/_config.js | 2 +- .../samples/error-boundary-3/main.svelte | 6 +-- 4 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 .changeset/large-balloons-agree.md diff --git a/.changeset/large-balloons-agree.md b/.changeset/large-balloons-agree.md new file mode 100644 index 0000000000..9355336862 --- /dev/null +++ b/.changeset/large-balloons-agree.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't show adjusted error messages in boundaries diff --git a/packages/svelte/src/internal/client/error-handling.js b/packages/svelte/src/internal/client/error-handling.js index 378f7408ef..b12f21adfc 100644 --- a/packages/svelte/src/internal/client/error-handling.js +++ b/packages/svelte/src/internal/client/error-handling.js @@ -7,14 +7,16 @@ import { BOUNDARY_EFFECT, EFFECT_RAN } from './constants.js'; import { define_property, get_descriptor } from '../shared/utils.js'; import { active_effect } from './runtime.js'; +const adjustments = new WeakMap(); + /** * @param {unknown} error */ export function handle_error(error) { var effect = /** @type {Effect} */ (active_effect); - if (DEV && error instanceof Error) { - adjust_error(error, effect); + if (DEV && error instanceof Error && !adjustments.has(error)) { + adjustments.set(error, get_adjustments(error, effect)); } if ((effect.f & EFFECT_RAN) === 0) { @@ -48,21 +50,19 @@ export function invoke_error_boundary(error, effect) { effect = effect.parent; } + if (error instanceof Error) { + apply_adjustments(error); + } + throw error; } -/** @type {WeakSet} */ -const adjusted_errors = new WeakSet(); - /** * Add useful information to the error message/stack in development * @param {Error} error * @param {Effect} effect */ -function adjust_error(error, effect) { - if (adjusted_errors.has(error)) return; - adjusted_errors.add(error); - +function get_adjustments(error, effect) { const message_descriptor = get_descriptor(error, 'message'); // if the message was already changed and it's not configurable we can't change it @@ -78,17 +78,28 @@ function adjust_error(error, effect) { context = context.p; } - define_property(error, 'message', { - value: error.message + `\n${component_stack}\n` - }); + return { + message: error.message + `\n${component_stack}\n`, + stack: error.stack + ?.split('\n') + .filter((line) => !line.includes('svelte/src/internal')) + .join('\n') + }; +} + +/** + * @param {Error} error + */ +function apply_adjustments(error) { + const adjusted = adjustments.get(error); + + if (adjusted) { + define_property(error, 'message', { + value: adjusted.message + }); - if (error.stack) { - // Filter out internal modules define_property(error, 'stack', { - value: error.stack - .split('\n') - .filter((line) => !line.includes('svelte/src/internal')) - .join('\n') + value: adjusted.stack }); } } diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-3/_config.js b/packages/svelte/tests/runtime-runes/samples/error-boundary-3/_config.js index 040e13676e..06da8f667c 100644 --- a/packages/svelte/tests/runtime-runes/samples/error-boundary-3/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-3/_config.js @@ -9,6 +9,6 @@ export default test({ flushSync(); assert.deepEqual(logs, ['error caught']); - assert.htmlEqual(target.innerHTML, `
Fallback!
`); + assert.htmlEqual(target.innerHTML, `
oh no!
`); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-3/main.svelte index bad84666c0..bc7fe072c4 100644 --- a/packages/svelte/tests/runtime-runes/samples/error-boundary-3/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-3/main.svelte @@ -1,6 +1,6 @@