Skip to content

Commit f98a483

Browse files
committed
fix: preserve non-function handler behavior
fix #15
1 parent 37a80f8 commit f98a483

11 files changed

+100
-54
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ ASTs. If you use the `--commentWorkarounds=true` option it will try to prevent m
4343
from getting deleted but it sometimes causes an assertion to fail in `recast`.
4444

4545
There are a few edge cases where `asyncify` produces funky output. It's intended to not break
46-
any existing behavior (at the moment there are a few bugs that do, which I need to fix)
46+
any existing behavior (I know of no cases where it does, and I have fixed several such issues)
4747
but sometimes the output will be be semantically wrong even if it behaves
4848
correctly. For example, I've seen a case where doing an async operation several times in a row:
4949

src/util/canDefinitelyInvoke.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as t from '@babel/types'
2+
import { NodePath } from '@babel/traverse'
3+
4+
export default function canDefinitelyInvoke<T extends t.Node>(
5+
expr: NodePath<T>
6+
): boolean {
7+
if (expr.isIdentifier()) {
8+
let target: NodePath<any> | undefined = expr
9+
while (target) {
10+
if (target.isIdentifier()) {
11+
target = target.scope.getBinding(target.node.name)?.path
12+
} else if (target.isVariableDeclarator()) {
13+
target = (target as NodePath<t.VariableDeclarator>).get('init')
14+
} else {
15+
break
16+
}
17+
}
18+
return target ? target.isFunction() : false
19+
}
20+
return expr.isFunction()
21+
}

src/util/unwindCatch.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import mergeCatchIntoTryFinally from './mergeCatchIntoFinally'
1212
import { awaited } from './builders'
1313
import convertConditionalReturns from './convertConditionalReturns'
1414
import findNode from './findNode'
15+
import canDefinitelyInvoke from './canDefinitelyInvoke'
1516

1617
export default function unwindCatch(
1718
handler: NodePath<t.Expression>
@@ -34,6 +35,9 @@ export default function unwindCatch(
3435
}
3536

3637
if (!handler.isFunction()) {
38+
if (!canDefinitelyInvoke(handler)) {
39+
return getPreceedingLink(link)
40+
}
3741
const callee = handler.node
3842
;[handler] = handler.replaceWith(
3943
t.arrowFunctionExpression(

src/util/unwindFinally.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import convertBodyToBlockStatement from './convertBodyToBlockStatement'
1010
import convertConditionalReturns from './convertConditionalReturns'
1111
import mergeStatementsIntoTryFinally from './mergeStatementsIntoTryFinally'
1212
import findNode from './findNode'
13+
import canDefinitelyInvoke from './canDefinitelyInvoke'
1314

1415
export default function unwindFinally(
1516
handler: NodePath<t.Expression>
@@ -23,6 +24,9 @@ export default function unwindFinally(
2324
}
2425

2526
if (!handler.isFunction()) {
27+
if (!canDefinitelyInvoke(handler)) {
28+
return preceedingLink
29+
}
2630
const callee = handler.node
2731
;[handler] = handler.replaceWith(
2832
t.arrowFunctionExpression([], t.callExpression(callee, []))

src/util/unwindThen.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import prependBodyStatement from './prependBodyStatement'
1111
import replaceLink from './replaceLink'
1212
import convertConditionalReturns from './convertConditionalReturns'
1313
import findNode from './findNode'
14+
import canDefinitelyInvoke from './canDefinitelyInvoke'
1415

1516
export function unwindThen(
1617
handler: NodePath<t.Expression>
@@ -59,8 +60,11 @@ export function unwindThen(
5960
preceedingLink.node
6061
)
6162
}
62-
return findNode(
63-
replaceLink(link, t.callExpression(handler.node, [preceeding])) as any,
64-
preceedingLink.node
65-
)
63+
if (canDefinitelyInvoke(handler)) {
64+
return findNode(
65+
replaceLink(link, t.callExpression(handler.node, [preceeding])) as any,
66+
preceedingLink.node
67+
)
68+
}
69+
return preceedingLink
6670
}

test/fixtures/catchExpressionBody_Returned.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export const input = `
2+
const handleError = () => {}
23
function foo() {
34
return baz.catch(handleError)
45
}
@@ -7,6 +8,7 @@ function foo() {
78
export const options = {}
89

910
export const expected = `
11+
const handleError = () => {}
1012
async function foo() {
1113
try {
1214
return await baz

test/fixtures/catch_handlersThatCantBeUnwound.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,37 +39,37 @@ export const options = {}
3939

4040
export const expected = `
4141
async function foo() {
42-
return String(await bar.catch(baz => {
42+
return bar.catch(baz => {
4343
switch (baz) {
4444
case 2: return
4545
}
4646
console.log('test')
47-
}))
47+
}).then(String)
4848
}
4949
async function bar() {
50-
return String(await a.catch(b => {
50+
return a.catch(b => {
5151
for (const i of [1, 2, 3]) {
5252
return
5353
}
5454
console.log('test')
55-
}))
55+
}).then(String)
5656
}
5757
async function qux() {
58-
return String(await a.catch(b => {
58+
return a.catch(b => {
5959
while (i) {
6060
return
6161
}
6262
console.log('test')
63-
}))
63+
}).then(String)
6464
}
6565
async function baz() {
66-
return String(await a.catch(b => {
66+
return a.catch(b => {
6767
if (a) {
6868
if (b) {
6969
return
7070
}
7171
}
7272
console.log('test')
73-
}))
73+
}).then(String)
7474
}
7575
`

test/fixtures/finallyIdentifier_Returned.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export const input = `
2+
function cleanup() {
3+
}
24
async function foo() {
35
return await baz.finally(cleanup)
46
}
@@ -7,6 +9,8 @@ async function foo() {
79
export const options = {}
810

911
export const expected = `
12+
function cleanup() {
13+
}
1014
async function foo() {
1115
try {
1216
return await baz

test/fixtures/finally_handlersThatCantBeUnwound.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,37 +39,37 @@ export const options = {}
3939

4040
export const expected = `
4141
async function foo() {
42-
return String(await bar.finally(async () => {
42+
return bar.finally(async () => {
4343
switch (baz) {
4444
case 2: return
4545
}
4646
console.log('test')
47-
}))
47+
}).then(String)
4848
}
4949
async function bar() {
50-
return String(await a.finally(async () => {
50+
return a.finally(async () => {
5151
for (const i of [1, 2, 3]) {
5252
return
5353
}
5454
console.log('test')
55-
}))
55+
}).then(String)
5656
}
5757
async function qux() {
58-
return String(await a.finally(async () => {
58+
return a.finally(async () => {
5959
while (i) {
6060
return
6161
}
6262
console.log('test')
63-
}))
63+
}).then(String)
6464
}
6565
async function baz() {
66-
return String(await a.finally(async () => {
66+
return a.finally(async () => {
6767
if (a) {
6868
if (b) {
6969
return
7070
}
7171
}
7272
console.log('test')
73-
}))
73+
}).then(String)
7474
}
7575
`
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const input = `
2+
async function foo() {
3+
const handler = 2
4+
await baz.then(makeHandler(5))
5+
}
6+
`
7+
8+
export const options = {}
9+
10+
export const expected = `
11+
async function foo() {
12+
const handler = 2
13+
await baz.then(makeHandler(5))
14+
}
15+
`

test/fixtures/then_handlersThatCantBeUnwound.ts

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -39,45 +39,37 @@ export const options = {}
3939

4040
export const expected = `
4141
async function foo() {
42-
return String(
43-
await bar.then(async baz => {
44-
switch (baz) {
45-
case 2: return
46-
}
47-
console.log('test')
48-
})
49-
)
42+
return bar.then(async baz => {
43+
switch (baz) {
44+
case 2: return
45+
}
46+
console.log('test')
47+
}).then(String)
5048
}
5149
async function bar() {
52-
return String(
53-
await a.then(async b => {
54-
for (const i of [1, 2, 3]) {
55-
return
56-
}
57-
console.log('test')
58-
})
59-
)
50+
return a.then(async b => {
51+
for (const i of [1, 2, 3]) {
52+
return
53+
}
54+
console.log('test')
55+
}).then(String)
6056
}
6157
async function qux() {
62-
return String(
63-
await a.then(async b => {
64-
while (i) {
65-
return
66-
}
67-
console.log('test')
68-
})
69-
)
58+
return a.then(async b => {
59+
while (i) {
60+
return
61+
}
62+
console.log('test')
63+
}).then(String)
7064
}
7165
async function baz() {
72-
return String(
73-
await a.then(async b => {
74-
if (a) {
75-
if (b) {
76-
return
77-
}
66+
return a.then(async b => {
67+
if (a) {
68+
if (b) {
69+
return
7870
}
79-
console.log('test')
80-
})
81-
)
71+
}
72+
console.log('test')
73+
}).then(String)
8274
}
8375
`

0 commit comments

Comments
 (0)