From 808fbb4989d0e1687577cb4534e4d8350fb8971d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 11 Sep 2025 11:49:14 -0400 Subject: [PATCH 1/5] chore: inline `suspend` (#16755) * chore: inline `suspend` * invert condition --- packages/svelte/src/internal/client/index.js | 2 +- .../src/internal/client/reactivity/async.js | 21 +++++++++++++++--- .../src/internal/client/reactivity/batch.js | 22 ------------------- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index dbff5c459971..3c5409bcfec8 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -103,7 +103,7 @@ export { save, track_reactivity_loss } from './reactivity/async.js'; -export { flushSync as flush, suspend } from './reactivity/batch.js'; +export { flushSync as flush } from './reactivity/batch.js'; export { async_derived, user_derived as derived, diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index b7a5d5cdb7c0..a109a1f4d8fd 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -11,7 +11,7 @@ import { set_active_effect, set_active_reaction } from '../runtime.js'; -import { current_batch, suspend } from './batch.js'; +import { Batch, current_batch } from './batch.js'; import { async_derived, current_async_effect, @@ -178,7 +178,13 @@ export function unset_context() { * @param {() => Promise} fn */ export async function async_body(fn) { - var unsuspend = suspend(); + var boundary = get_boundary(); + var batch = /** @type {Batch} */ (current_batch); + var pending = boundary.is_pending(); + + boundary.update_pending_count(1); + if (!pending) batch.increment(); + var active = /** @type {Effect} */ (active_effect); try { @@ -188,6 +194,15 @@ export async function async_body(fn) { invoke_error_boundary(error, active); } } finally { - unsuspend(); + boundary.update_pending_count(-1); + + if (pending) { + batch.flush(); + } else { + batch.activate(); + batch.decrement(); + } + + unset_context(); } } diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index e504ae2e3fa5..3d234f5bba4c 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -653,28 +653,6 @@ export function schedule_effect(signal) { queued_root_effects.push(effect); } -export function suspend() { - var boundary = get_boundary(); - var batch = /** @type {Batch} */ (current_batch); - var pending = boundary.is_pending(); - - boundary.update_pending_count(1); - if (!pending) batch.increment(); - - return function unsuspend() { - boundary.update_pending_count(-1); - - if (!pending) { - batch.activate(); - batch.decrement(); - } else { - batch.flush(); - } - - unset_context(); - }; -} - /** * Forcibly remove all current batches, to prevent cross-talk between tests */ From a0598014d2b634566d8a62acca6b0c7601054e18 Mon Sep 17 00:00:00 2001 From: Aaron Ajose Date: Sun, 14 Sep 2025 22:09:40 +0300 Subject: [PATCH 2/5] docs: Fix some inaccuracies (#16759) * docs: Fix some inaccuracies * Apply suggestions from code review --------- Co-authored-by: Rich Harris --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0653b08b7640..e940252892b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ The [Open Source Guides](https://opensource.guide/) website has a collection of ## Get involved -There are many ways to contribute to Svelte, and many of them do not involve writing any code. Here's a few ideas to get started: +There are many ways to contribute to Svelte, and many of them do not involve writing any code. Here are a few ideas to get started: - Simply start using Svelte. Go through the [Getting Started](https://svelte.dev/docs#getting-started) guide. Does everything work as expected? If not, we're always looking for improvements. Let us know by [opening an issue](#reporting-new-issues). - Look through the [open issues](https://github.com/sveltejs/svelte/issues). A good starting point would be issues tagged [good first issue](https://github.com/sveltejs/svelte/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). Provide workarounds, ask for clarification, or suggest labels. Help [triage issues](#triaging-issues-and-pull-requests). @@ -90,9 +90,9 @@ A good test plan has the exact commands you ran and their output, provides scree #### Writing tests -All tests are located in `/test` folder. +All tests are located in the `/tests` folder. -Test samples are kept in `/test/xxx/samples` folder. +Test samples are kept in `/tests/xxx/samples` folders. #### Running tests From 8b4e1fcb7aa321c15382652b60d430bcd0bda960 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 15 Sep 2025 10:00:45 -0400 Subject: [PATCH 3/5] chore: extract a couple of drive-by fixes from other branch (#16772) * chore: extract a couple of drive-by fixes from other branch * more --- packages/svelte/src/internal/client/dom/operations.js | 6 +++--- packages/svelte/src/internal/client/dom/template.js | 3 +++ packages/svelte/src/internal/client/reactivity/batch.js | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index abc29a7670cb..c527ca23e372 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -130,11 +130,11 @@ export function child(node, is_text) { /** * Don't mark this as side-effect-free, hydration needs to walk all nodes - * @param {DocumentFragment | TemplateNode[]} fragment - * @param {boolean} is_text + * @param {DocumentFragment | TemplateNode | TemplateNode[]} fragment + * @param {boolean} [is_text] * @returns {Node | null} */ -export function first_child(fragment, is_text) { +export function first_child(fragment, is_text = false) { if (!hydrating) { // when not hydrating, `fragment` is a `DocumentFragment` (the result of calling `open_frag`) var first = /** @type {DocumentFragment} */ (get_first_child(/** @type {Node} */ (fragment))); diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 265a52262f4b..135ca86610e7 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -316,6 +316,9 @@ export function text(value = '') { return node; } +/** + * @returns {TemplateNode | DocumentFragment} + */ export function comment() { // we're not delegating to `template` here for performance reasons if (hydrating) { diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 3d234f5bba4c..35aff7d4c9e1 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -10,12 +10,10 @@ import { INERT, RENDER_EFFECT, ROOT_EFFECT, - USER_EFFECT, MAYBE_DIRTY } from '#client/constants'; import { async_mode_flag } from '../../flags/index.js'; import { deferred, define_property } from '../../shared/utils.js'; -import { get_boundary } from '../dom/blocks/boundary.js'; import { active_effect, is_dirty, @@ -30,7 +28,6 @@ import { DEV } from 'esm-env'; import { invoke_error_boundary } from '../error-handling.js'; import { old_values } from './sources.js'; import { unlink_effect } from './effects.js'; -import { unset_context } from './async.js'; /** @type {Set} */ const batches = new Set(); From 8b106b94f41a87ebfff251f70d602dda70265dc9 Mon Sep 17 00:00:00 2001 From: 7nik Date: Mon, 15 Sep 2025 20:24:31 +0300 Subject: [PATCH 4/5] fix: correctly SSR hidden="until-found" (#16773) --- .changeset/pink-gifts-sell.md | 5 +++++ packages/svelte/src/internal/shared/attributes.js | 4 ++++ packages/svelte/src/utils.js | 1 - .../samples/attribute-spread-hidden-2/_expected.html | 1 + .../samples/attribute-spread-hidden-2/main.svelte | 3 +++ 5 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .changeset/pink-gifts-sell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte diff --git a/.changeset/pink-gifts-sell.md b/.changeset/pink-gifts-sell.md new file mode 100644 index 000000000000..f3f91ff7d982 --- /dev/null +++ b/.changeset/pink-gifts-sell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly SSR hidden="until-found" diff --git a/packages/svelte/src/internal/shared/attributes.js b/packages/svelte/src/internal/shared/attributes.js index c8758c1d4d4d..a96e71ff6ffb 100644 --- a/packages/svelte/src/internal/shared/attributes.js +++ b/packages/svelte/src/internal/shared/attributes.js @@ -23,6 +23,10 @@ const replacements = { */ export function attr(name, value, is_boolean = false) { if (value == null || (!value && is_boolean)) return ''; + // attribute hidden for values other than "until-found" behaves like a boolean attribute + if (name === 'hidden' && value !== 'until-found') { + is_boolean = true; + } const normalized = (name in replacements && replacements[name].get(value)) || value; const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index f8c39253aca1..f8a7e8d46d1c 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -154,7 +154,6 @@ const DOM_BOOLEAN_ATTRIBUTES = [ 'default', 'disabled', 'formnovalidate', - 'hidden', 'indeterminate', 'inert', 'ismap', diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html b/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html new file mode 100644 index 000000000000..80937efaee33 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html @@ -0,0 +1 @@ +
A
diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte new file mode 100644 index 000000000000..6738b34054aa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte @@ -0,0 +1,3 @@ +
A
+
B
+
C
\ No newline at end of file From 8c982f61013a517bec2edf68a82ce1a1188fcd8f Mon Sep 17 00:00:00 2001 From: 7nik Date: Tue, 16 Sep 2025 11:45:18 +0300 Subject: [PATCH 5/5] fix: correct wrong fix, get test to actually do something (#16779) #16773 added a test with _expected.html to a wrong suit, so it was ignored, and this allowed to slip in a new bug of printing the falsy hidden attribute as hidden (shortened enabled form). That fix wasn't released yet, so no changeset. --- packages/svelte/src/internal/shared/attributes.js | 2 +- .../samples/attribute-spread-hidden}/_expected.html | 0 .../samples/attribute-spread-hidden}/main.svelte | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename packages/svelte/tests/{runtime-runes/samples/attribute-spread-hidden-2 => server-side-rendering/samples/attribute-spread-hidden}/_expected.html (100%) rename packages/svelte/tests/{runtime-runes/samples/attribute-spread-hidden-2 => server-side-rendering/samples/attribute-spread-hidden}/main.svelte (100%) diff --git a/packages/svelte/src/internal/shared/attributes.js b/packages/svelte/src/internal/shared/attributes.js index a96e71ff6ffb..4ad550e8d612 100644 --- a/packages/svelte/src/internal/shared/attributes.js +++ b/packages/svelte/src/internal/shared/attributes.js @@ -22,11 +22,11 @@ const replacements = { * @returns {string} */ export function attr(name, value, is_boolean = false) { - if (value == null || (!value && is_boolean)) return ''; // attribute hidden for values other than "until-found" behaves like a boolean attribute if (name === 'hidden' && value !== 'until-found') { is_boolean = true; } + if (value == null || (!value && is_boolean)) return ''; const normalized = (name in replacements && replacements[name].get(value)) || value; const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html b/packages/svelte/tests/server-side-rendering/samples/attribute-spread-hidden/_expected.html similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/_expected.html rename to packages/svelte/tests/server-side-rendering/samples/attribute-spread-hidden/_expected.html diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte b/packages/svelte/tests/server-side-rendering/samples/attribute-spread-hidden/main.svelte similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/attribute-spread-hidden-2/main.svelte rename to packages/svelte/tests/server-side-rendering/samples/attribute-spread-hidden/main.svelte