use crate::ast::{Namespace, RuntimeHelper};
use crate::options::CodegenOptions;
use crate::runtime_helpers::RuntimeHelpers;
use super::helpers::default_helper_alias;
use vize_carton::FxHashSet;
use vize_carton::String;
use vize_carton::ToCompactString;
use vize_carton::camelize;
use vize_carton::capitalize;
pub struct CodegenContext {
pub(super) code: String,
pub(super) indent_level: u32,
#[allow(dead_code)]
pub(super) ssr: bool,
pub(super) helper_alias: fn(RuntimeHelper) -> &'static str,
pub(super) runtime_global_name: String,
pub(super) runtime_module_name: String,
pub(super) options: CodegenOptions,
pub(super) pure: bool,
pub(super) used_helpers: RuntimeHelpers,
pub(super) cache_index: usize,
pub(super) slot_params: FxHashSet<String>,
pub(super) skip_is_prop: bool,
pub(super) skip_scope_id: bool,
pub(super) skip_normalize: bool,
pub(super) in_v_for: bool,
pub(super) skip_v_memo: bool,
pub(super) props_is_plain_element: bool,
pub(super) parent_ns: Namespace,
pub(super) static_cache: bool,
pub(super) v_if_branch_counter: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct CodegenSections {
pub imports_len: usize,
pub assets_start: usize,
pub assets_end: usize,
pub return_expr_start: usize,
pub return_expr_end: usize,
}
pub struct CodegenResult {
pub code: String,
pub preamble: String,
pub map: Option<String>,
}
pub struct CodegenResultWithSections {
pub result: CodegenResult,
pub sections: Option<CodegenSections>,
}
impl CodegenResultWithSections {
pub fn into_result(self) -> CodegenResult {
self.result
}
}
impl CodegenContext {
pub fn new(options: CodegenOptions) -> Self {
Self {
code: String::with_capacity(4096),
indent_level: 0,
ssr: options.ssr,
helper_alias: default_helper_alias,
runtime_global_name: options.runtime_global_name.to_compact_string(),
runtime_module_name: options.runtime_module_name.to_compact_string(),
options,
pure: false,
used_helpers: RuntimeHelpers::default(),
cache_index: 0,
slot_params: FxHashSet::default(),
skip_is_prop: false,
skip_scope_id: false,
skip_normalize: false,
in_v_for: false,
skip_v_memo: false,
props_is_plain_element: false,
parent_ns: Namespace::Html,
static_cache: false,
v_if_branch_counter: 0,
}
}
pub(super) fn with_parent_namespace<T>(
&mut self,
ns: Namespace,
f: impl FnOnce(&mut Self) -> T,
) -> T {
let prev = self.parent_ns;
self.parent_ns = ns;
let result = f(self);
self.parent_ns = prev;
result
}
pub(super) fn next_v_if_branch_key(&mut self) -> usize {
let key = self.v_if_branch_counter;
self.v_if_branch_counter = self.v_if_branch_counter.saturating_add(1);
key
}
pub fn add_slot_params(&mut self, params: &[String]) {
for param in params {
self.slot_params.insert(param.clone());
}
}
pub fn remove_slot_params(&mut self, params: &[String]) {
for param in params {
self.slot_params.remove(param);
}
}
pub fn is_slot_param(&self, name: &str) -> bool {
self.slot_params.contains(name)
}
#[inline]
pub fn has_slot_params(&self) -> bool {
!self.slot_params.is_empty()
}
#[inline]
pub fn cache_handlers_in_current_scope(&self) -> bool {
self.options.cache_handlers && !self.has_slot_params()
}
pub fn next_cache_index(&mut self) -> usize {
let index = self.cache_index;
self.cache_index += 1;
index
}
#[inline]
pub fn push(&mut self, code: &str) {
self.code.push_str(code);
}
#[inline]
pub fn push_line(&mut self, code: &str) {
self.push(code);
self.newline();
}
#[inline]
pub fn newline(&mut self) {
self.code.push('\n');
for _ in 0..self.indent_level {
self.code.push_str(" ");
}
}
#[inline]
pub fn indent(&mut self) {
self.indent_level += 1;
}
#[inline]
pub fn deindent(&mut self) {
if self.indent_level > 0 {
self.indent_level -= 1;
}
}
#[inline]
pub fn push_pure(&mut self) {
if self.pure {
self.code.push_str("/*#__PURE__*/ ");
}
}
#[inline]
pub fn helper(&self, helper: RuntimeHelper) -> &'static str {
(self.helper_alias)(helper)
}
#[inline]
pub fn use_helper(&mut self, helper: RuntimeHelper) {
self.used_helpers.add(helper);
}
pub fn is_component_in_bindings(&self, component: &str) -> bool {
self.resolve_component_binding_name(component).is_some()
}
pub fn resolve_component_binding_name(&self, component: &str) -> Option<String> {
let metadata = self.options.binding_metadata.as_ref()?;
let resolve_base = |name: &str| {
if metadata.bindings.contains_key(name) {
return Some(name.to_compact_string());
}
let camel = camelize(name);
if metadata.bindings.contains_key(camel.as_str()) {
return Some(camel);
}
let pascal = capitalize(&camel);
if metadata.bindings.contains_key(pascal.as_str()) {
return Some(pascal);
}
None
};
if let Some((base, suffix)) = component.split_once('.') {
let resolved_base = resolve_base(base)?;
let mut resolved = String::with_capacity(resolved_base.len() + suffix.len() + 1);
resolved.push_str(resolved_base.as_str());
resolved.push('.');
resolved.push_str(suffix);
return Some(resolved);
}
resolve_base(component)
}
#[inline]
#[allow(dead_code)]
pub fn push_str(&mut self, code: &str) {
self.code.push_str(code);
}
#[inline]
#[allow(dead_code)]
pub fn push_line_fmt(&mut self, args: std::fmt::Arguments<'_>) {
use std::fmt::Write as _;
let _ = self.write_fmt(args);
self.newline();
}
pub fn into_code(self) -> String {
self.code
}
pub fn code_as_str(&self) -> &str {
&self.code
}
}
impl std::fmt::Write for CodegenContext {
#[inline]
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.code.push_str(s);
Ok(())
}
}