add removeLastNewline to generator and add newlines to generated nodes

This commit is contained in:
Sebastian McKenzie
2014-11-03 14:40:33 +11:00
parent 95f3ca6348
commit 9a3c973280
4 changed files with 132 additions and 69 deletions

View File

@@ -52,11 +52,17 @@ CodeGenerator.prototype.mark = function (node, locType) {
});
};
CodeGenerator.prototype.newline = function () {
CodeGenerator.prototype.newline = function (removeLast) {
if (!this.buf) return;
if (removeLast) this.removeLastNewline();
this.buf = this.buf.replace(/\n(\s+)$/, "\n");
this._push("\n");
};
CodeGenerator.prototype.removeLastNewline = function () {
if (this.isLast("\n")) this.buf = this.buf.slice(0, -1);
};
CodeGenerator.prototype.semicolon = function () {
this._push(";");
};
@@ -117,6 +123,8 @@ CodeGenerator.prototype.generate = function () {
this.print(ast);
this.buf = this.buf.trimRight();
var map = this.map;
if (map) map = map.toJSON();
@@ -180,13 +188,27 @@ CodeGenerator.prototype.printSequence = function (print, nodes, opts) {
};
opts.print = function (node, i) {
print(node, node && node.start != null && {
var needs = function (fn) {
if (node.start != null) {
// user node
if (!fn(node)) return;
} else {
// generated node
if (!t.needsWhitespace(node)) return;
if (self.isLast("{")) return;
self.removeLastNewline();
}
self.newline();
};
print(node, node && {
before: function () {
if (needsNewlineBefore(node)) self.newline();
needs(needsNewlineBefore);
},
after: function () {
if (needsNewlineAfter(node)) self.newline();
needs(needsNewlineAfter);
}
});
};

View File

@@ -13,7 +13,7 @@ exports.BlockStatement = function (node, print) {
this.push("{");
this.newline();
print.sequence(node.body, { indent: true });
this.newline();
this.newline(true);
this.push("}");
}
};

View File

@@ -27,7 +27,7 @@ exports.ClassBody = function (node, print) {
print.sequence(node.body);
this.dedent();
this.newline();
this.newline(true);
this.push("}");
}
};

View File

@@ -3,38 +3,48 @@ var b = require("./builders");
var n = require("acorn-ast-types").namedTypes;
var _ = require("lodash");
exports.FUNCTION_TYPES = ["ArrowFunctionExpression", "FunctionDeclaration", "FunctionExpression"];
exports.PATTERN_TYPES = ["ArrayPattern", "ObjectPattern"];
exports.BINARY_TYPES = ["BinaryExpression", "LogicalExpression"];
exports.UNARY_TYPES = ["UnaryExpression", "SpreadElement", "SpreadProperty"];
var t = exports;
exports.aliases = {
//
_.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;
t["is" + isKey] = function (node) {
return node && _.contains(types, node.type);
};
};
buildIs("Function", "FUNCTION", ["ArrowFunctionExpression", "FunctionDeclaration", "FunctionExpression"]);
buildIs("Class", "CALSS", ["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"]
FunctionExpression: ["Function"],
ClassDeclaration: ["Class"],
ClassExpression: ["Class"]
};
exports.isUnaryLike = function (node) {
return _.contains(exports.UNARY_TYPES, node.type);
};
//
exports.isBinary = function (node) {
return _.contains(exports.BINARY_TYPES, node.type);
};
exports.isFunction = function (node) {
return _.contains(exports.FUNCTION_TYPES, node.type);
};
exports.isPattern = function (node) {
return _.contains(exports.PATTERN_TYPES, node.type);
};
exports.isReferenced = function (node, parent) {
t.isReferenced = function (node, parent) {
// we're a property key so we aren't referenced
if (parent.type === "Property" && parent.key === node) return false;
if (t.isProperty(parent) && parent.key === node) return false;
var isMemberExpression = parent.type === "MemberExpression";
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;
@@ -48,18 +58,18 @@ exports.isReferenced = function (node, parent) {
return false;
};
exports.ensureBlock = function (node) {
node.body = exports.toBlock(node.body, node);
t.ensureBlock = function (node) {
node.body = t.toBlock(node.body, node);
};
exports.toBlock = function (node, parent) {
if (node.type === "BlockStatement") {
t.toBlock = function (node, parent) {
if (t.isBlockStatement(node)) {
return node;
}
if (!_.isArray(node)) {
if (!n.Statement.check(node)) {
if (exports.isFunction(parent)) {
if (t.isFunction(parent)) {
node = b.returnStatement(node);
} else {
node = b.expressionStatement(node);
@@ -72,43 +82,74 @@ exports.toBlock = function (node, parent) {
return b.blockStatement(node);
};
exports.getSpecifierName = function (specifier) {
t.getSpecifierName = function (specifier) {
return specifier.name || specifier.id;
};
exports.ensureExpressionType = function (node) {
node.type = {
FunctionDeclaration: "FunctionExpression",
ClassDeclaration: "ClassExpression"
}[node.type] || node.type;
return node;
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);
});
};
exports.needsParans = function (node, parent) {
t.needsParans = function (node, parent) {
if (!parent) return false;
//
if (exports.isUnaryLike(node)) {
return parent.type === "MemberExpression" && parent.object === node;
if (t.isUnaryLike(node)) {
return t.isMemberExpression(parent) && parent.object === node;
}
if (exports.isBinary(node)) {
if (t.isBinary(node)) {
//
if (parent.type === "CallExpression" && parent.callee === node) {
if (t.isCallExpression(parent) && parent.callee === node) {
return true;
}
//
if (exports.isUnaryLike(parent)) {
if (t.isUnaryLike(parent)) {
return true;
}
//
if (parent.type === "MemberExpression" && parent.object === node) {
if (t.isMemberExpression(parent) && parent.object === node) {
return true;
}
if (exports.isBinary(parent)) {
if (t.isBinary(parent)) {
var parentOp = parent.operator;
var parentPos = PRECEDENCE[parentOp];
@@ -125,8 +166,8 @@ exports.needsParans = function (node, parent) {
}
}
if (node.type === "SequenceExpression") {
if (parent.type === "ForStatement") {
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
@@ -134,7 +175,7 @@ exports.needsParans = function (node, parent) {
return false;
}
if (parent.type === "ExpressionStatement" && parent.expression === node) {
if (t.isExpressionStatement(parent) && parent.expression === node) {
return false;
}
@@ -144,9 +185,9 @@ exports.needsParans = function (node, parent) {
}
//
if (node.type === "YieldExpression") {
return exports.isBinary(parent)
|| exports.isUnaryLike(parent)
if (t.isYieldExpression(node)) {
return t.isBinary(parent)
|| t.isUnaryLike(parent)
|| parent.type === "CallExpression"
|| parent.type === "MemberExpression"
|| parent.type === "NewExpression"
@@ -154,61 +195,61 @@ exports.needsParans = function (node, parent) {
|| parent.type === "YieldExpression";
}
if (parent.type === "NewExpression" && parent.callee === node) {
if (t.isNewExpression(parent) && parent.callee === node) {
return traverse.hasType(node, "CallExpression");
}
// (1).valueOf()
if (node.type === "Literal" && _.isNumber(node.value) && parent.type === "MemberExpression" && parent.object === node) {
if (t.isLiteral(node) && _.isNumber(node.value) && t.isMemberExpression(parent) && parent.object === node) {
return true;
}
if (node.type === "AssignmentExpression" || node.type === "ConditionalExpression") {
if (t.isAssignmentExpression(node) || t.isConditionalExpression(node)) {
//
if (exports.isUnaryLike(parent)) {
if (t.isUnaryLike(parent)) {
return true;
}
//
if (exports.isBinary(parent)) {
if (t.isBinary(parent)) {
return true;
}
//
if (parent.type === "CallExpression" && parent.callee === node) {
if (t.isCallExpression(parent) && parent.callee === node) {
return true;
}
//
if (parent.type === "ConditionalExpression" && parent.test === node) {
if (t.isConditionalExpression(parent) && parent.test === node) {
return true;
}
//
if (parent.type === "MemberExpression" && parent.object === node) {
if (t.isMemberExpression(parent) && parent.object === node) {
return true;
}
}
if (node.type === "FunctionExpression") {
if (t.isFunctionExpression(node)) {
// function () {};
if (parent.type === "ExpressionStatement") {
if (t.isExpressionStatement(parent)) {
return true;
}
// (function test() {}).name;
if (parent.type === "MemberExpression" && parent.object === node) {
if (t.isMemberExpression(parent) && parent.object === node) {
return true;
}
// (function () {})();
if (parent.type === "CallExpression" && parent.callee === node) {
if (t.isCallExpression(parent) && parent.callee === node) {
return true;
}
}
// ({ x, y }) = { x: 5, y: 6 };
if (node.type === "ObjectPattern" && parent.type === "AssignmentExpression" && parent.left == node) {
if (t.isObjectPattern(node) && t.isAssignmentExpression(parent) && parent.left == node) {
return true;
}