|
|
|
|
@ -1,36 +1,44 @@
|
|
|
|
|
import Wrapper from './shared/Wrapper';
|
|
|
|
|
import Renderer from '../Renderer';
|
|
|
|
|
import Block from '../Block';
|
|
|
|
|
import Slot from '../../nodes/Slot';
|
|
|
|
|
import FragmentWrapper from './Fragment';
|
|
|
|
|
import Wrapper from './shared/Wrapper.js';
|
|
|
|
|
import FragmentWrapper from './Fragment.js';
|
|
|
|
|
import { b, p, x } from 'code-red';
|
|
|
|
|
import { sanitize } from '../../../utils/names';
|
|
|
|
|
import add_to_set from '../../utils/add_to_set';
|
|
|
|
|
import get_slot_data from '../../utils/get_slot_data';
|
|
|
|
|
import { is_reserved_keyword } from '../../utils/reserved_keywords';
|
|
|
|
|
import is_dynamic from './shared/is_dynamic';
|
|
|
|
|
import { Identifier, ObjectExpression, Node } from 'estree';
|
|
|
|
|
import create_debugging_comment from './shared/create_debugging_comment';
|
|
|
|
|
|
|
|
|
|
import { sanitize } from '../../../utils/names.js';
|
|
|
|
|
import add_to_set from '../../utils/add_to_set.js';
|
|
|
|
|
import get_slot_data from '../../utils/get_slot_data.js';
|
|
|
|
|
import { is_reserved_keyword } from '../../utils/reserved_keywords.js';
|
|
|
|
|
import is_dynamic from './shared/is_dynamic.js';
|
|
|
|
|
import create_debugging_comment from './shared/create_debugging_comment.js';
|
|
|
|
|
|
|
|
|
|
/** @extends Wrapper */
|
|
|
|
|
export default class SlotWrapper extends Wrapper {
|
|
|
|
|
node: Slot;
|
|
|
|
|
fragment: FragmentWrapper;
|
|
|
|
|
fallback: Block | null = null;
|
|
|
|
|
slot_block: Block;
|
|
|
|
|
|
|
|
|
|
var: Identifier = { type: 'Identifier', name: 'slot' };
|
|
|
|
|
dependencies: Set<string> = new Set(['$$scope']);
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
renderer: Renderer,
|
|
|
|
|
block: Block,
|
|
|
|
|
parent: Wrapper,
|
|
|
|
|
node: Slot,
|
|
|
|
|
strip_whitespace: boolean,
|
|
|
|
|
next_sibling: Wrapper
|
|
|
|
|
) {
|
|
|
|
|
super(renderer, block, parent, node);
|
|
|
|
|
|
|
|
|
|
/** @type {import('../../nodes/Slot.js').default} */
|
|
|
|
|
node;
|
|
|
|
|
|
|
|
|
|
/** @type {import('./Fragment.js').default} */
|
|
|
|
|
fragment;
|
|
|
|
|
|
|
|
|
|
/** @type {import('../Block.js').default | null} */
|
|
|
|
|
fallback = null;
|
|
|
|
|
|
|
|
|
|
/** @type {import('../Block.js').default} */
|
|
|
|
|
slot_block;
|
|
|
|
|
|
|
|
|
|
/** @type {import('estree').Identifier} */
|
|
|
|
|
var = { type: 'Identifier', name: 'slot' };
|
|
|
|
|
|
|
|
|
|
/** @type {Set<string>} */
|
|
|
|
|
dependencies = new Set(['$$scope']);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {import('../Renderer.js').default} renderer
|
|
|
|
|
* @param {import('../Block.js').default} block
|
|
|
|
|
* @param {import('./shared/Wrapper.js').default} parent
|
|
|
|
|
* @param {import('../../nodes/Slot.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.children.length) {
|
|
|
|
|
this.fallback = block.child({
|
|
|
|
|
comment: create_debugging_comment(this.node.children[0], this.renderer.component),
|
|
|
|
|
@ -39,141 +47,104 @@ export default class SlotWrapper extends Wrapper {
|
|
|
|
|
});
|
|
|
|
|
renderer.blocks.push(this.fallback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.fragment = new FragmentWrapper(
|
|
|
|
|
renderer,
|
|
|
|
|
this.fallback,
|
|
|
|
|
node.children,
|
|
|
|
|
this,
|
|
|
|
|
strip_whitespace,
|
|
|
|
|
next_sibling
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.fragment = new FragmentWrapper(renderer, this.fallback, node.children, this, strip_whitespace, next_sibling);
|
|
|
|
|
this.node.values.forEach((attribute) => {
|
|
|
|
|
add_to_set(this.dependencies, attribute.dependencies);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
block.add_dependencies(this.dependencies);
|
|
|
|
|
|
|
|
|
|
// we have to do this, just in case
|
|
|
|
|
block.add_intro();
|
|
|
|
|
block.add_outro();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
const { renderer } = this;
|
|
|
|
|
|
|
|
|
|
const { slot_name } = this.node;
|
|
|
|
|
|
|
|
|
|
if (this.slot_block) {
|
|
|
|
|
block = this.slot_block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let get_slot_changes_fn: Identifier | 'null';
|
|
|
|
|
let get_slot_spread_changes_fn: Identifier | undefined;
|
|
|
|
|
let get_slot_context_fn: Identifier | 'null';
|
|
|
|
|
|
|
|
|
|
if (this.node.values.size > 0) {
|
|
|
|
|
get_slot_changes_fn = renderer.component.get_unique_name(
|
|
|
|
|
`get_${sanitize(slot_name)}_slot_changes`
|
|
|
|
|
);
|
|
|
|
|
get_slot_context_fn = renderer.component.get_unique_name(
|
|
|
|
|
`get_${sanitize(slot_name)}_slot_context`
|
|
|
|
|
);
|
|
|
|
|
/** @type {import('estree').Identifier | 'null'} */
|
|
|
|
|
let get_slot_changes_fn;
|
|
|
|
|
|
|
|
|
|
const changes = x`{}` as ObjectExpression;
|
|
|
|
|
|
|
|
|
|
const spread_dynamic_dependencies = new Set<string>();
|
|
|
|
|
/** @type {import('estree').Identifier | undefined} */
|
|
|
|
|
let get_slot_spread_changes_fn;
|
|
|
|
|
|
|
|
|
|
/** @type {import('estree').Identifier | 'null'} */
|
|
|
|
|
let get_slot_context_fn;
|
|
|
|
|
if (this.node.values.size > 0) {
|
|
|
|
|
get_slot_changes_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_changes`);
|
|
|
|
|
get_slot_context_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_context`);
|
|
|
|
|
const changes = /** @type {import('estree').ObjectExpression} */ (x `{}`);
|
|
|
|
|
const spread_dynamic_dependencies = new Set();
|
|
|
|
|
this.node.values.forEach((attribute) => {
|
|
|
|
|
if (attribute.type === 'Spread') {
|
|
|
|
|
add_to_set(
|
|
|
|
|
spread_dynamic_dependencies,
|
|
|
|
|
Array.from(attribute.dependencies).filter((name) => this.is_dependency_dynamic(name))
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
const dynamic_dependencies = Array.from(attribute.dependencies).filter((name) =>
|
|
|
|
|
this.is_dependency_dynamic(name)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
add_to_set(spread_dynamic_dependencies, Array.from(attribute.dependencies).filter((name) => this.is_dependency_dynamic(name)));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const dynamic_dependencies = Array.from(attribute.dependencies).filter((name) => this.is_dependency_dynamic(name));
|
|
|
|
|
if (dynamic_dependencies.length > 0) {
|
|
|
|
|
changes.properties.push(p `${attribute.name}: ${renderer.dirty(dynamic_dependencies)}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
renderer.blocks.push(b `
|
|
|
|
|
const ${get_slot_changes_fn} = #dirty => ${changes};
|
|
|
|
|
const ${get_slot_context_fn} = #ctx => ${get_slot_data(this.node.values, block)};
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
if (spread_dynamic_dependencies.size) {
|
|
|
|
|
get_slot_spread_changes_fn = renderer.component.get_unique_name(
|
|
|
|
|
`get_${sanitize(slot_name)}_slot_spread_changes`
|
|
|
|
|
);
|
|
|
|
|
get_slot_spread_changes_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_spread_changes`);
|
|
|
|
|
renderer.blocks.push(b `
|
|
|
|
|
const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(
|
|
|
|
|
Array.from(spread_dynamic_dependencies)
|
|
|
|
|
)};
|
|
|
|
|
const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(Array.from(spread_dynamic_dependencies))};
|
|
|
|
|
`);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
get_slot_changes_fn = 'null';
|
|
|
|
|
get_slot_context_fn = 'null';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let has_fallback = !!this.fallback;
|
|
|
|
|
if (this.fallback) {
|
|
|
|
|
this.fragment.render(this.fallback, null, x`#nodes` as Identifier);
|
|
|
|
|
this.fragment.render(this.fallback, null, /** @type {import('estree').Identifier} */ (x `#nodes`));
|
|
|
|
|
has_fallback = this.fallback.has_content();
|
|
|
|
|
if (!has_fallback) {
|
|
|
|
|
renderer.remove_block(this.fallback);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const slot = block.get_unique_name(`${sanitize(slot_name)}_slot`);
|
|
|
|
|
const slot_definition = block.get_unique_name(`${sanitize(slot_name)}_slot_template`);
|
|
|
|
|
const slot_or_fallback = has_fallback
|
|
|
|
|
? block.get_unique_name(`${sanitize(slot_name)}_slot_or_fallback`)
|
|
|
|
|
: slot;
|
|
|
|
|
|
|
|
|
|
block.chunks.init.push(b `
|
|
|
|
|
const ${slot_definition} = ${renderer.reference('#slots')}.${slot_name};
|
|
|
|
|
const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference(
|
|
|
|
|
'$$scope'
|
|
|
|
|
)}, ${get_slot_context_fn});
|
|
|
|
|
const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn});
|
|
|
|
|
${has_fallback ? b `const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null}
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
block.chunks.create.push(b `if (${slot_or_fallback}) ${slot_or_fallback}.c();`);
|
|
|
|
|
|
|
|
|
|
if (renderer.options.hydratable) {
|
|
|
|
|
block.chunks.claim.push(b `if (${slot_or_fallback}) ${slot_or_fallback}.l(${parent_nodes});`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
block.chunks.mount.push(b `
|
|
|
|
|
if (${slot_or_fallback}) {
|
|
|
|
|
${slot_or_fallback}.m(${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});
|
|
|
|
|
}
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
block.chunks.intro.push(b `@transition_in(${slot_or_fallback}, #local);`);
|
|
|
|
|
|
|
|
|
|
block.chunks.outro.push(b `@transition_out(${slot_or_fallback}, #local);`);
|
|
|
|
|
|
|
|
|
|
const dynamic_dependencies = Array.from(this.dependencies).filter((name) =>
|
|
|
|
|
this.is_dependency_dynamic(name)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const dynamic_dependencies = Array.from(this.dependencies).filter((name) => this.is_dependency_dynamic(name));
|
|
|
|
|
const fallback_dynamic_dependencies = has_fallback
|
|
|
|
|
? Array.from(this.fallback.dependencies).filter((name) => this.is_dependency_dynamic(name))
|
|
|
|
|
: [];
|
|
|
|
|
|
|
|
|
|
let condition = renderer.dirty(dynamic_dependencies);
|
|
|
|
|
if (block.has_outros) {
|
|
|
|
|
condition = x `!#current || ${condition}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// conditions to treat everything as dirty
|
|
|
|
|
const all_dirty_conditions = [
|
|
|
|
|
get_slot_spread_changes_fn ? x `${get_slot_spread_changes_fn}(#dirty)` : null,
|
|
|
|
|
@ -183,47 +154,36 @@ export default class SlotWrapper extends Wrapper {
|
|
|
|
|
? all_dirty_conditions.reduce((condition1, condition2) => x `${condition1} || ${condition2}`)
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
let slot_update: Node[];
|
|
|
|
|
/** @type {import('estree').Node[]} */
|
|
|
|
|
let slot_update;
|
|
|
|
|
if (all_dirty_condition) {
|
|
|
|
|
const dirty = x`${all_dirty_condition} ? @get_all_dirty_from_scope(${renderer.reference(
|
|
|
|
|
'$$scope'
|
|
|
|
|
)}) : @get_slot_changes(${slot_definition}, ${renderer.reference(
|
|
|
|
|
'$$scope'
|
|
|
|
|
)}, #dirty, ${get_slot_changes_fn})`;
|
|
|
|
|
|
|
|
|
|
const dirty = x `${all_dirty_condition} ? @get_all_dirty_from_scope(${renderer.reference('$$scope')}) : @get_slot_changes(${slot_definition}, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn})`;
|
|
|
|
|
slot_update = b `
|
|
|
|
|
if (${slot}.p && ${condition}) {
|
|
|
|
|
@update_slot_base(${slot}, ${slot_definition}, #ctx, ${renderer.reference(
|
|
|
|
|
'$$scope'
|
|
|
|
|
)}, ${dirty}, ${get_slot_context_fn});
|
|
|
|
|
@update_slot_base(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_context_fn});
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
slot_update = b `
|
|
|
|
|
if (${slot}.p && ${condition}) {
|
|
|
|
|
@update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference(
|
|
|
|
|
'$$scope'
|
|
|
|
|
)}, #dirty, ${get_slot_changes_fn}, ${get_slot_context_fn});
|
|
|
|
|
@update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_context_fn});
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let fallback_condition = renderer.dirty(fallback_dynamic_dependencies);
|
|
|
|
|
let fallback_dirty = x `#dirty`;
|
|
|
|
|
if (block.has_outros) {
|
|
|
|
|
fallback_condition = x `!#current || ${fallback_condition}`;
|
|
|
|
|
fallback_dirty = x `!#current ? ${renderer.get_initial_dirty()} : ${fallback_dirty}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fallback_update =
|
|
|
|
|
has_fallback &&
|
|
|
|
|
const fallback_update = has_fallback &&
|
|
|
|
|
fallback_dynamic_dependencies.length > 0 &&
|
|
|
|
|
b `
|
|
|
|
|
if (${slot_or_fallback} && ${slot_or_fallback}.p && ${fallback_condition}) {
|
|
|
|
|
${slot_or_fallback}.p(#ctx, ${fallback_dirty});
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
if (fallback_update) {
|
|
|
|
|
block.chunks.update.push(b `
|
|
|
|
|
if (${slot}) {
|
|
|
|
|
@ -232,22 +192,30 @@ export default class SlotWrapper extends Wrapper {
|
|
|
|
|
${fallback_update}
|
|
|
|
|
}
|
|
|
|
|
`);
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
block.chunks.update.push(b `
|
|
|
|
|
if (${slot}) {
|
|
|
|
|
${slot_update}
|
|
|
|
|
}
|
|
|
|
|
`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
block.chunks.destroy.push(b `if (${slot_or_fallback}) ${slot_or_fallback}.d(detaching);`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
is_dependency_dynamic(name: string) {
|
|
|
|
|
if (name === '$$scope') return true;
|
|
|
|
|
if (this.node.scope.is_let(name)) return true;
|
|
|
|
|
if (is_reserved_keyword(name)) return true;
|
|
|
|
|
/** @param {string} name */
|
|
|
|
|
is_dependency_dynamic(name) {
|
|
|
|
|
if (name === '$$scope')
|
|
|
|
|
return true;
|
|
|
|
|
if (this.node.scope.is_let(name))
|
|
|
|
|
return true;
|
|
|
|
|
if (is_reserved_keyword(name))
|
|
|
|
|
return true;
|
|
|
|
|
const variable = this.renderer.component.var_lookup.get(name);
|
|
|
|
|
return is_dynamic(variable);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|