From 94da90ff15ef8288aa5b490bf938b67bf3cbf627 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.ts | 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 b21a1aa24b..c3d9f9c560 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -374,7 +374,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 d10c165198..1948b4e3ea 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -6,6 +6,7 @@ import Element from '../../nodes/Element'; import { x } from 'code-red'; import Expression from '../../nodes/shared/Expression'; import remove_whitespace_children from './utils/remove_whitespace_children'; +import fix_attribute_casing from '../../render_dom/wrappers/Element/fix_attribute_casing'; export default function(node: Element, renderer: Renderer, options: RenderOptions) { @@ -45,16 +46,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)} }`); } } }); @@ -67,7 +68,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 && @@ -75,17 +76,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.ts b/test/runtime/index.ts index 153ac9dbb8..265c2285ff 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -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 d16cb5f96b..63ecefb221 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 19aa33d65e..fa00cb6bc3 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 3f82e8b601..397d8c5684 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']);