use itertools::Itertools;
use rustc_span::Symbol;
use swc_atoms::Atom;
use swc_common::DUMMY_SP;
use swc_ecma_ast::Function;
use swc_ecma_ast::{ModuleDecl, ModuleItem, TsType};
use crate::analyzer::{AxumRoute, BridgeMarkedItem};
use crate::analyzer::{BridgeMarkedItemKind, InertiaProp};
use crate::callbacks::{ConfigStrExt, config};
use crate::namespace::NamespacedPath;
use crate::swc_utils;
use swc_ecma_ast::Expr;
pub const INERTIA_FLASH_PROPS_TYPE_NAME: &str = "FlashProps";
pub const INERTIA_SHARE_PROPS_TYPE_NAME: &str = "ShareProps";
pub const ROUTE_DEFINITIONS_TYPE_NAME: &str = "RouteDefinitions";
pub fn insert_bridge_marked_items<'a, 'tcx>(
cg: &super::RiptCodegen<'tcx, '_>,
items: impl Iterator<Item = &'a BridgeMarkedItem<'tcx>>,
) where
'tcx: 'a,
{
for item in items {
match item.kind() {
BridgeMarkedItemKind::Ty(ty) => {
cg.as_swc(*ty, item.span());
}
}
}
}
pub fn insert_axum_route_types<'a, 'tcx>(
cg: &super::RiptCodegen<'tcx, '_>,
routes: impl Iterator<Item = &'a AxumRoute<'tcx>>,
) where
'tcx: 'a,
{
for route in routes {
route.path_tys().for_each(|ty| {
let _ = cg.as_swc(ty, route.span());
});
route.data_ty(&cg.analyzer).map(|ty| {
let _ = cg.as_swc(ty, route.span());
});
}
}
pub fn insert_flash_props(cg: &super::RiptCodegen<'_, '_>) {
let flash_props = cg.analyzer.inertia_flash_props();
let adt = props_into_object_type(cg, flash_props);
cg.insert_ts_type(
NamespacedPath::from_mod_syntax(config().ript_preamble_namespace())
.with_extra_segment(Symbol::intern(INERTIA_FLASH_PROPS_TYPE_NAME)),
adt,
);
}
pub fn insert_share_props(cg: &super::RiptCodegen<'_, '_>) {
let share_props = cg.analyzer.inertia_share_props();
let adt = props_into_object_type(cg, share_props);
cg.insert_ts_type(
NamespacedPath::from_mod_syntax(config().ript_preamble_namespace())
.with_extra_segment(Symbol::intern(INERTIA_SHARE_PROPS_TYPE_NAME)),
adt,
);
}
pub fn insert_inertia_route_href_builders<'a, 'tcx>(
cg: &super::RiptCodegen<'tcx, '_>,
routes: impl Iterator<Item = &'a AxumRoute<'tcx>>,
) where
'tcx: 'a,
{
routes.for_each(|r| {
let (tpl, params) = cg.axum_route_path_template_literal(r.id());
let path = cg.analyzer.namespaced_path(r.id());
let hfn = build_href_fn(r.route_path_symbol(), tpl.into(), params);
let item = ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(swc_ecma_ast::ExportDecl {
span: DUMMY_SP,
decl: swc_ecma_ast::Decl::Fn(swc_ecma_ast::FnDecl {
ident: swc_utils::ident(path.item_name().as_str().namespace_name()),
declare: false,
function: Box::new(hfn),
}),
}));
cg.insert_node(path, item);
})
}
pub fn insert_inertia_route_definitions_type<'a, 'tcx>(
cg: &super::RiptCodegen<'tcx, '_>,
routes: impl Iterator<Item = &'a AxumRoute<'tcx>>,
) where
'tcx: 'a,
{
let share_props: Vec<_> = cg.analyzer.inertia_share_props().map(|p| *p).collect();
let routes_ts_defs = routes
.map(|r| {
let data_ty = r.data_ty(&cg.analyzer);
let href = r.route_path_symbol();
let props_by_component = cg
.analyzer
.inertia_props_for_axum_route(r.id())
.chain(share_props.iter())
.filter_map(|p| {
cg.analyzer
.component_for_inertia_prop(p.id())
.map(|c| (c, p))
})
.group_by(|(component, _)| *component);
(r.http_method(), href, data_ty, props_by_component, r.span())
})
.map(|(method, href, data_ty, props_by_component, route_span)| {
let method = swc_utils::lit_str_type(method.static_str());
let href = swc_utils::lit_str_type(href.as_str());
let data_ty = data_ty.map(|t| cg.as_swc(t, route_span));
let props_by_component = props_by_component.into_iter().map(|(component, props)| {
let props_obj = props_into_object_type(cg, props.map(|(_component, p)| p));
swc_utils::object_member_type_element()
.key(component.as_str())
.type_ann(props_obj)
.optional(false)
.build()
});
let props_by_component = swc_utils::object_type_from_elements(props_by_component);
(
method,
href,
data_ty.unwrap_or_else(swc_utils::never_type),
props_by_component,
)
})
.map(|(method, href, data, props)| {
vec![
swc_utils::object_member_type_element()
.key("method")
.type_ann(method)
.optional(false)
.build(),
swc_utils::object_member_type_element()
.key("href")
.type_ann(href)
.optional(false)
.build(),
swc_utils::object_member_type_element()
.key("props")
.type_ann(props)
.optional(false)
.build(),
swc_utils::object_member_type_element()
.key("data")
.type_ann(data)
.optional(false)
.build(),
]
});
let routes_ts_defs = swc_utils::union_type(
routes_ts_defs.map(|els| swc_utils::object_type_from_elements(els.into_iter())),
);
let path = NamespacedPath::from_mod_syntax(config().ript_preamble_namespace())
.with_extra_segment(Symbol::intern(ROUTE_DEFINITIONS_TYPE_NAME));
cg.insert_ts_type(path, routes_ts_defs);
}
fn props_into_object_type<'cg, 'tcx>(
cg: &super::RiptCodegen<'tcx, '_>,
props: impl Iterator<Item = &'cg InertiaProp<'tcx>>,
) -> swc_ecma_ast::TsType
where
'tcx: 'cg,
{
swc_utils::object_type_from_elements(
cg.inertia_prop_tys_flattened_into_unions(props.map(|p| p))
.into_iter(),
)
}
fn build_href_fn(
raw_sym: Symbol,
path: Expr,
path_ty: impl Iterator<Item = (Atom, TsType)>,
) -> Function {
let params = path_ty
.map(|(k, v)| {
(
k.clone(),
swc_utils::object_member_type_element()
.key(k)
.type_ann(v)
.optional(false)
.build(),
)
})
.collect_vec();
let return_stmt = swc_utils::object_expr_from_props(
vec![
swc_utils::object_prop()
.key("hrefTemplate")
.inner(swc_utils::lit_str_as_const(raw_sym.as_str()).into())
.call(),
swc_utils::object_prop().key("href").inner(path).call(),
]
.into_iter(),
);
swc_utils::binding_fn()
.params(if params.is_empty() {
vec![]
} else {
vec![swc_utils::destructuring_param_from_elements(
params.into_iter(),
)]
})
.stmts(vec![swc_utils::return_stmt(return_stmt)])
.build()
}