swc_ecma_preset_env/corejs3/
usage.rs1use 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 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 self.may_inject_global(deps)
60 }
61
62 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 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 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 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 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 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 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 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}