use crate::{
ast::{
callee_ident_name, imported_specifier_name, make_ident, make_ident_arg, read_static_string,
},
dictionary_imports::{ImportKind, InjectedImports},
extra_caller::ExtraCallerContext,
packages::{GET_INTLAYER_ASYNC, PACKAGE_LIST, PACKAGE_LIST_DYNAMIC},
pre_pass::CallerMap,
};
use std::collections::{BTreeMap, HashSet};
use swc_core::ecma::{
ast::*,
visit::{VisitMut, VisitMutWith},
};
pub struct TransformVisitor<'a> {
import_mode: ImportKind,
dictionary_mode_map: &'a BTreeMap<String, String>,
caller_map: &'a CallerMap,
packages_with_dynamic_call: &'a HashSet<String>,
packages_with_fetch_call: &'a HashSet<String>,
extra: Option<ExtraCallerContext<'a>>,
pub injected_imports: InjectedImports,
}
impl<'a> TransformVisitor<'a> {
pub fn new(
import_mode: ImportKind,
dictionary_mode_map: &'a BTreeMap<String, String>,
caller_map: &'a CallerMap,
packages_with_dynamic_call: &'a HashSet<String>,
packages_with_fetch_call: &'a HashSet<String>,
extra: Option<ExtraCallerContext<'a>>,
) -> Self {
Self {
import_mode,
dictionary_mode_map,
caller_map,
packages_with_dynamic_call,
packages_with_fetch_call,
extra,
injected_imports: InjectedImports::default(),
}
}
fn package_uses_dynamic_helpers(&self, package_specifier: &str) -> bool {
if !PACKAGE_LIST_DYNAMIC.contains(&package_specifier) {
return false;
}
self.import_mode.is_dynamic_helper()
|| self.packages_with_dynamic_call.contains(package_specifier)
|| self.packages_with_fetch_call.contains(package_specifier)
}
fn dictionary_override(&self, dictionary_key: &str) -> Option<ImportKind> {
ImportKind::from_option(
self.dictionary_mode_map
.get(dictionary_key)
.map(String::as_str),
)
}
fn rewrite_native_call(
&mut self,
call: &mut CallExpr,
caller_name: &str,
caller_package: Option<&str>,
) {
let Some(arg) = call.args.first() else {
return;
};
let Some(dictionary_key) = read_static_string(&arg.expr) else {
return;
};
let dictionary_override = self.dictionary_override(&dictionary_key);
let uses_dynamic_helpers =
caller_package.is_some_and(|package| self.package_uses_dynamic_helpers(package));
let import_kind = if caller_name == GET_INTLAYER_ASYNC {
match dictionary_override {
Some(ImportKind::Fetch) => ImportKind::Fetch,
_ => ImportKind::Dynamic,
}
} else if caller_name != "useIntlayer" {
ImportKind::Static
} else if uses_dynamic_helpers {
dictionary_override.unwrap_or(self.import_mode)
} else {
dictionary_override
.filter(|kind| kind.is_dynamic_helper())
.unwrap_or(ImportKind::Static)
};
let ident = self
.injected_imports
.ident_for(&dictionary_key, import_kind);
if import_kind.is_dynamic_helper() {
call.args.insert(0, make_ident_arg(ident));
} else {
let Some(first_arg) = call.args.first_mut() else {
return;
};
first_arg.expr = Box::new(Expr::Ident(ident));
}
}
fn rewrite_native_import_specifier(
&self,
named: &mut ImportNamedSpecifier,
should_use_dynamic_helpers: bool,
) {
let imported_name = imported_specifier_name(named);
let replacement = match imported_name.as_str() {
"useIntlayer" if should_use_dynamic_helpers => "useDictionaryDynamic",
"useIntlayer" => "useDictionary",
"getIntlayer" => "getDictionary",
GET_INTLAYER_ASYNC => "getDictionaryAsync",
_ => return,
};
named.imported = Some(ModuleExportName::Ident(make_ident(replacement)));
}
}
impl VisitMut for TransformVisitor<'_> {
fn visit_mut_expr(&mut self, expr: &mut Expr) {
expr.visit_mut_children_with(self);
let Expr::Call(call) = expr else {
return;
};
let Some(callee_name) = callee_ident_name(&call.callee).map(str::to_string) else {
return;
};
let Some(meta) = self.caller_map.get(&callee_name) else {
return;
};
let extra_index = meta.extra_index;
let caller_name = meta.original_name.clone();
let caller_package = meta.package.clone();
match extra_index {
Some(extra_index) => {
if let Some(extra) = self.extra.as_ref() {
extra.rewrite_call(call, extra_index, &mut self.injected_imports);
}
}
None => self.rewrite_native_call(call, &caller_name, caller_package.as_deref()),
}
}
fn visit_mut_import_decl(&mut self, import: &mut ImportDecl) {
import.visit_mut_children_with(self);
let package_specifier = import.src.value.as_str().unwrap_or_default().to_string();
let is_native_package = PACKAGE_LIST.contains(&package_specifier.as_str());
let is_extra_package = self
.extra
.as_ref()
.is_some_and(|extra| extra.owns_import_source(&package_specifier));
if !is_native_package && !is_extra_package {
return;
}
let should_use_dynamic_helpers =
is_native_package && self.package_uses_dynamic_helpers(&package_specifier);
for specifier in &mut import.specifiers {
let ImportSpecifier::Named(named) = specifier else {
continue;
};
if is_native_package {
self.rewrite_native_import_specifier(named, should_use_dynamic_helpers);
}
if let Some(extra) = self.extra.as_ref() {
extra.rewrite_import_specifier(named, &package_specifier, self.caller_map);
}
}
}
}