Convert src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts to JavaScript

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

@ -37,6 +37,7 @@ import {
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';
const regex_contains_radio_or_checkbox_or_file = /radio|checkbox|file/; const regex_contains_radio_or_checkbox_or_file = /radio|checkbox|file/;
const regex_contains_radio_or_checkbox_or_range_or_file = /radio|checkbox|range|file/; const regex_contains_radio_or_checkbox_or_range_or_file = /radio|checkbox|range|file/;
const events = [ const events = [
@ -504,6 +505,7 @@ 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') {
/** @type {import('estree').Node} */
let text = string_literal( let text = string_literal(
/** @type {import('../Text.js').default} */ (this.fragment.nodes[0]).data /** @type {import('../Text.js').default} */ (this.fragment.nodes[0]).data
); );
@ -525,6 +527,7 @@ export default class ElementWrapper extends Wrapper {
value: { raw: '' } value: { raw: '' }
} }
}; };
/** @type {import('estree').Node} */
let literal = { let literal = {
type: 'TemplateLiteral', type: 'TemplateLiteral',
expressions: [], expressions: [],
@ -633,15 +636,15 @@ export default class ElementWrapper extends Wrapper {
}: true` }: true`
); );
/** @type {Class<x>>} */ /** @type {string | ReturnType<typeof 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 { } else {
reference = `"${( reference = `"${
/** @type {String} */ (this.node.tag_expr.node.value) || '' /** @type {String} */ (this.node.tag_expr.node.value || '').toUpperCase()
).toUpperCase()}"`; }"`;
} }
} else if (this.node.namespace) { } else if (this.node.namespace) {
reference = x`${this.node.tag_expr.manipulate(block)}`; reference = x`${this.node.tag_expr.manipulate(block)}`;
@ -660,6 +663,8 @@ export default class ElementWrapper extends Wrapper {
/** @param {import('../../Block.js').default} block */ /** @param {import('../../Block.js').default} block */
add_directives_in_order(block) { add_directives_in_order(block) {
/** @typedef {EventHandler | BindingGroup | Binding | Action} OrderedAttribute */
const binding_groups = events const binding_groups = events
.map((event) => ({ .map((event) => ({
events: event.event_names, events: event.event_names,
@ -1121,7 +1126,7 @@ export default class ElementWrapper extends Wrapper {
${outro && b`@add_transform(${this.var}, ${rect});`} ${outro && b`@add_transform(${this.var}, ${rect});`}
`); `);
/** @type {Class<x>>} */ /** @type {import('estree').Node | ReturnType<typeof x>} */
let params; let params;
if (this.node.animation.expression) { if (this.node.animation.expression) {
params = this.node.animation.expression.manipulate(block); params = this.node.animation.expression.manipulate(block);

@ -1,114 +1,94 @@
import Wrapper from '../shared/Wrapper'; import Wrapper from '../shared/Wrapper.js';
import BindingWrapper from '../Element/Binding'; import BindingWrapper from '../Element/Binding.js';
import Renderer from '../../Renderer'; import SlotTemplateWrapper from '../SlotTemplate.js';
import Block from '../../Block'; import { sanitize } from '../../../../utils/names.js';
import InlineComponent from '../../../nodes/InlineComponent'; import add_to_set from '../../../utils/add_to_set.js';
import FragmentWrapper from '../Fragment';
import SlotTemplateWrapper from '../SlotTemplate';
import { sanitize } from '../../../../utils/names';
import add_to_set from '../../../utils/add_to_set';
import { b, x, p } from 'code-red'; import { b, x, p } from 'code-red';
import Attribute from '../../../nodes/Attribute'; import is_dynamic from '../shared/is_dynamic.js';
import TemplateScope from '../../../nodes/shared/TemplateScope'; import bind_this from '../shared/bind_this.js';
import is_dynamic from '../shared/is_dynamic'; import EventHandler from '../Element/EventHandler.js';
import bind_this from '../shared/bind_this';
import { Node, Identifier, ObjectExpression } from 'estree';
import EventHandler from '../Element/EventHandler';
import { extract_names } from 'periscopic'; import { extract_names } from 'periscopic';
import mark_each_block_bindings from '../shared/mark_each_block_bindings'; import mark_each_block_bindings from '../shared/mark_each_block_bindings.js';
import { string_to_member_expression } from '../../../utils/string_to_member_expression'; import { string_to_member_expression } from '../../../utils/string_to_member_expression.js';
import SlotTemplate from '../../../nodes/SlotTemplate'; import { is_head } from '../shared/is_head.js';
import { is_head } from '../shared/is_head'; import compiler_warnings from '../../../compiler_warnings.js';
import compiler_warnings from '../../../compiler_warnings'; import { namespaces } from '../../../../utils/namespaces.js';
import { namespaces } from '../../../../utils/namespaces'; import { extract_ignores_above_node } from '../../../../utils/extract_svelte_ignore.js';
import { extract_ignores_above_node } from '../../../../utils/extract_svelte_ignore';
type SlotDefinition = {
block: Block;
scope: TemplateScope;
get_context?: Node;
get_changes?: Node;
};
const regex_invalid_variable_identifier_characters = /[^a-zA-Z_$]/g; const regex_invalid_variable_identifier_characters = /[^a-zA-Z_$]/g;
/** @extends Wrapper */
export default class InlineComponentWrapper extends Wrapper { export default class InlineComponentWrapper extends Wrapper {
var: Identifier;
slots: Map<string, SlotDefinition> = new Map();
node: InlineComponent;
fragment: FragmentWrapper;
children: Array<Wrapper | FragmentWrapper> = [];
constructor( /** @type {import('estree').Identifier} */
renderer: Renderer, var;
block: Block,
parent: Wrapper, /** @type {Map<string, SlotDefinition>} */
node: InlineComponent, slots = new Map();
strip_whitespace: boolean,
next_sibling: Wrapper /** @type {import('../../../nodes/InlineComponent.js').default} */
) { node;
super(renderer, block, parent, node);
/** @type {import('../Fragment.js').default} */
fragment;
/** @type {Array<Wrapper | import('../Fragment.js').default>} */
children = [];
/**
* @param {import('../../Renderer.js').default} renderer
* @param {import('../../Block.js').default} block
* @param {import('../shared/Wrapper.js').default} parent
* @param {import('../../../nodes/InlineComponent.js').default} node
* @param {boolean} strip_whitespace
* @param {import('../shared/Wrapper.js').default} next_sibling
*/
constructor(renderer, block, parent, node, strip_whitespace, next_sibling) {
super(renderer, block, parent, node);
if (this.node.expression) { if (this.node.expression) {
block.add_dependencies(this.node.expression.dependencies); block.add_dependencies(this.node.expression.dependencies);
} }
this.node.attributes.forEach((attr) => { this.node.attributes.forEach((attr) => {
block.add_dependencies(attr.dependencies); block.add_dependencies(attr.dependencies);
}); });
this.node.bindings.forEach((binding) => { this.node.bindings.forEach((binding) => {
if (binding.is_contextual) { if (binding.is_contextual) {
mark_each_block_bindings(this, binding); mark_each_block_bindings(this, binding);
} }
block.add_dependencies(binding.expression.dependencies); block.add_dependencies(binding.expression.dependencies);
}); });
this.node.handlers.forEach((handler) => { this.node.handlers.forEach((handler) => {
if (handler.expression) { if (handler.expression) {
block.add_dependencies(handler.expression.dependencies); block.add_dependencies(handler.expression.dependencies);
} }
}); });
this.node.css_custom_properties.forEach((attr) => { this.node.css_custom_properties.forEach((attr) => {
block.add_dependencies(attr.dependencies); block.add_dependencies(attr.dependencies);
}); });
this.var = { this.var = {
type: 'Identifier', type: 'Identifier',
name: (this.node.name === 'svelte:self' name: (this.node.name === 'svelte:self'
? renderer.component.name.name ? renderer.component.name.name
: this.node.name === 'svelte:component' : this.node.name === 'svelte:component'
? 'switch_instance' ? 'switch_instance'
: sanitize(this.node.name) : sanitize(this.node.name)).toLowerCase()
).toLowerCase()
}; };
if (this.node.children.length) { if (this.node.children.length) {
this.node.lets.forEach((l) => { this.node.lets.forEach((l) => {
extract_names(l.value || l.name).forEach((name) => { extract_names(l.value || l.name).forEach((name) => {
renderer.add_to_context(name, true); renderer.add_to_context(name, true);
}); });
}); });
this.children = this.node.children.map((child) => new SlotTemplateWrapper(renderer, block, this,
this.children = this.node.children.map( /** @type {import('../../../nodes/SlotTemplate.js').default} */ (child), strip_whitespace, next_sibling));
(child) =>
new SlotTemplateWrapper(
renderer,
block,
this,
child as SlotTemplate,
strip_whitespace,
next_sibling
)
);
} }
block.add_outro(); block.add_outro();
} }
set_slot(name: string, slot_definition: SlotDefinition) { /**
* @param {string} name
* @param {SlotDefinition} slot_definition
*/
set_slot(name, slot_definition) {
if (this.slots.has(name)) { if (this.slots.has(name)) {
if (name === 'default') { if (name === 'default') {
throw new Error('Found elements without slot attribute when using slot="default"'); throw new Error('Found elements without slot attribute when using slot="default"');
@ -117,14 +97,12 @@ export default class InlineComponentWrapper extends Wrapper {
} }
this.slots.set(name, slot_definition); this.slots.set(name, slot_definition);
} }
warn_if_reactive() { warn_if_reactive() {
const { name } = this.node; const { name } = this.node;
const variable = this.renderer.component.var_lookup.get(name); const variable = this.renderer.component.var_lookup.get(name);
if (!variable) { if (!variable) {
return; return;
} }
const ignores = extract_ignores_above_node(this.node); const ignores = extract_ignores_above_node(this.node);
this.renderer.component.push_ignores(ignores); this.renderer.component.push_ignores(ignores);
if (variable.reassigned || variable.export_name || variable.is_reactive_dependency) { if (variable.reassigned || variable.export_name || variable.is_reactive_dependency) {
@ -133,30 +111,33 @@ export default class InlineComponentWrapper extends Wrapper {
this.renderer.component.pop_ignores(); this.renderer.component.pop_ignores();
} }
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { /**
* @param {import('../../Block.js').default} block
* @param {import('estree').Identifier} parent_node
* @param {import('estree').Identifier} parent_nodes
*/
render(block, parent_node, parent_nodes) {
this.warn_if_reactive(); this.warn_if_reactive();
const { renderer } = this; const { renderer } = this;
const { component } = renderer; const { component } = renderer;
const name = this.var; const name = this.var;
block.add_variable(name); block.add_variable(name);
const component_opts = /** @type {import('estree').ObjectExpression} */ (x `{}`);
const component_opts = x`{}` as ObjectExpression; /** @type {Array<Node | import('estree').Node[]>} */
const statements = [];
const statements: Array<Node | Node[]> = [];
const updates: Array<Node | Node[]> = [];
/** @type {Array<Node | import('estree').Node[]>} */
const updates = [];
this.children.forEach((child) => { this.children.forEach((child) => {
this.renderer.add_to_context('$$scope', true); this.renderer.add_to_context('$$scope', true);
child.render(block, null, x`#nodes` as Identifier); child.render(block, null, /** @type {import('estree').Identifier} */ (x `#nodes`));
}); });
let props: Identifier | undefined; /** @type {import('estree').Identifier | undefined} */
let props;
const name_changes = block.get_unique_name(`${name.name}_changes`); const name_changes = block.get_unique_name(`${name.name}_changes`);
const uses_spread = !!this.node.attributes.find((a) => a.is_spread); const uses_spread = !!this.node.attributes.find((a) => a.is_spread);
// removing empty slot // removing empty slot
for (const slot of this.slots.keys()) { for (const slot of this.slots.keys()) {
if (!this.slots.get(slot).block.has_content()) { if (!this.slots.get(slot).block.has_content()) {
@ -164,7 +145,6 @@ export default class InlineComponentWrapper extends Wrapper {
this.slots.delete(slot); this.slots.delete(slot);
} }
} }
const has_css_custom_properties = this.node.css_custom_properties.length > 0; const has_css_custom_properties = this.node.css_custom_properties.length > 0;
const is_svg_namespace = this.node.namespace === namespaces.svg; const is_svg_namespace = this.node.namespace === namespaces.svg;
const css_custom_properties_wrapper_element = is_svg_namespace ? 'g' : 'div'; const css_custom_properties_wrapper_element = is_svg_namespace ? 'g' : 'div';
@ -174,195 +154,163 @@ export default class InlineComponentWrapper extends Wrapper {
if (has_css_custom_properties) { if (has_css_custom_properties) {
block.add_variable(css_custom_properties_wrapper); block.add_variable(css_custom_properties_wrapper);
} }
const initial_props = this.slots.size > 0
const initial_props =
this.slots.size > 0
? [ ? [
p`$$slots: { p `$$slots: {
${Array.from(this.slots).map(([name, slot]) => { ${Array.from(this.slots).map(([name, slot]) => {
return p`${name}: [${slot.block.name}, ${slot.get_context || null}, ${ return p `${name}: [${slot.block.name}, ${slot.get_context || null}, ${slot.get_changes || null}]`;
slot.get_changes || null
}]`;
})} })}
}`, }`,
p`$$scope: { p `$$scope: {
ctx: #ctx ctx: #ctx
}` }`
] ]
: []; : [];
const attribute_object = uses_spread const attribute_object = uses_spread
? x`{ ${initial_props} }` ? x `{ ${initial_props} }`
: x`{ : x `{
${this.node.attributes.map((attr) => p`${attr.name}: ${attr.get_value(block)}`)}, ${this.node.attributes.map((attr) => p `${attr.name}: ${attr.get_value(block)}`)},
${initial_props} ${initial_props}
}`; }`;
if (this.node.attributes.length || this.node.bindings.length || initial_props.length) { if (this.node.attributes.length || this.node.bindings.length || initial_props.length) {
if (!uses_spread && this.node.bindings.length === 0) { if (!uses_spread && this.node.bindings.length === 0) {
component_opts.properties.push(p`props: ${attribute_object}`); component_opts.properties.push(p `props: ${attribute_object}`);
} else { }
else {
props = block.get_unique_name(`${name.name}_props`); props = block.get_unique_name(`${name.name}_props`);
component_opts.properties.push(p`props: ${props}`); component_opts.properties.push(p `props: ${props}`);
} }
} }
if (component.compile_options.dev) { if (component.compile_options.dev) {
// TODO this is a terrible hack, but without it the component // TODO this is a terrible hack, but without it the component
// will complain that options.target is missing. This would // will complain that options.target is missing. This would
// work better if components had separate public and private // work better if components had separate public and private
// APIs // APIs
component_opts.properties.push(p`$$inline: true`); component_opts.properties.push(p `$$inline: true`);
} }
const fragment_dependencies = new Set(this.slots.size ? ['$$scope'] : []); const fragment_dependencies = new Set(this.slots.size ? ['$$scope'] : []);
this.slots.forEach((slot) => { this.slots.forEach((slot) => {
slot.block.dependencies.forEach((name) => { slot.block.dependencies.forEach((name) => {
const is_let = slot.scope.is_let(name); const is_let = slot.scope.is_let(name);
const variable = renderer.component.var_lookup.get(name); const variable = renderer.component.var_lookup.get(name);
if (is_let || is_dynamic(variable))
if (is_let || is_dynamic(variable)) fragment_dependencies.add(name); fragment_dependencies.add(name);
}); });
}); });
const dynamic_attributes = this.node.attributes.filter((a) => a.get_dependencies().length > 0); const dynamic_attributes = this.node.attributes.filter((a) => a.get_dependencies().length > 0);
if (!uses_spread &&
if (
!uses_spread &&
(dynamic_attributes.length > 0 || (dynamic_attributes.length > 0 ||
this.node.bindings.length > 0 || this.node.bindings.length > 0 ||
fragment_dependencies.size > 0) fragment_dependencies.size > 0)) {
) { updates.push(b `const ${name_changes} = {};`);
updates.push(b`const ${name_changes} = {};`);
} }
if (this.node.attributes.length) { if (this.node.attributes.length) {
if (uses_spread) { if (uses_spread) {
const levels = block.get_unique_name(`${this.var.name}_spread_levels`); const levels = block.get_unique_name(`${this.var.name}_spread_levels`);
const initial_props = []; const initial_props = [];
const changes = []; const changes = [];
const all_dependencies: Set<string> = new Set(); /** @type {Set<string>} */
const all_dependencies = new Set();
this.node.attributes.forEach((attr) => { this.node.attributes.forEach((attr) => {
add_to_set(all_dependencies, attr.dependencies); add_to_set(all_dependencies, attr.dependencies);
}); });
this.node.attributes.forEach((attr, i) => { this.node.attributes.forEach((attr, i) => {
const { name, dependencies } = attr; const { name, dependencies } = attr;
const condition = dependencies.size > 0 && dependencies.size !== all_dependencies.size
const condition =
dependencies.size > 0 && dependencies.size !== all_dependencies.size
? renderer.dirty(Array.from(dependencies)) ? renderer.dirty(Array.from(dependencies))
: null; : null;
const unchanged = dependencies.size === 0; const unchanged = dependencies.size === 0;
let change_object: Node | ReturnType<typeof x>; /** @type {Class<x>>} */
let change_object;
if (attr.is_spread) { if (attr.is_spread) {
const value = attr.expression.manipulate(block); const value = attr.expression.manipulate(block);
initial_props.push(value); initial_props.push(value);
let value_object = value; let value_object = value;
if (attr.expression.node.type !== 'ObjectExpression') { if (attr.expression.node.type !== 'ObjectExpression') {
value_object = x`@get_spread_object(${value})`; value_object = x `@get_spread_object(${value})`;
} }
change_object = value_object; change_object = value_object;
} else { }
const obj = x`{ ${name}: ${attr.get_value(block)} }`; else {
const obj = x `{ ${name}: ${attr.get_value(block)} }`;
initial_props.push(obj); initial_props.push(obj);
change_object = obj; change_object = obj;
} }
changes.push(unchanged
changes.push( ? x `${levels}[${i}]`
unchanged
? x`${levels}[${i}]`
: condition : condition
? x`${condition} && ${change_object}` ? x `${condition} && ${change_object}`
: change_object : change_object);
);
}); });
block.chunks.init.push(b `
block.chunks.init.push(b`
const ${levels} = [ const ${levels} = [
${initial_props} ${initial_props}
]; ];
`); `);
statements.push(b `
statements.push(b`
for (let #i = 0; #i < ${levels}.length; #i += 1) { for (let #i = 0; #i < ${levels}.length; #i += 1) {
${props} = @assign(${props}, ${levels}[#i]); ${props} = @assign(${props}, ${levels}[#i]);
} }
`); `);
if (all_dependencies.size) { if (all_dependencies.size) {
const condition = renderer.dirty(Array.from(all_dependencies)); const condition = renderer.dirty(Array.from(all_dependencies));
updates.push(b `
updates.push(b`
const ${name_changes} = ${condition} ? @get_spread_update(${levels}, [ const ${name_changes} = ${condition} ? @get_spread_update(${levels}, [
${changes} ${changes}
]) : {} ]) : {}
`); `);
} else { }
updates.push(b` else {
updates.push(b `
const ${name_changes} = {}; const ${name_changes} = {};
`); `);
} }
} else { }
dynamic_attributes.forEach((attribute: Attribute) => { else {
dynamic_attributes.forEach((attribute) => {
const dependencies = attribute.get_dependencies(); const dependencies = attribute.get_dependencies();
if (dependencies.length > 0) { if (dependencies.length > 0) {
const condition = renderer.dirty(dependencies); const condition = renderer.dirty(dependencies);
updates.push(b `
updates.push(b`
if (${condition}) ${name_changes}.${attribute.name} = ${attribute.get_value(block)}; if (${condition}) ${name_changes}.${attribute.name} = ${attribute.get_value(block)};
`); `);
} }
}); });
} }
} }
if (fragment_dependencies.size > 0) { if (fragment_dependencies.size > 0) {
updates.push(b` updates.push(b `
if (${renderer.dirty(Array.from(fragment_dependencies))}) { if (${renderer.dirty(Array.from(fragment_dependencies))}) {
${name_changes}.$$scope = { dirty: #dirty, ctx: #ctx }; ${name_changes}.$$scope = { dirty: #dirty, ctx: #ctx };
}`); }`);
} }
const munged_bindings = this.node.bindings.map((binding) => { const munged_bindings = this.node.bindings.map((binding) => {
component.has_reactive_assignments = true; component.has_reactive_assignments = true;
if (binding.name === 'this') { if (binding.name === 'this') {
return bind_this(component, block, new BindingWrapper(block, binding, this), this.var); return bind_this(component, block, new BindingWrapper(block, binding, this), this.var);
} }
const id = component.get_unique_name(`${this.var.name}_${binding.name}_binding`); const id = component.get_unique_name(`${this.var.name}_${binding.name}_binding`);
renderer.add_to_context(id.name); renderer.add_to_context(id.name);
const callee = renderer.reference(id); const callee = renderer.reference(id);
const updating = block.get_unique_name(`updating_${binding.name}`); const updating = block.get_unique_name(`updating_${binding.name}`);
block.add_variable(updating); block.add_variable(updating);
const snippet = binding.expression.manipulate(block); const snippet = binding.expression.manipulate(block);
statements.push(b `
statements.push(b`
if (${snippet} !== void 0) { if (${snippet} !== void 0) {
${props}.${binding.name} = ${snippet}; ${props}.${binding.name} = ${snippet};
}`); }`);
updates.push(b `
updates.push(b`
if (!${updating} && ${renderer.dirty(Array.from(binding.expression.dependencies))}) { if (!${updating} && ${renderer.dirty(Array.from(binding.expression.dependencies))}) {
${updating} = true; ${updating} = true;
${name_changes}.${binding.name} = ${snippet}; ${name_changes}.${binding.name} = ${snippet};
@add_flush_callback(() => ${updating} = false); @add_flush_callback(() => ${updating} = false);
} }
`); `);
const contextual_dependencies = Array.from(binding.expression.contextual_dependencies); const contextual_dependencies = Array.from(binding.expression.contextual_dependencies);
const dependencies = Array.from(binding.expression.dependencies); const dependencies = Array.from(binding.expression.dependencies);
let lhs = binding.raw_expression; let lhs = binding.raw_expression;
if (binding.is_contextual && binding.expression.node.type === 'Identifier') { if (binding.is_contextual && binding.expression.node.type === 'Identifier') {
// bind:x={y} — we can't just do `y = x`, we need to // bind:x={y} — we can't just do `y = x`, we need to
// to `array[index] = x; // to `array[index] = x;
@ -372,91 +320,72 @@ export default class InlineComponentWrapper extends Wrapper {
contextual_dependencies.push(object.name, property.name); contextual_dependencies.push(object.name, property.name);
} }
const params: Identifier[] = [x`#value` as Identifier]; /** @type {import('estree').Identifier[]} */
const args = [x`#value`]; const params = [/** @type {import('estree').Identifier} */ (x `#value`)];
const args = [x `#value`];
if (contextual_dependencies.length > 0) { if (contextual_dependencies.length > 0) {
contextual_dependencies.forEach((name) => { contextual_dependencies.forEach((name) => {
params.push({ params.push({
type: 'Identifier', type: 'Identifier',
name name
}); });
renderer.add_to_context(name, true); renderer.add_to_context(name, true);
args.push(renderer.reference(name)); args.push(renderer.reference(name));
}); });
block.maintain_context = true; // TODO put this somewhere more logical block.maintain_context = true; // TODO put this somewhere more logical
} }
block.chunks.init.push(b `
block.chunks.init.push(b`
function ${id}(#value) { function ${id}(#value) {
${callee}(${args}); ${callee}(${args});
} }
`); `);
let invalidate_binding = b `
let invalidate_binding = b`
${lhs} = #value; ${lhs} = #value;
${renderer.invalidate(dependencies[0])}; ${renderer.invalidate(dependencies[0])};
`; `;
if (binding.expression.node.type === 'MemberExpression') { if (binding.expression.node.type === 'MemberExpression') {
invalidate_binding = b` invalidate_binding = b `
if ($$self.$$.not_equal(${lhs}, #value)) { if ($$self.$$.not_equal(${lhs}, #value)) {
${invalidate_binding} ${invalidate_binding}
} }
`; `;
} }
const body = b `
const body = b`
function ${id}(${params}) { function ${id}(${params}) {
${invalidate_binding} ${invalidate_binding}
} }
`; `;
component.partly_hoisted.push(body); component.partly_hoisted.push(body);
return b `@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}));`;
return b`@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}));`;
}); });
const munged_handlers = this.node.handlers.map((handler) => { const munged_handlers = this.node.handlers.map((handler) => {
const event_handler = new EventHandler(handler, this); const event_handler = new EventHandler(handler, this);
let snippet = event_handler.get_snippet(block); let snippet = event_handler.get_snippet(block);
if (handler.modifiers.has('once')) snippet = x`@once(${snippet})`; if (handler.modifiers.has('once'))
snippet = x `@once(${snippet})`;
return b`${name}.$on("${handler.name}", ${snippet});`; return b `${name}.$on("${handler.name}", ${snippet});`;
}); });
const mount_target = has_css_custom_properties const mount_target = has_css_custom_properties
? css_custom_properties_wrapper ? css_custom_properties_wrapper
: parent_node || '#target'; : parent_node || '#target';
const mount_anchor = has_css_custom_properties ? 'null' : parent_node ? 'null' : '#anchor'; const mount_anchor = has_css_custom_properties ? 'null' : parent_node ? 'null' : '#anchor';
const to_claim = parent_nodes && this.renderer.options.hydratable; const to_claim = parent_nodes && this.renderer.options.hydratable;
let claim_nodes = parent_nodes; let claim_nodes = parent_nodes;
if (this.node.name === 'svelte:component') { if (this.node.name === 'svelte:component') {
const switch_value = block.get_unique_name('switch_value'); const switch_value = block.get_unique_name('switch_value');
const switch_props = block.get_unique_name('switch_props'); const switch_props = block.get_unique_name('switch_props');
const snippet = this.node.expression.manipulate(block); const snippet = this.node.expression.manipulate(block);
const dependencies = this.node.expression.dynamic_dependencies(); const dependencies = this.node.expression.dynamic_dependencies();
if (has_css_custom_properties) { if (has_css_custom_properties) {
this.set_css_custom_properties( this.set_css_custom_properties(block, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace);
block,
css_custom_properties_wrapper,
css_custom_properties_wrapper_element,
is_svg_namespace
);
} }
block.chunks.init.push(b `
block.chunks.init.push(b`
var ${switch_value} = ${snippet}; var ${switch_value} = ${snippet};
function ${switch_props}(#ctx) { function ${switch_props}(#ctx) {
${ ${(this.node.attributes.length > 0 || this.node.bindings.length > 0) &&
(this.node.attributes.length > 0 || this.node.bindings.length > 0) && b `
b` ${props && b `let ${props} = ${attribute_object};`}`}
${props && b`let ${props} = ${attribute_object};`}`
}
${statements} ${statements}
return ${component_opts}; return ${component_opts};
} }
@ -468,63 +397,41 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_handlers} ${munged_handlers}
} }
`); `);
block.chunks.create.push(b `if (${name}) @create_component(${name}.$$.fragment);`);
block.chunks.create.push(b`if (${name}) @create_component(${name}.$$.fragment);`);
if (css_custom_properties_wrapper) if (css_custom_properties_wrapper)
this.create_css_custom_properties_wrapper_mount_chunk( this.create_css_custom_properties_wrapper_mount_chunk(block, parent_node, css_custom_properties_wrapper);
block, block.chunks.mount.push(b `if (${name}) @mount_component(${name}, ${mount_target}, ${mount_anchor});`);
parent_node,
css_custom_properties_wrapper
);
block.chunks.mount.push(
b`if (${name}) @mount_component(${name}, ${mount_target}, ${mount_anchor});`
);
if (to_claim) { if (to_claim) {
if (css_custom_properties_wrapper) if (css_custom_properties_wrapper)
claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk( claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace);
block, block.chunks.claim.push(b `if (${name}) @claim_component(${name}.$$.fragment, ${claim_nodes});`);
claim_nodes,
css_custom_properties_wrapper,
css_custom_properties_wrapper_element,
is_svg_namespace
);
block.chunks.claim.push(
b`if (${name}) @claim_component(${name}.$$.fragment, ${claim_nodes});`
);
} }
if (updates.length) { if (updates.length) {
block.chunks.update.push(b` block.chunks.update.push(b `
${updates} ${updates}
`); `);
} }
const tmp_anchor = this.get_or_create_anchor(block, parent_node, parent_nodes); const tmp_anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const anchor = has_css_custom_properties ? 'null' : tmp_anchor; const anchor = has_css_custom_properties ? 'null' : tmp_anchor;
const update_mount_node = has_css_custom_properties const update_mount_node = has_css_custom_properties
? css_custom_properties_wrapper ? css_custom_properties_wrapper
: this.get_update_mount_node(tmp_anchor); : this.get_update_mount_node(tmp_anchor);
const update_insert = const update_insert = css_custom_properties_wrapper &&
css_custom_properties_wrapper &&
(tmp_anchor.name !== 'null' (tmp_anchor.name !== 'null'
? b`@insert(${tmp_anchor}.parentNode, ${css_custom_properties_wrapper}, ${tmp_anchor});` ? b `@insert(${tmp_anchor}.parentNode, ${css_custom_properties_wrapper}, ${tmp_anchor});`
: b`@insert(${parent_node}, ${css_custom_properties_wrapper}, ${tmp_anchor});`); : b `@insert(${parent_node}, ${css_custom_properties_wrapper}, ${tmp_anchor});`);
let update_condition = x `${switch_value} !== (${switch_value} = ${snippet})`;
let update_condition = x`${switch_value} !== (${switch_value} = ${snippet})`;
if (dependencies.length > 0) { if (dependencies.length > 0) {
update_condition = x`${block.renderer.dirty(dependencies)} && ${update_condition}`; update_condition = x `${block.renderer.dirty(dependencies)} && ${update_condition}`;
} }
block.chunks.update.push(b `
block.chunks.update.push(b`
if (${update_condition}) { if (${update_condition}) {
if (${name}) { if (${name}) {
@group_outros(); @group_outros();
const old_component = ${name}; const old_component = ${name};
@transition_out(old_component.$$.fragment, 1, 0, () => { @transition_out(old_component.$$.fragment, 1, 0, () => {
@destroy_component(old_component, 1); @destroy_component(old_component, 1);
${has_css_custom_properties ? b`@detach(${update_mount_node})` : null} ${has_css_custom_properties ? b `@detach(${update_mount_node})` : null}
}); });
@check_outros(); @check_outros();
} }
@ -543,156 +450,123 @@ export default class InlineComponentWrapper extends Wrapper {
${name} = null; ${name} = null;
} }
} else if (${switch_value}) { } else if (${switch_value}) {
${updates.length > 0 && b`${name}.$set(${name_changes});`} ${updates.length > 0 && b `${name}.$set(${name_changes});`}
} }
`); `);
block.chunks.intro.push(b `
block.chunks.intro.push(b`
if (${name}) @transition_in(${name}.$$.fragment, #local); if (${name}) @transition_in(${name}.$$.fragment, #local);
`); `);
block.chunks.outro.push(b `if (${name}) @transition_out(${name}.$$.fragment, #local);`);
block.chunks.outro.push(b`if (${name}) @transition_out(${name}.$$.fragment, #local);`); block.chunks.destroy.push(b `if (${name}) @destroy_component(${name}, ${parent_node ? null : 'detaching'});`);
}
block.chunks.destroy.push( else {
b`if (${name}) @destroy_component(${name}, ${parent_node ? null : 'detaching'});` const expression = this.node.name === 'svelte:self'
);
} else {
const expression =
this.node.name === 'svelte:self'
? component.name ? component.name
: this.renderer.reference(string_to_member_expression(this.node.name)); : this.renderer.reference(string_to_member_expression(this.node.name));
block.chunks.init.push(b `
block.chunks.init.push(b` ${(this.node.attributes.length > 0 || this.node.bindings.length > 0) &&
${ b `
(this.node.attributes.length > 0 || this.node.bindings.length > 0) && ${props && b `let ${props} = ${attribute_object};`}`}
b`
${props && b`let ${props} = ${attribute_object};`}`
}
${statements} ${statements}
${name} = new ${expression}(${component_opts}); ${name} = new ${expression}(${component_opts});
${munged_bindings} ${munged_bindings}
${munged_handlers} ${munged_handlers}
`); `);
if (has_css_custom_properties) { if (has_css_custom_properties) {
this.set_css_custom_properties( this.set_css_custom_properties(block, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace);
block, }
css_custom_properties_wrapper, block.chunks.create.push(b `@create_component(${name}.$$.fragment);`);
css_custom_properties_wrapper_element,
is_svg_namespace
);
}
block.chunks.create.push(b`@create_component(${name}.$$.fragment);`);
if (css_custom_properties_wrapper) if (css_custom_properties_wrapper)
this.create_css_custom_properties_wrapper_mount_chunk( this.create_css_custom_properties_wrapper_mount_chunk(block, parent_node, css_custom_properties_wrapper);
block, block.chunks.mount.push(b `@mount_component(${name}, ${mount_target}, ${mount_anchor});`);
parent_node,
css_custom_properties_wrapper
);
block.chunks.mount.push(b`@mount_component(${name}, ${mount_target}, ${mount_anchor});`);
if (to_claim) { if (to_claim) {
if (css_custom_properties_wrapper) if (css_custom_properties_wrapper)
claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk( claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace);
block, block.chunks.claim.push(b `@claim_component(${name}.$$.fragment, ${claim_nodes});`);
claim_nodes,
css_custom_properties_wrapper,
css_custom_properties_wrapper_element,
is_svg_namespace
);
block.chunks.claim.push(b`@claim_component(${name}.$$.fragment, ${claim_nodes});`);
} }
block.chunks.intro.push(b `
block.chunks.intro.push(b`
@transition_in(${name}.$$.fragment, #local); @transition_in(${name}.$$.fragment, #local);
`); `);
if (updates.length) { if (updates.length) {
block.chunks.update.push(b` block.chunks.update.push(b `
${updates} ${updates}
${name}.$set(${name_changes}); ${name}.$set(${name_changes});
`); `);
} }
block.chunks.destroy.push(b `
block.chunks.destroy.push(b`
@destroy_component(${name}, ${parent_node ? null : 'detaching'}); @destroy_component(${name}, ${parent_node ? null : 'detaching'});
`); `);
block.chunks.outro.push(b `@transition_out(${name}.$$.fragment, #local);`);
block.chunks.outro.push(b`@transition_out(${name}.$$.fragment, #local);`);
} }
} }
private create_css_custom_properties_wrapper_mount_chunk( /**
block: Block, * @private
parent_node: Identifier, * @param {import('../../Block.js').default} block
css_custom_properties_wrapper: Identifier | null * @param {import('estree').Identifier} parent_node
) { * @param {import('estree').Identifier | null} css_custom_properties_wrapper
*/
create_css_custom_properties_wrapper_mount_chunk(block, parent_node, css_custom_properties_wrapper) {
if (parent_node) { if (parent_node) {
block.chunks.mount.push(b`@append(${parent_node}, ${css_custom_properties_wrapper})`); block.chunks.mount.push(b `@append(${parent_node}, ${css_custom_properties_wrapper})`);
if (is_head(parent_node)) { if (is_head(parent_node)) {
block.chunks.destroy.push(b`@detach(${css_custom_properties_wrapper});`); block.chunks.destroy.push(b `@detach(${css_custom_properties_wrapper});`);
} }
} else { }
block.chunks.mount.push(b`@insert(#target, ${css_custom_properties_wrapper}, #anchor);`); else {
block.chunks.mount.push(b `@insert(#target, ${css_custom_properties_wrapper}, #anchor);`);
// TODO we eventually need to consider what happens to elements // TODO we eventually need to consider what happens to elements
// that belong to the same outgroup as an outroing element... // that belong to the same outgroup as an outroing element...
block.chunks.destroy.push( block.chunks.destroy.push(b `if (detaching && ${this.var}) @detach(${css_custom_properties_wrapper});`);
b`if (detaching && ${this.var}) @detach(${css_custom_properties_wrapper});`
);
} }
} }
private create_css_custom_properties_wrapper_claim_chunk( /**
block: Block, * @private
parent_nodes: Identifier, * @param {import('../../Block.js').default} block
css_custom_properties_wrapper: Identifier | null, * @param {import('estree').Identifier} parent_nodes
css_custom_properties_wrapper_element: string, * @param {import('estree').Identifier | null} css_custom_properties_wrapper
is_svg_namespace: boolean * @param {string} css_custom_properties_wrapper_element
) { * @param {boolean} is_svg_namespace
*/
create_css_custom_properties_wrapper_claim_chunk(block, parent_nodes, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace) {
const nodes = block.get_unique_name(`${css_custom_properties_wrapper.name}_nodes`); const nodes = block.get_unique_name(`${css_custom_properties_wrapper.name}_nodes`);
const claim_element = is_svg_namespace ? x`@claim_svg_element` : x`@claim_element`; const claim_element = is_svg_namespace ? x `@claim_svg_element` : x `@claim_element`;
block.chunks.claim.push(b` block.chunks.claim.push(b `
${css_custom_properties_wrapper} = ${claim_element}(${parent_nodes}, "${css_custom_properties_wrapper_element.toUpperCase()}", { style: true }) ${css_custom_properties_wrapper} = ${claim_element}(${parent_nodes}, "${css_custom_properties_wrapper_element.toUpperCase()}", { style: true })
var ${nodes} = @children(${css_custom_properties_wrapper}); var ${nodes} = @children(${css_custom_properties_wrapper});
`); `);
return nodes; return nodes;
} }
private set_css_custom_properties( /**
block: Block, * @private
css_custom_properties_wrapper: Identifier, * @param {import('../../Block.js').default} block
css_custom_properties_wrapper_element: string, * @param {import('estree').Identifier} css_custom_properties_wrapper
is_svg_namespace: boolean * @param {string} css_custom_properties_wrapper_element
) { * @param {boolean} is_svg_namespace
const element = is_svg_namespace ? x`@svg_element` : x`@element`; */
block.chunks.create.push( set_css_custom_properties(block, css_custom_properties_wrapper, css_custom_properties_wrapper_element, is_svg_namespace) {
b`${css_custom_properties_wrapper} = ${element}("${css_custom_properties_wrapper_element}");` const element = is_svg_namespace ? x `@svg_element` : x `@element`;
); block.chunks.create.push(b `${css_custom_properties_wrapper} = ${element}("${css_custom_properties_wrapper_element}");`);
if (!is_svg_namespace) if (!is_svg_namespace)
block.chunks.hydrate.push( block.chunks.hydrate.push(b `@set_style(${css_custom_properties_wrapper}, "display", "contents");`);
b`@set_style(${css_custom_properties_wrapper}, "display", "contents");`
);
this.node.css_custom_properties.forEach((attr) => { this.node.css_custom_properties.forEach((attr) => {
const dependencies = attr.get_dependencies(); const dependencies = attr.get_dependencies();
const should_cache = attr.should_cache(); const should_cache = attr.should_cache();
const last = const last = should_cache &&
should_cache && block.get_unique_name(`${attr.name.replace(regex_invalid_variable_identifier_characters, '_')}_last`);
block.get_unique_name( if (should_cache)
`${attr.name.replace(regex_invalid_variable_identifier_characters, '_')}_last` block.add_variable(last);
);
if (should_cache) block.add_variable(last);
const value = attr.get_value(block); const value = attr.get_value(block);
const init = should_cache ? x`${last} = ${value}` : value; const init = should_cache ? x `${last} = ${value}` : value;
block.chunks.hydrate.push(b `@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${init});`);
block.chunks.hydrate.push(
b`@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${init});`
);
if (dependencies.length > 0) { if (dependencies.length > 0) {
let condition = block.renderer.dirty(dependencies); let condition = block.renderer.dirty(dependencies);
if (should_cache) condition = x`${condition} && (${last} !== (${last} = ${value}))`; if (should_cache)
block.chunks.update.push(b` condition = x `${condition} && (${last} !== (${last} = ${value}))`;
block.chunks.update.push(b `
if (${condition}) { if (${condition}) {
@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${should_cache ? last : value}); @set_style(${css_custom_properties_wrapper}, "${attr.name}", ${should_cache ? last : value});
} }
@ -701,3 +575,14 @@ export default class InlineComponentWrapper extends Wrapper {
}); });
} }
} }
/**
* @typedef {{
* block: Block;
* scope: TemplateScope;
* get_context?: Node;
* get_changes?: Node;
* }} SlotDefinition
*/

Loading…
Cancel
Save