From d3776c1723c17736198e0bc96c2fb75c69b0756f Mon Sep 17 00:00:00 2001 From: Tensorworker <178221229+tensorworkerr@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:40:08 -0500 Subject: [PATCH 1/6] fix: handle non-string values in svelte:element this attribute (#17499) * fix: handle non-string values in svelte:element this attribute * chore: add changeset * fix(review): throw error in DEV for invalid svelte:element tag, ignore in PROD * chore: remove unused expected.html from test * fix(review): refine validate.js logic * fix(review): use else if (string) logic in server/index.js * fix(review): swap validation order in compiler, revert runtime changes * test: move reproduction to runtime-legacy per review * Apply suggestion from @7nik --------- Co-authored-by: tensorworker Co-authored-by: 7nik --- .changeset/clever-cats-dance.md | 5 +++++ .../client/visitors/SvelteElement.js | 2 +- .../server/visitors/SvelteElement.js | 2 +- .../_config.js | 17 +++++++++++++++++ .../main.svelte | 5 +++++ 5 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 .changeset/clever-cats-dance.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/dynamic-element-invalid-this-content/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/dynamic-element-invalid-this-content/main.svelte diff --git a/.changeset/clever-cats-dance.md b/.changeset/clever-cats-dance.md new file mode 100644 index 0000000000..3322608095 --- /dev/null +++ b/.changeset/clever-cats-dance.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: handle non-string values in `svelte:element` `this` attribute diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index cfa79b73ca..c8192cf00a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -117,10 +117,10 @@ export function SvelteElement(node, context) { ); if (dev) { + statements.push(b.stmt(b.call('$.validate_dynamic_element_tag', get_tag))); if (node.fragment.nodes.length > 0) { statements.push(b.stmt(b.call('$.validate_void_dynamic_element', get_tag))); } - statements.push(b.stmt(b.call('$.validate_dynamic_element_tag', get_tag))); } const location = dev && locator(node.start); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js index 6fc1ca5dfc..1ac15893f2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js @@ -29,10 +29,10 @@ export function SvelteElement(node, context) { tag = b.id(tag_id); } + context.state.init.push(b.stmt(b.call('$.validate_dynamic_element_tag', b.thunk(tag)))); if (node.fragment.nodes.length > 0) { context.state.init.push(b.stmt(b.call('$.validate_void_dynamic_element', b.thunk(tag)))); } - context.state.init.push(b.stmt(b.call('$.validate_dynamic_element_tag', b.thunk(tag)))); } const state = { diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-invalid-this-content/_config.js b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-invalid-this-content/_config.js new file mode 100644 index 0000000000..b0dcaa8fdc --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-invalid-this-content/_config.js @@ -0,0 +1,17 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client', 'server'], + + compileOptions: { + dev: true + }, + + get props() { + return { tag: true }; + }, + + error: + 'svelte_element_invalid_this_value\n' + + 'The `this` prop on `` must be a string, if defined' +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-invalid-this-content/main.svelte b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-invalid-this-content/main.svelte new file mode 100644 index 0000000000..cc6736a497 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-invalid-this-content/main.svelte @@ -0,0 +1,5 @@ + + +content From f679259232307b5e39ee6a6cc4d39bbd475ea1be Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 21 Jan 2026 19:30:36 -0500 Subject: [PATCH 2/6] fix: prevent duplicate dependencies without using array.includes (#17503) * fix: prevent duplicate dependencies without using array.includes * changeset --- .changeset/gold-coats-change.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 .changeset/gold-coats-change.md diff --git a/.changeset/gold-coats-change.md b/.changeset/gold-coats-change.md new file mode 100644 index 0000000000..0415de9e7c --- /dev/null +++ b/.changeset/gold-coats-change.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: faster deduplication of dependencies diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 0e12324d72..6988c6247a 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -309,6 +309,20 @@ export function update_reaction(reaction) { if (previous_reaction !== null && previous_reaction !== reaction) { read_version++; + // update the `rv` of the previous reaction's deps — both existing and new — + // so that they are not added again + if (previous_reaction.deps !== null) { + for (let i = 0; i < previous_skipped_deps; i += 1) { + previous_reaction.deps[i].rv = read_version; + } + } + + if (previous_deps !== null) { + for (const dep of previous_deps) { + dep.rv = read_version; + } + } + if (untracked_writes !== null) { if (previous_untracked_writes === null) { previous_untracked_writes = untracked_writes; @@ -526,7 +540,7 @@ export function get(signal) { skipped_deps++; } else if (new_deps === null) { new_deps = [signal]; - } else if (!new_deps.includes(signal)) { + } else { new_deps.push(signal); } } From 0ce2313e35ae680ebd60f4632a593759a9462629 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 21 Jan 2026 19:31:23 -0500 Subject: [PATCH 3/6] chore: squelch hydration warnings (#17509) * chore: squelch some hydration warnings * squelch another warning --- .../hydration/samples/no-reset-debug/_config.js | 14 +++++++++++++- packages/svelte/tests/hydration/test.ts | 4 ++-- .../hydratable-clobbering-complicated/main.svelte | 11 ++++++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/svelte/tests/hydration/samples/no-reset-debug/_config.js b/packages/svelte/tests/hydration/samples/no-reset-debug/_config.js index f47bee71df..2bdb29e436 100644 --- a/packages/svelte/tests/hydration/samples/no-reset-debug/_config.js +++ b/packages/svelte/tests/hydration/samples/no-reset-debug/_config.js @@ -1,3 +1,15 @@ import { test } from '../../test'; -export default test({}); +/** @type {typeof console.log} */ +let log; + +export default test({ + before_test() { + log = console.log; + console.log = () => {}; + }, + + after_test() { + console.log = log; + } +}); diff --git a/packages/svelte/tests/hydration/test.ts b/packages/svelte/tests/hydration/test.ts index d7257c71d3..16e4070303 100644 --- a/packages/svelte/tests/hydration/test.ts +++ b/packages/svelte/tests/hydration/test.ts @@ -53,6 +53,8 @@ const { test, run } = suite(async (config, cwd) => { await compile_directory(cwd, 'server', config.compileOptions); } + config.before_test?.(); + const target = window.document.body; const head = window.document.head; @@ -72,8 +74,6 @@ const { test, run } = suite(async (config, cwd) => { head.innerHTML = override_head ?? rendered.head; } - config.before_test?.(); - try { const snapshot = config.snapshot ? config.snapshot(target) : {}; diff --git a/packages/svelte/tests/server-side-rendering/samples/hydratable-clobbering-complicated/main.svelte b/packages/svelte/tests/server-side-rendering/samples/hydratable-clobbering-complicated/main.svelte index 358488c3ac..64d30b2a7f 100644 --- a/packages/svelte/tests/server-side-rendering/samples/hydratable-clobbering-complicated/main.svelte +++ b/packages/svelte/tests/server-side-rendering/samples/hydratable-clobbering-complicated/main.svelte @@ -1,16 +1,21 @@ \ No newline at end of file + + +

{await (await (a.nested)).one}

+

{await a.two}

+

{await (await (b.nested)).one}

+

{await b.two}

From c1fab8caae8334ebc1db7fb37d618ca204e461ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 22:03:34 -0500 Subject: [PATCH 4/6] Version Packages (#17508) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/clever-cats-dance.md | 5 ----- .changeset/gold-coats-change.md | 5 ----- .changeset/loud-buses-guess.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 14 insertions(+), 17 deletions(-) delete mode 100644 .changeset/clever-cats-dance.md delete mode 100644 .changeset/gold-coats-change.md delete mode 100644 .changeset/loud-buses-guess.md diff --git a/.changeset/clever-cats-dance.md b/.changeset/clever-cats-dance.md deleted file mode 100644 index 3322608095..0000000000 --- a/.changeset/clever-cats-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: handle non-string values in `svelte:element` `this` attribute diff --git a/.changeset/gold-coats-change.md b/.changeset/gold-coats-change.md deleted file mode 100644 index 0415de9e7c..0000000000 --- a/.changeset/gold-coats-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: faster deduplication of dependencies diff --git a/.changeset/loud-buses-guess.md b/.changeset/loud-buses-guess.md deleted file mode 100644 index d6c7c3d5a4..0000000000 --- a/.changeset/loud-buses-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: export `parseCss` from `svelte/compiler` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index d019b7883f..21cbb4ee66 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.48.0 + +### Minor Changes + +- feat: export `parseCss` from `svelte/compiler` ([#17496](https://github.com/sveltejs/svelte/pull/17496)) + +### Patch Changes + +- fix: handle non-string values in `svelte:element` `this` attribute ([#17499](https://github.com/sveltejs/svelte/pull/17499)) + +- fix: faster deduplication of dependencies ([#17503](https://github.com/sveltejs/svelte/pull/17503)) + ## 5.47.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 38cac7b1cd..bbb519f8e8 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.47.1", + "version": "5.48.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a5360f7195..c1ced8dad3 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.47.1'; +export const VERSION = '5.48.0'; export const PUBLIC_VERSION = '5'; From d95887deb6e1f6222b6aadef6c4493f43755fae3 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 22 Jan 2026 19:30:44 +0100 Subject: [PATCH 5/6] fix: hoist snippets above const in same block (#17516) * fix: hoist snippets above const in same block * fix: type error --- .changeset/blue-parts-double.md | 5 +++++ .../compiler/phases/3-transform/client/transform-client.js | 1 + .../src/compiler/phases/3-transform/client/types.d.ts | 2 ++ .../phases/3-transform/client/visitors/Fragment.js | 7 ++++--- .../phases/3-transform/client/visitors/RegularElement.js | 3 ++- .../phases/3-transform/client/visitors/SnippetBlock.js | 2 +- .../phases/3-transform/client/visitors/SvelteBoundary.js | 2 +- .../phases/3-transform/client/visitors/shared/component.js | 2 +- .../samples/snippet-const-same-block/_config.js | 5 +++++ .../samples/snippet-const-same-block/main.svelte | 4 ++++ 10 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 .changeset/blue-parts-double.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-const-same-block/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-const-same-block/main.svelte diff --git a/.changeset/blue-parts-double.md b/.changeset/blue-parts-double.md new file mode 100644 index 0000000000..223e51fdae --- /dev/null +++ b/.changeset/blue-parts-double.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: hoist snippets above const in same block 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 5c00aa591f..fc202f1bc2 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 @@ -170,6 +170,7 @@ export function client_component(analysis, options) { // these are set inside the `Fragment` visitor, and cannot be used until then init: /** @type {any} */ (null), consts: /** @type {any} */ (null), + snippets: /** @type {any} */ (null), let_directives: /** @type {any} */ (null), update: /** @type {any} */ (null), after_update: /** @type {any} */ (null), 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 d64b1d4126..4438ec015b 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 @@ -49,6 +49,8 @@ export interface ComponentClientTransformState extends ClientTransformState { readonly update: Statement[]; /** Stuff that happens after the render effect (control blocks, dynamic elements, bindings, actions, etc) */ readonly after_update: Statement[]; + /** Transformed `{#snippets }` declarations */ + readonly snippets: Statement[]; /** Transformed `{@const }` declarations */ readonly consts: Statement[]; /** Transformed async `{@const }` declarations (if any) and those coming after them */ diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index 18017ea557..79a443967c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -1,13 +1,13 @@ /** @import { Expression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ -import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../../../constants.js'; import * as b from '#compiler/builders'; +import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../../../constants.js'; import { clean_nodes, infer_namespace } from '../../utils.js'; import { transform_template } from '../transform-template/index.js'; +import { Template } from '../transform-template/template.js'; import { process_children } from './shared/fragment.js'; import { build_render_statement, Memoizer } from './shared/utils.js'; -import { Template } from '../transform-template/template.js'; /** * @param {AST.Fragment} node @@ -60,6 +60,7 @@ export function Fragment(node, context) { const state = { ...context.state, init: [], + snippets: [], consts: [], let_directives: [], update: [], @@ -150,7 +151,7 @@ export function Fragment(node, context) { } } - body.push(...state.let_directives, ...state.consts); + body.push(...state.snippets, ...state.let_directives, ...state.consts); if (state.async_consts && state.async_consts.thunks.length > 0) { body.push(b.var(state.async_consts.id, b.call('$.run', b.array(state.async_consts.thunks)))); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 1faf44b902..720522beaf 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -329,7 +329,7 @@ export function RegularElement(node, context) { ); /** @type {typeof state} */ - const child_state = { ...state, init: [], update: [], after_update: [] }; + const child_state = { ...state, init: [], update: [], after_update: [], snippets: [] }; for (const node of hoisted) { context.visit(node, child_state); @@ -441,6 +441,7 @@ export function RegularElement(node, context) { // Wrap children in `{...}` to avoid declaration conflicts context.state.init.push( b.block([ + ...child_state.snippets, ...child_state.init, ...element_state.init, child_state.update.length > 0 ? build_render_statement(child_state) : b.empty, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js index 1af737f05b..6c3a48c5ea 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js @@ -89,6 +89,6 @@ export function SnippetBlock(node, context) { context.state.instance_level_snippets.push(declaration); } } else { - context.state.init.push(declaration); + context.state.snippets.push(declaration); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js index d64fcda2e8..f929a3bc47 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js @@ -77,7 +77,7 @@ export function SvelteBoundary(node, context) { /** @type {Statement[]} */ const statements = []; - context.visit(child, { ...context.state, init: statements }); + context.visit(child, { ...context.state, snippets: statements }); const snippet = /** @type {VariableDeclaration} */ (statements[0]); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 4651f6f733..bb72794af8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -333,7 +333,7 @@ export function build_component(node, component_name, loc, context) { // can be used as props without creating conflicts context.visit(child, { ...context.state, - init: snippet_declarations + snippets: snippet_declarations }); push_prop(b.prop('init', child.expression, child.expression)); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-const-same-block/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-const-same-block/_config.js new file mode 100644 index 0000000000..27f013d6d1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-const-same-block/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + test() {} +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-const-same-block/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-const-same-block/main.svelte new file mode 100644 index 0000000000..70c7d64f18 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-const-same-block/main.svelte @@ -0,0 +1,4 @@ +{#if true} + {@const xx = test} + {#snippet test()}{/snippet} +{/if} From f9cc2d25b9b348fb6c1c2b6b6f973804e6a5ed8c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 22 Jan 2026 16:05:26 -0500 Subject: [PATCH 6/6] fix: better code generation for const tags with async dependencies (#17518) --- .changeset/witty-streets-carry.md | 5 +++ .../3-transform/client/visitors/ConstTag.js | 16 ++++++--- .../3-transform/server/visitors/ConstTag.js | 6 ++-- .../snapshot/samples/async-const/_config.js | 3 ++ .../_expected/client/index.svelte.js | 35 +++++++++++++++++++ .../_expected/server/index.svelte.js | 33 +++++++++++++++++ .../snapshot/samples/async-const/index.svelte | 6 ++++ 7 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 .changeset/witty-streets-carry.md create mode 100644 packages/svelte/tests/snapshot/samples/async-const/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/async-const/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-const/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/async-const/index.svelte diff --git a/.changeset/witty-streets-carry.md b/.changeset/witty-streets-carry.md new file mode 100644 index 0000000000..f02d998409 --- /dev/null +++ b/.changeset/witty-streets-carry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: better code generation for const tags with async dependencies diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js index a9f922623e..ffeffe6ea4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js @@ -1,4 +1,4 @@ -/** @import { Pattern } from 'estree' */ +/** @import { Expression, Identifier, Pattern } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ /** @import { ExpressionMetadata } from '../../../nodes.js' */ @@ -88,8 +88,8 @@ export function ConstTag(node, context) { /** * @param {ComponentContext['state']} state - * @param {import('estree').Identifier} id - * @param {import('estree').Expression} expression + * @param {Identifier} id + * @param {Expression} expression * @param {ExpressionMetadata} metadata * @param {import('#compiler').Binding[]} bindings */ @@ -99,7 +99,9 @@ function add_const_declaration(state, id, expression, metadata, bindings) { const after = dev ? [b.stmt(b.call('$.get', id))] : []; const has_await = metadata.has_await; - const blockers = [...metadata.dependencies].map((dep) => dep.blocker).filter((b) => b !== null); + const blockers = [...metadata.dependencies] + .map((dep) => dep.blocker) + .filter((b) => b !== null && b.object !== state.async_consts?.id); if (has_await || state.async_consts || blockers.length > 0) { const run = (state.async_consts ??= { @@ -112,7 +114,11 @@ function add_const_declaration(state, id, expression, metadata, bindings) { const assignment = b.assignment('=', id, expression); const body = after.length === 0 ? assignment : b.block([b.stmt(assignment), ...after]); - if (blockers.length > 0) run.thunks.push(b.thunk(b.call('Promise.all', b.array(blockers)))); + if (blockers.length === 1) { + run.thunks.push(b.thunk(/** @type {Expression} */ (blockers[0]))); + } else if (blockers.length > 0) { + run.thunks.push(b.thunk(b.call('Promise.all', b.array(blockers)))); + } run.thunks.push(b.thunk(body, has_await)); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js index fdc31e5238..d2ff9a10b4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js @@ -15,7 +15,7 @@ export function ConstTag(node, context) { const has_await = node.metadata.expression.has_await; const blockers = [...node.metadata.expression.dependencies] .map((dep) => dep.blocker) - .filter((b) => b !== null); + .filter((b) => b !== null && b.object !== context.state.async_consts?.id); if (has_await || context.state.async_consts || blockers.length > 0) { const run = (context.state.async_consts ??= { @@ -30,7 +30,9 @@ export function ConstTag(node, context) { context.state.init.push(b.let(identifier.name)); } - if (blockers.length > 0) { + if (blockers.length === 1) { + run.thunks.push(b.thunk(/** @type {Expression} */ (blockers[0]))); + } else if (blockers.length > 0) { run.thunks.push(b.thunk(b.call('Promise.all', b.array(blockers)))); } diff --git a/packages/svelte/tests/snapshot/samples/async-const/_config.js b/packages/svelte/tests/snapshot/samples/async-const/_config.js new file mode 100644 index 0000000000..2e30bbeb16 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/async-const/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({ compileOptions: { experimental: { async: true } } }); diff --git a/packages/svelte/tests/snapshot/samples/async-const/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-const/_expected/client/index.svelte.js new file mode 100644 index 0000000000..8bd7e97780 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/async-const/_expected/client/index.svelte.js @@ -0,0 +1,35 @@ +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/async'; +import * as $ from 'svelte/internal/client'; + +var root_1 = $.from_html(`

`); + +export default function Async_const($$anchor) { + var fragment = $.comment(); + var node = $.first_child(fragment); + + { + var consequent = ($$anchor) => { + let a; + let b; + + var promises = $.run([ + async () => a = (await $.save($.async_derived(async () => (await $.save(1))())))(), + () => b = $.derived(() => $.get(a) + 1) + ]); + + var p = root_1(); + var text = $.child(p, true); + + $.reset(p); + $.template_effect(() => $.set_text(text, $.get(b)), void 0, void 0, [promises[1]]); + $.append($$anchor, p); + }; + + $.if(node, ($$render) => { + if (true) $$render(consequent); + }); + } + + $.append($$anchor, fragment); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/async-const/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-const/_expected/server/index.svelte.js new file mode 100644 index 0000000000..a726b903bc --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/async-const/_expected/server/index.svelte.js @@ -0,0 +1,33 @@ +import 'svelte/internal/flags/async'; +import * as $ from 'svelte/internal/server'; + +export default function Async_const($$renderer) { + if (true) { + $$renderer.push(''); + + let a; + let b; + + var promises = $$renderer.run([ + async () => { + a = (await $.save(1))(); + }, + + () => { + b = a + 1; + } + ]); + + $$renderer.push(`

`); + + $$renderer.async([promises[1]], ($$renderer) => { + $$renderer.push(() => $.escape(b)); + }); + + $$renderer.push(`

`); + } else { + $$renderer.push(''); + } + + $$renderer.push(``); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/async-const/index.svelte b/packages/svelte/tests/snapshot/samples/async-const/index.svelte new file mode 100644 index 0000000000..cf1bb40d88 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/async-const/index.svelte @@ -0,0 +1,6 @@ +{#if true} + {@const a = await 1} + {@const b = a + 1} + +

{b}

+{/if}