use crate::write::{LinkSink, Writer};
use vize_l0::{SourceBlock, Span};
use vize_l2::file::{BindingRef, DeclarationKind};
use vize_l2::lang::js::{NativeSelectedSetup, SetupAnnotation, VueSetup};
mod segments;
pub const COMPONENT_BINDING: &str = "_sfc_main";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SetupEmitErrorKind {
MissingBinding,
GeneratedBindingCollision,
InvalidTypeAnnotation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetupEmitError {
pub span: Span,
pub kind: SetupEmitErrorKind,
}
pub fn emit_setup<L: LinkSink>(
setup: &VueSetup<'_, '_, '_, '_>,
) -> Result<Writer<L>, SetupEmitError> {
emit_body(setup)
}
pub fn emit_selected_setup<L: LinkSink>(
setup: &NativeSelectedSetup<'_, '_>,
) -> Result<Writer<L>, SetupEmitError> {
emit_body(setup)
}
pub fn write_selected_setup_runtime_segments<L: LinkSink>(
setup: &NativeSelectedSetup<'_, '_>,
writer: &mut Writer<L>,
) -> Result<(), SetupEmitError> {
segments::validate(setup)?;
segments::append(setup, writer)
}
trait SetupInput<'owner, 'arena: 'owner> {
fn source(&self) -> SourceBlock<'arena>;
fn bindings(&self) -> impl Iterator<Item = BindingRef<'owner, 'arena>>;
fn type_annotations(&self) -> impl Iterator<Item = SetupAnnotation<'owner, 'arena>>;
}
impl<'owner, 'arena: 'owner> SetupInput<'owner, 'arena> for VueSetup<'owner, '_, '_, 'arena> {
fn source(&self) -> SourceBlock<'arena> {
self.source()
}
fn bindings(&self) -> impl Iterator<Item = BindingRef<'owner, 'arena>> {
self.bindings()
}
fn type_annotations(&self) -> impl Iterator<Item = SetupAnnotation<'owner, 'arena>> {
self.type_annotations()
}
}
impl<'owner, 'arena: 'owner> SetupInput<'owner, 'arena> for NativeSelectedSetup<'owner, 'arena> {
fn source(&self) -> SourceBlock<'arena> {
self.source()
}
fn bindings(&self) -> impl Iterator<Item = BindingRef<'owner, 'arena>> {
self.bindings()
}
fn type_annotations(&self) -> impl Iterator<Item = SetupAnnotation<'owner, 'arena>> {
self.type_annotations()
}
}
fn emit_body<'owner, 'arena: 'owner, L: LinkSink>(
setup: &impl SetupInput<'owner, 'arena>,
) -> Result<Writer<L>, SetupEmitError> {
let fail = |span, kind| SetupEmitError { span, kind };
for binding in setup.bindings() {
let declaration = binding
.declaration()
.ok_or_else(|| fail(setup.source().span(), SetupEmitErrorKind::MissingBinding))?;
if matches!(declaration.name.as_str(), "Object" | "__value" | "__v_raw") {
return Err(fail(
declaration.span,
SetupEmitErrorKind::GeneratedBindingCollision,
));
}
}
let source = setup.source();
segments::validate(setup)?;
let mut writer = Writer::default();
writer.push("const _sfc_main = {\n setup(__props, { expose: __expose }) {\n __expose();\n");
segments::append(setup, &mut writer)?;
writer.push("\n;\n const __returned__ = {\n");
for binding in setup.bindings() {
let declaration = binding
.declaration()
.ok_or_else(|| fail(source.span(), SetupEmitErrorKind::MissingBinding))?;
let name = declaration.name.as_str();
let span = declaration.span;
writer.push(" get ");
writer.push_named(name, span, name);
writer.push("() { return ");
writer.push_named(name, span, name);
writer.push(" },\n");
if matches!(
declaration.kind,
DeclarationKind::Let | DeclarationKind::Var
) {
writer.push(" set ");
writer.push_named(name, span, name);
writer.push("(__value) { ");
writer.push_named(name, span, name);
writer.push(" = __value },\n");
}
}
writer.push(" };\n Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true });\n return __returned__;\n }\n}\n");
Ok(writer)
}