From fadf8af3cf150bbf67184b68219d39bc2163f99f Mon Sep 17 00:00:00 2001 From: Alan Pierce Date: Sun, 11 Nov 2018 17:18:24 -0800 Subject: [PATCH] Port all babel-parser changes from 2018-10-01 to 2018-11-09 (#333) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Notably, this adds support for TypeScript 3.0 syntax. Details: 07862e727 Fix perf issue in typescript parser plugin (#8792) 🚫 Already implemented with a similar fix. 3c8740171 [decorators] [typescript] Parse type parameters (#8767) ✅ Straightforward port. a5b5ed928 Typescript - Tuple elements can be optional (#8720) ✅ Straightforward port. 2575312d1 Fix parsing of slash after class expression (#8804) ✅ Already worked, added test. 850bc1d3d class private methods and properties: should not allow spaces between # and identifier (#8756) 🚫 Error checking only. 08454ece4 Typescript - Tuples can include rest elements (#8805) ✅ Straightforward port. d2c75c2d3 fix: corrects handling of newline after async with paren-less arrow function (#8830) ✅ Straightforward port with new regression test. ce1a6526e flow-bin@0.82.0 (#8832) 🚫 Nothing to port. 8ee857e26 v7.1.3 🚫 Nothing to port. 929567523 Fixes #8865 (#8866) 🚫 Sucrase doesn't maintain curLine. e4929e11f [flow] Explicit inexact objects with `...` (#8884) ✅ Relatively straightforward port with test. No error handling ported. cd81b079e Allow function types in type params within arrow return types (#8954) ✅ Already worked, just implemented test. f216a7b06 [flow] Add support for parsing `_` as implicit instantiation in call/new (#8883) ✅ Already worked, complexity in the babel code seemed to all be around error handling. 24c4901ff Remove Babylon plugins for features already merged to the ECMAScript spec (#8448) 🚫 Sucrase doesn't have parser plugins. e3b2c1aff fix: Do not allow TypeCastExpressions w/o parens (#8956) 🚫 Just extra error handling. 2194842d1 Typescript: Validate tuple type element positions (#8828) 🚫 Just error handling. 2fa198463 Fix await in function name and parameters (#7727) 🚫 Just error handling. afe67a703 v7.1.5 🚫 Release only. 5d5cd8612 Fix several edge cases with context expression state (#8972) ✅ Added tests, but seems like everything was already working. The fixes were all around context, and Sucrase got rid of context. 343f776ca Rename primitive types to reserved types (#8984) 🚫 Code doesn't exist in Sucrase. 4f206b241 prettier@1.15.1 (#9001) 🚫 Tooling upgrade. --- src/parser/plugins/flow.ts | 7 +++ src/parser/plugins/typescript.ts | 21 ++++++++- src/parser/traverser/expression.ts | 7 ++- src/parser/traverser/statement.ts | 13 ++++++ test/flow-test.ts | 51 ++++++++++++++++++++ test/sucrase-test.ts | 74 ++++++++++++++++++++++++++++++ test/typescript-test.ts | 35 ++++++++++++++ 7 files changed, 206 insertions(+), 2 deletions(-) diff --git a/src/parser/plugins/flow.ts b/src/parser/plugins/flow.ts index 5f54bbda..b5994b72 100644 --- a/src/parser/plugins/flow.ts +++ b/src/parser/plugins/flow.ts @@ -453,6 +453,13 @@ function flowParseObjectType(allowStatic: boolean, allowExact: boolean, allowPro function flowParseObjectTypeProperty(): void { if (match(tt.ellipsis)) { expect(tt.ellipsis); + if (!eat(tt.comma)) { + eat(tt.semi); + } + // Explicit inexact object syntax. + if (match(tt.braceR)) { + return; + } flowParseType(); } else { flowParseObjectPropertyKey(); diff --git a/src/parser/plugins/typescript.ts b/src/parser/plugins/typescript.ts index f63cc14c..4d597867 100644 --- a/src/parser/plugins/typescript.ts +++ b/src/parser/plugins/typescript.ts @@ -28,6 +28,7 @@ import { } from "../traverser/expression"; import {parseBindingList} from "../traverser/lval"; import { + baseParseMaybeDecoratorArguments, parseBlockBody, parseClass, parseClassProperty, @@ -418,12 +419,23 @@ function tsParseMappedType(): void { function tsParseTupleType(): void { tsParseBracketedList( ParsingContext.TupleElementTypes, - tsParseType, + tsParseTupleElementType, /* bracket */ true, /* skipFirstToken */ false, ); } +function tsParseTupleElementType(): void { + // parses `...TsType[]` + if (eat(tt.ellipsis)) { + tsParseType(); + return; + } + // parses `TsType?` + tsParseType(); + eat(tt.question); +} + function tsParseParenthesizedType(): void { expect(tt.parenL); tsParseType(); @@ -1402,3 +1414,10 @@ export function tsParseAssignableListItemTypes(): void { tsTryParseTypeAnnotation(); popTypeContext(oldIsType); } + +export function tsParseMaybeDecoratorArguments(): void { + if (match(tt.lessThan)) { + tsParseTypeArguments(); + } + baseParseMaybeDecoratorArguments(); +} diff --git a/src/parser/traverser/expression.ts b/src/parser/traverser/expression.ts index b9acdb6e..1c3d430b 100644 --- a/src/parser/traverser/expression.ts +++ b/src/parser/traverser/expression.ts @@ -449,7 +449,12 @@ export function parseExprAtom(): boolean { next(); parseFunction(functionStart, false, false); return false; - } else if (canBeArrow && contextualKeyword === ContextualKeyword._async && match(tt.name)) { + } else if ( + canBeArrow && + !canInsertSemicolon() && + contextualKeyword === ContextualKeyword._async && + match(tt.name) + ) { parseIdentifier(); expect(tt.arrow); // let foo = bar => {}; diff --git a/src/parser/traverser/statement.ts b/src/parser/traverser/statement.ts index 6abc6944..e5a134e4 100644 --- a/src/parser/traverser/statement.ts +++ b/src/parser/traverser/statement.ts @@ -25,6 +25,7 @@ import { tsParseExportDeclaration, tsParseIdentifierStatement, tsParseImportEqualsDeclaration, + tsParseMaybeDecoratorArguments, tsStartParseFunctionParams, tsTryParseClassMemberWithIsStatic, tsTryParseExport, @@ -243,6 +244,18 @@ function parseDecorator(): void { parseIdentifier(); } } + parseMaybeDecoratorArguments(); +} + +function parseMaybeDecoratorArguments(): void { + if (isTypeScriptEnabled) { + tsParseMaybeDecoratorArguments(); + } else { + baseParseMaybeDecoratorArguments(); + } +} + +export function baseParseMaybeDecoratorArguments(): void { if (eat(tt.parenL)) { parseCallExpressionArguments(tt.parenR); } diff --git a/test/flow-test.ts b/test/flow-test.ts index 9b6a9935..ab1cd0fe 100644 --- a/test/flow-test.ts +++ b/test/flow-test.ts @@ -328,4 +328,55 @@ describe("transform flow", () => { `, ); }); + + it("allows explicit inexact types", () => { + assertFlowResult( + ` + type T = {...}; + type U = {x: number, ...}; + type V = {x: number, ...V, ...U}; + `, + `"use strict"; + + + + `, + ); + }); + + it("allows function types as type parameters", () => { + assertFlowResult( + ` + type T = Array<(string) => number> + `, + `"use strict"; + + `, + ); + }); + + it("allows underscore type arguments in invocations", () => { + assertFlowResult( + ` + test< + _, + _, + number, + _, + _, + >(); + new test<_>(); + `, + `"use strict"; + test + + + + + +(); + new test(); + `, + ); + }); }); diff --git a/test/sucrase-test.ts b/test/sucrase-test.ts index 71aced58..55dfd5d5 100644 --- a/test/sucrase-test.ts +++ b/test/sucrase-test.ts @@ -629,4 +629,78 @@ describe("sucrase", () => { {transforms: ["imports", "typescript"]}, ); }); + + it("allows a class expression followed by a division operator", () => { + assertResult( + ` + x = class {} / foo + `, + ` + x = class {} / foo + `, + {transforms: []}, + ); + }); + + it("handles newline after async in paren-less arrow function", () => { + assertResult( + ` + import async from 'foo'; + async + x => x + `, + `"use strict";${IMPORT_DEFAULT_PREFIX} + var _foo = require('foo'); var _foo2 = _interopRequireDefault(_foo); + _foo2.default + x => x + `, + {transforms: ["imports"]}, + ); + }); + + it("handles various parser edge cases around regexes", () => { + assertResult( + ` + for (const {a} of /b/) {} + + for (let {a} of /b/) {} + + for (var {a} of /b/) {} + + function *f() { yield + {}/1/g + } + + function* bar() { yield class {} } + + <> + + + + `, + `const _jsxFileName = ""; + for (const {a} of /b/) {} + + for (let {a} of /b/) {} + + for (var {a} of /b/) {} + + function *f() { yield + {}/1/g + } + + function* bar() { yield class {} } + + React.createElement(React.Fragment, null + , React.createElement(Select, { prop: { function: 'test' }, __self: this, __source: {fileName: _jsxFileName, lineNumber: 15}} ) + , React.createElement(Select, { prop: { class: 'test' }, __self: this, __source: {fileName: _jsxFileName, lineNumber: 16}} ) + , React.createElement(Select, { prop: { delete: 'test' }, __self: this, __source: {fileName: _jsxFileName, lineNumber: 17}} ) + , React.createElement(Select, { prop: { enum: 'test' }, __self: this, __source: {fileName: _jsxFileName, lineNumber: 18}} ) + ) + `, + {transforms: ["jsx"]}, + ); + }); }); diff --git a/test/typescript-test.ts b/test/typescript-test.ts index 38416e09..efe82cfd 100644 --- a/test/typescript-test.ts +++ b/test/typescript-test.ts @@ -1203,4 +1203,39 @@ describe("typescript transform", () => { `, ); }); + + it("parses type arguments on decorators", () => { + assertTypeScriptResult( + ` + @decorator() + class Test {} + `, + `"use strict"; + @decorator() + class Test {} + `, + ); + }); + + it("properly parses tuple types with optional values", () => { + assertTypeScriptResult( + ` + let x: [string, number?, (string | number)?]; + `, + `"use strict"; + let x; + `, + ); + }); + + it("allows a rest element on a tuple type", () => { + assertTypeScriptResult( + ` + let x: [string, ...number[]]; + `, + `"use strict"; + let x; + `, + ); + }); });