@@ -78,68 +78,103 @@ const TransformAsyncMethodsIntoGeneratorMethods = {
78
78
}
79
79
80
80
// Thus far, we've established that value is `myTask = task(...)`.
81
- // Now we need to check if the last argument is an async ArrowFunctionExpress
81
+ // Now we need to check if the last argument is an async ArrowFunctionExpress,
82
+ // possibly wrapped in other modifier functions such as `waitFor()`
82
83
83
- const maybeAsyncArrowPath = path . get (
84
+ // If there are modifier functions applied, this will capture the
85
+ // top-level one
86
+ let rootModifierPath ;
87
+
88
+ let maybeAsyncArrowPath = path . get (
84
89
`value.arguments.${ value . arguments . length - 1 } `
85
90
) ;
86
- if ( ! maybeAsyncArrowPath && ! maybeAsyncArrowPath . node ) {
87
- return ;
88
- }
89
- const maybeAsyncArrow = maybeAsyncArrowPath . node ;
90
- if (
91
- maybeAsyncArrow &&
92
- maybeAsyncArrow . type === 'ArrowFunctionExpression' &&
93
- maybeAsyncArrow . async
94
- ) {
95
- convertFunctionExpressionIntoGenerator (
96
- maybeAsyncArrowPath ,
97
- state ,
98
- factoryFunctionName
99
- ) ;
91
+ while ( maybeAsyncArrowPath && maybeAsyncArrowPath . node ) {
92
+ const maybeAsyncArrow = maybeAsyncArrowPath . node ;
93
+
94
+ if (
95
+ maybeAsyncArrow . type === 'ArrowFunctionExpression' &&
96
+ maybeAsyncArrow . async
97
+ ) {
98
+ // It's an async arrow function, so convert it
99
+ convertFunctionExpressionIntoGenerator (
100
+ maybeAsyncArrowPath ,
101
+ rootModifierPath ,
102
+ state ,
103
+ factoryFunctionName
104
+ ) ;
105
+ break ;
106
+ } else if ( maybeAsyncArrow . type === 'CallExpression' ) {
107
+ // It's a call expression, so save it as the modifier functions root
108
+ // if we don't already have one and then traverse into it
109
+ rootModifierPath = rootModifierPath || maybeAsyncArrowPath ;
110
+ maybeAsyncArrowPath = maybeAsyncArrowPath . get ( 'arguments.0' ) ;
111
+ } else {
112
+ break ;
113
+ }
100
114
}
101
115
}
102
116
} ,
103
117
} ;
104
118
105
119
function convertFunctionExpressionIntoGenerator (
106
- path ,
120
+ taskFnPath ,
121
+ rootModifierPath ,
107
122
state ,
108
123
factoryFunctionName
109
124
) {
110
- if ( path && path . node . async ) {
111
- if ( isArrowFunctionExpression ( path ) ) {
125
+ if ( taskFnPath && taskFnPath . node . async ) {
126
+ if ( isArrowFunctionExpression ( taskFnPath ) ) {
112
127
// At this point we have something that looks like
113
128
//
114
129
// foo = task(this?, {}?, async () => {})
115
130
//
131
+ // or (if there are modifier functions applied)
132
+ //
133
+ // foo = task(this?, {}?, modifier1(modifier2(async () => {})))
134
+ //
116
135
// and we need to convert it to
117
136
//
118
137
// foo = buildTask(contextFn, options | null, taskName, bufferPolicyName?)
119
138
//
120
139
// where conextFn is
121
140
//
122
141
// () => ({ context: this, generator: function * () { ... } })
142
+ //
143
+ // or (if there are modifier functions applied)
144
+ //
145
+ // () => ({ context: this, generator: modifier1(modifier2(function * () { ... } })))
146
+
147
+ // Before we start moving things around, let's save off the task()
148
+ // CallExpression path
149
+ const taskPath = ( rootModifierPath || taskFnPath ) . parentPath ;
123
150
124
151
// Replace the async arrow fn with a generator fn
125
- let asyncArrowFnBody = path . node . body ;
152
+ let asyncArrowFnBody = taskFnPath . node . body ;
126
153
if ( asyncArrowFnBody . type !== 'BlockStatement' ) {
127
154
// Need to convert `async () => expr` with `async () => { return expr }`
128
155
asyncArrowFnBody = blockStatement ( [ returnStatement ( asyncArrowFnBody ) ] ) ;
129
156
}
130
157
131
158
const taskGeneratorFn = functionExpression (
132
- path . node . id ,
133
- path . node . params ,
159
+ taskFnPath . node . id ,
160
+ taskFnPath . node . params ,
134
161
asyncArrowFnBody ,
135
162
true
136
163
) ;
164
+ taskFnPath = taskFnPath . replaceWith ( taskGeneratorFn ) [ 0 ] ;
137
165
138
166
const contextFn = arrowFunctionExpression (
139
167
[ ] ,
140
168
objectExpression ( [
141
169
objectProperty ( identifier ( 'context' ) , thisExpression ( ) ) ,
142
- objectProperty ( identifier ( 'generator' ) , taskGeneratorFn ) ,
170
+ objectProperty (
171
+ identifier ( 'generator' ) ,
172
+ // We've swapped out the task fn for a generator function, possibly
173
+ // inside some modifier functions. Now we want to move that whole
174
+ // tree, including any modifier functions, into this generator
175
+ // property.
176
+ ( rootModifierPath || taskFnPath ) . node
177
+ ) ,
143
178
] )
144
179
) ;
145
180
@@ -152,15 +187,15 @@ function convertFunctionExpressionIntoGenerator(
152
187
) ;
153
188
}
154
189
155
- const originalArgs = path . parentPath . node . arguments ;
190
+ const originalArgs = taskPath . node . arguments ;
156
191
157
192
// task(this, async() => {}) was the original API, but we don't actually
158
193
// need the `this` arg (we determine the `this` context from the contextFn async arrow fn)
159
194
if ( originalArgs [ 0 ] && originalArgs [ 0 ] . type === 'ThisExpression' ) {
160
195
originalArgs . shift ( ) ;
161
196
}
162
197
163
- const taskName = extractTaskNameFromClassProperty ( path ) ;
198
+ const taskName = extractTaskNameFromClassProperty ( taskPath ) ;
164
199
let optionsOrNull ;
165
200
166
201
// remaining args should either be [options, async () => {}] or [async () => {}]
@@ -192,7 +227,7 @@ function convertFunctionExpressionIntoGenerator(
192
227
]
193
228
) ;
194
229
195
- let newPath = path . parentPath . replaceWith ( buildTaskCall ) [ 0 ] ;
230
+ let newPath = taskPath . replaceWith ( buildTaskCall ) [ 0 ] ;
196
231
newPath . traverse ( {
197
232
FunctionExpression ( path ) {
198
233
if ( ! path . node . generator ) {
@@ -224,11 +259,11 @@ const TransformAwaitIntoYield = {
224
259
* in this method we extract the name from the ClassProperty assignment so that we can pass it in
225
260
* to the options hash when constructing the Task.
226
261
*
227
- * @param {babel.NodePath<babel.types.ArrowFunctionExpression > } asyncArrowFnPath
262
+ * @param {babel.NodePath<babel.types.CallExpression > } taskPath
228
263
* @returns {string | null }
229
264
*/
230
- function extractTaskNameFromClassProperty ( asyncArrowFnPath ) {
231
- const maybeClassPropertyPath = asyncArrowFnPath . parentPath . parentPath ;
265
+ function extractTaskNameFromClassProperty ( taskPath ) {
266
+ const maybeClassPropertyPath = taskPath . parentPath ;
232
267
if (
233
268
maybeClassPropertyPath &&
234
269
maybeClassPropertyPath . node . type === 'ClassProperty'
0 commit comments