-
Notifications
You must be signed in to change notification settings - Fork 5
/
defeatureify.js
120 lines (101 loc) · 4.03 KB
/
defeatureify.js
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
var SourceModifier = require("./lib/source_modifier");
var esprima = require('esprima');
var defeatureify = function(source, config) {
config = config || {};
enabled = config.enabled || {};
var namespace = config.namespace || "Ember";
var debugStatements = config.debugStatements;
var tree = esprima.parse(source, {
range: true,
loc: true
});
var sourceModifier = new SourceModifier(source);
var getCalleeExpression = function(node) {
if (node.type === 'MemberExpression') {
return getCalleeExpression(node.object) + '.' + (node.property.name || node.property.value);
}
else if (node.type === 'Identifier') { return node.name; }
else { return null; }
};
// naively searches for "body" keys in nodes to recurse through
var findBody = function(node) {
for (var key in node) {
if (key === 'body' && Array.isArray(node[key])) {
node[key].forEach(walk);
} else if (typeof node[key] === 'object' && node !== null) {
findBody(node[key]);
}
}
};
var shouldProcessNode = function(node) {
// test object.property.property
if (node.test.callee.object &&
node.test.callee.object.object &&
node.test.callee.object.property) {
// test namespace.FEATURES.isEnabled()
if (node.test.callee.object.object.name === namespace &&
node.test.callee.object.property.name === "FEATURES" &&
node.test.callee.property.name === "isEnabled") {
return true;
}
}
// test object.object.{object,property} and object.property
if (node.test.callee.object &&
node.test.callee.object.object &&
node.test.callee.object.object.property &&
node.test.callee.object.object.object &&
node.test.callee.object.property) {
// test namespace['default'].FEATURES.isEnabled()
if (node.test.callee.object.object.object.name === namespace &&
node.test.callee.object.object.property.value === 'default' &&
node.test.callee.object.property.name === "FEATURES" &&
node.test.callee.property.name === "isEnabled") {
return true;
}
}
};
var walk = function(node) {
if (node.type === "IfStatement") {
if (node.test.type === "CallExpression" && node.test.callee) {
if (shouldProcessNode(node)) {
var featureName = node.test.arguments[0].value;
if (enabled[featureName] === true) {
// remove if (x) {
sourceModifier.replace(node.range[0],
node.consequent.range[0], "");
// TODO: reindent
// remove closing brace }
var body = node.consequent.body;
var lastStatement = body[body.length - 1];
sourceModifier.replace(node.consequent.range[1] - 1,
node.consequent.range[1] - 1, "");
// remove else clause if exists
if (node.alternate && node.alternate.type === "BlockStatement") {
sourceModifier.replace(node.consequent.range[1], node.alternate.range[1], "");
}
} else if (enabled[featureName] === false || enabled[featureName] === undefined) {
// remove if, leave else
if (!node.alternate) {
sourceModifier.replace(node.range[0], node.range[1], "");
} else {
sourceModifier.replace(node.range[0], node.alternate.range[0], "");
sourceModifier.replace(node.alternate.range[1]-1, node.alternate.range[1]-1, "");
}
}
}
}
}
if (config.enableStripDebug && node.type === "ExpressionStatement" && node.expression) {
if (node.expression.type === "CallExpression") {
var calleeExpression = getCalleeExpression(node.expression.callee);
if (debugStatements.indexOf(calleeExpression) != -1) {
sourceModifier.replace(node.range[0], node.range[1], "");
}
}
}
findBody(node);
};
walk(tree);
return sourceModifier.toString();
};
module.exports = defeatureify;