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']);