})"
`;
+exports[`SFC compile <script setup> async/await detection expression statement 1`] = `
+"import { withAsyncContext as _withAsyncContext } from 'vue'
+
+export default {
+ async setup(__props, { expose }) {
+ expose()
+
+let __temp, __restore
+;(
+ ([__temp,__restore] = _withAsyncContext(() => {
+ return foo
+ })),
+ __temp = await __temp,
+ __restore()
+)
+return { }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection nested await 1`] = `
+"import { withAsyncContext as _withAsyncContext } from 'vue'
+
+export default {
+ async setup(__props, { expose }) {
+ expose()
+
+let __temp, __restore
+;(
+ ([__temp,__restore] = _withAsyncContext(async () => {
+ return ((
+ ([__temp,__restore] = _withAsyncContext(() => {
+ return foo
+ })),
+ __temp = await __temp,
+ __restore(),
+ __temp
+))
+ })),
+ __temp = await __temp,
+ __restore()
+)
+return { }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection nested await 2`] = `
+"import { withAsyncContext as _withAsyncContext } from 'vue'
+
+export default {
+ async setup(__props, { expose }) {
+ expose()
+
+let __temp, __restore
+;(
+ ([__temp,__restore] = _withAsyncContext(async () => {
+ return (((
+ ([__temp,__restore] = _withAsyncContext(() => {
+ return foo
+ })),
+ __temp = await __temp,
+ __restore(),
+ __temp
+)))
+ })),
+ __temp = await __temp,
+ __restore()
+)
+return { }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection nested await 3`] = `
+"import { withAsyncContext as _withAsyncContext } from 'vue'
+
+export default {
+ async setup(__props, { expose }) {
+ expose()
+
+let __temp, __restore
+;(
+ ([__temp,__restore] = _withAsyncContext(async () => {
+ return ((
+ ([__temp,__restore] = _withAsyncContext(async () => {
+ return ((
+ ([__temp,__restore] = _withAsyncContext(() => {
+ return foo
+ })),
+ __temp = await __temp,
+ __restore(),
+ __temp
+))
+ })),
+ __temp = await __temp,
+ __restore(),
+ __temp
+))
+ })),
+ __temp = await __temp,
+ __restore()
+)
+return { }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection nested statements 1`] = `
+"import { withAsyncContext as _withAsyncContext } from 'vue'
+
+export default {
+ async setup(__props, { expose }) {
+ expose()
+
+let __temp, __restore
+if (ok) { ;(
+ ([__temp,__restore] = _withAsyncContext(() => {
+ return foo
+ })),
+ __temp = await __temp,
+ __restore()
+) } else { ;(
+ ([__temp,__restore] = _withAsyncContext(() => {
+ return bar
+ })),
+ __temp = await __temp,
+ __restore()
+) }
+return { }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection ref 1`] = `
+"import { withAsyncContext as _withAsyncContext, ref as _ref } from 'vue'
+
+export default {
+ async setup(__props, { expose }) {
+ expose()
+
+let __temp, __restore
+let a = _ref(1 + ((
+ ([__temp,__restore] = _withAsyncContext(() => {
+ return foo
+ })),
+ __temp = await __temp,
+ __restore(),
+ __temp
+)))
+return { a }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection should ignore await inside functions 1`] = `
+"export default {
+ setup(__props, { expose }) {
+ expose()
+async function foo() { await bar }
+return { foo }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection should ignore await inside functions 2`] = `
+"export default {
+ setup(__props, { expose }) {
+ expose()
+const foo = async () => { await bar }
+return { foo }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection should ignore await inside functions 3`] = `
+"export default {
+ setup(__props, { expose }) {
+ expose()
+const obj = { async method() { await bar }}
+return { obj }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection should ignore await inside functions 4`] = `
+"export default {
+ setup(__props, { expose }) {
+ expose()
+const cls = class Foo { async method() { await bar }}
+return { cls }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> async/await detection variable 1`] = `
+"import { withAsyncContext as _withAsyncContext } from 'vue'
+
+export default {
+ async setup(__props, { expose }) {
+ expose()
+
+let __temp, __restore
+const a = 1 + ((
+ ([__temp,__restore] = _withAsyncContext(() => {
+ return foo
+ })),
+ __temp = await __temp,
+ __restore(),
+ __temp
+))
+return { a }
+}
+
+}"
+`;
+
exports[`SFC compile <script setup> binding analysis for destructur 1`] = `
"export default {
setup(__props, { expose }) {
})
describe('async/await detection', () => {
- function assertAwaitDetection(
- code: string,
- expected: string | ((content: string) => boolean),
- shouldAsync = true
- ) {
+ function assertAwaitDetection(code: string, shouldAsync = true) {
const { content } = compile(`<script setup>${code}</script>`, {
refSugar: true
})
expect(content).toMatch(`let __temp, __restore`)
}
expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
- if (typeof expected === 'string') {
- expect(content).toMatch(expected)
- } else {
- expect(expected(content)).toBe(true)
- }
+ assertCode(content)
}
test('expression statement', () => {
- assertAwaitDetection(
- `await foo`,
- `;(([__temp,__restore]=_withAsyncContext(()=>(foo))),__temp=await __temp,__restore())`
- )
+ assertAwaitDetection(`await foo`)
})
test('variable', () => {
- assertAwaitDetection(
- `const a = 1 + (await foo)`,
- `1 + ((([__temp,__restore]=_withAsyncContext(()=>(foo))),__temp=await __temp,__restore(),__temp))`
- )
+ assertAwaitDetection(`const a = 1 + (await foo)`)
})
test('ref', () => {
- assertAwaitDetection(
- `let a = $ref(1 + (await foo))`,
- `1 + ((([__temp,__restore]=_withAsyncContext(()=>(foo))),__temp=await __temp,__restore(),__temp))`
- )
+ assertAwaitDetection(`let a = $ref(1 + (await foo))`)
+ })
+
+ test('nested await', () => {
+ assertAwaitDetection(`await (await foo)`)
+ assertAwaitDetection(`await ((await foo))`)
+ assertAwaitDetection(`await (await (await foo))`)
})
test('nested statements', () => {
- assertAwaitDetection(`if (ok) { await foo } else { await bar }`, code => {
- return (
- code.includes(
- `;(([__temp,__restore]=_withAsyncContext(()=>(foo))),__temp=await __temp,__restore())`
- ) &&
- code.includes(
- `;(([__temp,__restore]=_withAsyncContext(()=>(bar))),__temp=await __temp,__restore())`
- )
- )
- })
+ assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
})
test('should ignore await inside functions', () => {
// function declaration
- assertAwaitDetection(
- `async function foo() { await bar }`,
- `await bar`,
- false
- )
+ assertAwaitDetection(`async function foo() { await bar }`, false)
// function expression
- assertAwaitDetection(
- `const foo = async () => { await bar }`,
- `await bar`,
- false
- )
+ assertAwaitDetection(`const foo = async () => { await bar }`, false)
// object method
- assertAwaitDetection(
- `const obj = { async method() { await bar }}`,
- `await bar`,
- false
- )
+ assertAwaitDetection(`const obj = { async method() { await bar }}`, false)
// class method
assertAwaitDetection(
`const cls = class Foo { async method() { await bar }}`,
- `await bar`,
false
)
})
/**
* await foo()
* -->
- * (([__temp, __restore] = withAsyncContext(() => foo())),__temp=await __temp,__restore(),__temp)
+ * (([__temp, __restore] = withAsyncContext(async () => foo())),__temp=await __temp,__restore(),__temp)
*/
function processAwait(node: AwaitExpression, isStatement: boolean) {
+ const argumentStart =
+ node.argument.extra && node.argument.extra.parenthesized
+ ? (node.argument.extra.parenStart as number)
+ : node.argument.start!
+
+ const argumentStr = source.slice(
+ argumentStart + startOffset,
+ node.argument.end! + startOffset
+ )
+
+ const containsNestedAwait = /\bawait\b/.test(argumentStr)
+
s.overwrite(
node.start! + startOffset,
- node.argument.start! + startOffset,
- `${isStatement ? `;` : ``}(([__temp,__restore]=${helper(
+ argumentStart + startOffset,
+ `${isStatement ? `;` : ``}(\n ([__temp,__restore] = ${helper(
`withAsyncContext`
- )}(()=>(`
+ )}(${containsNestedAwait ? `async ` : ``}() => {\n return `
)
s.appendLeft(
node.end! + startOffset,
- `))),__temp=await __temp,__restore()${isStatement ? `` : `,__temp`})`
+ `\n })),\n __temp = await __temp,\n __restore()${
+ isStatement ? `` : `,\n __temp`
+ }\n)`
)
}