1#![deny(clippy::all)]
2#![allow(dead_code)]
3#![recursion_limit = "256"]
4
5use std::{path::PathBuf, sync::Arc};
6
7use preset_env_base::query::targets_to_versions;
8pub use preset_env_base::{query::Targets, version::Version, BrowserData, Versions};
9use rustc_hash::FxHashSet;
10use serde::Deserialize;
11use swc_atoms::{atom, Atom};
12use swc_common::{comments::Comments, pass::Optional, FromVariant, Mark, SyntaxContext, DUMMY_SP};
13use swc_ecma_ast::*;
14use swc_ecma_transforms::{
15 compat::{
16 bugfixes,
17 class_fields_use_set::class_fields_use_set,
18 es2015::{self, generator::generator},
19 es2016, es2017, es2018, es2019, es2020, es2021, es2022, es3,
20 regexp::{self, regexp},
21 },
22 feature::FeatureFlag,
23 Assumptions,
24};
25use swc_ecma_utils::{prepend_stmts, ExprFactory};
26use swc_ecma_visit::{visit_mut_pass, VisitMut, VisitMutWith, VisitWith};
27
28pub use self::transform_data::Feature;
29
30#[macro_use]
31mod util;
32mod corejs2;
33mod corejs3;
34mod regenerator;
35mod transform_data;
36
37pub fn preset_env<C>(
38 unresolved_mark: Mark,
39 comments: Option<C>,
40 c: Config,
41 assumptions: Assumptions,
42 feature_set: &mut FeatureFlag,
43) -> impl Pass
44where
45 C: Comments + Clone,
46{
47 let loose = c.loose;
48 let targets = targets_to_versions(c.targets).expect("failed to parse targets");
49 let is_any_target = targets.is_any_target();
50
51 let (include, included_modules) = FeatureOrModule::split(c.include);
52 let (exclude, excluded_modules) = FeatureOrModule::split(c.exclude);
53
54 let pass = noop_pass();
55
56 macro_rules! should_enable {
57 ($feature:ident, $default:expr) => {{
58 let f = transform_data::Feature::$feature;
59 !exclude.contains(&f)
60 && (c.force_all_transforms
61 || (is_any_target
62 || include.contains(&f)
63 || f.should_enable(&targets, c.bugfixes, $default)))
64 }};
65 }
66
67 macro_rules! add {
68 ($prev:expr, $feature:ident, $pass:expr) => {{
69 add!($prev, $feature, $pass, false)
70 }};
71 ($prev:expr, $feature:ident, $pass:expr, $default:expr) => {{
72 let f = transform_data::Feature::$feature;
73
74 let enable = should_enable!($feature, $default);
75
76 if !enable {
77 *feature_set |= swc_ecma_transforms::feature::FeatureFlag::$feature;
78 }
79
80 if c.debug {
81 println!("{}: {:?}", f.as_str(), enable);
82 }
83 ($prev, Optional::new($pass, enable))
84 }};
85 }
86
87 let pass = (
88 pass,
89 Optional::new(
90 class_fields_use_set(assumptions.pure_getters),
91 assumptions.set_public_class_fields,
92 ),
93 );
94
95 let pass = {
96 let enable_dot_all_regex = should_enable!(DotAllRegex, false);
97 let enable_named_capturing_groups_regex = should_enable!(NamedCapturingGroupsRegex, false);
98 let enable_sticky_regex = should_enable!(StickyRegex, false);
99 let enable_unicode_property_regex = should_enable!(UnicodePropertyRegex, false);
100 let enable_unicode_regex = should_enable!(UnicodeRegex, false);
101 let enable_unicode_sets_regex = should_enable!(UnicodeSetsRegex, false);
102
103 let enable = enable_dot_all_regex
104 || enable_named_capturing_groups_regex
105 || enable_sticky_regex
106 || enable_unicode_property_regex
107 || enable_unicode_regex;
108
109 (
110 pass,
111 Optional::new(
112 regexp(regexp::Config {
113 dot_all_regex: enable_dot_all_regex,
114 has_indices: false,
116 lookbehind_assertion: false,
118 named_capturing_groups_regex: enable_named_capturing_groups_regex,
119 sticky_regex: enable_sticky_regex,
120 unicode_property_regex: enable_unicode_property_regex,
121 unicode_regex: enable_unicode_regex,
122 unicode_sets_regex: enable_unicode_sets_regex,
123 }),
124 enable,
125 ),
126 )
127 };
128
129 let pass = add!(pass, ClassStaticBlock, es2022::static_blocks());
136 let pass = add!(
137 pass,
138 ClassProperties,
139 es2022::class_properties(
140 es2022::class_properties::Config {
141 private_as_properties: loose || assumptions.private_fields_as_properties,
142 set_public_fields: loose || assumptions.set_public_class_fields,
143 constant_super: loose || assumptions.constant_super,
144 no_document_all: loose || assumptions.no_document_all,
145 pure_getter: loose || assumptions.pure_getters,
146 },
147 unresolved_mark
148 )
149 );
150 let pass = add!(pass, PrivatePropertyInObject, es2022::private_in_object());
151
152 let pass = add!(
154 pass,
155 LogicalAssignmentOperators,
156 es2021::logical_assignments()
157 );
158
159 let pass = add!(pass, ExportNamespaceFrom, es2020::export_namespace_from());
162 let pass = add!(
163 pass,
164 NullishCoalescing,
165 es2020::nullish_coalescing(es2020::nullish_coalescing::Config {
166 no_document_all: loose || assumptions.no_document_all
167 })
168 );
169
170 let pass = add!(
171 pass,
172 OptionalChaining,
173 es2020::optional_chaining(
174 es2020::optional_chaining::Config {
175 no_document_all: loose || assumptions.no_document_all,
176 pure_getter: loose || assumptions.pure_getters
177 },
178 unresolved_mark
179 )
180 );
181
182 let pass = add!(pass, OptionalCatchBinding, es2019::optional_catch_binding());
184
185 let pass = add!(
187 pass,
188 ObjectRestSpread,
189 es2018::object_rest_spread(es2018::object_rest_spread::Config {
190 no_symbol: loose || assumptions.object_rest_no_symbols,
191 set_property: loose || assumptions.set_spread_properties,
192 pure_getters: loose || assumptions.pure_getters
193 })
194 );
195
196 let pass = add!(
198 pass,
199 AsyncToGenerator,
200 es2017::async_to_generator(
201 es2017::async_to_generator::Config {
202 ignore_function_name: loose || assumptions.ignore_function_name,
203 ignore_function_length: loose || assumptions.ignore_function_length,
204 },
205 unresolved_mark
206 )
207 );
208
209 let pass = add!(pass, ExponentiationOperator, es2016::exponentiation());
211
212 let pass = add!(pass, BlockScopedFunctions, es2015::block_scoped_functions());
214 let pass = add!(
215 pass,
216 TemplateLiterals,
217 es2015::template_literal(es2015::template_literal::Config {
218 ignore_to_primitive: loose || assumptions.ignore_to_primitive_hint,
219 mutable_template: loose || assumptions.mutable_template_object
220 }),
221 true
222 );
223 let pass = add!(
224 pass,
225 Classes,
226 es2015::classes(es2015::classes::Config {
227 constant_super: loose || assumptions.constant_super,
228 no_class_calls: loose || assumptions.no_class_calls,
229 set_class_methods: loose || assumptions.set_class_methods,
230 super_is_callable_constructor: loose || assumptions.super_is_callable_constructor,
231 })
232 );
233 let pass = add!(
234 pass,
235 Spread,
236 es2015::spread(es2015::spread::Config { loose }),
237 true
238 );
239 let pass = add!(pass, ObjectSuper, es2015::object_super());
240 let pass = add!(pass, FunctionName, es2015::function_name());
241 let pass = add!(pass, ShorthandProperties, es2015::shorthand());
242 let pass = add!(
243 pass,
244 Parameters,
245 es2015::parameters(
246 es2015::parameters::Config {
247 ignore_function_length: loose || assumptions.ignore_function_length
248 },
249 unresolved_mark
250 )
251 );
252 let pass = add!(pass, ArrowFunctions, es2015::arrow(unresolved_mark));
253 let pass = add!(pass, DuplicateKeys, es2015::duplicate_keys());
254 let pass = add!(pass, StickyRegex, es2015::sticky_regex());
255 let pass = add!(pass, TypeOfSymbol, es2015::typeof_symbol());
257 let pass = add!(
258 pass,
259 ForOf,
260 es2015::for_of(es2015::for_of::Config {
261 loose,
262 assume_array: loose || assumptions.iterable_is_array
263 }),
264 true
265 );
266 let pass = add!(
267 pass,
268 ComputedProperties,
269 es2015::computed_properties(es2015::computed_props::Config { loose }),
270 true
271 );
272 let pass = add!(
273 pass,
274 Destructuring,
275 es2015::destructuring(es2015::destructuring::Config { loose }),
276 true
277 );
278 let pass = add!(
279 pass,
280 BlockScoping,
281 es2015::block_scoping(unresolved_mark),
282 true
283 );
284 let pass = add!(
285 pass,
286 Regenerator,
287 generator(unresolved_mark, comments),
288 true
289 );
290
291 let pass = add!(pass, NewTarget, es2015::new_target(), true);
292
293 let pass = add!(pass, PropertyLiterals, es3::property_literals());
305 let pass = add!(
306 pass,
307 MemberExpressionLiterals,
308 es3::member_expression_literals()
309 );
310 let pass = add!(pass, ReservedWords, es3::reserved_words(c.dynamic_import));
311
312 let pass = add!(pass, BugfixEdgeDefaultParam, bugfixes::edge_default_param());
314 let pass = add!(
315 pass,
316 BugfixAsyncArrowsInClass,
317 bugfixes::async_arrows_in_class(unresolved_mark)
318 );
319 let pass = add!(
320 pass,
321 BugfixTaggedTemplateCaching,
322 bugfixes::template_literal_caching()
323 );
324 let pass = add!(
325 pass,
326 BugfixSafariIdDestructuringCollisionInFunctionExpression,
327 bugfixes::safari_id_destructuring_collision_in_function_expression()
328 );
329
330 if c.debug {
331 println!("Targets: {:?}", targets);
332 }
333
334 (
335 pass,
336 visit_mut_pass(Polyfills {
337 mode: c.mode,
338 regenerator: should_enable!(Regenerator, true),
339 corejs: c.core_js.unwrap_or(Version {
340 major: 3,
341 minor: 0,
342 patch: 0,
343 }),
344 shipped_proposals: c.shipped_proposals,
345 targets,
346 includes: included_modules,
347 excludes: excluded_modules,
348 unresolved_mark,
349 }),
350 )
351}
352
353#[derive(Debug)]
354struct Polyfills {
355 mode: Option<Mode>,
356 targets: Arc<Versions>,
357 shipped_proposals: bool,
358 corejs: Version,
359 regenerator: bool,
360 includes: FxHashSet<String>,
361 excludes: FxHashSet<String>,
362 unresolved_mark: Mark,
363}
364impl Polyfills {
365 fn collect<T>(&mut self, m: &mut T) -> Vec<Atom>
366 where
367 T: VisitWith<corejs2::UsageVisitor>
368 + VisitWith<corejs3::UsageVisitor>
369 + VisitMutWith<corejs2::Entry>
370 + VisitMutWith<corejs3::Entry>,
371 {
372 let required = match self.mode {
373 None => Default::default(),
374 Some(Mode::Usage) => {
375 let mut r = match self.corejs {
376 Version { major: 2, .. } => {
377 let mut v = corejs2::UsageVisitor::new(self.targets.clone());
378 m.visit_with(&mut v);
379
380 v.required
381 }
382 Version { major: 3, .. } => {
383 let mut v = corejs3::UsageVisitor::new(
384 self.targets.clone(),
385 self.shipped_proposals,
386 self.corejs,
387 );
388 m.visit_with(&mut v);
389 v.required
390 }
391
392 _ => unimplemented!("corejs version other than 2 / 3"),
393 };
394
395 if regenerator::is_required(m) {
396 r.insert("regenerator-runtime/runtime.js");
397 }
398
399 r
400 }
401 Some(Mode::Entry) => match self.corejs {
402 Version { major: 2, .. } => {
403 let mut v = corejs2::Entry::new(self.targets.clone(), self.regenerator);
404 m.visit_mut_with(&mut v);
405 v.imports
406 }
407
408 Version { major: 3, .. } => {
409 let mut v =
410 corejs3::Entry::new(self.targets.clone(), self.corejs, !self.regenerator);
411 m.visit_mut_with(&mut v);
412 v.imports
413 }
414
415 _ => unimplemented!("corejs version other than 2 / 3"),
416 },
417 };
418 required
419 .iter()
420 .filter(|s| {
421 !s.starts_with("esnext") || !required.contains(&s.replace("esnext", "es").as_str())
422 })
423 .filter(|s| !self.excludes.contains(&***s))
424 .map(|s| -> Atom {
425 if *s != "regenerator-runtime/runtime.js" {
426 format!("core-js/modules/{}.js", s).into()
427 } else {
428 "regenerator-runtime/runtime.js".to_string().into()
429 }
430 })
431 .chain(self.includes.iter().map(|s| {
432 if s != "regenerator-runtime/runtime.js" {
433 format!("core-js/modules/{}.js", s).into()
434 } else {
435 "regenerator-runtime/runtime.js".to_string().into()
436 }
437 }))
438 .collect::<Vec<_>>()
439 }
440}
441impl VisitMut for Polyfills {
442 fn visit_mut_module(&mut self, m: &mut Module) {
443 let span = m.span;
444 let required = self.collect(m);
445 if cfg!(debug_assertions) {
446 let mut v = required.into_iter().collect::<Vec<_>>();
447 v.sort();
448 prepend_stmts(
449 &mut m.body,
450 v.into_iter().map(|src| {
451 ImportDecl {
452 span,
453 specifiers: Vec::new(),
454 src: Str {
455 span: DUMMY_SP,
456 raw: None,
457 value: src,
458 }
459 .into(),
460 type_only: false,
461 with: None,
462 phase: Default::default(),
463 }
464 .into()
465 }),
466 );
467 } else {
468 prepend_stmts(
469 &mut m.body,
470 required.into_iter().map(|src| {
471 ImportDecl {
472 span,
473 specifiers: Vec::new(),
474 src: Str {
475 span: DUMMY_SP,
476 raw: None,
477 value: src,
478 }
479 .into(),
480 type_only: false,
481 with: None,
482 phase: Default::default(),
483 }
484 .into()
485 }),
486 );
487 }
488
489 m.body.retain(|item| !matches!(item, ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { src, .. })) if src.span == DUMMY_SP && src.value == atom!("")));
490 }
491
492 fn visit_mut_script(&mut self, m: &mut Script) {
493 let span = m.span;
494 let required = self.collect(m);
495 if cfg!(debug_assertions) {
496 let mut v = required.into_iter().collect::<Vec<_>>();
497 v.sort();
498 prepend_stmts(
499 &mut m.body,
500 v.into_iter().map(|src| {
501 ExprStmt {
502 span: DUMMY_SP,
503 expr: CallExpr {
504 span,
505 callee: Ident {
506 ctxt: SyntaxContext::empty().apply_mark(self.unresolved_mark),
507 sym: "require".into(),
508 ..Default::default()
509 }
510 .as_callee(),
511 args: vec![Str {
512 span: DUMMY_SP,
513 value: src,
514 raw: None,
515 }
516 .as_arg()],
517 type_args: None,
518 ..Default::default()
519 }
520 .into(),
521 }
522 .into()
523 }),
524 );
525 } else {
526 prepend_stmts(
527 &mut m.body,
528 required.into_iter().map(|src| {
529 ExprStmt {
530 span: DUMMY_SP,
531 expr: CallExpr {
532 span,
533 callee: Ident {
534 ctxt: SyntaxContext::empty().apply_mark(self.unresolved_mark),
535 sym: "require".into(),
536 ..Default::default()
537 }
538 .as_callee(),
539 args: vec![Str {
540 span: DUMMY_SP,
541 value: src,
542 raw: None,
543 }
544 .as_arg()],
545 ..Default::default()
546 }
547 .into(),
548 }
549 .into()
550 }),
551 );
552 }
553 }
554}
555
556#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
557pub enum Mode {
558 #[serde(rename = "usage")]
559 Usage,
560 #[serde(rename = "entry")]
561 Entry,
562}
563
564#[derive(Debug, Clone, Deserialize, Default)]
565#[serde(rename_all = "camelCase")]
566pub struct Config {
567 #[serde(default)]
568 pub mode: Option<Mode>,
569
570 #[serde(default)]
571 pub debug: bool,
572
573 #[serde(default)]
574 pub dynamic_import: bool,
575
576 #[serde(default)]
577 pub loose: bool,
578
579 #[serde(default)]
584 pub skip: Vec<Atom>,
585
586 #[serde(default)]
587 pub include: Vec<FeatureOrModule>,
588
589 #[serde(default)]
590 pub exclude: Vec<FeatureOrModule>,
591
592 #[serde(default)]
594 pub core_js: Option<Version>,
595
596 #[serde(default)]
597 pub targets: Option<Targets>,
598
599 #[serde(default = "default_path")]
600 pub path: PathBuf,
601
602 #[serde(default)]
603 pub shipped_proposals: bool,
604
605 #[serde(default)]
606 pub force_all_transforms: bool,
607
608 #[serde(default)]
609 pub bugfixes: bool,
610}
611
612fn default_path() -> PathBuf {
613 if cfg!(target_arch = "wasm32") {
614 Default::default()
615 } else {
616 std::env::current_dir().unwrap()
617 }
618}
619
620#[derive(Debug, Clone, Deserialize, FromVariant)]
621#[serde(untagged)]
622pub enum FeatureOrModule {
623 Feature(Feature),
624 CoreJsModule(String),
625}
626
627impl FeatureOrModule {
628 pub fn split(vec: Vec<FeatureOrModule>) -> (Vec<Feature>, FxHashSet<String>) {
629 let mut features: Vec<_> = Default::default();
630 let mut modules: FxHashSet<_> = Default::default();
631
632 for v in vec {
633 match v {
634 FeatureOrModule::Feature(f) => features.push(f),
635 FeatureOrModule::CoreJsModule(m) => {
636 modules.insert(m);
637 }
638 }
639 }
640
641 (features, modules)
642 }
643}