babel-plugin-transform-dead-code-elimination
Version:
Babel 6 fork of babel-plugin-dead-code-elimination.
255 lines (210 loc) • 8.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function () {
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(path, state) {
if (!state.opts.experimentalInlining) return;
var node = path.node,
scope = path.scope;
var binding = scope.getBinding(node.name);
if (!binding || binding.references > 1 || !binding.constant) return;
if (binding.kind === "param" || binding.kind === "module") return;
// Do not remove exports like `export function t() { }`.
if (t.isExportDeclaration(binding.path.parent)) return;
var replacement = binding.path.node;
if (t.isVariableDeclarator(replacement)) {
if (t.isArrayPattern(replacement.id)) {
// don't try to inline across array destructuring
return;
}
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 (path.findParent(function (path) {
return path.node === replacement;
})) {
return;
}
t.toExpression(replacement);
scope.removeBinding(node.name);
binding.path.remove();
path.replaceWith(replacement);
},
"ClassDeclaration|FunctionDeclaration": function ClassDeclarationFunctionDeclaration(path) {
var node = path.node,
scope = path.scope;
if (t.isClass(node) && node.decorators && node.decorators.length) {
// We don't want to remove classes that have attached decorators.
// The decorator itself is referencing the class and might have side effects, like
// registering the class somewhere else.
return;
}
// Anonymous functions will either be used immediately
// or assigned to a variable, which will be DCE'd if it's unused.
// Either way, we can't do anything here.
if (!node.id) return;
var binding = scope.getBinding(node.id.name);
if (binding && !binding.referenced) {
path.remove();
// binding might never be read (have no references) yet still be written to
// so replace all constantViolations (assignments) with the assigned value
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = binding.constantViolations[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _path = _step.value;
// constantViolations that aren't assignments should be duplicate function or var declarations
// which will be removed when this visitor gets to them a second time
if (t.isAssignmentExpression(_path)) {
_path.replaceWith(_path.node.right);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
},
VariableDeclarator: function VariableDeclarator(_ref) {
var node = _ref.node,
scope = _ref.scope;
if (!t.isIdentifier(node.id) || !scope.isPure(node.init, true)) return;
visitor["ClassDeclaration|FunctionDeclaration"].apply(this, arguments);
},
ConditionalExpression: {
exit: function exit(path) {
var node = path.node;
var evaluateTest = path.get("test").evaluateTruthy();
if (evaluateTest === true) {
path.replaceWith(node.consequent);
} else if (evaluateTest === false) {
path.replaceWith(node.alternate);
}
}
},
LogicalExpression: {
exit: function exit(path) {
var node = path.node;
var operator = path.get("operator").node;
var leftOfOperatorTest = path.get("left").evaluateTruthy();
if (operator === "&&") {
if (leftOfOperatorTest === true) {
path.replaceWith(node.right);
} else if (leftOfOperatorTest === false) {
path.replaceWith(node.left);
}
} else if (operator === "||") {
if (leftOfOperatorTest === true) {
path.replaceWith(node.left);
} else if (leftOfOperatorTest === false) {
path.replaceWith(node.right);
}
}
}
},
BlockStatement: {
exit: function exit(path) {
var paths = path.get("body");
var purge = false;
for (var i = 0; i < paths.length; i++) {
var _path2 = paths[i];
if (!purge && t.isCompletionStatement(_path2)) {
purge = true;
continue;
}
if (purge && !t.isFunctionDeclaration(_path2) && !_path2.node._blockHoist) {
_path2.remove();
}
}
}
},
IfStatement: {
exit: function exit(path) {
var node = path.node;
var consequent = node.consequent;
var alternate = node.alternate;
var test = node.test;
var evaluateTest = path.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) {
path.replaceWithMultiple(toStatements(consequent));
return;
}
// 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) {
path.replaceWithMultiple(toStatements(alternate));
} else {
path.remove();
}
return;
}
// 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 { visitor: visitor };
};
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }