diff --git a/.changeset/tricky-radios-vanish.md b/.changeset/tricky-radios-vanish.md new file mode 100644 index 0000000000..0935148fba --- /dev/null +++ b/.changeset/tricky-radios-vanish.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove leading newline from `
` contents
diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js
index b53a3110bd..62a635de35 100644
--- a/packages/svelte/src/compiler/phases/3-transform/utils.js
+++ b/packages/svelte/src/compiler/phases/3-transform/utils.js
@@ -5,6 +5,7 @@
 import {
 	regex_ends_with_whitespaces,
 	regex_not_whitespace,
+	regex_starts_with_newline,
 	regex_starts_with_whitespaces
 } from '../patterns.js';
 import * as b from '../../utils/builders.js';
@@ -270,6 +271,22 @@ export function clean_nodes(
 
 	var first = trimmed[0];
 
+	// initial newline inside a `
` is disregarded, if not followed by another newline
+	if (parent.type === 'RegularElement' && parent.name === 'pre' && first.type === 'Text') {
+		const text = first.data.replace(regex_starts_with_newline, '');
+		if (text !== first.data) {
+			const tmp = text.replace(regex_starts_with_newline, '');
+			if (text === tmp) {
+				first.data = text;
+				first.raw = first.raw.replace(regex_starts_with_newline, '');
+				if (first.data === '') {
+					trimmed.shift();
+					first = trimmed[0];
+				}
+			}
+		}
+	}
+
 	// Special case: Add a comment if this is a lone script tag. This ensures that our run_scripts logic in template.js
 	// will always be able to call node.replaceWith() on the script tag in order to make it run. If we don't add this
 	// and would still call node.replaceWith() on the script tag, it would be a no-op because the script tag has no parent.
diff --git a/packages/svelte/tests/hydration/samples/pre-first-node-newline/_config.js b/packages/svelte/tests/hydration/samples/pre-first-node-newline/_config.js
new file mode 100644
index 0000000000..c838aab749
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/pre-first-node-newline/_config.js
@@ -0,0 +1,5 @@
+import { test } from '../../test';
+
+// A note about _expected.html: It is different from body.html because we're
+// testing against target.innerHTML which already removed the redundant first newline
+export default test({});
diff --git a/packages/svelte/tests/hydration/samples/pre-first-node-newline/_expected.html b/packages/svelte/tests/hydration/samples/pre-first-node-newline/_expected.html
new file mode 100644
index 0000000000..26de34d1ed
--- /dev/null
+++ b/packages/svelte/tests/hydration/samples/pre-first-node-newline/_expected.html
@@ -0,0 +1,7 @@
+
static content no line
	static content ignored line
+
+	static content relevant line
+
+
+
+
diff --git a/packages/svelte/tests/hydration/samples/pre-first-node-newline/main.svelte b/packages/svelte/tests/hydration/samples/pre-first-node-newline/main.svelte new file mode 100644 index 0000000000..9764b49e99 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/pre-first-node-newline/main.svelte @@ -0,0 +1,23 @@ + + +
static content no line
+ +
+	static content ignored line
+
+ +
+
+	static content relevant line
+
+ +
+
{name}
+
+ +
+
+
{name}
+
diff --git a/packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js b/packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js index f1a42e996a..d7e1fe9d43 100644 --- a/packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js @@ -2,17 +2,9 @@ import { test } from '../../test'; export default test({ mode: ['client', 'server'], // output is correct, but test suite chokes on the extra ssr comment which is harmless - withoutNormalizeHtml: true, - html: get_html(false), - ssrHtml: get_html(true) -}); - -/** @param {boolean} ssr */ -function get_html(ssr) { - // ssr rendered HTML has an extra newline prefixed within `
` tag,
-	// if the 
 tag starts with `\n`
-	// because when browser parses the SSR rendered HTML, it will ignore the 1st '\n' character
-	return `${ssr ? '' : ''}
  A
+	withoutNormalizeHtml: 'only-strip-comments', // because whitespace inside pre tags is significant
+	// Note how we're testing against target.innerHTML which already removed the redundant first newline
+	html: `
  A
   B
   
     C
@@ -35,5 +27,5 @@ function get_html(ssr) {
 leading newlines
without spaces
  with spaces  
${' '}
 newline after leading space
 
-multiple leading newlines
${ssr ? '' : ''}`; -} +multiple leading newlines
` +});