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}