diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 6ed0e79a718a..f76033a877a6 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -11,6 +11,7 @@ import { BIND_NONE, SCOPE_OTHER, BIND_TS_ENUM, + BIND_TS_CONST_ENUM, BIND_TS_TYPE, BIND_TS_INTERFACE, BIND_TS_FN_TYPE, @@ -1156,7 +1157,7 @@ export default (superClass: Class): Class => node.id = this.parseIdentifier(); this.checkLVal( node.id, - BIND_TS_ENUM, + isConst ? BIND_TS_CONST_ENUM : BIND_TS_ENUM, undefined, "typescript enum declaration", ); diff --git a/packages/babel-parser/src/plugins/typescript/scope.js b/packages/babel-parser/src/plugins/typescript/scope.js index 949a87ee86c6..f706f1e88e99 100644 --- a/packages/babel-parser/src/plugins/typescript/scope.js +++ b/packages/babel-parser/src/plugins/typescript/scope.js @@ -4,6 +4,7 @@ import ScopeHandler, { Scope } from "../../util/scope"; import { BIND_KIND_TYPE, BIND_FLAGS_TS_ENUM, + BIND_FLAGS_TS_CONST_ENUM, BIND_FLAGS_TS_EXPORT_ONLY, BIND_KIND_VALUE, BIND_FLAGS_CLASS, @@ -18,6 +19,9 @@ class TypeScriptScope extends Scope { // enums (which are also in .types) enums: string[] = []; + // const enums (which are also in .enums and .types) + constEnums: string[] = []; + // classes (which are also in .lexical) and interface (which are also in .types) classes: string[] = []; @@ -55,6 +59,7 @@ export default class TypeScriptScopeHandler extends ScopeHandler -1) { - // Enums can be merged with other enums - return !(bindingType & BIND_FLAGS_TS_ENUM); + if (bindingType & BIND_FLAGS_TS_ENUM) { + // Enums can be merged with other enums if they are both + // const or both non-const. + const isConst = !!(bindingType & BIND_FLAGS_TS_CONST_ENUM); + const wasConst = scope.constEnums.indexOf(name) > -1; + return isConst !== wasConst; + } + return true; } if (bindingType & BIND_FLAGS_CLASS && scope.classes.indexOf(name) > -1) { if (scope.lexical.indexOf(name) > -1) { diff --git a/packages/babel-parser/src/util/scopeflags.js b/packages/babel-parser/src/util/scopeflags.js index 65fe08c1847c..8c9697b3a416 100644 --- a/packages/babel-parser/src/util/scopeflags.js +++ b/packages/babel-parser/src/util/scopeflags.js @@ -37,37 +37,39 @@ export function functionFlags(isAsync: boolean, isGenerator: boolean) { // These flags are meant to be _only_ used inside the Scope class (or subclasses). // prettier-ignore -export const BIND_KIND_VALUE = 0b0000_0000_01, - BIND_KIND_TYPE = 0b0000_0000_10, +export const BIND_KIND_VALUE = 0b00000_0000_01, + BIND_KIND_TYPE = 0b00000_0000_10, // Used in checkLVal and declareName to determine the type of a binding - BIND_SCOPE_VAR = 0b0000_0001_00, // Var-style binding - BIND_SCOPE_LEXICAL = 0b0000_0010_00, // Let- or const-style binding - BIND_SCOPE_FUNCTION = 0b0000_0100_00, // Function declaration - BIND_SCOPE_OUTSIDE = 0b0000_1000_00, // Special case for function names as + BIND_SCOPE_VAR = 0b00000_0001_00, // Var-style binding + BIND_SCOPE_LEXICAL = 0b00000_0010_00, // Let- or const-style binding + BIND_SCOPE_FUNCTION = 0b00000_0100_00, // Function declaration + BIND_SCOPE_OUTSIDE = 0b00000_1000_00, // Special case for function names as // bound inside the function // Misc flags - BIND_FLAGS_NONE = 0b0001_0000_00, - BIND_FLAGS_CLASS = 0b0010_0000_00, - BIND_FLAGS_TS_ENUM = 0b0100_0000_00, - BIND_FLAGS_TS_EXPORT_ONLY = 0b1000_0000_00; + BIND_FLAGS_NONE = 0b00001_0000_00, + BIND_FLAGS_CLASS = 0b00010_0000_00, + BIND_FLAGS_TS_ENUM = 0b00100_0000_00, + BIND_FLAGS_TS_CONST_ENUM = 0b01000_0000_00, + BIND_FLAGS_TS_EXPORT_ONLY = 0b10000_0000_00; // These flags are meant to be _only_ used by Scope consumers // prettier-ignore -/* = is value? | is type? | scope | misc flags */ -export const BIND_CLASS = BIND_KIND_VALUE | BIND_KIND_TYPE | BIND_SCOPE_LEXICAL | BIND_FLAGS_CLASS , - BIND_LEXICAL = BIND_KIND_VALUE | 0 | BIND_SCOPE_LEXICAL | 0 , - BIND_VAR = BIND_KIND_VALUE | 0 | BIND_SCOPE_VAR | 0 , - BIND_FUNCTION = BIND_KIND_VALUE | 0 | BIND_SCOPE_FUNCTION | 0 , - 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, +/* = is value? | is type? | scope | misc flags */ +export const BIND_CLASS = BIND_KIND_VALUE | BIND_KIND_TYPE | BIND_SCOPE_LEXICAL | BIND_FLAGS_CLASS , + BIND_LEXICAL = BIND_KIND_VALUE | 0 | BIND_SCOPE_LEXICAL | 0 , + BIND_VAR = BIND_KIND_VALUE | 0 | BIND_SCOPE_VAR | 0 , + BIND_FUNCTION = BIND_KIND_VALUE | 0 | BIND_SCOPE_FUNCTION | 0 , + 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, // 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_NONE = 0 | 0 | 0 | BIND_FLAGS_NONE , + BIND_OUTSIDE = BIND_KIND_VALUE | 0 | 0 | BIND_FLAGS_NONE , - BIND_TS_NAMESPACE = BIND_TS_FN_TYPE; + BIND_TS_CONST_ENUM = BIND_TS_ENUM | BIND_FLAGS_TS_CONST_ENUM, + BIND_TS_NAMESPACE = BIND_TS_FN_TYPE; export type BindingTypes = | typeof BIND_NONE diff --git a/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-constenum/input.js b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-constenum/input.js new file mode 100644 index 000000000000..cb487f9b6b22 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-constenum/input.js @@ -0,0 +1,2 @@ +const enum Foo {} +const enum Foo {} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-constenum/output.json b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-constenum/output.json new file mode 100644 index 000000000000..2120fa78e381 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-constenum/output.json @@ -0,0 +1,103 @@ +{ + "type": "File", + "start": 0, + "end": 35, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 17 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 35, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 17 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TSEnumDeclaration", + "start": 0, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "const": true, + "id": { + "type": "Identifier", + "start": 11, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 11 + }, + "end": { + "line": 1, + "column": 14 + }, + "identifierName": "Foo" + }, + "name": "Foo" + }, + "members": [] + }, + { + "type": "TSEnumDeclaration", + "start": 18, + "end": 35, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 17 + } + }, + "const": true, + "id": { + "type": "Identifier", + "start": 29, + "end": 32, + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 2, + "column": 14 + }, + "identifierName": "Foo" + }, + "name": "Foo" + }, + "members": [] + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-enum/input.js b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-enum/input.js new file mode 100644 index 000000000000..860bf512749f --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-enum/input.js @@ -0,0 +1,2 @@ +const enum X {} +enum X {} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-enum/options.json b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-enum/options.json new file mode 100644 index 000000000000..ee82975e59a3 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-constenum-enum/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Identifier 'X' has already been declared (2:5)" +} diff --git a/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-enum-constenum/input.js b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-enum-constenum/input.js new file mode 100644 index 000000000000..1b224627f1e8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-enum-constenum/input.js @@ -0,0 +1,2 @@ +enum X {} +const enum X {} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-enum-constenum/options.json b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-enum-constenum/options.json new file mode 100644 index 000000000000..c77fcb7927ac --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-enum-constenum/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Identifier 'X' has already been declared (2:11)" +}