index.js
4.72 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = function (_ref) {
var Plugin = _ref.Plugin;
var t = _ref.types;
function toStatements(node) {
if (t.isBlockStatement(node)) {
var hasBlockScoped = false;
for (var i = 0; i < node.body.length; i++) {
var bodyNode = node.body[i];
if (t.isBlockScoped(bodyNode)) hasBlockScoped = true;
}
if (!hasBlockScoped) {
return node.body;
}
}
return node;
}
var visitor = {
ReferencedIdentifier: function ReferencedIdentifier(node, parent, scope) {
var binding = scope.getBinding(node.name);
if (!binding || binding.references > 1 || !binding.constant) return;
if (binding.kind === "param" || binding.kind === "module") return;
var replacement = binding.path.node;
if (t.isVariableDeclarator(replacement)) {
replacement = replacement.init;
}
if (!replacement) return;
// ensure it's a "pure" type
if (!scope.isPure(replacement, true)) return;
if (t.isClass(replacement) || t.isFunction(replacement)) {
// don't change this if it's in a different scope, this can be bad
// for performance since it may be inside a loop or deeply nested in
// hot code
if (binding.path.scope.parent !== scope) return;
}
if (this.findParent(function (path) {
return path.node === replacement;
})) {
return;
}
t.toExpression(replacement);
scope.removeBinding(node.name);
binding.path.dangerouslyRemove();
return replacement;
},
"ClassDeclaration|FunctionDeclaration": function ClassDeclarationFunctionDeclaration(node, parent, scope) {
var binding = scope.getBinding(node.id.name);
if (binding && !binding.referenced) {
this.dangerouslyRemove();
}
},
VariableDeclarator: function VariableDeclarator(node, parent, scope) {
if (!t.isIdentifier(node.id) || !scope.isPure(node.init, true)) return;
visitor["ClassDeclaration|FunctionDeclaration"].apply(this, arguments);
},
ConditionalExpression: function ConditionalExpression(node) {
var evaluateTest = this.get("test").evaluateTruthy();
if (evaluateTest === true) {
return node.consequent;
} else if (evaluateTest === false) {
return node.alternate;
}
},
BlockStatement: function BlockStatement() {
var paths = this.get("body");
var purge = false;
for (var i = 0; i < paths.length; i++) {
var path = paths[i];
if (!purge && path.isCompletionStatement()) {
purge = true;
continue;
}
if (purge && !path.isFunctionDeclaration()) {
path.dangerouslyRemove();
}
}
},
IfStatement: {
exit: function exit(node) {
var consequent = node.consequent;
var alternate = node.alternate;
var test = node.test;
var evaluateTest = this.get("test").evaluateTruthy();
// we can check if a test will be truthy 100% and if so then we can inline
// the consequent and completely ignore the alternate
//
// if (true) { foo; } -> { foo; }
// if ("foo") { foo; } -> { foo; }
//
if (evaluateTest === true) {
return toStatements(consequent);
}
// we can check if a test will be falsy 100% and if so we can inline the
// alternate if there is one and completely remove the consequent
//
// if ("") { bar; } else { foo; } -> { foo; }
// if ("") { bar; } ->
//
if (evaluateTest === false) {
if (alternate) {
return toStatements(alternate);
} else {
return this.dangerouslyRemove();
}
}
// remove alternate blocks that are empty
//
// if (foo) { foo; } else {} -> if (foo) { foo; }
//
if (t.isBlockStatement(alternate) && !alternate.body.length) {
alternate = node.alternate = null;
}
// if the consequent block is empty turn alternate blocks into a consequent
// and flip the test
//
// if (foo) {} else { bar; } -> if (!foo) { bar; }
//
if (t.isBlockStatement(consequent) && !consequent.body.length && t.isBlockStatement(alternate) && alternate.body.length) {
node.consequent = node.alternate;
node.alternate = null;
node.test = t.unaryExpression("!", test, true);
}
}
}
};
return new Plugin("dead-code-elimination", {
metadata: {
group: "builtin-pre",
experimental: true
},
visitor: visitor
});
};
module.exports = exports["default"];