use vize_l0::{Span, ToCompactString, id::NodeId};
use vize_l3::decision::vapor::{NativeTemplateVaporAnalysis, VaporPart, VaporUnsupported};
use crate::module::{AssemblyError, assemble_template_with_prelude};
use crate::runtime::{Runtime, vocabulary};
use crate::write::{Emitted, LinkSink, Writer};
mod component;
mod literal;
pub use component::emit_component;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VaporErrorKind {
Unsupported(VaporUnsupported),
MissingFacts,
ForeignRoot,
RuntimeHelper,
ScriptSource,
StyledSource,
ComponentFragmentLifecycle,
Assembly(AssemblyError),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VaporError {
pub node: Option<NodeId>,
pub span: Span,
pub kind: VaporErrorKind,
}
pub fn emit_template<L: LinkSink>(
analysis: &NativeTemplateVaporAnalysis<'_, '_>,
) -> Result<Emitted<L>, VaporError> {
let (prelude, render) = fragments(analysis)?;
assemble_template_with_prelude(prelude, render, vocabulary(Runtime::VueVapor))
.map_err(|assembly| error(VaporErrorKind::Assembly(assembly)))
}
fn error(kind: VaporErrorKind) -> VaporError {
VaporError {
node: None,
span: Span::new(0, 0),
kind,
}
}
fn fragments<L: LinkSink>(
analysis: &NativeTemplateVaporAnalysis<'_, '_>,
) -> Result<(Writer<L>, Writer<L>), VaporError> {
let facts = analysis
.vapor()
.ok_or_else(|| error(VaporErrorKind::MissingFacts))?;
if let Some(rejection) = facts.unsupported().first() {
return Err(VaporError {
node: Some(rejection.node),
span: rejection.span,
kind: VaporErrorKind::Unsupported(rejection.reason),
});
}
let runtime = vocabulary(Runtime::VueVapor);
let mut prelude = Writer::default();
let mut render = Writer::default();
render.push("function render(_ctx) {");
render.indent();
for (index, root) in facts.roots().iter().enumerate() {
let parts = facts
.parts(root)
.ok_or_else(|| error(VaporErrorKind::ForeignRoot))?;
render.newline();
render.push("const n");
number(&mut render, index);
render.push(" = ");
let helper = runtime
.helper("template")
.ok_or_else(|| error(VaporErrorKind::RuntimeHelper))?;
prelude.use_helper(helper);
prelude.push("const t");
number(&mut prelude, index);
prelude.push(" = _template(\"");
if let [VaporPart::Text { text, .. }] = parts {
prelude.anchor(text.span.start);
literal::raw(&mut prelude, text.content);
} else {
for part in parts {
literal::html_part(&mut prelude, part);
}
}
prelude.push(if facts.inherit_attrs() == Some(root.node()) {
"\", 3)\n"
} else {
"\", 2)\n"
});
render.push("t");
number(&mut render, index);
render.push("()");
}
render.newline();
render.push("return ");
let count = facts.roots().len();
if count != 1 {
render.push("[");
}
for index in 0..count {
if index != 0 {
render.push(", ");
}
render.push("n");
number(&mut render, index);
}
if count != 1 {
render.push("]");
}
render.deindent();
render.newline();
render.push("}");
Ok((prelude, render))
}
fn number<L: LinkSink>(writer: &mut Writer<L>, value: usize) {
writer.push(value.to_compact_string().as_str());
}