From 82fde88780647ac9699d9dec4108580f1865e55b Mon Sep 17 00:00:00 2001 From: Manuel <30698007+manuel3108@users.noreply.github.com> Date: Thu, 29 Jan 2026 03:54:29 +0100 Subject: [PATCH] fix: `print()` multiline behaviour (#17319) * way cleaner new line handling for base_elements * fix * more fixes * use esrap preview * changeset * bump `esrap` * update snapshot * update more snapshots * Update .changeset/rich-zoos-walk.md --------- Co-authored-by: Rich Harris --- .changeset/rich-zoos-walk.md | 5 ++ packages/svelte/package.json | 2 +- packages/svelte/src/compiler/print/index.js | 64 ++++--------------- .../output.svelte | 2 +- .../output.svelte | 2 +- .../output.svelte | 2 +- .../print/samples/formatting/input.svelte | 9 ++- .../print/samples/formatting/output.svelte | 34 +++++++++- .../print/samples/html-document/output.svelte | 4 ++ .../samples/style-directive/output.svelte | 5 +- .../samples/svelte-fragment/output.svelte | 1 + .../print/samples/svelte-head/output.svelte | 1 + .../_expected/client/index.svelte.js | 2 - .../_expected/server/index.svelte.js | 1 - .../_expected/client/index.svelte.js | 2 - .../_expected/server/index.svelte.js | 2 - .../_expected/client/index.svelte.js | 1 - pnpm-lock.yaml | 10 +-- 18 files changed, 75 insertions(+), 74 deletions(-) create mode 100644 .changeset/rich-zoos-walk.md diff --git a/.changeset/rich-zoos-walk.md b/.changeset/rich-zoos-walk.md new file mode 100644 index 0000000000..fc540d7123 --- /dev/null +++ b/.changeset/rich-zoos-walk.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly separate multiline html blocks from each other in `print()` diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 699473ecc6..d7dc67d528 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -176,7 +176,7 @@ "clsx": "^2.1.1", "devalue": "^5.6.2", "esm-env": "^1.2.1", - "esrap": "^2.2.1", + "esrap": "^2.2.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/packages/svelte/src/compiler/print/index.js b/packages/svelte/src/compiler/print/index.js index 4aed743dba..094c578b83 100644 --- a/packages/svelte/src/compiler/print/index.js +++ b/packages/svelte/src/compiler/print/index.js @@ -115,57 +115,17 @@ function base_element(node, context) { const is_doctype_node = node.name.toLowerCase() === '!doctype'; const is_self_closing = is_void(node.name) || (node.type === 'Component' && node.fragment.nodes.length === 0); - let multiline_content = false; if (is_doctype_node) child_context.write(`>`); else if (is_self_closing) { child_context.write(`${multiline_attributes ? '' : ' '}/>`); } else { child_context.write('>'); - - // Process the element's content in a separate context for measurement - const content_context = child_context.new(); - const allow_inline_content = child_context.measure() < LINE_BREAK_THRESHOLD; - block(content_context, node.fragment, allow_inline_content); - - // Determine if content should be formatted on multiple lines - multiline_content = content_context.measure() > LINE_BREAK_THRESHOLD; - - if (multiline_content) { - child_context.newline(); - - // Only indent if attributes are inline and content itself isn't already multiline - const should_indent = !multiline_attributes && !content_context.multiline; - if (should_indent) { - child_context.indent(); - } - - child_context.append(content_context); - - if (should_indent) { - child_context.dedent(); - } - - child_context.newline(); - } else { - child_context.append(content_context); - } - + block(child_context, node.fragment, true); child_context.write(``); } - const break_line_after = child_context.measure() > LINE_BREAK_THRESHOLD; - - if ((multiline_content || multiline_attributes) && !context.empty()) { - context.newline(); - } - context.append(child_context); - - if (is_self_closing) return; - if (multiline_content || multiline_attributes || break_line_after) { - context.newline(); - } } /** @type {Visitors} */ @@ -412,6 +372,8 @@ const svelte_visitors = { } } else { sequence.push(child_node); + + if (child_node.type === 'RegularElement') flush(); } } @@ -420,18 +382,20 @@ const svelte_visitors = { let multiline = false; let width = 0; - const child_contexts = items.map((sequence) => { - const child_context = context.new(); + const child_contexts = items + .filter((x) => x.length > 0) + .map((sequence) => { + const child_context = context.new(); - for (const node of sequence) { - child_context.visit(node); - multiline ||= child_context.multiline; - } + for (const node of sequence) { + child_context.visit(node); + multiline ||= child_context.multiline; + } - width += child_context.measure(); + width += child_context.measure(); - return child_context; - }); + return child_context; + }); multiline ||= width > LINE_BREAK_THRESHOLD; diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte index 26012e1115..90d2e2e036 100644 --- a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte @@ -4,4 +4,4 @@ - + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte index 328966b63b..20e41646b3 100644 --- a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte @@ -3,4 +3,4 @@ let body; - + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte index 1e763577df..49e9462768 100644 --- a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte @@ -1,2 +1,2 @@ - + \ No newline at end of file diff --git a/packages/svelte/tests/print/samples/formatting/input.svelte b/packages/svelte/tests/print/samples/formatting/input.svelte index 9b1898e9c8..43c3b363cb 100644 --- a/packages/svelte/tests/print/samples/formatting/input.svelte +++ b/packages/svelte/tests/print/samples/formatting/input.svelte @@ -1 +1,8 @@ -

{m.hello_world({ name: 'SvelteKit User' })}

If you use VSCode, install the Sherlock i18n extensionfor a better i18n experience.

+ + +

{m.hello_world({ name: 'SvelteKit User' })}

If you use VSCode, install the Sherlock i18n extensionfor a better i18n experience.

+ +
+ + + diff --git a/packages/svelte/tests/print/samples/formatting/output.svelte b/packages/svelte/tests/print/samples/formatting/output.svelte index 7b40642580..ba871c66a5 100644 --- a/packages/svelte/tests/print/samples/formatting/output.svelte +++ b/packages/svelte/tests/print/samples/formatting/output.svelte @@ -4,18 +4,46 @@

{m.hello_world({ name: 'SvelteKit User' })}

+
+

If you use VSCode, install the - Sherlock i18n extension - + >Sherlock i18n extension + for a better i18n experience.

+ + +
+ + +
+
+ + + + + + + + + diff --git a/packages/svelte/tests/print/samples/html-document/output.svelte b/packages/svelte/tests/print/samples/html-document/output.svelte index 765f9ca84b..94ef78d0a3 100644 --- a/packages/svelte/tests/print/samples/html-document/output.svelte +++ b/packages/svelte/tests/print/samples/html-document/output.svelte @@ -1,12 +1,16 @@ + + + Svelte App +
Hello World
diff --git a/packages/svelte/tests/print/samples/style-directive/output.svelte b/packages/svelte/tests/print/samples/style-directive/output.svelte index 5aa3a6dcdb..27c12c5d47 100644 --- a/packages/svelte/tests/print/samples/style-directive/output.svelte +++ b/packages/svelte/tests/print/samples/style-directive/output.svelte @@ -1,8 +1,7 @@
...
+
- ... -
+>... diff --git a/packages/svelte/tests/print/samples/svelte-fragment/output.svelte b/packages/svelte/tests/print/samples/svelte-fragment/output.svelte index eb80023626..73a4451ed8 100644 --- a/packages/svelte/tests/print/samples/svelte-fragment/output.svelte +++ b/packages/svelte/tests/print/samples/svelte-fragment/output.svelte @@ -4,6 +4,7 @@

Hello

+

All rights reserved.

Copyright (c) 2019 Svelte Industries

diff --git a/packages/svelte/tests/print/samples/svelte-head/output.svelte b/packages/svelte/tests/print/samples/svelte-head/output.svelte index 68d352260e..da4345d731 100644 --- a/packages/svelte/tests/print/samples/svelte-head/output.svelte +++ b/packages/svelte/tests/print/samples/svelte-head/output.svelte @@ -1,5 +1,6 @@ Hello world! + yes1 = await $.async_derived(() => 1), async () => yes2 = await $.async_derived(async () => foo(await 1)), - () => no1 = $.derived(async () => { return await 1; }), @@ -33,7 +32,6 @@ export default function Async_in_derived($$anchor, $$props) { var promises = $.run([ async () => yes1 = (await $.save($.async_derived(async () => (await $.save(1))())))(), async () => yes2 = (await $.save($.async_derived(async () => foo((await $.save(1))()))))(), - () => no1 = $.derived(() => (async () => { return await 1; })()), diff --git a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js index 1fd184fa79..58358b3ded 100644 --- a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js @@ -8,7 +8,6 @@ export default function Async_in_derived($$renderer, $$props) { var $$promises = $$renderer.run([ async () => yes1 = await 1, async () => yes2 = foo(await 1), - () => no1 = (async () => { return await 1; })(), 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 218951b836..762a23754c 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 @@ -14,7 +14,6 @@ export default function Function_prop_no_getter($$anchor) { onmousedown: () => $.set(count, $.get(count) + 1), onmouseup, onmouseenter: () => $.set(count, plusOne($.get(count)), true), - children: ($$anchor, $$slotProps) => { $.next(); @@ -23,7 +22,6 @@ export default function Function_prop_no_getter($$anchor) { $.template_effect(() => $.set_text(text, `clicks: ${$.get(count) ?? ''}`)); $.append($$anchor, text); }, - $$slots: { default: true } }); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js index 855ae30d21..e2936b7f6e 100644 --- a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js @@ -13,11 +13,9 @@ export default function Function_prop_no_getter($$renderer) { onmousedown: () => count += 1, onmouseup, onmouseenter: () => count = plusOne(count), - children: ($$renderer) => { $$renderer.push(`clicks: ${$.escape(count)}`); }, - $$slots: { default: true } }); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/functional-templating/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/functional-templating/_expected/client/index.svelte.js index d4034dc55d..792d5421e1 100644 --- a/packages/svelte/tests/snapshot/samples/functional-templating/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/functional-templating/_expected/client/index.svelte.js @@ -6,7 +6,6 @@ var root = $.from_tree( [ ['h1', null, 'hello'], ' ', - [ 'div', { class: 'potato' }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b616fc0577..b14dc104ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,8 +96,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 esrap: - specifier: ^2.2.1 - version: 2.2.1 + specifier: ^2.2.2 + version: 2.2.2 is-reference: specifier: ^3.0.3 version: 3.0.3 @@ -1356,8 +1356,8 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} - esrap@2.2.1: - resolution: {integrity: sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg==} + esrap@2.2.2: + resolution: {integrity: sha512-zA6497ha+qKvoWIK+WM9NAh5ni17sKZKhbS5B3PoYbBvaYHZWoS33zmFybmyqpn07RLUxSmn+RCls2/XF+d0oQ==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3720,7 +3720,7 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.2.1: + esrap@2.2.2: dependencies: '@jridgewell/sourcemap-codec': 1.5.0