Lint and format src/compiler/compile/render_ssr/handlers/Element.js

pull/8569/head
Simon Holthausen 3 years ago
parent 94c42c52f2
commit 960c7294c8

@ -1,5 +1,9 @@
import { is_void } from '../../../../shared/utils/names.js'; 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 {
get_attribute_expression,
get_attribute_value,
get_class_attribute_value
} from './shared/get_attribute_value';
import { boolean_attributes } from '../../../../shared/boolean_attributes'; import { boolean_attributes } from '../../../../shared/boolean_attributes';
import { is_name_contenteditable, is_contenteditable } from '../../utils/contenteditable'; import { is_name_contenteditable, is_contenteditable } from '../../utils/contenteditable';
import { p, x } from 'code-red'; import { p, x } from 'code-red';
@ -14,227 +18,233 @@ import { regex_starts_with_newline } from '../../../utils/patterns';
* @param {import('../Renderer.js').RenderOptions} options * @param {import('../Renderer.js').RenderOptions} options
*/ */
export default function (node, renderer, options) { export default function (node, renderer, options) {
const children = remove_whitespace_children(node.children, node.next); const children = remove_whitespace_children(node.children, node.next);
// awkward special case // awkward special case
let node_contents; let node_contents;
const contenteditable = is_contenteditable(node); const contenteditable = is_contenteditable(node);
if (node.is_dynamic_element) { if (node.is_dynamic_element) {
renderer.push(); renderer.push();
} }
renderer.add_string('<'); renderer.add_string('<');
add_tag_name(); add_tag_name();
const class_expression_list = node.classes.map((class_directive) => { const class_expression_list = node.classes.map((class_directive) => {
const { expression, name } = class_directive; const { expression, name } = class_directive;
const snippet = expression ? expression.node : x `#ctx.${name}`; // TODO is this right? const snippet = expression ? expression.node : x`#ctx.${name}`; // TODO is this right?
return x `${snippet} ? "${name}" : ""`; return x`${snippet} ? "${name}" : ""`;
}); });
if (node.needs_manual_style_scoping) { if (node.needs_manual_style_scoping) {
class_expression_list.push(x `"${node.component.stylesheet.id}"`); class_expression_list.push(x`"${node.component.stylesheet.id}"`);
} }
const class_expression = class_expression_list.length > 0 && const class_expression =
class_expression_list.reduce((lhs, rhs) => x `${lhs} + ' ' + ${rhs}`); class_expression_list.length > 0 &&
const style_expression_list = node.styles.map((style_directive) => { class_expression_list.reduce((lhs, rhs) => x`${lhs} + ' ' + ${rhs}`);
let { name, important, expression: { node: expression } } = style_directive; const style_expression_list = node.styles.map((style_directive) => {
if (important) { let {
expression = x `${expression} + ' !important'`; name,
} important,
return p `"${name}": ${expression}`; expression: { node: expression }
}); } = style_directive;
const style_expression = style_expression_list.length > 0 && x `{ ${style_expression_list} }`; if (important) {
if (node.attributes.some((attr) => attr.is_spread)) { expression = x`${expression} + ' !important'`;
// TODO dry this out }
const args = []; return p`"${name}": ${expression}`;
node.attributes.forEach((attribute) => { });
if (attribute.is_spread) { const style_expression = style_expression_list.length > 0 && x`{ ${style_expression_list} }`;
args.push(x `@escape_object(${attribute.expression.node})`); if (node.attributes.some((attr) => attr.is_spread)) {
} // TODO dry this out
else { const args = [];
const attr_name = node.namespace === namespaces.foreign node.attributes.forEach((attribute) => {
? attribute.name if (attribute.is_spread) {
: fix_attribute_casing(attribute.name); args.push(x`@escape_object(${attribute.expression.node})`);
const name = attribute.name.toLowerCase(); } else {
if (name === 'value' && node.name.toLowerCase() === 'textarea') { const attr_name =
node_contents = get_attribute_value(attribute); node.namespace === namespaces.foreign
} ? attribute.name
else if (attribute.is_true) { : fix_attribute_casing(attribute.name);
args.push(x `{ ${attr_name}: true }`); const name = attribute.name.toLowerCase();
} if (name === 'value' && node.name.toLowerCase() === 'textarea') {
else if (boolean_attributes.has(name) && node_contents = get_attribute_value(attribute);
attribute.chunks.length === 1 && } else if (attribute.is_true) {
attribute.chunks[0].type !== 'Text') { args.push(x`{ ${attr_name}: true }`);
// a boolean attribute with one non-Text chunk } else if (
args.push(x `{ ${attr_name}: ${( /** @type {Expression} */(attribute.chunks[0])).node} || null }`); boolean_attributes.has(name) &&
} attribute.chunks.length === 1 &&
else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { attribute.chunks[0].type !== 'Text'
const snippet = ( /** @type {Expression} */(attribute.chunks[0])).node; ) {
args.push(x `{ ${attr_name}: @escape_attribute_value(${snippet}) }`); // a boolean attribute with one non-Text chunk
} args.push(
else { x`{ ${attr_name}: ${/** @type {Expression} */ (attribute.chunks[0]).node} || null }`
args.push(x `{ ${attr_name}: ${get_attribute_value(attribute)} }`); );
} } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') {
} const snippet = /** @type {Expression} */ (attribute.chunks[0]).node;
}); args.push(x`{ ${attr_name}: @escape_attribute_value(${snippet}) }`);
renderer.add_expression(x `@spread([${args}], { classes: ${class_expression}, styles: ${style_expression} })`); } else {
} args.push(x`{ ${attr_name}: ${get_attribute_value(attribute)} }`);
else { }
let add_class_attribute = !!class_expression; }
let add_style_attribute = !!style_expression; });
node.attributes.forEach((attribute) => { renderer.add_expression(
const name = attribute.name.toLowerCase(); x`@spread([${args}], { classes: ${class_expression}, styles: ${style_expression} })`
const attr_name = node.namespace === namespaces.foreign );
? attribute.name } else {
: fix_attribute_casing(attribute.name); let add_class_attribute = !!class_expression;
if (name === 'value' && node.name.toLowerCase() === 'textarea') { let add_style_attribute = !!style_expression;
node_contents = get_attribute_value(attribute); node.attributes.forEach((attribute) => {
} const name = attribute.name.toLowerCase();
else if (attribute.is_true) { const attr_name =
renderer.add_string(` ${attr_name}`); node.namespace === namespaces.foreign
} ? attribute.name
else if (boolean_attributes.has(name) && : fix_attribute_casing(attribute.name);
attribute.chunks.length === 1 && if (name === 'value' && node.name.toLowerCase() === 'textarea') {
attribute.chunks[0].type !== 'Text') { node_contents = get_attribute_value(attribute);
// a boolean attribute with one non-Text chunk } else if (attribute.is_true) {
renderer.add_string(' '); renderer.add_string(` ${attr_name}`);
renderer.add_expression(x `${( /** @type {Expression} */(attribute.chunks[0])).node} ? "${attr_name}" : ""`); } else if (
} boolean_attributes.has(name) &&
else if (name === 'class' && class_expression) { attribute.chunks.length === 1 &&
add_class_attribute = false; attribute.chunks[0].type !== 'Text'
renderer.add_string(` ${attr_name}="`); ) {
renderer.add_expression(x `[${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim()`); // a boolean attribute with one non-Text chunk
renderer.add_string('"'); renderer.add_string(' ');
} renderer.add_expression(
else if (name === 'style' && style_expression) { x`${/** @type {Expression} */ (attribute.chunks[0]).node} ? "${attr_name}" : ""`
add_style_attribute = false; );
renderer.add_expression(x `@add_styles(@merge_ssr_styles(${get_attribute_value(attribute)}, ${style_expression}))`); } else if (name === 'class' && class_expression) {
} add_class_attribute = false;
else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { renderer.add_string(` ${attr_name}="`);
const snippet = ( /** @type {Expression} */(attribute.chunks[0])).node; renderer.add_expression(
renderer.add_expression(x `@add_attribute("${attr_name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`); x`[${get_class_attribute_value(attribute)}, ${class_expression}].join(' ').trim()`
} );
else { renderer.add_string('"');
renderer.add_string(` ${attr_name}="`); } else if (name === 'style' && style_expression) {
renderer.add_expression((name === 'class' ? get_class_attribute_value : get_attribute_value)(attribute)); add_style_attribute = false;
renderer.add_string('"'); renderer.add_expression(
} x`@add_styles(@merge_ssr_styles(${get_attribute_value(attribute)}, ${style_expression}))`
}); );
if (add_class_attribute) { } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') {
renderer.add_expression(x `@add_classes((${class_expression}).trim())`); const snippet = /** @type {Expression} */ (attribute.chunks[0]).node;
} renderer.add_expression(
if (add_style_attribute) { x`@add_attribute("${attr_name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`
renderer.add_expression(x `@add_styles(${style_expression})`); );
} } else {
} renderer.add_string(` ${attr_name}="`);
node.bindings.forEach((binding) => { renderer.add_expression(
const { name, expression } = binding; (name === 'class' ? get_class_attribute_value : get_attribute_value)(attribute)
if (binding.is_readonly) { );
return; renderer.add_string('"');
} }
if (name === 'group') { });
const value_attribute = node.attributes.find(({ name }) => name === 'value'); if (add_class_attribute) {
if (value_attribute) { renderer.add_expression(x`@add_classes((${class_expression}).trim())`);
const value = get_attribute_expression(value_attribute); }
const type = node.get_static_attribute_value('type'); if (add_style_attribute) {
const bound = expression.node; renderer.add_expression(x`@add_styles(${style_expression})`);
const condition = type === 'checkbox' ? x `~${bound}.indexOf(${value})` : x `${value} === ${bound}`; }
renderer.add_expression(x `${condition} ? @add_attribute("checked", true, 1) : ""`); }
} node.bindings.forEach((binding) => {
} const { name, expression } = binding;
else if (contenteditable && is_name_contenteditable(name)) { if (binding.is_readonly) {
node_contents = expression.node; return;
// TODO where was this used? }
// value = name === 'textContent' ? x`@escape($$value)` : x`$$value`; if (name === 'group') {
} const value_attribute = node.attributes.find(({ name }) => name === 'value');
else if (binding.name === 'value' && node.name === 'textarea') { if (value_attribute) {
const snippet = expression.node; const value = get_attribute_expression(value_attribute);
node_contents = x `${snippet} || ""`; const type = node.get_static_attribute_value('type');
} const bound = expression.node;
else if (binding.name === 'value' && node.name === 'select') { const condition =
// NOTE: do not add "value" attribute on <select /> type === 'checkbox' ? x`~${bound}.indexOf(${value})` : x`${value} === ${bound}`;
} renderer.add_expression(x`${condition} ? @add_attribute("checked", true, 1) : ""`);
else { }
const snippet = expression.node; } else if (contenteditable && is_name_contenteditable(name)) {
renderer.add_expression(x `@add_attribute("${name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`); node_contents = expression.node;
} // TODO where was this used?
}); // value = name === 'textContent' ? x`@escape($$value)` : x`$$value`;
if (options.hydratable) { } else if (binding.name === 'value' && node.name === 'textarea') {
if (node.can_optimise_to_html_string && !options.has_added_svelte_hash) { const snippet = expression.node;
renderer.add_string(` data-svelte-h="${node.hash()}"`); node_contents = x`${snippet} || ""`;
options = { ...options, has_added_svelte_hash: true }; } else if (binding.name === 'value' && node.name === 'select') {
} // NOTE: do not add "value" attribute on <select />
} } else {
renderer.add_string('>'); const snippet = expression.node;
if (node_contents !== undefined) { renderer.add_expression(
if (contenteditable) { x`@add_attribute("${name}", ${snippet}, ${boolean_attributes.has(name) ? 1 : 0})`
renderer.push(); );
renderer.render(children, options); }
const result = renderer.pop(); });
renderer.add_expression(x `($$value => $$value === void 0 ? ${result} : $$value)(${node_contents})`); if (options.hydratable) {
} if (node.can_optimise_to_html_string && !options.has_added_svelte_hash) {
else { renderer.add_string(` data-svelte-h="${node.hash()}"`);
if (node.name === 'textarea') { options = { ...options, has_added_svelte_hash: true };
// Two or more leading newlines are required to restore the leading newline immediately after `<textarea>`. }
// see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions }
const value_attribute = node.attributes.find(({ name }) => name === 'value'); renderer.add_string('>');
if (value_attribute) { if (node_contents !== undefined) {
const first = value_attribute.chunks[0]; if (contenteditable) {
if (first && first.type === 'Text' && regex_starts_with_newline.test(first.data)) { renderer.push();
renderer.add_string('\n'); renderer.render(children, options);
} const result = renderer.pop();
} renderer.add_expression(
} x`($$value => $$value === void 0 ? ${result} : $$value)(${node_contents})`
renderer.add_expression(node_contents); );
} } else {
add_close_tag(); if (node.name === 'textarea') {
} // Two or more leading newlines are required to restore the leading newline immediately after `<textarea>`.
else { // see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions
if (node.name === 'pre') { const value_attribute = node.attributes.find(({ name }) => name === 'value');
// Two or more leading newlines are required to restore the leading newline immediately after `<pre>`. if (value_attribute) {
// see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element const first = value_attribute.chunks[0];
// see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions if (first && first.type === 'Text' && regex_starts_with_newline.test(first.data)) {
const first = children[0]; renderer.add_string('\n');
if (first && first.type === 'Text' && regex_starts_with_newline.test(first.data)) { }
renderer.add_string('\n'); }
} }
} renderer.add_expression(node_contents);
if (node.is_dynamic_element) }
renderer.push(); add_close_tag();
renderer.render(children, options); } else {
if (node.is_dynamic_element) { if (node.name === 'pre') {
const children = renderer.pop(); // Two or more leading newlines are required to restore the leading newline immediately after `<pre>`.
renderer.add_expression(x `@is_void(#tag) ? '' : ${children}`); // see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
} // see https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions
add_close_tag(); const first = children[0];
} if (first && first.type === 'Text' && regex_starts_with_newline.test(first.data)) {
if (node.is_dynamic_element) { renderer.add_string('\n');
let content = renderer.pop(); }
if (options.dev && node.children.length > 0) }
content = x `(() => { @validate_void_dynamic_element(#tag); return ${content}; })()`; if (node.is_dynamic_element) renderer.push();
renderer.add_expression(x `((#tag) => { renderer.render(children, options);
${options.dev && x `@validate_dynamic_element(#tag)`} 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 = 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} : ''; return #tag ? ${content} : '';
})(${node.tag_expr.node})`); })(${node.tag_expr.node})`);
} }
function add_close_tag() { function add_close_tag() {
if (node.tag_expr.node.type === 'Literal') { if (node.tag_expr.node.type === 'Literal') {
if (!is_void(/** @type {string} */ (node.tag_expr.node.value))) { if (!is_void(/** @type {string} */ (node.tag_expr.node.value))) {
renderer.add_string('</'); renderer.add_string('</');
add_tag_name(); add_tag_name();
renderer.add_string('>'); renderer.add_string('>');
} }
return; return;
} }
renderer.add_expression(x `@is_void(#tag) ? '' : \`</\${#tag}>\``); renderer.add_expression(x`@is_void(#tag) ? '' : \`</\${#tag}>\``);
} }
function add_tag_name() { function add_tag_name() {
if (node.tag_expr.node.type === 'Literal') { if (node.tag_expr.node.type === 'Literal') {
renderer.add_string(/** @type {string} */ (node.tag_expr.node.value)); renderer.add_string(/** @type {string} */ (node.tag_expr.node.value));
} } else {
else { renderer.add_expression(/** @type {ESExpression} */ (node.tag_expr.node));
renderer.add_expression(/** @type {ESExpression} */ (node.tag_expr.node)); }
} }
}
} }

Loading…
Cancel
Save