1
+ use crate :: ctx:: Ctx ;
1
2
use oxc_allocator:: TakeIn ;
2
3
use oxc_ast:: { NONE , ast:: * } ;
4
+ use oxc_compat:: ESFeature ;
3
5
use oxc_ecmascript:: {
4
6
constant_evaluation:: { ConstantEvaluation , ConstantValue } ,
5
7
side_effects:: MayHaveSideEffects ,
6
8
} ;
7
9
use oxc_span:: { ContentEq , GetSpan } ;
8
- use oxc_syntax:: es_target:: ESTarget ;
9
-
10
- use crate :: ctx:: Ctx ;
11
10
12
11
use super :: PeepholeOptimizations ;
13
12
@@ -299,70 +298,73 @@ impl<'a> PeepholeOptimizations {
299
298
}
300
299
301
300
// Try using the "??" or "?." operators
302
- if ctx. options ( ) . target >= ESTarget :: ES2020 {
303
- if let Expression :: BinaryExpression ( test_binary) = & mut expr. test {
304
- if let Some ( is_negate) = match test_binary. operator {
305
- BinaryOperator :: Inequality => Some ( true ) ,
306
- BinaryOperator :: Equality => Some ( false ) ,
307
- _ => None ,
308
- } {
309
- // a == null / a != null / (a = foo) == null / (a = foo) != null
310
- let value_expr_with_id_name = if test_binary. left . is_null ( ) {
311
- if let Some ( id) = Self :: extract_id_or_assign_to_id ( & test_binary. right )
312
- . filter ( |id| !ctx. is_global_reference ( id) )
313
- {
314
- Some ( ( id. name , & mut test_binary. right ) )
315
- } else {
316
- None
317
- }
318
- } else if test_binary. right . is_null ( ) {
319
- if let Some ( id) = Self :: extract_id_or_assign_to_id ( & test_binary. left )
320
- . filter ( |id| !ctx. is_global_reference ( id) )
321
- {
322
- Some ( ( id. name , & mut test_binary. left ) )
323
- } else {
324
- None
325
- }
326
- } else {
327
- None
328
- } ;
329
- if let Some ( ( target_id_name, value_expr) ) = value_expr_with_id_name {
330
- // `a == null ? b : a` -> `a ?? b`
331
- // `a != null ? a : b` -> `a ?? b`
332
- // `(a = foo) == null ? b : a` -> `(a = foo) ?? b`
333
- // `(a = foo) != null ? a : b` -> `(a = foo) ?? b`
334
- let maybe_same_id_expr =
301
+ if ( ctx. supports_feature ( ESFeature :: ES2020NullishCoalescingOperator )
302
+ || ctx. supports_feature ( ESFeature :: ES2020OptionalChaining ) )
303
+ && let Expression :: BinaryExpression ( test_binary) = & mut expr. test
304
+ && let Some ( is_negate) = match test_binary. operator {
305
+ BinaryOperator :: Inequality => Some ( true ) ,
306
+ BinaryOperator :: Equality => Some ( false ) ,
307
+ _ => None ,
308
+ }
309
+ {
310
+ // a == null / a != null / (a = foo) == null / (a = foo) != null
311
+ let value_expr_with_id_name = if test_binary. left . is_null ( ) {
312
+ if let Some ( id) = Self :: extract_id_or_assign_to_id ( & test_binary. right )
313
+ . filter ( |id| !ctx. is_global_reference ( id) )
314
+ {
315
+ Some ( ( id. name , & mut test_binary. right ) )
316
+ } else {
317
+ None
318
+ }
319
+ } else if test_binary. right . is_null ( ) {
320
+ if let Some ( id) = Self :: extract_id_or_assign_to_id ( & test_binary. left )
321
+ . filter ( |id| !ctx. is_global_reference ( id) )
322
+ {
323
+ Some ( ( id. name , & mut test_binary. left ) )
324
+ } else {
325
+ None
326
+ }
327
+ } else {
328
+ None
329
+ } ;
330
+ if let Some ( ( target_id_name, value_expr) ) = value_expr_with_id_name {
331
+ if ctx. supports_feature ( ESFeature :: ES2020NullishCoalescingOperator ) {
332
+ // `a == null ? b : a` -> `a ?? b`
333
+ // `a != null ? a : b` -> `a ?? b`
334
+ // `(a = foo) == null ? b : a` -> `(a = foo) ?? b`
335
+ // `(a = foo) != null ? a : b` -> `(a = foo) ?? b`
336
+ let maybe_same_id_expr =
337
+ if is_negate { & mut expr. consequent } else { & mut expr. alternate } ;
338
+ if maybe_same_id_expr. is_specific_id ( & target_id_name) {
339
+ return Some ( ctx. ast . expression_logical (
340
+ expr. span ,
341
+ value_expr. take_in ( ctx. ast ) ,
342
+ LogicalOperator :: Coalesce ,
343
+ if is_negate {
344
+ expr. alternate . take_in ( ctx. ast )
345
+ } else {
346
+ expr. consequent . take_in ( ctx. ast )
347
+ } ,
348
+ ) ) ;
349
+ }
350
+ }
351
+ if ctx. supports_feature ( ESFeature :: ES2020OptionalChaining ) {
352
+ // "a == null ? undefined : a.b.c[d](e)" => "a?.b.c[d](e)"
353
+ // "a != null ? a.b.c[d](e) : undefined" => "a?.b.c[d](e)"
354
+ // "(a = foo) == null ? undefined : a.b.c[d](e)" => "(a = foo)?.b.c[d](e)"
355
+ // "(a = foo) != null ? a.b.c[d](e) : undefined" => "(a = foo)?.b.c[d](e)"
356
+ let maybe_undefined_expr =
357
+ if is_negate { & expr. alternate } else { & expr. consequent } ;
358
+ if ctx. is_expression_undefined ( maybe_undefined_expr) {
359
+ let expr_to_inject_optional_chaining =
335
360
if is_negate { & mut expr. consequent } else { & mut expr. alternate } ;
336
- if maybe_same_id_expr. is_specific_id ( & target_id_name) {
337
- return Some ( ctx. ast . expression_logical (
338
- expr. span ,
339
- value_expr. take_in ( ctx. ast ) ,
340
- LogicalOperator :: Coalesce ,
341
- if is_negate {
342
- expr. alternate . take_in ( ctx. ast )
343
- } else {
344
- expr. consequent . take_in ( ctx. ast )
345
- } ,
346
- ) ) ;
347
- }
348
-
349
- // "a == null ? undefined : a.b.c[d](e)" => "a?.b.c[d](e)"
350
- // "a != null ? a.b.c[d](e) : undefined" => "a?.b.c[d](e)"
351
- // "(a = foo) == null ? undefined : a.b.c[d](e)" => "(a = foo)?.b.c[d](e)"
352
- // "(a = foo) != null ? a.b.c[d](e) : undefined" => "(a = foo)?.b.c[d](e)"
353
- let maybe_undefined_expr =
354
- if is_negate { & expr. alternate } else { & expr. consequent } ;
355
- if ctx. is_expression_undefined ( maybe_undefined_expr) {
356
- let expr_to_inject_optional_chaining =
357
- if is_negate { & mut expr. consequent } else { & mut expr. alternate } ;
358
- if Self :: inject_optional_chaining_if_matched (
359
- & target_id_name,
360
- value_expr,
361
- expr_to_inject_optional_chaining,
362
- ctx,
363
- ) {
364
- return Some ( expr_to_inject_optional_chaining. take_in ( ctx. ast ) ) ;
365
- }
361
+ if Self :: inject_optional_chaining_if_matched (
362
+ & target_id_name,
363
+ value_expr,
364
+ expr_to_inject_optional_chaining,
365
+ ctx,
366
+ ) {
367
+ return Some ( expr_to_inject_optional_chaining. take_in ( ctx. ast ) ) ;
366
368
}
367
369
}
368
370
}
@@ -590,18 +592,7 @@ impl<'a> PeepholeOptimizations {
590
592
591
593
#[ cfg( test) ]
592
594
mod test {
593
- use oxc_syntax:: es_target:: ESTarget ;
594
-
595
- use crate :: {
596
- CompressOptions ,
597
- tester:: { test, test_options, test_same} ,
598
- } ;
599
-
600
- fn test_es2019 ( source_text : & str , expected : & str ) {
601
- let target = ESTarget :: ES2019 ;
602
- let options = CompressOptions { target, ..CompressOptions :: default ( ) } ;
603
- test_options ( source_text, expected, & options) ;
604
- }
595
+ use crate :: tester:: { test, test_same, test_target} ;
605
596
606
597
#[ test]
607
598
fn test_minimize_expr_condition ( ) {
@@ -663,7 +654,7 @@ mod test {
663
654
test ( "var a; a != null ? a : b" , "var a; a ?? b" ) ;
664
655
test ( "var a; (a = _a) != null ? a : b" , "var a; (a = _a) ?? b" ) ;
665
656
test ( "v = a != null ? a : b" , "v = a == null ? b : a" ) ; // accessing global `a` may have a getter with side effects
666
- test_es2019 ( "var a; v = a != null ? a : b" , "var a; v = a == null ? b : a" ) ;
657
+ test_target ( "var a; v = a != null ? a : b" , "var a; v = a == null ? b : a" , "chrome79 ") ;
667
658
test ( "var a; v = a != null ? a.b.c[d](e) : undefined" , "var a; v = a?.b.c[d](e)" ) ;
668
659
test (
669
660
"var a; v = (a = _a) != null ? a.b.c[d](e) : undefined" ,
@@ -674,9 +665,10 @@ mod test {
674
665
"var a, undefined = 1; v = a != null ? a.b.c[d](e) : undefined" ,
675
666
"var a; v = a == null ? 1 : a.b.c[d](e)" ,
676
667
) ;
677
- test_es2019 (
668
+ test_target (
678
669
"var a; v = a != null ? a.b.c[d](e) : undefined" ,
679
670
"var a; v = a == null ? void 0 : a.b.c[d](e)" ,
671
+ "chrome79" ,
680
672
) ;
681
673
test ( "v = cmp !== 0 ? cmp : (bar, cmp);" , "v = (cmp === 0 && bar, cmp);" ) ;
682
674
test ( "v = cmp === 0 ? cmp : (bar, cmp);" , "v = (cmp === 0 || bar, cmp);" ) ;
0 commit comments