use crate::ast::RuntimeHelper;
use crate::options::CodegenOptions;
use super::helpers::default_helper_alias;
use vize_carton::camelize;
use vize_carton::capitalize;
use vize_carton::FxHashSet;
use vize_carton::String;
use vize_carton::ToCompactString;
pub struct CodegenContext {
pub(super) code: Vec<u8>,
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: FxHashSet<RuntimeHelper>,
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 struct CodegenResult {
pub code: String,
pub preamble: String,
pub map: Option<String>,
}
impl CodegenContext {
pub fn new(options: CodegenOptions) -> Self {
Self {
code: Vec::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: FxHashSet::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,
}
}
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_bytes(&mut self, bytes: &[u8]) {
self.code.extend_from_slice(bytes);
}
#[inline]
pub fn push(&mut self, code: &str) {
self.code.extend_from_slice(code.as_bytes());
}
#[inline]
pub fn push_line(&mut self, code: &str) {
self.push(code);
self.newline();
}
#[inline]
pub fn newline(&mut self) {
self.code.push(b'\n');
for _ in 0..self.indent_level {
self.code.extend_from_slice(b" ");
}
}
#[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.extend_from_slice(b"/*#__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.insert(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.extend_from_slice(code.as_bytes());
}
#[inline]
#[allow(dead_code)]
pub fn push_line_fmt(&mut self, args: std::fmt::Arguments<'_>) {
use std::fmt::Write as _;
self.write_fmt(args).unwrap();
self.newline();
}
pub fn into_code(self) -> String {
unsafe { String::from_utf8_unchecked(self.code) }
}
pub fn code_as_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(&self.code) }
}
}
impl std::fmt::Write for CodegenContext {
#[inline]
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.code.extend_from_slice(s.as_bytes());
Ok(())
}
}