use rspack_util::SpanExt;
use swc_core::{
common::Spanned,
ecma::ast::{TaggedTpl, Tpl},
};
use super::BasicEvaluatedExpression;
use crate::visitors::JavascriptParser;
#[derive(Debug, Clone, Copy)]
pub enum TemplateStringKind {
Cooked,
Raw,
}
#[inline]
fn get_simplified_template_result<'a>(
scanner: &mut JavascriptParser,
kind: TemplateStringKind,
node: &'a Tpl,
) -> (
Vec<BasicEvaluatedExpression<'a>>,
Vec<BasicEvaluatedExpression<'a>>,
) {
let mut quasis: Vec<BasicEvaluatedExpression<'a>> = vec![];
let mut parts: Vec<BasicEvaluatedExpression<'a>> = vec![];
for i in 0..node.quasis.len() {
let quasi_expr = &node.quasis[i];
let quasi = match kind {
TemplateStringKind::Cooked => {
quasi_expr
.cooked
.as_ref()
.and_then(|q| q.as_atom())
.unwrap_or(&quasi_expr.raw)
}
TemplateStringKind::Raw => &quasi_expr.raw,
};
if i > 0 {
let prev_expr = parts.last_mut().expect("should not empty");
let expr = scanner.evaluate_expression(&node.exprs[i - 1]);
if !expr.could_have_side_effects()
&& let Some(str) = expr.as_string()
{
prev_expr.set_string(format!("{}{}{}", prev_expr.string(), str, quasi));
prev_expr.set_range(prev_expr.range().0, quasi_expr.span().real_hi());
prev_expr.set_expression(None);
let prev_expr = quasis.last_mut().expect("should not empty");
prev_expr.set_string(format!("{}{}{}", prev_expr.string(), str, quasi));
prev_expr.set_range(prev_expr.range().0, quasi_expr.span().real_hi());
prev_expr.set_expression(None);
continue;
}
parts.push(expr);
}
let part = || {
let mut part = BasicEvaluatedExpression::new();
part.set_string(quasi.to_string());
part.set_range(quasi_expr.span().real_lo(), quasi_expr.span().real_hi());
part
};
quasis.push(part());
parts.push(part())
}
(quasis, parts)
}
#[inline]
pub fn eval_tpl_expression<'a>(
scanner: &mut JavascriptParser,
tpl: &'a Tpl,
) -> Option<BasicEvaluatedExpression<'a>> {
let kind = TemplateStringKind::Cooked;
let (quasis, mut parts) = get_simplified_template_result(scanner, kind, tpl);
if parts.len() == 1 {
let mut part = parts.remove(0);
part.set_range(tpl.span().real_lo(), tpl.span().real_hi());
Some(part)
} else {
let mut res = BasicEvaluatedExpression::with_range(tpl.span().real_lo(), tpl.span().real_hi());
res.set_template_string(quasis, parts, kind);
Some(res)
}
}
#[inline]
pub fn eval_tagged_tpl_expression<'a>(
scanner: &mut JavascriptParser,
tagged_tpl: &'a TaggedTpl,
) -> Option<BasicEvaluatedExpression<'a>> {
let tag = scanner.evaluate_expression(&tagged_tpl.tag);
if !tag.is_identifier() || tag.identifier() != "String.raw" {
return None;
};
let kind = TemplateStringKind::Raw;
let tpl = &tagged_tpl.tpl;
let (quasis, parts) = get_simplified_template_result(scanner, kind, tpl);
let mut res =
BasicEvaluatedExpression::with_range(tagged_tpl.span().real_lo(), tagged_tpl.span().real_hi());
res.set_template_string(quasis, parts, kind);
Some(res)
}