swc_ecma_preset_env/
transform_data.rs

1use once_cell::sync::Lazy;
2use preset_env_base::{
3    version::{should_enable, Version},
4    BrowserData, Versions,
5};
6use rustc_hash::FxHashMap;
7use string_enum::StringEnum;
8
9impl Feature {
10    pub fn should_enable(self, target: &Versions, bugfixes: bool, default: bool) -> bool {
11        let f = if bugfixes {
12            &BUGFIX_FEATURES[&self]
13        } else {
14            if !FEATURES.contains_key(&self) {
15                return false;
16            }
17            &FEATURES[&self]
18        };
19
20        should_enable(target, f, default)
21    }
22}
23
24#[derive(Clone, Copy, PartialEq, Eq, StringEnum, Hash)]
25#[non_exhaustive]
26pub enum Feature {
27    /// `transform-template-literals`
28    TemplateLiterals,
29
30    /// `transform-literals`
31    Literals,
32
33    /// `transform-function-name`
34    FunctionName,
35
36    /// `transform-arrow-functions`
37    ArrowFunctions,
38
39    /// `transform-block-scoped-functions`
40    BlockScopedFunctions,
41
42    /// `transform-classes`
43    Classes,
44
45    /// `transform-object-super`
46    ObjectSuper,
47
48    /// `transform-shorthand-properties`
49    ShorthandProperties,
50
51    /// `transform-duplicate-keys`
52    DuplicateKeys,
53
54    /// `transform-computed-properties`
55    ComputedProperties,
56
57    /// `transform-for-of`
58    ForOf,
59
60    /// `transform-sticky-regex`
61    StickyRegex,
62
63    /// `transform-dotall-regex`
64    DotAllRegex,
65
66    /// `transform-unicode-regex`
67    UnicodeRegex,
68
69    /// `transform-spread`
70    Spread,
71
72    /// `transform-parameters`
73    Parameters,
74
75    /// `transform-destructuring`
76    Destructuring,
77
78    /// `transform-block-scoping`
79    BlockScoping,
80
81    /// `transform-typeof-symbol`
82    TypeOfSymbol,
83
84    /// `transform-new-target`
85    NewTarget,
86
87    /// `transform-regenerator`
88    Regenerator,
89
90    /// `transform-exponentiation-operator`
91    ExponentiationOperator,
92
93    /// `transform-async-to-generator`
94    AsyncToGenerator,
95
96    /// `transform-async-generator-functions`
97    #[string_enum(alias("proposal-async-generator-functions"))]
98    AsyncGeneratorFunctions,
99
100    /// `transform-object-rest-spread`
101    #[string_enum(alias("proposal-object-rest-spread"))]
102    ObjectRestSpread,
103
104    /// `transform-unicode-property-regex`
105    #[string_enum(alias("proposal-unicode-property-regex"))]
106    UnicodePropertyRegex,
107
108    /// `transform-json-strings`
109    #[string_enum(alias("proposal-json-strings"))]
110    JsonStrings,
111
112    /// `transform-optional-catch-binding`
113    #[string_enum(alias("proposal-optional-catch-binding"))]
114    OptionalCatchBinding,
115
116    /// `transform-named-capturing-groups-regex`
117    NamedCapturingGroupsRegex,
118
119    /// `transform-member-expression-literals`
120    MemberExpressionLiterals,
121
122    /// `transform-property-literals`
123    PropertyLiterals,
124
125    /// `transform-reserved-words`
126    ReservedWords,
127
128    /// `transform-export-namespace-from`
129    #[string_enum(alias("proposal-export-namespace-from"))]
130    ExportNamespaceFrom,
131
132    /// `transform-nullish-coalescing-operator`
133    #[string_enum(alias("proposal-nullish-coalescing-operator"))]
134    NullishCoalescing,
135
136    /// `transform-logical-assignment-operators`
137    #[string_enum(alias("proposal-logical-assignment-operators"))]
138    LogicalAssignmentOperators,
139
140    /// `transform-optional-chaining`
141    #[string_enum(alias("proposal-optional-chaining"))]
142    OptionalChaining,
143
144    /// `transform-class-properties`
145    #[string_enum(alias("proposal-class-properties"))]
146    ClassProperties,
147
148    /// `transform-numeric-separator`
149    #[string_enum(alias("proposal-numeric-separator"))]
150    NumericSeparator,
151
152    /// `transform-private-methods`
153    #[string_enum(alias("proposal-private-methods"))]
154    PrivateMethods,
155
156    /// `transform-class-static-block`
157    #[string_enum(alias("proposal-class-static-block"))]
158    ClassStaticBlock,
159
160    /// `transform-private-property-in-object`
161    #[string_enum(alias("proposal-private-property-in-object"))]
162    PrivatePropertyInObject,
163
164    /// `transform-unicode-escapes`
165    UnicodeEscapes,
166
167    /// `transform-unicode-sets-regex`
168    UnicodeSetsRegex,
169
170    /// `transform-duplicate-named-capturing-groups-regex`
171    DuplicateNamedCapturingGroupsRegex, // TODO
172
173    /// `bugfix/transform-async-arrows-in-class`
174    BugfixAsyncArrowsInClass,
175
176    /// `bugfix/transform-edge-default-parameters`
177    BugfixEdgeDefaultParam,
178
179    /// `bugfix/transform-tagged-template-caching`
180    BugfixTaggedTemplateCaching,
181
182    /// `bugfix/transform-safari-id-destructuring-collision-in-function-expression`
183    BugfixSafariIdDestructuringCollisionInFunctionExpression,
184
185    /// `bugfix/transform-edge-function-name`
186    BugfixTransformEdgeFunctionName, // TODO
187
188    /// `bugfix/transform-safari-block-shadowing`
189    BugfixTransformSafariBlockShadowing, // TODO
190
191    /// `bugfix/transform-safari-for-shadowing`
192    BugfixTransformSafariForShadowing, // TODO
193
194    /// `bugfix/transform-v8-spread-parameters-in-optional-chaining`
195    BugfixTransformV8SpreadParametersInOptionalChaining, // TODO
196
197    /// `bugfix/transform-v8-static-class-fields-redefine-readonly`
198    BugfixTransformV8StaticClassFieldsRedefineReadonly, // TODO
199
200    /// `bugfix/transform-firefox-class-in-computed-class-key`
201    BugfixTransformFirefoxClassInComputedClassKey, // TODO
202
203    /// `bugfix/transform-safari-class-field-initializer-scope`
204    BugfixTransformSafariClassFieldInitializerScope, // TODO
205}
206
207// [TODO]: Unify Feature flag
208impl From<Feature> for swc_ecma_compiler::Features {
209    fn from(value: Feature) -> Self {
210        match value {
211            Feature::ClassStaticBlock => Self::STATIC_BLOCKS,
212            Feature::OptionalChaining => Self::OPTIONAL_CHAINING,
213            Feature::PrivatePropertyInObject => Self::PRIVATE_IN_OBJECT,
214            Feature::LogicalAssignmentOperators => Self::LOGICAL_ASSIGNMENTS,
215            Feature::ExportNamespaceFrom => Self::EXPORT_NAMESPACE_FROM,
216            _ => Self::empty(),
217        }
218    }
219}
220
221pub(crate) static FEATURES: Lazy<FxHashMap<Feature, BrowserData<Option<Version>>>> =
222    Lazy::new(|| {
223        let map: FxHashMap<Feature, BrowserData<Option<String>>> =
224            serde_json::from_str(include_str!("../data/@babel/compat-data/data/plugins.json"))
225                .expect("failed to parse json");
226
227        map.into_iter()
228            .map(|(feature, version)| {
229                (
230                    feature,
231                    version.map_value(|version| {
232                        if matches!(version.as_deref(), Some("tp")) {
233                            return None;
234                        }
235
236                        version.map(|v| {
237                            v.parse().unwrap_or_else(|err| {
238                                panic!("failed to parse `{v}` as a version: {err:?}")
239                            })
240                        })
241                    }),
242                )
243            })
244            .collect()
245    });
246
247pub(crate) static BUGFIX_FEATURES: Lazy<FxHashMap<Feature, BrowserData<Option<Version>>>> =
248    Lazy::new(|| {
249        let map: FxHashMap<Feature, BrowserData<Option<String>>> = serde_json::from_str(
250            include_str!("../data/@babel/compat-data/data/plugin-bugfixes.json"),
251        )
252        .expect("failed to parse json");
253
254        FEATURES
255            .clone()
256            .into_iter()
257            .chain(map.into_iter().map(|(feature, version)| {
258                (
259                    feature,
260                    version.map_value(|version| version.map(|v| v.parse().unwrap())),
261                )
262            }))
263            .collect()
264    });
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269
270    #[test]
271    fn arrow() {
272        assert!(Feature::ArrowFunctions.should_enable(
273            &BrowserData {
274                ie: Some("11.0.0".parse().unwrap()),
275                ..Default::default()
276            },
277            false,
278            false
279        ));
280    }
281
282    #[test]
283    fn tpl_lit() {
284        assert!(!Feature::TemplateLiterals.should_enable(
285            &BrowserData {
286                chrome: Some("71.0.0".parse().unwrap()),
287                ..Default::default()
288            },
289            false,
290            true
291        ));
292    }
293
294    #[test]
295    fn tpl_lit_bugfixes() {
296        // Enable template literals pass in Safari 9 without bugfixes option
297        assert!(Feature::TemplateLiterals.should_enable(
298            &BrowserData {
299                safari: Some("9.0.0".parse().unwrap()),
300                ..Default::default()
301            },
302            false,
303            false
304        ));
305
306        assert!(!Feature::BugfixTaggedTemplateCaching.should_enable(
307            &BrowserData {
308                safari: Some("10.0.0".parse().unwrap()),
309                ..Default::default()
310            },
311            false,
312            false
313        ));
314
315        // Don't enable it with the bugfixes option. Bugfix pass enabled instead.
316        assert!(!Feature::TemplateLiterals.should_enable(
317            &BrowserData {
318                safari: Some("9.0.0".parse().unwrap()),
319                ..Default::default()
320            },
321            true,
322            false
323        ));
324
325        assert!(Feature::BugfixTaggedTemplateCaching.should_enable(
326            &BrowserData {
327                safari: Some("9.0.0".parse().unwrap()),
328                ..Default::default()
329            },
330            true,
331            false
332        ));
333
334        assert!(!Feature::BugfixTaggedTemplateCaching.should_enable(
335            &BrowserData {
336                safari: Some("13.0.0".parse().unwrap()),
337                ..Default::default()
338            },
339            true,
340            false
341        ));
342    }
343
344    #[test]
345    fn edge_default_param_bug() {
346        // Enable params pass in Edge 17 without bugfixes option
347        assert!(Feature::Parameters.should_enable(
348            &BrowserData {
349                edge: Some("17.0.0".parse().unwrap()),
350                ..Default::default()
351            },
352            false,
353            false
354        ));
355
356        assert!(!Feature::BugfixEdgeDefaultParam.should_enable(
357            &BrowserData {
358                edge: Some("17.0.0".parse().unwrap()),
359                ..Default::default()
360            },
361            false,
362            false
363        ));
364
365        // Don't enable it with the bugfixes option. Bugfix pass enabled instead.
366        assert!(!Feature::Parameters.should_enable(
367            &BrowserData {
368                edge: Some("17.0.0".parse().unwrap()),
369                ..Default::default()
370            },
371            true,
372            false
373        ));
374
375        assert!(Feature::BugfixEdgeDefaultParam.should_enable(
376            &BrowserData {
377                edge: Some("17.0.0".parse().unwrap()),
378                ..Default::default()
379            },
380            true,
381            false
382        ));
383
384        assert!(!Feature::BugfixEdgeDefaultParam.should_enable(
385            &BrowserData {
386                edge: Some("18.0.0".parse().unwrap()),
387                ..Default::default()
388            },
389            true,
390            false
391        ));
392    }
393
394    #[test]
395    fn async_arrows_in_class_bug() {
396        // Enable async to generator pass in Safari 10.1 without bugfixes option
397        assert!(Feature::AsyncToGenerator.should_enable(
398            &BrowserData {
399                safari: Some("10.1.0".parse().unwrap()),
400                ..Default::default()
401            },
402            false,
403            false
404        ));
405
406        assert!(!Feature::BugfixAsyncArrowsInClass.should_enable(
407            &BrowserData {
408                safari: Some("10.1.0".parse().unwrap()),
409                ..Default::default()
410            },
411            false,
412            false
413        ));
414
415        // Don't enable it with the bugfixes option. Bugfix pass enabled instead.
416        assert!(!Feature::AsyncToGenerator.should_enable(
417            &BrowserData {
418                safari: Some("10.1.0".parse().unwrap()),
419                ..Default::default()
420            },
421            true,
422            false
423        ));
424
425        assert!(Feature::BugfixAsyncArrowsInClass.should_enable(
426            &BrowserData {
427                safari: Some("10.1.0".parse().unwrap()),
428                ..Default::default()
429            },
430            true,
431            false
432        ));
433
434        assert!(!Feature::BugfixAsyncArrowsInClass.should_enable(
435            &BrowserData {
436                safari: Some("11.1.0".parse().unwrap()),
437                ..Default::default()
438            },
439            true,
440            false
441        ));
442    }
443
444    #[test]
445    fn block_scoping() {
446        // Enable block scoping pass in Safari 10 without bugfixes option
447        assert!(Feature::BlockScoping.should_enable(
448            &BrowserData {
449                safari: Some("10.0.0".parse().unwrap()),
450                ..Default::default()
451            },
452            false,
453            false
454        ));
455
456        // Don't enable it with the bugfixes option.
457        assert!(!Feature::BlockScoping.should_enable(
458            &BrowserData {
459                safari: Some("10.0.0".parse().unwrap()),
460                ..Default::default()
461            },
462            true,
463            false
464        ));
465    }
466}