Lint and format src/compiler/compile/render_dom/wrappers/Element/index.js

pull/8569/head
Simon Holthausen 3 years ago
parent 3ad9c11c03
commit 461dda587e

@ -9,7 +9,15 @@ import { namespaces } from '../../../../utils/namespaces.js';
import AttributeWrapper from './Attribute.js'; import AttributeWrapper from './Attribute.js';
import StyleAttributeWrapper from './StyleAttribute.js'; import StyleAttributeWrapper from './StyleAttribute.js';
import SpreadAttributeWrapper from './SpreadAttribute.js'; import SpreadAttributeWrapper from './SpreadAttribute.js';
import { regex_dimensions, regex_starts_with_newline, regex_backslashes, regex_border_box_size, regex_content_box_size, regex_device_pixel_content_box_size, regex_content_rect } from '../../../../utils/patterns.js'; import {
regex_dimensions,
regex_starts_with_newline,
regex_backslashes,
regex_border_box_size,
regex_content_box_size,
regex_device_pixel_content_box_size,
regex_content_rect
} from '../../../../utils/patterns.js';
import Binding from './Binding.js'; import Binding from './Binding.js';
import add_to_set from '../../../utils/add_to_set.js'; import add_to_set from '../../../utils/add_to_set.js';
import { add_event_handler } from '../shared/add_event_handlers.js'; import { add_event_handler } from '../shared/add_event_handlers.js';
@ -22,7 +30,10 @@ import Action from '../../../nodes/Action.js';
import MustacheTagWrapper from '../MustacheTag.js'; import MustacheTagWrapper from '../MustacheTag.js';
import RawMustacheTagWrapper from '../RawMustacheTag.js'; import RawMustacheTagWrapper from '../RawMustacheTag.js';
import is_dynamic from '../shared/is_dynamic.js'; import is_dynamic from '../shared/is_dynamic.js';
import { is_name_contenteditable, has_contenteditable_attr } from '../../../utils/contenteditable.js'; import {
is_name_contenteditable,
has_contenteditable_attr
} from '../../../utils/contenteditable.js';
import create_debugging_comment from '../shared/create_debugging_comment.js'; import create_debugging_comment from '../shared/create_debugging_comment.js';
import { push_array } from '../../../../utils/push_array.js'; import { push_array } from '../../../../utils/push_array.js';
import CommentWrapper from '../Comment.js'; import CommentWrapper from '../Comment.js';
@ -31,11 +42,12 @@ const regex_contains_radio_or_checkbox_or_range_or_file = /radio|checkbox|range|
const events = [ const events = [
{ {
event_names: ['input'], event_names: ['input'],
filter: (node, _name) => node.name === 'textarea' || filter: (node, _name) =>
node.name === 'textarea' ||
(node.name === 'input' && (node.name === 'input' &&
!regex_contains_radio_or_checkbox_or_range_or_file.test( !regex_contains_radio_or_checkbox_or_range_or_file.test(
/** @type {string} */ (node.get_static_attribute_value('type'))
/** @type {string} */ (node.get_static_attribute_value('type')))) ))
}, },
{ {
event_names: ['input'], event_names: ['input'],
@ -43,15 +55,17 @@ const events = [
}, },
{ {
event_names: ['change'], event_names: ['change'],
filter: (node, _name) => node.name === 'select' || filter: (node, _name) =>
node.name === 'select' ||
(node.name === 'input' && (node.name === 'input' &&
regex_contains_radio_or_checkbox_or_file.test( regex_contains_radio_or_checkbox_or_file.test(
/** @type {string} */ (node.get_static_attribute_value('type'))
/** @type {string} */ (node.get_static_attribute_value('type')))) ))
}, },
{ {
event_names: ['change', 'input'], event_names: ['change', 'input'],
filter: (node, _name) => node.name === 'input' && node.get_static_attribute_value('type') === 'range' filter: (node, _name) =>
node.name === 'input' && node.get_static_attribute_value('type') === 'range'
}, },
// resize events // resize events
{ {
@ -73,7 +87,8 @@ const events = [
// media events // media events
{ {
event_names: ['timeupdate'], event_names: ['timeupdate'],
filter: (node, name) => node.is_media_node() && (name === 'currentTime' || name === 'played' || name === 'ended') filter: (node, name) =>
node.is_media_node() && (name === 'currentTime' || name === 'played' || name === 'ended')
}, },
{ {
event_names: ['durationchange'], event_names: ['durationchange'],
@ -109,7 +124,8 @@ const events = [
}, },
{ {
event_names: ['resize'], event_names: ['resize'],
filter: (node, name) => node.is_media_node() && (name === 'videoHeight' || name === 'videoWidth') filter: (node, name) =>
node.is_media_node() && (name === 'videoHeight' || name === 'videoWidth')
}, },
{ {
// from https://html.spec.whatwg.org/multipage/media.html#ready-states // from https://html.spec.whatwg.org/multipage/media.html#ready-states
@ -141,7 +157,6 @@ const regex_minus_signs = /-/g;
/** @extends Wrapper */ /** @extends Wrapper */
export default class ElementWrapper extends Wrapper { export default class ElementWrapper extends Wrapper {
/** @type {import('../../../nodes/Element.js').default} */ /** @type {import('../../../nodes/Element.js').default} */
node; node;
@ -212,7 +227,14 @@ export default class ElementWrapper extends Wrapper {
type: CHILD_DYNAMIC_ELEMENT_BLOCK type: CHILD_DYNAMIC_ELEMENT_BLOCK
}); });
renderer.blocks.push(this.child_dynamic_element_block); renderer.blocks.push(this.child_dynamic_element_block);
this.child_dynamic_element = new ElementWrapper(renderer, this.child_dynamic_element_block, parent, node, strip_whitespace, next_sibling); this.child_dynamic_element = new ElementWrapper(
renderer,
this.child_dynamic_element_block,
parent,
node,
strip_whitespace,
next_sibling
);
// the original svelte:element is never used for rendering, because // the original svelte:element is never used for rendering, because
// it gets assigned a child_dynamic_element which is used in all rendering logic. // it gets assigned a child_dynamic_element which is used in all rendering logic.
// so doing all of this on the original svelte:element will just cause double // so doing all of this on the original svelte:element will just cause double
@ -236,34 +258,45 @@ export default class ElementWrapper extends Wrapper {
} }
return new AttributeWrapper(this, block, attribute); return new AttributeWrapper(this, block, attribute);
}); });
this.has_dynamic_attribute = !!this.attributes.find((attr) => attr.node.get_dependencies().length > 0); this.has_dynamic_attribute = !!this.attributes.find(
(attr) => attr.node.get_dependencies().length > 0
);
// ordinarily, there'll only be one... but we need to handle // ordinarily, there'll only be one... but we need to handle
// the rare case where an element can have multiple bindings, // the rare case where an element can have multiple bindings,
// e.g. <audio bind:paused bind:currentTime> // e.g. <audio bind:paused bind:currentTime>
this.bindings = this.node.bindings.map((binding) => new Binding(block, binding, this)); this.bindings = this.node.bindings.map((binding) => new Binding(block, binding, this));
this.event_handlers = this.node.handlers.map((event_handler) => new EventHandler(event_handler, this)); this.event_handlers = this.node.handlers.map(
(event_handler) => new EventHandler(event_handler, this)
);
if (node.intro || node.outro) { if (node.intro || node.outro) {
if (node.intro) if (node.intro) block.add_intro(node.intro.is_local);
block.add_intro(node.intro.is_local); if (node.outro) block.add_outro(node.outro.is_local);
if (node.outro)
block.add_outro(node.outro.is_local);
} }
if (node.animation) { if (node.animation) {
block.add_animation(); block.add_animation();
} }
block.add_dependencies(node.tag_expr.dependencies); block.add_dependencies(node.tag_expr.dependencies);
// add directive and handler dependencies // add directive and handler dependencies
[node.animation, node.outro, ...node.actions, ...node.classes, ...node.styles].forEach((directive) => { [node.animation, node.outro, ...node.actions, ...node.classes, ...node.styles].forEach(
(directive) => {
if (directive && directive.expression) { if (directive && directive.expression) {
block.add_dependencies(directive.expression.dependencies); block.add_dependencies(directive.expression.dependencies);
} }
}); }
);
node.handlers.forEach((handler) => { node.handlers.forEach((handler) => {
if (handler.expression) { if (handler.expression) {
block.add_dependencies(handler.expression.dependencies); block.add_dependencies(handler.expression.dependencies);
} }
}); });
this.fragment = new FragmentWrapper(renderer, block, node.children, this, strip_whitespace, next_sibling); this.fragment = new FragmentWrapper(
renderer,
block,
node.children,
this,
strip_whitespace,
next_sibling
);
this.element_data_name = block.get_unique_name(`${this.var.name}_data`); this.element_data_name = block.get_unique_name(`${this.var.name}_data`);
} }
@ -275,8 +308,7 @@ export default class ElementWrapper extends Wrapper {
render(block, parent_node, parent_nodes) { render(block, parent_node, parent_nodes) {
if (this.child_dynamic_element) { if (this.child_dynamic_element) {
this.render_dynamic_element(block, parent_node, parent_nodes); this.render_dynamic_element(block, parent_node, parent_nodes);
} } else {
else {
this.render_element(block, parent_node, parent_nodes); this.render_element(block, parent_node, parent_nodes);
} }
} }
@ -287,15 +319,20 @@ export default class ElementWrapper extends Wrapper {
* @param {import('estree').Identifier} parent_nodes * @param {import('estree').Identifier} parent_nodes
*/ */
render_dynamic_element(block, parent_node, parent_nodes) { render_dynamic_element(block, parent_node, parent_nodes) {
this.child_dynamic_element.render(this.child_dynamic_element_block, null, this.child_dynamic_element.render(
/** @type {unknown} */ /** @type {import('estree').Identifier} */ ((x `#nodes`))); this.child_dynamic_element_block,
null,
/** @type {unknown} */ /** @type {import('estree').Identifier} */ (x`#nodes`)
);
const is_tag_dynamic = this.node.tag_expr.dynamic_dependencies().length > 0; const is_tag_dynamic = this.node.tag_expr.dynamic_dependencies().length > 0;
const tag = this.node.tag_expr.manipulate(block); const tag = this.node.tag_expr.manipulate(block);
block.chunks.init.push(b` block.chunks.init.push(b`
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`} ${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
${this.renderer.options.dev && ${
this.renderer.options.dev &&
this.node.children.length > 0 && this.node.children.length > 0 &&
b `@validate_void_dynamic_element(${tag});`} b`@validate_void_dynamic_element(${tag});`
}
let ${this.var} = ${tag} && ${this.child_dynamic_element_block.name}(#ctx); let ${this.var} = ${tag} && ${this.child_dynamic_element_block.name}(#ctx);
`); `);
block.chunks.create.push(b` block.chunks.create.push(b`
@ -332,28 +369,35 @@ export default class ElementWrapper extends Wrapper {
} else if (${not_equal}(${previous_tag}, ${tag})) { } else if (${not_equal}(${previous_tag}, ${tag})) {
${this.var}.d(1); ${this.var}.d(1);
${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`} ${this.renderer.options.dev && b`@validate_dynamic_element(${tag});`}
${this.renderer.options.dev && ${
this.renderer.options.dev &&
this.node.children.length > 0 && this.node.children.length > 0 &&
b `@validate_void_dynamic_element(${tag});`} b`@validate_void_dynamic_element(${tag});`
}
${this.var} = ${this.child_dynamic_element_block.name}(#ctx); ${this.var} = ${this.child_dynamic_element_block.name}(#ctx);
${previous_tag} = ${tag}; ${previous_tag} = ${tag};
${this.var}.c(); ${this.var}.c();
${has_transitions && ${
has_transitions &&
b`if (${tag_will_be_removed}) { b`if (${tag_will_be_removed}) {
${tag_will_be_removed} = false; ${tag_will_be_removed} = false;
@transition_in(${this.var}) @transition_in(${this.var})
}`} }`
}
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor}); ${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
} else { } else {
${has_transitions && ${
has_transitions &&
b`if (${tag_will_be_removed}) { b`if (${tag_will_be_removed}) {
${tag_will_be_removed} = false; ${tag_will_be_removed} = false;
@transition_in(${this.var}) @transition_in(${this.var})
}`} }`
}
${this.var}.p(#ctx, #dirty); ${this.var}.p(#ctx, #dirty);
} }
} else if (${previous_tag}) { } else if (${previous_tag}) {
${has_transitions ${
has_transitions
? b` ? b`
${tag_will_be_removed} = true; ${tag_will_be_removed} = true;
@group_outros(); @group_outros();
@ -368,11 +412,11 @@ export default class ElementWrapper extends Wrapper {
${this.var}.d(1); ${this.var}.d(1);
${this.var} = null; ${this.var} = null;
${previous_tag} = ${tag}; ${previous_tag} = ${tag};
`} `
} }
`);
} }
else { `);
} else {
block.chunks.update.push(b` block.chunks.update.push(b`
if (${tag}) { if (${tag}) {
${this.var}.p(#ctx, #dirty); ${this.var}.p(#ctx, #dirty);
@ -409,8 +453,7 @@ export default class ElementWrapper extends Wrapper {
render_element(block, parent_node, parent_nodes) { render_element(block, parent_node, parent_nodes) {
const { renderer } = this; const { renderer } = this;
const hydratable = renderer.options.hydratable; const hydratable = renderer.options.hydratable;
if (this.node.name === 'noscript') if (this.node.name === 'noscript') return;
return;
const node = this.var; const node = this.var;
const nodes = parent_nodes && block.get_unique_name(`${this.var.name}_nodes`); // if we're in unclaimable territory, i.e. <head>, parent_nodes is null const nodes = parent_nodes && block.get_unique_name(`${this.var.name}_nodes`); // if we're in unclaimable territory, i.e. <head>, parent_nodes is null
const children = x`@children(${this.node.name === 'template' ? x`${node}.content` : node})`; const children = x`@children(${this.node.name === 'template' ? x`${node}.content` : node})`;
@ -428,14 +471,15 @@ export default class ElementWrapper extends Wrapper {
var ${nodes} = ${children}; var ${nodes} = ${children};
`); `);
} }
} } else {
else {
block.chunks.claim.push(b`${node} = ${render_statement};`); block.chunks.claim.push(b`${node} = ${render_statement};`);
} }
} }
if (parent_node) { if (parent_node) {
const append = b`@append(${parent_node}, ${node});`; const append = b`@append(${parent_node}, ${node});`;
( /** @type {import('estree').CallExpression} */(( /** @type {import('estree').ExpressionStatement} */(append[0])).expression)).callee.loc = { /** @type {import('estree').CallExpression} */ (
/** @type {import('estree').ExpressionStatement} */ (append[0]).expression
).callee.loc = {
start: this.renderer.locate(this.node.start), start: this.renderer.locate(this.node.start),
end: this.renderer.locate(this.node.end) end: this.renderer.locate(this.node.end)
}; };
@ -443,10 +487,11 @@ export default class ElementWrapper extends Wrapper {
if (is_head(parent_node)) { if (is_head(parent_node)) {
block.chunks.destroy.push(b`@detach(${node});`); block.chunks.destroy.push(b`@detach(${node});`);
} }
} } else {
else {
const insert = b`@insert(#target, ${node}, #anchor);`; const insert = b`@insert(#target, ${node}, #anchor);`;
( /** @type {import('estree').CallExpression} */(( /** @type {import('estree').ExpressionStatement} */(insert[0])).expression)).callee.loc = { /** @type {import('estree').CallExpression} */ (
/** @type {import('estree').ExpressionStatement} */ (insert[0]).expression
).callee.loc = {
start: this.renderer.locate(this.node.start), start: this.renderer.locate(this.node.start),
end: this.renderer.locate(this.node.end) end: this.renderer.locate(this.node.end)
}; };
@ -459,7 +504,9 @@ export default class ElementWrapper extends Wrapper {
// skip textcontent for <template>. append nodes to TemplateElement.content instead // skip textcontent for <template>. append nodes to TemplateElement.content instead
if (can_optimise_to_html_string) { if (can_optimise_to_html_string) {
if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') { if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
let text = string_literal(( /** @type {import('../Text.js').default} */(this.fragment.nodes[0])).data); let text = string_literal(
/** @type {import('../Text.js').default} */ (this.fragment.nodes[0]).data
);
if (hydratable) { if (hydratable) {
const variable = block.get_unique_name('textContent'); const variable = block.get_unique_name('textContent');
block.add_variable(variable, text); block.add_variable(variable, text);
@ -467,10 +514,11 @@ export default class ElementWrapper extends Wrapper {
} }
block.chunks.create.push(b`${node}.textContent = ${text};`); block.chunks.create.push(b`${node}.textContent = ${text};`);
if (hydratable) { if (hydratable) {
block.chunks.claim.push(b `if (@get_svelte_dataset(${node}) !== "${this.node.hash()}") ${node}.textContent = ${text};`); block.chunks.claim.push(
} b`if (@get_svelte_dataset(${node}) !== "${this.node.hash()}") ${node}.textContent = ${text};`
);
} }
else { } else {
const state = { const state = {
quasi: { quasi: {
type: 'TemplateElement', type: 'TemplateElement',
@ -484,8 +532,14 @@ export default class ElementWrapper extends Wrapper {
}; };
const can_use_raw_text = !this.node.can_use_innerhtml && can_use_textcontent; const can_use_raw_text = !this.node.can_use_innerhtml && can_use_textcontent;
to_html( to_html(
/** @type {unknown} */ /** @type {Array<ElementWrapper | import('../Comment.js').default | import('../Text.js').default>} */ (
/** @type {unknown} */ /** @type {Array<ElementWrapper | import('../Comment.js').default | import('../Text.js').default>} */ ((this.fragment.nodes)), block, literal, state, can_use_raw_text); this.fragment.nodes
),
block,
literal,
state,
can_use_raw_text
);
literal.quasis.push(/** @type {any} */ (state.quasi)); literal.quasis.push(/** @type {any} */ (state.quasi));
if (hydratable) { if (hydratable) {
const variable = block.get_unique_name('textContent'); const variable = block.get_unique_name('textContent');
@ -495,18 +549,20 @@ export default class ElementWrapper extends Wrapper {
const property = this.node.can_use_innerhtml ? 'innerHTML' : 'textContent'; const property = this.node.can_use_innerhtml ? 'innerHTML' : 'textContent';
block.chunks.create.push(b`${node}.${property} = ${literal};`); block.chunks.create.push(b`${node}.${property} = ${literal};`);
if (hydratable) { if (hydratable) {
block.chunks.claim.push(b `if (@get_svelte_dataset(${node}) !== "${this.node.hash()}") ${node}.${property} = ${literal};`); block.chunks.claim.push(
} b`if (@get_svelte_dataset(${node}) !== "${this.node.hash()}") ${node}.${property} = ${literal};`
);
} }
} }
else { } else {
this.fragment.nodes.forEach((child) => { this.fragment.nodes.forEach((child) => {
child.render(block, this.node.name === 'template' ? x`${node}.content` : node, nodes, { child.render(block, this.node.name === 'template' ? x`${node}.content` : node, nodes, {
element_data_name: this.element_data_name element_data_name: this.element_data_name
}); });
}); });
} }
const event_handler_or_binding_uses_context = this.bindings.some((binding) => binding.handler.uses_context) || const event_handler_or_binding_uses_context =
this.bindings.some((binding) => binding.handler.uses_context) ||
this.node.handlers.some((handler) => handler.uses_context) || this.node.handlers.some((handler) => handler.uses_context) ||
this.node.actions.some((action) => action.uses_context); this.node.actions.some((action) => action.uses_context);
if (event_handler_or_binding_uses_context) { if (event_handler_or_binding_uses_context) {
@ -514,8 +570,7 @@ export default class ElementWrapper extends Wrapper {
} }
if (this.node.is_dynamic_element) { if (this.node.is_dynamic_element) {
this.add_dynamic_element_attributes(block); this.add_dynamic_element_attributes(block);
} } else {
else {
this.add_attributes(block); this.add_attributes(block);
} }
this.add_directives_in_order(block); this.add_directives_in_order(block);
@ -525,11 +580,17 @@ export default class ElementWrapper extends Wrapper {
this.add_styles(block); this.add_styles(block);
this.add_manual_style_scoping(block); this.add_manual_style_scoping(block);
if (nodes && hydratable && !this.void && !can_optimise_to_html_string) { if (nodes && hydratable && !this.void && !can_optimise_to_html_string) {
block.chunks.claim.push(b `${this.node.children.length > 0 ? nodes : children}.forEach(@detach);`); block.chunks.claim.push(
b`${this.node.children.length > 0 ? nodes : children}.forEach(@detach);`
);
} }
if (renderer.options.dev) { if (renderer.options.dev) {
const loc = renderer.locate(this.node.start); const loc = renderer.locate(this.node.start);
block.chunks.hydrate.push(b `@add_location(${this.var}, ${renderer.file_var}, ${loc.line - 1}, ${loc.column}, ${this.node.start});`); block.chunks.hydrate.push(
b`@add_location(${this.var}, ${renderer.file_var}, ${loc.line - 1}, ${loc.column}, ${
this.node.start
});`
);
} }
block.renderer.dirty(this.node.tag_expr.dynamic_dependencies()); block.renderer.dirty(this.node.tag_expr.dynamic_dependencies());
} }
@ -563,22 +624,28 @@ export default class ElementWrapper extends Wrapper {
get_claim_statement(block, nodes, to_optimise_hydration) { get_claim_statement(block, nodes, to_optimise_hydration) {
const attributes = this.attributes const attributes = this.attributes
.filter((attr) => !(attr instanceof SpreadAttributeWrapper) && !attr.property_name) .filter((attr) => !(attr instanceof SpreadAttributeWrapper) && !attr.property_name)
.map((attr) => p `${( /** @type {import('./StyleAttribute.js').default | import('./Attribute.js').default} */(attr)).name}: true`); .map(
(attr) =>
p`${
/** @type {import('./StyleAttribute.js').default | import('./Attribute.js').default} */ (
attr
).name
}: true`
);
/** @type {Class<x>>} */ /** @type {Class<x>>} */
let reference; let reference;
if (this.node.tag_expr.node.type === 'Literal') { if (this.node.tag_expr.node.type === 'Literal') {
if (this.node.namespace) { if (this.node.namespace) {
reference = `"${this.node.tag_expr.node.value}"`; reference = `"${this.node.tag_expr.node.value}"`;
} else {
reference = `"${(
/** @type {String} */ (this.node.tag_expr.node.value) || ''
).toUpperCase()}"`;
} }
else { } else if (this.node.namespace) {
reference = `"${(( /** @type {String} */(this.node.tag_expr.node.value)) || '').toUpperCase()}"`;
}
}
else if (this.node.namespace) {
reference = x`${this.node.tag_expr.manipulate(block)}`; reference = x`${this.node.tag_expr.manipulate(block)}`;
} } else {
else {
reference = x`(${this.node.tag_expr.manipulate(block)} || 'null').toUpperCase()`; reference = x`(${this.node.tag_expr.manipulate(block)} || 'null').toUpperCase()`;
} }
if (to_optimise_hydration) { if (to_optimise_hydration) {
@ -586,8 +653,7 @@ export default class ElementWrapper extends Wrapper {
} }
if (this.node.namespace === namespaces.svg) { if (this.node.namespace === namespaces.svg) {
return x`@claim_svg_element(${nodes}, ${reference}, { ${attributes} })`; return x`@claim_svg_element(${nodes}, ${reference}, { ${attributes} })`;
} } else {
else {
return x`@claim_element(${nodes}, ${reference}, { ${attributes} })`; return x`@claim_element(${nodes}, ${reference}, { ${attributes} })`;
} }
} }
@ -608,38 +674,30 @@ export default class ElementWrapper extends Wrapper {
function getOrder(item) { function getOrder(item) {
if (item instanceof EventHandler) { if (item instanceof EventHandler) {
return item.node.start; return item.node.start;
} } else if (item instanceof Binding) {
else if (item instanceof Binding) {
return item.node.start; return item.node.start;
} } else if (item instanceof Action) {
else if (item instanceof Action) {
return item.start; return item.start;
} } else {
else {
return item.bindings[0].node.start; return item.bindings[0].node.start;
} }
} }
(
/** @type {OrderedAttribute[]} */ ([ /** @type {OrderedAttribute[]} */ ([
...binding_groups, ...binding_groups,
...this.event_handlers, ...this.event_handlers,
this_binding, this_binding,
...this.node.actions ...this.node.actions
])) ])
.filter(Boolean) .filter(Boolean)
.sort((a, b) => getOrder(a) - getOrder(b)) .sort((a, b) => getOrder(a) - getOrder(b))
.forEach((item) => { .forEach((item) => {
if (item instanceof EventHandler) { if (item instanceof EventHandler) {
add_event_handler(block, this.var, item); add_event_handler(block, this.var, item);
} } else if (item instanceof Binding) {
else if (item instanceof Binding) {
this.add_this_binding(block, item); this.add_this_binding(block, item);
} } else if (item instanceof Action) {
else if (item instanceof Action) {
add_action(block, this.var, item); add_action(block, this.var, item);
} } else {
else {
this.add_bindings(block, item); this.add_bindings(block, item);
} }
}); });
@ -651,15 +709,15 @@ export default class ElementWrapper extends Wrapper {
*/ */
add_bindings(block, binding_group) { add_bindings(block, binding_group) {
const { renderer } = this; const { renderer } = this;
if (binding_group.bindings.length === 0) if (binding_group.bindings.length === 0) return;
return;
renderer.component.has_reactive_assignments = true; renderer.component.has_reactive_assignments = true;
const lock = binding_group.bindings.some((binding) => binding.needs_lock) const lock = binding_group.bindings.some((binding) => binding.needs_lock)
? block.get_unique_name(`${this.var.name}_updating`) ? block.get_unique_name(`${this.var.name}_updating`)
: null; : null;
if (lock) if (lock) block.add_variable(lock, x`false`);
block.add_variable(lock, x `false`); const handler = renderer.component.get_unique_name(
const handler = renderer.component.get_unique_name(`${this.var.name}_${binding_group.events.join('_')}_handler`); `${this.var.name}_${binding_group.events.join('_')}_handler`
);
renderer.add_to_context(handler.name); renderer.add_to_context(handler.name);
// TODO figure out how to handle locks // TODO figure out how to handle locks
const needs_lock = binding_group.bindings.some((binding) => binding.needs_lock); const needs_lock = binding_group.bindings.some((binding) => binding.needs_lock);
@ -702,8 +760,7 @@ export default class ElementWrapper extends Wrapper {
${callee}.call(${this.var}, ${args}); ${callee}.call(${this.var}, ${args});
} }
`); `);
} } else {
else {
block.chunks.init.push(b` block.chunks.init.push(b`
function ${handler}() { function ${handler}() {
${needs_lock && b`${lock} = true;`} ${needs_lock && b`${lock} = true;`}
@ -727,45 +784,60 @@ export default class ElementWrapper extends Wrapper {
} }
`); `);
binding_group.events.forEach((name) => { binding_group.events.forEach((name) => {
if ([ if (
[
'elementresize', 'elementresize',
'elementresizecontentbox', 'elementresizecontentbox',
'elementresizeborderbox', 'elementresizeborderbox',
'elementresizedevicepixelcontentbox' 'elementresizedevicepixelcontentbox'
].indexOf(name) !== -1) { ].indexOf(name) !== -1
) {
const resize_listener = block.get_unique_name(`${this.var.name}_resize_listener`); const resize_listener = block.get_unique_name(`${this.var.name}_resize_listener`);
block.add_variable(resize_listener); block.add_variable(resize_listener);
// Can't dynamically do `@fn[name]`, code-red doesn't know how to resolve it // Can't dynamically do `@fn[name]`, code-red doesn't know how to resolve it
switch (name) { switch (name) {
case 'elementresize': case 'elementresize':
block.chunks.mount.push(b `${resize_listener} = @add_iframe_resize_listener(${this.var}, ${callee}.bind(${this.var}));`); block.chunks.mount.push(
b`${resize_listener} = @add_iframe_resize_listener(${this.var}, ${callee}.bind(${this.var}));`
);
break; break;
case 'elementresizecontentbox': case 'elementresizecontentbox':
block.chunks.mount.push(b `${resize_listener} = @resize_observer_content_box.observe(${this.var}, ${callee}.bind(${this.var}));`); block.chunks.mount.push(
b`${resize_listener} = @resize_observer_content_box.observe(${this.var}, ${callee}.bind(${this.var}));`
);
break; break;
case 'elementresizeborderbox': case 'elementresizeborderbox':
block.chunks.mount.push(b `${resize_listener} = @resize_observer_border_box.observe(${this.var}, ${callee}.bind(${this.var}));`); block.chunks.mount.push(
b`${resize_listener} = @resize_observer_border_box.observe(${this.var}, ${callee}.bind(${this.var}));`
);
break; break;
case 'elementresizedevicepixelcontentbox': case 'elementresizedevicepixelcontentbox':
block.chunks.mount.push(b `${resize_listener} = @resize_observer_device_pixel_content_box.observe(${this.var}, ${callee}.bind(${this.var}));`); block.chunks.mount.push(
b`${resize_listener} = @resize_observer_device_pixel_content_box.observe(${this.var}, ${callee}.bind(${this.var}));`
);
break; break;
} }
block.chunks.destroy.push(b`${resize_listener}();`); block.chunks.destroy.push(b`${resize_listener}();`);
} } else {
else {
block.event_listeners.push(x`@listen(${this.var}, "${name}", ${callee})`); block.event_listeners.push(x`@listen(${this.var}, "${name}", ${callee})`);
} }
}); });
const some_initial_state_is_undefined = binding_group.bindings const some_initial_state_is_undefined = binding_group.bindings
.map((binding) => x`${binding.snippet} === void 0`) .map((binding) => x`${binding.snippet} === void 0`)
.reduce((lhs, rhs) => x`${lhs} || ${rhs}`); .reduce((lhs, rhs) => x`${lhs} || ${rhs}`);
const should_initialise = this.node.name === 'select' || const should_initialise =
binding_group.bindings.find((binding) => binding.node.name === 'indeterminate' || this.node.name === 'select' ||
binding_group.bindings.find(
(binding) =>
binding.node.name === 'indeterminate' ||
is_name_contenteditable(binding.node.name) || is_name_contenteditable(binding.node.name) ||
binding.is_readonly_media_attribute()); binding.is_readonly_media_attribute()
);
if (should_initialise) { if (should_initialise) {
const callback = has_local_function ? handler : x`() => ${callee}.call(${this.var})`; const callback = has_local_function ? handler : x`() => ${callee}.call(${this.var})`;
block.chunks.hydrate.push(b `if (${some_initial_state_is_undefined}) @add_render_callback(${callback});`); block.chunks.hydrate.push(
b`if (${some_initial_state_is_undefined}) @add_render_callback(${callback});`
);
} }
if (binding_group.events[0] === 'elementresize') { if (binding_group.events[0] === 'elementresize') {
block.chunks.hydrate.push(b`@add_render_callback(() => ${callee}.call(${this.var}));`); block.chunks.hydrate.push(b`@add_render_callback(() => ${callee}.call(${this.var}));`);
@ -793,8 +865,7 @@ export default class ElementWrapper extends Wrapper {
if (attribute.node.name === 'class') { if (attribute.node.name === 'class') {
const dependencies = attribute.node.get_dependencies(); const dependencies = attribute.node.get_dependencies();
push_array(this.class_dependencies, dependencies); push_array(this.class_dependencies, dependencies);
} } else if (attribute.node.name === 'style') {
else if (attribute.node.name === 'style') {
add_to_set(this.dynamic_style_dependencies, attribute.node.get_dependencies()); add_to_set(this.dynamic_style_dependencies, attribute.node.get_dependencies());
} }
}); });
@ -819,12 +890,13 @@ export default class ElementWrapper extends Wrapper {
const snippet = attr.node.expression.manipulate(block); const snippet = attr.node.expression.manipulate(block);
initial_props.push(snippet); initial_props.push(snippet);
updates.push(condition ? x`${condition} && ${snippet}` : snippet); updates.push(condition ? x`${condition} && ${snippet}` : snippet);
} } else {
else {
const name = attr.property_name || attr.name; const name = attr.property_name || attr.name;
initial_props.push(x`{ ${name}: ${attr.get_init(block, attr.get_value(block))} }`); initial_props.push(x`{ ${name}: ${attr.get_init(block, attr.get_value(block))} }`);
const snippet = x`{ ${name}: ${attr.should_cache ? attr.last : attr.get_value(block)} }`; const snippet = x`{ ${name}: ${attr.should_cache ? attr.last : attr.get_value(block)} }`;
updates.push(condition ? x `${attr.get_dom_update_conditions(block, condition)} && ${snippet}` : snippet); updates.push(
condition ? x`${attr.get_dom_update_conditions(block, condition)} && ${snippet}` : snippet
);
} }
}); });
block.chunks.init.push(b` block.chunks.init.push(b`
@ -835,7 +907,8 @@ export default class ElementWrapper extends Wrapper {
${this.element_data_name} = @assign(${this.element_data_name}, ${levels}[#i]); ${this.element_data_name} = @assign(${this.element_data_name}, ${levels}[#i]);
} }
`); `);
const fn = this.node.namespace === namespaces.svg const fn =
this.node.namespace === namespaces.svg
? x`@set_svg_attributes` ? x`@set_svg_attributes`
: this.node.is_dynamic_element : this.node.is_dynamic_element
? x`@set_dynamic_element_data(${this.node.tag_expr.manipulate(block)})` ? x`@set_dynamic_element_data(${this.node.tag_expr.manipulate(block)})`
@ -860,17 +933,22 @@ export default class ElementWrapper extends Wrapper {
'value' in ${this.element_data_name} && (${this.element_data_name}.multiple ? @select_options : @select_option)(${this.var}, ${this.element_data_name}.value); 'value' in ${this.element_data_name} && (${this.element_data_name}.multiple ? @select_options : @select_option)(${this.var}, ${this.element_data_name}.value);
`); `);
block.chunks.update.push(b` block.chunks.update.push(b`
if (${block.renderer.dirty(Array.from(dependencies))} && 'value' in ${this.element_data_name}) (${this.element_data_name}.multiple ? @select_options : @select_option)(${this.var}, ${this.element_data_name}.value); if (${block.renderer.dirty(Array.from(dependencies))} && 'value' in ${this.element_data_name}) (${
this.element_data_name
}.multiple ? @select_options : @select_option)(${this.var}, ${this.element_data_name}.value);
`); `);
} } else if (
else if (this.node.name === 'input' && this.node.name === 'input' &&
this.attributes.find((attr) => attr.node.name === 'value')) { this.attributes.find((attr) => attr.node.name === 'value')
) {
const type = this.node.get_static_attribute_value('type'); const type = this.node.get_static_attribute_value('type');
if (type === null || if (
type === null ||
type === '' || type === '' ||
type === 'text' || type === 'text' ||
type === 'email' || type === 'email' ||
type === 'password') { type === 'password'
) {
block.chunks.mount.push(b` block.chunks.mount.push(b`
if ('value' in ${this.element_data_name}) { if ('value' in ${this.element_data_name}) {
${this.var}.value = ${this.element_data_name}.value; ${this.var}.value = ${this.element_data_name}.value;
@ -892,8 +970,7 @@ export default class ElementWrapper extends Wrapper {
/** @param {import('../../Block.js').default} block */ /** @param {import('../../Block.js').default} block */
add_dynamic_element_attributes(block) { add_dynamic_element_attributes(block) {
if (this.attributes.length === 0) if (this.attributes.length === 0) return;
return;
if (this.has_dynamic_attribute) { if (this.has_dynamic_attribute) {
this.add_spread_attributes(block); this.add_spread_attributes(block);
return; return;
@ -902,13 +979,13 @@ export default class ElementWrapper extends Wrapper {
this.attributes.forEach((attr) => { this.attributes.forEach((attr) => {
if (attr instanceof SpreadAttributeWrapper) { if (attr instanceof SpreadAttributeWrapper) {
static_attributes.push({ type: 'SpreadElement', argument: attr.node.expression.node }); static_attributes.push({ type: 'SpreadElement', argument: attr.node.expression.node });
} } else {
else {
const name = attr.property_name || attr.name; const name = attr.property_name || attr.name;
static_attributes.push(p`${name}: ${attr.get_value(block)}`); static_attributes.push(p`${name}: ${attr.get_value(block)}`);
} }
}); });
const fn = this.node.namespace === namespaces.svg const fn =
this.node.namespace === namespaces.svg
? x`@set_svg_attributes` ? x`@set_svg_attributes`
: this.node.is_dynamic_element : this.node.is_dynamic_element
? x`@set_dynamic_element_data(${this.node.tag_expr.manipulate(block)})` ? x`@set_dynamic_element_data(${this.node.tag_expr.manipulate(block)})`
@ -919,8 +996,7 @@ export default class ElementWrapper extends Wrapper {
/** @param {import('../../Block.js').default} block */ /** @param {import('../../Block.js').default} block */
add_transitions(block) { add_transitions(block) {
const { intro, outro } = this.node; const { intro, outro } = this.node;
if (!intro && !outro) if (!intro && !outro) return;
return;
if (intro === outro) { if (intro === outro) {
// bidirectional transition // bidirectional transition
const name = block.get_unique_name(`${this.var.name}_transition`); const name = block.get_unique_name(`${this.var.name}_transition`);
@ -949,14 +1025,12 @@ export default class ElementWrapper extends Wrapper {
${outro_block} ${outro_block}
} }
`); `);
} } else {
else {
block.chunks.intro.push(intro_block); block.chunks.intro.push(intro_block);
block.chunks.outro.push(outro_block); block.chunks.outro.push(outro_block);
} }
block.chunks.destroy.push(b`if (detaching && ${name}) ${name}.end();`); block.chunks.destroy.push(b`if (detaching && ${name}) ${name}.end();`);
} } else {
else {
const intro_name = intro && block.get_unique_name(`${this.var.name}_intro`); const intro_name = intro && block.get_unique_name(`${this.var.name}_intro`);
const outro_name = outro && block.get_unique_name(`${this.var.name}_outro`); const outro_name = outro && block.get_unique_name(`${this.var.name}_outro`);
if (intro) { if (intro) {
@ -974,8 +1048,7 @@ export default class ElementWrapper extends Wrapper {
}); });
`; `;
block.chunks.outro.push(b`if (${intro_name}) ${intro_name}.invalidate();`); block.chunks.outro.push(b`if (${intro_name}) ${intro_name}.invalidate();`);
} } else {
else {
intro_block = b` intro_block = b`
if (!${intro_name}) { if (!${intro_name}) {
@add_render_callback(() => { @add_render_callback(() => {
@ -1019,16 +1092,17 @@ export default class ElementWrapper extends Wrapper {
block.chunks.destroy.push(b`if (detaching && ${outro_name}) ${outro_name}.end();`); block.chunks.destroy.push(b`if (detaching && ${outro_name}) ${outro_name}.end();`);
} }
} }
if ((intro && intro.expression && intro.expression.dependencies.size) || if (
(outro && outro.expression && outro.expression.dependencies.size)) { (intro && intro.expression && intro.expression.dependencies.size) ||
(outro && outro.expression && outro.expression.dependencies.size)
) {
block.maintain_context = true; block.maintain_context = true;
} }
} }
/** @param {import('../../Block.js').default} block */ /** @param {import('../../Block.js').default} block */
add_animation(block) { add_animation(block) {
if (!this.node.animation) if (!this.node.animation) return;
return;
const { outro } = this.node; const { outro } = this.node;
const rect = block.get_unique_name('rect'); const rect = block.get_unique_name('rect');
const stop_animation = block.get_unique_name('stop_animation'); const stop_animation = block.get_unique_name('stop_animation');
@ -1058,8 +1132,7 @@ export default class ElementWrapper extends Wrapper {
block.chunks.measure.push(b`${params_var} = ${params};`); block.chunks.measure.push(b`${params_var} = ${params};`);
params = params_var; params = params_var;
} }
} } else {
else {
params = x`{}`; params = x`{}`;
} }
const name = this.renderer.reference(this.node.animation.name); const name = this.renderer.reference(this.node.animation.name);
@ -1079,8 +1152,7 @@ export default class ElementWrapper extends Wrapper {
if (expression) { if (expression) {
snippet = expression.manipulate(block); snippet = expression.manipulate(block);
dependencies = expression.dependencies; dependencies = expression.dependencies;
} } else {
else {
snippet = name; snippet = name;
dependencies = new Set([name]); dependencies = new Set([name]);
} }
@ -1088,8 +1160,7 @@ export default class ElementWrapper extends Wrapper {
block.chunks.hydrate.push(updater); block.chunks.hydrate.push(updater);
if ((this.node.is_dynamic_element || has_spread) && this.has_dynamic_attribute) { if ((this.node.is_dynamic_element || has_spread) && this.has_dynamic_attribute) {
block.chunks.update.push(updater); block.chunks.update.push(updater);
} } else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
else if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
const all_dependencies = this.class_dependencies.concat(...dependencies); const all_dependencies = this.class_dependencies.concat(...dependencies);
let condition = block.renderer.dirty(all_dependencies); let condition = block.renderer.dirty(all_dependencies);
if (block.has_outros) { if (block.has_outros) {
@ -1133,7 +1204,9 @@ export default class ElementWrapper extends Wrapper {
cached_snippet = block.get_unique_name(`style_${name.replace(regex_minus_signs, '_')}`); cached_snippet = block.get_unique_name(`style_${name.replace(regex_minus_signs, '_')}`);
block.add_variable(cached_snippet, snippet); block.add_variable(cached_snippet, snippet);
} }
const updater = b `@set_style(${this.var}, "${name}", ${should_cache ? cached_snippet : snippet}, ${important ? 1 : null})`; const updater = b`@set_style(${this.var}, "${name}", ${
should_cache ? cached_snippet : snippet
}, ${important ? 1 : null})`;
block.chunks.hydrate.push(updater); block.chunks.hydrate.push(updater);
const self_deps = expression.dynamic_dependencies(); const self_deps = expression.dynamic_dependencies();
const all_deps = new Set([...self_deps, ...this.dynamic_style_dependencies]); const all_deps = new Set([...self_deps, ...this.dynamic_style_dependencies]);
@ -1148,10 +1221,8 @@ export default class ElementWrapper extends Wrapper {
}`); }`);
} }
block.chunks.update.push(updater); block.chunks.update.push(updater);
} } else {
else { if (all_deps.size === 0) return;
if (all_deps.size === 0)
return;
if (should_cache) { if (should_cache) {
condition = x`${condition} && ${cached_snippet} !== (${cached_snippet} = ${snippet})`; condition = x`${condition} && ${cached_snippet} !== (${cached_snippet} = ${snippet})`;
} }
@ -1196,8 +1267,7 @@ function to_html(wrappers, block, literal, state, can_use_raw_text) {
wrappers.forEach((wrapper) => { wrappers.forEach((wrapper) => {
if (wrapper instanceof CommentWrapper) { if (wrapper instanceof CommentWrapper) {
state.quasi.value.raw += wrapper.text(); state.quasi.value.raw += wrapper.text();
} } else if (wrapper instanceof TextWrapper) {
else if (wrapper instanceof TextWrapper) {
// Don't add the <pre>/<textarea> newline logic here because pre/textarea.innerHTML // Don't add the <pre>/<textarea> newline logic here because pre/textarea.innerHTML
// would keep the leading newline, too, only someParent.innerHTML = '..<pre/textarea>..' won't // would keep the leading newline, too, only someParent.innerHTML = '..<pre/textarea>..' won't
if (wrapper.use_space()) { if (wrapper.use_space()) {
@ -1205,30 +1275,30 @@ function to_html(wrappers, block, literal, state, can_use_raw_text) {
state.quasi.value.raw += ' '; state.quasi.value.raw += ' ';
return; return;
} }
const parent = /** @type {import('../../../nodes/Element.js').default} */ (wrapper.node.parent); const parent = /** @type {import('../../../nodes/Element.js').default} */ (
const raw = parent && (parent.name === 'script' || parent.name === 'style' || can_use_raw_text); wrapper.node.parent
);
const raw =
parent && (parent.name === 'script' || parent.name === 'style' || can_use_raw_text);
state.quasi.value.raw += (raw ? wrapper.data : escape_html(wrapper.data)) state.quasi.value.raw += (raw ? wrapper.data : escape_html(wrapper.data))
.replace(regex_backslashes, '\\\\') .replace(regex_backslashes, '\\\\')
.replace(regex_backticks, '\\`') .replace(regex_backticks, '\\`')
.replace(regex_dollar_signs, '\\$'); .replace(regex_dollar_signs, '\\$');
} } else if (wrapper instanceof MustacheTagWrapper || wrapper instanceof RawMustacheTagWrapper) {
else if (wrapper instanceof MustacheTagWrapper || wrapper instanceof RawMustacheTagWrapper) {
literal.quasis.push(state.quasi); literal.quasis.push(state.quasi);
literal.expressions.push(wrapper.node.expression.manipulate(block)); literal.expressions.push(wrapper.node.expression.manipulate(block));
state.quasi = { state.quasi = {
type: 'TemplateElement', type: 'TemplateElement',
value: { raw: '' } value: { raw: '' }
}; };
} } else if (wrapper.node.name === 'noscript') {
else if (wrapper.node.name === 'noscript') {
// do nothing // do nothing
} } else {
else {
const nodeName = wrapper.node.name; const nodeName = wrapper.node.name;
// element // element
state.quasi.value.raw += `<${nodeName}`; state.quasi.value.raw += `<${nodeName}`;
const is_empty_textarea = nodeName === 'textarea' && wrapper.fragment.nodes.length === 0; const is_empty_textarea = nodeName === 'textarea' && wrapper.fragment.nodes.length === 0;
( /** @type {ElementWrapper} */(wrapper)).attributes.forEach((attr) => { /** @type {ElementWrapper} */ (wrapper).attributes.forEach((attr) => {
if (is_empty_textarea && attr.node.name === 'value') { if (is_empty_textarea && attr.node.name === 'value') {
// The value attribute of <textarea> renders as content. // The value attribute of <textarea> renders as content.
return; return;
@ -1243,9 +1313,11 @@ function to_html(wrappers, block, literal, state, can_use_raw_text) {
// Two or more leading newlines are required to restore the leading newline immediately after `<pre>`. // Two or more leading newlines are required to restore the leading newline immediately after `<pre>`.
// see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element // see https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
const first = wrapper.fragment.nodes[0]; const first = wrapper.fragment.nodes[0];
if (first && if (
first &&
first.node.type === 'Text' && first.node.type === 'Text' &&
regex_starts_with_newline.test(first.node.data)) { regex_starts_with_newline.test(first.node.data)
) {
state.quasi.value.raw += '\n'; state.quasi.value.raw += '\n';
} }
} }
@ -1263,11 +1335,15 @@ function to_html(wrappers, block, literal, state, can_use_raw_text) {
} }
} }
to_html( to_html(
/** @type {Array<ElementWrapper | import('../Text.js').default>} */ (
/** @type {Array<ElementWrapper | import('../Text.js').default>} */ (wrapper.fragment.nodes), block, literal, state); wrapper.fragment.nodes
),
block,
literal,
state
);
state.quasi.value.raw += `</${nodeName}>`; state.quasi.value.raw += `</${nodeName}>`;
} } else {
else {
state.quasi.value.raw += '/>'; state.quasi.value.raw += '/>';
} }
} }
@ -1284,8 +1360,7 @@ function to_html_for_attr_value(attr, block, literal, state) {
attr.node.chunks.forEach((chunk) => { attr.node.chunks.forEach((chunk) => {
if (chunk.type === 'Text') { if (chunk.type === 'Text') {
state.quasi.value.raw += escape_html(chunk.data); state.quasi.value.raw += escape_html(chunk.data);
} } else {
else {
literal.quasis.push(state.quasi); literal.quasis.push(state.quasi);
literal.expressions.push(chunk.manipulate(block)); literal.expressions.push(chunk.manipulate(block));
state.quasi = { state.quasi = {
@ -1296,9 +1371,6 @@ function to_html_for_attr_value(attr, block, literal, state) {
}); });
} }
/** @typedef {Object} BindingGroup /** @typedef {Object} BindingGroup
* @property {string[]} events * @property {string[]} events
* @property {Binding[]} bindings * @property {Binding[]} bindings

Loading…
Cancel
Save