Skip to content

Commit ae942cd

Browse files
authored
fix(compiler-sfc): support nested await statements (#4458)
fix #4448
1 parent 524688b commit ae942cd

File tree

3 files changed

+261
-53
lines changed

3 files changed

+261
-53
lines changed

packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap

+227
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,233 @@ export default /*#__PURE__*/ Object.assign(__default__, {
9696
})"
9797
`;
9898
99+
exports[`SFC compile <script setup> async/await detection expression statement 1`] = `
100+
"import { withAsyncContext as _withAsyncContext } from 'vue'
101+
102+
export default {
103+
async setup(__props, { expose }) {
104+
expose()
105+
106+
let __temp, __restore
107+
;(
108+
([__temp,__restore] = _withAsyncContext(() => {
109+
return foo
110+
})),
111+
__temp = await __temp,
112+
__restore()
113+
)
114+
return { }
115+
}
116+
117+
}"
118+
`;
119+
120+
exports[`SFC compile <script setup> async/await detection nested await 1`] = `
121+
"import { withAsyncContext as _withAsyncContext } from 'vue'
122+
123+
export default {
124+
async setup(__props, { expose }) {
125+
expose()
126+
127+
let __temp, __restore
128+
;(
129+
([__temp,__restore] = _withAsyncContext(async () => {
130+
return ((
131+
([__temp,__restore] = _withAsyncContext(() => {
132+
return foo
133+
})),
134+
__temp = await __temp,
135+
__restore(),
136+
__temp
137+
))
138+
})),
139+
__temp = await __temp,
140+
__restore()
141+
)
142+
return { }
143+
}
144+
145+
}"
146+
`;
147+
148+
exports[`SFC compile <script setup> async/await detection nested await 2`] = `
149+
"import { withAsyncContext as _withAsyncContext } from 'vue'
150+
151+
export default {
152+
async setup(__props, { expose }) {
153+
expose()
154+
155+
let __temp, __restore
156+
;(
157+
([__temp,__restore] = _withAsyncContext(async () => {
158+
return (((
159+
([__temp,__restore] = _withAsyncContext(() => {
160+
return foo
161+
})),
162+
__temp = await __temp,
163+
__restore(),
164+
__temp
165+
)))
166+
})),
167+
__temp = await __temp,
168+
__restore()
169+
)
170+
return { }
171+
}
172+
173+
}"
174+
`;
175+
176+
exports[`SFC compile <script setup> async/await detection nested await 3`] = `
177+
"import { withAsyncContext as _withAsyncContext } from 'vue'
178+
179+
export default {
180+
async setup(__props, { expose }) {
181+
expose()
182+
183+
let __temp, __restore
184+
;(
185+
([__temp,__restore] = _withAsyncContext(async () => {
186+
return ((
187+
([__temp,__restore] = _withAsyncContext(async () => {
188+
return ((
189+
([__temp,__restore] = _withAsyncContext(() => {
190+
return foo
191+
})),
192+
__temp = await __temp,
193+
__restore(),
194+
__temp
195+
))
196+
})),
197+
__temp = await __temp,
198+
__restore(),
199+
__temp
200+
))
201+
})),
202+
__temp = await __temp,
203+
__restore()
204+
)
205+
return { }
206+
}
207+
208+
}"
209+
`;
210+
211+
exports[`SFC compile <script setup> async/await detection nested statements 1`] = `
212+
"import { withAsyncContext as _withAsyncContext } from 'vue'
213+
214+
export default {
215+
async setup(__props, { expose }) {
216+
expose()
217+
218+
let __temp, __restore
219+
if (ok) { ;(
220+
([__temp,__restore] = _withAsyncContext(() => {
221+
return foo
222+
})),
223+
__temp = await __temp,
224+
__restore()
225+
) } else { ;(
226+
([__temp,__restore] = _withAsyncContext(() => {
227+
return bar
228+
})),
229+
__temp = await __temp,
230+
__restore()
231+
) }
232+
return { }
233+
}
234+
235+
}"
236+
`;
237+
238+
exports[`SFC compile <script setup> async/await detection ref 1`] = `
239+
"import { withAsyncContext as _withAsyncContext, ref as _ref } from 'vue'
240+
241+
export default {
242+
async setup(__props, { expose }) {
243+
expose()
244+
245+
let __temp, __restore
246+
let a = _ref(1 + ((
247+
([__temp,__restore] = _withAsyncContext(() => {
248+
return foo
249+
})),
250+
__temp = await __temp,
251+
__restore(),
252+
__temp
253+
)))
254+
return { a }
255+
}
256+
257+
}"
258+
`;
259+
260+
exports[`SFC compile <script setup> async/await detection should ignore await inside functions 1`] = `
261+
"export default {
262+
setup(__props, { expose }) {
263+
expose()
264+
async function foo() { await bar }
265+
return { foo }
266+
}
267+
268+
}"
269+
`;
270+
271+
exports[`SFC compile <script setup> async/await detection should ignore await inside functions 2`] = `
272+
"export default {
273+
setup(__props, { expose }) {
274+
expose()
275+
const foo = async () => { await bar }
276+
return { foo }
277+
}
278+
279+
}"
280+
`;
281+
282+
exports[`SFC compile <script setup> async/await detection should ignore await inside functions 3`] = `
283+
"export default {
284+
setup(__props, { expose }) {
285+
expose()
286+
const obj = { async method() { await bar }}
287+
return { obj }
288+
}
289+
290+
}"
291+
`;
292+
293+
exports[`SFC compile <script setup> async/await detection should ignore await inside functions 4`] = `
294+
"export default {
295+
setup(__props, { expose }) {
296+
expose()
297+
const cls = class Foo { async method() { await bar }}
298+
return { cls }
299+
}
300+
301+
}"
302+
`;
303+
304+
exports[`SFC compile <script setup> async/await detection variable 1`] = `
305+
"import { withAsyncContext as _withAsyncContext } from 'vue'
306+
307+
export default {
308+
async setup(__props, { expose }) {
309+
expose()
310+
311+
let __temp, __restore
312+
const a = 1 + ((
313+
([__temp,__restore] = _withAsyncContext(() => {
314+
return foo
315+
})),
316+
__temp = await __temp,
317+
__restore(),
318+
__temp
319+
))
320+
return { a }
321+
}
322+
323+
}"
324+
`;
325+
99326
exports[`SFC compile <script setup> binding analysis for destructur 1`] = `
100327
"export default {
101328
setup(__props, { expose }) {

packages/compiler-sfc/__tests__/compileScript.spec.ts

+15-48
Original file line numberDiff line numberDiff line change
@@ -1057,82 +1057,49 @@ const emit = defineEmits(['a', 'b'])
10571057
})
10581058

10591059
describe('async/await detection', () => {
1060-
function assertAwaitDetection(
1061-
code: string,
1062-
expected: string | ((content: string) => boolean),
1063-
shouldAsync = true
1064-
) {
1060+
function assertAwaitDetection(code: string, shouldAsync = true) {
10651061
const { content } = compile(`<script setup>${code}</script>`, {
10661062
refSugar: true
10671063
})
10681064
if (shouldAsync) {
10691065
expect(content).toMatch(`let __temp, __restore`)
10701066
}
10711067
expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
1072-
if (typeof expected === 'string') {
1073-
expect(content).toMatch(expected)
1074-
} else {
1075-
expect(expected(content)).toBe(true)
1076-
}
1068+
assertCode(content)
10771069
}
10781070

10791071
test('expression statement', () => {
1080-
assertAwaitDetection(
1081-
`await foo`,
1082-
`;(([__temp,__restore]=_withAsyncContext(()=>(foo))),__temp=await __temp,__restore())`
1083-
)
1072+
assertAwaitDetection(`await foo`)
10841073
})
10851074

10861075
test('variable', () => {
1087-
assertAwaitDetection(
1088-
`const a = 1 + (await foo)`,
1089-
`1 + ((([__temp,__restore]=_withAsyncContext(()=>(foo))),__temp=await __temp,__restore(),__temp))`
1090-
)
1076+
assertAwaitDetection(`const a = 1 + (await foo)`)
10911077
})
10921078

10931079
test('ref', () => {
1094-
assertAwaitDetection(
1095-
`let a = $ref(1 + (await foo))`,
1096-
`1 + ((([__temp,__restore]=_withAsyncContext(()=>(foo))),__temp=await __temp,__restore(),__temp))`
1097-
)
1080+
assertAwaitDetection(`let a = $ref(1 + (await foo))`)
1081+
})
1082+
1083+
test('nested await', () => {
1084+
assertAwaitDetection(`await (await foo)`)
1085+
assertAwaitDetection(`await ((await foo))`)
1086+
assertAwaitDetection(`await (await (await foo))`)
10981087
})
10991088

11001089
test('nested statements', () => {
1101-
assertAwaitDetection(`if (ok) { await foo } else { await bar }`, code => {
1102-
return (
1103-
code.includes(
1104-
`;(([__temp,__restore]=_withAsyncContext(()=>(foo))),__temp=await __temp,__restore())`
1105-
) &&
1106-
code.includes(
1107-
`;(([__temp,__restore]=_withAsyncContext(()=>(bar))),__temp=await __temp,__restore())`
1108-
)
1109-
)
1110-
})
1090+
assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
11111091
})
11121092

11131093
test('should ignore await inside functions', () => {
11141094
// function declaration
1115-
assertAwaitDetection(
1116-
`async function foo() { await bar }`,
1117-
`await bar`,
1118-
false
1119-
)
1095+
assertAwaitDetection(`async function foo() { await bar }`, false)
11201096
// function expression
1121-
assertAwaitDetection(
1122-
`const foo = async () => { await bar }`,
1123-
`await bar`,
1124-
false
1125-
)
1097+
assertAwaitDetection(`const foo = async () => { await bar }`, false)
11261098
// object method
1127-
assertAwaitDetection(
1128-
`const obj = { async method() { await bar }}`,
1129-
`await bar`,
1130-
false
1131-
)
1099+
assertAwaitDetection(`const obj = { async method() { await bar }}`, false)
11321100
// class method
11331101
assertAwaitDetection(
11341102
`const cls = class Foo { async method() { await bar }}`,
1135-
`await bar`,
11361103
false
11371104
)
11381105
})

packages/compiler-sfc/src/compileScript.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -509,19 +509,33 @@ export function compileScript(
509509
/**
510510
* await foo()
511511
* -->
512-
* (([__temp, __restore] = withAsyncContext(() => foo())),__temp=await __temp,__restore(),__temp)
512+
* (([__temp, __restore] = withAsyncContext(async () => foo())),__temp=await __temp,__restore(),__temp)
513513
*/
514514
function processAwait(node: AwaitExpression, isStatement: boolean) {
515+
const argumentStart =
516+
node.argument.extra && node.argument.extra.parenthesized
517+
? (node.argument.extra.parenStart as number)
518+
: node.argument.start!
519+
520+
const argumentStr = source.slice(
521+
argumentStart + startOffset,
522+
node.argument.end! + startOffset
523+
)
524+
525+
const containsNestedAwait = /\bawait\b/.test(argumentStr)
526+
515527
s.overwrite(
516528
node.start! + startOffset,
517-
node.argument.start! + startOffset,
518-
`${isStatement ? `;` : ``}(([__temp,__restore]=${helper(
529+
argumentStart + startOffset,
530+
`${isStatement ? `;` : ``}(\n ([__temp,__restore] = ${helper(
519531
`withAsyncContext`
520-
)}(()=>(`
532+
)}(${containsNestedAwait ? `async ` : ``}() => {\n return `
521533
)
522534
s.appendLeft(
523535
node.end! + startOffset,
524-
`))),__temp=await __temp,__restore()${isStatement ? `` : `,__temp`})`
536+
`\n })),\n __temp = await __temp,\n __restore()${
537+
isStatement ? `` : `,\n __temp`
538+
}\n)`
525539
)
526540
}
527541

0 commit comments

Comments
 (0)