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
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 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 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 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 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 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 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 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 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}