#[path = "../transform/context.rs"]
mod context;
#[path = "../transform/element.rs"]
pub mod element;
#[path = "../transform/structural.rs"]
pub mod structural;
#[path = "../transform/traverse.rs"]
pub mod traverse;
use vize_carton::{Box, Bump, SmallVec, String, Vec, profile};
use vize_croquis::{Croquis, ScopeChain};
use crate::errors::CompilerError;
use crate::options::TransformOptions;
use crate::runtime_helpers::RuntimeHelpers;
use crate::{
CacheExpression, DirectiveNode, ElementNode, ForNode, IfBranchNode, IfNode, JsChildNode,
PropNode, RootNode, RuntimeHelper, TemplateChildNode,
};
use traverse::traverse_children;
pub type NodeTransform<'a> =
fn(&mut TransformContext<'a>, &mut TemplateChildNode<'a>) -> Option<ExitFns<'a>>;
pub type ExitFn<'a> = std::boxed::Box<dyn FnOnce(&mut TransformContext<'a>) + 'a>;
pub type ExitFns<'a> = SmallVec<[ExitFn<'a>; 2]>;
pub type DirectiveTransform<'a> = fn(
&mut TransformContext<'a>,
&mut ElementNode<'a>,
&DirectiveNode<'a>,
) -> Option<DirectiveTransformResult<'a>>;
pub struct DirectiveTransformResult<'a> {
pub props: Vec<'a, PropNode<'a>>,
pub remove_directive: bool,
pub ssr_tag_type: Option<u8>,
}
pub type StructuralDirectiveTransform<'a> =
fn(&mut TransformContext<'a>, &mut ElementNode<'a>, &DirectiveNode<'a>) -> Option<ExitFn<'a>>;
pub struct TransformContext<'a> {
pub allocator: &'a Bump,
pub options: TransformOptions,
pub source: String,
pub root: Option<*mut RootNode<'a>>,
pub parent: Option<ParentNode<'a>>,
pub grandparent: Option<ParentNode<'a>>,
pub current_node: Option<*mut TemplateChildNode<'a>>,
pub child_index: usize,
pub helpers: RuntimeHelpers,
pub components: std::vec::Vec<String>,
pub directives: std::vec::Vec<String>,
#[cfg(feature = "legacy")]
pub(crate) filters: std::vec::Vec<String>,
pub hoists: Vec<'a, Option<JsChildNode<'a>>>,
pub cached: Vec<'a, Option<Box<'a, CacheExpression<'a>>>>,
pub temps: u32,
pub scope_chain: ScopeChain,
pub scoped_slots: u32,
pub in_v_once: bool,
pub in_ssr: bool,
pub errors: std::vec::Vec<CompilerError>,
pub(crate) template_syntax_quirks: bool,
pub(crate) node_removed: bool,
pub(crate) analysis: Option<&'a Croquis>,
pub(crate) hoisted_scope_id: Option<String>,
}
#[derive(Clone, Copy)]
pub enum ParentNode<'a> {
Root(*mut RootNode<'a>),
Element(*mut ElementNode<'a>),
If(*mut IfNode<'a>),
IfBranch(*mut IfBranchNode<'a>),
For(*mut ForNode<'a>),
}
impl<'a> ParentNode<'a> {
#[allow(clippy::mut_from_ref)]
pub fn children_mut(&self) -> &mut Vec<'a, TemplateChildNode<'a>> {
unsafe {
match self {
ParentNode::Root(r) => &mut (*(*r)).children,
ParentNode::Element(e) => &mut (*(*e)).children,
ParentNode::If(_) => {
panic!("IfNode doesn't have direct children")
}
ParentNode::IfBranch(b) => &mut (*(*b)).children,
ParentNode::For(f) => &mut (*(*f)).children,
}
}
}
}
pub fn transform<'a>(
allocator: &'a Bump,
root: &mut RootNode<'a>,
options: TransformOptions,
analysis: Option<&'a Croquis>,
) -> std::vec::Vec<CompilerError> {
transform_inner(allocator, root, options, analysis, false, None)
}
pub fn transform_with_template_syntax_quirks<'a>(
allocator: &'a Bump,
root: &mut RootNode<'a>,
options: TransformOptions,
analysis: Option<&'a Croquis>,
) -> std::vec::Vec<CompilerError> {
transform_inner(allocator, root, options, analysis, true, None)
}
#[deprecated(note = "use transform_with_template_syntax_quirks instead")]
pub fn transform_with_vue_parser_quirks<'a>(
allocator: &'a Bump,
root: &mut RootNode<'a>,
options: TransformOptions,
analysis: Option<&'a Croquis>,
) -> std::vec::Vec<CompilerError> {
transform_with_template_syntax_quirks(allocator, root, options, analysis)
}
#[doc(hidden)]
pub fn transform_with_hoisted_scope_id<'a>(
allocator: &'a Bump,
root: &mut RootNode<'a>,
options: TransformOptions,
analysis: Option<&'a Croquis>,
hoisted_scope_id: Option<String>,
) -> std::vec::Vec<CompilerError> {
transform_inner(allocator, root, options, analysis, false, hoisted_scope_id)
}
#[doc(hidden)]
pub fn transform_with_template_syntax_quirks_and_hoisted_scope_id<'a>(
allocator: &'a Bump,
root: &mut RootNode<'a>,
options: TransformOptions,
analysis: Option<&'a Croquis>,
hoisted_scope_id: Option<String>,
) -> std::vec::Vec<CompilerError> {
transform_inner(allocator, root, options, analysis, true, hoisted_scope_id)
}
#[doc(hidden)]
#[deprecated(note = "use transform_with_template_syntax_quirks_and_hoisted_scope_id instead")]
pub fn transform_with_vue_parser_quirks_and_hoisted_scope_id<'a>(
allocator: &'a Bump,
root: &mut RootNode<'a>,
options: TransformOptions,
analysis: Option<&'a Croquis>,
hoisted_scope_id: Option<String>,
) -> std::vec::Vec<CompilerError> {
transform_with_template_syntax_quirks_and_hoisted_scope_id(
allocator,
root,
options,
analysis,
hoisted_scope_id,
)
}
fn transform_inner<'a>(
allocator: &'a Bump,
root: &mut RootNode<'a>,
options: TransformOptions,
analysis: Option<&'a Croquis>,
template_syntax_quirks: bool,
hoisted_scope_id: Option<String>,
) -> std::vec::Vec<CompilerError> {
let source = root.source.clone();
let mut ctx = if let Some(analysis) = analysis {
TransformContext::with_analysis_and_template_syntax_quirks(
allocator,
source,
options,
analysis,
template_syntax_quirks,
)
} else {
TransformContext::new_with_template_syntax_quirks(
allocator,
source,
options,
template_syntax_quirks,
)
};
ctx.hoisted_scope_id = hoisted_scope_id;
ctx.root = Some(root as *mut _);
#[cfg(feature = "legacy")]
{
use vize_armature::legacy::LegacyDialectCapabilities;
let caps = LegacyDialectCapabilities::for_dialect(ctx.options.dialect);
crate::steps::legacy::desugar_legacy_template(allocator, root, caps);
}
profile!(
"atelier.transform.traverse_children",
traverse_children(&mut ctx, ParentNode::Root(root as *mut _))
);
use crate::steps::hoist_static::hoist_static;
profile!(
"atelier.transform.hoist_static",
hoist_static(&mut ctx, &mut root.children)
);
profile!(
"atelier.transform.register_root_helpers",
register_root_helpers(&mut ctx, root)
);
for helper in ctx.helpers.iter() {
root.helpers.push(helper);
}
for component in ctx.components.into_iter() {
root.components.push(component);
}
for directive in ctx.directives.into_iter() {
root.directives.push(directive);
}
#[cfg(feature = "legacy")]
for filter in ctx.filters.into_iter() {
root.filters.push(filter);
}
for hoist in ctx.hoists.into_iter() {
root.hoists.push(hoist);
}
root.temps = ctx.temps;
root.transformed = true;
ctx.errors
}
fn register_root_helpers<'a>(ctx: &mut TransformContext<'a>, root: &mut RootNode<'a>) {
if root.children.is_empty() {
return;
}
if root.children.len() > 1 {
ctx.helper(RuntimeHelper::OpenBlock);
ctx.helper(RuntimeHelper::CreateElementBlock);
ctx.helper(RuntimeHelper::Fragment);
}
}
#[cfg(test)]
mod tests;