Skip to main content

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