From 9d45aba6942bc691ee35a016aecbc968b50b8a2d Mon Sep 17 00:00:00 2001 From: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> Date: Tue, 31 Jan 2023 15:16:56 +0800 Subject: [PATCH] review and improve --- .../src/const-enum.ts | 2 +- .../src/enum.ts | 93 ++++++++++--------- .../fixtures/enum/mix-references/output.js | 4 +- .../input.ts | 6 ++ .../output.js | 6 +- 5 files changed, 62 insertions(+), 49 deletions(-) rename packages/babel-plugin-transform-typescript/test/fixtures/enum/{outer-const-foldable => ts5.0-const-foldable}/input.ts (73%) rename packages/babel-plugin-transform-typescript/test/fixtures/enum/{outer-const-foldable => ts5.0-const-foldable}/output.js (77%) diff --git a/packages/babel-plugin-transform-typescript/src/const-enum.ts b/packages/babel-plugin-transform-typescript/src/const-enum.ts index 9e388d37a642..dbb04beb0aba 100644 --- a/packages/babel-plugin-transform-typescript/src/const-enum.ts +++ b/packages/babel-plugin-transform-typescript/src/const-enum.ts @@ -27,7 +27,7 @@ export default function transpileConstEnum( ); } - const { nodes: entries } = translateEnumValues(path, t); + const { enumValues: entries } = translateEnumValues(path, t); if (isExported) { const obj = t.objectExpression( diff --git a/packages/babel-plugin-transform-typescript/src/enum.ts b/packages/babel-plugin-transform-typescript/src/enum.ts index 11de316ab90f..182e41d9ed42 100644 --- a/packages/babel-plugin-transform-typescript/src/enum.ts +++ b/packages/babel-plugin-transform-typescript/src/enum.ts @@ -1,6 +1,5 @@ -import { template, types } from "@babel/core"; +import { template, types as t } from "@babel/core"; import type { NodePath } from "@babel/traverse"; -import type * as t from "@babel/types"; import assert from "assert"; type t = typeof t; @@ -84,7 +83,7 @@ const buildEnumMember = (isString: boolean, options: Record) => * `(function (E) { ... assignments ... })(E || (E = {}));` */ function enumFill(path: NodePath, t: t, id: t.Identifier) { - const { nodes: x, data } = translateEnumValues(path, t); + const { enumValues: x, data } = translateEnumValues(path, t); const assignments = x.map(([memberName, memberValue]) => buildEnumMember(t.isStringLiteral(memberValue), { ENUM: t.cloneNode(id), @@ -145,7 +144,7 @@ export function translateEnumValues(path: NodePath, t: t) { return { data: seen, - nodes: path.get("members").map(memberPath => { + enumValues: path.get("members").map(memberPath => { const member = memberPath.node; const name = t.isIdentifier(member.id) ? member.id.name : member.id.value; const initializerPath = memberPath.get("initializer"); @@ -205,31 +204,16 @@ export function translateEnumValues(path: NodePath, t: t) { // Based on the TypeScript repository's `evalConstant` in `checker.ts`. function evaluate( path: NodePath, - seen: PreviousEnumMembers, + prevMembers?: PreviousEnumMembers, + seen: Set = new Set(), ): number | string | undefined { return evalConstant(path); function evalConstant(path: NodePath): number | string | undefined { const expr = path.node; switch (expr.type) { - case "MemberExpression": { - const obj = expr.object; - const prop = expr.property; - if ( - !types.isIdentifier(obj) || - (expr.computed - ? !types.isStringLiteral(prop) - : !types.isIdentifier(prop)) - ) { - return; - } - const bindingIdentifier = path.scope.getBindingIdentifier(obj.name); - const data = ENUMS.get(bindingIdentifier); - if (!data) return; - // @ts-expect-error checked above - return data.get(prop.computed ? prop.value : prop.name); - } - + case "MemberExpression": + return evaluateRef(path, prevMembers, seen); case "StringLiteral": return expr.value; case "UnaryExpression": @@ -240,16 +224,8 @@ function evaluate( return expr.value; case "ParenthesizedExpression": return evalConstant(path.get("expression")); - case "Identifier": { - let value = seen.get(expr.name); - if (value === undefined) { - value = evaluateIdentifier(path); - if (value !== undefined) { - seen.set(expr.name, value); - } - } - return value; - } + case "Identifier": + return evaluateRef(path, prevMembers, seen); case "TemplateLiteral": { if (expr.quasis.length === 1) { return expr.quasis[0].value.cooked; @@ -263,7 +239,7 @@ function evaluate( str += quasis[i].value.cooked; if (i + 1 < quasis.length) { - const value = evaluateIdentifier(paths[i]); + const value = evaluateRef(paths[i], prevMembers, seen); if (value === undefined) return undefined; str += value; } @@ -275,17 +251,44 @@ function evaluate( } } - function evaluateIdentifier(path: NodePath) { - if (path.isIdentifier()) { - const binding = path.scope.getBinding(path.node.name); - if (binding?.kind === "const") { - const result = path.evaluate(); - if ( - result.confident && - (typeof result.value === "number" || typeof result.value === "string") - ) { - return result.value; - } + function evaluateRef( + path: NodePath, + prevMembers: PreviousEnumMembers, + seen: Set, + ): number | string | undefined { + if (path.isMemberExpression()) { + const expr = path.node; + + const obj = expr.object; + const prop = expr.property; + if ( + !t.isIdentifier(obj) || + (expr.computed ? !t.isStringLiteral(prop) : !t.isIdentifier(prop)) + ) { + return; + } + const bindingIdentifier = path.scope.getBindingIdentifier(obj.name); + const data = ENUMS.get(bindingIdentifier); + if (!data) return; + // @ts-expect-error checked above + return data.get(prop.computed ? prop.value : prop.name); + } else if (path.isIdentifier()) { + const name = path.node.name; + + let value = prevMembers?.get(name); + if (value !== undefined) { + return value; + } + + if (seen.has(path.node)) return; + + const bindingInitPath = path.resolve(); //It only resolves constant bindings + if (bindingInitPath) { + seen.add(path.node); + + value = evaluate(bindingInitPath, undefined, seen); + prevMembers?.set(name, value); + return value; } } } 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 a7782acec602..7ac74a98f5d4 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 @@ -3,14 +3,14 @@ var Foo; (function (Foo) { Foo[Foo["a"] = 10] = "a"; Foo[Foo["b"] = 10] = "b"; - Foo[Foo["c"] = Foo.b + x] = "c"; + Foo[Foo["c"] = 20] = "c"; })(Foo || (Foo = {})); var Bar; (function (Bar) { Bar[Bar["D"] = 10] = "D"; Bar[Bar["E"] = 10] = "E"; Bar[Bar["F"] = Math.E] = "F"; - Bar[Bar["G"] = Bar.E + Foo.c] = "G"; + Bar[Bar["G"] = 30] = "G"; })(Bar || (Bar = {})); var Baz; (function (Baz) { diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/outer-const-foldable/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/enum/ts5.0-const-foldable/input.ts similarity index 73% rename from packages/babel-plugin-transform-typescript/test/fixtures/enum/outer-const-foldable/input.ts rename to packages/babel-plugin-transform-typescript/test/fixtures/enum/ts5.0-const-foldable/input.ts index a85e4092eaee..0acbbb970b5d 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/outer-const-foldable/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/ts5.0-const-foldable/input.ts @@ -7,7 +7,13 @@ const enum Values { Second, // 11 Third, // 12 } + +const xxx = 100 + Values.First; +const yyy = xxx; + const enum Routes { Parts = `${Prefix}/parts`, // "/data/parts" Invoices = `${Prefix}/invoices`, // "/data/invoices" + x = `${Values.First}/x`, + y = `${yyy}/y`, } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/enum/outer-const-foldable/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/enum/ts5.0-const-foldable/output.js similarity index 77% rename from packages/babel-plugin-transform-typescript/test/fixtures/enum/outer-const-foldable/output.js rename to packages/babel-plugin-transform-typescript/test/fixtures/enum/ts5.0-const-foldable/output.js index 33227ac5fe26..e839254369de 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/enum/outer-const-foldable/output.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/enum/ts5.0-const-foldable/output.js @@ -8,8 +8,12 @@ var Values; // 12 Values[Values["Second"] = 11] = "Second"; Values[Values["Third"] = 12] = "Third"; })(Values || (Values = {})); -var Routes; // "/data/invoices" +const xxx = 100 + Values.First; +const yyy = xxx; +var Routes; (function (Routes) { Routes["Parts"] = "/data/parts"; Routes["Invoices"] = "/data/invoices"; + Routes["x"] = "10/x"; + Routes["y"] = "110/y"; })(Routes || (Routes = {}));