use vize_carton::{Box, Bump, CompactString, String};
use vize_croquis::reactivity::ReactiveKind;
use vize_croquis::{BindingType, Croquis, ScopeBinding, ScopeKind, VForScopeData, VSlotScopeData};
use crate::errors::{CompilerError, ErrorCode};
use crate::options::TransformOptions;
use crate::*;
use super::TransformContext;
impl<'a> TransformContext<'a> {
pub fn new(allocator: &'a Bump, source: String, options: TransformOptions) -> Self {
Self::new_with_template_syntax_quirks(allocator, source, options, false)
}
pub fn new_with_template_syntax_quirks(
allocator: &'a Bump,
source: String,
options: TransformOptions,
template_syntax_quirks: bool,
) -> Self {
let ssr = options.ssr;
Self {
allocator,
source,
options,
root: None,
parent: None,
grandparent: None,
current_node: None,
child_index: 0,
helpers: crate::runtime_helpers::RuntimeHelpers::default(),
components: std::vec::Vec::new(),
directives: std::vec::Vec::new(),
#[cfg(feature = "legacy")]
filters: std::vec::Vec::new(),
hoists: vize_carton::Vec::new_in(allocator),
cached: vize_carton::Vec::new_in(allocator),
temps: 0,
scope_chain: vize_croquis::ScopeChain::new(),
scoped_slots: 0,
in_v_once: false,
in_ssr: ssr,
errors: std::vec::Vec::new(),
template_syntax_quirks,
node_removed: false,
analysis: None,
hoisted_scope_id: None,
}
}
#[deprecated(note = "use new_with_template_syntax_quirks instead")]
pub fn new_with_vue_parser_quirks(
allocator: &'a Bump,
source: String,
options: TransformOptions,
vue_parser_quirks: bool,
) -> Self {
Self::new_with_template_syntax_quirks(allocator, source, options, vue_parser_quirks)
}
pub fn with_analysis(
allocator: &'a Bump,
source: String,
options: TransformOptions,
analysis: &'a Croquis,
) -> Self {
Self::with_analysis_and_template_syntax_quirks(allocator, source, options, analysis, false)
}
pub fn with_analysis_and_template_syntax_quirks(
allocator: &'a Bump,
source: String,
options: TransformOptions,
analysis: &'a Croquis,
template_syntax_quirks: bool,
) -> Self {
let mut ctx = Self::new_with_template_syntax_quirks(
allocator,
source,
options,
template_syntax_quirks,
);
ctx.analysis = Some(analysis);
ctx
}
#[deprecated(note = "use with_analysis_and_template_syntax_quirks instead")]
pub fn with_analysis_and_vue_parser_quirks(
allocator: &'a Bump,
source: String,
options: TransformOptions,
analysis: &'a Croquis,
vue_parser_quirks: bool,
) -> Self {
Self::with_analysis_and_template_syntax_quirks(
allocator,
source,
options,
analysis,
vue_parser_quirks,
)
}
pub fn set_analysis(&mut self, analysis: &'a Croquis) {
self.analysis = Some(analysis);
}
#[inline]
pub fn analysis(&self) -> Option<&Croquis> {
self.analysis
}
#[inline]
pub fn has_analysis(&self) -> bool {
self.analysis.is_some()
}
#[inline]
pub fn template_syntax_quirks(&self) -> bool {
self.template_syntax_quirks
}
#[inline]
#[deprecated(note = "use template_syntax_quirks instead")]
pub fn vue_parser_quirks(&self) -> bool {
self.template_syntax_quirks()
}
pub fn is_variable_defined(&self, name: &str) -> bool {
if self.scope_chain.is_defined(name) {
return true;
}
if let Some(analysis) = &self.analysis {
return analysis.is_defined(name);
}
if let Some(metadata) = &self.options.binding_metadata {
return metadata.bindings.contains_key(name);
}
false
}
pub fn get_binding_type(&self, name: &str) -> Option<BindingType> {
if let Some((_, binding)) = self.scope_chain.lookup(name) {
return Some(binding.binding_type);
}
if let Some(analysis) = &self.analysis {
return analysis.get_binding_type(name);
}
if let Some(metadata) = &self.options.binding_metadata {
return metadata.bindings.get(name).copied();
}
None
}
pub fn needs_setup_prefix(&self, name: &str) -> bool {
if self.scope_chain.is_defined(name) {
return false;
}
if let Some(binding_type) = self.get_binding_type(name) {
matches!(
binding_type,
BindingType::SetupConst
| BindingType::SetupLet
| BindingType::SetupRef
| BindingType::SetupMaybeRef
| BindingType::SetupReactiveConst
)
} else {
false
}
}
pub fn is_ref(&self, name: &str) -> bool {
if let Some(analysis) = &self.analysis {
analysis.bindings.is_ref(name)
} else if let Some(binding_type) = self.get_binding_type(name) {
matches!(
binding_type,
BindingType::SetupRef | BindingType::SetupMaybeRef
)
} else {
false
}
}
pub fn is_prop(&self, name: &str) -> bool {
if let Some(analysis) = &self.analysis {
analysis.bindings.is_prop(name)
} else {
matches!(
self.get_binding_type(name),
Some(BindingType::Props | BindingType::PropsAliased)
)
}
}
pub fn get_reactive_kind(&self, name: &str) -> Option<ReactiveKind> {
self.analysis?.reactivity.lookup(name).map(|s| s.kind)
}
pub fn is_readonly_binding(&self, name: &str) -> bool {
self.get_reactive_kind(name).is_some_and(|k| {
matches!(
k,
ReactiveKind::Computed | ReactiveKind::Readonly | ReactiveKind::ShallowReadonly
)
})
}
pub fn is_component_registered(&self, name: &str) -> bool {
if let Some(analysis) = &self.analysis
&& analysis.is_component_registered(name)
{
return true;
}
if let Some(metadata) = &self.options.binding_metadata
&& metadata.bindings.contains_key(name)
{
return true;
}
false
}
pub fn helper(&mut self, helper: RuntimeHelper) {
self.helpers.add(helper);
}
pub fn remove_helper(&mut self, helper: RuntimeHelper) {
self.helpers.remove(helper);
}
pub fn has_helper(&self, helper: RuntimeHelper) -> bool {
self.helpers.contains(helper)
}
pub fn add_component(&mut self, component: impl Into<String>) {
let component = component.into();
if !self.components.contains(&component) {
self.components.push(component);
}
}
pub fn add_directive(&mut self, directive: impl Into<String>) {
let directive = directive.into();
if !self.directives.contains(&directive) {
self.directives.push(directive);
}
}
#[cfg(feature = "legacy")]
pub(crate) fn add_filter(&mut self, filter: impl Into<String>) {
let filter = filter.into();
if !self.filters.contains(&filter) {
self.filters.push(filter);
}
}
#[cfg(feature = "legacy")]
#[inline]
pub(crate) fn supports_filters(&self) -> bool {
vize_armature::legacy::LegacyDialectCapabilities::for_dialect(self.options.dialect)
.supports_filters
}
#[cfg(feature = "legacy")]
#[inline]
pub(crate) fn supports_v2_event_sugar(&self) -> bool {
matches!(
vize_armature::legacy::LegacyVueVersion::from_dialect(self.options.dialect),
Some(vize_armature::legacy::LegacyVueVersion::V2)
)
}
pub fn add_identifier(&mut self, id: impl Into<CompactString>) {
self.scope_chain
.add_binding(id.into(), ScopeBinding::new(BindingType::SetupConst, 0));
}
pub fn enter_scope(&mut self, kind: ScopeKind) {
self.scope_chain.enter_scope(kind);
}
pub fn exit_scope(&mut self) {
self.scope_chain.exit_scope();
}
pub fn enter_v_for_scope(
&mut self,
value_alias: Option<&str>,
key_alias: Option<&str>,
index_alias: Option<&str>,
source: &str,
) {
self.scope_chain.enter_v_for_scope(
VForScopeData {
value_alias: CompactString::new(value_alias.unwrap_or("")),
value_bindings: value_alias
.map(|alias| vize_carton::smallvec![CompactString::new(alias)])
.unwrap_or_default(),
key_alias: key_alias.map(CompactString::new),
index_alias: index_alias.map(CompactString::new),
source: CompactString::new(source),
key_expression: None,
},
0,
0,
);
}
pub fn enter_v_slot_scope(
&mut self,
name: &str,
props_pattern: Option<&str>,
prop_names: &[String],
start: u32,
end: u32,
) {
self.scope_chain.enter_v_slot_scope(
VSlotScopeData {
name: CompactString::new(name),
props_pattern: props_pattern.map(CompactString::new),
prop_names: prop_names
.iter()
.map(|name| CompactString::new(name.as_str()))
.collect(),
component: None,
},
start,
end,
);
}
pub fn is_in_scope(&self, id: &str) -> bool {
self.scope_chain.is_defined(id)
}
pub fn hoist(&mut self, node: JsChildNode<'a>) -> usize {
let index = self.hoists.len();
self.hoists.push(Some(node));
index
}
pub fn cache(&mut self, exp: CacheExpression<'a>) -> usize {
let index = self.cached.len();
let boxed = Box::new_in(exp, self.allocator);
self.cached.push(Some(boxed));
index
}
pub fn on_error(&mut self, code: ErrorCode, loc: Option<SourceLocation>) {
self.errors.push(CompilerError::new(code, loc));
}
pub fn on_error_with_message(
&mut self,
code: ErrorCode,
message: impl Into<CompactString>,
loc: Option<SourceLocation>,
) {
self.errors
.push(CompilerError::with_message(code, message, loc));
}
pub fn replace_node(&mut self, new_node: TemplateChildNode<'a>) {
if let Some(parent) = &self.parent {
let children = parent.children_mut();
if self.child_index < children.len() {
children[self.child_index] = new_node;
self.current_node = Some(&mut children[self.child_index] as *mut _);
}
}
}
pub fn take_current_node(&mut self) -> Option<TemplateChildNode<'a>> {
if let Some(parent) = &self.parent {
let children = parent.children_mut();
if self.child_index < children.len() {
let placeholder = TemplateChildNode::Comment(Box::new_in(
CommentNode::new("", SourceLocation::STUB),
self.allocator,
));
let taken = std::mem::replace(&mut children[self.child_index], placeholder);
return Some(taken);
}
}
None
}
pub fn remove_node(&mut self) {
if let Some(parent) = &self.parent {
let children = parent.children_mut();
if self.child_index < children.len() {
children.remove(self.child_index);
self.current_node = None;
self.node_removed = true;
}
}
}
pub fn remove_node_at(&mut self, index: usize) {
if let Some(parent) = &self.parent {
let children = parent.children_mut();
if index < children.len() {
children.remove(index);
if index < self.child_index {
self.child_index -= 1;
}
self.node_removed = true;
}
}
}
pub fn was_node_removed(&self) -> bool {
self.node_removed
}
pub fn reset_node_removed(&mut self) {
self.node_removed = false;
}
}
pub(super) fn clone_expression<'a>(
allocator: &'a Bump,
exp: &ExpressionNode<'a>,
) -> ExpressionNode<'a> {
match exp {
ExpressionNode::Simple(s) => ExpressionNode::Simple(Box::new_in(
SimpleExpressionNode {
content: s.content.clone(),
is_static: s.is_static,
const_type: s.const_type,
loc: s.loc.clone(),
js_ast: None,
hoisted: None,
identifiers: None,
is_handler_key: s.is_handler_key,
is_ref_transformed: s.is_ref_transformed,
},
allocator,
)),
ExpressionNode::Compound(c) => {
ExpressionNode::Simple(Box::new_in(
SimpleExpressionNode {
content: c.loc.source.clone(),
is_static: false,
const_type: ConstantType::NotConstant,
loc: c.loc.clone(),
js_ast: None,
hoisted: None,
identifiers: None,
is_handler_key: c.is_handler_key,
is_ref_transformed: false,
},
allocator,
))
}
}
}