"use strict"; exports.__esModule = true; var babylon_1 = require("babylon"); var b = require("babel-types"); var binaryOperation_1 = require("./binaryOperation"); function expressionToConstant(expression, options) { if (options === void 0) { options = {}; } var constant = true; function toConstant(expression) { if (!constant) return; if (b.isArrayExpression(expression)) { var result_1 = []; for (var i = 0; constant && i < expression.elements.length; i++) { var element = expression.elements[i]; if (b.isSpreadElement(element)) { var spread = toConstant(element.argument); if (!(isSpreadable(spread) && constant)) { constant = false; } else { result_1.push.apply(result_1, spread); } } else { result_1.push(toConstant(element)); } } return result_1; } if (b.isBinaryExpression(expression)) { var left = toConstant(expression.left); var right = toConstant(expression.right); return constant && binaryOperation_1["default"](expression.operator, left, right); } if (b.isBooleanLiteral(expression)) { return expression.value; } if (b.isCallExpression(expression)) { var args = []; for (var i = 0; constant && i < expression.arguments.length; i++) { var arg = expression.arguments[i]; if (b.isSpreadElement(arg)) { var spread = toConstant(arg.argument); if (!(isSpreadable(spread) && constant)) { constant = false; } else { args.push.apply(args, spread); } } else { args.push(toConstant(arg)); } } if (!constant) return; if (b.isMemberExpression(expression.callee)) { var object = toConstant(expression.callee.object); if (!object || !constant) { constant = false; return; } var member = expression.callee.computed ? toConstant(expression.callee.property) : b.isIdentifier(expression.callee.property) ? expression.callee.property.name : undefined; if (member === undefined && !expression.callee.computed) { constant = false; } if (!constant) return; if (canCallMethod(object, '' + member)) { return object[member].apply(object, args); } } else { var callee = toConstant(expression.callee); if (!constant) return; return callee.apply(null, args); } } if (b.isConditionalExpression(expression)) { var test = toConstant(expression.test); return test ? toConstant(expression.consequent) : toConstant(expression.alternate); } if (b.isIdentifier(expression)) { if (options.constants && {}.hasOwnProperty.call(options.constants, expression.name)) { return options.constants[expression.name]; } } if (b.isLogicalExpression(expression)) { var left = toConstant(expression.left); var right = toConstant(expression.right); if (constant && expression.operator === '&&') { return left && right; } if (constant && expression.operator === '||') { return left || right; } } if (b.isMemberExpression(expression)) { var object = toConstant(expression.object); if (!object || !constant) { constant = false; return; } var member = expression.computed ? toConstant(expression.property) : b.isIdentifier(expression.property) ? expression.property.name : undefined; if (member === undefined && !expression.computed) { constant = false; } if (!constant) return; if ({}.hasOwnProperty.call(object, '' + member) && member[0] !== '_') { return object[member]; } } if (b.isNullLiteral(expression)) { return null; } if (b.isNumericLiteral(expression)) { return expression.value; } if (b.isObjectExpression(expression)) { var result_2 = {}; for (var i = 0; constant && i < expression.properties.length; i++) { var property = expression.properties[i]; if (b.isObjectProperty(property)) { if (property.shorthand) { constant = false; return; } var key = property.computed ? toConstant(property.key) : b.isIdentifier(property.key) ? property.key.name : b.isStringLiteral(property.key) ? property.key.value : undefined; if (!key || key[0] === '_') { constant = false; } if (!constant) return; var value = toConstant(property.value); if (!constant) return; result_2[key] = value; } else if (b.isObjectMethod(property)) { constant = false; } else if (b.isSpreadProperty(property)) { var argument = toConstant(property.argument); if (!argument) constant = false; if (!constant) return; Object.assign(result_2, argument); } } return result_2; } if (b.isParenthesizedExpression(expression)) { return toConstant(expression.expression); } if (b.isRegExpLiteral(expression)) { return new RegExp(expression.pattern, expression.flags); } if (b.isSequenceExpression(expression)) { for (var i = 0; i < expression.expressions.length - 1 && constant; i++) { toConstant(expression.expressions[i]); } return toConstant(expression.expressions[expression.expressions.length - 1]); } if (b.isStringLiteral(expression)) { return expression.value; } // TODO: TaggedTemplateExpression if (b.isTemplateLiteral(expression)) { var result_3 = ''; for (var i = 0; i < expression.quasis.length; i++) { var quasi = expression.quasis[i]; result_3 += quasi.value.cooked; if (i < expression.expressions.length) { result_3 += '' + toConstant(expression.expressions[i]); } } return result_3; } if (b.isUnaryExpression(expression)) { var argument = toConstant(expression.argument); if (!constant) { return; } switch (expression.operator) { case '-': return -argument; case '+': return +argument; case '!': return !argument; case '~': return ~argument; case 'typeof': return typeof argument; case 'void': return void argument; } } constant = false; } var result = toConstant(expression); return constant ? { constant: true, result: result } : { constant: false }; } exports.expressionToConstant = expressionToConstant; function isSpreadable(value) { return (typeof value === 'string' || Array.isArray(value) || (typeof Set !== 'undefined' && value instanceof Set) || (typeof Map !== 'undefined' && value instanceof Map)); } function shallowEqual(a, b) { if (a === b) return true; if (a && b && typeof a === 'object' && typeof b === 'object') { for (var key in a) { if (a[key] !== b[key]) { return false; } } for (var key in b) { if (a[key] !== b[key]) { return false; } } return true; } return false; } function canCallMethod(object, member) { switch (typeof object) { case 'boolean': switch (member) { case 'toString': return true; default: return false; } case 'number': switch (member) { case 'toExponential': case 'toFixed': case 'toPrecision': case 'toString': return true; default: return false; } case 'string': switch (member) { case 'charAt': case 'charCodeAt': case 'codePointAt': case 'concat': case 'endsWith': case 'includes': case 'indexOf': case 'lastIndexOf': case 'match': case 'normalize': case 'padEnd': case 'padStart': case 'repeat': case 'replace': case 'search': case 'slice': case 'split': case 'startsWith': case 'substr': case 'substring': case 'toLowerCase': case 'toUpperCase': case 'trim': return true; default: return false; } default: if (object instanceof RegExp) { switch (member) { case 'test': case 'exec': return true; default: return false; } } return {}.hasOwnProperty.call(object, member) && member[0] !== '_'; } } var EMPTY_OBJECT = {}; var lastSrc = ''; var lastConstants = EMPTY_OBJECT; var lastOptions = EMPTY_OBJECT; var lastResult = null; var lastWasConstant = false; function isConstant(src, constants, options) { if (constants === void 0) { constants = EMPTY_OBJECT; } if (options === void 0) { options = EMPTY_OBJECT; } if (lastSrc === src && shallowEqual(lastConstants, constants) && shallowEqual(lastOptions, options)) { return lastWasConstant; } lastSrc = src; lastConstants = constants; var ast; try { ast = babylon_1.parseExpression(src, options); } catch (ex) { return (lastWasConstant = false); } var _a = expressionToConstant(ast, { constants: constants }), result = _a.result, constant = _a.constant; lastResult = result; return (lastWasConstant = constant); } exports.isConstant = isConstant; function toConstant(src, constants, options) { if (constants === void 0) { constants = EMPTY_OBJECT; } if (options === void 0) { options = EMPTY_OBJECT; } if (!isConstant(src, constants, options)) { throw new Error(JSON.stringify(src) + ' is not constant.'); } return lastResult; } exports.toConstant = toConstant; exports["default"] = isConstant; module.exports = isConstant; module.exports["default"] = isConstant; module.exports.expressionToConstant = expressionToConstant; module.exports.isConstant = isConstant; module.exports.toConstant = toConstant;