use rspack_util::SpanExt;
use swc_experimental_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<'parser: 'a, 'a>(
scanner: &mut JavascriptParser<'parser>,
kind: TemplateStringKind,
node: &'a Tpl<'a>,
) -> (
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().map_or_else(
|| quasi_expr.raw.to_string(),
|q| q.as_wtf8().to_string_lossy().to_string(),
)
}
TemplateStringKind::Raw => quasi_expr.raw.to_string(),
};
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.clone());
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<'parser: 'a, 'a>(
scanner: &mut JavascriptParser<'parser>,
tpl: &'a Tpl<'a>,
) -> 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<'parser: 'a, 'a>(
scanner: &mut JavascriptParser<'parser>,
tagged_tpl: &'a TaggedTpl<'a>,
) -> 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)
}