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 }, unresolved_mark),
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!(
286        pass,
287        TypeOfSymbol,
288        es2015::typeof_symbol(es2015::typeof_symbol::Config { loose })
289    );
290    let pass = add!(
291        pass,
292        ComputedProperties,
293        es2015::computed_properties(es2015::computed_props::Config { loose }),
294        true
295    );
296    let pass = add!(
297        pass,
298        Destructuring,
299        es2015::destructuring(es2015::destructuring::Config { loose }),
300        true
301    );
302    let pass = add!(
303        pass,
304        BlockScoping,
305        es2015::block_scoping(unresolved_mark),
306        true
307    );
308    let pass = add!(
309        pass,
310        Regenerator,
311        generator(unresolved_mark, comments),
312        true
313    );
314
315    // TODO:
316    //    Literals,
317    //    ObjectSuper,
318    //    DotAllRegex,
319    //    UnicodeRegex,
320    //    AsyncGeneratorFunctions,
321    //    UnicodePropertyRegex,
322    //    JsonStrings,
323    //    NamedCapturingGroupsRegex,
324
325    // ES 3
326    let pass = add!(pass, PropertyLiterals, es3::property_literals());
327    let pass = add!(
328        pass,
329        MemberExpressionLiterals,
330        es3::member_expression_literals()
331    );
332    let pass = add!(pass, ReservedWords, es3::reserved_words(dynamic_import));
333
334    // Bugfixes
335    let pass = add!(pass, BugfixEdgeDefaultParam, bugfixes::edge_default_param());
336    let pass = add!(
337        pass,
338        BugfixAsyncArrowsInClass,
339        bugfixes::async_arrows_in_class(unresolved_mark)
340    );
341    let pass = add!(
342        pass,
343        BugfixTaggedTemplateCaching,
344        bugfixes::template_literal_caching()
345    );
346
347    add!(
348        pass,
349        BugfixSafariIdDestructuringCollisionInFunctionExpression,
350        bugfixes::safari_id_destructuring_collision_in_function_expression()
351    )
352}
353
354pub fn transform_from_env<C>(
355    unresolved_mark: Mark,
356    comments: Option<C>,
357    env_config: EnvConfig,
358    assumptions: Assumptions,
359) -> impl Pass
360where
361    C: Comments + Clone,
362{
363    let pass = Optional::new(
364        node_colon_prefix_strip::strip_node_colon_prefix(unresolved_mark),
365        env_config.feature_config.targets.node.is_some_and(|v| {
366            // Manually copied from https://nodejs.org/api/esm.html#node-imports
367            v < Version {
368                major: 14,
369                minor: 18,
370                patch: 0,
371            } || v.major == 15
372        }),
373    );
374
375    let pass = (
376        pass,
377        transform_internal(
378            unresolved_mark,
379            comments,
380            assumptions,
381            env_config.config.loose,
382            env_config.config.dynamic_import,
383            env_config.config.debug,
384            move |f| env_config.feature_config.caniuse(f),
385        ),
386    );
387
388    if env_config.config.debug {
389        println!("Targets: {:?}", &env_config.core_js_config.targets);
390    }
391
392    (
393        pass,
394        visit_mut_pass(Polyfills {
395            mode: env_config.config.mode,
396            regenerator: false,
397            corejs: env_config.config.core_js.unwrap_or(Version {
398                major: 3,
399                minor: 0,
400                patch: 0,
401            }),
402            shipped_proposals: env_config.config.shipped_proposals,
403            targets: env_config.core_js_config.targets,
404            includes: env_config.core_js_config.included_modules,
405            excludes: env_config.core_js_config.excluded_modules,
406            unresolved_mark,
407        }),
408    )
409}
410
411pub fn transform_from_es_version<C>(
412    unresolved_mark: Mark,
413    comments: Option<C>,
414    es_version: EsVersion,
415    assumptions: Assumptions,
416    loose: bool,
417) -> impl Pass
418where
419    C: Comments + Clone,
420{
421    transform_internal(
422        unresolved_mark,
423        comments,
424        assumptions,
425        loose,
426        true,
427        false,
428        move |f| es_version.caniuse(f),
429    )
430}
431
432#[derive(Debug)]
433struct Polyfills {
434    mode: Option<Mode>,
435    targets: Arc<Versions>,
436    shipped_proposals: bool,
437    corejs: Version,
438    regenerator: bool,
439    includes: FxHashSet<String>,
440    excludes: FxHashSet<String>,
441    unresolved_mark: Mark,
442}
443impl Polyfills {
444    fn collect<T>(&mut self, m: &mut T) -> Vec<Atom>
445    where
446        T: VisitWith<corejs2::UsageVisitor>
447            + VisitWith<corejs3::UsageVisitor>
448            + VisitMutWith<corejs2::Entry>
449            + VisitMutWith<corejs3::Entry>,
450    {
451        let required = match self.mode {
452            None => Default::default(),
453            Some(Mode::Usage) => {
454                let mut r = match self.corejs {
455                    Version { major: 2, .. } => {
456                        let mut v = corejs2::UsageVisitor::new(self.targets.clone());
457                        m.visit_with(&mut v);
458
459                        v.required
460                    }
461                    Version { major: 3, .. } => {
462                        let mut v = corejs3::UsageVisitor::new(
463                            self.targets.clone(),
464                            self.shipped_proposals,
465                            self.corejs,
466                        );
467                        m.visit_with(&mut v);
468                        v.required
469                    }
470
471                    _ => unimplemented!("corejs version other than 2 / 3"),
472                };
473
474                if regenerator::is_required(m) {
475                    r.insert("regenerator-runtime/runtime.js");
476                }
477
478                r
479            }
480            Some(Mode::Entry) => match self.corejs {
481                Version { major: 2, .. } => {
482                    let mut v = corejs2::Entry::new(self.targets.clone(), self.regenerator);
483                    m.visit_mut_with(&mut v);
484                    v.imports
485                }
486
487                Version { major: 3, .. } => {
488                    let mut v =
489                        corejs3::Entry::new(self.targets.clone(), self.corejs, !self.regenerator);
490                    m.visit_mut_with(&mut v);
491                    v.imports
492                }
493
494                _ => unimplemented!("corejs version other than 2 / 3"),
495            },
496        };
497        required
498            .iter()
499            .filter(|s| {
500                !s.starts_with("esnext") || !required.contains(&s.replace("esnext", "es").as_str())
501            })
502            .filter(|s| !self.excludes.contains(&***s))
503            .map(|s| -> Atom {
504                if *s != "regenerator-runtime/runtime.js" {
505                    format!("core-js/modules/{s}.js").into()
506                } else {
507                    "regenerator-runtime/runtime.js".to_string().into()
508                }
509            })
510            .chain(self.includes.iter().map(|s| {
511                if s != "regenerator-runtime/runtime.js" {
512                    format!("core-js/modules/{s}.js").into()
513                } else {
514                    "regenerator-runtime/runtime.js".to_string().into()
515                }
516            }))
517            .collect::<Vec<_>>()
518    }
519}
520impl VisitMut for Polyfills {
521    fn visit_mut_module(&mut self, m: &mut Module) {
522        let span = m.span;
523        let required = self.collect(m);
524        if cfg!(debug_assertions) {
525            let mut v = required.into_iter().collect::<Vec<_>>();
526            v.sort();
527            prepend_stmts(
528                &mut m.body,
529                v.into_iter().map(|src| {
530                    ImportDecl {
531                        span,
532                        specifiers: Vec::new(),
533                        src: Str {
534                            span: DUMMY_SP,
535                            raw: None,
536                            value: src,
537                        }
538                        .into(),
539                        type_only: false,
540                        with: None,
541                        phase: Default::default(),
542                    }
543                    .into()
544                }),
545            );
546        } else {
547            prepend_stmts(
548                &mut m.body,
549                required.into_iter().map(|src| {
550                    ImportDecl {
551                        span,
552                        specifiers: Vec::new(),
553                        src: Str {
554                            span: DUMMY_SP,
555                            raw: None,
556                            value: src,
557                        }
558                        .into(),
559                        type_only: false,
560                        with: None,
561                        phase: Default::default(),
562                    }
563                    .into()
564                }),
565            );
566        }
567
568        m.body.retain(|item| !matches!(item, ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { src, .. })) if src.span == DUMMY_SP && src.value == atom!("")));
569    }
570
571    fn visit_mut_script(&mut self, m: &mut Script) {
572        let span = m.span;
573        let required = self.collect(m);
574        if cfg!(debug_assertions) {
575            let mut v = required.into_iter().collect::<Vec<_>>();
576            v.sort();
577            prepend_stmts(
578                &mut m.body,
579                v.into_iter().map(|src| {
580                    ExprStmt {
581                        span: DUMMY_SP,
582                        expr: CallExpr {
583                            span,
584                            callee: Ident {
585                                ctxt: SyntaxContext::empty().apply_mark(self.unresolved_mark),
586                                sym: atom!("require"),
587                                ..Default::default()
588                            }
589                            .as_callee(),
590                            args: vec![Str {
591                                span: DUMMY_SP,
592                                value: src,
593                                raw: None,
594                            }
595                            .as_arg()],
596                            type_args: None,
597                            ..Default::default()
598                        }
599                        .into(),
600                    }
601                    .into()
602                }),
603            );
604        } else {
605            prepend_stmts(
606                &mut m.body,
607                required.into_iter().map(|src| {
608                    ExprStmt {
609                        span: DUMMY_SP,
610                        expr: CallExpr {
611                            span,
612                            callee: Ident {
613                                ctxt: SyntaxContext::empty().apply_mark(self.unresolved_mark),
614                                sym: atom!("require"),
615                                ..Default::default()
616                            }
617                            .as_callee(),
618                            args: vec![Str {
619                                span: DUMMY_SP,
620                                value: src,
621                                raw: None,
622                            }
623                            .as_arg()],
624                            ..Default::default()
625                        }
626                        .into(),
627                    }
628                    .into()
629                }),
630            );
631        }
632    }
633}
634
635#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
636pub enum Mode {
637    #[serde(rename = "usage")]
638    Usage,
639    #[serde(rename = "entry")]
640    Entry,
641}
642
643#[derive(Debug, Clone, Deserialize, Default)]
644#[serde(rename_all = "camelCase")]
645pub struct Config {
646    #[serde(default)]
647    pub mode: Option<Mode>,
648
649    #[serde(default)]
650    pub debug: bool,
651
652    #[serde(default)]
653    pub dynamic_import: bool,
654
655    #[serde(default)]
656    pub loose: bool,
657
658    /// Skipped es features.
659    ///
660    /// e.g.)
661    ///  - `core-js/modules/foo`
662    #[serde(default)]
663    pub skip: Vec<Atom>,
664
665    #[serde(default)]
666    pub include: Vec<FeatureOrModule>,
667
668    #[serde(default)]
669    pub exclude: Vec<FeatureOrModule>,
670
671    /// The version of the used core js.
672    #[serde(default)]
673    pub core_js: Option<Version>,
674
675    #[serde(default)]
676    pub targets: Option<Targets>,
677
678    #[serde(default)]
679    pub path: Option<PathBuf>,
680
681    #[serde(default)]
682    pub shipped_proposals: bool,
683
684    #[serde(default)]
685    pub force_all_transforms: bool,
686
687    #[serde(default)]
688    pub bugfixes: bool,
689}
690
691#[derive(Debug, Clone, Default)]
692pub struct FeatureConfig {
693    targets: Arc<Versions>,
694    include: Vec<Feature>,
695    exclude: Vec<Feature>,
696    is_any_target: bool,
697    force_all_transforms: bool,
698    bugfixes: bool,
699}
700
701struct CoreJSConfig {
702    targets: Arc<Versions>,
703    included_modules: FxHashSet<String>,
704    excluded_modules: FxHashSet<String>,
705}
706
707pub struct EnvConfig {
708    config: Config,
709    feature_config: Arc<FeatureConfig>,
710    core_js_config: CoreJSConfig,
711}
712
713impl From<Config> for EnvConfig {
714    fn from(mut config: Config) -> Self {
715        let targets = targets_to_versions(config.targets.take(), config.path.take())
716            .expect("failed to parse targets");
717        let is_any_target = targets.is_any_target();
718
719        let (include, included_modules) = FeatureOrModule::split(config.include.clone());
720        let (exclude, excluded_modules) = FeatureOrModule::split(config.exclude.clone());
721
722        let feature_config = FeatureConfig {
723            targets: Arc::clone(&targets),
724            include,
725            exclude,
726            is_any_target,
727            force_all_transforms: config.force_all_transforms,
728            bugfixes: config.bugfixes,
729        };
730        let core_js_config = CoreJSConfig {
731            targets: Arc::clone(&targets),
732            included_modules,
733            excluded_modules,
734        };
735        Self {
736            config,
737            feature_config: Arc::new(feature_config),
738            core_js_config,
739        }
740    }
741}
742
743impl EnvConfig {
744    pub fn get_feature_config(&self) -> Arc<FeatureConfig> {
745        Arc::clone(&self.feature_config)
746    }
747}
748
749impl Caniuse for FeatureConfig {
750    fn caniuse(&self, feature: Feature) -> bool {
751        if self.exclude.contains(&feature) {
752            return true;
753        }
754
755        if self.force_all_transforms || self.is_any_target {
756            return false;
757        }
758
759        if self.include.contains(&feature) {
760            return false;
761        }
762
763        !feature.should_enable(&self.targets, self.bugfixes, false)
764    }
765}
766
767#[derive(Debug, Clone, Deserialize, FromVariant)]
768#[serde(untagged)]
769pub enum FeatureOrModule {
770    Feature(Feature),
771    CoreJsModule(String),
772}
773
774impl FeatureOrModule {
775    pub fn split(vec: Vec<FeatureOrModule>) -> (Vec<Feature>, FxHashSet<String>) {
776        let mut features: Vec<_> = Default::default();
777        let mut modules: FxHashSet<_> = Default::default();
778
779        for v in vec {
780            match v {
781                FeatureOrModule::Feature(f) => features.push(f),
782                FeatureOrModule::CoreJsModule(m) => {
783                    modules.insert(m);
784                }
785            }
786        }
787
788        (features, modules)
789    }
790}
791
792impl Caniuse for EsVersion {
793    fn caniuse(&self, feature: Feature) -> bool {
794        // EsNext supports all features
795        if self == &EsVersion::EsNext {
796            return true;
797        }
798
799        match feature {
800            // ES2022
801            Feature::ClassProperties
802            | Feature::ClassStaticBlock
803            | Feature::PrivatePropertyInObject => *self >= EsVersion::Es2022,
804
805            // ES2021
806            Feature::LogicalAssignmentOperators => *self >= EsVersion::Es2021,
807
808            // ES2020
809            Feature::ExportNamespaceFrom
810            | Feature::NullishCoalescing
811            | Feature::OptionalChaining => *self >= EsVersion::Es2020,
812
813            // ES2019
814            Feature::OptionalCatchBinding => *self >= EsVersion::Es2019,
815
816            // ES2018
817            Feature::ObjectRestSpread
818            | Feature::DotAllRegex
819            | Feature::NamedCapturingGroupsRegex
820            | Feature::UnicodePropertyRegex => *self >= EsVersion::Es2018,
821
822            // ES2017
823            Feature::AsyncToGenerator => *self >= EsVersion::Es2017,
824
825            // ES2016
826            Feature::ExponentiationOperator => *self >= EsVersion::Es2016,
827
828            // ES2015
829            Feature::ArrowFunctions
830            | Feature::BlockScopedFunctions
831            | Feature::BlockScoping
832            | Feature::Classes
833            | Feature::ComputedProperties
834            | Feature::Destructuring
835            | Feature::DuplicateKeys
836            | Feature::ForOf
837            | Feature::FunctionName
838            | Feature::NewTarget
839            | Feature::ObjectSuper
840            | Feature::Parameters
841            | Feature::Regenerator
842            | Feature::ShorthandProperties
843            | Feature::Spread
844            | Feature::StickyRegex
845            | Feature::TemplateLiterals
846            | Feature::TypeOfSymbol
847            | Feature::UnicodeRegex => *self >= EsVersion::Es2015,
848
849            // ES5
850            Feature::PropertyLiterals
851            | Feature::MemberExpressionLiterals
852            | Feature::ReservedWords => *self >= EsVersion::Es5,
853
854            // bugfix not exists in EsVsersion
855            Feature::BugfixAsyncArrowsInClass
856            | Feature::BugfixEdgeDefaultParam
857            | Feature::BugfixTaggedTemplateCaching
858            | Feature::BugfixSafariIdDestructuringCollisionInFunctionExpression
859            | Feature::BugfixTransformEdgeFunctionName
860            | Feature::BugfixTransformSafariBlockShadowing
861            | Feature::BugfixTransformSafariForShadowing
862            | Feature::BugfixTransformV8SpreadParametersInOptionalChaining
863            | Feature::BugfixTransformV8StaticClassFieldsRedefineReadonly
864            | Feature::BugfixTransformFirefoxClassInComputedClassKey
865            | Feature::BugfixTransformSafariClassFieldInitializerScope => true,
866
867            _ => true,
868        }
869    }
870}