swc_ecma_preset_env/
lib.rs

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, es2022, es3,
20        regexp::{self, regexp},
21    },
22    Assumptions,
23};
24use swc_ecma_utils::{prepend_stmts, ExprFactory};
25use swc_ecma_visit::{visit_mut_pass, VisitMut, VisitMutWith, VisitWith};
26
27pub use self::transform_data::Feature;
28
29#[macro_use]
30mod util;
31mod corejs2;
32mod corejs3;
33mod node_colon_prefix_strip;
34mod regenerator;
35mod transform_data;
36
37pub trait Caniuse {
38    fn caniuse(&self, feature: Feature) -> bool;
39}
40
41fn transform_internal<C>(
42    unresolved_mark: Mark,
43    comments: Option<C>,
44    assumptions: Assumptions,
45    loose: bool,
46    dynamic_import: bool,
47    debug: bool,
48    caniuse: impl (Fn(Feature) -> bool),
49) -> impl Pass
50where
51    C: Comments + Clone,
52{
53    let pass = noop_pass();
54
55    macro_rules! add {
56        ($prev:expr, $feature:ident, $pass:expr) => {{
57            add!($prev, $feature, $pass, false)
58        }};
59        ($prev:expr, $feature:ident, $pass:expr, $default:expr) => {{
60            let f = transform_data::Feature::$feature;
61
62            let enable = !caniuse(f);
63
64            if debug {
65                println!("{}: {:?}", f.as_str(), enable);
66            }
67            ($prev, Optional::new($pass, enable))
68        }};
69    }
70
71    macro_rules! add_compiler {
72        ($($feature:ident)|*) => {{
73            let mut feature = swc_ecma_compiler::Features::empty();
74            $(
75                {
76                    let f = transform_data::Feature::$feature;
77                    let enable = !caniuse(f);
78
79                    if debug {
80                        println!("{}: {:?}", f.as_str(), enable);
81                    }
82
83                    if enable {
84                        feature |= swc_ecma_compiler::Features::from(f);
85                    }
86                }
87            )*
88            feature
89        }};
90        (| $($feature:ident)|*) => {{
91            add_compiler!($($feature)|*)
92        }};
93        ($prev:expr, $($feature:ident)|*) => {{
94            let feature = add_compiler!($($feature)|*);
95            (
96                $prev,
97                swc_ecma_compiler::Compiler::new(swc_ecma_compiler::Config {
98                    includes: feature,
99                    assumptions,
100                    ..Default::default()
101                }),
102            )
103        }};
104        ($prev:expr, | $($feature:ident)|*) => {{
105            add_compiler!($prev, $($feature)|*)
106        }};
107    }
108
109    let pass = (
110        pass,
111        Optional::new(
112            class_fields_use_set(assumptions.pure_getters),
113            assumptions.set_public_class_fields,
114        ),
115    );
116
117    let pass = {
118        let enable_dot_all_regex = !caniuse(Feature::DotAllRegex);
119        let enable_named_capturing_groups_regex = !caniuse(Feature::NamedCapturingGroupsRegex);
120        let enable_sticky_regex = !caniuse(Feature::StickyRegex);
121        let enable_unicode_property_regex = !caniuse(Feature::UnicodePropertyRegex);
122        let enable_unicode_regex = !caniuse(Feature::UnicodeRegex);
123        let enable_unicode_sets_regex = !caniuse(Feature::UnicodeSetsRegex);
124
125        let enable = enable_dot_all_regex
126            || enable_named_capturing_groups_regex
127            || enable_sticky_regex
128            || enable_unicode_property_regex
129            || enable_unicode_regex;
130
131        (
132            pass,
133            Optional::new(
134                regexp(regexp::Config {
135                    dot_all_regex: enable_dot_all_regex,
136                    // TODO: add Feature:HasIndicesRegex
137                    has_indices: false,
138                    // TODO: add Feature::LookbehindAssertion
139                    lookbehind_assertion: false,
140                    named_capturing_groups_regex: enable_named_capturing_groups_regex,
141                    sticky_regex: enable_sticky_regex,
142                    unicode_property_regex: enable_unicode_property_regex,
143                    unicode_regex: enable_unicode_regex,
144                    unicode_sets_regex: enable_unicode_sets_regex,
145                }),
146                enable,
147            ),
148        )
149    };
150
151    // Proposals
152
153    // ES2022
154    // static block needs to be placed before class property
155    // because it transforms into private static property
156
157    let pass = add_compiler!(pass, ClassStaticBlock);
158    let pass = add!(
159        pass,
160        ClassProperties,
161        es2022::class_properties(
162            es2022::class_properties::Config {
163                private_as_properties: loose || assumptions.private_fields_as_properties,
164                set_public_fields: loose || assumptions.set_public_class_fields,
165                constant_super: loose || assumptions.constant_super,
166                no_document_all: loose || assumptions.no_document_all,
167                pure_getter: loose || assumptions.pure_getters,
168            },
169            unresolved_mark
170        )
171    );
172
173    #[rustfmt::skip]
174    let pass = add_compiler!(
175        pass,
176        /* ES2022 */ | PrivatePropertyInObject
177        /* ES2021 */ | LogicalAssignmentOperators
178        /* ES2020 */ | ExportNamespaceFrom
179    );
180
181    // ES2020
182    let pass = add!(
183        pass,
184        NullishCoalescing,
185        es2020::nullish_coalescing(es2020::nullish_coalescing::Config {
186            no_document_all: loose || assumptions.no_document_all
187        })
188    );
189
190    let pass = add!(
191        pass,
192        OptionalChaining,
193        es2020::optional_chaining(
194            es2020::optional_chaining::Config {
195                no_document_all: loose || assumptions.no_document_all,
196                pure_getter: loose || assumptions.pure_getters
197            },
198            unresolved_mark
199        )
200    );
201
202    // ES2019
203    let pass = add!(pass, OptionalCatchBinding, es2019::optional_catch_binding());
204
205    // ES2018
206    let pass = add!(
207        pass,
208        ObjectRestSpread,
209        es2018::object_rest_spread(es2018::object_rest_spread::Config {
210            no_symbol: loose || assumptions.object_rest_no_symbols,
211            set_property: loose || assumptions.set_spread_properties,
212            pure_getters: loose || assumptions.pure_getters
213        })
214    );
215
216    // ES2017
217    let pass = add!(
218        pass,
219        AsyncToGenerator,
220        es2017::async_to_generator(
221            es2017::async_to_generator::Config {
222                ignore_function_length: loose || assumptions.ignore_function_length,
223            },
224            unresolved_mark
225        )
226    );
227
228    // ES2016
229    let pass = add!(pass, ExponentiationOperator, es2016::exponentiation());
230
231    // ES2015
232    let pass = add!(pass, BlockScopedFunctions, es2015::block_scoped_functions());
233    let pass = add!(
234        pass,
235        TemplateLiterals,
236        es2015::template_literal(es2015::template_literal::Config {
237            ignore_to_primitive: loose || assumptions.ignore_to_primitive_hint,
238            mutable_template: loose || assumptions.mutable_template_object
239        }),
240        true
241    );
242    let pass = add!(
243        pass,
244        Classes,
245        es2015::classes(es2015::classes::Config {
246            constant_super: loose || assumptions.constant_super,
247            no_class_calls: loose || assumptions.no_class_calls,
248            set_class_methods: loose || assumptions.set_class_methods,
249            super_is_callable_constructor: loose || assumptions.super_is_callable_constructor,
250        })
251    );
252    let pass = add!(pass, NewTarget, es2015::new_target(), true);
253    let pass = add!(
254        pass,
255        Spread,
256        es2015::spread(es2015::spread::Config { loose }),
257        true
258    );
259    let pass = add!(pass, ObjectSuper, es2015::object_super());
260    let pass = add!(pass, ShorthandProperties, es2015::shorthand());
261    let pass = add!(pass, FunctionName, es2015::function_name());
262    let pass = add!(
263        pass,
264        ForOf,
265        es2015::for_of(es2015::for_of::Config {
266            loose,
267            assume_array: loose || assumptions.iterable_is_array
268        }),
269        true
270    );
271    let pass = add!(
272        pass,
273        Parameters,
274        es2015::parameters(
275            es2015::parameters::Config {
276                ignore_function_length: loose || assumptions.ignore_function_length
277            },
278            unresolved_mark
279        )
280    );
281    let pass = add!(pass, ArrowFunctions, es2015::arrow(unresolved_mark));
282    let pass = add!(pass, DuplicateKeys, es2015::duplicate_keys());
283    let pass = add!(pass, StickyRegex, es2015::sticky_regex());
284    let pass = add!(pass, TypeOfSymbol, es2015::instance_of());
285    let pass = add!(pass, TypeOfSymbol, es2015::typeof_symbol());
286    let pass = add!(
287        pass,
288        ComputedProperties,
289        es2015::computed_properties(es2015::computed_props::Config { loose }),
290        true
291    );
292    let pass = add!(
293        pass,
294        Destructuring,
295        es2015::destructuring(es2015::destructuring::Config { loose }),
296        true
297    );
298    let pass = add!(
299        pass,
300        BlockScoping,
301        es2015::block_scoping(unresolved_mark),
302        true
303    );
304    let pass = add!(
305        pass,
306        Regenerator,
307        generator(unresolved_mark, comments),
308        true
309    );
310
311    // TODO:
312    //    Literals,
313    //    ObjectSuper,
314    //    DotAllRegex,
315    //    UnicodeRegex,
316    //    AsyncGeneratorFunctions,
317    //    UnicodePropertyRegex,
318    //    JsonStrings,
319    //    NamedCapturingGroupsRegex,
320
321    // ES 3
322    let pass = add!(pass, PropertyLiterals, es3::property_literals());
323    let pass = add!(
324        pass,
325        MemberExpressionLiterals,
326        es3::member_expression_literals()
327    );
328    let pass = add!(pass, ReservedWords, es3::reserved_words(dynamic_import));
329
330    // Bugfixes
331    let pass = add!(pass, BugfixEdgeDefaultParam, bugfixes::edge_default_param());
332    let pass = add!(
333        pass,
334        BugfixAsyncArrowsInClass,
335        bugfixes::async_arrows_in_class(unresolved_mark)
336    );
337    let pass = add!(
338        pass,
339        BugfixTaggedTemplateCaching,
340        bugfixes::template_literal_caching()
341    );
342
343    add!(
344        pass,
345        BugfixSafariIdDestructuringCollisionInFunctionExpression,
346        bugfixes::safari_id_destructuring_collision_in_function_expression()
347    )
348}
349
350pub fn transform_from_env<C>(
351    unresolved_mark: Mark,
352    comments: Option<C>,
353    env_config: EnvConfig,
354    assumptions: Assumptions,
355) -> impl Pass
356where
357    C: Comments + Clone,
358{
359    let pass = Optional::new(
360        node_colon_prefix_strip::strip_node_colon_prefix(unresolved_mark),
361        env_config.feature_config.targets.node.is_some_and(|v| {
362            // Manually copied from https://nodejs.org/api/esm.html#node-imports
363            v < Version {
364                major: 14,
365                minor: 18,
366                patch: 0,
367            } || v.major == 15
368        }),
369    );
370
371    let pass = (
372        pass,
373        transform_internal(
374            unresolved_mark,
375            comments,
376            assumptions,
377            env_config.config.loose,
378            env_config.config.dynamic_import,
379            env_config.config.debug,
380            move |f| env_config.feature_config.caniuse(f),
381        ),
382    );
383
384    if env_config.config.debug {
385        println!("Targets: {:?}", &env_config.core_js_config.targets);
386    }
387
388    (
389        pass,
390        visit_mut_pass(Polyfills {
391            mode: env_config.config.mode,
392            regenerator: false,
393            corejs: env_config.config.core_js.unwrap_or(Version {
394                major: 3,
395                minor: 0,
396                patch: 0,
397            }),
398            shipped_proposals: env_config.config.shipped_proposals,
399            targets: env_config.core_js_config.targets,
400            includes: env_config.core_js_config.included_modules,
401            excludes: env_config.core_js_config.excluded_modules,
402            unresolved_mark,
403        }),
404    )
405}
406
407pub fn transform_from_es_version<C>(
408    unresolved_mark: Mark,
409    comments: Option<C>,
410    es_version: EsVersion,
411    assumptions: Assumptions,
412    loose: bool,
413) -> impl Pass
414where
415    C: Comments + Clone,
416{
417    transform_internal(
418        unresolved_mark,
419        comments,
420        assumptions,
421        loose,
422        true,
423        false,
424        move |f| es_version.caniuse(f),
425    )
426}
427
428#[derive(Debug)]
429struct Polyfills {
430    mode: Option<Mode>,
431    targets: Arc<Versions>,
432    shipped_proposals: bool,
433    corejs: Version,
434    regenerator: bool,
435    includes: FxHashSet<String>,
436    excludes: FxHashSet<String>,
437    unresolved_mark: Mark,
438}
439impl Polyfills {
440    fn collect<T>(&mut self, m: &mut T) -> Vec<Atom>
441    where
442        T: VisitWith<corejs2::UsageVisitor>
443            + VisitWith<corejs3::UsageVisitor>
444            + VisitMutWith<corejs2::Entry>
445            + VisitMutWith<corejs3::Entry>,
446    {
447        let required = match self.mode {
448            None => Default::default(),
449            Some(Mode::Usage) => {
450                let mut r = match self.corejs {
451                    Version { major: 2, .. } => {
452                        let mut v = corejs2::UsageVisitor::new(self.targets.clone());
453                        m.visit_with(&mut v);
454
455                        v.required
456                    }
457                    Version { major: 3, .. } => {
458                        let mut v = corejs3::UsageVisitor::new(
459                            self.targets.clone(),
460                            self.shipped_proposals,
461                            self.corejs,
462                        );
463                        m.visit_with(&mut v);
464                        v.required
465                    }
466
467                    _ => unimplemented!("corejs version other than 2 / 3"),
468                };
469
470                if regenerator::is_required(m) {
471                    r.insert("regenerator-runtime/runtime.js");
472                }
473
474                r
475            }
476            Some(Mode::Entry) => match self.corejs {
477                Version { major: 2, .. } => {
478                    let mut v = corejs2::Entry::new(self.targets.clone(), self.regenerator);
479                    m.visit_mut_with(&mut v);
480                    v.imports
481                }
482
483                Version { major: 3, .. } => {
484                    let mut v =
485                        corejs3::Entry::new(self.targets.clone(), self.corejs, !self.regenerator);
486                    m.visit_mut_with(&mut v);
487                    v.imports
488                }
489
490                _ => unimplemented!("corejs version other than 2 / 3"),
491            },
492        };
493        required
494            .iter()
495            .filter(|s| {
496                !s.starts_with("esnext") || !required.contains(&s.replace("esnext", "es").as_str())
497            })
498            .filter(|s| !self.excludes.contains(&***s))
499            .map(|s| -> Atom {
500                if *s != "regenerator-runtime/runtime.js" {
501                    format!("core-js/modules/{s}.js").into()
502                } else {
503                    "regenerator-runtime/runtime.js".to_string().into()
504                }
505            })
506            .chain(self.includes.iter().map(|s| {
507                if s != "regenerator-runtime/runtime.js" {
508                    format!("core-js/modules/{s}.js").into()
509                } else {
510                    "regenerator-runtime/runtime.js".to_string().into()
511                }
512            }))
513            .collect::<Vec<_>>()
514    }
515}
516impl VisitMut for Polyfills {
517    fn visit_mut_module(&mut self, m: &mut Module) {
518        let span = m.span;
519        let required = self.collect(m);
520        if cfg!(debug_assertions) {
521            let mut v = required.into_iter().collect::<Vec<_>>();
522            v.sort();
523            prepend_stmts(
524                &mut m.body,
525                v.into_iter().map(|src| {
526                    ImportDecl {
527                        span,
528                        specifiers: Vec::new(),
529                        src: Str {
530                            span: DUMMY_SP,
531                            raw: None,
532                            value: src,
533                        }
534                        .into(),
535                        type_only: false,
536                        with: None,
537                        phase: Default::default(),
538                    }
539                    .into()
540                }),
541            );
542        } else {
543            prepend_stmts(
544                &mut m.body,
545                required.into_iter().map(|src| {
546                    ImportDecl {
547                        span,
548                        specifiers: Vec::new(),
549                        src: Str {
550                            span: DUMMY_SP,
551                            raw: None,
552                            value: src,
553                        }
554                        .into(),
555                        type_only: false,
556                        with: None,
557                        phase: Default::default(),
558                    }
559                    .into()
560                }),
561            );
562        }
563
564        m.body.retain(|item| !matches!(item, ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { src, .. })) if src.span == DUMMY_SP && src.value == atom!("")));
565    }
566
567    fn visit_mut_script(&mut self, m: &mut Script) {
568        let span = m.span;
569        let required = self.collect(m);
570        if cfg!(debug_assertions) {
571            let mut v = required.into_iter().collect::<Vec<_>>();
572            v.sort();
573            prepend_stmts(
574                &mut m.body,
575                v.into_iter().map(|src| {
576                    ExprStmt {
577                        span: DUMMY_SP,
578                        expr: CallExpr {
579                            span,
580                            callee: Ident {
581                                ctxt: SyntaxContext::empty().apply_mark(self.unresolved_mark),
582                                sym: atom!("require"),
583                                ..Default::default()
584                            }
585                            .as_callee(),
586                            args: vec![Str {
587                                span: DUMMY_SP,
588                                value: src,
589                                raw: None,
590                            }
591                            .as_arg()],
592                            type_args: None,
593                            ..Default::default()
594                        }
595                        .into(),
596                    }
597                    .into()
598                }),
599            );
600        } else {
601            prepend_stmts(
602                &mut m.body,
603                required.into_iter().map(|src| {
604                    ExprStmt {
605                        span: DUMMY_SP,
606                        expr: CallExpr {
607                            span,
608                            callee: Ident {
609                                ctxt: SyntaxContext::empty().apply_mark(self.unresolved_mark),
610                                sym: atom!("require"),
611                                ..Default::default()
612                            }
613                            .as_callee(),
614                            args: vec![Str {
615                                span: DUMMY_SP,
616                                value: src,
617                                raw: None,
618                            }
619                            .as_arg()],
620                            ..Default::default()
621                        }
622                        .into(),
623                    }
624                    .into()
625                }),
626            );
627        }
628    }
629}
630
631#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
632pub enum Mode {
633    #[serde(rename = "usage")]
634    Usage,
635    #[serde(rename = "entry")]
636    Entry,
637}
638
639#[derive(Debug, Clone, Deserialize, Default)]
640#[serde(rename_all = "camelCase")]
641pub struct Config {
642    #[serde(default)]
643    pub mode: Option<Mode>,
644
645    #[serde(default)]
646    pub debug: bool,
647
648    #[serde(default)]
649    pub dynamic_import: bool,
650
651    #[serde(default)]
652    pub loose: bool,
653
654    /// Skipped es features.
655    ///
656    /// e.g.)
657    ///  - `core-js/modules/foo`
658    #[serde(default)]
659    pub skip: Vec<Atom>,
660
661    #[serde(default)]
662    pub include: Vec<FeatureOrModule>,
663
664    #[serde(default)]
665    pub exclude: Vec<FeatureOrModule>,
666
667    /// The version of the used core js.
668    #[serde(default)]
669    pub core_js: Option<Version>,
670
671    #[serde(default)]
672    pub targets: Option<Targets>,
673
674    #[serde(default)]
675    pub path: Option<PathBuf>,
676
677    #[serde(default)]
678    pub shipped_proposals: bool,
679
680    #[serde(default)]
681    pub force_all_transforms: bool,
682
683    #[serde(default)]
684    pub bugfixes: bool,
685}
686
687#[derive(Debug, Clone, Default)]
688pub struct FeatureConfig {
689    targets: Arc<Versions>,
690    include: Vec<Feature>,
691    exclude: Vec<Feature>,
692    is_any_target: bool,
693    force_all_transforms: bool,
694    bugfixes: bool,
695}
696
697struct CoreJSConfig {
698    targets: Arc<Versions>,
699    included_modules: FxHashSet<String>,
700    excluded_modules: FxHashSet<String>,
701}
702
703pub struct EnvConfig {
704    config: Config,
705    feature_config: Arc<FeatureConfig>,
706    core_js_config: CoreJSConfig,
707}
708
709impl From<Config> for EnvConfig {
710    fn from(mut config: Config) -> Self {
711        let targets = targets_to_versions(config.targets.take(), config.path.take())
712            .expect("failed to parse targets");
713        let is_any_target = targets.is_any_target();
714
715        let (include, included_modules) = FeatureOrModule::split(config.include.clone());
716        let (exclude, excluded_modules) = FeatureOrModule::split(config.exclude.clone());
717
718        let feature_config = FeatureConfig {
719            targets: Arc::clone(&targets),
720            include,
721            exclude,
722            is_any_target,
723            force_all_transforms: config.force_all_transforms,
724            bugfixes: config.bugfixes,
725        };
726        let core_js_config = CoreJSConfig {
727            targets: Arc::clone(&targets),
728            included_modules,
729            excluded_modules,
730        };
731        Self {
732            config,
733            feature_config: Arc::new(feature_config),
734            core_js_config,
735        }
736    }
737}
738
739impl EnvConfig {
740    pub fn get_feature_config(&self) -> Arc<FeatureConfig> {
741        Arc::clone(&self.feature_config)
742    }
743}
744
745impl Caniuse for FeatureConfig {
746    fn caniuse(&self, feature: Feature) -> bool {
747        if self.exclude.contains(&feature) {
748            return true;
749        }
750
751        if self.force_all_transforms || self.is_any_target {
752            return false;
753        }
754
755        if self.include.contains(&feature) {
756            return false;
757        }
758
759        !feature.should_enable(&self.targets, self.bugfixes, false)
760    }
761}
762
763#[derive(Debug, Clone, Deserialize, FromVariant)]
764#[serde(untagged)]
765pub enum FeatureOrModule {
766    Feature(Feature),
767    CoreJsModule(String),
768}
769
770impl FeatureOrModule {
771    pub fn split(vec: Vec<FeatureOrModule>) -> (Vec<Feature>, FxHashSet<String>) {
772        let mut features: Vec<_> = Default::default();
773        let mut modules: FxHashSet<_> = Default::default();
774
775        for v in vec {
776            match v {
777                FeatureOrModule::Feature(f) => features.push(f),
778                FeatureOrModule::CoreJsModule(m) => {
779                    modules.insert(m);
780                }
781            }
782        }
783
784        (features, modules)
785    }
786}
787
788impl Caniuse for EsVersion {
789    fn caniuse(&self, feature: Feature) -> bool {
790        // EsNext supports all features
791        if self == &EsVersion::EsNext {
792            return true;
793        }
794
795        match feature {
796            // ES2022
797            Feature::ClassProperties
798            | Feature::ClassStaticBlock
799            | Feature::PrivatePropertyInObject => *self >= EsVersion::Es2022,
800
801            // ES2021
802            Feature::LogicalAssignmentOperators => *self >= EsVersion::Es2021,
803
804            // ES2020
805            Feature::ExportNamespaceFrom
806            | Feature::NullishCoalescing
807            | Feature::OptionalChaining => *self >= EsVersion::Es2020,
808
809            // ES2019
810            Feature::OptionalCatchBinding => *self >= EsVersion::Es2019,
811
812            // ES2018
813            Feature::ObjectRestSpread
814            | Feature::DotAllRegex
815            | Feature::NamedCapturingGroupsRegex
816            | Feature::UnicodePropertyRegex => *self >= EsVersion::Es2018,
817
818            // ES2017
819            Feature::AsyncToGenerator => *self >= EsVersion::Es2017,
820
821            // ES2016
822            Feature::ExponentiationOperator => *self >= EsVersion::Es2016,
823
824            // ES2015
825            Feature::ArrowFunctions
826            | Feature::BlockScopedFunctions
827            | Feature::BlockScoping
828            | Feature::Classes
829            | Feature::ComputedProperties
830            | Feature::Destructuring
831            | Feature::DuplicateKeys
832            | Feature::ForOf
833            | Feature::FunctionName
834            | Feature::NewTarget
835            | Feature::ObjectSuper
836            | Feature::Parameters
837            | Feature::Regenerator
838            | Feature::ShorthandProperties
839            | Feature::Spread
840            | Feature::StickyRegex
841            | Feature::TemplateLiterals
842            | Feature::TypeOfSymbol
843            | Feature::UnicodeRegex => *self >= EsVersion::Es2015,
844
845            // ES5
846            Feature::PropertyLiterals
847            | Feature::MemberExpressionLiterals
848            | Feature::ReservedWords => *self >= EsVersion::Es5,
849
850            // bugfix not exists in EsVsersion
851            Feature::BugfixAsyncArrowsInClass
852            | Feature::BugfixEdgeDefaultParam
853            | Feature::BugfixTaggedTemplateCaching
854            | Feature::BugfixSafariIdDestructuringCollisionInFunctionExpression
855            | Feature::BugfixTransformEdgeFunctionName
856            | Feature::BugfixTransformSafariBlockShadowing
857            | Feature::BugfixTransformSafariForShadowing
858            | Feature::BugfixTransformV8SpreadParametersInOptionalChaining
859            | Feature::BugfixTransformV8StaticClassFieldsRedefineReadonly
860            | Feature::BugfixTransformFirefoxClassInComputedClassKey
861            | Feature::BugfixTransformSafariClassFieldInitializerScope => true,
862
863            _ => true,
864        }
865    }
866}