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
207pub(crate) static FEATURES: Lazy<FxHashMap<Feature, BrowserData<Option<Version>>>> =
208    Lazy::new(|| {
209        let map: FxHashMap<Feature, BrowserData<Option<String>>> =
210            serde_json::from_str(include_str!("../data/@babel/compat-data/data/plugins.json"))
211                .expect("failed to parse json");
212
213        map.into_iter()
214            .map(|(feature, version)| {
215                (
216                    feature,
217                    version.map_value(|version| {
218                        if matches!(version.as_deref(), Some("tp")) {
219                            return None;
220                        }
221
222                        version.map(|v| {
223                            v.parse().unwrap_or_else(|err| {
224                                panic!("failed to parse `{v}` as a version: {err:?}")
225                            })
226                        })
227                    }),
228                )
229            })
230            .collect()
231    });
232
233pub(crate) static BUGFIX_FEATURES: Lazy<FxHashMap<Feature, BrowserData<Option<Version>>>> =
234    Lazy::new(|| {
235        let map: FxHashMap<Feature, BrowserData<Option<String>>> = serde_json::from_str(
236            include_str!("../data/@babel/compat-data/data/plugin-bugfixes.json"),
237        )
238        .expect("failed to parse json");
239
240        FEATURES
241            .clone()
242            .into_iter()
243            .chain(map.into_iter().map(|(feature, version)| {
244                (
245                    feature,
246                    version.map_value(|version| version.map(|v| v.parse().unwrap())),
247                )
248            }))
249            .collect()
250    });
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    #[test]
257    fn arrow() {
258        assert!(Feature::ArrowFunctions.should_enable(
259            &BrowserData {
260                ie: Some("11.0.0".parse().unwrap()),
261                ..Default::default()
262            },
263            false,
264            false
265        ));
266    }
267
268    #[test]
269    fn tpl_lit() {
270        assert!(!Feature::TemplateLiterals.should_enable(
271            &BrowserData {
272                chrome: Some("71.0.0".parse().unwrap()),
273                ..Default::default()
274            },
275            false,
276            true
277        ));
278    }
279
280    #[test]
281    fn tpl_lit_bugfixes() {
282        // Enable template literals pass in Safari 9 without bugfixes option
283        assert!(Feature::TemplateLiterals.should_enable(
284            &BrowserData {
285                safari: Some("9.0.0".parse().unwrap()),
286                ..Default::default()
287            },
288            false,
289            false
290        ));
291
292        assert!(!Feature::BugfixTaggedTemplateCaching.should_enable(
293            &BrowserData {
294                safari: Some("10.0.0".parse().unwrap()),
295                ..Default::default()
296            },
297            false,
298            false
299        ));
300
301        // Don't enable it with the bugfixes option. Bugfix pass enabled instead.
302        assert!(!Feature::TemplateLiterals.should_enable(
303            &BrowserData {
304                safari: Some("9.0.0".parse().unwrap()),
305                ..Default::default()
306            },
307            true,
308            false
309        ));
310
311        assert!(Feature::BugfixTaggedTemplateCaching.should_enable(
312            &BrowserData {
313                safari: Some("9.0.0".parse().unwrap()),
314                ..Default::default()
315            },
316            true,
317            false
318        ));
319
320        assert!(!Feature::BugfixTaggedTemplateCaching.should_enable(
321            &BrowserData {
322                safari: Some("13.0.0".parse().unwrap()),
323                ..Default::default()
324            },
325            true,
326            false
327        ));
328    }
329
330    #[test]
331    fn edge_default_param_bug() {
332        // Enable params pass in Edge 17 without bugfixes option
333        assert!(Feature::Parameters.should_enable(
334            &BrowserData {
335                edge: Some("17.0.0".parse().unwrap()),
336                ..Default::default()
337            },
338            false,
339            false
340        ));
341
342        assert!(!Feature::BugfixEdgeDefaultParam.should_enable(
343            &BrowserData {
344                edge: Some("17.0.0".parse().unwrap()),
345                ..Default::default()
346            },
347            false,
348            false
349        ));
350
351        // Don't enable it with the bugfixes option. Bugfix pass enabled instead.
352        assert!(!Feature::Parameters.should_enable(
353            &BrowserData {
354                edge: Some("17.0.0".parse().unwrap()),
355                ..Default::default()
356            },
357            true,
358            false
359        ));
360
361        assert!(Feature::BugfixEdgeDefaultParam.should_enable(
362            &BrowserData {
363                edge: Some("17.0.0".parse().unwrap()),
364                ..Default::default()
365            },
366            true,
367            false
368        ));
369
370        assert!(!Feature::BugfixEdgeDefaultParam.should_enable(
371            &BrowserData {
372                edge: Some("18.0.0".parse().unwrap()),
373                ..Default::default()
374            },
375            true,
376            false
377        ));
378    }
379
380    #[test]
381    fn async_arrows_in_class_bug() {
382        // Enable async to generator pass in Safari 10.1 without bugfixes option
383        assert!(Feature::AsyncToGenerator.should_enable(
384            &BrowserData {
385                safari: Some("10.1.0".parse().unwrap()),
386                ..Default::default()
387            },
388            false,
389            false
390        ));
391
392        assert!(!Feature::BugfixAsyncArrowsInClass.should_enable(
393            &BrowserData {
394                safari: Some("10.1.0".parse().unwrap()),
395                ..Default::default()
396            },
397            false,
398            false
399        ));
400
401        // Don't enable it with the bugfixes option. Bugfix pass enabled instead.
402        assert!(!Feature::AsyncToGenerator.should_enable(
403            &BrowserData {
404                safari: Some("10.1.0".parse().unwrap()),
405                ..Default::default()
406            },
407            true,
408            false
409        ));
410
411        assert!(Feature::BugfixAsyncArrowsInClass.should_enable(
412            &BrowserData {
413                safari: Some("10.1.0".parse().unwrap()),
414                ..Default::default()
415            },
416            true,
417            false
418        ));
419
420        assert!(!Feature::BugfixAsyncArrowsInClass.should_enable(
421            &BrowserData {
422                safari: Some("11.1.0".parse().unwrap()),
423                ..Default::default()
424            },
425            true,
426            false
427        ));
428    }
429
430    #[test]
431    fn block_scoping() {
432        // Enable block scoping pass in Safari 10 without bugfixes option
433        assert!(Feature::BlockScoping.should_enable(
434            &BrowserData {
435                safari: Some("10.0.0".parse().unwrap()),
436                ..Default::default()
437            },
438            false,
439            false
440        ));
441
442        // Don't enable it with the bugfixes option.
443        assert!(!Feature::BlockScoping.should_enable(
444            &BrowserData {
445                safari: Some("10.0.0".parse().unwrap()),
446                ..Default::default()
447            },
448            true,
449            false
450        ));
451    }
452}