swc_ecma_preset_env/corejs3/
usage.rs

1use std::sync::Arc;
2
3use indexmap::IndexSet;
4use preset_env_base::version::{should_enable, Version};
5use rustc_hash::FxBuildHasher;
6use swc_atoms::Atom;
7use swc_ecma_ast::*;
8use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
9
10use super::{
11    builtin::{self, CoreJSPolyfillDescriptor},
12    data,
13};
14use crate::{corejs3::compat::DATA as CORE_JS_COMPAT_DATA, Versions};
15
16pub(crate) struct UsageVisitor {
17    shipped_proposals: bool,
18    is_any_target: bool,
19    target: Arc<Versions>,
20    corejs_version: Version,
21    pub required: IndexSet<&'static str, FxBuildHasher>,
22}
23
24impl UsageVisitor {
25    pub fn new(target: Arc<Versions>, shipped_proposals: bool, corejs_version: Version) -> Self {
26        //        let mut v = Self { required: Vec::new() };
27        //
28        //
29        //        let is_web_target = target
30        //            .iter()
31        //            .any(|(name, version)| name != "node" &&
32        // version.is_some());
33        //
34        //        println!(
35        //            "is_any_target={:?}\nis_web_target={:?}",
36        //            is_any_target, is_web_target
37        //        );
38        //
39        //        // Web default
40        //        if is_any_target || is_web_target {
41        //            v.add(&["web.timers", "web.immediate",
42        // "web.dom.iterable"]);        }
43        //        v
44
45        Self {
46            shipped_proposals,
47            is_any_target: target.is_any_target(),
48            target,
49            corejs_version,
50            required: Default::default(),
51        }
52    }
53
54    fn add(&mut self, desc: &CoreJSPolyfillDescriptor) {
55        let deps = desc.global();
56
57        // TODO: Exclude based on object
58
59        self.may_inject_global(deps)
60    }
61
62    /// Add imports
63    fn may_inject_global(&mut self, features: impl ExactSizeIterator<Item = &'static str>) {
64        let UsageVisitor {
65            shipped_proposals,
66            is_any_target,
67            target,
68            corejs_version,
69            ..
70        } = self;
71
72        for f in features {
73            if !*shipped_proposals && f.starts_with("esnext.") {
74                continue;
75            }
76
77            let feature = CORE_JS_COMPAT_DATA.get(f);
78
79            if !*is_any_target {
80                if let Some(feature) = feature {
81                    if !should_enable(target, feature, true) {
82                        continue;
83                    }
84                }
85            }
86
87            if let Some(version) = data::modules_by_version(f) {
88                if version > *corejs_version {
89                    if *shipped_proposals {
90                        if let Some(esnext_module) = data::esnext_fallback(f) {
91                            if let Some(esnext_version) = data::modules_by_version(esnext_module) {
92                                if esnext_version <= *corejs_version {
93                                    self.required.insert(esnext_module);
94                                }
95                            }
96                        }
97                    }
98                    continue;
99                }
100            }
101
102            self.required.insert(f);
103        }
104    }
105
106    fn add_builtin(&mut self, built_in: &str) {
107        if let Some(features) = builtin::builtin_types_get(built_in) {
108            self.add(&features);
109        }
110    }
111
112    fn add_property_deps(&mut self, obj: &Expr, prop: &Atom) {
113        let obj = match obj {
114            Expr::Ident(i) => &i.sym,
115            _ => {
116                self.add_property_deps_inner(None, prop);
117                return;
118            }
119        };
120
121        self.add_property_deps_inner(Some(obj), prop)
122    }
123
124    fn add_property_deps_inner(&mut self, obj: Option<&Atom>, prop: &Atom) {
125        if let Some(obj) = obj {
126            if data::POSSIBLE_GLOBAL_OBJECTS.contains(&&**obj) {
127                self.add_builtin(prop);
128            }
129
130            if let Some(map) = builtin::static_properties_get(obj) {
131                if let Some(features) = map.get(prop) {
132                    self.add(&features);
133                    return;
134                }
135            }
136        }
137
138        if let Some(features) = builtin::instance_properties_get(prop) {
139            self.add(&features);
140        }
141    }
142
143    fn visit_object_pat_props(&mut self, obj: &Expr, props: &[ObjectPatProp]) {
144        let obj = match obj {
145            Expr::Ident(i) => Some(&i.sym),
146            _ => None,
147        };
148
149        for p in props {
150            match p {
151                ObjectPatProp::KeyValue(KeyValuePatProp {
152                    key: PropName::Ident(i),
153                    ..
154                }) => self.add_property_deps_inner(obj, &i.sym),
155                ObjectPatProp::Assign(AssignPatProp { key, .. }) => {
156                    self.add_property_deps_inner(obj, &key.sym)
157                }
158
159                _ => {}
160            }
161        }
162    }
163}
164
165impl Visit for UsageVisitor {
166    noop_visit_type!(fail);
167
168    /// `[a, b] = c`
169    fn visit_array_pat(&mut self, p: &ArrayPat) {
170        p.visit_children_with(self);
171
172        self.may_inject_global(builtin::common_iterators())
173    }
174
175    fn visit_assign_expr(&mut self, e: &AssignExpr) {
176        e.visit_children_with(self);
177
178        if let AssignTarget::Pat(AssignTargetPat::Object(o)) = &e.left {
179            self.visit_object_pat_props(&e.right, &o.props)
180        }
181    }
182
183    fn visit_bin_expr(&mut self, e: &BinExpr) {
184        e.visit_children_with(self);
185
186        if e.op == op!("in") {
187            // 'entries' in Object
188            // 'entries' in [1, 2, 3]
189            if let Expr::Lit(Lit::Str(s)) = &*e.left {
190                let prop_atom = s.value.to_atom_lossy();
191                self.add_property_deps(&e.right, prop_atom.as_ref());
192            }
193        }
194    }
195
196    /// import('something').then(...)
197    /// RegExp(".", "us")
198    fn visit_call_expr(&mut self, e: &CallExpr) {
199        e.visit_children_with(self);
200
201        if let Callee::Import(_) = &e.callee {
202            self.may_inject_global(builtin::promise_dependencies())
203        }
204    }
205
206    fn visit_expr(&mut self, e: &Expr) {
207        e.visit_children_with(self);
208
209        if let Expr::Ident(i) = e {
210            self.add_builtin(&i.sym)
211        }
212    }
213
214    /// `[...spread]`
215    fn visit_expr_or_spread(&mut self, e: &ExprOrSpread) {
216        e.visit_children_with(self);
217        if e.spread.is_some() {
218            self.may_inject_global(builtin::common_iterators())
219        }
220    }
221
222    /// for-of
223    fn visit_for_of_stmt(&mut self, s: &ForOfStmt) {
224        s.visit_children_with(self);
225
226        self.may_inject_global(builtin::common_iterators())
227    }
228
229    fn visit_function(&mut self, f: &Function) {
230        f.visit_children_with(self);
231
232        if f.is_async {
233            self.may_inject_global(builtin::promise_dependencies())
234        }
235    }
236
237    fn visit_member_expr(&mut self, e: &MemberExpr) {
238        e.obj.visit_with(self);
239        if let MemberProp::Computed(c) = &e.prop {
240            if let Expr::Lit(Lit::Str(s)) = &*c.expr {
241                let prop_atom = s.value.to_atom_lossy();
242                self.add_property_deps(&e.obj, prop_atom.as_ref());
243            }
244            c.visit_with(self);
245        }
246
247        // Object.entries
248        // [1, 2, 3].entries
249        if let MemberProp::Ident(i) = &e.prop {
250            self.add_property_deps(&e.obj, &i.sym)
251        }
252    }
253
254    fn visit_super_prop_expr(&mut self, e: &SuperPropExpr) {
255        if let SuperProp::Computed(c) = &e.prop {
256            c.visit_with(self);
257        }
258    }
259
260    fn visit_var_declarator(&mut self, d: &VarDeclarator) {
261        d.visit_children_with(self);
262
263        if let Some(ref init) = d.init {
264            if let Pat::Object(ref o) = d.name {
265                self.visit_object_pat_props(init, &o.props)
266            }
267        } else if let Pat::Object(ref o) = d.name {
268            self.visit_object_pat_props(&Ident::default().into(), &o.props)
269        }
270    }
271
272    /// `yield*`
273    fn visit_yield_expr(&mut self, e: &YieldExpr) {
274        e.visit_children_with(self);
275
276        if e.delegate {
277            self.may_inject_global(builtin::common_iterators())
278        }
279    }
280}