1
1
import { inject , injectable , } from 'inversify' ;
2
2
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers' ;
3
3
4
+ import * as estraverse from 'estraverse' ;
4
5
import * as ESTree from 'estree' ;
5
6
6
7
import { IOptions } from '../../interfaces/options/IOptions' ;
@@ -13,12 +14,19 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
13
14
import { AbstractNodeTransformer } from '../AbstractNodeTransformer' ;
14
15
import { NodeFactory } from '../../node/NodeFactory' ;
15
16
import { NodeGuards } from '../../node/NodeGuards' ;
17
+ import { NodeLiteralUtils } from '../../node/NodeLiteralUtils' ;
18
+ import { NodeUtils } from '../../node/NodeUtils' ;
16
19
17
20
/**
18
21
* Splits strings into parts
19
22
*/
20
23
@injectable ( )
21
24
export class SplitStringTransformer extends AbstractNodeTransformer {
25
+ /**
26
+ * @type {number }
27
+ */
28
+ private static readonly firstPassChunkLength : number = 1000 ;
29
+
22
30
/**
23
31
* @type {NodeTransformer[] }
24
32
*/
@@ -85,52 +93,104 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
85
93
}
86
94
87
95
/**
96
+ * Needs to split string on chunks of length `splitStringsChunkLength` in two pass, because of
97
+ * `Maximum call stack size exceeded` error in `esrecurse` package
98
+ *
88
99
* @param {Literal } literalNode
89
100
* @param {Node } parentNode
90
101
* @returns {Node }
91
102
*/
92
103
public transformNode ( literalNode : ESTree . Literal , parentNode : ESTree . Node ) : ESTree . Node {
93
- if ( typeof literalNode . value !== 'string' ) {
104
+ if ( NodeLiteralUtils . isProhibitedLiteralNode ( literalNode , parentNode ) ) {
94
105
return literalNode ;
95
106
}
96
107
97
- if ( NodeGuards . isPropertyNode ( parentNode ) && ! parentNode . computed && parentNode . key === literalNode ) {
108
+ // pass #1: split string on a large chunks with length of `firstPassChunkLength`
109
+ const firstPassChunksNode : ESTree . Node = this . transformLiteralNodeByChunkLength (
110
+ literalNode ,
111
+ parentNode ,
112
+ SplitStringTransformer . firstPassChunkLength
113
+ ) ;
114
+
115
+ // pass #2: split large chunks on a chunks with length of `splitStringsChunkLength`
116
+ const secondPassChunksNode : ESTree . Node = estraverse . replace ( firstPassChunksNode , {
117
+ /* tslint:disable:no-shadowed-variable */
118
+ enter : ( node : ESTree . Node , parentNode : ESTree . Node | null ) => {
119
+ if ( parentNode && NodeGuards . isLiteralNode ( node ) ) {
120
+ return this . transformLiteralNodeByChunkLength (
121
+ node ,
122
+ parentNode ,
123
+ this . options . splitStringsChunkLength
124
+ ) ;
125
+ }
126
+ }
127
+ } ) ;
128
+
129
+ return secondPassChunksNode ;
130
+ }
131
+
132
+ /**
133
+ * @param {Literal } literalNode
134
+ * @param {Node } parentNode
135
+ * @param {number } chunkLength
136
+ * @returns {Node }
137
+ */
138
+ private transformLiteralNodeByChunkLength (
139
+ literalNode : ESTree . Literal ,
140
+ parentNode : ESTree . Node ,
141
+ chunkLength : number
142
+ ) : ESTree . Node {
143
+ if ( typeof literalNode . value !== 'string' ) {
98
144
return literalNode ;
99
145
}
100
146
101
- if ( this . options . splitStringsChunkLength >= literalNode . value . length ) {
147
+ if ( chunkLength >= literalNode . value . length ) {
102
148
return literalNode ;
103
149
}
104
150
105
151
const stringChunks : string [ ] = SplitStringTransformer . chunkString (
106
152
literalNode . value ,
107
- this . options . splitStringsChunkLength
153
+ chunkLength
108
154
) ;
109
155
110
- return this . transformStringChunksToBinaryExpressionNode ( stringChunks ) ;
156
+ const binaryExpressionNode : ESTree . BinaryExpression =
157
+ this . transformStringChunksToBinaryExpressionNode ( stringChunks ) ;
158
+
159
+ NodeUtils . parentizeAst ( binaryExpressionNode ) ;
160
+ NodeUtils . parentizeNode ( binaryExpressionNode , parentNode ) ;
161
+
162
+ return binaryExpressionNode ;
111
163
}
112
164
113
165
/**
114
166
* @param {string[] } chunks
115
167
* @returns {BinaryExpression }
116
168
*/
117
- private transformStringChunksToBinaryExpressionNode ( chunks : string [ ] ) : ESTree . BinaryExpression | ESTree . Literal {
118
- const lastChunk : string | undefined = chunks . pop ( ) ;
169
+ private transformStringChunksToBinaryExpressionNode ( chunks : string [ ] ) : ESTree . BinaryExpression {
170
+ const firstChunk : string | undefined = chunks . shift ( ) ;
171
+ const secondChunk : string | undefined = chunks . shift ( ) ;
119
172
120
- if ( lastChunk === undefined ) {
121
- throw new Error ( 'Last chunk value should not be empty' ) ;
173
+ if ( ! firstChunk || ! secondChunk ) {
174
+ throw new Error ( 'First and second chunks values should not be empty' ) ;
122
175
}
123
176
124
- const lastChunkLiteralNode : ESTree . Literal = NodeFactory . literalNode ( lastChunk ) ;
125
-
126
- if ( chunks . length === 0 ) {
127
- return lastChunkLiteralNode ;
128
- }
129
-
130
- return NodeFactory . binaryExpressionNode (
177
+ const initialBinaryExpressionNode : ESTree . BinaryExpression = NodeFactory . binaryExpressionNode (
131
178
'+' ,
132
- this . transformStringChunksToBinaryExpressionNode ( chunks ) ,
133
- lastChunkLiteralNode
179
+ NodeFactory . literalNode ( firstChunk ) ,
180
+ NodeFactory . literalNode ( secondChunk )
181
+ ) ;
182
+
183
+ return chunks . reduce < ESTree . BinaryExpression > (
184
+ ( binaryExpressionNode : ESTree . BinaryExpression , chunk : string ) => {
185
+ const chunkLiteralNode : ESTree . Literal = NodeFactory . literalNode ( chunk ) ;
186
+
187
+ return NodeFactory . binaryExpressionNode (
188
+ '+' ,
189
+ binaryExpressionNode ,
190
+ chunkLiteralNode
191
+ ) ;
192
+ } ,
193
+ initialBinaryExpressionNode
134
194
) ;
135
195
}
136
196
}
0 commit comments