@@ -4,7 +4,6 @@ import { TypescriptCreator } from '../../helper/creator';
4
4
import { MockDefiner } from '../../mockDefiner/mockDefiner' ;
5
5
import { ModuleName } from '../../mockDefiner/modules/moduleName' ;
6
6
import { TypescriptHelper } from '../helper/helper' ;
7
- import { TransformerLogger } from '../../logger/transformerLogger' ;
8
7
9
8
export interface MethodSignature {
10
9
parameters ?: ts . ParameterDeclaration [ ] ;
@@ -17,21 +16,56 @@ export function GetMethodDescriptor(propertyName: ts.PropertyName, methodSignatu
17
16
const propertyNameString : string = TypescriptHelper . GetStringPropertyName ( propertyName ) ;
18
17
const propertyNameStringLiteral : ts . StringLiteral = ts . createStringLiteral ( propertyNameString ) ;
19
18
20
- const [ signatureWithMostParameters ] : MethodSignature [ ] = [ ...methodSignatures ] . sort (
21
- (
22
- { parameters : leftParameters = [ ] } : MethodSignature ,
23
- { parameters : rightParameters = [ ] } : MethodSignature ,
24
- ) => rightParameters . length - leftParameters . length ,
19
+ const signatureWithMostParameters : MethodSignature = methodSignatures . reduce (
20
+ ( acc : MethodSignature , signature : MethodSignature ) => {
21
+ const longestParametersLength : number = ( acc . parameters || [ ] ) . length ;
22
+ const parametersLength : number = ( signature . parameters || [ ] ) . length ;
23
+
24
+ return parametersLength < longestParametersLength ? acc : signature ;
25
+ } ,
25
26
) ;
26
27
27
28
const longestParameterList : ts . ParameterDeclaration [ ] = signatureWithMostParameters . parameters || [ ] ;
28
29
29
- const block : ts . Block = ts . createBlock (
30
- [
31
- ResolveSignatureElseBranch ( methodSignatures , longestParameterList ) ,
32
- ] ,
33
- true ,
34
- ) ;
30
+ const declarationVariableMap : Map < ts . ParameterDeclaration , ts . Identifier > = new Map < ts . ParameterDeclaration , ts . Identifier > ( ) ;
31
+
32
+ const declarationVariables : ts . VariableDeclaration [ ] = methodSignatures . reduce (
33
+ ( variables : ts . VariableDeclaration [ ] , { parameters = [ ] } : MethodSignature ) => {
34
+ let i : number = 0 ;
35
+ for ( const parameter of parameters ) {
36
+ if ( declarationVariableMap . has ( parameter ) ) {
37
+ continue ;
38
+ }
39
+
40
+ const declarationType : ts . TypeNode | undefined = parameter . type ;
41
+ if ( declarationType && ts . isTypeReferenceNode ( declarationType ) ) {
42
+ const variableIdentifier : ts . Identifier = ts . createIdentifier ( `__${ i ++ } ` ) ;
43
+
44
+ declarationVariableMap . set ( parameter , variableIdentifier ) ;
45
+
46
+ const declaration : ts . Declaration = TypescriptHelper . GetDeclarationFromNode ( declarationType . typeName ) ;
47
+
48
+ variables . push (
49
+ TypescriptCreator . createVariableDeclaration (
50
+ variableIdentifier ,
51
+ ts . createStringLiteral ( MockDefiner . instance . getDeclarationKeyMap ( declaration ) ) ,
52
+ ) ,
53
+ ) ;
54
+ }
55
+ }
56
+
57
+ return variables ;
58
+ } , [ ] as ts . VariableDeclaration [ ] ) ;
59
+
60
+ const statements : ts . Statement [ ] = [ ] ;
61
+
62
+ if ( declarationVariables . length ) {
63
+ statements . push ( TypescriptCreator . createVariableStatement ( declarationVariables ) ) ;
64
+ }
65
+
66
+ statements . push ( ResolveSignatureElseBranch ( declarationVariableMap , methodSignatures , longestParameterList ) ) ;
67
+
68
+ const block : ts . Block = ts . createBlock ( statements , true ) ;
35
69
36
70
const propertyValueFunction : ts . ArrowFunction = TypescriptCreator . createArrowFunction (
37
71
block ,
@@ -41,7 +75,7 @@ export function GetMethodDescriptor(propertyName: ts.PropertyName, methodSignatu
41
75
return TypescriptCreator . createCall ( providerGetMethod , [ propertyNameStringLiteral , propertyValueFunction ] ) ;
42
76
}
43
77
44
- function CreateTypeEquality ( signatureType : ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
78
+ function CreateTypeEquality ( signatureType : ts . Identifier | ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
45
79
const identifier : ts . Identifier = ts . createIdentifier ( primaryDeclaration . name . getText ( ) ) ;
46
80
47
81
if ( ! signatureType ) {
@@ -59,25 +93,30 @@ function CreateTypeEquality(signatureType: ts.TypeNode | undefined, primaryDecla
59
93
ts . createTypeOf ( identifier ) ,
60
94
signatureType ? ts . createStringLiteral ( signatureType . getText ( ) ) : ts . createVoidZero ( ) ,
61
95
) ;
62
- } else {
63
- // FIXME: Support `instanceof Class`, falls back to Object for now. The fallback causes undefined behavior!
64
- TransformerLogger ( ) . overloadNonLiteralParameterNotSupported ( signatureType . getText ( ) ) ;
65
- return ts . createBinary ( identifier , ts . SyntaxKind . InstanceOfKeyword , ts . createIdentifier ( 'Object' ) ) ;
66
96
}
97
+
98
+ if ( ts . isIdentifier ( signatureType ) ) {
99
+ return ts . createStrictEquality (
100
+ ts . createPropertyAccess ( identifier , '__factory' ) ,
101
+ signatureType ,
102
+ ) ;
103
+ }
104
+
105
+ return ts . createBinary ( identifier , ts . SyntaxKind . InstanceOfKeyword , ts . createIdentifier ( 'Object' ) ) ;
67
106
}
68
107
69
- function CreateUnionTypeOfEquality ( signatureType : ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
70
- const typeNodes : ts . TypeNode [ ] = [ ] ;
108
+ function CreateUnionTypeOfEquality ( signatureType : ts . Identifier | ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
109
+ const typeNodesAndVariableReferences : Array < ts . TypeNode | ts . Identifier > = [ ] ;
71
110
72
111
if ( signatureType ) {
73
- if ( ts . isUnionTypeNode ( signatureType ) ) {
74
- typeNodes . push ( ...signatureType . types ) ;
112
+ if ( ts . isTypeNode ( signatureType ) && ts . isUnionTypeNode ( signatureType ) ) {
113
+ typeNodesAndVariableReferences . push ( ...signatureType . types ) ;
75
114
} else {
76
- typeNodes . push ( signatureType ) ;
115
+ typeNodesAndVariableReferences . push ( signatureType ) ;
77
116
}
78
117
}
79
118
80
- const [ firstType , ...remainingTypes ] : ts . TypeNode [ ] = typeNodes ;
119
+ const [ firstType , ...remainingTypes ] : Array < ts . TypeNode | ts . Identifier > = typeNodesAndVariableReferences ;
81
120
82
121
return remainingTypes . reduce (
83
122
( prevStatement : ts . Expression , typeNode : ts . TypeNode ) =>
@@ -89,22 +128,53 @@ function CreateUnionTypeOfEquality(signatureType: ts.TypeNode | undefined, prima
89
128
) ;
90
129
}
91
130
92
- function ResolveParameterBranch ( declarations : ts . ParameterDeclaration [ ] , allDeclarations : ts . ParameterDeclaration [ ] , returnValue : ts . Expression , elseBranch : ts . Statement ) : ts . Statement {
131
+ function ResolveParameterBranch (
132
+ declarationVariableMap : Map < ts . ParameterDeclaration , ts . Identifier > ,
133
+ declarations : ts . ParameterDeclaration [ ] ,
134
+ allDeclarations : ts . ParameterDeclaration [ ] ,
135
+ returnValue : ts . Expression ,
136
+ elseBranch : ts . Statement ,
137
+ ) : ts . Statement {
93
138
const [ firstDeclaration , ...remainingDeclarations ] : Array < ts . ParameterDeclaration | undefined > = declarations ;
94
139
140
+ const variableReferenceOrType : ( declaration : ts . ParameterDeclaration ) => ts . Identifier | ts . TypeNode | undefined =
141
+ ( declaration : ts . ParameterDeclaration ) => {
142
+ if ( declarationVariableMap . has ( declaration ) ) {
143
+ return declarationVariableMap . get ( declaration ) ;
144
+ } else {
145
+ return declaration . type ;
146
+ }
147
+ } ;
148
+
149
+ // TODO: These conditions quickly grow in size, but it should be possible to
150
+ // squeeze things together and optimize it with something like:
151
+ //
152
+ // const typeOf = function (left, right) { return typeof left === right; }
153
+ // const evaluate = (function(left, right) { return this._ = this._ || typeOf(left, right); }).bind({})
154
+ //
155
+ // if (evaluate(firstArg, 'boolean') && evaluate(secondArg, 'number') && ...) {
156
+ // ...
157
+ // }
158
+ //
159
+ // `this._' acts as a cache, since the control flow may evaluate the same
160
+ // conditions multiple times.
95
161
const condition : ts . Expression = remainingDeclarations . reduce (
96
162
( prevStatement : ts . Expression , declaration : ts . ParameterDeclaration , index : number ) =>
97
163
ts . createLogicalAnd (
98
164
prevStatement ,
99
- CreateUnionTypeOfEquality ( declaration . type , allDeclarations [ index + 1 ] ) ,
165
+ CreateUnionTypeOfEquality ( variableReferenceOrType ( declaration ) , allDeclarations [ index + 1 ] ) ,
100
166
) ,
101
- CreateUnionTypeOfEquality ( firstDeclaration ?. type , allDeclarations [ 0 ] ) ,
167
+ CreateUnionTypeOfEquality ( variableReferenceOrType ( firstDeclaration ) , allDeclarations [ 0 ] ) ,
102
168
) ;
103
169
104
170
return ts . createIf ( condition , ts . createReturn ( returnValue ) , elseBranch ) ;
105
171
}
106
172
107
- export function ResolveSignatureElseBranch ( signatures : MethodSignature [ ] , longestParameterList : ts . ParameterDeclaration [ ] ) : ts . Statement {
173
+ export function ResolveSignatureElseBranch (
174
+ declarationVariableMap : Map < ts . ParameterDeclaration , ts . Identifier > ,
175
+ signatures : MethodSignature [ ] ,
176
+ longestParameterList : ts . ParameterDeclaration [ ] ,
177
+ ) : ts . Statement {
108
178
const transformOverloadsOption : TsAutoMockOverloadOptions = GetTsAutoMockOverloadOptions ( ) ;
109
179
110
180
const [ signature , ...remainingSignatures ] : MethodSignature [ ] = signatures . filter ( ( _ : unknown , notFirst : number ) => transformOverloadsOption || ! notFirst ) ;
@@ -114,10 +184,10 @@ export function ResolveSignatureElseBranch(signatures: MethodSignature[], longes
114
184
return ts . createReturn ( signature . returnValue ) ;
115
185
}
116
186
117
- const elseBranch : ts . Statement = ResolveSignatureElseBranch ( remainingSignatures , longestParameterList ) ;
187
+ const elseBranch : ts . Statement = ResolveSignatureElseBranch ( declarationVariableMap , remainingSignatures , longestParameterList ) ;
118
188
119
189
const currentParameters : ts . ParameterDeclaration [ ] = signature . parameters || [ ] ;
120
- return ResolveParameterBranch ( currentParameters , longestParameterList , signature . returnValue , elseBranch ) ;
190
+ return ResolveParameterBranch ( declarationVariableMap , currentParameters , longestParameterList , signature . returnValue , elseBranch ) ;
121
191
}
122
192
123
193
function CreateProviderGetMethod ( ) : ts . PropertyAccessExpression {
0 commit comments