|
|
|
@ -2362,7 +2362,6 @@ export const template_visitors = {
|
|
|
|
|
EachBlock(node, context) {
|
|
|
|
|
const each_node_meta = node.metadata;
|
|
|
|
|
const collection = /** @type {Expression} */ (context.visit(node.expression));
|
|
|
|
|
let each_item_is_reactive = true;
|
|
|
|
|
|
|
|
|
|
if (!each_node_meta.is_controlled) {
|
|
|
|
|
context.state.template.push('<!>');
|
|
|
|
@ -2372,36 +2371,29 @@ export const template_visitors = {
|
|
|
|
|
context.state.init.push(b.const(each_node_meta.array_name, b.thunk(collection)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The runtime needs to know what kind of each block this is in order to optimize for the
|
|
|
|
|
// key === item (we avoid extra allocations). In that case, the item doesn't need to be reactive.
|
|
|
|
|
// We can guarantee this by knowing that in order for the item of the each block to change, they
|
|
|
|
|
// would need to mutate the key/item directly in the array. Given that in runes mode we use ===
|
|
|
|
|
// equality, we can apply a fast-path (as long as the index isn't reactive).
|
|
|
|
|
let each_type = 0;
|
|
|
|
|
let flags = 0;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
node.key &&
|
|
|
|
|
(node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index)
|
|
|
|
|
) {
|
|
|
|
|
each_type |= EACH_KEYED;
|
|
|
|
|
// If there's a destructuring, then we likely need the generated $$index
|
|
|
|
|
if (node.index || node.context.type !== 'Identifier') {
|
|
|
|
|
each_type |= EACH_INDEX_REACTIVE;
|
|
|
|
|
flags |= EACH_KEYED;
|
|
|
|
|
|
|
|
|
|
if (node.index) {
|
|
|
|
|
flags |= EACH_INDEX_REACTIVE;
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
context.state.analysis.runes &&
|
|
|
|
|
|
|
|
|
|
// In runes mode, if key === item, we don't need to wrap the item in a source
|
|
|
|
|
const key_is_item =
|
|
|
|
|
node.key.type === 'Identifier' &&
|
|
|
|
|
node.context.type === 'Identifier' &&
|
|
|
|
|
node.context.name === node.key.name &&
|
|
|
|
|
(each_type & EACH_INDEX_REACTIVE) === 0
|
|
|
|
|
) {
|
|
|
|
|
// Fast-path for when the key === item
|
|
|
|
|
each_item_is_reactive = false;
|
|
|
|
|
} else {
|
|
|
|
|
each_type |= EACH_ITEM_REACTIVE;
|
|
|
|
|
node.context.name === node.key.name;
|
|
|
|
|
|
|
|
|
|
if (!context.state.analysis.runes || !key_is_item) {
|
|
|
|
|
flags |= EACH_ITEM_REACTIVE;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
each_type |= EACH_ITEM_REACTIVE;
|
|
|
|
|
flags |= EACH_ITEM_REACTIVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Since `animate:` can only appear on elements that are the sole child of a keyed each block,
|
|
|
|
@ -2414,15 +2406,15 @@ export const template_visitors = {
|
|
|
|
|
return child.attributes.some((attr) => attr.type === 'AnimateDirective');
|
|
|
|
|
})
|
|
|
|
|
) {
|
|
|
|
|
each_type |= EACH_IS_ANIMATED;
|
|
|
|
|
flags |= EACH_IS_ANIMATED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (each_node_meta.is_controlled) {
|
|
|
|
|
each_type |= EACH_IS_CONTROLLED;
|
|
|
|
|
flags |= EACH_IS_CONTROLLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (context.state.analysis.runes) {
|
|
|
|
|
each_type |= EACH_IS_STRICT_EQUALS;
|
|
|
|
|
flags |= EACH_IS_STRICT_EQUALS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the array is a store expression, we need to invalidate it when the array is changed.
|
|
|
|
@ -2447,10 +2439,12 @@ export const template_visitors = {
|
|
|
|
|
);
|
|
|
|
|
return [array, ...transitive_dependencies];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (each_node_meta.array_name) {
|
|
|
|
|
indirect_dependencies.push(b.call(each_node_meta.array_name));
|
|
|
|
|
} else {
|
|
|
|
|
indirect_dependencies.push(collection);
|
|
|
|
|
|
|
|
|
|
const transitive_dependencies = serialize_transitive_dependencies(
|
|
|
|
|
each_node_meta.references,
|
|
|
|
|
context
|
|
|
|
@ -2469,6 +2463,7 @@ export const template_visitors = {
|
|
|
|
|
// into separate expressions, at which point this is called again with an identifier or member expression
|
|
|
|
|
return serialize_set_binding(assignment, context, () => assignment);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const left = object(assignment.left);
|
|
|
|
|
const value = get_assignment_value(assignment, context);
|
|
|
|
|
const invalidate = b.call(
|
|
|
|
@ -2509,10 +2504,12 @@ export const template_visitors = {
|
|
|
|
|
const item_with_loc = with_loc(item, id);
|
|
|
|
|
return b.call('$.unwrap', item_with_loc);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (node.index) {
|
|
|
|
|
const index_binding = /** @type {import('#compiler').Binding} */ (
|
|
|
|
|
context.state.scope.get(node.index)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
index_binding.expression = (id) => {
|
|
|
|
|
const index_with_loc = with_loc(index, id);
|
|
|
|
|
return b.call('$.unwrap', index_with_loc);
|
|
|
|
@ -2575,7 +2572,7 @@ export const template_visitors = {
|
|
|
|
|
declarations.push(b.let(node.index, index));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (context.state.options.dev && (each_type & EACH_KEYED) !== 0) {
|
|
|
|
|
if (context.state.options.dev && (flags & EACH_KEYED) !== 0) {
|
|
|
|
|
context.state.init.push(
|
|
|
|
|
b.stmt(b.call('$.validate_each_keys', b.thunk(collection), key_function))
|
|
|
|
|
);
|
|
|
|
@ -2584,7 +2581,7 @@ export const template_visitors = {
|
|
|
|
|
/** @type {Expression[]} */
|
|
|
|
|
const args = [
|
|
|
|
|
context.state.node,
|
|
|
|
|
b.literal(each_type),
|
|
|
|
|
b.literal(flags),
|
|
|
|
|
each_node_meta.array_name ? each_node_meta.array_name : b.thunk(collection),
|
|
|
|
|
key_function,
|
|
|
|
|
b.arrow([b.id('$$anchor'), item, index], b.block(declarations.concat(block.body)))
|
|
|
|
|