From c8232107a6d34ebfcbab723e60c57fda1f308475 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 21 Feb 2020 09:23:03 +0800 Subject: [PATCH] fix some hydration bug, more to go --- .../render_dom/wrappers/Element/index.ts | 2 +- .../compile/render_ssr/handlers/Element.ts | 17 +++++++++-------- test/runtime/index.js | 15 ++++++++++++++- .../attribute-boolean-indeterminate/_config.js | 4 ++++ .../samples/attribute-dynamic-type/_config.js | 3 +-- test/runtime/samples/ondestroy-deep/_config.js | 3 +++ 6 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 9291f329b6..e9fac59869 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -424,7 +424,7 @@ export default class ElementWrapper extends Wrapper { get_claim_statement(nodes: Identifier) { const attributes = this.node.attributes .filter((attr) => attr.type === 'Attribute') - .map((attr) => p`${attr.name}: true`); + .map((attr) => p`${fix_attribute_casing(attr.name)}: true`); const name = this.node.namespace ? this.node.name diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 6e6a61974a..20abb07ee2 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -6,6 +6,7 @@ import Renderer, { RenderOptions } from '../Renderer'; import Element from '../../nodes/Element'; import { x } from 'code-red'; import Expression from '../../nodes/shared/Expression'; +import fix_attribute_casing from '../../render_dom/wrappers/Element/fix_attribute_casing'; export default function(node: Element, renderer: Renderer, options: RenderOptions & { slot_scopes: Map; @@ -51,16 +52,16 @@ export default function(node: Element, renderer: Renderer, options: RenderOption if (name === 'value' && node.name.toLowerCase() === 'textarea') { node_contents = get_attribute_value(attribute); } else if (attribute.is_true) { - args.push(x`{ ${attribute.name}: true }`); + args.push(x`{ ${fix_attribute_casing(attribute.name)}: true }`); } else if ( boolean_attributes.has(name) && attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text' ) { // a boolean attribute with one non-Text chunk - args.push(x`{ ${attribute.name}: ${(attribute.chunks[0] as Expression).node} || null }`); + args.push(x`{ ${fix_attribute_casing(attribute.name)}: ${(attribute.chunks[0] as Expression).node} || null }`); } else { - args.push(x`{ ${attribute.name}: ${get_attribute_value(attribute)} }`); + args.push(x`{ ${fix_attribute_casing(attribute.name)}: ${get_attribute_value(attribute)} }`); } } }); @@ -73,7 +74,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption if (name === 'value' && node.name.toLowerCase() === 'textarea') { node_contents = get_attribute_value(attribute); } else if (attribute.is_true) { - renderer.add_string(` ${attribute.name}`); + renderer.add_string(` ${fix_attribute_casing(attribute.name)}`); } else if ( boolean_attributes.has(name) && attribute.chunks.length === 1 && @@ -81,17 +82,17 @@ export default function(node: Element, renderer: Renderer, options: RenderOption ) { // a boolean attribute with one non-Text chunk renderer.add_string(` `); - renderer.add_expression(x`${(attribute.chunks[0] as Expression).node} ? "${attribute.name}" : ""`); + renderer.add_expression(x`${(attribute.chunks[0] as Expression).node} ? "${fix_attribute_casing(attribute.name)}" : ""`); } else if (name === 'class' && class_expression) { add_class_attribute = false; - renderer.add_string(` ${attribute.name}="`); + renderer.add_string(` ${fix_attribute_casing(attribute.name)}="`); renderer.add_expression(x`[${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim()`); renderer.add_string(`"`); } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { const snippet = (attribute.chunks[0] as Expression).node; - renderer.add_expression(x`@add_attribute("${attribute.name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`); + renderer.add_expression(x`@add_attribute("${fix_attribute_casing(attribute.name)}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`); } else { - renderer.add_string(` ${attribute.name}="`); + renderer.add_string(` ${fix_attribute_casing(attribute.name)}="`); renderer.add_expression((name === 'class' ? get_class_attribute_value : get_attribute_value)(attribute)); renderer.add_string(`"`); } diff --git a/test/runtime/index.js b/test/runtime/index.js index f070eb8185..c02226c14a 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -153,6 +153,17 @@ describe("runtime", () => { const target = window.document.querySelector("main"); + if (hydrate) { + // ssr into target + compileOptions.generate = 'ssr'; + cleanRequireCache(); + const SsrSvelteComponent = require(`./samples/${dir}/main.svelte`).default; + const { html } = SsrSvelteComponent.render(config.props); + target.innerHTML = html; + + delete compileOptions.generate; + } + const warnings = []; const warn = console.warn; console.warn = warning => { @@ -182,7 +193,9 @@ describe("runtime", () => { throw new Error("Received unexpected warnings"); } - if (config.html) { + if (hydrate && config.ssrHtml) { + assert.htmlEqual(target.innerHTML, config.ssrHtml); + } else if (config.html) { assert.htmlEqual(target.innerHTML, config.html); } diff --git a/test/runtime/samples/attribute-boolean-indeterminate/_config.js b/test/runtime/samples/attribute-boolean-indeterminate/_config.js index 0e24f92c51..e680abdbfb 100644 --- a/test/runtime/samples/attribute-boolean-indeterminate/_config.js +++ b/test/runtime/samples/attribute-boolean-indeterminate/_config.js @@ -11,6 +11,10 @@ export default { `, + // somehow ssr will render indeterminate="" + // the hydrated html will still contain that attribute + ssrHtml: ``, + test({ assert, component, target }) { const input = target.querySelector('input'); diff --git a/test/runtime/samples/attribute-dynamic-type/_config.js b/test/runtime/samples/attribute-dynamic-type/_config.js index 92f6ce196d..b125c1cdb8 100644 --- a/test/runtime/samples/attribute-dynamic-type/_config.js +++ b/test/runtime/samples/attribute-dynamic-type/_config.js @@ -1,12 +1,11 @@ export default { - skip_if_ssr: true, - props: { inputType: 'text', inputValue: 42 }, html: ``, + ssrHtml: ``, test({ assert, component, target }) { const input = target.querySelector('input'); diff --git a/test/runtime/samples/ondestroy-deep/_config.js b/test/runtime/samples/ondestroy-deep/_config.js index 4431a2e163..ff623ff78c 100644 --- a/test/runtime/samples/ondestroy-deep/_config.js +++ b/test/runtime/samples/ondestroy-deep/_config.js @@ -2,6 +2,9 @@ import { destroyed, reset } from './destroyed.js'; export default { test({ assert, component }) { + // for hydration, ssr may have pushed to `destroyed` + reset(); + component.visible = false; assert.deepEqual(destroyed, ['A', 'B', 'C']);