index.js
2.52 KB
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
'use strict'
var acorn = require('acorn');
var walk = require('acorn/dist/walk');
var lastSRC = '(null)';
var lastRes = true;
var lastConstants = undefined;
var STATEMENT_WHITE_LIST = {
'EmptyStatement': true,
'ExpressionStatement': true,
};
var EXPRESSION_WHITE_LIST = {
'ParenthesizedExpression': true,
'ArrayExpression': true,
'ObjectExpression': true,
'SequenceExpression': true,
'TemplateLiteral': true,
'UnaryExpression': true,
'BinaryExpression': true,
'LogicalExpression': true,
'ConditionalExpression': true,
'Identifier': true,
'Literal': true,
'ComprehensionExpression': true,
'TaggedTemplateExpression': true,
'MemberExpression': true,
'CallExpression': true,
'NewExpression': true,
};
module.exports = isConstant;
function isConstant(src, constants) {
src = '(' + src + ')';
if (lastSRC === src && lastConstants === constants) return lastRes;
lastSRC = src;
lastConstants = constants;
if (!isExpression(src)) return lastRes = false;
var ast;
try {
ast = acorn.parse(src, {
ecmaVersion: 6,
allowReturnOutsideFunction: true,
allowImportExportEverywhere: true,
allowHashBang: true
});
} catch (ex) {
return lastRes = false;
}
var isConstant = true;
walk.simple(ast, {
Statement: function (node) {
if (isConstant) {
if (STATEMENT_WHITE_LIST[node.type] !== true) {
isConstant = false;
}
}
},
Expression: function (node) {
if (isConstant) {
if (EXPRESSION_WHITE_LIST[node.type] !== true) {
isConstant = false;
}
}
},
MemberExpression: function (node) {
if (isConstant) {
if (node.computed) isConstant = false;
else if (node.property.name[0] === '_') isConstant = false;
}
},
Identifier: function (node) {
if (isConstant) {
if (!constants || !(node.name in constants)) {
isConstant = false;
}
}
},
});
return lastRes = isConstant;
}
isConstant.isConstant = isConstant;
isConstant.toConstant = toConstant;
function toConstant(src, constants) {
if (!isConstant(src, constants)) throw new Error(JSON.stringify(src) + ' is not constant.');
return Function(Object.keys(constants || {}).join(','), 'return (' + src + ')').apply(null, Object.keys(constants || {}).map(function (key) {
return constants[key];
}));
}
function isExpression(src) {
try {
eval('throw "STOP"; (function () { return (' + src + '); })()');
return false;
}
catch (err) {
return err === 'STOP';
}
}