do not render content for null/undefined and void element in ssr

pull/7453/head
tanhauhau 4 years ago committed by baseballyama
parent f13d07bd5c
commit 532413d484

@ -277,11 +277,9 @@ export default class ElementWrapper extends Wrapper {
const tag = this.node.tag_expr.manipulate(block);
block.add_variable(previous_tag, tag);
const has_children_of_dynamic_element = block.get_unique_name('has_children_of_dynamic_element');
block.add_variable(has_children_of_dynamic_element, x`${this.node.children.length > 0 ? 'true' : 'false'}`);
block.chunks.init.push(b`
${this.renderer.options.dev && b`@validate_dynamic_element(${tag}, ${has_children_of_dynamic_element});`}
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
${this.renderer.options.dev && this.node.children.length > 0 && b`@validate_void_dynamic_element(${tag});`}
let ${this.var} = ${tag} && ${this.child_dynamic_element_block.name}(#ctx);
`);
@ -312,7 +310,8 @@ export default class ElementWrapper extends Wrapper {
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
} else if (${not_equal}(${previous_tag}, ${tag})) {
${this.var}.d(1);
${this.renderer.options.dev && b`@validate_dynamic_element(${tag}, ${has_children_of_dynamic_element});`}
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
${this.renderer.options.dev && this.node.children.length > 0 && b`@validate_void_dynamic_element(${tag});`}
${this.var} = ${this.child_dynamic_element_block.name}(#ctx);
${this.var}.c();
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});

@ -9,7 +9,7 @@ import remove_whitespace_children from './utils/remove_whitespace_children';
import fix_attribute_casing from '../../render_dom/wrappers/Element/fix_attribute_casing';
import { namespaces } from '../../../utils/namespaces';
import { start_newline } from '../../../utils/patterns';
import { Expression as ESExpression } from 'estree';
import { Node, Expression as ESExpression } from 'estree';
export default function (node: Element, renderer: Renderer, options: RenderOptions) {
@ -24,6 +24,10 @@ export default function (node: Element, renderer: Renderer, options: RenderOptio
node.attributes.some((attribute) => attribute.name === 'contenteditable')
);
if (node.is_dynamic_element) {
renderer.push();
}
renderer.add_string('<');
add_tag_name();
@ -192,10 +196,24 @@ export default function (node: Element, renderer: Renderer, options: RenderOptio
renderer.add_string('\n');
}
}
if (node.is_dynamic_element) renderer.push();
renderer.render(children, options);
if (node.is_dynamic_element) {
const children = renderer.pop();
renderer.add_expression(x`@is_void(#tag) ? '' : ${children}`);
}
add_close_tag();
}
if (node.is_dynamic_element) {
let content: Node = renderer.pop();
if (options.dev && node.children.length > 0) content = x`(() => { @validate_void_dynamic_element(#tag); return ${content}; })()`;
renderer.add_expression(x`((#tag) => {
${options.dev && x`@validate_dynamic_element(#tag)`}
return #tag ? ${content} : '';
})(${node.tag_expr.node})`);
}
function add_close_tag() {
if (node.tag_expr.node.type === 'Literal') {
if (!is_void(node.tag_expr.node.value as string)) {
@ -205,7 +223,7 @@ export default function (node: Element, renderer: Renderer, options: RenderOptio
}
return;
}
renderer.add_expression(x`((tag) => @is_void(String(tag || '')) ? '' : \`</\${tag}>\`)(${node.tag_expr.node})`);
renderer.add_expression(x`@is_void(#tag) ? '' : \`</\${#tag}>\``);
}
function add_tag_name() {

@ -108,13 +108,16 @@ export function validate_slots(name, slot, keys) {
}
}
export function validate_dynamic_element(tag: unknown, has_children: boolean) {
export function validate_dynamic_element(tag: unknown) {
const is_string = typeof tag === 'string';
if (tag && !is_string) {
throw new Error('<svelte:element> expects "this" attribute to be a string.');
}
if (is_string && is_void(tag as string) && has_children) {
throw new Error(`<${tag}> element is self-closing and cannot have content.`);
}
export function validate_void_dynamic_element(tag: undefined | string) {
if (tag && is_void(tag)) {
throw new Error(`<svelte:element this="${tag}"> is self-closing and cannot have content.`);
}
}

@ -5,5 +5,5 @@ export default {
props: {
tag: 'br'
},
error: '<br> element is self-closing and cannot have content.'
error: '<svelte:element this="br"> is self-closing and cannot have content.'
};

@ -5,5 +5,5 @@ export default {
props: {
tag: 'br'
},
error: '<br> element is self-closing and cannot have content.'
error: '<svelte:element this="br"> is self-closing and cannot have content.'
};

@ -5,5 +5,5 @@ export default {
props: {
tag: 'br'
},
error: '<br> element is self-closing and cannot have content.'
error: '<svelte:element this="br"> is self-closing and cannot have content.'
};

@ -0,0 +1,6 @@
export default {
props: {
tag: 'br'
},
html: '<br>'
};

@ -0,0 +1,6 @@
<script>
import Nested from './Nested.svelte';
export let tag;
</script>
<svelte:element this='{tag}'><Nested/></svelte:element>
Loading…
Cancel
Save