index.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. 'use strict'
  2. var acorn = require('acorn');
  3. var walk = require('acorn/dist/walk');
  4. var lastSRC = '(null)';
  5. var lastRes = true;
  6. var lastConstants = undefined;
  7. var STATEMENT_WHITE_LIST = {
  8. 'EmptyStatement': true,
  9. 'ExpressionStatement': true,
  10. };
  11. var EXPRESSION_WHITE_LIST = {
  12. 'ParenthesizedExpression': true,
  13. 'ArrayExpression': true,
  14. 'ObjectExpression': true,
  15. 'SequenceExpression': true,
  16. 'TemplateLiteral': true,
  17. 'UnaryExpression': true,
  18. 'BinaryExpression': true,
  19. 'LogicalExpression': true,
  20. 'ConditionalExpression': true,
  21. 'Identifier': true,
  22. 'Literal': true,
  23. 'ComprehensionExpression': true,
  24. 'TaggedTemplateExpression': true,
  25. 'MemberExpression': true,
  26. 'CallExpression': true,
  27. 'NewExpression': true,
  28. };
  29. module.exports = isConstant;
  30. function isConstant(src, constants) {
  31. src = '(' + src + ')';
  32. if (lastSRC === src && lastConstants === constants) return lastRes;
  33. lastSRC = src;
  34. lastConstants = constants;
  35. if (!isExpression(src)) return lastRes = false;
  36. var ast;
  37. try {
  38. ast = acorn.parse(src, {
  39. ecmaVersion: 6,
  40. allowReturnOutsideFunction: true,
  41. allowImportExportEverywhere: true,
  42. allowHashBang: true
  43. });
  44. } catch (ex) {
  45. return lastRes = false;
  46. }
  47. var isConstant = true;
  48. walk.simple(ast, {
  49. Statement: function (node) {
  50. if (isConstant) {
  51. if (STATEMENT_WHITE_LIST[node.type] !== true) {
  52. isConstant = false;
  53. }
  54. }
  55. },
  56. Expression: function (node) {
  57. if (isConstant) {
  58. if (EXPRESSION_WHITE_LIST[node.type] !== true) {
  59. isConstant = false;
  60. }
  61. }
  62. },
  63. MemberExpression: function (node) {
  64. if (isConstant) {
  65. if (node.computed) isConstant = false;
  66. else if (node.property.name[0] === '_') isConstant = false;
  67. }
  68. },
  69. Identifier: function (node) {
  70. if (isConstant) {
  71. if (!constants || !(node.name in constants)) {
  72. isConstant = false;
  73. }
  74. }
  75. },
  76. });
  77. return lastRes = isConstant;
  78. }
  79. isConstant.isConstant = isConstant;
  80. isConstant.toConstant = toConstant;
  81. function toConstant(src, constants) {
  82. if (!isConstant(src, constants)) throw new Error(JSON.stringify(src) + ' is not constant.');
  83. return Function(Object.keys(constants || {}).join(','), 'return (' + src + ')').apply(null, Object.keys(constants || {}).map(function (key) {
  84. return constants[key];
  85. }));
  86. }
  87. function isExpression(src) {
  88. try {
  89. eval('throw "STOP"; (function () { return (' + src + '); })()');
  90. return false;
  91. }
  92. catch (err) {
  93. return err === 'STOP';
  94. }
  95. }