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 <simon.holthausen@vercel.com>
unsafe-mutation-docs
Rich Harris 4 days ago committed by GitHub
parent 08a9d123e8
commit 77378688b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: remove leading newline from `<pre>` contents

@ -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 `<pre>` 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.

@ -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({});

@ -0,0 +1,7 @@
<!--[--><pre>static content no line</pre> <pre> static content ignored line
</pre> <pre>
static content relevant line
</pre> <pre><div><span></span></div>
</pre> <pre>
<div><span></span></div>
</pre><!--]-->

@ -0,0 +1,23 @@
<script>
let name = $state('');
</script>
<pre>static content no line</pre>
<pre>
static content ignored line
</pre>
<pre>
static content relevant line
</pre>
<pre>
<div><span>{name}</span></div>
</pre>
<pre>
<div><span>{name}</span></div>
</pre>

@ -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 `<pre>` tag,
// if the <pre> tag starts with `\n`
// because when browser parses the SSR rendered HTML, it will ignore the 1st '\n' character
return `${ssr ? '<!--[-->' : ''}<pre id="pre"> 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: `<pre id="pre"> A
B
<span>
C
@ -35,5 +27,5 @@ function get_html(ssr) {
leading newlines</pre></div> <div id="pre-without-leading-newline"><pre>without spaces</pre> <pre> with spaces </pre> <pre>${' '}
newline after leading space</pre></div> <pre id="pre-with-multiple-leading-newlines">
multiple leading newlines</pre>${ssr ? '<!--]-->' : ''}`;
}
multiple leading newlines</pre>`
});

Loading…
Cancel
Save