mirror of
https://github.com/wu736139669/Monkey-Test.git
synced 2026-08-06 06:14:53 +00:00
170 lines
4.7 KiB
JavaScript
170 lines
4.7 KiB
JavaScript
"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"]; |