Skip to content

Commit

Permalink
Port babel-parser changes from 2020-12-03 to 2021-02-16
Browse files Browse the repository at this point in the history
Instructions:
https://github.com/alangpierce/sucrase/wiki/Porting-changes-from-Babel's-parser

Changes:
8478027d1a Make sure babel parser throws exactly same recoverable errors when estree plugin is enabled (#12375)
🚫 Only affects error handling.

3bd6a3d781 Make sure estree test should not throw if babel parser does not throw (#12443)
🚫 estree/test code not relevant to Sucrase.

285402d82f Add StaticBlock to Table of Contents in AST spec (#12449)
🚫 Docs only.

2ba9265198 Add missing semicolon in AST spec (#12454)
🚫 Docs only.

b422c7f0ef [babel 8] Disallow sequence expressions in JSX expression containers (#12447)
🚫 Only affects error handling.

e901454096 v7.12.10
🚫 Release only.

a0c1a9a9e4 Disallow non octal decimal escape before use strict (#12366)
🚫 Only affects error handling.

a1acfba387 v7.12.11
🚫 Release only.

0dbc3dd4a0 Add errorRecovery option to type definitions in babel-parser (#12564)
🚫 Type definitions only.

fbef603c43 Report a SyntaxError for `}` and `>` in JSX text (#12451)
🚫 Error handling only.

e8b08523f4 docs: add package-specific documentation links (#12531)
🚫 Docs only.

c1473e30b2 [ts]Add optional property to OptionalCallExpression node that has type arguments (#12562)
🚫 AST only.

6c9a481e83 @babel/preset-typescript: fix tsx assigment issue (#12599)
🚫 Bug doesn't appear in Sucrase.

62290aa1ba [babel 8] Don't create `TSParenthesizedType` nodes by default (#12608)
🚫 AST only.

568679e301 fix: start TypePredicate node after returnToken (#12678)
🚫 AST only.

8fcba6eb55 Raise recoverable error for await expressions in sync functions (#12520)
🚫 Validation only.

fbfd1b2aa6 fix(parser): throw error with wrong typescript 'export declare' (#12684)
🚫 Validation only.

45fdde0ce2 (ts) Throw for abstract methods in a non-abstract class (#12686)
🚫 Validation only.

8cf0a757d5 Recover from "missing semicolon" errors (#12437)
🚫 Validation only.

108564fdad refactor: raise AwaitNotInAsyncContext when an AwaitExpression will be parsed (#12716)
🚫 Validation only.

ecfe20395b spec: disable await binding identifier within static block (#12661)
🚫 Validation only.

20664a430e Permit %%placeholder%% in left-hand-side of a let declaration (#12725)
🚫 Sucrase does not support placeholders.

d291edbdf1 v7.12.13
🚫 Release only.

94ba66c89b fix(ts): allow abstract methods with `export default abstract class` (#12748)
🚫 Bug fix in validation code.

dd18d87152 v7.12.14
🚫 Release only.

d1cf66e8c1 fix(ts): include leading operator in `TSUnionType` and `TSIntersectionType` locations (#12757)
🚫 AST only.

b1921d2d9b v7.12.15
🚫 Release only.

eccbcca948 [TS] Create `TSUnionType` or `TSIntersectionType` when typealias has a leading operator (#12758)
🚫 AST only.

bf523da0b0 fix(ts): include `asserts` in `TSTypePredicate` location (#12763)
🚫 AST only.

e735266dee Avoid importing `.json` files (#12759)
🚫 Babel-internal change.

d242ea04c8 babel-parser(ts): Raise recoverable error for abstract interface (#12771)
🚫 Only affects AST and error handling.

f1a327506e ts: Throw recoverable for duplicates access modifier (#12775)
🚫 Validation only.

c07185207c [parser] Fix scope handling of Flow declared functions (#12735)
🚫 Reported bug seems to work fine in Sucrase. The identifier role seems to be missing for flow `declare function`, but the impact is unclear, and flow support is lower priority anyway.

dd5c9f958c v7.12.16
🚫 Release only.

c22e72eb24 Raise recoverable error for type members with invalid modifiers (#12785)
🚫 Validation only.

4819ce70e4 refactor: fix typo in error.js (#12808)
🚫 Docs only.

8e9143f06f fix(ts): parse multiline declarations correctly (#12776)
✅ Ported the change and also added tests and better docs for `tsParseDeclaration`.
  • Loading branch information
Alan Pierce authored and Alan Pierce committed Jul 5, 2021
1 parent 291ccc5 commit 198f58d
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 30 deletions.
72 changes: 43 additions & 29 deletions src/parser/plugins/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
eatContextual,
expect,
expectContextual,
hasFollowingLineBreak,
hasPrecedingLineBreak,
isContextual,
isLineTerminator,
Expand Down Expand Up @@ -955,70 +956,75 @@ function tsParseExpressionStatement(contextualKeyword: ContextualKeyword): boole
return false;
}

// Common to tsTryParseDeclare, tsTryParseExportDeclaration, and tsParseExpressionStatement.
// Returns true if it matched a declaration.
/**
* Common code for parsing a declaration.
*
* isBeforeToken indicates that the current parser state is at the contextual
* keyword (and that it is not yet emitted) rather than reading the token after
* it. When isBeforeToken is true, we may be preceded by an `export` token and
* should include that token in a type context we create, e.g. to handle
* `export interface` or `export type`. (This is a bit of a hack and should be
* cleaned up at some point.)
*
* Returns true if it matched a declaration.
*/
function tsParseDeclaration(contextualKeyword: ContextualKeyword, isBeforeToken: boolean): boolean {
switch (contextualKeyword) {
case ContextualKeyword._abstract:
if (tsCheckLineTerminatorAndMatch(tt._class, isBeforeToken)) {
if (isBeforeToken) next();
if (tsCheckLineTerminator(isBeforeToken) && match(tt._class)) {
state.tokens[state.tokens.length - 1].type = tt._abstract;
parseClass(/* isStatement */ true, /* optionalId */ false);
return true;
}
break;

case ContextualKeyword._enum:
if (tsCheckLineTerminatorAndMatch(tt.name, isBeforeToken)) {
if (isBeforeToken) next();
if (tsCheckLineTerminator(isBeforeToken) && match(tt.name)) {
state.tokens[state.tokens.length - 1].type = tt._enum;
tsParseEnumDeclaration();
return true;
}
break;

case ContextualKeyword._interface:
if (tsCheckLineTerminatorAndMatch(tt.name, isBeforeToken)) {
if (tsCheckLineTerminator(isBeforeToken) && match(tt.name)) {
// `next` is true in "export" and "declare" contexts, so we want to remove that token
// as well.
const oldIsType = pushTypeContext(1);
if (isBeforeToken) next();
const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
tsParseInterfaceDeclaration();
popTypeContext(oldIsType);
return true;
}
break;

case ContextualKeyword._module:
if (isBeforeToken) next();
if (match(tt.string)) {
const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
tsParseAmbientExternalModuleDeclaration();
popTypeContext(oldIsType);
return true;
} else if (tsCheckLineTerminatorAndMatch(tt.name, isBeforeToken)) {
const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
if (isBeforeToken) next();
tsParseModuleOrNamespaceDeclaration();
popTypeContext(oldIsType);
return true;
if (tsCheckLineTerminator(isBeforeToken)) {
if (match(tt.string)) {
const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
tsParseAmbientExternalModuleDeclaration();
popTypeContext(oldIsType);
return true;
} else if (match(tt.name)) {
const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
tsParseModuleOrNamespaceDeclaration();
popTypeContext(oldIsType);
return true;
}
}
break;

case ContextualKeyword._namespace:
if (tsCheckLineTerminatorAndMatch(tt.name, isBeforeToken)) {
const oldIsType = pushTypeContext(1);
if (isBeforeToken) next();
if (tsCheckLineTerminator(isBeforeToken) && match(tt.name)) {
const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
tsParseModuleOrNamespaceDeclaration();
popTypeContext(oldIsType);
return true;
}
break;

case ContextualKeyword._type:
if (tsCheckLineTerminatorAndMatch(tt.name, isBeforeToken)) {
const oldIsType = pushTypeContext(1);
if (isBeforeToken) next();
if (tsCheckLineTerminator(isBeforeToken) && match(tt.name)) {
const oldIsType = pushTypeContext(isBeforeToken ? 2 : 1);
tsParseTypeAliasDeclaration();
popTypeContext(oldIsType);
return true;
Expand All @@ -1031,8 +1037,16 @@ function tsParseDeclaration(contextualKeyword: ContextualKeyword, isBeforeToken:
return false;
}

function tsCheckLineTerminatorAndMatch(tokenType: TokenType, isBeforeToken: boolean): boolean {
return !isLineTerminator() && (isBeforeToken || match(tokenType));
function tsCheckLineTerminator(isBeforeToken: boolean): boolean {
if (isBeforeToken) {
if (hasFollowingLineBreak()) {
return false;
}
next();
return true;
} else {
return !isLineTerminator();
}
}

// Returns true if there was a generic async arrow function.
Expand Down
18 changes: 17 additions & 1 deletion src/parser/traverser/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {eat, finishToken, lookaheadTypeAndKeyword, match} from "../tokenizer/index";
import {eat, finishToken, lookaheadTypeAndKeyword, match, nextTokenStart} from "../tokenizer/index";
import type {ContextualKeyword} from "../tokenizer/keywords";
import {formatTokenType, TokenType, TokenType as tt} from "../tokenizer/types";
import {charCodes} from "../util/charcodes";
Expand Down Expand Up @@ -50,6 +50,22 @@ export function hasPrecedingLineBreak(): boolean {
return false;
}

export function hasFollowingLineBreak(): boolean {
const nextStart = nextTokenStart();
for (let i = state.end; i < nextStart; i++) {
const code = input.charCodeAt(i);
if (
code === charCodes.lineFeed ||
code === charCodes.carriageReturn ||
code === 0x2028 ||
code === 0x2029
) {
return true;
}
}
return false;
}

export function isLineTerminator(): boolean {
return eat(tt.semi) || canInsertSemicolon();
}
Expand Down
80 changes: 80 additions & 0 deletions test/typescript-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,23 @@ describe("typescript transform", () => {
`,
);
});

it("handles and removes `declare module` syntax with an identifier", () => {
assertTypeScriptResult(
`
declare module Builtins {
let result: string[];
export = result;
}
`,
`"use strict";
`,
);
});
Expand Down Expand Up @@ -2458,6 +2475,69 @@ describe("typescript transform", () => {
);
});

it("does not handle a module declaration split by newlines", () => {
// See https://github.com/babel/babel/issues/12773 , the proper parsing of
// this code is to treat each line as separate valid JS.
assertTypeScriptESMResult(
`
declare
module
"A"
{}
`,
`
declare
module
"A"
{}
`,
);
});

it("allows and removes namespace statements", () => {
assertTypeScriptESMResult(
`
namespace foo {}
`,
`
`,
);
});

it("allows and removes export namespace statements", () => {
assertTypeScriptESMResult(
`
export namespace foo {}
`,
`
`,
);
});

it("allows and removes module statements", () => {
assertTypeScriptESMResult(
`
module foo {}
`,
`
`,
);
});

it("allows and removes export module statements", () => {
assertTypeScriptESMResult(
`
export module foo {}
`,
`
`,
);
});

it("transforms constructor initializers even with disableESTransforms", () => {
assertTypeScriptESMResult(
`
Expand Down

0 comments on commit 198f58d

Please sign in to comment.