diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 6ee253b1c3..3e6c5d6368 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -1,269 +1,240 @@ -import { is_void } from '../../../../shared/utils/names'; -import { - get_attribute_expression, - get_attribute_value, - get_class_attribute_value -} from './shared/get_attribute_value'; +import { is_void } from '../../../../shared/utils/names.js'; +import { get_attribute_expression, get_attribute_value, get_class_attribute_value } from './shared/get_attribute_value'; import { boolean_attributes } from '../../../../shared/boolean_attributes'; import { is_name_contenteditable, is_contenteditable } from '../../utils/contenteditable'; -import Renderer, { RenderOptions } from '../Renderer'; -import Binding from '../../nodes/Binding'; -import Element from '../../nodes/Element'; import { p, 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'; import { namespaces } from '../../../utils/namespaces'; import { regex_starts_with_newline } from '../../../utils/patterns'; -import { Node, Expression as ESExpression } from 'estree'; -export default function (node: Element, renderer: Renderer, options: RenderOptions) { - const children = remove_whitespace_children(node.children, node.next); - - // awkward special case - let node_contents; - - const contenteditable = is_contenteditable(node); - - if (node.is_dynamic_element) { - renderer.push(); - } - - renderer.add_string('<'); - add_tag_name(); - - const class_expression_list = node.classes.map((class_directive) => { - const { expression, name } = class_directive; - const snippet = expression ? expression.node : x`#ctx.${name}`; // TODO is this right? - return x`${snippet} ? "${name}" : ""`; - }); - if (node.needs_manual_style_scoping) { - class_expression_list.push(x`"${node.component.stylesheet.id}"`); - } - const class_expression = - class_expression_list.length > 0 && - class_expression_list.reduce((lhs, rhs) => x`${lhs} + ' ' + ${rhs}`); - - const style_expression_list = node.styles.map((style_directive) => { - let { - name, - important, - expression: { node: expression } - } = style_directive; - if (important) { - expression = x`${expression} + ' !important'`; - } - return p`"${name}": ${expression}`; - }); - - const style_expression = style_expression_list.length > 0 && x`{ ${style_expression_list} }`; - - if (node.attributes.some((attr) => attr.is_spread)) { - // TODO dry this out - const args = []; - node.attributes.forEach((attribute) => { - if (attribute.is_spread) { - args.push(x`@escape_object(${attribute.expression.node})`); - } else { - const attr_name = - node.namespace === namespaces.foreign - ? attribute.name - : fix_attribute_casing(attribute.name); - const name = attribute.name.toLowerCase(); - if (name === 'value' && node.name.toLowerCase() === 'textarea') { - node_contents = get_attribute_value(attribute); - } else if (attribute.is_true) { - args.push(x`{ ${attr_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`{ ${attr_name}: ${(attribute.chunks[0] as Expression).node} || null }`); - } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { - const snippet = (attribute.chunks[0] as Expression).node; - args.push(x`{ ${attr_name}: @escape_attribute_value(${snippet}) }`); - } else { - args.push(x`{ ${attr_name}: ${get_attribute_value(attribute)} }`); - } - } - }); - - renderer.add_expression( - x`@spread([${args}], { classes: ${class_expression}, styles: ${style_expression} })` - ); - } else { - let add_class_attribute = !!class_expression; - let add_style_attribute = !!style_expression; - node.attributes.forEach((attribute) => { - const name = attribute.name.toLowerCase(); - const attr_name = - node.namespace === namespaces.foreign - ? attribute.name - : fix_attribute_casing(attribute.name); - if (name === 'value' && node.name.toLowerCase() === 'textarea') { - node_contents = get_attribute_value(attribute); - } else if (attribute.is_true) { - renderer.add_string(` ${attr_name}`); - } else if ( - boolean_attributes.has(name) && - attribute.chunks.length === 1 && - attribute.chunks[0].type !== 'Text' - ) { - // a boolean attribute with one non-Text chunk - renderer.add_string(' '); - renderer.add_expression( - x`${(attribute.chunks[0] as Expression).node} ? "${attr_name}" : ""` - ); - } else if (name === 'class' && class_expression) { - add_class_attribute = false; - renderer.add_string(` ${attr_name}="`); - renderer.add_expression( - x`[${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim()` - ); - renderer.add_string('"'); - } else if (name === 'style' && style_expression) { - add_style_attribute = false; - renderer.add_expression( - x`@add_styles(@merge_ssr_styles(${get_attribute_value(attribute)}, ${style_expression}))` - ); - } 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("${attr_name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})` - ); - } else { - renderer.add_string(` ${attr_name}="`); - renderer.add_expression( - (name === 'class' ? get_class_attribute_value : get_attribute_value)(attribute) - ); - renderer.add_string('"'); - } - }); - if (add_class_attribute) { - renderer.add_expression(x`@add_classes((${class_expression}).trim())`); - } - if (add_style_attribute) { - renderer.add_expression(x`@add_styles(${style_expression})`); - } - } - - node.bindings.forEach((binding: Binding) => { - const { name, expression } = binding; - - if (binding.is_readonly) { - return; - } - - if (name === 'group') { - const value_attribute = node.attributes.find(({ name }) => name === 'value'); - if (value_attribute) { - const value = get_attribute_expression(value_attribute); - const type = node.get_static_attribute_value('type'); - const bound = expression.node; - const condition = - type === 'checkbox' ? x`~${bound}.indexOf(${value})` : x`${value} === ${bound}`; - renderer.add_expression(x`${condition} ? @add_attribute("checked", true, 1) : ""`); - } - } else if (contenteditable && is_name_contenteditable(name)) { - node_contents = expression.node; - - // TODO where was this used? - // value = name === 'textContent' ? x`@escape($$value)` : x`$$value`; - } else if (binding.name === 'value' && node.name === 'textarea') { - const snippet = expression.node; - node_contents = x`${snippet} || ""`; - } else if (binding.name === 'value' && node.name === 'select') { - // NOTE: do not add "value" attribute on