1
- import stringify = require( 'json-stringify-safe' )
2
1
import { uniqBy } from 'lodash'
2
+ import { Options } from '.'
3
+ import { generateType } from './generator'
3
4
import { AST , T_ANY , T_UNKNOWN } from './types/AST'
4
5
import { log } from './utils'
5
6
6
- export function optimize ( ast : AST , processed = new Set < AST > ( ) ) : AST {
7
- log ( 'cyan' , 'optimizer' , ast , processed . has ( ast ) ? '(FROM CACHE)' : '' )
8
-
7
+ export function optimize ( ast : AST , options : Options , processed = new Set < AST > ( ) ) : AST {
9
8
if ( processed . has ( ast ) ) {
10
9
return ast
11
10
}
@@ -15,41 +14,61 @@ export function optimize(ast: AST, processed = new Set<AST>()): AST {
15
14
switch ( ast . type ) {
16
15
case 'INTERFACE' :
17
16
return Object . assign ( ast , {
18
- params : ast . params . map ( _ => Object . assign ( _ , { ast : optimize ( _ . ast , processed ) } ) )
17
+ params : ast . params . map ( _ => Object . assign ( _ , { ast : optimize ( _ . ast , options , processed ) } ) )
19
18
} )
20
19
case 'INTERSECTION' :
21
20
case 'UNION' :
21
+ // Start with the leaves...
22
+ const optimizedAST = Object . assign ( ast , {
23
+ params : ast . params . map ( _ => optimize ( _ , options , processed ) )
24
+ } )
25
+
22
26
// [A, B, C, Any] -> Any
23
- if ( ast . params . some ( _ => _ . type === 'ANY' ) ) {
24
- log ( 'cyan' , 'optimizer' , '[A, B, C, Any] -> Any' , ast )
27
+ if ( optimizedAST . params . some ( _ => _ . type === 'ANY' ) ) {
28
+ log ( 'cyan' , 'optimizer' , '[A, B, C, Any] -> Any' , optimizedAST )
25
29
return T_ANY
26
30
}
27
31
28
32
// [A, B, C, Unknown] -> Unknown
29
- if ( ast . params . some ( _ => _ . type === 'UNKNOWN' ) ) {
30
- log ( 'cyan' , 'optimizer' , '[A, B, C, Unknown] -> Unknown' , ast )
33
+ if ( optimizedAST . params . some ( _ => _ . type === 'UNKNOWN' ) ) {
34
+ log ( 'cyan' , 'optimizer' , '[A, B, C, Unknown] -> Unknown' , optimizedAST )
31
35
return T_UNKNOWN
32
36
}
33
37
38
+ // [A (named), A] -> [A (named)]
39
+ if (
40
+ optimizedAST . params . every ( _ => {
41
+ const a = generateType ( omitStandaloneName ( _ ) , options )
42
+ const b = generateType ( omitStandaloneName ( optimizedAST . params [ 0 ] ) , options )
43
+ return a === b
44
+ } ) &&
45
+ optimizedAST . params . some ( _ => _ . standaloneName !== undefined )
46
+ ) {
47
+ log ( 'cyan' , 'optimizer' , '[A (named), A] -> [A (named)]' , optimizedAST )
48
+ optimizedAST . params = optimizedAST . params . filter ( _ => _ . standaloneName !== undefined )
49
+ }
50
+
34
51
// [A, B, B] -> [A, B]
35
- const shouldTakeStandaloneNameIntoAccount = ast . params . filter ( _ => _ . standaloneName ) . length > 1
36
- const params = uniqBy (
37
- ast . params ,
38
- _ => `
39
- ${ _ . type } -
40
- ${ shouldTakeStandaloneNameIntoAccount ? _ . standaloneName : '' } -
41
- ${ stringify ( ( _ as any ) . params ) }
42
- `
43
- )
44
- if ( params . length !== ast . params . length ) {
45
- log ( 'cyan' , 'optimizer' , '[A, B, B] -> [A, B]' , ast )
46
- ast . params = params
52
+ const params = uniqBy ( optimizedAST . params , _ => generateType ( _ , options ) )
53
+ if ( params . length !== optimizedAST . params . length ) {
54
+ log ( 'cyan' , 'optimizer' , '[A, B, B] -> [A, B]' , optimizedAST )
55
+ optimizedAST . params = params
47
56
}
48
57
49
- return Object . assign ( ast , {
50
- params : ast . params . map ( _ => optimize ( _ , processed ) )
58
+ return Object . assign ( optimizedAST , {
59
+ params : optimizedAST . params . map ( _ => optimize ( _ , options , processed ) )
51
60
} )
52
61
default :
53
62
return ast
54
63
}
55
64
}
65
+
66
+ // TODO: More clearly disambiguate standalone names vs. aliased names instead.
67
+ function omitStandaloneName < A extends AST > ( ast : A ) : A {
68
+ switch ( ast . type ) {
69
+ case 'ENUM' :
70
+ return ast
71
+ default :
72
+ return { ...ast , standaloneName : undefined }
73
+ }
74
+ }
0 commit comments