fix: allow enum member without initializer after non-literal member (#13865)

This commit is contained in:
Mickey Rose 2021-10-22 14:52:23 +02:00 committed by GitHub
parent c75fa21cb8
commit cfe6739dc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 21 deletions

View File

@ -98,47 +98,51 @@ function enumFill(path, t, id) {
* Z = X | Y, * Z = X | Y,
* } * }
*/ */
type PreviousEnumMembers = { type PreviousEnumMembers = Map<string, number | string>;
[name: string]: number | string;
};
export function translateEnumValues( export function translateEnumValues(
path: NodePath<t.TSEnumDeclaration>, path: NodePath<t.TSEnumDeclaration>,
t: typeof import("@babel/types"), t: typeof import("@babel/types"),
): Array<[name: string, value: t.Expression]> { ): Array<[name: string, value: t.Expression]> {
const seen: PreviousEnumMembers = Object.create(null); const seen: PreviousEnumMembers = new Map();
// Start at -1 so the first enum member is its increment, 0. // Start at -1 so the first enum member is its increment, 0.
let prev: number | typeof undefined = -1; let constValue: number | string | undefined = -1;
let lastName: string;
return path.node.members.map(member => { return path.node.members.map(member => {
const name = t.isIdentifier(member.id) ? member.id.name : member.id.value; const name = t.isIdentifier(member.id) ? member.id.name : member.id.value;
const initializer = member.initializer; const initializer = member.initializer;
let value: t.Expression; let value: t.Expression;
if (initializer) { if (initializer) {
const constValue = evaluate(initializer, seen); constValue = evaluate(initializer, seen);
if (constValue !== undefined) { if (constValue !== undefined) {
seen[name] = constValue; seen.set(name, constValue);
if (typeof constValue === "number") { if (typeof constValue === "number") {
value = t.numericLiteral(constValue); value = t.numericLiteral(constValue);
prev = constValue;
} else { } else {
assert(typeof constValue === "string"); assert(typeof constValue === "string");
value = t.stringLiteral(constValue); value = t.stringLiteral(constValue);
prev = undefined;
} }
} else { } else {
value = initializer; value = initializer;
prev = 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 { } else {
if (prev !== undefined) { // create dynamic initializer: 1 + ENUM["PREVIOUS"]
prev++; const lastRef = t.memberExpression(
value = t.numericLiteral(prev); t.cloneNode(path.node.id),
seen[name] = prev; t.stringLiteral(lastName),
} else { true,
throw path.buildCodeFrameError("Enum member must have initializer."); );
} value = t.binaryExpression("+", t.numericLiteral(1), lastRef);
} }
lastName = name;
return [name, value]; return [name, value];
}); });
} }
@ -163,7 +167,7 @@ function evaluate(
case "ParenthesizedExpression": case "ParenthesizedExpression":
return evalConstant(expr.expression); return evalConstant(expr.expression);
case "Identifier": case "Identifier":
return seen[expr.name]; return seen.get(expr.name);
case "TemplateLiteral": case "TemplateLiteral":
if (expr.quasis.length === 1) { if (expr.quasis.length === 1) {
return expr.quasis[0].value.cooked; return expr.quasis[0].value.cooked;

View File

@ -1,3 +0,0 @@
{
"throws": "Enum member must have initializer."
}

View File

@ -0,0 +1,6 @@
var E;
(function (E) {
E[E["a"] = Math.sin(1)] = "a";
E[E["b"] = 1 + E["a"]] = "b";
})(E || (E = {}));

View File

@ -0,0 +1,13 @@
enum socketType {
SOCKET,
SERVER,
IPC,
}
enum constants {
SOCKET = socketType.SOCKET,
SERVER = socketType.SERVER,
IPC = socketType.IPC,
UV_READABLE,
UV_WRITABLE,
}

View File

@ -0,0 +1,17 @@
var socketType;
(function (socketType) {
socketType[socketType["SOCKET"] = 0] = "SOCKET";
socketType[socketType["SERVER"] = 1] = "SERVER";
socketType[socketType["IPC"] = 2] = "IPC";
})(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 = {}));