Skip to content

Commit df815ac

Browse files
committed
fix #4018: fix for async as boolean edge case
1 parent 59950c9 commit df815ac

File tree

3 files changed

+32
-2
lines changed

3 files changed

+32
-2
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66

77
Due to a copy+paste typo, the binary published to `@esbuild/netbsd-arm64` was not actually for `arm64`, and didn't run in that environment. This release should fix running esbuild in that environment (NetBSD on 64-bit ARM). Sorry about the mistake.
88

9+
* Fix esbuild incorrectly rejecting valid TypeScript edge case ([#4027](https://github.com/evanw/esbuild/issues/4027))
10+
11+
The following TypeScript code is valid:
12+
13+
```ts
14+
export function open(async?: boolean): void {
15+
console.log(async as boolean)
16+
}
17+
```
18+
19+
Before this version, esbuild would fail to parse this with a syntax error as it expected the token sequence `async as ...` to be the start of an async arrow function expression `async as => ...`. This edge case should be parsed correctly by esbuild starting with this release.
20+
921
## 2024
1022

1123
All esbuild versions published in the year 2024 (versions 0.19.12 through 0.24.2) can be found in [CHANGELOG-2024.md](./CHANGELOG-2024.md).

internal/js_parser/js_parser.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -2879,9 +2879,10 @@ func (p *parser) parseAsyncPrefixExpr(asyncRange logger.Range, level js_ast.L, f
28792879
// "async x => {}"
28802880
case js_lexer.TIdentifier:
28812881
if level <= js_ast.LAssign {
2882-
// See https://github.com/tc39/ecma262/issues/2034 for details
28832882
isArrowFn := true
28842883
if (flags&exprFlagForLoopInit) != 0 && p.lexer.Identifier.String == "of" {
2884+
// See https://github.com/tc39/ecma262/issues/2034 for details
2885+
28852886
// "for (async of" is only an arrow function if the next token is "=>"
28862887
isArrowFn = p.checkForArrowAfterTheCurrentToken()
28872888

@@ -2891,6 +2892,18 @@ func (p *parser) parseAsyncPrefixExpr(asyncRange logger.Range, level js_ast.L, f
28912892
p.log.AddError(&p.tracker, r, "For loop initializers cannot start with \"async of\"")
28922893
panic(js_lexer.LexerPanic{})
28932894
}
2895+
} else if p.options.ts.Parse && p.lexer.Token == js_lexer.TIdentifier {
2896+
// Make sure we can parse the following TypeScript code:
2897+
//
2898+
// export function open(async?: boolean): void {
2899+
// console.log(async as boolean)
2900+
// }
2901+
//
2902+
// TypeScript solves this by using a two-token lookahead to check for
2903+
// "=>" after an identifier after the "async". This is done in
2904+
// "isUnParenthesizedAsyncArrowFunctionWorker" which was introduced
2905+
// here: https://github.com/microsoft/TypeScript/pull/8444
2906+
isArrowFn = p.checkForArrowAfterTheCurrentToken()
28942907
}
28952908

28962909
if isArrowFn {

internal/js_parser/ts_parser_test.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -2307,7 +2307,7 @@ func TestTSArrow(t *testing.T) {
23072307

23082308
expectPrintedTS(t, "async (): void => {}", "async () => {\n};\n")
23092309
expectPrintedTS(t, "async (a): void => {}", "async (a) => {\n};\n")
2310-
expectParseErrorTS(t, "async x: void => {}", "<stdin>: ERROR: Expected \"=>\" but found \":\"\n")
2310+
expectParseErrorTS(t, "async x: void => {}", "<stdin>: ERROR: Expected \";\" but found \"x\"\n")
23112311

23122312
expectPrintedTS(t, "function foo(x: boolean): asserts x", "")
23132313
expectPrintedTS(t, "function foo(x: boolean): asserts<T>", "")
@@ -2331,6 +2331,11 @@ func TestTSArrow(t *testing.T) {
23312331
expectParseErrorTargetTS(t, 5, "return check ? (hover = 2, bar) : baz()", "")
23322332
expectParseErrorTargetTS(t, 5, "return check ? (hover = 2, bar) => 0 : baz()",
23332333
"<stdin>: ERROR: Transforming default arguments to the configured target environment is not supported yet\n")
2334+
2335+
// https://github.com/evanw/esbuild/issues/4027
2336+
expectPrintedTS(t, "function f(async?) { g(async in x) }", "function f(async) {\n g(async in x);\n}\n")
2337+
expectPrintedTS(t, "function f(async?) { g(async as boolean) }", "function f(async) {\n g(async);\n}\n")
2338+
expectPrintedTS(t, "function f() { g(async as => boolean) }", "function f() {\n g(async (as) => boolean);\n}\n")
23342339
}
23352340

23362341
func TestTSSuperCall(t *testing.T) {

0 commit comments

Comments
 (0)