diff --git a/lib/6to5/generation/node.js b/lib/6to5/generation/node.js deleted file mode 100644 index e2ec453297..0000000000 --- a/lib/6to5/generation/node.js +++ /dev/null @@ -1,294 +0,0 @@ -module.exports = Node; - -var t = require("../types"); -var _ = require("lodash"); - -function Node(node, parent) { - this.parent = parent; - this.node = node; -} - -// - -Node.whitespace = { - FunctionExpression: 1, - FunctionStatement: 1, - ClassExpression: 1, - ClassStatement: 1, - ForOfStatement: 1, - ForInStatement: 1, - ForStatement: 1, - SwitchStatement: 1, - IfStatement: { before: 1 }, - //Property: { before: 1 }, - Literal: { after: 1 } -}; - -_.each(Node.whitespace, function (amounts, type) { - if (_.isNumber(amounts)) amounts = { after: amounts, before: amounts }; - Node.whitespace[type] = amounts; -}); - -// - -Node.PRECEDENCE = {}; - -_.each([ - ["||"], - ["&&"], - ["|"], - ["^"], - ["&"], - ["==", "===", "!=", "!=="], - ["<", ">", "<=", ">=", "in", "instanceof"], - [">>", "<<", ">>>"], - ["+", "-"], - ["*", "/", "%"] -], function (tier, i) { - _.each(tier, function (op) { - Node.PRECEDENCE[op] = i; - }); -}); - -// - -Node.prototype.isUserWhitespacable = function () { - //var parent = this.parent; - var node = this.node; - - if (t.isUserWhitespacable(node)) { - return true; - } - - //if (t.isArrayExpression(parent)) { - // return true; - //} - - return false; -}; - -Node.prototype.needsWhitespace = function (type) { - var parent = this.parent; - var node = this.node; - if (!node) return 0; - - if (t.isExpressionStatement(node)) { - node = node.expression; - } - - if (type === "before") { - if (t.isProperty(node) && parent.properties[0] === node) { - return 1; - } - - if (t.isSwitchCase(node) && parent.cases[0] === node) { - return 1; - } - } - - if (type === "after") { - if (t.isCallExpression(node)) { - return 1; - } - - var exprs = []; - - if (t.isVariableDeclaration(node)) { - exprs = _.map(node.declarations, "init"); - } - - if (t.isArrayExpression(node)) { - exprs = node.elements; - } - - if (t.isObjectExpression(node)) { - exprs = node.properties; - } - - var lines = 0; - - _.each(exprs, function (expr) { - lines = Node.needsWhitespace(expr, node, type); - if (lines) return false; - }); - - if (lines) return lines; - } - - if (t.isCallExpression(node) && t.isFunction(node.callee)) { - return 1; - } - - var opts = Node.whitespace[node.type]; - return (opts && opts[type]) || 0; -}; - -Node.prototype.needsWhitespaceBefore = function () { - return this.needsWhitespace("before"); -}; - -Node.prototype.needsWhitespaceAfter = function () { - return this.needsWhitespace("after"); -}; - -Node.prototype.needsParens = function () { - var parent = this.parent; - var node = this.node; - - 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 = Node.PRECEDENCE[parentOp]; - - var nodeOp = node.operator; - var nodePos = Node.PRECEDENCE[nodeOp]; - - if (parentPos > nodePos) { - return true; - } - - if (parentPos === nodePos && parent.right === node) { - return true; - } - } - } - - if (t.isBinaryExpression(node) && node.operator === "in") { - // var i = (1 in []); - if (t.isVariableDeclarator(parent)) { - return true; - } - - // for ((1 in []);;); - if (t.isFor(parent)) { - return true; - } - } - - // (class {}); - if (t.isClassExpression(node) && t.isExpressionStatement(parent)) { - 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 t.isCallExpression(node) || _.some(node, function (val) { - return t.isCallExpression(val); - }); - } - - // (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; -}; - -_.each(Node.prototype, function (fn, key) { - Node[key] = function (node, parent) { - var n = new Node(node, parent); - - var args = _.toArray(arguments).slice(2); - return n[key].apply(n, args); - }; -}); diff --git a/lib/6to5/generation/node/index.js b/lib/6to5/generation/node/index.js new file mode 100644 index 0000000000..470fe00f24 --- /dev/null +++ b/lib/6to5/generation/node/index.js @@ -0,0 +1,93 @@ +module.exports = Node; + +var whitespace = require("./whitespace"); +var parens = require("./parentheses"); +var t = require("../../types"); +var _ = require("lodash"); + +var find = function (obj, node, parent) { + var result; + + _.each(obj, function (fn, type) { + if (t["is" + type](node)) { + result = fn(node, parent); + if (result != null) return false; + } + }); + + return result; +}; + +function Node(node, parent) { + this.parent = parent; + this.node = node; +} + +Node.prototype.isUserWhitespacable = function () { + //var parent = this.parent; + var node = this.node; + + if (t.isUserWhitespacable(node)) { + return true; + } + + //if (t.isArrayExpression(parent)) { + // return true; + //} + + return false; +}; + +Node.prototype.needsWhitespace = function (type) { + var parent = this.parent; + var node = this.node; + if (!node) return 0; + + if (t.isExpressionStatement(node)) { + node = node.expression; + } + + var lines = find(whitespace[type].nodes, node, parent); + if (lines) return lines; + + _.each(find(whitespace[type].list, node, parent), function (expr) { + lines = Node.needsWhitespace(expr, node, type); + if (lines) return false; + }); + if (lines) return lines; + + var opts = whitespace.static[node.type]; + return (opts && opts[type]) || 0; +}; + +Node.prototype.needsWhitespaceBefore = function () { + return this.needsWhitespace("before"); +}; + +Node.prototype.needsWhitespaceAfter = function () { + return this.needsWhitespace("after"); +}; + +Node.prototype.needsParens = function () { + var parent = this.parent; + var node = this.node; + + if (!parent) return false; + + if (t.isNewExpression(parent) && parent.callee === node) { + return t.isCallExpression(node) || _.some(node, function (val) { + return t.isCallExpression(val); + }); + } + + return find(parens, node, parent); +}; + +_.each(Node.prototype, function (fn, key) { + Node[key] = function (node, parent) { + var n = new Node(node, parent); + + var args = _.toArray(arguments).slice(2); + return n[key].apply(n, args); + }; +}); diff --git a/lib/6to5/generation/node/parentheses.js b/lib/6to5/generation/node/parentheses.js new file mode 100644 index 0000000000..32f4314d79 --- /dev/null +++ b/lib/6to5/generation/node/parentheses.js @@ -0,0 +1,151 @@ +var Node = require("./index"); +var t = require("../../types"); +var _ = require("lodash"); + +var PRECEDENCE = {}; + +_.each([ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "===", "!=", "!=="], + ["<", ">", "<=", ">=", "in", "instanceof"], + [">>", "<<", ">>>"], + ["+", "-"], + ["*", "/", "%"] +], function (tier, i) { + _.each(tier, function (op) { + PRECEDENCE[op] = i; + }); +}); + +exports.Binary = function (node, parent) { + 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 nodePos = PRECEDENCE[nodeOp]; + + if (parentPos > nodePos) { + return true; + } + + if (parentPos === nodePos && parent.right === node) { + return true; + } + } +}; + +exports.BinaryExpression = function (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; + } + } +}; + +exports.SequenceExpression = function (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; +}; + +exports.YieldExpression = function (node, parent) { + return t.isBinary(parent) || + t.isUnaryLike(parent) || + t.isCallExpression(parent) || + t.isMemberExpression(parent) || + t.isNewExpression(parent) || + t.isConditionalExpression(parent) || + t.isYieldExpression(parent); +}; + +exports.Literal = function (node, parent) { + // (1).valueOf() + if (_.isNumber(node.value) && t.isMemberExpression(parent) && parent.object === node) { + return true; + } +}; + +exports.ClassExpression = function (node, parent) { + return t.isExpressionStatement(parent); +}; + +exports.UnaryLike = function (node, parent) { + return t.isMemberExpression(parent) && parent.object === node; +}; + +exports.FunctionExpression = function (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; + } +}; + +exports.AssignmentExpression = +exports.ConditionalExpression = function (node, parent) { + 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; + } + + return false; +}; diff --git a/lib/6to5/generation/node/whitespace.js b/lib/6to5/generation/node/whitespace.js new file mode 100644 index 0000000000..cc44070ec6 --- /dev/null +++ b/lib/6to5/generation/node/whitespace.js @@ -0,0 +1,59 @@ +var _ = require("lodash"); +var t = require("../../types"); + +exports.static = { + FunctionExpression: 1, + FunctionStatement: 1, + ClassExpression: 1, + ClassStatement: 1, + ForOfStatement: 1, + ForInStatement: 1, + ForStatement: 1, + SwitchStatement: 1, + IfStatement: { before: 1 }, + CallExpression: { after: 1 }, + Literal: { after: 1 } +}; + +_.each(exports.static, function (amounts, type) { + if (_.isNumber(amounts)) amounts = { after: amounts, before: amounts }; + exports.static[type] = amounts; +}); + +exports.before = { + nodes: { + Property: function (node, parent) { + if (parent.properties[0] === node) { + return 1; + } + }, + + SwitchCase: function (node, parent) { + if (parent.cases[0] === node) { + return 1; + } + }, + + CallExpression: function (node, parent) { + if (t.isFunction(node.callee)) { + return 1; + } + } + } +}; + +exports.after = { + list: { + VariableDeclaration: function (node) { + return _.map(node.declarations, "init");; + }, + + ArrayExpression: function (node) { + return node.elements; + }, + + ObjectExpression: function (node) { + return node.properties; + } + } +};