fix: @html mismatch in hydration

raw-hydration-mismatch
gtmnayan 2 years ago
parent abf257306b
commit 508e30c681

@ -1,7 +1 @@
<script>
import Counter from "./lib/Counter.svelte";
</script>
<div>
Hello world!
</div>
{@html false ? 'foo' : 'bar'}

@ -69,11 +69,12 @@ const watcher = watch([
async generateBundle(_, bundle) {
const result = bundle['entry-server.js'];
const mod = (0, eval)(result.code);
const { html } = mod.render();
const { html, head } = mod.render();
writeFileSync(
'dist/index.html',
readFileSync('src/template.html', 'utf-8')
.replace('<!--app-head-->', head)
.replace('<!--app-html-->', html)
.replace('<!--app-title-->', svelte.VERSION)
);

@ -47,7 +47,7 @@ export default class RawMustacheTagWrapper extends Tag {
block.chunks.create.push(b`${html_tag} = new @HtmlTag(${is_svg ? 'true' : 'false'});`);
if (this.renderer.options.hydratable) {
block.chunks.claim.push(
b`${html_tag} = @claim_html_tag(${_parent_nodes}, ${is_svg ? 'true' : 'false'});`
b`${html_tag} = @claim_html_tag(${_parent_nodes}, ${is_svg ? 'true' : 'false'}, ${init});`
);
}
block.chunks.hydrate.push(b`${html_tag}.a = ${update_anchor};`);

@ -1,10 +1,19 @@
import { x } from 'code-red';
/**
* @param {import('../../nodes/RawMustacheTag.js').default} node
* @param {import('../Renderer.js').default} renderer
* @param {import('../private.js').RenderOptions} options
*/
export default function (node, renderer, options) {
if (options.hydratable) renderer.add_string('<!-- HTML_TAG_START -->');
if (!options.hydratable) {
renderer.add_expression(/** @type {import('estree').Expression} */ (node.expression.node));
if (options.hydratable) renderer.add_string('<!-- HTML_TAG_END -->');
} else {
renderer.add_expression(x`(() => {
const #html_string = ${node.expression.node} + '';
const #hash = /* @__PURE__ */ @hash(#html_string);
return \`<!-- HTML_\${#hash}_START -->\${#html_string}<!-- HTML_\${#hash}_END -->\`;
})()`);
}
}

@ -1,5 +1,5 @@
import { ResizeObserverSingleton } from './ResizeObserverSingleton.js';
import { contenteditable_truthy_values, has_prop } from './utils.js';
import { contenteditable_truthy_values, has_prop, hash } from './utils.js';
// Track which nodes are claimed during hydration. Unclaimed nodes can then be removed from the DOM
// at the end of hydration without touching the remaining nodes.
let is_hydrating = false;
@ -789,11 +789,13 @@ function get_comment_idx(nodes, text, start) {
* @param {boolean} is_svg
* @returns {HtmlTagHydration}
*/
export function claim_html_tag(nodes, is_svg) {
export function claim_html_tag(nodes, is_svg, content) {
// find html opening tag
const start_index = get_comment_idx(nodes, 'HTML_TAG_START', 0);
const end_index = get_comment_idx(nodes, 'HTML_TAG_END', start_index + 1);
if (start_index === -1 || end_index === -1) {
const content_hash = hash(content + '');
const start_index = get_comment_idx(nodes, `HTML_${content_hash}_START`, 0);
const end_index = get_comment_idx(nodes, `HTML_${content_hash}_END`, start_index + 1);
if (start_index === -1 || end_index === -1) { // Content mismatch, recreate
return new HtmlTagHydration(is_svg);
}

@ -1,5 +1,6 @@
import { append_empty_stylesheet, detach, get_root_for_style } from './dom.js';
import { raf } from './environment.js';
import { hash } from './utils.js';
// we need to store the information for multiple documents because a Svelte application could also contain iframes
// https://github.com/sveltejs/svelte/issues/3624
@ -8,18 +9,6 @@ const managed_styles = new Map();
let active = 0;
// https://github.com/darkskyapp/string-hash/blob/master/index.js
/**
* @param {string} str
* @returns {number}
*/
function hash(str) {
let hash = 5381;
let i = str.length;
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
return hash >>> 0;
}
/**
* @param {Document | ShadowRoot} doc
* @param {Element & ElementCSSInlineStyle} node

@ -289,3 +289,15 @@ export function split_css_unit(value) {
}
export const contenteditable_truthy_values = ['', true, 1, 'true', 'contenteditable'];
// https://github.com/darkskyapp/string-hash/blob/master/index.js
/**
* @param {string} str
* @returns {number}
*/
export function hash(str) {
let hash = 5381;
let i = str.length;
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
return hash >>> 0;
}

@ -1,8 +1,8 @@
<p><!-- HTML_TAG_START --></p>
<p><!-- HTML_2920328455_START --></p>
<p>invalid</p>
<!-- HTML_TAG_END -->
<!-- HTML_2920328455_END -->
<p></p>
<p><!-- HTML_TAG_START --></p>
<p><!-- HTML_2920328455_START --></p>
<p>invalid</p>
<!-- HTML_TAG_END -->
<!-- HTML_2920328455_END -->
<p></p>

@ -1,5 +1,5 @@
<svg>
<!-- HTML_TAG_START -->
<!-- HTML_1284501889_START -->
<circle cx="200" cy="500" r="200"></circle>
<!-- HTML_TAG_END -->
<!-- HTML_1284501889_END -->
</svg>

Before

Width:  |  Height:  |  Size: 106 B

After

Width:  |  Height:  |  Size: 120 B

@ -1,4 +1,4 @@
<!-- HTML_TAG_START -->
<!-- HTML_2526333745_START -->
<p>this is some html</p>
<p>and so is this</p>
<!-- HTML_TAG_END -->
<!-- HTML_2526333745_END -->

Loading…
Cancel
Save