swc_ecma_preset_env/corejs2/
mod.rs

1use std::sync::Arc;
2
3use indexmap::IndexSet;
4use preset_env_base::{version::should_enable, Versions};
5use rustc_hash::FxBuildHasher;
6use swc_atoms::Atom;
7use swc_ecma_ast::*;
8use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
9
10use self::builtin::BUILTINS;
11pub(crate) use self::entry::Entry;
12
13mod builtin;
14mod data;
15mod entry;
16
17pub(crate) struct UsageVisitor {
18    is_any_target: bool,
19    target: Arc<Versions>,
20    pub required: IndexSet<&'static str, FxBuildHasher>,
21}
22
23impl UsageVisitor {
24    pub fn new(target: Arc<Versions>) -> Self {
25        //        let mut v = Self { required: Vec::new() };
26        //
27        //
28        //        let is_web_target = target
29        //            .iter()
30        //            .any(|(name, version)| name != "node" &&
31        // version.is_some());
32        //
33        //        println!(
34        //            "is_any_target={:?}\nis_web_target={:?}",
35        //            is_any_target, is_web_target
36        //        );
37        //
38        //        // Web default
39        //        if is_any_target || is_web_target {
40        //            v.add(&["web.timers", "web.immediate",
41        // "web.dom.iterable"]);        }
42        //        v
43
44        //if target.is_any_target() || target.node.is_none() {
45        //    v.add(&["web.timers", "web.immediate", "web.dom.iterable"]);
46        //}
47
48        Self {
49            is_any_target: target.is_any_target(),
50            target,
51            required: Default::default(),
52        }
53    }
54
55    /// Add imports
56    fn add(&mut self, features: impl ExactSizeIterator<Item = &'static str>) {
57        let UsageVisitor {
58            is_any_target,
59            target,
60            ..
61        } = self;
62
63        self.required.extend(features.filter(|f| {
64            if !*is_any_target {
65                if let Some(v) = BUILTINS.get(*f) {
66                    // Skip
67                    if !should_enable(target, v, true) {
68                        return false;
69                    }
70                }
71            }
72
73            true
74        }));
75    }
76
77    fn add_property_deps_inner(&mut self, obj: Option<&Atom>, prop: &Atom) {
78        if let Some(obj) = obj {
79            if let Some(map) = data::static_properties_get(obj) {
80                if let Some(features) = map.get(prop) {
81                    self.add(features);
82                }
83            }
84        }
85
86        if let Some(features) = data::instance_properties_get(prop) {
87            self.add(features);
88        }
89    }
90
91    fn visit_object_pat_props(&mut self, obj: &Expr, props: &[ObjectPatProp]) {
92        let obj = match obj {
93            Expr::Ident(i) => Some(&i.sym),
94            _ => None,
95        };
96
97        for p in props {
98            match p {
99                ObjectPatProp::KeyValue(KeyValuePatProp {
100                    key: PropName::Ident(i),
101                    ..
102                }) => self.add_property_deps_inner(obj, &i.sym),
103                ObjectPatProp::Assign(AssignPatProp { key, .. }) => {
104                    self.add_property_deps_inner(obj, &key.sym)
105                }
106
107                _ => {}
108            }
109        }
110    }
111}
112
113// TODO:
114//     Program(path: NodePath) {
115//      path.get("body").forEach(bodyPath => {
116//        if (isPolyfillSource(getRequireSource(bodyPath))) {
117//          console.warn(NO_DIRECT_POLYFILL_IMPORT);
118//          bodyPath.remove();
119//        }
120//      });
121//    },
122
123/// Detects usage of types
124impl Visit for UsageVisitor {
125    noop_visit_type!(fail);
126
127    fn visit_ident(&mut self, node: &Ident) {
128        node.visit_children_with(self);
129
130        if let Some(features) = data::builtin_types_get(&node.sym) {
131            self.add(features);
132        }
133    }
134
135    fn visit_var_declarator(&mut self, d: &VarDeclarator) {
136        d.visit_children_with(self);
137
138        if let Some(ref init) = d.init {
139            if let Pat::Object(ref o) = d.name {
140                self.visit_object_pat_props(init, &o.props)
141            }
142        } else if let Pat::Object(ref o) = d.name {
143            self.visit_object_pat_props(&Ident::default().into(), &o.props)
144        }
145    }
146
147    fn visit_assign_expr(&mut self, e: &AssignExpr) {
148        e.visit_children_with(self);
149
150        if let AssignTarget::Pat(AssignTargetPat::Object(o)) = &e.left {
151            self.visit_object_pat_props(&e.right, &o.props)
152        }
153    }
154
155    /// Detects usage of instance properties and static properties.
156    ///
157    ///  - `Array.from`
158    fn visit_member_expr(&mut self, node: &MemberExpr) {
159        node.obj.visit_with(self);
160        if let MemberProp::Computed(c) = &node.prop {
161            c.visit_with(self);
162        }
163        //enter(path: NodePath) {
164        //    const { node } = path;
165        //    const { object, property } = node;
166        //
167        //    // ignore namespace
168        //    if (isNamespaced(path.get("object"))) return;
169        //
170        //    let evaluatedPropType = object.name;
171        //    let propertyName = "";
172        //    let instanceType = "";
173        //
174        //    if (node.computed) {
175        //        if (t.isStringLiteral(property)) {
176        //            propertyName = property.value;
177        //        } else {
178        //            const result = path.get("property").evaluate();
179        //            if (result.confident && result.value) {
180        //                propertyName = result.value;
181        //            }
182        //        }
183        //    } else {
184        //        propertyName = property.name;
185        //    }
186        //
187        //    if (path.scope.getBindingIdentifier(object.name)) {
188        //        const result = path.get("object").evaluate();
189        //        if (result.value) {
190        //            instanceType = getType(result.value);
191        //        } else if (result.deopt && result.deopt.isIdentifier()) {
192        //            evaluatedPropType = result.deopt.node.name;
193        //        }
194        //    }
195        //
196        //    if (has(STATIC_PROPERTIES, evaluatedPropType)) {
197        //        const BuiltInProperties = STATIC_PROPERTIES[evaluatedPropType];
198        //        if (has(BuiltInProperties, propertyName)) {
199        //            const StaticPropertyDependencies =
200        // BuiltInProperties[propertyName];
201        // this.addUnsupported(StaticPropertyDependencies);        }
202        //    }
203        //
204        //    if (has(INSTANCE_PROPERTIES, propertyName)) {
205        //        let InstancePropertyDependencies = INSTANCE_PROPERTIES[propertyName];
206        //        if (instanceType) {
207        //            InstancePropertyDependencies =
208        // InstancePropertyDependencies.filter(                module =>
209        // module.includes(instanceType),            );
210        //        }
211        //        this.addUnsupported(InstancePropertyDependencies);
212        //    }
213        //},
214        //
215        //// Symbol.match
216        //exit(path: NodePath) {
217        //    const { name } = path.node.object;
218        //
219        //    if (!has(BUILT_INS, name)) return;
220        //    if (path.scope.getBindingIdentifier(name)) return;
221        //
222        //    const BuiltInDependencies = BUILT_INS[name];
223        //    this.addUnsupported(BuiltInDependencies);
224        //},
225
226        match &node.prop {
227            MemberProp::Ident(i) => {
228                //
229                if let Some(imports) = data::instance_properties_get(&i.sym) {
230                    self.add(imports);
231                }
232            }
233            MemberProp::Computed(ComputedPropName { expr, .. }) => {
234                if let Expr::Lit(Lit::Str(Str { value, .. })) = &**expr {
235                    if let Some(imports) = data::instance_properties_get(&value.to_string_lossy()) {
236                        self.add(imports);
237                    }
238                }
239            }
240            _ => {}
241        }
242        if let Expr::Ident(obj) = &*node.obj {
243            if let Some(props) = data::static_properties_get(&obj.sym) {
244                match &node.prop {
245                    MemberProp::Computed(ComputedPropName { expr, .. }) => {
246                        if let Expr::Lit(Lit::Str(Str { value, .. })) = &**expr {
247                            if let Some(imports) =
248                                data::instance_properties_get(&value.to_string_lossy())
249                            {
250                                self.add(imports);
251                            }
252                        }
253                    }
254
255                    MemberProp::Ident(ref p) => {
256                        if let Some(imports) = props.get(&p.sym) {
257                            self.add(imports);
258                        }
259                    }
260
261                    _ => {}
262                }
263            }
264        }
265    }
266
267    // maybe not needed?
268    fn visit_super_prop_expr(&mut self, node: &SuperPropExpr) {
269        if let SuperProp::Computed(c) = &node.prop {
270            c.visit_with(self);
271        }
272    }
273
274    ///
275    /// - `arr[Symbol.iterator]()`
276    fn visit_call_expr(&mut self, e: &CallExpr) {
277        e.visit_children_with(self);
278
279        if match &e.callee {
280            Callee::Expr(callee) => matches!(&**callee, Expr::Member(MemberExpr {
281                    prop: MemberProp::Computed(ComputedPropName { expr, .. }),
282                    ..
283                }) if is_symbol_iterator(expr)),
284            _ => false,
285        } {
286            self.add(["web.dom.iterable"].iter().copied())
287        }
288    }
289
290    ///
291    /// - `Symbol.iterator in arr`
292    fn visit_bin_expr(&mut self, e: &BinExpr) {
293        e.visit_children_with(self);
294
295        match e.op {
296            op!("in") if is_symbol_iterator(&e.left) => {
297                self.add(["web.dom.iterable"].iter().copied())
298            }
299            _ => {}
300        }
301    }
302
303    ///
304    /// - `yield*`
305    fn visit_yield_expr(&mut self, e: &YieldExpr) {
306        e.visit_children_with(self);
307
308        if e.delegate {
309            self.add(["web.dom.iterable"].iter().copied())
310        }
311    }
312}
313
314fn is_symbol_iterator(e: &Expr) -> bool {
315    match e {
316        Expr::Member(MemberExpr { obj, prop, .. }) if prop.is_ident_with("iterator") => {
317            obj.is_ident_ref_to("Symbol")
318        }
319        _ => false,
320    }
321}