240 lines
7.1 KiB
JavaScript
240 lines
7.1 KiB
JavaScript
"use strict";
|
|
|
|
var evaluate = require("babel-helper-evaluate-path");
|
|
|
|
function isPureAndUndefined(rval) {
|
|
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
tdz = _ref.tdz,
|
|
_ref$scope = _ref.scope,
|
|
scope = _ref$scope === undefined ? { hasBinding: function hasBinding() {
|
|
return false;
|
|
} } : _ref$scope;
|
|
|
|
if (rval.isIdentifier() && rval.node.name === "undefined") {
|
|
// deopt right away if undefined is a local binding
|
|
if (scope.hasBinding(rval.node.name, true /* no globals */)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (!rval.isPure()) {
|
|
return false;
|
|
}
|
|
var evaluation = evaluate(rval, { tdz });
|
|
return evaluation.confident === true && evaluation.value === undefined;
|
|
}
|
|
|
|
function getLoopParent(path, scopeParent) {
|
|
var parent = path.findParent(function (p) {
|
|
return p.isLoop() || p === scopeParent;
|
|
});
|
|
// don't traverse higher than the function the var is defined in.
|
|
return parent === scopeParent ? null : parent;
|
|
}
|
|
|
|
function getFunctionParent(path, scopeParent) {
|
|
var parent = path.findParent(function (p) {
|
|
return p.isFunction();
|
|
});
|
|
// don't traverse higher than the function the var is defined in.
|
|
return parent === scopeParent ? null : parent;
|
|
}
|
|
|
|
function getFunctionReferences(path, scopeParent) {
|
|
var references = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Set();
|
|
|
|
for (var func = getFunctionParent(path, scopeParent); func; func = getFunctionParent(func, scopeParent)) {
|
|
var id = func.node.id;
|
|
var binding = id && func.scope.getBinding(id.name);
|
|
|
|
if (!binding) {
|
|
continue;
|
|
}
|
|
|
|
binding.referencePaths.forEach(function (path) {
|
|
if (!references.has(path)) {
|
|
references.add(path);
|
|
getFunctionReferences(path, scopeParent, references);
|
|
}
|
|
});
|
|
}
|
|
return references;
|
|
}
|
|
|
|
function hasViolation(declarator, scope, start) {
|
|
var binding = scope.getBinding(declarator.node.id.name);
|
|
if (!binding) {
|
|
return true;
|
|
}
|
|
|
|
var scopeParent = declarator.getFunctionParent();
|
|
|
|
var violation = binding.constantViolations.some(function (v) {
|
|
// https://github.com/babel/minify/issues/630
|
|
if (!v.node) {
|
|
return false;
|
|
}
|
|
// return 'true' if we cannot guarantee the violation references
|
|
// the initialized identifier after
|
|
var violationStart = v.node.start;
|
|
if (violationStart === undefined || violationStart < start) {
|
|
return true;
|
|
}
|
|
|
|
var references = getFunctionReferences(v, scopeParent);
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = references[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var ref = _step.value;
|
|
|
|
if (ref.node.start === undefined || ref.node.start < start) {
|
|
return true;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var loop = getLoopParent(declarator, scopeParent); loop; loop = getLoopParent(loop, scopeParent)) {
|
|
if (loop.node.end === undefined || loop.node.end > violationStart) {
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
|
|
return violation;
|
|
}
|
|
|
|
module.exports = function () {
|
|
return {
|
|
name: "transform-remove-undefined",
|
|
visitor: {
|
|
SequenceExpression(path, _ref2) {
|
|
var _ref2$opts = _ref2.opts;
|
|
_ref2$opts = _ref2$opts === undefined ? {} : _ref2$opts;
|
|
var tdz = _ref2$opts.tdz;
|
|
|
|
var expressions = path.get("expressions");
|
|
|
|
for (var i = 0; i < expressions.length; i++) {
|
|
var expr = expressions[i];
|
|
if (!isPureAndUndefined(expr, { tdz, scope: path.scope })) continue;
|
|
|
|
// last value
|
|
if (i === expressions.length - 1) {
|
|
if (path.parentPath.isExpressionStatement()) {
|
|
expr.remove();
|
|
}
|
|
} else {
|
|
expr.remove();
|
|
}
|
|
}
|
|
},
|
|
|
|
ReturnStatement(path, _ref3) {
|
|
var _ref3$opts = _ref3.opts;
|
|
_ref3$opts = _ref3$opts === undefined ? {} : _ref3$opts;
|
|
var tdz = _ref3$opts.tdz;
|
|
|
|
if (path.node.argument !== null) {
|
|
if (isPureAndUndefined(path.get("argument"), {
|
|
tdz,
|
|
scope: path.scope
|
|
})) {
|
|
path.node.argument = null;
|
|
}
|
|
}
|
|
},
|
|
|
|
VariableDeclaration(path, _ref4) {
|
|
var _ref4$opts = _ref4.opts;
|
|
_ref4$opts = _ref4$opts === undefined ? {} : _ref4$opts;
|
|
var tdz = _ref4$opts.tdz;
|
|
|
|
switch (path.node.kind) {
|
|
case "const":
|
|
break;
|
|
case "let":
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = path.get("declarations")[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var declarator = _step2.value;
|
|
|
|
if (isPureAndUndefined(declarator.get("init"), { tdz })) {
|
|
declarator.node.init = null;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
case "var":
|
|
var start = path.node.start;
|
|
if (start === undefined) {
|
|
// This is common for plugin-generated nodes
|
|
break;
|
|
}
|
|
var scope = path.scope;
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = path.get("declarations")[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var _declarator = _step3.value;
|
|
|
|
if (isPureAndUndefined(_declarator.get("init")) && !hasViolation(_declarator, scope, start)) {
|
|
_declarator.node.init = null;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}; |