mod generate;
pub(crate) mod helpers;
use crate::transforms::v_memo::{get_memo_exp, has_v_memo};
use crate::{ForNode, RuntimeHelper, TemplateChildNode};
use super::{
children::generate_children, context::CodegenContext, expression::generate_expression,
};
use generate::generate_for_item;
use helpers::extract_for_params;
use vize_carton::String;
use vize_carton::ToCompactString;
#[allow(unused_imports)]
pub(crate) use helpers::{
extract_destructure_params, get_element_key, is_numeric_source, is_valid_ident, split_top_level,
};
pub fn generate_for(ctx: &mut CodegenContext, for_node: &ForNode<'_>) {
generate_for_inner(ctx, for_node, None)
}
pub(crate) fn generate_for_with_fragment_key(
ctx: &mut CodegenContext,
for_node: &ForNode<'_>,
generate_key: &dyn Fn(&mut CodegenContext),
) {
generate_for_inner(ctx, for_node, Some(generate_key))
}
fn generate_for_inner(
ctx: &mut CodegenContext,
for_node: &ForNode<'_>,
generate_fragment_key: Option<&dyn Fn(&mut CodegenContext)>,
) {
ctx.use_helper(RuntimeHelper::OpenBlock);
ctx.use_helper(RuntimeHelper::CreateElementBlock);
ctx.use_helper(RuntimeHelper::Fragment);
ctx.use_helper(RuntimeHelper::RenderList);
let is_stable = is_numeric_source(&for_node.source);
let has_key = for_node.children.iter().any(|child| {
if let TemplateChildNode::Element(el) = child {
get_element_key(el).is_some()
} else {
false
}
});
let fragment_flag = if is_stable {
64 } else if has_key {
128 } else {
256 };
ctx.push("(");
ctx.push(ctx.helper(RuntimeHelper::OpenBlock));
if is_stable {
ctx.push("(), ");
} else {
ctx.push("(true), ");
}
ctx.push(ctx.helper(RuntimeHelper::CreateElementBlock));
ctx.push("(");
ctx.push(ctx.helper(RuntimeHelper::Fragment));
if let Some(generate_key) = generate_fragment_key {
ctx.push(", { key: ");
generate_key(ctx);
ctx.push(" }, ");
} else {
ctx.push(", null, ");
}
ctx.push(ctx.helper(RuntimeHelper::RenderList));
ctx.push("(");
generate_expression(ctx, &for_node.source);
ctx.push(", (");
let mut callback_params: Vec<String> = Vec::new();
if let Some(value) = &for_node.value_alias {
generate_expression(ctx, value);
extract_for_params(value, &mut callback_params);
} else {
ctx.push("_item");
}
if let Some(key) = &for_node.key_alias {
ctx.push(", ");
generate_expression(ctx, key);
extract_for_params(key, &mut callback_params);
}
if let Some(index) = &for_node.object_index_alias {
ctx.push(", ");
generate_expression(ctx, index);
extract_for_params(index, &mut callback_params);
}
ctx.add_slot_params(&callback_params);
let child_memo_exp = if for_node.children.len() == 1 {
if let TemplateChildNode::Element(el) = &for_node.children[0] {
if has_v_memo(el) {
get_memo_exp(el)
} else {
None
}
} else {
None
}
} else {
None
};
if let Some(memo_exp) = child_memo_exp {
ctx.use_helper(RuntimeHelper::WithMemo);
if for_node.key_alias.is_none() {
ctx.push(", __");
}
if for_node.object_index_alias.is_none() {
ctx.push(", ___");
}
ctx.push(", _cached) => {");
ctx.indent();
ctx.newline();
ctx.push("const _memo = (");
generate_expression(ctx, memo_exp);
ctx.push(")");
ctx.newline();
ctx.use_helper(RuntimeHelper::IsMemoSame);
let key_exp = if let TemplateChildNode::Element(el) = &for_node.children[0] {
get_element_key(el)
} else {
None
};
ctx.push("if (_cached && _cached.el && ");
if let Some(key) = key_exp {
ctx.push("_cached.key === ");
generate_expression(ctx, key);
ctx.push(" && ");
}
ctx.push(ctx.helper(RuntimeHelper::IsMemoSame));
ctx.push("(_cached, _memo)) return _cached");
ctx.newline();
ctx.push("const _item = ");
let prev_in_v_for = ctx.in_v_for;
ctx.in_v_for = true;
ctx.skip_v_memo = true;
generate_for_item(ctx, &for_node.children[0], is_stable);
ctx.skip_v_memo = false;
ctx.in_v_for = prev_in_v_for;
ctx.newline();
ctx.push("_item.memo = _memo");
ctx.newline();
ctx.push("return _item");
ctx.remove_slot_params(&callback_params);
ctx.deindent();
ctx.newline();
let cache_index = ctx.next_cache_index();
let flag_name = match fragment_flag {
64 => "STABLE_FRAGMENT",
128 => "KEYED_FRAGMENT",
256 => "UNKEYED_FRAGMENT",
_ => "FRAGMENT",
};
ctx.push("}, _cache, ");
ctx.push(&cache_index.to_compact_string());
ctx.push("), ");
ctx.push(&fragment_flag.to_compact_string());
ctx.push(" /* ");
ctx.push(flag_name);
ctx.push(" */))");
} else {
ctx.push(") => {");
ctx.indent();
ctx.newline();
ctx.push("return ");
let prev_in_v_for = ctx.in_v_for;
ctx.in_v_for = true;
if for_node.children.len() == 1 {
generate_for_item(ctx, &for_node.children[0], is_stable);
} else {
generate_children(ctx, &for_node.children);
}
ctx.in_v_for = prev_in_v_for;
ctx.remove_slot_params(&callback_params);
ctx.deindent();
ctx.newline();
let flag_name = match fragment_flag {
64 => "STABLE_FRAGMENT",
128 => "KEYED_FRAGMENT",
256 => "UNKEYED_FRAGMENT",
_ => "FRAGMENT",
};
ctx.push("}), ");
ctx.push(&fragment_flag.to_compact_string());
ctx.push(" /* ");
ctx.push(flag_name);
ctx.push(" */))");
}
}