1"use strict"; 2exports.__esModule = true; 3var babylon_1 = require("babylon"); 4var b = require("babel-types"); 5var binaryOperation_1 = require("./binaryOperation"); 6function expressionToConstant(expression, options) { 7 if (options === void 0) { options = {}; } 8 var constant = true; 9 function toConstant(expression) { 10 if (!constant) 11 return; 12 if (b.isArrayExpression(expression)) { 13 var result_1 = []; 14 for (var i = 0; constant && i < expression.elements.length; i++) { 15 var element = expression.elements[i]; 16 if (b.isSpreadElement(element)) { 17 var spread = toConstant(element.argument); 18 if (!(isSpreadable(spread) && constant)) { 19 constant = false; 20 } 21 else { 22 result_1.push.apply(result_1, spread); 23 } 24 } 25 else { 26 result_1.push(toConstant(element)); 27 } 28 } 29 return result_1; 30 } 31 if (b.isBinaryExpression(expression)) { 32 var left = toConstant(expression.left); 33 var right = toConstant(expression.right); 34 return constant && binaryOperation_1["default"](expression.operator, left, right); 35 } 36 if (b.isBooleanLiteral(expression)) { 37 return expression.value; 38 } 39 if (b.isCallExpression(expression)) { 40 var args = []; 41 for (var i = 0; constant && i < expression.arguments.length; i++) { 42 var arg = expression.arguments[i]; 43 if (b.isSpreadElement(arg)) { 44 var spread = toConstant(arg.argument); 45 if (!(isSpreadable(spread) && constant)) { 46 constant = false; 47 } 48 else { 49 args.push.apply(args, spread); 50 } 51 } 52 else { 53 args.push(toConstant(arg)); 54 } 55 } 56 if (!constant) 57 return; 58 if (b.isMemberExpression(expression.callee)) { 59 var object = toConstant(expression.callee.object); 60 if (!object || !constant) { 61 constant = false; 62 return; 63 } 64 var member = expression.callee.computed 65 ? toConstant(expression.callee.property) 66 : b.isIdentifier(expression.callee.property) 67 ? expression.callee.property.name 68 : undefined; 69 if (member === undefined && !expression.callee.computed) { 70 constant = false; 71 } 72 if (!constant) 73 return; 74 if (canCallMethod(object, '' + member)) { 75 return object[member].apply(object, args); 76 } 77 } 78 else { 79 var callee = toConstant(expression.callee); 80 if (!constant) 81 return; 82 return callee.apply(null, args); 83 } 84 } 85 if (b.isConditionalExpression(expression)) { 86 var test = toConstant(expression.test); 87 return test 88 ? toConstant(expression.consequent) 89 : toConstant(expression.alternate); 90 } 91 if (b.isIdentifier(expression)) { 92 if (options.constants && 93 {}.hasOwnProperty.call(options.constants, expression.name)) { 94 return options.constants[expression.name]; 95 } 96 } 97 if (b.isLogicalExpression(expression)) { 98 var left = toConstant(expression.left); 99 var right = toConstant(expression.right); 100 if (constant && expression.operator === '&&') { 101 return left && right; 102 } 103 if (constant && expression.operator === '||') { 104 return left || right; 105 } 106 } 107 if (b.isMemberExpression(expression)) { 108 var object = toConstant(expression.object); 109 if (!object || !constant) { 110 constant = false; 111 return; 112 } 113 var member = expression.computed 114 ? toConstant(expression.property) 115 : b.isIdentifier(expression.property) 116 ? expression.property.name 117 : undefined; 118 if (member === undefined && !expression.computed) { 119 constant = false; 120 } 121 if (!constant) 122 return; 123 if ({}.hasOwnProperty.call(object, '' + member) && member[0] !== '_') { 124 return object[member]; 125 } 126 } 127 if (b.isNullLiteral(expression)) { 128 return null; 129 } 130 if (b.isNumericLiteral(expression)) { 131 return expression.value; 132 } 133 if (b.isObjectExpression(expression)) { 134 var result_2 = {}; 135 for (var i = 0; constant && i < expression.properties.length; i++) { 136 var property = expression.properties[i]; 137 if (b.isObjectProperty(property)) { 138 if (property.shorthand) { 139 constant = false; 140 return; 141 } 142 var key = property.computed 143 ? toConstant(property.key) 144 : b.isIdentifier(property.key) 145 ? property.key.name 146 : b.isStringLiteral(property.key) 147 ? property.key.value 148 : undefined; 149 if (!key || key[0] === '_') { 150 constant = false; 151 } 152 if (!constant) 153 return; 154 var value = toConstant(property.value); 155 if (!constant) 156 return; 157 result_2[key] = value; 158 } 159 else if (b.isObjectMethod(property)) { 160 constant = false; 161 } 162 else if (b.isSpreadProperty(property)) { 163 var argument = toConstant(property.argument); 164 if (!argument) 165 constant = false; 166 if (!constant) 167 return; 168 Object.assign(result_2, argument); 169 } 170 } 171 return result_2; 172 } 173 if (b.isParenthesizedExpression(expression)) { 174 return toConstant(expression.expression); 175 } 176 if (b.isRegExpLiteral(expression)) { 177 return new RegExp(expression.pattern, expression.flags); 178 } 179 if (b.isSequenceExpression(expression)) { 180 for (var i = 0; i < expression.expressions.length - 1 && constant; i++) { 181 toConstant(expression.expressions[i]); 182 } 183 return toConstant(expression.expressions[expression.expressions.length - 1]); 184 } 185 if (b.isStringLiteral(expression)) { 186 return expression.value; 187 } 188 // TODO: TaggedTemplateExpression 189 if (b.isTemplateLiteral(expression)) { 190 var result_3 = ''; 191 for (var i = 0; i < expression.quasis.length; i++) { 192 var quasi = expression.quasis[i]; 193 result_3 += quasi.value.cooked; 194 if (i < expression.expressions.length) { 195 result_3 += '' + toConstant(expression.expressions[i]); 196 } 197 } 198 return result_3; 199 } 200 if (b.isUnaryExpression(expression)) { 201 var argument = toConstant(expression.argument); 202 if (!constant) { 203 return; 204 } 205 switch (expression.operator) { 206 case '-': 207 return -argument; 208 case '+': 209 return +argument; 210 case '!': 211 return !argument; 212 case '~': 213 return ~argument; 214 case 'typeof': 215 return typeof argument; 216 case 'void': 217 return void argument; 218 } 219 } 220 constant = false; 221 } 222 var result = toConstant(expression); 223 return constant ? { constant: true, result: result } : { constant: false }; 224} 225exports.expressionToConstant = expressionToConstant; 226function isSpreadable(value) { 227 return (typeof value === 'string' || 228 Array.isArray(value) || 229 (typeof Set !== 'undefined' && value instanceof Set) || 230 (typeof Map !== 'undefined' && value instanceof Map)); 231} 232function shallowEqual(a, b) { 233 if (a === b) 234 return true; 235 if (a && b && typeof a === 'object' && typeof b === 'object') { 236 for (var key in a) { 237 if (a[key] !== b[key]) { 238 return false; 239 } 240 } 241 for (var key in b) { 242 if (a[key] !== b[key]) { 243 return false; 244 } 245 } 246 return true; 247 } 248 return false; 249} 250function canCallMethod(object, member) { 251 switch (typeof object) { 252 case 'boolean': 253 switch (member) { 254 case 'toString': 255 return true; 256 default: 257 return false; 258 } 259 case 'number': 260 switch (member) { 261 case 'toExponential': 262 case 'toFixed': 263 case 'toPrecision': 264 case 'toString': 265 return true; 266 default: 267 return false; 268 } 269 case 'string': 270 switch (member) { 271 case 'charAt': 272 case 'charCodeAt': 273 case 'codePointAt': 274 case 'concat': 275 case 'endsWith': 276 case 'includes': 277 case 'indexOf': 278 case 'lastIndexOf': 279 case 'match': 280 case 'normalize': 281 case 'padEnd': 282 case 'padStart': 283 case 'repeat': 284 case 'replace': 285 case 'search': 286 case 'slice': 287 case 'split': 288 case 'startsWith': 289 case 'substr': 290 case 'substring': 291 case 'toLowerCase': 292 case 'toUpperCase': 293 case 'trim': 294 return true; 295 default: 296 return false; 297 } 298 default: 299 if (object instanceof RegExp) { 300 switch (member) { 301 case 'test': 302 case 'exec': 303 return true; 304 default: 305 return false; 306 } 307 } 308 return {}.hasOwnProperty.call(object, member) && member[0] !== '_'; 309 } 310} 311var EMPTY_OBJECT = {}; 312var lastSrc = ''; 313var lastConstants = EMPTY_OBJECT; 314var lastOptions = EMPTY_OBJECT; 315var lastResult = null; 316var lastWasConstant = false; 317function isConstant(src, constants, options) { 318 if (constants === void 0) { constants = EMPTY_OBJECT; } 319 if (options === void 0) { options = EMPTY_OBJECT; } 320 if (lastSrc === src && 321 shallowEqual(lastConstants, constants) && 322 shallowEqual(lastOptions, options)) { 323 return lastWasConstant; 324 } 325 lastSrc = src; 326 lastConstants = constants; 327 var ast; 328 try { 329 ast = babylon_1.parseExpression(src, options); 330 } 331 catch (ex) { 332 return (lastWasConstant = false); 333 } 334 var _a = expressionToConstant(ast, { constants: constants }), result = _a.result, constant = _a.constant; 335 lastResult = result; 336 return (lastWasConstant = constant); 337} 338exports.isConstant = isConstant; 339function toConstant(src, constants, options) { 340 if (constants === void 0) { constants = EMPTY_OBJECT; } 341 if (options === void 0) { options = EMPTY_OBJECT; } 342 if (!isConstant(src, constants, options)) { 343 throw new Error(JSON.stringify(src) + ' is not constant.'); 344 } 345 return lastResult; 346} 347exports.toConstant = toConstant; 348exports["default"] = isConstant; 349module.exports = isConstant; 350module.exports["default"] = isConstant; 351module.exports.expressionToConstant = expressionToConstant; 352module.exports.isConstant = isConstant; 353module.exports.toConstant = toConstant; 354