All files / style9/src/helpers translate-enum-values.js

31.25% Statements 20/64
11.9% Branches 5/42
66.67% Functions 4/6
31.25% Lines 20/64

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134                8x 8x   8x 1x   1x 1x 1x 1x   1x 1x 1x 1x 1x       1x 1x 1x                               1x           1x     1x   1x                                                                                                                                                          
/*
MIT License
 
Copyright (c) 2014-present Sebastian McKenzie and other contributors
 
https://git.io/JtdzW
*/
 
const assert = require('assert');
const t = require('@babel/types');
 
module.exports = function translateEnumValues(path) {
  const seen = Object.create(null);
  // Start at -1 so the first enum member is its increment, 0.
  let prev = -1;
  return path.node.members.map(member => {
    const name = t.isIdentifier(member.id) ? member.id.name : member.id.value;
    const initializer = member.initializer;
    let value;
    Eif (initializer) {
      const constValue = evaluate(initializer, seen);
      Eif (constValue !== undefined) {
        seen[name] = constValue;
        Iif (typeof constValue === 'number') {
          value = t.numericLiteral(constValue);
          prev = constValue;
        } else {
          assert(typeof constValue === 'string');
          value = t.stringLiteral(constValue);
          prev = undefined;
        }
      } else {
        value = initializer;
        prev = undefined;
      }
    } else {
      if (prev !== undefined) {
        prev++;
        value = t.numericLiteral(prev);
        seen[name] = prev;
      } else {
        throw path.buildCodeFrameError('Enum member must have initializer.');
      }
    }
 
    return [name, value];
  });
};
 
// Based on the TypeScript repository's `evalConstant` in `checker.ts`.
function evaluate(expr, seen) {
  return evalConstant(expr);
 
  function evalConstant(expr) {
    switch (expr.type) {
      case 'StringLiteral':
        return expr.value;
      case 'UnaryExpression':
        return evalUnaryExpression(expr);
      case 'BinaryExpression':
        return evalBinaryExpression(expr);
      case 'NumericLiteral':
        return expr.value;
      case 'ParenthesizedExpression':
        return evalConstant(expr.expression);
      case 'Identifier':
        return seen[expr.name];
      case 'TemplateLiteral':
        if (expr.quasis.length === 1) {
          return expr.quasis[0].value.cooked;
        }
      /* falls through */
      default:
        return undefined;
    }
  }
 
  function evalUnaryExpression({ argument, operator }) {
    const value = evalConstant(argument);
    if (value === undefined) {
      return undefined;
    }
 
    switch (operator) {
      case '+':
        return value;
      case '-':
        return -value;
      case '~':
        return ~value;
      default:
        return undefined;
    }
  }
 
  function evalBinaryExpression(expr) {
    const left = evalConstant(expr.left);
    if (left === undefined) {
      return undefined;
    }
    const right = evalConstant(expr.right);
    if (right === undefined) {
      return undefined;
    }
 
    switch (expr.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;
      default:
        return undefined;
    }
  }
}