intlayer_swc_plugin/
optimize.rs1use crate::{
12 ast::{
13 callee_ident_name, imported_specifier_name, make_ident, make_ident_arg, read_static_string,
14 },
15 dictionary_imports::{ImportKind, InjectedImports},
16 extra_caller::ExtraCallerContext,
17 packages::{GET_INTLAYER_ASYNC, PACKAGE_LIST, PACKAGE_LIST_DYNAMIC},
18 pre_pass::CallerMap,
19};
20use std::collections::{BTreeMap, HashSet};
21use swc_core::ecma::{
22 ast::*,
23 visit::{VisitMut, VisitMutWith},
24};
25
26pub struct TransformVisitor<'a> {
27 import_mode: ImportKind,
28 dictionary_mode_map: &'a BTreeMap<String, String>,
29 caller_map: &'a CallerMap,
30 packages_with_dynamic_call: &'a HashSet<String>,
33 packages_with_fetch_call: &'a HashSet<String>,
36 extra: Option<ExtraCallerContext<'a>>,
38 pub injected_imports: InjectedImports,
40}
41
42impl<'a> TransformVisitor<'a> {
43 pub fn new(
44 import_mode: ImportKind,
45 dictionary_mode_map: &'a BTreeMap<String, String>,
46 caller_map: &'a CallerMap,
47 packages_with_dynamic_call: &'a HashSet<String>,
48 packages_with_fetch_call: &'a HashSet<String>,
49 extra: Option<ExtraCallerContext<'a>>,
50 ) -> Self {
51 Self {
52 import_mode,
53 dictionary_mode_map,
54 caller_map,
55 packages_with_dynamic_call,
56 packages_with_fetch_call,
57 extra,
58 injected_imports: InjectedImports::default(),
59 }
60 }
61
62 fn package_uses_dynamic_helpers(&self, package_specifier: &str) -> bool {
70 if !PACKAGE_LIST_DYNAMIC.contains(&package_specifier) {
71 return false;
72 }
73
74 self.import_mode.is_dynamic_helper()
75 || self.packages_with_dynamic_call.contains(package_specifier)
76 || self.packages_with_fetch_call.contains(package_specifier)
77 }
78
79 fn dictionary_override(&self, dictionary_key: &str) -> Option<ImportKind> {
81 ImportKind::from_option(
82 self.dictionary_mode_map
83 .get(dictionary_key)
84 .map(String::as_str),
85 )
86 }
87
88 fn rewrite_native_call(
95 &mut self,
96 call: &mut CallExpr,
97 caller_name: &str,
98 caller_package: Option<&str>,
99 ) {
100 let Some(arg) = call.args.first() else {
101 return;
102 };
103
104 let Some(dictionary_key) = read_static_string(&arg.expr) else {
108 return;
109 };
110
111 let dictionary_override = self.dictionary_override(&dictionary_key);
112
113 let uses_dynamic_helpers =
114 caller_package.is_some_and(|package| self.package_uses_dynamic_helpers(package));
115
116 let import_kind = if caller_name == GET_INTLAYER_ASYNC {
117 match dictionary_override {
122 Some(ImportKind::Fetch) => ImportKind::Fetch,
123 _ => ImportKind::Dynamic,
124 }
125 } else if caller_name != "useIntlayer" {
126 ImportKind::Static
127 } else if uses_dynamic_helpers {
128 dictionary_override.unwrap_or(self.import_mode)
129 } else {
130 dictionary_override
133 .filter(|kind| kind.is_dynamic_helper())
134 .unwrap_or(ImportKind::Static)
135 };
136
137 let ident = self
138 .injected_imports
139 .ident_for(&dictionary_key, import_kind);
140
141 if import_kind.is_dynamic_helper() {
142 call.args.insert(0, make_ident_arg(ident));
144 } else {
145 let Some(first_arg) = call.args.first_mut() else {
148 return;
149 };
150 first_arg.expr = Box::new(Expr::Ident(ident));
151 }
152 }
153
154 fn rewrite_native_import_specifier(
157 &self,
158 named: &mut ImportNamedSpecifier,
159 should_use_dynamic_helpers: bool,
160 ) {
161 let imported_name = imported_specifier_name(named);
162
163 let replacement = match imported_name.as_str() {
164 "useIntlayer" if should_use_dynamic_helpers => "useDictionaryDynamic",
165 "useIntlayer" => "useDictionary",
166 "getIntlayer" => "getDictionary",
167 GET_INTLAYER_ASYNC => "getDictionaryAsync",
168 _ => return,
169 };
170
171 named.imported = Some(ModuleExportName::Ident(make_ident(replacement)));
172 }
173}
174
175impl VisitMut for TransformVisitor<'_> {
176 fn visit_mut_expr(&mut self, expr: &mut Expr) {
177 expr.visit_mut_children_with(self);
178
179 let Expr::Call(call) = expr else {
180 return;
181 };
182
183 let Some(callee_name) = callee_ident_name(&call.callee).map(str::to_string) else {
186 return;
187 };
188
189 let Some(meta) = self.caller_map.get(&callee_name) else {
190 return;
191 };
192 let extra_index = meta.extra_index;
193 let caller_name = meta.original_name.clone();
194 let caller_package = meta.package.clone();
195
196 match extra_index {
197 Some(extra_index) => {
198 if let Some(extra) = self.extra.as_ref() {
199 extra.rewrite_call(call, extra_index, &mut self.injected_imports);
200 }
201 }
202 None => self.rewrite_native_call(call, &caller_name, caller_package.as_deref()),
203 }
204 }
205
206 fn visit_mut_import_decl(&mut self, import: &mut ImportDecl) {
207 import.visit_mut_children_with(self);
208
209 let package_specifier = import.src.value.as_str().unwrap_or_default().to_string();
210
211 let is_native_package = PACKAGE_LIST.contains(&package_specifier.as_str());
212 let is_extra_package = self
213 .extra
214 .as_ref()
215 .is_some_and(|extra| extra.owns_import_source(&package_specifier));
216
217 if !is_native_package && !is_extra_package {
218 return;
219 }
220
221 let should_use_dynamic_helpers =
222 is_native_package && self.package_uses_dynamic_helpers(&package_specifier);
223
224 for specifier in &mut import.specifiers {
225 let ImportSpecifier::Named(named) = specifier else {
226 continue;
227 };
228
229 if is_native_package {
230 self.rewrite_native_import_specifier(named, should_use_dynamic_helpers);
231 }
232
233 if let Some(extra) = self.extra.as_ref() {
234 extra.rewrite_import_specifier(named, &package_specifier, self.caller_map);
235 }
236 }
237 }
238}