1
1
import * as t from '@babel/types'
2
2
import { NodePath } from '@babel/traverse'
3
- import restOfBlockStatement from './restOfBlockStatement'
3
+ import replaceWithStatements from './replaceWithStatements'
4
+ import removeRestOfBlockStatement from './removeRestOfBlockStatement'
4
5
5
6
function isLastStatementInBlock ( path : NodePath < any > ) : boolean {
6
7
const { parentPath } = path
@@ -9,81 +10,74 @@ function isLastStatementInBlock(path: NodePath<any>): boolean {
9
10
return ( path as NodePath < any > ) === body [ body . length - 1 ]
10
11
}
11
12
12
- function isInBranch < T extends t . Statement > (
13
- path : NodePath < T > ,
14
- branch : 'consequent' | 'alternate'
15
- ) : boolean {
16
- const { parentPath } = path
17
- if ( parentPath . isIfStatement ( ) )
18
- return ( path as NodePath < any > ) === parentPath . get ( branch )
19
- if ( parentPath . isBlockStatement ( ) ) {
20
- const grandparent = parentPath . parentPath
21
- return grandparent . isIfStatement ( ) && parentPath === grandparent . get ( branch )
13
+ function hasReturn ( path : NodePath < t . Statement > ) : boolean {
14
+ if ( path . isReturnStatement ( ) ) return true
15
+ if ( path . isBlockStatement ( ) ) {
16
+ for ( const child of ( path as NodePath < t . BlockStatement > ) . get ( 'body' ) ) {
17
+ if ( child . isReturnStatement ( ) ) return true
18
+ }
22
19
}
23
20
return false
24
21
}
25
22
26
- const isInConsequent = < T extends t . Statement > ( path : NodePath < T > ) =>
27
- isInBranch ( path , 'consequent' )
28
- const isInAlternate = < T extends t . Statement > ( path : NodePath < T > ) =>
29
- isInBranch ( path , 'alternate' )
30
-
31
- function convertToBlockStatement (
32
- blockOrExpression : NodePath < any >
33
- ) : NodePath < t . BlockStatement > {
34
- if ( blockOrExpression . isBlockStatement ( ) ) return blockOrExpression
35
- return ( blockOrExpression . replaceWith (
36
- t . blockStatement (
37
- blockOrExpression . node == null
38
- ? [ ]
39
- : [
40
- blockOrExpression . isStatement ( )
41
- ? blockOrExpression . node
42
- : t . expressionStatement ( blockOrExpression . node ) ,
43
- ]
23
+ function splitBranches (
24
+ path : NodePath < t . IfStatement >
25
+ ) : {
26
+ returning : NodePath < t . Statement > [ ]
27
+ notReturning : ( NodePath < t . Statement > | NodePath < null > ) [ ]
28
+ } {
29
+ const returning : NodePath < t . Statement > [ ] = [ ]
30
+ const notReturning : ( NodePath < t . Statement > | NodePath < null > ) [ ] = [ ]
31
+ let p : NodePath < t . IfStatement > | NodePath < null > = path
32
+ while ( p . isIfStatement ( ) ) {
33
+ const consequent = ( p as NodePath < t . IfStatement > ) . get ( 'consequent' )
34
+ const alternate : NodePath < any > = ( p as NodePath < t . IfStatement > ) . get (
35
+ 'alternate'
44
36
)
45
- ) as any ) [ 0 ]
46
- }
37
+ ; ( hasReturn ( consequent ) ? returning : notReturning ) . push ( consequent )
38
+ if ( ! alternate . isIfStatement ( ) ) {
39
+ ; ( hasReturn ( alternate ) ? returning : notReturning ) . push ( alternate )
40
+ }
47
41
48
- function addRestToConsequent < T extends t . Statement > ( path : NodePath < T > ) : void {
49
- const ifStatement = path . findParent ( p => p . isIfStatement ( ) )
50
- if ( ! ifStatement ) throw new Error ( 'failed to find parent IfStatement' )
51
- const rest = restOfBlockStatement ( ifStatement )
52
- if ( ! rest . length ) return
53
- const consequent = ( ifStatement as NodePath < t . IfStatement > ) . get ( 'consequent' )
54
- const restNodes = rest . map ( ( path : NodePath < t . Statement > ) => path . node )
55
- convertToBlockStatement ( consequent ) . pushContainer ( 'body' , restNodes )
56
- rest . forEach ( ( path : NodePath < t . Statement > ) => path . remove ( ) )
57
- }
58
-
59
- function addRestToAlternate < T extends t . Statement > ( path : NodePath < T > ) : void {
60
- const ifStatement = path . findParent ( p => p . isIfStatement ( ) )
61
- if ( ! ifStatement ) throw new Error ( 'failed to find parent IfStatement' )
62
- const rest = restOfBlockStatement ( ifStatement )
63
- if ( ! rest . length ) return
64
- let alternate : NodePath < any > = ifStatement
65
- while ( alternate . isIfStatement ( ) ) {
66
- alternate = ( alternate as NodePath < t . IfStatement > ) . get ( 'alternate' )
42
+ p = alternate
67
43
}
68
- const restNodes = rest . map ( ( path : NodePath < t . Statement > ) => path . node )
69
- convertToBlockStatement ( alternate ) . pushContainer ( 'body' , restNodes )
70
- rest . forEach ( ( path : NodePath < t . Statement > ) => path . remove ( ) )
44
+ return { returning, notReturning }
71
45
}
72
46
73
47
export default function convertConditionalReturns (
74
48
parent : NodePath < t . BlockStatement >
75
49
) : boolean {
50
+ let ifDepth = 0
76
51
let isUnwindable = true
52
+ const ifStatements : NodePath < t . IfStatement > [ ] = [ ]
77
53
const returnStatements : NodePath < t . ReturnStatement > [ ] = [ ]
78
54
parent . traverse (
79
55
{
56
+ IfStatement : {
57
+ enter ( path : NodePath < t . IfStatement > ) {
58
+ if ( path . parentPath . isIfStatement ( ) ) return
59
+ ifDepth ++
60
+ const { returning, notReturning } = splitBranches ( path )
61
+ if ( returning . length > 0 ) {
62
+ if ( notReturning . length === 1 ) {
63
+ ifStatements . push ( path )
64
+ } else if ( notReturning . length > 1 ) {
65
+ isUnwindable = false
66
+ path . stop ( )
67
+ return
68
+ }
69
+ }
70
+ } ,
71
+ exit ( path : NodePath < t . IfStatement > ) {
72
+ if ( path . parentPath . isIfStatement ( ) ) return
73
+ ifDepth --
74
+ } ,
75
+ } ,
80
76
ReturnStatement ( path : NodePath < t . ReturnStatement > ) {
81
77
let { parentPath } = path
82
- let ifDepth = 0
83
78
let loopDepth = 0
84
79
while ( parentPath && parentPath !== parent ) {
85
- if ( parentPath . isIfStatement ( ) ) ifDepth ++
86
- else if ( parentPath . isLoop ( ) ) loopDepth ++
80
+ if ( parentPath . isLoop ( ) ) loopDepth ++
87
81
if (
88
82
loopDepth > 1 ||
89
83
( ! isLastStatementInBlock ( parentPath ) &&
@@ -104,11 +98,15 @@ export default function convertConditionalReturns(
104
98
parent . state
105
99
)
106
100
if ( ! isUnwindable ) return false
107
- let returnStatement
108
- while ( ( returnStatement = returnStatements . pop ( ) ) ) {
109
- if ( isInConsequent ( returnStatement ) ) addRestToAlternate ( returnStatement )
110
- else if ( isInAlternate ( returnStatement ) )
111
- addRestToConsequent ( returnStatement )
101
+ let ifStatement
102
+ while ( ( ifStatement = ifStatements . pop ( ) ) ) {
103
+ const {
104
+ notReturning : [ branch ] ,
105
+ } = splitBranches ( ifStatement )
106
+ const rest = removeRestOfBlockStatement ( ifStatement )
107
+ if ( branch . isBlockStatement ( ) )
108
+ ( branch as NodePath < t . BlockStatement > ) . pushContainer ( 'body' , rest )
109
+ else replaceWithStatements ( branch , rest )
112
110
}
113
111
return true
114
112
}
0 commit comments