From 1326eaa76d8891257f0897baeb95eb95ca6033a8 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 1 Jan 2022 03:37:01 +0800 Subject: [PATCH 1/6] Fix TypeScript Enum Reference --- .../src/enum.ts | 21 ++++++++++++++++++- .../fixtures/enum/mix-references/input.ts | 6 ++++++ .../fixtures/enum/mix-references/output.js | 8 +++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js diff --git a/packages/babel-plugin-transform-typescript/src/enum.ts b/packages/babel-plugin-transform-typescript/src/enum.ts index dc13399a80d7..2bd328b3d8cf 100644 --- a/packages/babel-plugin-transform-typescript/src/enum.ts +++ b/packages/babel-plugin-transform-typescript/src/enum.ts @@ -1,7 +1,8 @@ import assert from "assert"; import { template } from "@babel/core"; import type * as t from "@babel/types"; -import type { NodePath } from "@babel/traverse"; +import traverse from "@babel/traverse"; +import type { NodePath, Visitor } from "@babel/traverse"; export default function transpileEnum(path, t) { const { node } = path; @@ -124,6 +125,24 @@ export function translateEnumValues( value = t.stringLiteral(constValue); } } else { + const IdentifierVisitor: Visitor = { + Identifier(expr) { + if (seen.has(expr.node.name)) { + expr.replaceWith( + t.memberExpression( + t.cloneNode(path.node.id), + t.cloneNode(expr.node), + ), + ); + } + }, + MemberExpression(expr) { + expr.skip(); + }, + }; + + traverse(initializer, IdentifierVisitor, path.scope); + value = initializer; } } else if (typeof constValue === "number") { diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts new file mode 100644 index 000000000000..2a9cff55240d --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts @@ -0,0 +1,6 @@ +var x = 10; +enum Foo { + a = 10, + b = a, + c = b + x, +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js new file mode 100644 index 000000000000..3c996e055dc2 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js @@ -0,0 +1,8 @@ +var x = 10; +var Foo; + +(function (Foo) { + Foo[Foo["a"] = 10] = "a"; + Foo[Foo["b"] = 10] = "b"; + Foo[Foo["c"] = Foo.b + x] = "c"; +})(Foo || (Foo = {})); From 83c7fa0130dceaf376b2fe8e46faaf16b25539ac Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 1 Jan 2022 09:17:43 +0800 Subject: [PATCH 2/6] Update enum tests --- .../test/fixtures/enum/mix-references/input.ts | 8 +++++++- .../test/fixtures/enum/mix-references/output.js | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts index 2a9cff55240d..269168f11253 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts @@ -1,6 +1,12 @@ var x = 10; + +enum Bar { + d = 1, +} + enum Foo { a = 10, b = a, c = b + x, -} + d = Bar.d, +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js index 3c996e055dc2..dfbd936e99da 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js @@ -1,8 +1,15 @@ var x = 10; +var Bar; + +(function (Bar) { + Bar[Bar["d"] = 1] = "d"; +})(Bar || (Bar = {})); + var Foo; (function (Foo) { Foo[Foo["a"] = 10] = "a"; Foo[Foo["b"] = 10] = "b"; Foo[Foo["c"] = Foo.b + x] = "c"; + Foo[Foo["d"] = Bar.d] = "d"; })(Foo || (Foo = {})); From 40c50c27123b7e5bfb4bfad4a0f830ca4af4019b Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 1 Jan 2022 09:26:34 +0800 Subject: [PATCH 3/6] Update enum tests --- .../test/fixtures/enum/mix-references/input.ts | 11 ++++++----- .../test/fixtures/enum/mix-references/output.js | 15 ++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts index 269168f11253..2ba9f0033607 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts @@ -1,12 +1,13 @@ var x = 10; -enum Bar { - d = 1, -} - enum Foo { a = 10, b = a, c = b + x, - d = Bar.d, +} + +enum Bar { + b = Foo.a, + E = b, + F = Math.E, } \ No newline at end of file diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js index dfbd936e99da..68dcae7c3c5d 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js @@ -1,15 +1,16 @@ var x = 10; -var Bar; - -(function (Bar) { - Bar[Bar["d"] = 1] = "d"; -})(Bar || (Bar = {})); - var Foo; (function (Foo) { Foo[Foo["a"] = 10] = "a"; Foo[Foo["b"] = 10] = "b"; Foo[Foo["c"] = Foo.b + x] = "c"; - Foo[Foo["d"] = Bar.d] = "d"; })(Foo || (Foo = {})); + +var Bar; + +(function (Bar) { + Bar[Bar["b"] = Foo.a] = "b"; + Bar[Bar["E"] = b] = "E"; + Bar[Bar["F"] = Math.E] = "F"; +})(Bar || (Bar = {})); From 933d994a2dc0a339834b68deed96d0d3885319d3 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 1 Jan 2022 13:58:09 +0800 Subject: [PATCH 4/6] FIX TypeScript Enum --- packages/babel-plugin-transform-typescript/src/enum.ts | 9 +++++++-- .../test/fixtures/enum/mix-references/input.ts | 7 ++++--- .../test/fixtures/enum/mix-references/output.js | 5 +++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/enum.ts b/packages/babel-plugin-transform-typescript/src/enum.ts index 2bd328b3d8cf..8598a5212be3 100644 --- a/packages/babel-plugin-transform-typescript/src/enum.ts +++ b/packages/babel-plugin-transform-typescript/src/enum.ts @@ -134,6 +134,7 @@ export function translateEnumValues( t.cloneNode(expr.node), ), ); + expr.skip(); } }, MemberExpression(expr) { @@ -141,9 +142,12 @@ export function translateEnumValues( }, }; - traverse(initializer, IdentifierVisitor, path.scope); + const exprStmt = t.expressionStatement(initializer); - value = initializer; + traverse(t.program([exprStmt]), IdentifierVisitor); + + value = exprStmt.expression; + seen.set(name, undefined); } } else if (typeof constValue === "number") { constValue += 1; @@ -159,6 +163,7 @@ export function translateEnumValues( true, ); value = t.binaryExpression("+", t.numericLiteral(1), lastRef); + seen.set(name, undefined); } lastName = name; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts index 2ba9f0033607..15c7541e304e 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts @@ -7,7 +7,8 @@ enum Foo { } enum Bar { - b = Foo.a, - E = b, + D = Foo.a, + E = D, F = Math.E, -} \ No newline at end of file + G = E + Foo.c, +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js index 68dcae7c3c5d..5557ea87ef38 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js @@ -10,7 +10,8 @@ var Foo; var Bar; (function (Bar) { - Bar[Bar["b"] = Foo.a] = "b"; - Bar[Bar["E"] = b] = "E"; + Bar[Bar["D"] = Foo.a] = "D"; + Bar[Bar["E"] = Bar.D] = "E"; Bar[Bar["F"] = Math.E] = "F"; + Bar[Bar["G"] = Bar.E + Foo.c] = "G"; })(Bar || (Bar = {})); From 741614d80e6f01b66f50589387bc105d9a259964 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 1 Jan 2022 21:35:56 +0800 Subject: [PATCH 5/6] Refactor TypeScript Enum Reference --- .../src/enum.ts | 62 +++++++++++-------- .../fixtures/enum/mix-references/input.ts | 7 +++ .../fixtures/enum/mix-references/output.js | 8 +++ 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/enum.ts b/packages/babel-plugin-transform-typescript/src/enum.ts index 8598a5212be3..59fd56dcd691 100644 --- a/packages/babel-plugin-transform-typescript/src/enum.ts +++ b/packages/babel-plugin-transform-typescript/src/enum.ts @@ -1,8 +1,7 @@ -import assert from "assert"; import { template } from "@babel/core"; -import type * as t from "@babel/types"; -import traverse from "@babel/traverse"; -import type { NodePath, Visitor } from "@babel/traverse"; +import type { NodePath } from "@babel/traverse"; +import * as t from "@babel/types"; +import assert from "assert"; export default function transpileEnum(path, t) { const { node } = path; @@ -101,6 +100,28 @@ function enumFill(path, t, id) { */ type PreviousEnumMembers = Map; +type EnumSelfReferenceVisitorState = { + seen: PreviousEnumMembers; + path: NodePath; +}; + +function ReferencedIdentifier( + expr: NodePath, + state: EnumSelfReferenceVisitorState, +) { + const { seen, path } = state; + if (expr.isIdentifier() && seen.has(expr.node.name)) { + expr.replaceWith( + t.memberExpression(t.cloneNode(path.node.id), t.cloneNode(expr.node)), + ); + expr.skip(); + } +} + +const enumSelfReferenceVisitor = { + ReferencedIdentifier, +}; + export function translateEnumValues( path: NodePath, t: typeof import("@babel/types"), @@ -110,7 +131,8 @@ export function translateEnumValues( let constValue: number | string | undefined = -1; let lastName: string; - return path.node.members.map(member => { + return path.get("members").map(memberPath => { + const member = memberPath.node; const name = t.isIdentifier(member.id) ? member.id.name : member.id.value; const initializer = member.initializer; let value: t.Expression; @@ -125,28 +147,18 @@ export function translateEnumValues( value = t.stringLiteral(constValue); } } else { - const IdentifierVisitor: Visitor = { - Identifier(expr) { - if (seen.has(expr.node.name)) { - expr.replaceWith( - t.memberExpression( - t.cloneNode(path.node.id), - t.cloneNode(expr.node), - ), - ); - expr.skip(); - } - }, - MemberExpression(expr) { - expr.skip(); - }, - }; - - const exprStmt = t.expressionStatement(initializer); + const initializerPath = memberPath.get("initializer"); - traverse(t.program([exprStmt]), IdentifierVisitor); + if (initializerPath.isReferencedIdentifier()) { + ReferencedIdentifier(initializerPath, { + seen, + path, + }); + } else { + initializerPath.traverse(enumSelfReferenceVisitor, { seen, path }); + } - value = exprStmt.expression; + value = initializerPath.node; seen.set(name, undefined); } } else if (typeof constValue === "number") { diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts index 15c7541e304e..e75d18715056 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts @@ -12,3 +12,10 @@ enum Bar { F = Math.E, G = E + Foo.c, } + +enum Baz { + a = 0, + b = 1, + // @ts-ignore + x = a.toString(), +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js index 5557ea87ef38..c36a3f16aa2e 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js @@ -15,3 +15,11 @@ var Bar; Bar[Bar["F"] = Math.E] = "F"; Bar[Bar["G"] = Bar.E + Foo.c] = "G"; })(Bar || (Bar = {})); + +var Baz; + +(function (Baz) { + Baz[Baz["a"] = 0] = "a"; + Baz[Baz["b"] = 1] = "b"; + Baz[Baz["x"] = Baz.a.toString()] = "x"; +})(Baz || (Baz = {})); From 2c8362f5d7292a0657fc4358f2b5e3eb97439879 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Mon, 3 Jan 2022 20:51:55 +0800 Subject: [PATCH 6/6] fix: resolve shadow binding --- .../src/enum.ts | 26 ++++++++++++------- .../fixtures/enum/mix-references/input.ts | 6 +++++ .../fixtures/enum/mix-references/output.js | 13 ++++++++++ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/enum.ts b/packages/babel-plugin-transform-typescript/src/enum.ts index 59fd56dcd691..79368a06656a 100644 --- a/packages/babel-plugin-transform-typescript/src/enum.ts +++ b/packages/babel-plugin-transform-typescript/src/enum.ts @@ -1,9 +1,14 @@ import { template } from "@babel/core"; import type { NodePath } from "@babel/traverse"; -import * as t from "@babel/types"; +import type * as t from "@babel/types"; import assert from "assert"; -export default function transpileEnum(path, t) { +type t = typeof t; + +export default function transpileEnum( + path: NodePath, + t: t, +) { const { node } = path; if (node.declare) { @@ -48,7 +53,7 @@ export default function transpileEnum(path, t) { } } -function makeVar(id, t, kind) { +function makeVar(id: t.Identifier, t: t, kind: "var" | "let" | "const") { return t.variableDeclaration(kind, [t.variableDeclarator(id)]); } @@ -66,14 +71,14 @@ const buildNumericAssignment = template(` ENUM[ENUM["NAME"] = VALUE] = "NAME"; `); -const buildEnumMember = (isString, options) => +const buildEnumMember = (isString: boolean, options: Record) => (isString ? buildStringAssignment : buildNumericAssignment)(options); /** * Generates the statement that fills in the variable declared by the enum. * `(function (E) { ... assignments ... })(E || (E = {}));` */ -function enumFill(path, t, id) { +function enumFill(path: NodePath, t: t, id: t.Identifier) { const x = translateEnumValues(path, t); const assignments = x.map(([memberName, memberValue]) => buildEnumMember(t.isStringLiteral(memberValue), { @@ -103,14 +108,16 @@ type PreviousEnumMembers = Map; type EnumSelfReferenceVisitorState = { seen: PreviousEnumMembers; path: NodePath; + t: t; }; function ReferencedIdentifier( expr: NodePath, state: EnumSelfReferenceVisitorState, ) { - const { seen, path } = state; - if (expr.isIdentifier() && seen.has(expr.node.name)) { + const { seen, path, t } = state; + const name = expr.node.name; + if (seen.has(name) && !expr.scope.hasOwnBinding(name)) { expr.replaceWith( t.memberExpression(t.cloneNode(path.node.id), t.cloneNode(expr.node)), ); @@ -124,7 +131,7 @@ const enumSelfReferenceVisitor = { export function translateEnumValues( path: NodePath, - t: typeof import("@babel/types"), + t: t, ): Array<[name: string, value: t.Expression]> { const seen: PreviousEnumMembers = new Map(); // Start at -1 so the first enum member is its increment, 0. @@ -151,11 +158,12 @@ export function translateEnumValues( if (initializerPath.isReferencedIdentifier()) { ReferencedIdentifier(initializerPath, { + t, seen, path, }); } else { - initializerPath.traverse(enumSelfReferenceVisitor, { seen, path }); + initializerPath.traverse(enumSelfReferenceVisitor, { t, seen, path }); } value = initializerPath.node; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts index e75d18715056..3f2b46e88a8e 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts @@ -18,4 +18,10 @@ enum Baz { b = 1, // @ts-ignore x = a.toString(), +} + +enum A { + a = 0, + b = (() => { let a = 1; return a + 1 })(), // a is shadowed + c = (() => { return a + 2 })(), // a refers to A.a } \ No newline at end of file diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js index c36a3f16aa2e..13664519e9e5 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/output.js @@ -23,3 +23,16 @@ var Baz; Baz[Baz["b"] = 1] = "b"; Baz[Baz["x"] = Baz.a.toString()] = "x"; })(Baz || (Baz = {})); + +var A; + +(function (A) { + A[A["a"] = 0] = "a"; + A[A["b"] = (() => { + let a = 1; + return a + 1; + })()] = "b"; + A[A["c"] = (() => { + return A.a + 2; + })()] = "c"; +})(A || (A = {}));