286 lines
6.2 KiB
JavaScript
286 lines
6.2 KiB
JavaScript
var traverse = require("./traverse");
|
|
var b = require("./builders");
|
|
var n = require("acorn-ast-types").namedTypes;
|
|
var _ = require("lodash");
|
|
|
|
var t = exports;
|
|
|
|
//
|
|
|
|
_.each(traverse.VISITOR_KEYS, function (keys, type) {
|
|
t["is" + type] = function (node) {
|
|
return node && node.type === type;
|
|
};
|
|
});
|
|
|
|
var buildIs = function (isKey, typeKey, types) {
|
|
t[typeKey + "_TYPES"] = types;
|
|
|
|
t["is" + isKey] = function (node) {
|
|
return node && _.contains(types, node.type);
|
|
};
|
|
};
|
|
|
|
buildIs("Function", "FUNCTION", ["ArrowFunctionExpression", "FunctionDeclaration", "FunctionExpression"]);
|
|
buildIs("Class", "CLASS", ["ClassDeclaration", "ClassExpression"]);
|
|
buildIs("Pattern", "PATTERN", ["ArrayPattern", "ObjectPattern"]);
|
|
buildIs("Binary", "BINARY", ["BinaryExpression", "LogicalExpression"]);
|
|
buildIs("UnaryLike", "UNARY", ["UnaryExpression", "SpreadElement", "SpreadProperty"]);
|
|
|
|
//
|
|
|
|
t.aliases = {
|
|
ArrowFunctionExpression: ["Function"],
|
|
FunctionDeclaration: ["Function"],
|
|
FunctionExpression: ["Function"],
|
|
|
|
ClassDeclaration: ["Class"],
|
|
ClassExpression: ["Class"]
|
|
};
|
|
|
|
//
|
|
|
|
t.isReferenced = function (node, parent) {
|
|
// we're a property key so we aren't referenced
|
|
if (t.isProperty(parent) && parent.key === node) return false;
|
|
|
|
var isMemberExpression = t.isMemberExpression(parent);
|
|
|
|
// we're in a member expression and we're the computed property so we're referenced
|
|
var isComputedProperty = isMemberExpression && parent.property === node && parent.computed;
|
|
|
|
// we're in a member expression and we're the object so we're referenced
|
|
var isObject = isMemberExpression && parent.object === node;
|
|
|
|
// we are referenced
|
|
if (!isMemberExpression || isComputedProperty || isObject) return true;
|
|
|
|
return false;
|
|
};
|
|
|
|
t.ensureBlock = function (node) {
|
|
node.body = t.toBlock(node.body, node);
|
|
};
|
|
|
|
t.toBlock = function (node, parent) {
|
|
if (t.isBlockStatement(node)) {
|
|
return node;
|
|
}
|
|
|
|
if (!_.isArray(node)) {
|
|
if (!n.Statement.check(node)) {
|
|
if (t.isFunction(parent)) {
|
|
node = b.returnStatement(node);
|
|
} else {
|
|
node = b.expressionStatement(node);
|
|
}
|
|
}
|
|
|
|
node = [node];
|
|
}
|
|
|
|
return b.blockStatement(node);
|
|
};
|
|
|
|
t.inherits = function (child, parent) {
|
|
child.loc = parent.loc;
|
|
child.end = parent.end;
|
|
child.range = parent.range;
|
|
child.start = parent.start;
|
|
return child;
|
|
};
|
|
|
|
t.getSpecifierName = function (specifier) {
|
|
return specifier.name || specifier.id;
|
|
};
|
|
|
|
t.needsWhitespace = function (node, expression) {
|
|
if (t.isExpressionStatement(node)) {
|
|
node = node.expression;
|
|
}
|
|
|
|
//
|
|
|
|
if (t.isFunction(node) || t.isClass(node)) {
|
|
return true;
|
|
}
|
|
|
|
if (expression) return false;
|
|
|
|
//
|
|
|
|
if (_.contains([
|
|
"Literal",
|
|
"CallExpression"
|
|
], node.type)) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
|
|
var exprs = [];
|
|
|
|
if (t.isVariableDeclaration(node)) {
|
|
exprs = _.map(node.declarations, "init");
|
|
}
|
|
|
|
if (t.isArrayExpression(node)) {
|
|
exprs = node.elements;
|
|
}
|
|
|
|
return exprs.some(function (expr) {
|
|
return t.needsWhitespace(expr, true);
|
|
});
|
|
};
|
|
|
|
t.needsParans = function (node, parent) {
|
|
if (!parent) return false;
|
|
|
|
//
|
|
if (t.isUnaryLike(node)) {
|
|
return t.isMemberExpression(parent) && parent.object === node;
|
|
}
|
|
|
|
if (t.isBinary(node)) {
|
|
//
|
|
if (t.isCallExpression(parent) && parent.callee === node) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
if (t.isUnaryLike(parent)) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
if (t.isMemberExpression(parent) && parent.object === node) {
|
|
return true;
|
|
}
|
|
|
|
if (t.isBinary(parent)) {
|
|
var parentOp = parent.operator;
|
|
var parentPos = PRECEDENCE[parentOp];
|
|
|
|
var nodeOp = node.operator;
|
|
var nodeOp = PRECEDENCE[nodeOp];
|
|
|
|
if (parentPos > nodeOp) {
|
|
return true;
|
|
}
|
|
|
|
if (parentPos === nodeOp && parent.right === node) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (t.isSequenceExpression(node)) {
|
|
if (t.isForStatement(parent)) {
|
|
// Although parentheses wouldn't hurt around sequence
|
|
// expressions in the head of for loops, traditional style
|
|
// dictates that e.g. i++, j++ should not be wrapped with
|
|
// parentheses.
|
|
return false;
|
|
}
|
|
|
|
if (t.isExpressionStatement(parent) && parent.expression === node) {
|
|
return false;
|
|
}
|
|
|
|
// Otherwise err on the side of overparenthesization, adding
|
|
// explicit exceptions above if this proves overzealous.
|
|
return true;
|
|
}
|
|
|
|
//
|
|
if (t.isYieldExpression(node)) {
|
|
return t.isBinary(parent)
|
|
|| t.isUnaryLike(parent)
|
|
|| t.isCallExpression(parent)
|
|
|| t.isMemberExpression(parent)
|
|
|| t.isNewExpression(parent)
|
|
|| t.isConditionalExpression(parent)
|
|
|| t.isYieldExpression(parent);
|
|
}
|
|
|
|
if (t.isNewExpression(parent) && parent.callee === node) {
|
|
return traverse.hasType(node, "CallExpression");
|
|
}
|
|
|
|
// (1).valueOf()
|
|
if (t.isLiteral(node) && _.isNumber(node.value) && t.isMemberExpression(parent) && parent.object === node) {
|
|
return true;
|
|
}
|
|
|
|
if (t.isAssignmentExpression(node) || t.isConditionalExpression(node)) {
|
|
//
|
|
if (t.isUnaryLike(parent)) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
if (t.isBinary(parent)) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
if (t.isCallExpression(parent) && parent.callee === node) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
if (t.isConditionalExpression(parent) && parent.test === node) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
if (t.isMemberExpression(parent) && parent.object === node) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (t.isFunctionExpression(node)) {
|
|
// function () {};
|
|
if (t.isExpressionStatement(parent)) {
|
|
return true;
|
|
}
|
|
|
|
// (function test() {}).name;
|
|
if (t.isMemberExpression(parent) && parent.object === node) {
|
|
return true;
|
|
}
|
|
|
|
// (function () {})();
|
|
if (t.isCallExpression(parent) && parent.callee === node) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// ({ x, y }) = { x: 5, y: 6 };
|
|
if (t.isObjectPattern(node) && t.isAssignmentExpression(parent) && parent.left == node) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
var PRECEDENCE = {};
|
|
|
|
_.each([
|
|
["||"],
|
|
["&&"],
|
|
["|"],
|
|
["^"],
|
|
["&"],
|
|
["==", "===", "!=", "!=="],
|
|
["<", ">", "<=", ">=", "in", "instanceof"],
|
|
[">>", "<<", ">>>"],
|
|
["+", "-"],
|
|
["*", "/", "%"]
|
|
], function(tier, i) {
|
|
_.each(tier, function(op) {
|
|
PRECEDENCE[op] = i;
|
|
});
|
|
});
|
|
|