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