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, #[string_enum(alias("proposal-explicit-resource-management"))]
208 ExplicitResourceManagement,
209
210 RegexpModifiers,
212}
213
214impl 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 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 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 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 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 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 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 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 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}