Source: constant-folding.js

const escodegen = require("escodegen");
const espree = require("espree");
const estraverse = require("estraverse");
const { isConstant, newArrayNode, newNode, getValue } = require("./utils");

"use strict";

module.exports = constantFolding;

/**
 * A function that does constant folding onto the code it receives
 * @param {string} code A string containing the code to do the constant folding on
 * @returns {string} Returns the resulting code
 */
function constantFolding(code) {
  const t = espree.parse(code, { ecmaVersion: 6, loc: false });
  estraverse.traverse(t, {
    leave: function (n, p) {
      if (
        n.type == "BinaryExpression" &&
        n.left.type == "Literal" && n.right.type == "Literal"
      ) {
        binaryConstantFolding(n);
      }

      if (
        (n.type === "MemberExpression") &&
        (n.object.type === "ArrayExpression") &&
        (p.type !== "CallExpression")
      ) {
        let value = eval(arrayConstantFolding(n));
        Object.assign(n, newNode(value));
      }

      if (
        (n.type === "CallExpression") &&
        (n.callee.object.type === "ArrayExpression") &&
        isConstant(n.arguments) &&
        isConstant(n.callee.object.elements)
      ) {
        let value = arrayConstantFolding(n.callee, n.arguments);
        Array.isArray(value) ?
          Object.assign(n, newArrayNode(value)) :
          Object.assign(n, newNode(value));
      }
    }
  }
  );
  return escodegen.generate(t);
}

/**
 * Does ConstantFolding on binaryExpression nodes. I.E. turns 2+3 into 5.
 * @param {Object} n an AST node representing a binaryExpression.
 */
function binaryConstantFolding(n) {
  n.type = "Literal";

  n.value = eval(`${n.left.raw} ${n.operator} ${n.right.raw}`);
  n.raw = String(n.value);

  delete n.left;
  delete n.right;
}

/**
 * Does constant folding on Array operations.
 * @param {Object} code an AST node containing the expression statement
 * @param {Array} args arguments used on the function call. I.E. for [1].concat('d', 'e'), args = ['d', 'e']
 * @returns {string} the resulting js code to be used by escodegen
 */
function arrayConstantFolding(code, args) {
  let result = `[${code.object.elements.map(e => getValue(e))}]`;
  result += code.property.type === "Literal" ? `[${getValue(code.property)}]` : `.${getValue(code.property)}`;
  result += (args ? `(${args.map(e => getValue(e))})` : "");
  return eval(result);
}