astro-ghostcms/.pnpm-store/v3/files/e3/9a4c559710f4ee85e96c839f64a...

303 lines
8.9 KiB
Plaintext

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = transpileEnum;
exports.translateEnumValues = translateEnumValues;
var _core = require("@babel/core");
var _assert = require("assert");
var _helperAnnotateAsPure = require("@babel/helper-annotate-as-pure");
const ENUMS = new WeakMap();
const buildEnumWrapper = _core.template.expression(`
(function (ID) {
ASSIGNMENTS;
return ID;
})(INIT)
`);
function transpileEnum(path, t) {
const {
node,
parentPath
} = path;
if (node.declare) {
path.remove();
return;
}
const name = node.id.name;
const {
fill,
data,
isPure
} = enumFill(path, t, node.id);
switch (parentPath.type) {
case "BlockStatement":
case "ExportNamedDeclaration":
case "Program":
{
const isGlobal = t.isProgram(path.parent);
const isSeen = seen(parentPath);
let init = t.objectExpression([]);
if (isSeen || isGlobal) {
init = t.logicalExpression("||", t.cloneNode(fill.ID), init);
}
const enumIIFE = buildEnumWrapper(Object.assign({}, fill, {
INIT: init
}));
if (isPure) (0, _helperAnnotateAsPure.default)(enumIIFE);
if (isSeen) {
const toReplace = parentPath.isExportDeclaration() ? parentPath : path;
toReplace.replaceWith(t.expressionStatement(t.assignmentExpression("=", t.cloneNode(node.id), enumIIFE)));
} else {
path.scope.registerDeclaration(path.replaceWith(t.variableDeclaration(isGlobal ? "var" : "let", [t.variableDeclarator(node.id, enumIIFE)]))[0]);
}
ENUMS.set(path.scope.getBindingIdentifier(name), data);
break;
}
default:
throw new Error(`Unexpected enum parent '${path.parent.type}`);
}
function seen(parentPath) {
if (parentPath.isExportDeclaration()) {
return seen(parentPath.parentPath);
}
if (parentPath.getData(name)) {
return true;
} else {
parentPath.setData(name, true);
return false;
}
}
}
const buildStringAssignment = (0, _core.template)(`
ENUM["NAME"] = VALUE;
`);
const buildNumericAssignment = (0, _core.template)(`
ENUM[ENUM["NAME"] = VALUE] = "NAME";
`);
const buildEnumMember = (isString, options) => (isString ? buildStringAssignment : buildNumericAssignment)(options);
function enumFill(path, t, id) {
const {
enumValues: x,
data,
isPure
} = translateEnumValues(path, t);
const assignments = x.map(([memberName, memberValue]) => buildEnumMember(t.isStringLiteral(memberValue), {
ENUM: t.cloneNode(id),
NAME: memberName,
VALUE: memberValue
}));
return {
fill: {
ID: t.cloneNode(id),
ASSIGNMENTS: assignments
},
data,
isPure
};
}
function ReferencedIdentifier(expr, state) {
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)));
expr.skip();
}
}
const enumSelfReferenceVisitor = {
ReferencedIdentifier
};
function translateEnumValues(path, t) {
var _ENUMS$get;
const bindingIdentifier = path.scope.getBindingIdentifier(path.node.id.name);
const seen = (_ENUMS$get = ENUMS.get(bindingIdentifier)) != null ? _ENUMS$get : new Map();
let constValue = -1;
let lastName;
let isPure = true;
const 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");
const initializer = member.initializer;
let value;
if (initializer) {
constValue = computeConstantValue(initializerPath, seen);
if (constValue !== undefined) {
seen.set(name, constValue);
_assert(typeof constValue === "number" || typeof constValue === "string");
if (constValue === Infinity || Number.isNaN(constValue)) {
value = t.identifier(String(constValue));
} else if (constValue === -Infinity) {
value = t.unaryExpression("-", t.identifier("Infinity"));
} else {
value = t.valueToNode(constValue);
}
} else {
isPure && (isPure = initializerPath.isPure());
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 {
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];
});
return {
isPure,
data: seen,
enumValues
};
}
function computeConstantValue(path, prevMembers, seen = new Set()) {
return evaluate(path);
function evaluate(path) {
const expr = path.node;
switch (expr.type) {
case "MemberExpression":
return evaluateRef(path, prevMembers, seen);
case "StringLiteral":
return expr.value;
case "UnaryExpression":
return evalUnaryExpression(path);
case "BinaryExpression":
return evalBinaryExpression(path);
case "NumericLiteral":
return expr.value;
case "ParenthesizedExpression":
return evaluate(path.get("expression"));
case "Identifier":
return evaluateRef(path, prevMembers, seen);
case "TemplateLiteral":
{
if (expr.quasis.length === 1) {
return expr.quasis[0].value.cooked;
}
const paths = path.get("expressions");
const quasis = expr.quasis;
let str = "";
for (let i = 0; i < quasis.length; i++) {
str += quasis[i].value.cooked;
if (i + 1 < quasis.length) {
const value = evaluateRef(paths[i], prevMembers, seen);
if (value === undefined) return undefined;
str += value;
}
}
return str;
}
default:
return undefined;
}
}
function evaluateRef(path, prevMembers, seen) {
if (path.isMemberExpression()) {
const expr = path.node;
const obj = expr.object;
const prop = expr.property;
if (!_core.types.isIdentifier(obj) || (expr.computed ? !_core.types.isStringLiteral(prop) : !_core.types.isIdentifier(prop))) {
return;
}
const bindingIdentifier = path.scope.getBindingIdentifier(obj.name);
const data = ENUMS.get(bindingIdentifier);
if (!data) return;
return data.get(prop.computed ? prop.value : prop.name);
} else if (path.isIdentifier()) {
const name = path.node.name;
if (["Infinity", "NaN"].includes(name)) {
return Number(name);
}
let value = prevMembers == null ? void 0 : prevMembers.get(name);
if (value !== undefined) {
return value;
}
if (seen.has(path.node)) return;
seen.add(path.node);
value = computeConstantValue(path.resolve(), prevMembers, seen);
prevMembers == null || prevMembers.set(name, value);
return value;
}
}
function evalUnaryExpression(path) {
const value = evaluate(path.get("argument"));
if (value === undefined) {
return undefined;
}
switch (path.node.operator) {
case "+":
return value;
case "-":
return -value;
case "~":
return ~value;
default:
return undefined;
}
}
function evalBinaryExpression(path) {
const left = evaluate(path.get("left"));
if (left === undefined) {
return undefined;
}
const right = evaluate(path.get("right"));
if (right === undefined) {
return undefined;
}
switch (path.node.operator) {
case "|":
return left | right;
case "&":
return left & right;
case ">>":
return left >> right;
case ">>>":
return left >>> right;
case "<<":
return left << right;
case "^":
return left ^ right;
case "*":
return left * right;
case "/":
return left / right;
case "+":
return left + right;
case "-":
return left - right;
case "%":
return left % right;
case "**":
return Math.pow(left, right);
default:
return undefined;
}
}
}
//# sourceMappingURL=enum.js.map