split up babel core into multiple modules

This commit is contained in:
Sebastian McKenzie
2015-08-11 17:40:59 +01:00
parent a179f9a48b
commit 3ef9bffcc2
2384 changed files with 257 additions and 179 deletions

View File

@@ -0,0 +1,133 @@
import whitespace from "./whitespace";
import * as parens from "./parentheses";
import each from "lodash/collection/each";
import some from "lodash/collection/some";
import * as t from "babel-types";
/**
* Test if node matches a set of type-matcher pairs.
* @example
* find({
* VariableDeclaration(node, parent) {
* return true;
* }
* }, node, parent);
*/
var find = function (obj, node, parent) {
if (!obj) return;
var result;
var types = Object.keys(obj);
for (var i = 0; i < types.length; i++) {
var type = types[i];
if (t.is(type, node)) {
var fn = obj[type];
result = fn(node, parent);
if (result != null) break;
}
}
return result;
};
/**
* Whitespace and Parenthesis related methods for nodes.
*/
export default class Node {
constructor(node, parent) {
this.parent = parent;
this.node = node;
}
/**
* Test if `node` can have whitespace set by the user.
*/
static isUserWhitespacable(node) {
return t.isUserWhitespacable(node);
}
/**
* Test if a `node` requires whitespace.
*/
static needsWhitespace(node, parent, type) {
if (!node) return 0;
if (t.isExpressionStatement(node)) {
node = node.expression;
}
var linesInfo = find(whitespace.nodes, node, parent);
if (!linesInfo) {
var items = find(whitespace.list, node, parent);
if (items) {
for (var i = 0; i < items.length; i++) {
linesInfo = Node.needsWhitespace(items[i], node, type);
if (linesInfo) break;
}
}
}
return (linesInfo && linesInfo[type]) || 0;
}
/**
* Test if a `node` requires whitespace before it.
*/
static needsWhitespaceBefore(node, parent) {
return Node.needsWhitespace(node, parent, "before");
}
/**
* Test if a `note` requires whitespace after it.
*/
static needsWhitespaceAfter(node, parent) {
return Node.needsWhitespace(node, parent, "after");
}
/**
* Test if a `node` needs parentheses around it.
*/
static needsParens(node, parent) {
if (!parent) return false;
if (t.isNewExpression(parent) && parent.callee === node) {
if (t.isCallExpression(node)) return true;
var hasCall = some(node, function (val) {
return t.isCallExpression(val);
});
if (hasCall) return true;
}
return find(parens, node, parent);
}
}
/**
* Add all static methods from `Node` to `Node.prototype`.
*/
each(Node, function (fn, key) {
Node.prototype[key] = function () {
// Avoid leaking arguments to prevent deoptimization
var args = new Array(arguments.length + 2);
args[0] = this.node;
args[1] = this.parent;
for (var i = 0; i < args.length; i++) {
args[i + 2] = arguments[i];
}
return Node[key].apply(null, args);
};
});

View File

@@ -0,0 +1,238 @@
import each from "lodash/collection/each";
import * as t from "babel-types";
/**
* Create a mapping of operators to precendence.
*
* @example
* { "==": 6, "+": 9 }
*/
const PRECEDENCE = {};
each([
["||"],
["&&"],
["|"],
["^"],
["&"],
["==", "===", "!=", "!=="],
["<", ">", "<=", ">=", "in", "instanceof"],
[">>", "<<", ">>>"],
["+", "-"],
["*", "/", "%"],
["**"]
], function (tier, i) {
each(tier, function (op) {
PRECEDENCE[op] = i;
});
});
/**
* Test if NullableTypeAnnotation needs parentheses.
*/
export function NullableTypeAnnotation(node, parent) {
return t.isArrayTypeAnnotation(parent);
}
/**
* Alias NullableTypeAnnotation test as FunctionTypeAnnotation.
*/
export { NullableTypeAnnotation as FunctionTypeAnnotation };
/**
* Test if UpdateExpression needs parentheses.
*/
export function UpdateExpression(node, parent) {
if (t.isMemberExpression(parent) && parent.object === node) {
// (foo++).test()
return true;
}
}
/**
* Test if ObjectExpression needs parentheses.
*/
export function ObjectExpression(node, parent) {
if (t.isExpressionStatement(parent)) {
// ({ foo: "bar" });
return true;
}
if (t.isMemberExpression(parent) && parent.object === node) {
// ({ foo: "bar" }).foo
return true;
}
return false;
}
/**
* Test if Binary needs parentheses.
*/
export function Binary(node, parent) {
if ((t.isCallExpression(parent) || t.isNewExpression(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 nodePos = PRECEDENCE[nodeOp];
if (parentPos > nodePos) {
return true;
}
if (parentPos === nodePos && parent.right === node) {
return true;
}
}
}
/**
* Test if BinaryExpression needs parentheses.
*/
export function BinaryExpression(node, parent) {
if (node.operator === "in") {
// var i = (1 in []);
if (t.isVariableDeclarator(parent)) {
return true;
}
// for ((1 in []);;);
if (t.isFor(parent)) {
return true;
}
}
}
/**
* Test if SequenceExpression needs parentheses.
*/
export function SequenceExpression(node, parent) {
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;
}
/**
* Test if YieldExpression needs parentheses.
*/
export function YieldExpression(node, parent) {
return t.isBinary(parent) ||
t.isUnaryLike(parent) ||
t.isCallExpression(parent) ||
t.isMemberExpression(parent) ||
t.isNewExpression(parent) ||
t.isConditionalExpression(parent) ||
t.isYieldExpression(parent);
}
/**
* Test if ClassExpression needs parentheses.
*/
export function ClassExpression(node, parent) {
return t.isExpressionStatement(parent);
}
/**
* Test if UnaryLike needs parentheses.
*/
export function UnaryLike(node, parent) {
return t.isMemberExpression(parent) && parent.object === node;
}
/**
* Test if FunctionExpression needs parentheses.
*/
export function FunctionExpression(node, parent) {
// 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;
}
}
/**
* Test if ConditionalExpression needs parentheses.
*/
export function ConditionalExpression(node, parent) {
if (t.isUnaryLike(parent)) {
return true;
}
if (t.isBinary(parent)) {
return true;
}
if (t.isCallExpression(parent) || t.isNewExpression(parent)) {
if (parent.callee === node) {
return true;
}
}
if (t.isConditionalExpression(parent) && parent.test === node) {
return true;
}
if (t.isMemberExpression(parent) && parent.object === node) {
return true;
}
return false;
}
/**
* Test if AssignmentExpression needs parentheses.
*/
export function AssignmentExpression(node) {
if (t.isObjectPattern(node.left)) {
return true;
} else {
return ConditionalExpression(...arguments);
}
}

View File

@@ -0,0 +1,76 @@
/**
* Printer for nodes, needs a `generator` and a `parent`.
*/
export default class NodePrinter {
constructor(generator, parent) {
this.generator = generator;
this.parent = parent;
}
/**
* Description
*/
printInnerComments() {
if (!this.parent.innerComments) return;
var gen = this.generator;
gen.indent();
gen._printComments(this.parent.innerComments);
gen.dedent();
}
/**
* Print a plain node.
*/
plain(node, opts) {
return this.generator.print(node, this.parent, opts);
}
/**
* Print a sequence of nodes as statements.
*/
sequence(nodes, opts = {}) {
opts.statement = true;
return this.generator.printJoin(this, nodes, opts);
}
/**
* Print a sequence of nodes as expressions.
*/
join(nodes, opts) {
return this.generator.printJoin(this, nodes, opts);
}
/**
* Print a list of nodes, with a customizable separator (defaults to ",").
*/
list(items, opts = {}) {
if (opts.separator == null) {
opts.separator = ",";
if (!this.generator.format.compact) opts.separator += " ";
}
return this.join(items, opts);
}
/**
* Print a block-like node.
*/
block(node) {
return this.generator.printBlock(this, node);
}
/**
* Print node and indent comments.
*/
indentOnComments(node) {
return this.generator.printAndIndentOnComments(this, node);
}
}

View File

@@ -0,0 +1,229 @@
import isBoolean from "lodash/lang/isBoolean";
import each from "lodash/collection/each";
import map from "lodash/collection/map";
import * as t from "babel-types";
/**
* Crawl a node to test if it contains a CallExpression, a Function, or a Helper.
*
* @example
* crawl(node)
* // { hasCall: false, hasFunction: true, hasHelper: false }
*/
function crawl(node, state = {}) {
if (t.isMemberExpression(node)) {
crawl(node.object, state);
if (node.computed) crawl(node.property, state);
} else if (t.isBinary(node) || t.isAssignmentExpression(node)) {
crawl(node.left, state);
crawl(node.right, state);
} else if (t.isCallExpression(node)) {
state.hasCall = true;
crawl(node.callee, state);
} else if (t.isFunction(node)) {
state.hasFunction = true;
} else if (t.isIdentifier(node)) {
state.hasHelper = state.hasHelper || isHelper(node.callee);
}
return state;
}
/**
* Test if a node is or has a helper.
*/
function isHelper(node) {
if (t.isMemberExpression(node)) {
return isHelper(node.object) || isHelper(node.property);
} else if (t.isIdentifier(node)) {
return node.name === "require" || node.name[0] === "_";
} else if (t.isCallExpression(node)) {
return isHelper(node.callee);
} else if (t.isBinary(node) || t.isAssignmentExpression(node)) {
return (t.isIdentifier(node.left) && isHelper(node.left)) || isHelper(node.right);
} else {
return false;
}
}
/**
* [Please add a description.]
*/
function isType(node) {
return t.isLiteral(node) || t.isObjectExpression(node) || t.isArrayExpression(node) ||
t.isIdentifier(node) || t.isMemberExpression(node);
}
/**
* Tests for node types that need whitespace.
*/
exports.nodes = {
/**
* Test if AssignmentExpression needs whitespace.
*/
AssignmentExpression(node) {
var state = crawl(node.right);
if ((state.hasCall && state.hasHelper) || state.hasFunction) {
return {
before: state.hasFunction,
after: true
};
}
},
/**
* Test if SwitchCase needs whitespace.
*/
SwitchCase(node, parent) {
return {
before: node.consequent.length || parent.cases[0] === node
};
},
/**
* Test if LogicalExpression needs whitespace.
*/
LogicalExpression(node) {
if (t.isFunction(node.left) || t.isFunction(node.right)) {
return {
after: true
};
}
},
/**
* Test if Literal needs whitespace.
*/
Literal(node) {
if (node.value === "use strict") {
return {
after: true
};
}
},
/**
* Test if CallExpression needs whitespace.
*/
CallExpression(node) {
if (t.isFunction(node.callee) || isHelper(node)) {
return {
before: true,
after: true
};
}
},
/**
* Test if VariableDeclaration needs whitespace.
*/
VariableDeclaration(node) {
for (var i = 0; i < node.declarations.length; i++) {
var declar = node.declarations[i];
var enabled = isHelper(declar.id) && !isType(declar.init);
if (!enabled) {
var state = crawl(declar.init);
enabled = (isHelper(declar.init) && state.hasCall) || state.hasFunction;
}
if (enabled) {
return {
before: true,
after: true
};
}
}
},
/**
* Test if IfStatement needs whitespace.
*/
IfStatement(node) {
if (t.isBlockStatement(node.consequent)) {
return {
before: true,
after: true
};
}
}
};
/**
* Test if Property or SpreadProperty needs whitespace.
*/
exports.nodes.Property =
exports.nodes.SpreadProperty = function (node, parent) {
if (parent.properties[0] === node) {
return {
before: true
};
}
};
/**
* Returns lists from node types that need whitespace.
*/
exports.list = {
/**
* Return VariableDeclaration declarations init properties.
*/
VariableDeclaration(node) {
return map(node.declarations, "init");
},
/**
* Return VariableDeclaration elements.
*/
ArrayExpression(node) {
return node.elements;
},
/**
* Return VariableDeclaration properties.
*/
ObjectExpression(node) {
return node.properties;
}
};
/**
* Add whitespace tests for nodes and their aliases.
*/
each({
Function: true,
Class: true,
Loop: true,
LabeledStatement: true,
SwitchStatement: true,
TryStatement: true
}, function (amounts, type) {
if (isBoolean(amounts)) {
amounts = { after: amounts, before: amounts };
}
each([type].concat(t.FLIPPED_ALIAS_KEYS[type] || []), function (type) {
exports.nodes[type] = function () {
return amounts;
};
});
});