diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index b677bfd0c9..3e950d2657 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -20,6 +20,7 @@ import { SCOPE_OTHER, SCOPE_SIMPLE_CATCH, SCOPE_SUPER, + type BindingTypes, } from "../util/scopeflags"; const loopLabel = { kind: "loop" }, @@ -1611,11 +1612,12 @@ export default class StatementParser extends ExpressionParser { node: N.Class, isStatement: boolean, optionalId: ?boolean, + bindingType: BindingTypes = BIND_CLASS, ): void { if (this.match(tt.name)) { node.id = this.parseIdentifier(); if (isStatement) { - this.checkLVal(node.id, BIND_CLASS, undefined, "class name"); + this.checkLVal(node.id, bindingType, undefined, "class name"); } } else { if (optionalId || !isStatement) { diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 078797d74b..5d6922d329 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -14,8 +14,9 @@ import { BIND_TS_CONST_ENUM, BIND_TS_TYPE, BIND_TS_INTERFACE, - BIND_TS_FN_TYPE, + BIND_TS_AMBIENT, BIND_TS_NAMESPACE, + BIND_CLASS, } from "../../util/scopeflags"; import TypeScriptScopeHandler from "./scope"; @@ -1278,6 +1279,9 @@ export default (superClass: Class): Class => /* declarationPosition */ true, ); case tt._class: + // While this is also set by tsParseExpressionStatement, we need to set it + // before parsing the class declaration to now how to register it in the scope. + nany.declare = true; return this.parseClass( nany, /* isStatement */ true, @@ -1552,7 +1556,7 @@ export default (superClass: Class): Class => checkFunctionStatementId(node: N.Function): void { if (!node.body && node.id) { - this.checkLVal(node.id, BIND_TS_FN_TYPE, null, "function name"); + this.checkLVal(node.id, BIND_TS_AMBIENT, null, "function name"); } else { super.checkFunctionStatementId(...arguments); } @@ -1988,7 +1992,12 @@ export default (superClass: Class): Class => return; } - super.parseClassId(...arguments); + super.parseClassId( + node, + isStatement, + optionalId, + (node: any).declare ? BIND_TS_AMBIENT : BIND_CLASS, + ); const typeParameters = this.tsTryParseTypeParameters(); if (typeParameters) node.typeParameters = typeParameters; } diff --git a/packages/babel-parser/src/plugins/typescript/scope.js b/packages/babel-parser/src/plugins/typescript/scope.js index f706f1e88e..085b90e734 100644 --- a/packages/babel-parser/src/plugins/typescript/scope.js +++ b/packages/babel-parser/src/plugins/typescript/scope.js @@ -25,7 +25,7 @@ class TypeScriptScope extends Scope { // classes (which are also in .lexical) and interface (which are also in .types) classes: string[] = []; - // namespaces and bodyless-functions are too difficult to track, + // namespaces and ambient functions (or classes) are too difficult to track, // especially without type analysis. // We need to track them anyway, to avoid "X is not defined" errors // when exporting them. diff --git a/packages/babel-parser/src/util/scopeflags.js b/packages/babel-parser/src/util/scopeflags.js index 8c9697b3a4..43b18f6641 100644 --- a/packages/babel-parser/src/util/scopeflags.js +++ b/packages/babel-parser/src/util/scopeflags.js @@ -62,14 +62,14 @@ export const BIND_CLASS = BIND_KIND_VALUE | BIND_KIND_TYPE | BIND_SCOPE_ BIND_TS_INTERFACE = 0 | BIND_KIND_TYPE | 0 | BIND_FLAGS_CLASS , BIND_TS_TYPE = 0 | BIND_KIND_TYPE | 0 | 0 , BIND_TS_ENUM = BIND_KIND_VALUE | BIND_KIND_TYPE | BIND_SCOPE_LEXICAL | BIND_FLAGS_TS_ENUM, - BIND_TS_FN_TYPE = 0 | 0 | 0 | BIND_FLAGS_TS_EXPORT_ONLY, + BIND_TS_AMBIENT = 0 | 0 | 0 | BIND_FLAGS_TS_EXPORT_ONLY, // These bindings don't introduce anything in the scope. They are used for assignments and // function expressions IDs. BIND_NONE = 0 | 0 | 0 | BIND_FLAGS_NONE , BIND_OUTSIDE = BIND_KIND_VALUE | 0 | 0 | BIND_FLAGS_NONE , BIND_TS_CONST_ENUM = BIND_TS_ENUM | BIND_FLAGS_TS_CONST_ENUM, - BIND_TS_NAMESPACE = BIND_TS_FN_TYPE; + BIND_TS_NAMESPACE = 0 | 0 | 0 | BIND_FLAGS_TS_EXPORT_ONLY; export type BindingTypes = | typeof BIND_NONE @@ -81,5 +81,5 @@ export type BindingTypes = | typeof BIND_TS_INTERFACE | typeof BIND_TS_TYPE | typeof BIND_TS_ENUM - | typeof BIND_TS_FN_TYPE + | typeof BIND_TS_AMBIENT | typeof BIND_TS_NAMESPACE; diff --git a/packages/babel-parser/test/fixtures/typescript/scope/callable-class-ambient/input.ts b/packages/babel-parser/test/fixtures/typescript/scope/callable-class-ambient/input.ts new file mode 100644 index 0000000000..7714551a24 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/callable-class-ambient/input.ts @@ -0,0 +1,2 @@ +declare class C { } +declare function C(): void; diff --git a/packages/babel-parser/test/fixtures/typescript/scope/callable-class-ambient/output.json b/packages/babel-parser/test/fixtures/typescript/scope/callable-class-ambient/output.json new file mode 100644 index 0000000000..d62f3bba74 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/callable-class-ambient/output.json @@ -0,0 +1,151 @@ +{ + "type": "File", + "start": 0, + "end": 47, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 47, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "id": { + "type": "Identifier", + "start": 14, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "C" + }, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 16, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "body": [] + }, + "declare": true + }, + { + "type": "TSDeclareFunction", + "start": 20, + "end": 47, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 27 + } + }, + "id": { + "type": "Identifier", + "start": 37, + "end": 38, + "loc": { + "start": { + "line": 2, + "column": 17 + }, + "end": { + "line": 2, + "column": 18 + }, + "identifierName": "C" + }, + "name": "C" + }, + "generator": false, + "async": false, + "params": [], + "returnType": { + "type": "TSTypeAnnotation", + "start": 40, + "end": 46, + "loc": { + "start": { + "line": 2, + "column": 20 + }, + "end": { + "line": 2, + "column": 26 + } + }, + "typeAnnotation": { + "type": "TSVoidKeyword", + "start": 42, + "end": 46, + "loc": { + "start": { + "line": 2, + "column": 22 + }, + "end": { + "line": 2, + "column": 26 + } + } + } + }, + "declare": true + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/scope/callable-class/input.ts b/packages/babel-parser/test/fixtures/typescript/scope/callable-class/input.ts new file mode 100644 index 0000000000..e54e510e93 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/callable-class/input.ts @@ -0,0 +1,2 @@ +declare class C { } +function C() { } diff --git a/packages/babel-parser/test/fixtures/typescript/scope/callable-class/output.json b/packages/babel-parser/test/fixtures/typescript/scope/callable-class/output.json new file mode 100644 index 0000000000..205c683a02 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/callable-class/output.json @@ -0,0 +1,137 @@ +{ + "type": "File", + "start": 0, + "end": 36, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 36, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "declare": true, + "id": { + "type": "Identifier", + "start": 14, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "C" + }, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 16, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "body": [] + } + }, + { + "type": "FunctionDeclaration", + "start": 20, + "end": 36, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "id": { + "type": "Identifier", + "start": 29, + "end": 30, + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 10 + }, + "identifierName": "C" + }, + "name": "C" + }, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 33, + "end": 36, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "body": [], + "directives": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-import-ambient-class/input.ts b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-import-ambient-class/input.ts new file mode 100644 index 0000000000..e57fcdb01a --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-import-ambient-class/input.ts @@ -0,0 +1,3 @@ +import Something from './somewhere.js' + +declare class Something {} diff --git a/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-import-ambient-class/output.json b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-import-ambient-class/output.json new file mode 100644 index 0000000000..fe9e84eee5 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-import-ambient-class/output.json @@ -0,0 +1,154 @@ +{ + "type": "File", + "start": 0, + "end": 66, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 26 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 66, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 26 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start": 0, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start": 7, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "local": { + "type": "Identifier", + "start": 7, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 16 + }, + "identifierName": "Something" + }, + "name": "Something" + } + } + ], + "source": { + "type": "StringLiteral", + "start": 22, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 22 + }, + "end": { + "line": 1, + "column": 38 + } + }, + "extra": { + "rawValue": "./somewhere.js", + "raw": "'./somewhere.js'" + }, + "value": "./somewhere.js" + } + }, + { + "type": "ClassDeclaration", + "start": 40, + "end": 66, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 26 + } + }, + "declare": true, + "id": { + "type": "Identifier", + "start": 54, + "end": 63, + "loc": { + "start": { + "line": 3, + "column": 14 + }, + "end": { + "line": 3, + "column": 23 + }, + "identifierName": "Something" + }, + "name": "Something" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 64, + "end": 66, + "loc": { + "start": { + "line": 3, + "column": 24 + }, + "end": { + "line": 3, + "column": 26 + } + }, + "body": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file