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