diff --git a/lib/6to5/generator.js b/lib/6to5/generator.js index 4010cbd299..2e61b17eee 100644 --- a/lib/6to5/generator.js +++ b/lib/6to5/generator.js @@ -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); } }); }; diff --git a/lib/6to5/generators/base.js b/lib/6to5/generators/base.js index 00a36b426f..2fdecb1e61 100644 --- a/lib/6to5/generators/base.js +++ b/lib/6to5/generators/base.js @@ -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("}"); } }; diff --git a/lib/6to5/generators/classes.js b/lib/6to5/generators/classes.js index 4ed833ae33..06265e57bb 100644 --- a/lib/6to5/generators/classes.js +++ b/lib/6to5/generators/classes.js @@ -27,7 +27,7 @@ exports.ClassBody = function (node, print) { print.sequence(node.body); this.dedent(); - this.newline(); + this.newline(true); this.push("}"); } }; diff --git a/lib/6to5/types.js b/lib/6to5/types.js index 6ee402da8f..0200c4e65d 100644 --- a/lib/6to5/types.js +++ b/lib/6to5/types.js @@ -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; }