@@ -60,40 +60,65 @@ namespace ts.codefix {
60
60
symbol . members . forEach ( member => {
61
61
const memberElement = createClassElement ( member , /*modifiers*/ undefined ) ;
62
62
if ( memberElement ) {
63
- memberElements . push ( memberElement ) ;
63
+ memberElements . push ( ... memberElement ) ;
64
64
}
65
65
} ) ;
66
66
}
67
67
68
68
// all static members are stored in the "exports" array of symbol
69
69
if ( symbol . exports ) {
70
70
symbol . exports . forEach ( member => {
71
- const memberElement = createClassElement ( member , [ createToken ( SyntaxKind . StaticKeyword ) ] ) ;
72
- if ( memberElement ) {
73
- memberElements . push ( memberElement ) ;
71
+ if ( member . name === "prototype" ) {
72
+ const firstDeclaration = member . declarations [ 0 ] ;
73
+ // only one "x.prototype = { ... }" will pass
74
+ if ( member . declarations . length === 1 &&
75
+ isPropertyAccessExpression ( firstDeclaration ) &&
76
+ isBinaryExpression ( firstDeclaration . parent ) &&
77
+ firstDeclaration . parent . operatorToken . kind === SyntaxKind . EqualsToken &&
78
+ isObjectLiteralExpression ( firstDeclaration . parent . right )
79
+ ) {
80
+ const prototypes = firstDeclaration . parent . right ;
81
+ const memberElement = createClassElement ( prototypes . symbol , /** modifiers */ undefined ) ;
82
+ if ( memberElement ) {
83
+ memberElements . push ( ...memberElement ) ;
84
+ }
85
+ }
86
+ }
87
+ else {
88
+ const memberElement = createClassElement ( member , [ createToken ( SyntaxKind . StaticKeyword ) ] ) ;
89
+ if ( memberElement ) {
90
+ memberElements . push ( ...memberElement ) ;
91
+ }
74
92
}
75
93
} ) ;
76
94
}
77
95
78
96
return memberElements ;
79
97
80
- function shouldConvertDeclaration ( _target : PropertyAccessExpression , source : Expression ) {
98
+ function shouldConvertDeclaration ( _target : PropertyAccessExpression | ObjectLiteralExpression , source : Expression ) {
81
99
// Right now the only thing we can convert are function expressions - other values shouldn't get
82
100
// transformed. We can update this once ES public class properties are available.
83
- return isFunctionLike ( source ) ;
101
+ if ( isPropertyAccessExpression ( _target ) ) {
102
+ return isFunctionLike ( source ) ;
103
+ }
104
+ else {
105
+ return every ( _target . properties , x => isPropertyAssignment ( x ) && isFunctionLike ( x . initializer ) && ! ! x . name ) ;
106
+ }
84
107
}
85
108
86
- function createClassElement ( symbol : Symbol , modifiers : Modifier [ ] | undefined ) : ClassElement | undefined {
109
+ function createClassElement ( symbol : Symbol , modifiers : Modifier [ ] | undefined ) : ClassElement [ ] {
87
110
// Right now the only thing we can convert are function expressions, which are marked as methods
88
- if ( ! ( symbol . flags & SymbolFlags . Method ) ) {
89
- return ;
111
+ // or { x: y } type prototype assignments, which are marked as ObjectLiteral
112
+ const members : ClassElement [ ] = [ ] ;
113
+ if ( ! ( symbol . flags & SymbolFlags . Method ) && ! ( symbol . flags & SymbolFlags . ObjectLiteral ) ) {
114
+ return members ;
90
115
}
91
116
92
- const memberDeclaration = symbol . valueDeclaration as PropertyAccessExpression ;
117
+ const memberDeclaration = symbol . valueDeclaration as PropertyAccessExpression | ObjectLiteralExpression ;
93
118
const assignmentBinaryExpression = memberDeclaration . parent as BinaryExpression ;
94
119
95
120
if ( ! shouldConvertDeclaration ( memberDeclaration , assignmentBinaryExpression . right ) ) {
96
- return ;
121
+ return members ;
97
122
}
98
123
99
124
// delete the entire statement if this expression is the sole expression to take care of the semicolon at the end
@@ -102,50 +127,67 @@ namespace ts.codefix {
102
127
changes . delete ( sourceFile , nodeToDelete ) ;
103
128
104
129
if ( ! assignmentBinaryExpression . right ) {
105
- return createProperty ( [ ] , modifiers , symbol . name , /*questionToken*/ undefined ,
106
- /*type*/ undefined , /*initializer*/ undefined ) ;
130
+ members . push ( createProperty ( [ ] , modifiers , symbol . name , /*questionToken*/ undefined ,
131
+ /*type*/ undefined , /*initializer*/ undefined ) ) ;
132
+ return members ;
107
133
}
108
134
109
135
switch ( assignmentBinaryExpression . right . kind ) {
110
- case SyntaxKind . FunctionExpression : {
111
- const functionExpression = assignmentBinaryExpression . right as FunctionExpression ;
112
- const fullModifiers = concatenate ( modifiers , getModifierKindFromSource ( functionExpression , SyntaxKind . AsyncKeyword ) ) ;
113
- const method = createMethod ( /*decorators*/ undefined , fullModifiers , /*asteriskToken*/ undefined , memberDeclaration . name , /*questionToken*/ undefined ,
114
- /*typeParameters*/ undefined , functionExpression . parameters , /*type*/ undefined , functionExpression . body ) ;
115
- copyLeadingComments ( assignmentBinaryExpression , method , sourceFile ) ;
116
- return method ;
117
- }
118
-
119
- case SyntaxKind . ArrowFunction : {
120
- const arrowFunction = assignmentBinaryExpression . right as ArrowFunction ;
121
- const arrowFunctionBody = arrowFunction . body ;
122
- let bodyBlock : Block ;
123
-
124
- // case 1: () => { return [1,2,3] }
125
- if ( arrowFunctionBody . kind === SyntaxKind . Block ) {
126
- bodyBlock = arrowFunctionBody as Block ;
127
- }
128
- // case 2: () => [1,2,3]
129
- else {
130
- bodyBlock = createBlock ( [ createReturn ( arrowFunctionBody ) ] ) ;
131
- }
132
- const fullModifiers = concatenate ( modifiers , getModifierKindFromSource ( arrowFunction , SyntaxKind . AsyncKeyword ) ) ;
133
- const method = createMethod ( /*decorators*/ undefined , fullModifiers , /*asteriskToken*/ undefined , memberDeclaration . name , /*questionToken*/ undefined ,
134
- /*typeParameters*/ undefined , arrowFunction . parameters , /*type*/ undefined , bodyBlock ) ;
135
- copyLeadingComments ( assignmentBinaryExpression , method , sourceFile ) ;
136
- return method ;
136
+ case SyntaxKind . FunctionExpression :
137
+ case SyntaxKind . ArrowFunction :
138
+ return createFunctionLikeExpressionMember ( members , < FunctionExpression | ArrowFunction > assignmentBinaryExpression . right , ( < PropertyAccessExpression > memberDeclaration ) . name ) ;
139
+ case SyntaxKind . ObjectLiteralExpression : {
140
+ return map (
141
+ ( < ObjectLiteralExpression > assignmentBinaryExpression . right ) . properties ,
142
+ property => createFunctionLikeExpressionMember ( members , < FunctionExpression | ArrowFunction > ( < PropertyAssignment > property ) . initializer , property . name ! )
143
+ ) . reduce ( ( x , y ) => x . concat ( y ) ) ;
137
144
}
138
145
139
146
default : {
140
147
// Don't try to declare members in JavaScript files
141
148
if ( isSourceFileJS ( sourceFile ) ) {
142
- return ;
149
+ return members ;
143
150
}
144
- const prop = createProperty ( /*decorators*/ undefined , modifiers , memberDeclaration . name , /*questionToken*/ undefined ,
151
+ const prop = createProperty ( /*decorators*/ undefined , modifiers , ( < PropertyAccessExpression > memberDeclaration ) . name , /*questionToken*/ undefined ,
145
152
/*type*/ undefined , assignmentBinaryExpression . right ) ;
146
153
copyLeadingComments ( assignmentBinaryExpression . parent , prop , sourceFile ) ;
147
- return prop ;
154
+ members . push ( prop ) ;
155
+ return members ;
156
+ }
157
+ }
158
+
159
+ type MethodName = Parameters < typeof createMethod > [ 3 ] ;
160
+
161
+ function createFunctionLikeExpressionMember ( members : readonly ClassElement [ ] , expression : FunctionExpression | ArrowFunction , name : MethodName ) {
162
+ if ( isFunctionExpression ( expression ) ) return createFunctionExpressionMember ( members , expression , name ) ;
163
+ else return createArrowFunctionExpressionMember ( members , expression , name ) ;
164
+ }
165
+
166
+ function createFunctionExpressionMember ( members : readonly ClassElement [ ] , functionExpression : FunctionExpression , name : MethodName ) {
167
+ const fullModifiers = concatenate ( modifiers , getModifierKindFromSource ( functionExpression , SyntaxKind . AsyncKeyword ) ) ;
168
+ const method = createMethod ( /*decorators*/ undefined , fullModifiers , /*asteriskToken*/ undefined , name , /*questionToken*/ undefined ,
169
+ /*typeParameters*/ undefined , functionExpression . parameters , /*type*/ undefined , functionExpression . body ) ;
170
+ copyLeadingComments ( assignmentBinaryExpression , method , sourceFile ) ;
171
+ return members . concat ( method ) ;
172
+ }
173
+
174
+ function createArrowFunctionExpressionMember ( members : readonly ClassElement [ ] , arrowFunction : ArrowFunction , name : MethodName ) {
175
+ const arrowFunctionBody = arrowFunction . body ;
176
+ let bodyBlock : Block ;
177
+
178
+ // case 1: () => { return [1,2,3] }
179
+ if ( arrowFunctionBody . kind === SyntaxKind . Block ) {
180
+ bodyBlock = arrowFunctionBody as Block ;
181
+ }
182
+ // case 2: () => [1,2,3]
183
+ else {
184
+ bodyBlock = createBlock ( [ createReturn ( arrowFunctionBody ) ] ) ;
148
185
}
186
+ const fullModifiers = concatenate ( modifiers , getModifierKindFromSource ( arrowFunction , SyntaxKind . AsyncKeyword ) ) ;
187
+ const method = createMethod ( /*decorators*/ undefined , fullModifiers , /*asteriskToken*/ undefined , name , /*questionToken*/ undefined ,
188
+ /*typeParameters*/ undefined , arrowFunction . parameters , /*type*/ undefined , bodyBlock ) ;
189
+ copyLeadingComments ( assignmentBinaryExpression , method , sourceFile ) ;
190
+ return members . concat ( method ) ;
149
191
}
150
192
}
151
193
}
0 commit comments