Skip to content

Commit 7737868

Browse files
fix: remove leading newline from <pre> contents (#14922)
... if it's not followed by another newline, according to the spec Fixes #14767 --------- Co-authored-by: Simon Holthausen <[email protected]>
1 parent 08a9d12 commit 7737868

File tree

6 files changed

+62
-13
lines changed

6 files changed

+62
-13
lines changed

.changeset/tricky-radios-vanish.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: remove leading newline from `<pre>` contents

packages/svelte/src/compiler/phases/3-transform/utils.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import {
66
regex_ends_with_whitespaces,
77
regex_not_whitespace,
8+
regex_starts_with_newline,
89
regex_starts_with_whitespaces
910
} from '../patterns.js';
1011
import * as b from '../../utils/builders.js';
@@ -270,6 +271,22 @@ export function clean_nodes(
270271

271272
var first = trimmed[0];
272273

274+
// initial newline inside a `<pre>` is disregarded, if not followed by another newline
275+
if (parent.type === 'RegularElement' && parent.name === 'pre' && first.type === 'Text') {
276+
const text = first.data.replace(regex_starts_with_newline, '');
277+
if (text !== first.data) {
278+
const tmp = text.replace(regex_starts_with_newline, '');
279+
if (text === tmp) {
280+
first.data = text;
281+
first.raw = first.raw.replace(regex_starts_with_newline, '');
282+
if (first.data === '') {
283+
trimmed.shift();
284+
first = trimmed[0];
285+
}
286+
}
287+
}
288+
}
289+
273290
// Special case: Add a comment if this is a lone script tag. This ensures that our run_scripts logic in template.js
274291
// will always be able to call node.replaceWith() on the script tag in order to make it run. If we don't add this
275292
// and would still call node.replaceWith() on the script tag, it would be a no-op because the script tag has no parent.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '../../test';
2+
3+
// A note about _expected.html: It is different from body.html because we're
4+
// testing against target.innerHTML which already removed the redundant first newline
5+
export default test({});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<!--[--><pre>static content no line</pre> <pre> static content ignored line
2+
</pre> <pre>
3+
static content relevant line
4+
</pre> <pre><div><span></span></div>
5+
</pre> <pre>
6+
<div><span></span></div>
7+
</pre><!--]-->
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script>
2+
let name = $state('');
3+
</script>
4+
5+
<pre>static content no line</pre>
6+
7+
<pre>
8+
static content ignored line
9+
</pre>
10+
11+
<pre>
12+
13+
static content relevant line
14+
</pre>
15+
16+
<pre>
17+
<div><span>{name}</span></div>
18+
</pre>
19+
20+
<pre>
21+
22+
<div><span>{name}</span></div>
23+
</pre>

packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,9 @@ import { test } from '../../test';
22

33
export default test({
44
mode: ['client', 'server'], // output is correct, but test suite chokes on the extra ssr comment which is harmless
5-
withoutNormalizeHtml: true,
6-
html: get_html(false),
7-
ssrHtml: get_html(true)
8-
});
9-
10-
/** @param {boolean} ssr */
11-
function get_html(ssr) {
12-
// ssr rendered HTML has an extra newline prefixed within `<pre>` tag,
13-
// if the <pre> tag starts with `\n`
14-
// because when browser parses the SSR rendered HTML, it will ignore the 1st '\n' character
15-
return `${ssr ? '<!--[-->' : ''}<pre id="pre"> A
5+
withoutNormalizeHtml: 'only-strip-comments', // because whitespace inside pre tags is significant
6+
// Note how we're testing against target.innerHTML which already removed the redundant first newline
7+
html: `<pre id="pre"> A
168
B
179
<span>
1810
C
@@ -35,5 +27,5 @@ function get_html(ssr) {
3527
leading newlines</pre></div> <div id="pre-without-leading-newline"><pre>without spaces</pre> <pre> with spaces </pre> <pre>${' '}
3628
newline after leading space</pre></div> <pre id="pre-with-multiple-leading-newlines">
3729
38-
multiple leading newlines</pre>${ssr ? '<!--]-->' : ''}`;
39-
}
30+
multiple leading newlines</pre>`
31+
});

0 commit comments

Comments
 (0)