@@ -78,68 +78,108 @@ const TransformAsyncMethodsIntoGeneratorMethods = {
7878 }
7979
8080 // 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 ArrowFunctionExpression,
82+ // possibly wrapped in other modifier functions such as `waitFor()`
8283
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 (
8489 `value.arguments.${ value . arguments . length - 1 } `
8590 ) ;
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+ }
100114 }
101115 }
102116 } ,
103117} ;
104118
105119function convertFunctionExpressionIntoGenerator (
106- path ,
120+ taskFnPath ,
121+ rootModifierPath ,
107122 state ,
108123 factoryFunctionName
109124) {
110- if ( path && path . node . async ) {
111- if ( isArrowFunctionExpression ( path ) ) {
125+ if ( taskFnPath && taskFnPath . node . async ) {
126+ if ( isArrowFunctionExpression ( taskFnPath ) ) {
112127 // At this point we have something that looks like
113128 //
114129 // foo = task(this?, {}?, async () => {})
115130 //
131+ // or (if there are modifier functions applied)
132+ //
133+ // foo = task(this?, {}?, modifier1(modifier2(async () => {})))
134+ //
116135 // and we need to convert it to
117136 //
118137 // foo = buildTask(contextFn, options | null, taskName, bufferPolicyName?)
119138 //
120139 // where conextFn is
121140 //
122141 // () => ({ 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 ;
123150
124- // Replace the async arrow fn with a generator fn
125- let asyncArrowFnBody = path . node . body ;
151+ // Transform the async arrow task function into a generator function
152+ // (we'll do the actual transformation of `await`s into `yield`s below)
153+ let asyncArrowFnBody = taskFnPath . node . body ;
126154 if ( asyncArrowFnBody . type !== 'BlockStatement' ) {
127155 // Need to convert `async () => expr` with `async () => { return expr }`
128156 asyncArrowFnBody = blockStatement ( [ returnStatement ( asyncArrowFnBody ) ] ) ;
129157 }
130158
131159 const taskGeneratorFn = functionExpression (
132- path . node . id ,
133- path . node . params ,
160+ taskFnPath . node . id ,
161+ taskFnPath . node . params ,
134162 asyncArrowFnBody ,
135163 true
136164 ) ;
137165
166+ // Replace the async arrow task function with the generator function
167+ // in-place in the tree (and update `taskFnPath` to point to the new,
168+ // generator, task function)
169+ taskFnPath = taskFnPath . replaceWith ( taskGeneratorFn ) [ 0 ] ;
170+
138171 const contextFn = arrowFunctionExpression (
139172 [ ] ,
140173 objectExpression ( [
141174 objectProperty ( identifier ( 'context' ) , thisExpression ( ) ) ,
142- objectProperty ( identifier ( 'generator' ) , taskGeneratorFn ) ,
175+ objectProperty (
176+ identifier ( 'generator' ) ,
177+ // We've swapped out the task fn for a generator function, possibly
178+ // inside some modifier functions. Now we want to move that whole
179+ // tree, including any modifier functions, into this generator
180+ // property.
181+ ( rootModifierPath || taskFnPath ) . node
182+ ) ,
143183 ] )
144184 ) ;
145185
@@ -152,15 +192,15 @@ function convertFunctionExpressionIntoGenerator(
152192 ) ;
153193 }
154194
155- const originalArgs = path . parentPath . node . arguments ;
195+ const originalArgs = taskPath . node . arguments ;
156196
157197 // task(this, async() => {}) was the original API, but we don't actually
158198 // need the `this` arg (we determine the `this` context from the contextFn async arrow fn)
159199 if ( originalArgs [ 0 ] && originalArgs [ 0 ] . type === 'ThisExpression' ) {
160200 originalArgs . shift ( ) ;
161201 }
162202
163- const taskName = extractTaskNameFromClassProperty ( path ) ;
203+ const taskName = extractTaskNameFromClassProperty ( taskPath ) ;
164204 let optionsOrNull ;
165205
166206 // remaining args should either be [options, async () => {}] or [async () => {}]
@@ -192,7 +232,7 @@ function convertFunctionExpressionIntoGenerator(
192232 ]
193233 ) ;
194234
195- let newPath = path . parentPath . replaceWith ( buildTaskCall ) [ 0 ] ;
235+ let newPath = taskPath . replaceWith ( buildTaskCall ) [ 0 ] ;
196236 newPath . traverse ( {
197237 FunctionExpression ( path ) {
198238 if ( ! path . node . generator ) {
@@ -224,11 +264,11 @@ const TransformAwaitIntoYield = {
224264 * in this method we extract the name from the ClassProperty assignment so that we can pass it in
225265 * to the options hash when constructing the Task.
226266 *
227- * @param {babel.NodePath<babel.types.ArrowFunctionExpression > } asyncArrowFnPath
267+ * @param {babel.NodePath<babel.types.CallExpression > } taskPath
228268 * @returns {string | null }
229269 */
230- function extractTaskNameFromClassProperty ( asyncArrowFnPath ) {
231- const maybeClassPropertyPath = asyncArrowFnPath . parentPath . parentPath ;
270+ function extractTaskNameFromClassProperty ( taskPath ) {
271+ const maybeClassPropertyPath = taskPath . parentPath ;
232272 if (
233273 maybeClassPropertyPath &&
234274 maybeClassPropertyPath . node . type === 'ClassProperty'
0 commit comments