use crate::{RootNode, RuntimeHelper, TemplateChildNode, options::CodegenOptions};
use vize_carton::profile;
use super::children::is_directive_comment;
use super::context::{CodegenContext, CodegenResult, CodegenResultWithSections, CodegenSections};
use super::element::generate_root_node;
use super::generate::{collect_hoist_helpers, generate_hoists};
use super::node::generate_node;
use super::root::{
generate_assets, generate_function_signature, generate_preamble_from_helpers,
is_ignorable_root_text,
};
pub fn generate(root: &RootNode<'_>, options: CodegenOptions) -> CodegenResult {
generate_with_sections(root, options).into_result()
}
pub fn generate_with_sections(
root: &RootNode<'_>,
options: CodegenOptions,
) -> CodegenResultWithSections {
let mut ctx = CodegenContext::new(options);
ctx.static_cache = ctx.options.inline || !root.hoists.is_empty();
let root_children: std::vec::Vec<&TemplateChildNode<'_>> = root
.children
.iter()
.filter(|child| !is_ignorable_root_text(child) && !is_directive_comment(child))
.collect();
profile!(
"atelier.codegen.function_signature",
generate_function_signature(&mut ctx)
);
ctx.indent();
ctx.newline();
let assets_start = ctx.code.len();
profile!("atelier.codegen.assets", generate_assets(&mut ctx, root));
let assets_end = ctx.code.len();
ctx.push("return ");
let return_expr_start = ctx.code.len();
if root_children.is_empty() {
ctx.push("null");
} else if root_children.len() == 1 {
profile!(
"atelier.codegen.root_node",
generate_root_node(&mut ctx, root_children[0])
);
} else {
ctx.use_helper(RuntimeHelper::OpenBlock);
ctx.use_helper(RuntimeHelper::CreateElementBlock);
ctx.use_helper(RuntimeHelper::Fragment);
ctx.push("(");
ctx.push(ctx.helper(RuntimeHelper::OpenBlock));
ctx.push("(), ");
ctx.push(ctx.helper(RuntimeHelper::CreateElementBlock));
ctx.push("(");
ctx.push(ctx.helper(RuntimeHelper::Fragment));
ctx.push(", null, [");
ctx.indent();
for (i, child) in root_children.iter().enumerate() {
if i > 0 {
ctx.push(",");
}
ctx.newline();
profile!(
"atelier.codegen.fragment_child",
generate_node(&mut ctx, child)
);
}
ctx.deindent();
ctx.newline();
let non_comment_children = root_children
.iter()
.filter(|child| !matches!(child, TemplateChildNode::Comment(_)))
.count();
if non_comment_children == 1 {
ctx.push("], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))");
} else {
ctx.push("], 64 /* STABLE_FRAGMENT */))");
}
}
let return_expr_end = ctx.code.len();
ctx.deindent();
ctx.newline();
ctx.push("}");
let mut all_helpers: Vec<RuntimeHelper> = ctx.used_helpers.iter().collect();
let mut all_helper_bits = retain_unique_helpers(&mut all_helpers);
if root.helpers.contains(&RuntimeHelper::Unref) {
push_unique_helper(RuntimeHelper::Unref, &mut all_helpers, &mut all_helper_bits);
}
profile!(
"atelier.codegen.collect_hoist_helpers",
collect_hoist_helpers(root, &mut all_helpers)
);
all_helper_bits = retain_unique_helpers(&mut all_helpers);
let mut ordered_helpers = Vec::with_capacity(all_helpers.len());
let mut ordered_helper_bits = 0;
for helper in root.helpers.iter().copied() {
if has_helper(all_helper_bits, helper) {
push_unique_helper(helper, &mut ordered_helpers, &mut ordered_helper_bits);
}
}
for helper in all_helpers {
push_unique_helper(helper, &mut ordered_helpers, &mut ordered_helper_bits);
}
ordered_helpers.sort_by_key(|helper| vue_helper_import_rank(*helper));
let mut preamble = profile!(
"atelier.codegen.preamble",
generate_preamble_from_helpers(&ctx, &ordered_helpers)
);
let imports_len = preamble.len();
let hoists_code = profile!("atelier.codegen.hoists", generate_hoists(&ctx, root));
if !hoists_code.is_empty() {
preamble.push('\n');
preamble.push_str(&hoists_code);
}
let map = ctx.take_map_builder().map(|builder| {
let filename = ctx.options.filename.as_str();
builder.finish(ctx.code_as_str(), filename, root.source.as_str())
});
CodegenResultWithSections {
result: CodegenResult {
code: ctx.into_code(),
preamble,
map,
},
sections: Some(CodegenSections {
imports_len,
assets_start,
assets_end,
return_expr_start,
return_expr_end,
}),
}
}
fn retain_unique_helpers(helpers: &mut Vec<RuntimeHelper>) -> u128 {
let mut helper_bits = 0;
helpers.retain(|helper| push_helper_bit(*helper, &mut helper_bits));
helper_bits
}
fn push_unique_helper(
helper: RuntimeHelper,
helpers: &mut Vec<RuntimeHelper>,
helper_bits: &mut u128,
) {
if push_helper_bit(helper, helper_bits) {
helpers.push(helper);
}
}
fn push_helper_bit(helper: RuntimeHelper, helper_bits: &mut u128) -> bool {
let bit = helper_bit(helper);
if *helper_bits & bit != 0 {
return false;
}
*helper_bits |= bit;
true
}
fn has_helper(helper_bits: u128, helper: RuntimeHelper) -> bool {
helper_bits & helper_bit(helper) != 0
}
fn helper_bit(helper: RuntimeHelper) -> u128 {
let index = helper as u8;
debug_assert!(index < 128);
1u128 << index
}
fn vue_helper_import_rank(helper: RuntimeHelper) -> u8 {
match helper {
RuntimeHelper::ResolveComponent
| RuntimeHelper::ResolveDynamicComponent
| RuntimeHelper::ResolveDirective
| RuntimeHelper::ResolveFilter => 0,
RuntimeHelper::VModelRadio
| RuntimeHelper::VModelCheckbox
| RuntimeHelper::VModelText
| RuntimeHelper::VModelSelect
| RuntimeHelper::VModelDynamic => 1,
RuntimeHelper::WithDirectives | RuntimeHelper::WithModifiers | RuntimeHelper::WithKeys => 2,
RuntimeHelper::ToDisplayString => 3,
RuntimeHelper::CreateElementVNode
| RuntimeHelper::CreateVNode
| RuntimeHelper::RenderSlot => 4,
RuntimeHelper::NormalizeClass
| RuntimeHelper::NormalizeStyle
| RuntimeHelper::NormalizeProps
| RuntimeHelper::GuardReactiveProps
| RuntimeHelper::MergeProps
| RuntimeHelper::ToHandlers
| RuntimeHelper::Camelize
| RuntimeHelper::Capitalize
| RuntimeHelper::ToHandlerKey => 5,
RuntimeHelper::OpenBlock => 6,
RuntimeHelper::CreateElementBlock | RuntimeHelper::CreateBlock => 7,
RuntimeHelper::Fragment => 8,
RuntimeHelper::CreateComment | RuntimeHelper::CreateText | RuntimeHelper::CreateStatic => 9,
RuntimeHelper::RenderList
| RuntimeHelper::CreateSlots
| RuntimeHelper::SetBlockTracking
| RuntimeHelper::PushScopeId
| RuntimeHelper::PopScopeId
| RuntimeHelper::WithCtx
| RuntimeHelper::Unref
| RuntimeHelper::IsRef
| RuntimeHelper::WithMemo
| RuntimeHelper::IsMemoSame
| RuntimeHelper::VShow
| RuntimeHelper::Teleport
| RuntimeHelper::Suspense
| RuntimeHelper::KeepAlive
| RuntimeHelper::BaseTransition
| RuntimeHelper::Transition
| RuntimeHelper::TransitionGroup => 10,
RuntimeHelper::SsrInterpolate
| RuntimeHelper::SsrRenderVNode
| RuntimeHelper::SsrRenderComponent
| RuntimeHelper::SsrRenderSlot
| RuntimeHelper::SsrRenderSlotInner
| RuntimeHelper::SsrRenderAttrs
| RuntimeHelper::SsrRenderAttr
| RuntimeHelper::SsrRenderDynamicAttr
| RuntimeHelper::SsrIncludeBooleanAttr
| RuntimeHelper::SsrRenderClass
| RuntimeHelper::SsrRenderStyle
| RuntimeHelper::SsrRenderDynamicModel
| RuntimeHelper::SsrGetDynamicModelProps
| RuntimeHelper::SsrRenderList
| RuntimeHelper::SsrLooseEqual
| RuntimeHelper::SsrLooseContain
| RuntimeHelper::SsrGetDirectiveProps
| RuntimeHelper::SsrRenderTeleport
| RuntimeHelper::SsrRenderSuspense => 11,
}
}