babel-plugin-minify-dead-code-elimination
Advanced tools
Comparing version 0.1.2 to 0.1.3
240
lib/index.js
@@ -7,2 +7,25 @@ "use strict"; | ||
var _require = require("babel-helper-mark-eval-scopes"), | ||
markEvalScopes = _require.markEvalScopes, | ||
isEvalScopesMarked = _require.isMarked, | ||
hasEval = _require.hasEval; | ||
function prevSiblings(path) { | ||
var parentPath = path.parentPath; | ||
var siblings = []; | ||
var key = parentPath.key; | ||
while ((path = parentPath.getSibling(--key)).type) { | ||
siblings.push(path); | ||
} | ||
return siblings; | ||
} | ||
function forEachAncestor(path, callback) { | ||
while (path = path.parentPath) { | ||
callback(path); | ||
} | ||
} | ||
module.exports = function (_ref) { | ||
@@ -182,2 +205,6 @@ var t = _ref.types, | ||
if (hasEval(path.scope)) { | ||
return; | ||
} | ||
var scope = path.scope; | ||
@@ -196,2 +223,4 @@ | ||
var binding = scope.bindings[param.node.name]; | ||
if (!binding) continue; | ||
if (binding.referenced) { | ||
@@ -360,15 +389,54 @@ // when the first binding is referenced (right to left) | ||
var _ret6 = function () { | ||
var replacement = binding.path.node; | ||
var replacementPath = binding.path; | ||
var isReferencedBefore = false; | ||
if (binding.referencePaths.length > 1) { | ||
throw new Error("Expected only one reference"); | ||
} | ||
var refPath = binding.referencePaths[0]; | ||
if (t.isVariableDeclarator(replacement)) { | ||
replacement = replacement.init; | ||
// Bail out for ArrayPattern and ObjectPattern | ||
// TODO: maybe a more intelligent approach instead of simply bailing out | ||
if (!replacementPath.get("id").isIdentifier()) { | ||
return { | ||
v: "continue" | ||
}; | ||
} | ||
replacementPath = replacementPath.get("init"); | ||
var _ret7 = function () { | ||
var _prevSiblings = prevSiblings(replacementPath); | ||
// traverse ancestors of a reference checking if it's before declaration | ||
forEachAncestor(refPath, function (ancestor) { | ||
if (_prevSiblings.indexOf(ancestor) > -1) { | ||
isReferencedBefore = true; | ||
} | ||
}); | ||
// deopt if reference is in different scope than binding | ||
// since we don't know if it's sync or async execition | ||
// (i.e. whether value has been assigned to a reference or not) | ||
if (isReferencedBefore && refPath.scope !== binding.scope) { | ||
return { | ||
v: { | ||
v: "continue" | ||
} | ||
}; | ||
} | ||
// simulate hoisting by replacing value | ||
// with undefined if declaration is after reference | ||
replacement = isReferencedBefore ? t.unaryExpression("void", t.numericLiteral(0), true) : replacement.init; | ||
// Bail out for ArrayPattern and ObjectPattern | ||
// TODO: maybe a more intelligent approach instead of simply bailing out | ||
if (!replacementPath.get("id").isIdentifier()) { | ||
return { | ||
v: { | ||
v: "continue" | ||
} | ||
}; | ||
} | ||
replacementPath = replacementPath.get("init"); | ||
}(); | ||
if (typeof _ret7 === "object") return _ret7.v; | ||
} | ||
if (!replacement) { | ||
@@ -380,3 +448,3 @@ return { | ||
if (!scope.isPure(replacement, true)) { | ||
if (!scope.isPure(replacement, true) && !isReferencedBefore) { | ||
return { | ||
@@ -387,8 +455,3 @@ v: "continue" | ||
if (binding.referencePaths.length > 1) { | ||
throw new Error("Expected only one reference"); | ||
} | ||
var bail = false; | ||
var refPath = binding.referencePaths[0]; | ||
@@ -508,3 +571,3 @@ if (replacementPath.isIdentifier()) { | ||
// Not last in it's block? (See BlockStatement visitor) | ||
// Not last in its block? (See BlockStatement visitor) | ||
if (path.container.length - 1 !== path.key && !canExistAfterCompletion(path.getSibling(path.key + 1)) && path.parentPath.isBlockStatement()) { | ||
@@ -568,61 +631,2 @@ // This is probably a new oppurtinity by some other transform | ||
IfStatement: { | ||
exit(path) { | ||
var consequent = path.get("consequent"); | ||
var alternate = path.get("alternate"); | ||
var test = path.get("test"); | ||
var evaluateTest = 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([].concat(_toConsumableArray(toStatements(consequent)), _toConsumableArray(extractVars(alternate)))); | ||
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.node) { | ||
path.replaceWithMultiple([].concat(_toConsumableArray(toStatements(alternate)), _toConsumableArray(extractVars(consequent)))); | ||
return; | ||
} else { | ||
path.replaceWithMultiple(extractVars(consequent)); | ||
} | ||
} | ||
// remove alternate blocks that are empty | ||
// | ||
// if (foo) { foo; } else {} -> if (foo) { foo; } | ||
// | ||
if (alternate.isBlockStatement() && !alternate.node.body.length) { | ||
alternate.remove(); | ||
// For if-statements babel-traverse replaces with an empty block | ||
path.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 (consequent.isBlockStatement() && !consequent.node.body.length && alternate.isBlockStatement() && alternate.node.body.length) { | ||
consequent.replaceWith(alternate.node); | ||
alternate.remove(); | ||
// For if-statements babel-traverse replaces with an empty block | ||
path.node.alternate = null; | ||
test.replaceWith(t.unaryExpression("!", test.node, true)); | ||
} | ||
} | ||
}, | ||
SwitchStatement: { | ||
@@ -839,2 +843,68 @@ exit(path) { | ||
visitor: { | ||
IfStatement: { | ||
exit(path) { | ||
var consequent = path.get("consequent"); | ||
var alternate = path.get("alternate"); | ||
var test = path.get("test"); | ||
var evalResult = test.evaluate(); | ||
var isPure = test.isPure(); | ||
var replacements = []; | ||
if (evalResult.confident && !isPure && test.isSequenceExpression()) { | ||
replacements.push(t.expressionStatement(extractSequenceImpure(test))); | ||
} | ||
// 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 (evalResult.confident && evalResult.value) { | ||
path.replaceWithMultiple([].concat(replacements, _toConsumableArray(toStatements(consequent)), _toConsumableArray(extractVars(alternate)))); | ||
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 (evalResult.confident && !evalResult.value) { | ||
if (alternate.node) { | ||
path.replaceWithMultiple([].concat(replacements, _toConsumableArray(toStatements(alternate)), _toConsumableArray(extractVars(consequent)))); | ||
return; | ||
} else { | ||
path.replaceWithMultiple([].concat(replacements, _toConsumableArray(extractVars(consequent)))); | ||
} | ||
} | ||
// remove alternate blocks that are empty | ||
// | ||
// if (foo) { foo; } else {} -> if (foo) { foo; } | ||
// | ||
if (alternate.isBlockStatement() && !alternate.node.body.length) { | ||
alternate.remove(); | ||
// For if-statements babel-traverse replaces with an empty block | ||
path.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 (consequent.isBlockStatement() && !consequent.node.body.length && alternate.isBlockStatement() && alternate.node.body.length) { | ||
consequent.replaceWith(alternate.node); | ||
alternate.remove(); | ||
// For if-statements babel-traverse replaces with an empty block | ||
path.node.alternate = null; | ||
test.replaceWith(t.unaryExpression("!", test.node, true)); | ||
} | ||
} | ||
}, | ||
EmptyStatement(path) { | ||
@@ -845,2 +915,3 @@ if (path.parentPath.isBlockStatement() || path.parentPath.isProgram()) { | ||
}, | ||
Program: { | ||
@@ -864,2 +935,6 @@ exit(path) { | ||
if (!isEvalScopesMarked(path.scope)) { | ||
markEvalScopes(path); | ||
} | ||
// We need to run this plugin in isolation. | ||
@@ -986,3 +1061,3 @@ path.traverse(main, { | ||
if (scope !== path.scope) { | ||
var _ret7 = function () { | ||
var _ret8 = function () { | ||
if (t.isClass(replacement) || t.isFunction(replacement)) { | ||
@@ -1012,3 +1087,3 @@ return { | ||
if (typeof _ret7 === "object") return _ret7.v; | ||
if (typeof _ret8 === "object") return _ret8.v; | ||
} | ||
@@ -1234,2 +1309,13 @@ | ||
} | ||
function extractSequenceImpure(seq) { | ||
var expressions = seq.get("expressions"); | ||
var result = []; | ||
for (var i = 0; i < expressions.length; i++) { | ||
if (!expressions[i].isPure()) { | ||
result.push(expressions[i].node); | ||
} | ||
} | ||
return t.sequenceExpression(result); | ||
} | ||
}; |
{ | ||
"name": "babel-plugin-minify-dead-code-elimination", | ||
"version": "0.1.2", | ||
"version": "0.1.3", | ||
"description": "", | ||
@@ -15,2 +15,3 @@ "homepage": "https://github.com/babel/babili#readme", | ||
"dependencies": { | ||
"babel-helper-mark-eval-scopes": "^0.0.2", | ||
"babel-helper-remove-or-void": "^0.0.1", | ||
@@ -17,0 +18,0 @@ "lodash.some": "^4.6.0" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
42234
1096
3
1
+ Addedbabel-helper-mark-eval-scopes@0.0.2(transitive)