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 TemplateLiterals,
29
30 Literals,
32
33 FunctionName,
35
36 ArrowFunctions,
38
39 BlockScopedFunctions,
41
42 Classes,
44
45 ObjectSuper,
47
48 ShorthandProperties,
50
51 DuplicateKeys,
53
54 ComputedProperties,
56
57 ForOf,
59
60 StickyRegex,
62
63 DotAllRegex,
65
66 UnicodeRegex,
68
69 Spread,
71
72 Parameters,
74
75 Destructuring,
77
78 BlockScoping,
80
81 TypeOfSymbol,
83
84 NewTarget,
86
87 Regenerator,
89
90 ExponentiationOperator,
92
93 AsyncToGenerator,
95
96 #[string_enum(alias("proposal-async-generator-functions"))]
98 AsyncGeneratorFunctions,
99
100 #[string_enum(alias("proposal-object-rest-spread"))]
102 ObjectRestSpread,
103
104 #[string_enum(alias("proposal-unicode-property-regex"))]
106 UnicodePropertyRegex,
107
108 #[string_enum(alias("proposal-json-strings"))]
110 JsonStrings,
111
112 #[string_enum(alias("proposal-optional-catch-binding"))]
114 OptionalCatchBinding,
115
116 NamedCapturingGroupsRegex,
118
119 MemberExpressionLiterals,
121
122 PropertyLiterals,
124
125 ReservedWords,
127
128 #[string_enum(alias("proposal-export-namespace-from"))]
130 ExportNamespaceFrom,
131
132 #[string_enum(alias("proposal-nullish-coalescing-operator"))]
134 NullishCoalescing,
135
136 #[string_enum(alias("proposal-logical-assignment-operators"))]
138 LogicalAssignmentOperators,
139
140 #[string_enum(alias("proposal-optional-chaining"))]
142 OptionalChaining,
143
144 #[string_enum(alias("proposal-class-properties"))]
146 ClassProperties,
147
148 #[string_enum(alias("proposal-numeric-separator"))]
150 NumericSeparator,
151
152 #[string_enum(alias("proposal-private-methods"))]
154 PrivateMethods,
155
156 #[string_enum(alias("proposal-class-static-block"))]
158 ClassStaticBlock,
159
160 #[string_enum(alias("proposal-private-property-in-object"))]
162 PrivatePropertyInObject,
163
164 UnicodeEscapes,
166
167 UnicodeSetsRegex,
169
170 DuplicateNamedCapturingGroupsRegex, BugfixAsyncArrowsInClass,
175
176 BugfixEdgeDefaultParam,
178
179 BugfixTaggedTemplateCaching,
181
182 BugfixSafariIdDestructuringCollisionInFunctionExpression,
184
185 BugfixTransformEdgeFunctionName, BugfixTransformSafariBlockShadowing, BugfixTransformSafariForShadowing, BugfixTransformV8SpreadParametersInOptionalChaining, BugfixTransformV8StaticClassFieldsRedefineReadonly, BugfixTransformFirefoxClassInComputedClassKey, BugfixTransformSafariClassFieldInitializerScope, }
206
207impl 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 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 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 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 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 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 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 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 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}