Skip to content

Commit

Permalink
improve
Browse files Browse the repository at this point in the history
  • Loading branch information
liuxingbaoyu committed Jan 30, 2023
1 parent 26d7793 commit 267299e
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 62 deletions.
Expand Up @@ -27,7 +27,7 @@ export default function transpileConstEnum(
);
}

const entries = translateEnumValues(path, t);
const { nodes: entries } = translateEnumValues(path, t);

if (isExported) {
const obj = t.objectExpression(
Expand Down
135 changes: 81 additions & 54 deletions packages/babel-plugin-transform-typescript/src/enum.ts
@@ -1,10 +1,13 @@
import { template } from "@babel/core";
import type { NodePath } from "@babel/traverse";
import type * as t from "@babel/types";
import { isIdentifier, isStringLiteral } from "@babel/types";
import assert from "assert";

type t = typeof t;

const ENUMS = new WeakMap<t.Identifier, PreviousEnumMembers>();

export default function transpileEnum(
path: NodePath<t.TSEnumDeclaration>,
t: t,
Expand All @@ -17,7 +20,7 @@ export default function transpileEnum(
}

const name = node.id.name;
const fill = enumFill(path, t, node.id);
const { wrapper: fill, data } = enumFill(path, t, node.id);

switch (path.parent.type) {
case "BlockStatement":
Expand All @@ -33,6 +36,7 @@ export default function transpileEnum(
path.scope.registerDeclaration(
path.replaceWith(makeVar(node.id, t, isGlobal ? "var" : "let"))[0],
);
ENUMS.set(path.scope.getBindingIdentifier(name), data);
}
break;
}
Expand Down Expand Up @@ -81,7 +85,7 @@ const buildEnumMember = (isString: boolean, options: Record<string, unknown>) =>
* `(function (E) { ... assignments ... })(E || (E = {}));`
*/
function enumFill(path: NodePath<t.TSEnumDeclaration>, t: t, id: t.Identifier) {
const x = translateEnumValues(path, t);
const { nodes: x, data } = translateEnumValues(path, t);
const assignments = x.map(([memberName, memberValue]) =>
buildEnumMember(t.isStringLiteral(memberValue), {
ENUM: t.cloneNode(id),
Expand All @@ -90,10 +94,13 @@ function enumFill(path: NodePath<t.TSEnumDeclaration>, t: t, id: t.Identifier) {
}),
);

return buildEnumWrapper({
ID: t.cloneNode(id),
ASSIGNMENTS: assignments,
});
return {
wrapper: buildEnumWrapper({
ID: t.cloneNode(id),
ASSIGNMENTS: assignments,
}),
data: data,
};
}

/**
Expand Down Expand Up @@ -131,65 +138,69 @@ const enumSelfReferenceVisitor = {
ReferencedIdentifier,
};

export function translateEnumValues(
path: NodePath<t.TSEnumDeclaration>,
t: t,
): Array<[name: string, value: t.Expression]> {
export function translateEnumValues(path: NodePath<t.TSEnumDeclaration>, t: t) {
const seen: PreviousEnumMembers = new Map();
// Start at -1 so the first enum member is its increment, 0.
let constValue: number | string | undefined = -1;
let lastName: string;

return 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");
const initializer = member.initializer;
let value: t.Expression;
if (initializer) {
constValue = evaluate(initializerPath, seen);
if (constValue !== undefined) {
seen.set(name, constValue);
if (typeof constValue === "number") {
value = t.numericLiteral(constValue);
return {
data: seen,
nodes: 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");
const initializer = member.initializer;
let value: t.Expression;
if (initializer) {
constValue = evaluate(initializerPath, seen);
if (constValue !== undefined) {
seen.set(name, constValue);
if (typeof constValue === "number") {
value = t.numericLiteral(constValue);
} else {
assert(typeof constValue === "string");
value = t.stringLiteral(constValue);
}
} else {
assert(typeof constValue === "string");
value = t.stringLiteral(constValue);
if (initializerPath.isReferencedIdentifier()) {
ReferencedIdentifier(initializerPath, {
t,
seen,
path,
});
} else {
initializerPath.traverse(enumSelfReferenceVisitor, {
t,
seen,
path,
});
}

value = initializerPath.node;
seen.set(name, undefined);
}
} else if (typeof constValue === "number") {
constValue += 1;
value = t.numericLiteral(constValue);
seen.set(name, constValue);
} else if (typeof constValue === "string") {
throw path.buildCodeFrameError("Enum member must have initializer.");
} else {
if (initializerPath.isReferencedIdentifier()) {
ReferencedIdentifier(initializerPath, {
t,
seen,
path,
});
} else {
initializerPath.traverse(enumSelfReferenceVisitor, { t, seen, path });
}

value = initializerPath.node;
// create dynamic initializer: 1 + ENUM["PREVIOUS"]
const lastRef = t.memberExpression(
t.cloneNode(path.node.id),
t.stringLiteral(lastName),
true,
);
value = t.binaryExpression("+", t.numericLiteral(1), lastRef);
seen.set(name, undefined);
}
} else if (typeof constValue === "number") {
constValue += 1;
value = t.numericLiteral(constValue);
seen.set(name, constValue);
} else if (typeof constValue === "string") {
throw path.buildCodeFrameError("Enum member must have initializer.");
} else {
// create dynamic initializer: 1 + ENUM["PREVIOUS"]
const lastRef = t.memberExpression(
t.cloneNode(path.node.id),
t.stringLiteral(lastName),
true,
);
value = t.binaryExpression("+", t.numericLiteral(1), lastRef);
seen.set(name, undefined);
}

lastName = name;
return [name, value];
});
lastName = name;
return [name, value];
}) as Array<[name: string, value: t.Expression]>,
};
}

// Based on the TypeScript repository's `evalConstant` in `checker.ts`.
Expand All @@ -202,6 +213,22 @@ function evaluate(
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 (
!isIdentifier(obj) ||
(expr.computed ? !isStringLiteral(prop) : !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 "StringLiteral":
return expr.value;
case "UnaryExpression":
Expand Down
Expand Up @@ -7,8 +7,8 @@ var Foo;
})(Foo || (Foo = {}));
var Bar;
(function (Bar) {
Bar[Bar["D"] = Foo.a] = "D";
Bar[Bar["E"] = Bar.D] = "E";
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 = {}));
Expand Down
Expand Up @@ -6,9 +6,9 @@ var socketType;
})(socketType || (socketType = {}));
var constants;
(function (constants) {
constants[constants["SOCKET"] = socketType.SOCKET] = "SOCKET";
constants[constants["SERVER"] = socketType.SERVER] = "SERVER";
constants[constants["IPC"] = socketType.IPC] = "IPC";
constants[constants["UV_READABLE"] = 1 + constants["IPC"]] = "UV_READABLE";
constants[constants["UV_WRITABLE"] = 1 + constants["UV_READABLE"]] = "UV_WRITABLE";
constants[constants["SOCKET"] = 0] = "SOCKET";
constants[constants["SERVER"] = 1] = "SERVER";
constants[constants["IPC"] = 2] = "IPC";
constants[constants["UV_READABLE"] = 3] = "UV_READABLE";
constants[constants["UV_WRITABLE"] = 4] = "UV_WRITABLE";
})(constants || (constants = {}));

0 comments on commit 267299e

Please sign in to comment.