fix: remove outer hydration markers (#16800)

pull/16801/head
Rich Harris 1 day ago committed by GitHub
parent e5a75aecf8
commit 1c8456885c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: remove outer hydration markers

@ -120,19 +120,9 @@ export function hydrate(component, options) {
set_hydrating(true); set_hydrating(true);
set_hydrate_node(/** @type {Comment} */ (anchor)); set_hydrate_node(/** @type {Comment} */ (anchor));
hydrate_next();
const instance = _mount(component, { ...options, anchor }); const instance = _mount(component, { ...options, anchor });
if (
hydrate_node === null ||
hydrate_node.nodeType !== COMMENT_NODE ||
/** @type {Comment} */ (hydrate_node).data !== HYDRATION_END
) {
w.hydration_mismatch();
throw HYDRATION_ERROR;
}
set_hydrating(false); set_hydrating(false);
return /** @type {Exports} */ (instance); return /** @type {Exports} */ (instance);
@ -247,6 +237,15 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
if (hydrating) { if (hydrating) {
/** @type {Effect} */ (active_effect).nodes_end = hydrate_node; /** @type {Effect} */ (active_effect).nodes_end = hydrate_node;
if (
hydrate_node === null ||
hydrate_node.nodeType !== COMMENT_NODE ||
/** @type {Comment} */ (hydrate_node).data !== HYDRATION_END
) {
w.hydration_mismatch();
throw HYDRATION_ERROR;
}
} }
if (context) { if (context) {

@ -516,8 +516,7 @@ export class Renderer {
} }
let head = content.head + renderer.global.get_title(); let head = content.head + renderer.global.get_title();
let body = content.body;
const body = BLOCK_OPEN + content.body + BLOCK_CLOSE; // this inserts a fake boundary so hydration matches
for (const { hash, code } of renderer.global.css) { for (const { hash, code } of renderer.global.css) {
head += `<style id="${hash}">${code}</style>`; head += `<style id="${hash}">${code}</style>`;

@ -14,7 +14,7 @@ test('collects synchronous body content by default', () => {
const { head, body } = Renderer.render(component as unknown as Component); const { head, body } = Renderer.render(component as unknown as Component);
expect(head).toBe(''); expect(head).toBe('');
expect(body).toBe('<!--[--><!--[-->abc<!--]--><!--]-->'); expect(body).toBe('<!--[-->abc<!--]-->');
}); });
test('child type switches content area (head vs body)', () => { test('child type switches content area (head vs body)', () => {
@ -28,7 +28,7 @@ test('child type switches content area (head vs body)', () => {
const { head, body } = Renderer.render(component as unknown as Component); const { head, body } = Renderer.render(component as unknown as Component);
expect(head).toBe('<title>T</title>'); expect(head).toBe('<title>T</title>');
expect(body).toBe('<!--[--><!--[-->ab<!--]--><!--]-->'); expect(body).toBe('<!--[-->ab<!--]-->');
}); });
test('child inherits parent type when not specified', () => { test('child inherits parent type when not specified', () => {
@ -42,7 +42,7 @@ test('child inherits parent type when not specified', () => {
}; };
const { head, body } = Renderer.render(component as unknown as Component); const { head, body } = Renderer.render(component as unknown as Component);
expect(body).toBe('<!--[--><!--[--><!--]--><!--]-->'); expect(body).toBe('<!--[--><!--]-->');
expect(head).toBe('<meta name="x"/><style>/* css */</style>'); expect(head).toBe('<meta name="x"/><style>/* css */</style>');
}); });
@ -101,7 +101,7 @@ test('compact synchronously aggregates a range and can transform into head/body'
const { head, body } = Renderer.render(component as unknown as Component); const { head, body } = Renderer.render(component as unknown as Component);
expect(head).toBe('<h>H</h>'); expect(head).toBe('<h>H</h>');
expect(body).toBe('<!--[--><!--[-->abdc<!--]--><!--]-->'); expect(body).toBe('<!--[-->abdc<!--]-->');
}); });
test('local state is shallow-copied to children', () => { test('local state is shallow-copied to children', () => {
@ -216,7 +216,7 @@ describe('async', () => {
const result = await Renderer.render(component as unknown as Component); const result = await Renderer.render(component as unknown as Component);
expect(result.head).toBe(''); expect(result.head).toBe('');
expect(result.body).toBe('<!--[--><!--[-->123<!--]--><!--]-->'); expect(result.body).toBe('<!--[-->123<!--]-->');
expect(() => result.html).toThrow('html_deprecated'); expect(() => result.html).toThrow('html_deprecated');
}); });
@ -239,7 +239,7 @@ describe('async', () => {
const { body, head } = await Renderer.render(component as unknown as Component); const { body, head } = await Renderer.render(component as unknown as Component);
expect(head).toBe(''); expect(head).toBe('');
expect(body).toBe('<!--[--><!--[-->axb<!--]--><!--]-->'); expect(body).toBe('<!--[-->axb<!--]-->');
}); });
test('push accepts async functions in async context', async () => { test('push accepts async functions in async context', async () => {
@ -254,7 +254,7 @@ describe('async', () => {
const { head, body } = await Renderer.render(component as unknown as Component); const { head, body } = await Renderer.render(component as unknown as Component);
expect(head).toBe(''); expect(head).toBe('');
expect(body).toBe('<!--[--><!--[-->abc<!--]--><!--]-->'); expect(body).toBe('<!--[-->abc<!--]-->');
}); });
test('push handles async functions with different timing', async () => { test('push handles async functions with different timing', async () => {
@ -272,7 +272,7 @@ describe('async', () => {
const { head, body } = await Renderer.render(component as unknown as Component); const { head, body } = await Renderer.render(component as unknown as Component);
expect(head).toBe(''); expect(head).toBe('');
expect(body).toBe('<!--[--><!--[-->fastslowsync<!--]--><!--]-->'); expect(body).toBe('<!--[-->fastslowsync<!--]-->');
}); });
test('push async functions work with head content type', async () => { test('push async functions work with head content type', async () => {
@ -286,7 +286,7 @@ describe('async', () => {
}; };
const { head, body } = await Renderer.render(component as unknown as Component); const { head, body } = await Renderer.render(component as unknown as Component);
expect(body).toBe('<!--[--><!--[--><!--]--><!--]-->'); expect(body).toBe('<!--[--><!--]-->');
expect(head).toBe('<title>Async Title</title>'); expect(head).toBe('<title>Async Title</title>');
}); });
@ -305,7 +305,7 @@ describe('async', () => {
const { head, body } = await Renderer.render(component as unknown as Component); const { head, body } = await Renderer.render(component as unknown as Component);
expect(head).toBe(''); expect(head).toBe('');
expect(body).toBe('<!--[--><!--[-->start-async-child--end<!--]--><!--]-->'); expect(body).toBe('<!--[-->start-async-child--end<!--]-->');
}); });
test('push async functions work with compact operations', async () => { test('push async functions work with compact operations', async () => {
@ -324,7 +324,7 @@ describe('async', () => {
const { head, body } = await Renderer.render(component as unknown as Component); const { head, body } = await Renderer.render(component as unknown as Component);
expect(head).toBe(''); expect(head).toBe('');
expect(body).toBe('<!--[--><!--[-->ABC<!--]--><!--]-->'); expect(body).toBe('<!--[-->ABC<!--]-->');
}); });
test('push async functions are not supported in sync context', () => { test('push async functions are not supported in sync context', () => {

@ -1 +1 @@
<!--[--><!--[--><input> <p>Hello world!</p><!--]--><!--]--> <!--[--><input> <p>Hello world!</p><!--]-->

@ -1 +1 @@
<!--[--><!--[--><h1>Hello everybody!</h1><!--]--><!--]--> <!--[--><h1>Hello everybody!</h1><!--]-->

@ -1 +1 @@
<!--[--><!--[--><ul><!--[--><li>a</li><!--]--></ul> <ul><!--[--><li>a</li><!--]--></ul> <ul><!--[--><li>a</li><!--]--></ul> <!--[--><li>a</li> <li>a</li><!--]--> <!--[--><li>a</li> <li>a</li><!--]--> <!--[--><li>a</li> <li>a</li><!--]--><!--]--><!--]--> <!--[--><ul><!--[--><li>a</li><!--]--></ul> <ul><!--[--><li>a</li><!--]--></ul> <ul><!--[--><li>a</li><!--]--></ul> <!--[--><li>a</li> <li>a</li><!--]--> <!--[--><li>a</li> <li>a</li><!--]--> <!--[--><li>a</li> <li>a</li><!--]--><!--]-->

@ -1 +1 @@
<!--[--><!--[--><!--[!--><p>a</p><!--]--> <!--[--><p>empty</p><!--]--><!--]--><!--]--> <!--[--><!--[!--><p>a</p><!--]--> <!--[--><p>empty</p><!--]--><!--]-->

@ -1 +1 @@
<!--[--><!--[--><ul><!--[--><li>a</li><!--]--></ul> <ul><!--[--><li>a</li><!--]--></ul> <ul><!--[--><li>a</li><!--]--></ul> <!--[--><li>a</li> <li>a</li><!--]--> <!--[--><li>a</li> <li>a</li><!--]--> <!--[--><li>a</li> <li>a</li><!--]--><!--]--><!--]--> <!--[--><ul><!--[--><li>a</li><!--]--></ul> <ul><!--[--><li>a</li><!--]--></ul> <ul><!--[--><li>a</li><!--]--></ul> <!--[--><li>a</li> <li>a</li><!--]--> <!--[--><li>a</li> <li>a</li><!--]--> <!--[--><li>a</li> <li>a</li><!--]--><!--]-->

@ -1 +1 @@
<!--[--><!--[--><ul><!--[--><li>a</li><li>b</li><!--]--></ul> <ul><!--[--><li>a</li><li>b</li><!--]--></ul> <ul><!--[--><li>a</li><li>b</li><!--]--></ul> <!--[--><li>a</li> <li>a</li><li>b</li> <li>b</li><!--]--> <!--[--><li>a</li> <li>a</li><li>b</li> <li>b</li><!--]--> <!--[--><li>a</li> <li>a</li><li>b</li> <li>b</li><!--]--><!--]--><!--]--> <!--[--><ul><!--[--><li>a</li><li>b</li><!--]--></ul> <ul><!--[--><li>a</li><li>b</li><!--]--></ul> <ul><!--[--><li>a</li><li>b</li><!--]--></ul> <!--[--><li>a</li> <li>a</li><li>b</li> <li>b</li><!--]--> <!--[--><li>a</li> <li>a</li><li>b</li> <li>b</li><!--]--> <!--[--><li>a</li> <li>a</li><li>b</li> <li>b</li><!--]--><!--]-->

@ -1 +1 @@
<!--[--><!--[--><div class="bar"></div><!--]--><!--]--> <!--[--><div class="bar"></div><!--]-->

@ -1 +1 @@
<!--[--><!--[--><div class="bar"></div><!--]--><!--]--> <!--[--><div class="bar"></div><!--]-->

@ -1 +1 @@
<!--[--><!--[--><div></div> <!--[--><div></div> <div></div><!--]--><!--]--><!--]--> <!--[--><div></div> <!--[--><div></div> <div></div><!--]--><!--]-->

@ -1 +1 @@
<!--[--><!--[--><!--[--><!--]--> <div><!----><!----></div> hello<!--]--><!--]--> <!--[--><!--[--><!--]--> <div><!----><!----></div> hello<!--]-->

@ -1 +1 @@
<!--[--><!--[--><!--[!--><p>foo</p><!--]--><!--]--><!--]--> <!--[--><!--[!--><p>foo</p><!--]--><!--]-->

@ -1 +1 @@
<!--[--><!--[--><!---->.<input><!--]--><!--]--> <!--[--><!---->.<input><!--]-->

@ -1 +1 @@
<!--[--><!--[--><input type="text"><!--]--><!--]--> <!--[--><input type="text"><!--]-->

@ -1 +1 @@
<!--[--><!--[--><noscript>JavaScript is required for this site.</noscript> <h1>Hello!</h1><p>Count: 1</p><!--]--><!--]--> <!--[--><noscript>JavaScript is required for this site.</noscript> <h1>Hello!</h1><p>Count: 1</p><!--]-->

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

@ -1 +1 @@
<!--[--><!--[--><div></div><!--]--><!--]--> <!--[--><div></div><!--]-->

@ -1 +1 @@
<!--[--><!--[--><a href="/foo">foo</a> <a href="/foo">foo</a><!--]--><!--]--> <!--[--><a href="/foo">foo</a> <a href="/foo">foo</a><!--]-->

@ -1 +1 @@
<!--[--><!--[--><h1>call <a href="tel:+636-555-3226">+636-555-3226</a> now<span>!</span></h1><!--]--><!--]--> <!--[--><h1>call <a href="tel:+636-555-3226">+636-555-3226</a> now<span>!</span></h1><!--]-->

@ -1 +1 @@
<!--[--><!--[--><!----><script>{}<!----></script><!----><!--]--><!--]--> <!--[--><!----><script>{}<!----></script><!----><!--]-->

@ -1 +1 @@
<!--[--><!--[--><p>hydrated</p><!--]--><!--]--> <!--[--><p>hydrated</p><!--]-->

@ -1 +1 @@
<!--[--><!--[--><!--[--><p>child</p><!--]--> <!--[--><p>child</p><p>child</p><p>child</p><!--]--><!--]--><!--]--> <!--[--><!--[--><p>child</p><!--]--> <!--[--><p>child</p><p>child</p><p>child</p><!--]--><!--]-->

@ -1 +1 @@
<!--[--><!--[--><!--[--><p>thing</p><!--]--> <!--[--><p>thing</p><p>thing</p><p>thing</p><!--]--><!--]--><!--]--> <!--[--><!--[--><p>thing</p><!--]--> <!--[--><p>thing</p><p>thing</p><p>thing</p><!--]--><!--]-->

@ -1,2 +1,2 @@
<!-- unrelated comment --> <!-- unrelated comment -->
<!--[--><!--[--><!--[-->hello<!--]--><!--]--><!--]--> <!--[--><!--[-->hello<!--]--><!--]-->

@ -1,2 +1,2 @@
<!-- unrelated comment --> <!-- unrelated comment -->
<!--[--><!--[--><!--[-->hello<!--]--><!--]--><!--]--> <!--[--><!--[-->hello<!--]--><!--]-->

@ -1 +1 @@
<!--[--><!--[--><span><span></span></span><!--]--><!--]--> <!--[--><span><span></span></span><!--]-->

@ -1 +1 @@
<!--[--><!--[--><!---->x<!--]--><!--]--> <!--[--><!---->x<!--]-->

@ -1,2 +1,2 @@
<!--[--><!--[--> <!--[-->
<main><p>nested</p><!----></main><!--]--><!--]--> <main><p>nested</p><!----></main><!--]-->

@ -4,7 +4,7 @@ export default test({
withoutNormalizeHtml: true, withoutNormalizeHtml: true,
// Unable to test `html` with `<textarea>` content // Unable to test `html` with `<textarea>` content
// as the textarea#value will not show within `innerHtml` // as the textarea#value will not show within `innerHtml`
ssrHtml: `<!--[--><!--[--><textarea id="textarea"> A ssrHtml: `<!--[--><textarea id="textarea"> A
B B
</textarea> <div id="div-with-textarea"><textarea> A </textarea> <div id="div-with-textarea"><textarea> A
B B
@ -14,7 +14,7 @@ newline after leading space</textarea></div> <textarea id="textarea-with-multipl
multiple leading newlines</textarea> <div id="div-with-textarea-with-multiple-leading-newlines"><textarea> multiple leading newlines</textarea> <div id="div-with-textarea-with-multiple-leading-newlines"><textarea>
multiple leading newlines</textarea></div><!--]--><!--]-->`, multiple leading newlines</textarea></div><!--]-->`,
test({ assert, target }) { test({ assert, target }) {
// Test for <textarea> tag // Test for <textarea> tag
const elementTextarea = /** @type {HTMLTextAreaElement} */ (target.querySelector('#textarea')); const elementTextarea = /** @type {HTMLTextAreaElement} */ (target.querySelector('#textarea'));

@ -1 +1 @@
<!--[--><!--[--><p>before</p><!-- a comment --><p>after</p><!--]--><!--]--> <!--[--><p>before</p><!-- a comment --><p>after</p><!--]-->

@ -1 +1 @@
<!--[--><!--[--><div>Just a dummy page.</div><!--]--><!--]--> <!--[--><div>Just a dummy page.</div><!--]-->

@ -1,5 +1,4 @@
<!--[--> <!--[-->
<!--[-->
<!----> <!---->
<title>lorem</title> <title>lorem</title>
<!----> <!---->
@ -16,4 +15,3 @@
</script> </script>
<!----> <!---->
<!--]--> <!--]-->
<!--]-->

Loading…
Cancel
Save