Skip to content

Commit 1d71dff

Browse files
authored
Make TS InstantiationExpr parsing more permissive (#2188)
1 parent ba3f604 commit 1d71dff

File tree

3 files changed

+78
-29
lines changed

3 files changed

+78
-29
lines changed

CHANGELOG.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,22 @@
66

77
The upcoming version of TypeScript adds the `moduleSuffixes` field to `tsconfig.json` that introduces more rules to import path resolution. Setting `moduleSuffixes` to `[".ios", ".native", ""]` will try to look at the the relative files `./foo.ios.ts`, `./foo.native.ts`, and finally `./foo.ts` for an import path of `./foo`. Note that the empty string `""` in `moduleSuffixes` is necessary for TypeScript to also look-up `./foo.ts`. This was announced in the [TypeScript 4.7 beta blog post](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#resolution-customization-with-modulesuffixes).
88

9-
* Match the new ASI behavior from TypeScript nightly builds
9+
* Match the new ASI behavior from TypeScript nightly builds ([#2188](https://github.com/evanw/esbuild/pull/2188))
1010

11-
This release updates esbuild to match some very recent behavior changes in the TypeScript parser regarding automatic semicolon insertion. For more information, see the following issues:
11+
This release updates esbuild to match some very recent behavior changes in the TypeScript parser regarding automatic semicolon insertion. For more information, see TypeScript issues #48711 and #48654 (I'm not linking to them directly to avoid Dependabot linkback spam on these issues due to esbuild's popularity). The result is that the following TypeScript code is now considered valid TypeScript syntax:
1212

13-
* https://github.com/microsoft/TypeScript/issues/48711
13+
```ts
14+
class A<T> {}
15+
new A<number> /* ASI now happens here */
16+
if (0) {}
17+
18+
interface B {
19+
(a: number): typeof a /* ASI now happens here */
20+
<T>(): void
21+
}
22+
```
23+
24+
This fix was contributed by [@g-plane](https://github.com/g-plane).
1425

1526
## 0.14.36
1627

internal/js_parser/ts_parser.go

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,19 @@ func (p *parser) isTSArrowFnJSX() (isTSArrowFn bool) {
856856
return
857857
}
858858

859+
func (p *parser) nextTokenIsOpenParenOrLessThanOrDot() (result bool) {
860+
oldLexer := p.lexer
861+
p.lexer.Next()
862+
863+
result = p.lexer.Token == js_lexer.TOpenParen ||
864+
p.lexer.Token == js_lexer.TLessThan ||
865+
p.lexer.Token == js_lexer.TDot
866+
867+
// Restore the lexer
868+
p.lexer = oldLexer
869+
return
870+
}
871+
859872
// This function is taken from the official TypeScript compiler source code:
860873
// https://github.com/microsoft/TypeScript/blob/master/src/compiler/parser.ts
861874
func (p *parser) canFollowTypeArgumentsInExpression() bool {
@@ -867,35 +880,44 @@ func (p *parser) canFollowTypeArgumentsInExpression() bool {
867880
js_lexer.TTemplateHead: // foo<T> `...${100}...`
868881
return true
869882

883+
// Consider something a type argument list only if the following token can't start an expression.
870884
case
871-
// These tokens can't follow in a call expression, nor can they start an
872-
// expression. So, consider the type argument list part of an instantiation
873-
// expression.
874-
js_lexer.TComma, // foo<x>,
875-
js_lexer.TDot, // foo<x>.
876-
js_lexer.TQuestionDot, // foo<x>?.
877-
js_lexer.TCloseParen, // foo<x>)
878-
js_lexer.TCloseBracket, // foo<x>]
879-
js_lexer.TColon, // foo<x>:
880-
js_lexer.TSemicolon, // foo<x>;
881-
js_lexer.TQuestion, // foo<x>?
882-
js_lexer.TEqualsEquals, // foo<x> ==
883-
js_lexer.TEqualsEqualsEquals, // foo<x> ===
884-
js_lexer.TExclamationEquals, // foo<x> !=
885-
js_lexer.TExclamationEqualsEquals, // foo<x> !==
886-
js_lexer.TAmpersandAmpersand, // foo<x> &&
887-
js_lexer.TBarBar, // foo<x> ||
888-
js_lexer.TQuestionQuestion, // foo<x> ??
889-
js_lexer.TCaret, // foo<x> ^
890-
js_lexer.TAmpersand, // foo<x> &
891-
js_lexer.TBar, // foo<x> |
892-
js_lexer.TCloseBrace, // foo<x> }
893-
js_lexer.TEndOfFile: // foo<x>
894-
return true
885+
// From "isStartOfExpression()"
886+
js_lexer.TPlus,
887+
js_lexer.TMinus,
888+
js_lexer.TTilde,
889+
js_lexer.TExclamation,
890+
js_lexer.TDelete,
891+
js_lexer.TTypeof,
892+
js_lexer.TVoid,
893+
js_lexer.TPlusPlus,
894+
js_lexer.TMinusMinus,
895+
js_lexer.TLessThan,
896+
897+
// From "isStartOfLeftHandSideExpression()"
898+
js_lexer.TThis,
899+
js_lexer.TSuper,
900+
js_lexer.TNull,
901+
js_lexer.TTrue,
902+
js_lexer.TFalse,
903+
js_lexer.TNumericLiteral,
904+
js_lexer.TBigIntegerLiteral,
905+
js_lexer.TStringLiteral,
906+
js_lexer.TOpenBracket,
907+
js_lexer.TOpenBrace,
908+
js_lexer.TFunction,
909+
js_lexer.TClass,
910+
js_lexer.TNew,
911+
js_lexer.TSlash,
912+
js_lexer.TSlashEquals,
913+
js_lexer.TIdentifier:
914+
return false
915+
916+
case js_lexer.TImport:
917+
return !p.nextTokenIsOpenParenOrLessThanOrDot()
895918

896919
default:
897-
// Anything else treat as an expression.
898-
return false
920+
return true
899921
}
900922
}
901923

internal/js_parser/ts_parser_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,6 +1913,22 @@ func TestTSInstantiationExpression(t *testing.T) {
19131913
expectParseErrorTSX(t, "type x = y\n<number>\nz", "<stdin>: ERROR: Unexpected end of file\n")
19141914
expectParseErrorTS(t, "type x = typeof y\n<number>\nz\n</number>", "<stdin>: ERROR: Unterminated regular expression\n")
19151915
expectParseErrorTSX(t, "type x = typeof y\n<number>\nz", "<stdin>: ERROR: Unexpected end of file\n")
1916+
1917+
// See: https://github.com/microsoft/TypeScript/issues/48654
1918+
expectPrintedTS(t, "x<true>\ny", "x < true > y;\n")
1919+
expectPrintedTS(t, "x<true>\nif (y) {}", "x;\nif (y) {\n}\n")
1920+
expectPrintedTS(t, "x<true>\nimport 'y'", "x;\nimport \"y\";\n")
1921+
expectPrintedTS(t, "x<true>\nimport('y')", "x < true > import(\"y\");\n")
1922+
expectPrintedTS(t, "x<true>\nimport.meta", "x < true > import.meta;\n")
1923+
expectPrintedTS(t, "new x<number>\ny", "new x() < number > y;\n")
1924+
expectPrintedTS(t, "new x<number>\nif (y) {}", "new x();\nif (y) {\n}\n")
1925+
expectPrintedTS(t, "new x<true>\nimport 'y'", "new x();\nimport \"y\";\n")
1926+
expectPrintedTS(t, "new x<true>\nimport('y')", "new x() < true > import(\"y\");\n")
1927+
expectPrintedTS(t, "new x<true>\nimport.meta", "new x() < true > import.meta;\n")
1928+
1929+
// See: https://github.com/microsoft/TypeScript/issues/48759
1930+
expectParseErrorTS(t, "x<true>\nimport<T>('y')", "<stdin>: ERROR: Expected \"(\" but found \"<\"\n")
1931+
expectParseErrorTS(t, "new x<true>\nimport<T>('y')", "<stdin>: ERROR: Expected \"(\" but found \"<\"\n")
19161932
}
19171933

19181934
func TestTSExponentiation(t *testing.T) {

0 commit comments

Comments
 (0)