[Pkg-javascript-commits] [node-recast] 01/03: New upstream version 0.12.2
Julien Puydt
julien.puydt at laposte.net
Thu Mar 23 20:47:18 UTC 2017
This is an automated email from the git hooks/post-receive script.
jpuydt-guest pushed a commit to branch master
in repository node-recast.
commit 7fedcb88c39f0ca45452cc2bcd55579fe6ac67ec
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Thu Mar 23 21:24:24 2017 +0100
New upstream version 0.12.2
---
.travis.yml | 14 +-
README.md | 2 +-
lib/fast-path.js | 705 ++++++-------
lib/parser.js | 267 ++---
lib/patcher.js | 827 +++++++--------
lib/printer.js | 6 +-
lib/util.js | 37 +-
package.json | 12 +-
test/babylon.js | 40 +-
test/printer.js | 2988 +++++++++++++++++++++++++++---------------------------
10 files changed, 2502 insertions(+), 2396 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 7b3fa17..83ec23f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,18 +1,12 @@
language: node_js
node_js:
- - "7.0"
- - "6.0"
- - "5.0"
- - "4.0"
- - "iojs"
+ - "7"
+ - "6"
+ - "5"
+ - "4"
- "0.12"
- "0.11"
- "0.10"
- - "0.8"
-
-matrix:
- allow_failures:
- - node_js: "0.8"
# Allow Travis tests to run in containers.
sudo: false
diff --git a/README.md b/README.md
index 9ef9299..6d2d91b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# recast, _v_. [![Build Status](https://travis-ci.org/benjamn/recast.svg?branch=master)](https://travis-ci.org/benjamn/recast) [![Join the chat at https://gitter.im/benjamn/recast](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/benjamn/recast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+# recast, _v_. [![Build Status](https://travis-ci.org/benjamn/recast.svg?branch=master)](https://travis-ci.org/benjamn/recast) [![Join the chat at https://gitter.im/benjamn/recast](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/benjamn/recast?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Greenkeeper badge](https://badges.greenkeeper.io/benjamn/recast.svg)](https://greenkeeper.io/)
1. to give (a metal object) a different form by melting it down and reshaping it.
1. to form, fashion, or arrange again.
diff --git a/lib/fast-path.js b/lib/fast-path.js
index 017c57a..b8c8ffb 100644
--- a/lib/fast-path.js
+++ b/lib/fast-path.js
@@ -6,8 +6,8 @@ var isArray = types.builtInTypes.array;
var isNumber = types.builtInTypes.number;
function FastPath(value) {
- assert.ok(this instanceof FastPath);
- this.stack = [value];
+ assert.ok(this instanceof FastPath);
+ this.stack = [value];
}
var FPp = FastPath.prototype;
@@ -15,71 +15,77 @@ module.exports = FastPath;
// Static convenience function for coercing a value to a FastPath.
FastPath.from = function(obj) {
- if (obj instanceof FastPath) {
- // Return a defensive copy of any existing FastPath instances.
- return obj.copy();
- }
-
- if (obj instanceof types.NodePath) {
- // For backwards compatibility, unroll NodePath instances into
- // lightweight FastPath [..., name, value] stacks.
- var copy = Object.create(FastPath.prototype);
- var stack = [obj.value];
- for (var pp; (pp = obj.parentPath); obj = pp)
- stack.push(obj.name, pp.value);
- copy.stack = stack.reverse();
- return copy;
- }
+ if (obj instanceof FastPath) {
+ // Return a defensive copy of any existing FastPath instances.
+ return obj.copy();
+ }
+
+ if (obj instanceof types.NodePath) {
+ // For backwards compatibility, unroll NodePath instances into
+ // lightweight FastPath [..., name, value] stacks.
+ var copy = Object.create(FastPath.prototype);
+ var stack = [obj.value];
+ for (var pp; (pp = obj.parentPath); obj = pp)
+ stack.push(obj.name, pp.value);
+ copy.stack = stack.reverse();
+ return copy;
+ }
- // Otherwise use obj as the value of the new FastPath instance.
- return new FastPath(obj);
+ // Otherwise use obj as the value of the new FastPath instance.
+ return new FastPath(obj);
};
FPp.copy = function copy() {
- var copy = Object.create(FastPath.prototype);
- copy.stack = this.stack.slice(0);
- return copy;
+ var copy = Object.create(FastPath.prototype);
+ copy.stack = this.stack.slice(0);
+ return copy;
};
// The name of the current property is always the penultimate element of
// this.stack, and always a String.
FPp.getName = function getName() {
- var s = this.stack;
- var len = s.length;
- if (len > 1) {
- return s[len - 2];
- }
- // Since the name is always a string, null is a safe sentinel value to
- // return if we do not know the name of the (root) value.
- return null;
+ var s = this.stack;
+ var len = s.length;
+ if (len > 1) {
+ return s[len - 2];
+ }
+ // Since the name is always a string, null is a safe sentinel value to
+ // return if we do not know the name of the (root) value.
+ return null;
};
// The value of the current property is always the final element of
// this.stack.
FPp.getValue = function getValue() {
- var s = this.stack;
- return s[s.length - 1];
+ var s = this.stack;
+ return s[s.length - 1];
+};
+
+FPp.valueIsDuplicate = function () {
+ var s = this.stack;
+ var valueIndex = s.length - 1;
+ return s.lastIndexOf(s[valueIndex], valueIndex - 1) >= 0;
};
function getNodeHelper(path, count) {
- var s = path.stack;
+ var s = path.stack;
- for (var i = s.length - 1; i >= 0; i -= 2) {
- var value = s[i];
- if (n.Node.check(value) && --count < 0) {
- return value;
- }
+ for (var i = s.length - 1; i >= 0; i -= 2) {
+ var value = s[i];
+ if (n.Node.check(value) && --count < 0) {
+ return value;
}
+ }
- return null;
+ return null;
}
FPp.getNode = function getNode(count) {
- return getNodeHelper(this, ~~count);
+ return getNodeHelper(this, ~~count);
};
FPp.getParentNode = function getParentNode(count) {
- return getNodeHelper(this, ~~count + 1);
+ return getNodeHelper(this, ~~count + 1);
};
// The length of the stack can be either even or odd, depending on whether
@@ -88,11 +94,11 @@ FPp.getParentNode = function getParentNode(count) {
// even, though, which allows us to return the root value in constant time
// (i.e. without iterating backwards through the stack).
FPp.getRootValue = function getRootValue() {
- var s = this.stack;
- if (s.length % 2 === 0) {
- return s[1];
- }
- return s[0];
+ var s = this.stack;
+ if (s.length % 2 === 0) {
+ return s[1];
+ }
+ return s[0];
};
// Temporarily push properties named by string arguments given after the
@@ -101,18 +107,18 @@ FPp.getRootValue = function getRootValue() {
// be restored to its original state after the callback is finished, so it
// is probably a mistake to retain a reference to the path.
FPp.call = function call(callback/*, name1, name2, ... */) {
- var s = this.stack;
- var origLen = s.length;
- var value = s[origLen - 1];
- var argc = arguments.length;
- for (var i = 1; i < argc; ++i) {
- var name = arguments[i];
- value = value[name];
- s.push(name, value);
- }
- var result = callback(this);
- s.length = origLen;
- return result;
+ var s = this.stack;
+ var origLen = s.length;
+ var value = s[origLen - 1];
+ var argc = arguments.length;
+ for (var i = 1; i < argc; ++i) {
+ var name = arguments[i];
+ value = value[name];
+ s.push(name, value);
+ }
+ var result = callback(this);
+ s.length = origLen;
+ return result;
};
// Similar to FastPath.prototype.call, except that the value obtained by
@@ -120,254 +126,253 @@ FPp.call = function call(callback/*, name1, name2, ... */) {
// callback will be called with a reference to this path object for each
// element of the array.
FPp.each = function each(callback/*, name1, name2, ... */) {
- var s = this.stack;
- var origLen = s.length;
- var value = s[origLen - 1];
- var argc = arguments.length;
-
- for (var i = 1; i < argc; ++i) {
- var name = arguments[i];
- value = value[name];
- s.push(name, value);
+ var s = this.stack;
+ var origLen = s.length;
+ var value = s[origLen - 1];
+ var argc = arguments.length;
+
+ for (var i = 1; i < argc; ++i) {
+ var name = arguments[i];
+ value = value[name];
+ s.push(name, value);
+ }
+
+ for (var i = 0; i < value.length; ++i) {
+ if (i in value) {
+ s.push(i, value[i]);
+ // If the callback needs to know the value of i, call
+ // path.getName(), assuming path is the parameter name.
+ callback(this);
+ s.length -= 2;
}
+ }
- for (var i = 0; i < value.length; ++i) {
- if (i in value) {
- s.push(i, value[i]);
- // If the callback needs to know the value of i, call
- // path.getName(), assuming path is the parameter name.
- callback(this);
- s.length -= 2;
- }
- }
-
- s.length = origLen;
+ s.length = origLen;
};
// Similar to FastPath.prototype.each, except that the results of the
// callback function invocations are stored in an array and returned at
// the end of the iteration.
FPp.map = function map(callback/*, name1, name2, ... */) {
- var s = this.stack;
- var origLen = s.length;
- var value = s[origLen - 1];
- var argc = arguments.length;
-
- for (var i = 1; i < argc; ++i) {
- var name = arguments[i];
- value = value[name];
- s.push(name, value);
- }
-
- var result = new Array(value.length);
-
- for (var i = 0; i < value.length; ++i) {
- if (i in value) {
- s.push(i, value[i]);
- result[i] = callback(this, i);
- s.length -= 2;
- }
+ var s = this.stack;
+ var origLen = s.length;
+ var value = s[origLen - 1];
+ var argc = arguments.length;
+
+ for (var i = 1; i < argc; ++i) {
+ var name = arguments[i];
+ value = value[name];
+ s.push(name, value);
+ }
+
+ var result = new Array(value.length);
+
+ for (var i = 0; i < value.length; ++i) {
+ if (i in value) {
+ s.push(i, value[i]);
+ result[i] = callback(this, i);
+ s.length -= 2;
}
+ }
- s.length = origLen;
+ s.length = origLen;
- return result;
+ return result;
};
// Inspired by require("ast-types").NodePath.prototype.needsParens, but
// more efficient because we're iterating backwards through a stack.
FPp.needsParens = function(assumeExpressionContext) {
- var parent = this.getParentNode();
- if (!parent) {
- return false;
- }
+ var parent = this.getParentNode();
+ if (!parent) {
+ return false;
+ }
- var name = this.getName();
- var node = this.getNode();
+ var name = this.getName();
+ var node = this.getNode();
- // If the value of this path is some child of a Node and not a Node
- // itself, then it doesn't need parentheses. Only Node objects (in
- // fact, only Expression nodes) need parentheses.
- if (this.getValue() !== node) {
- return false;
- }
+ // If the value of this path is some child of a Node and not a Node
+ // itself, then it doesn't need parentheses. Only Node objects (in fact,
+ // only Expression nodes) need parentheses.
+ if (this.getValue() !== node) {
+ return false;
+ }
- // Only statements don't need parentheses.
- if (n.Statement.check(node)) {
- return false;
- }
+ // Only statements don't need parentheses.
+ if (n.Statement.check(node)) {
+ return false;
+ }
+
+ // Identifiers never need parentheses.
+ if (node.type === "Identifier") {
+ return false;
+ }
- // Identifiers never need parentheses.
- if (node.type === "Identifier") {
- return false;
+ if (parent.type === "ParenthesizedExpression") {
+ return false;
+ }
+
+ switch (node.type) {
+ case "UnaryExpression":
+ case "SpreadElement":
+ case "SpreadProperty":
+ return parent.type === "MemberExpression"
+ && name === "object"
+ && parent.object === node;
+
+ case "BinaryExpression":
+ case "LogicalExpression":
+ switch (parent.type) {
+ case "CallExpression":
+ return name === "callee"
+ && parent.callee === node;
+
+ case "UnaryExpression":
+ case "SpreadElement":
+ case "SpreadProperty":
+ return true;
+
+ case "MemberExpression":
+ return name === "object"
+ && parent.object === node;
+
+ case "BinaryExpression":
+ case "LogicalExpression":
+ var po = parent.operator;
+ var pp = PRECEDENCE[po];
+ var no = node.operator;
+ var np = PRECEDENCE[no];
+
+ if (pp > np) {
+ return true;
+ }
+
+ if (pp === np && name === "right") {
+ assert.strictEqual(parent.right, node);
+ return true;
+ }
+
+ default:
+ return false;
}
- if (parent.type === "ParenthesizedExpression") {
- return false;
+ case "SequenceExpression":
+ switch (parent.type) {
+ case "ReturnStatement":
+ return false;
+
+ case "ForStatement":
+ // 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;
+
+ case "ExpressionStatement":
+ return name !== "expression";
+
+ default:
+ // Otherwise err on the side of overparenthesization, adding
+ // explicit exceptions above if this proves overzealous.
+ return true;
}
- switch (node.type) {
+ case "YieldExpression":
+ switch (parent.type) {
+ case "BinaryExpression":
+ case "LogicalExpression":
case "UnaryExpression":
case "SpreadElement":
case "SpreadProperty":
- return parent.type === "MemberExpression"
- && name === "object"
- && parent.object === node;
+ case "CallExpression":
+ case "MemberExpression":
+ case "NewExpression":
+ case "ConditionalExpression":
+ case "YieldExpression":
+ return true;
+ default:
+ return false;
+ }
+
+ case "IntersectionTypeAnnotation":
+ case "UnionTypeAnnotation":
+ return parent.type === "NullableTypeAnnotation";
+
+ case "Literal":
+ return parent.type === "MemberExpression"
+ && isNumber.check(node.value)
+ && name === "object"
+ && parent.object === node;
+
+ case "AssignmentExpression":
+ case "ConditionalExpression":
+ switch (parent.type) {
+ case "UnaryExpression":
+ case "SpreadElement":
+ case "SpreadProperty":
case "BinaryExpression":
case "LogicalExpression":
- switch (parent.type) {
- case "CallExpression":
- return name === "callee"
- && parent.callee === node;
-
- case "UnaryExpression":
- case "SpreadElement":
- case "SpreadProperty":
- return true;
-
- case "MemberExpression":
- return name === "object"
- && parent.object === node;
-
- case "BinaryExpression":
- case "LogicalExpression":
- var po = parent.operator;
- var pp = PRECEDENCE[po];
- var no = node.operator;
- var np = PRECEDENCE[no];
-
- if (pp > np) {
- return true;
- }
-
- if (pp === np && name === "right") {
- assert.strictEqual(parent.right, node);
- return true;
- }
-
- default:
- return false;
- }
-
- case "SequenceExpression":
- switch (parent.type) {
- case "ReturnStatement":
- return false;
-
- case "ForStatement":
- // 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;
-
- case "ExpressionStatement":
- return name !== "expression";
-
- default:
- // Otherwise err on the side of overparenthesization, adding
- // explicit exceptions above if this proves overzealous.
- return true;
- }
+ return true;
+
+ case "CallExpression":
+ return name === "callee"
+ && parent.callee === node;
- case "YieldExpression":
- switch (parent.type) {
- case "BinaryExpression":
- case "LogicalExpression":
- case "UnaryExpression":
- case "SpreadElement":
- case "SpreadProperty":
- case "CallExpression":
- case "MemberExpression":
- case "NewExpression":
- case "ConditionalExpression":
- case "YieldExpression":
- return true;
-
- default:
- return false;
- }
-
- case "IntersectionTypeAnnotation":
- case "UnionTypeAnnotation":
- return parent.type === "NullableTypeAnnotation";
-
- case "Literal":
- return parent.type === "MemberExpression"
- && isNumber.check(node.value)
- && name === "object"
- && parent.object === node;
-
- case "AssignmentExpression":
case "ConditionalExpression":
- switch (parent.type) {
- case "UnaryExpression":
- case "SpreadElement":
- case "SpreadProperty":
- case "BinaryExpression":
- case "LogicalExpression":
- return true;
-
- case "CallExpression":
- return name === "callee"
- && parent.callee === node;
-
- case "ConditionalExpression":
- return name === "test"
- && parent.test === node;
-
- case "MemberExpression":
- return name === "object"
- && parent.object === node;
-
- default:
- return false;
- }
-
- case "ArrowFunctionExpression":
- if(n.CallExpression.check(parent) && name === 'callee') {
- return true;
- }
- if(n.MemberExpression.check(parent) && name === 'object') {
- return true;
- }
-
- return isBinary(parent);
-
- case "ObjectExpression":
- if (parent.type === "ArrowFunctionExpression" &&
- name === "body") {
- return true;
- }
+ return name === "test"
+ && parent.test === node;
+
+ case "MemberExpression":
+ return name === "object"
+ && parent.object === node;
default:
- if (parent.type === "NewExpression" &&
- name === "callee" &&
- parent.callee === node) {
- return containsCallExpression(node);
- }
+ return false;
}
- if (assumeExpressionContext !== true &&
- !this.canBeFirstInStatement() &&
- this.firstInStatement())
- return true;
+ case "ArrowFunctionExpression":
+ if(n.CallExpression.check(parent) && name === 'callee') {
+ return true;
+ }
+ if(n.MemberExpression.check(parent) && name === 'object') {
+ return true;
+ }
- return false;
+ return isBinary(parent);
+
+ case "ObjectExpression":
+ if (parent.type === "ArrowFunctionExpression" &&
+ name === "body") {
+ return true;
+ }
+
+ default:
+ if (parent.type === "NewExpression" &&
+ name === "callee" &&
+ parent.callee === node) {
+ return containsCallExpression(node);
+ }
+ }
+
+ if (assumeExpressionContext !== true &&
+ !this.canBeFirstInStatement() &&
+ this.firstInStatement())
+ return true;
+
+ return false;
};
function isBinary(node) {
- return n.BinaryExpression.check(node)
- || n.LogicalExpression.check(node);
+ return n.BinaryExpression.check(node)
+ || n.LogicalExpression.check(node);
}
function isUnaryLike(node) {
- return n.UnaryExpression.check(node)
- // I considered making SpreadElement and SpreadProperty subtypes
- // of UnaryExpression, but they're not really Expression nodes.
- || (n.SpreadElement && n.SpreadElement.check(node))
- || (n.SpreadProperty && n.SpreadProperty.check(node));
+ return n.UnaryExpression.check(node)
+ // I considered making SpreadElement and SpreadProperty subtypes of
+ // UnaryExpression, but they're not really Expression nodes.
+ || (n.SpreadElement && n.SpreadElement.check(node))
+ || (n.SpreadProperty && n.SpreadProperty.check(node));
}
var PRECEDENCE = {};
@@ -382,105 +387,105 @@ var PRECEDENCE = {};
["+", "-"],
["*", "/", "%", "**"]
].forEach(function(tier, i) {
- tier.forEach(function(op) {
- PRECEDENCE[op] = i;
- });
+ tier.forEach(function(op) {
+ PRECEDENCE[op] = i;
+ });
});
function containsCallExpression(node) {
- if (n.CallExpression.check(node)) {
- return true;
- }
+ if (n.CallExpression.check(node)) {
+ return true;
+ }
- if (isArray.check(node)) {
- return node.some(containsCallExpression);
- }
+ if (isArray.check(node)) {
+ return node.some(containsCallExpression);
+ }
- if (n.Node.check(node)) {
- return types.someField(node, function(name, child) {
- return containsCallExpression(child);
- });
- }
+ if (n.Node.check(node)) {
+ return types.someField(node, function(name, child) {
+ return containsCallExpression(child);
+ });
+ }
- return false;
+ return false;
}
FPp.canBeFirstInStatement = function() {
- var node = this.getNode();
- return !n.FunctionExpression.check(node)
- && !n.ObjectExpression.check(node);
+ var node = this.getNode();
+ return !n.FunctionExpression.check(node)
+ && !n.ObjectExpression.check(node);
};
FPp.firstInStatement = function() {
- var s = this.stack;
- var parentName, parent;
- var childName, child;
-
- for (var i = s.length - 1; i >= 0; i -= 2) {
- if (n.Node.check(s[i])) {
- childName = parentName;
- child = parent;
- parentName = s[i - 1];
- parent = s[i];
- }
-
- if (!parent || !child) {
- continue;
- }
-
- if (n.BlockStatement.check(parent) &&
- parentName === "body" &&
- childName === 0) {
- assert.strictEqual(parent.body[0], child);
- return true;
- }
-
- if (n.ExpressionStatement.check(parent) &&
- childName === "expression") {
- assert.strictEqual(parent.expression, child);
- return true;
- }
-
- if (n.SequenceExpression.check(parent) &&
- parentName === "expressions" &&
- childName === 0) {
- assert.strictEqual(parent.expressions[0], child);
- continue;
- }
-
- if (n.CallExpression.check(parent) &&
- childName === "callee") {
- assert.strictEqual(parent.callee, child);
- continue;
- }
-
- if (n.MemberExpression.check(parent) &&
- childName === "object") {
- assert.strictEqual(parent.object, child);
- continue;
- }
-
- if (n.ConditionalExpression.check(parent) &&
- childName === "test") {
- assert.strictEqual(parent.test, child);
- continue;
- }
-
- if (isBinary(parent) &&
- childName === "left") {
- assert.strictEqual(parent.left, child);
- continue;
- }
-
- if (n.UnaryExpression.check(parent) &&
- !parent.prefix &&
- childName === "argument") {
- assert.strictEqual(parent.argument, child);
- continue;
- }
-
- return false;
+ var s = this.stack;
+ var parentName, parent;
+ var childName, child;
+
+ for (var i = s.length - 1; i >= 0; i -= 2) {
+ if (n.Node.check(s[i])) {
+ childName = parentName;
+ child = parent;
+ parentName = s[i - 1];
+ parent = s[i];
}
- return true;
+ if (!parent || !child) {
+ continue;
+ }
+
+ if (n.BlockStatement.check(parent) &&
+ parentName === "body" &&
+ childName === 0) {
+ assert.strictEqual(parent.body[0], child);
+ return true;
+ }
+
+ if (n.ExpressionStatement.check(parent) &&
+ childName === "expression") {
+ assert.strictEqual(parent.expression, child);
+ return true;
+ }
+
+ if (n.SequenceExpression.check(parent) &&
+ parentName === "expressions" &&
+ childName === 0) {
+ assert.strictEqual(parent.expressions[0], child);
+ continue;
+ }
+
+ if (n.CallExpression.check(parent) &&
+ childName === "callee") {
+ assert.strictEqual(parent.callee, child);
+ continue;
+ }
+
+ if (n.MemberExpression.check(parent) &&
+ childName === "object") {
+ assert.strictEqual(parent.object, child);
+ continue;
+ }
+
+ if (n.ConditionalExpression.check(parent) &&
+ childName === "test") {
+ assert.strictEqual(parent.test, child);
+ continue;
+ }
+
+ if (isBinary(parent) &&
+ childName === "left") {
+ assert.strictEqual(parent.left, child);
+ continue;
+ }
+
+ if (n.UnaryExpression.check(parent) &&
+ !parent.prefix &&
+ childName === "argument") {
+ assert.strictEqual(parent.argument, child);
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
};
diff --git a/lib/parser.js b/lib/parser.js
index 60a5164..17f3b8a 100644
--- a/lib/parser.js
+++ b/lib/parser.js
@@ -10,149 +10,162 @@ var normalizeOptions = require("./options").normalize;
var fromString = require("./lines").fromString;
var attachComments = require("./comments").attach;
var util = require("./util");
+var Map = global.Map || require("core-js/es6/map");
exports.parse = function parse(source, options) {
- options = normalizeOptions(options);
-
- var lines = fromString(source, options);
-
- var sourceWithoutTabs = lines.toString({
- tabWidth: options.tabWidth,
- reuseWhitespace: false,
- useTabs: false
- });
-
- var comments = [];
- var program = options.parser.parse(sourceWithoutTabs, {
- jsx: true,
- loc: true,
- locations: true,
- range: options.range,
- comment: true,
- onComment: comments,
- tolerant: options.tolerant,
- ecmaVersion: 6,
- sourceType: 'module'
- });
-
- // If the source was empty, some parsers give loc.{start,end}.line
- // values of 0, instead of the minimum of 1.
- util.fixFaultyLocations(program, lines);
-
- program.loc = program.loc || {
- start: lines.firstPos(),
- end: lines.lastPos()
+ options = normalizeOptions(options);
+
+ var lines = fromString(source, options);
+
+ var sourceWithoutTabs = lines.toString({
+ tabWidth: options.tabWidth,
+ reuseWhitespace: false,
+ useTabs: false
+ });
+
+ var comments = [];
+ var program = options.parser.parse(sourceWithoutTabs, {
+ jsx: true,
+ loc: true,
+ locations: true,
+ range: options.range,
+ comment: true,
+ onComment: comments,
+ tolerant: options.tolerant,
+ ecmaVersion: 6,
+ sourceType: 'module'
+ });
+
+ // If the source was empty, some parsers give loc.{start,end}.line
+ // values of 0, instead of the minimum of 1.
+ util.fixFaultyLocations(program, lines);
+
+ program.loc = program.loc || {
+ start: lines.firstPos(),
+ end: lines.lastPos()
+ };
+
+ program.loc.lines = lines;
+ program.loc.indent = 0;
+
+ // Expand the Program node's .loc to include all comments, since
+ // typically its .loc.start and .loc.end will coincide with those of the
+ // first and last statements, respectively, excluding any comments that
+ // fall outside that region.
+ var trueProgramLoc = util.getTrueLoc(program, lines);
+ program.loc.start = trueProgramLoc.start;
+ program.loc.end = trueProgramLoc.end;
+
+ if (program.comments) {
+ comments = program.comments;
+ delete program.comments;
+ }
+
+ // In order to ensure we reprint leading and trailing program comments,
+ // wrap the original Program node with a File node.
+ var file = program;
+ if (file.type === "Program") {
+ var file = b.file(program, options.sourceFileName || null);
+ file.loc = {
+ lines: lines,
+ indent: 0,
+ start: lines.firstPos(),
+ end: lines.lastPos()
};
-
- program.loc.lines = lines;
- program.loc.indent = 0;
-
- // Expand the Program node's .loc to include all comments, since
- // typically its .loc.start and .loc.end will coincide with those of
- // the first and last statements, respectively, excluding any comments
- // that fall outside that region.
- var trueProgramLoc = util.getTrueLoc(program, lines);
- program.loc.start = trueProgramLoc.start;
- program.loc.end = trueProgramLoc.end;
-
- if (program.comments) {
- comments = program.comments;
- delete program.comments;
- }
-
- // In order to ensure we reprint leading and trailing program
- // comments, wrap the original Program node with a File node.
- var file = program;
- if (file.type === "Program") {
- var file = b.file(program, options.sourceFileName || null);
- file.loc = {
- lines: lines,
- indent: 0,
- start: lines.firstPos(),
- end: lines.lastPos()
- };
- } else if (file.type === "File") {
- program = file.program;
- }
-
- // Passing file.program here instead of just file means that initial
- // comments will be attached to program.body[0] instead of program.
- attachComments(
- comments,
- program.body.length ? file.program : file,
- lines
- );
-
- // Return a copy of the original AST so that any changes made may be
- // compared to the original.
- return new TreeCopier(lines).copy(file);
+ } else if (file.type === "File") {
+ program = file.program;
+ }
+
+ // Passing file.program here instead of just file means that initial
+ // comments will be attached to program.body[0] instead of program.
+ attachComments(
+ comments,
+ program.body.length ? file.program : file,
+ lines
+ );
+
+ // Return a copy of the original AST so that any changes made may be
+ // compared to the original.
+ return new TreeCopier(lines).copy(file);
};
function TreeCopier(lines) {
- assert.ok(this instanceof TreeCopier);
- this.lines = lines;
- this.indent = 0;
+ assert.ok(this instanceof TreeCopier);
+ this.lines = lines;
+ this.indent = 0;
+ this.seen = new Map;
}
var TCp = TreeCopier.prototype;
TCp.copy = function(node) {
- if (isArray.check(node)) {
- return node.map(this.copy, this);
- }
+ if (this.seen.has(node)) {
+ return this.seen.get(node);
+ }
+
+ if (isArray.check(node)) {
+ var copy = new Array(node.length);
+ this.seen.set(node, copy);
+ node.forEach(function (item, i) {
+ copy[i] = this.copy(item);
+ }, this);
+ return copy;
+ }
- if (!isObject.check(node)) {
- return node;
- }
+ if (!isObject.check(node)) {
+ return node;
+ }
- util.fixFaultyLocations(node, this.lines);
-
- var copy = Object.create(Object.getPrototypeOf(node), {
- original: { // Provide a link from the copy to the original.
- value: node,
- configurable: false,
- enumerable: false,
- writable: true
- }
- });
-
- var loc = node.loc;
- var oldIndent = this.indent;
- var newIndent = oldIndent;
-
- if (loc) {
- // When node is a comment, we set node.loc.indent to
- // node.loc.start.column so that, when/if we print the comment by
- // itself, we can strip that much whitespace from the left margin
- // of the comment. This only really matters for multiline Block
- // comments, but it doesn't hurt for Line comments.
- if (node.type === "Block" || node.type === "Line" ||
- node.type === "CommentBlock" || node.type === "CommentLine" ||
- this.lines.isPrecededOnlyByWhitespace(loc.start)) {
- newIndent = this.indent = loc.start.column;
- }
-
- loc.lines = this.lines;
- loc.indent = newIndent;
+ util.fixFaultyLocations(node, this.lines);
+
+ var copy = Object.create(Object.getPrototypeOf(node), {
+ original: { // Provide a link from the copy to the original.
+ value: node,
+ configurable: false,
+ enumerable: false,
+ writable: true
+ }
+ });
+
+ this.seen.set(node, copy);
+
+ var loc = node.loc;
+ var oldIndent = this.indent;
+ var newIndent = oldIndent;
+
+ if (loc) {
+ // When node is a comment, we set node.loc.indent to
+ // node.loc.start.column so that, when/if we print the comment by
+ // itself, we can strip that much whitespace from the left margin of
+ // the comment. This only really matters for multiline Block comments,
+ // but it doesn't hurt for Line comments.
+ if (node.type === "Block" || node.type === "Line" ||
+ node.type === "CommentBlock" || node.type === "CommentLine" ||
+ this.lines.isPrecededOnlyByWhitespace(loc.start)) {
+ newIndent = this.indent = loc.start.column;
}
- var keys = Object.keys(node);
- var keyCount = keys.length;
- for (var i = 0; i < keyCount; ++i) {
- var key = keys[i];
- if (key === "loc") {
- copy[key] = node[key];
- } else if (key === "tokens" &&
- node.type === "File") {
- // Preserve file.tokens (uncopied) in case client code cares
- // about it, even though Recast ignores it when reprinting.
- copy[key] = node[key];
- } else {
- copy[key] = this.copy(node[key]);
- }
+ loc.lines = this.lines;
+ loc.indent = newIndent;
+ }
+
+ var keys = Object.keys(node);
+ var keyCount = keys.length;
+ for (var i = 0; i < keyCount; ++i) {
+ var key = keys[i];
+ if (key === "loc") {
+ copy[key] = node[key];
+ } else if (key === "tokens" &&
+ node.type === "File") {
+ // Preserve file.tokens (uncopied) in case client code cares about
+ // it, even though Recast ignores it when reprinting.
+ copy[key] = node[key];
+ } else {
+ copy[key] = this.copy(node[key]);
}
+ }
- this.indent = oldIndent;
+ this.indent = oldIndent;
- return copy;
+ return copy;
};
diff --git a/lib/patcher.js b/lib/patcher.js
index bddfe68..ce66b12 100644
--- a/lib/patcher.js
+++ b/lib/patcher.js
@@ -15,378 +15,391 @@ var isString = types.builtInTypes.string;
var riskyAdjoiningCharExp = /[0-9a-z_$]/i;
function Patcher(lines) {
- assert.ok(this instanceof Patcher);
- assert.ok(lines instanceof linesModule.Lines);
+ assert.ok(this instanceof Patcher);
+ assert.ok(lines instanceof linesModule.Lines);
- var self = this,
- replacements = [];
+ var self = this,
+ replacements = [];
- self.replace = function(loc, lines) {
- if (isString.check(lines))
- lines = linesModule.fromString(lines);
+ self.replace = function(loc, lines) {
+ if (isString.check(lines))
+ lines = linesModule.fromString(lines);
- replacements.push({
- lines: lines,
- start: loc.start,
- end: loc.end
- });
+ replacements.push({
+ lines: lines,
+ start: loc.start,
+ end: loc.end
+ });
+ };
+
+ self.get = function(loc) {
+ // If no location is provided, return the complete Lines object.
+ loc = loc || {
+ start: { line: 1, column: 0 },
+ end: { line: lines.length,
+ column: lines.getLineLength(lines.length) }
};
- self.get = function(loc) {
- // If no location is provided, return the complete Lines object.
- loc = loc || {
- start: { line: 1, column: 0 },
- end: { line: lines.length,
- column: lines.getLineLength(lines.length) }
- };
-
- var sliceFrom = loc.start,
- toConcat = [];
-
- function pushSlice(from, to) {
- assert.ok(comparePos(from, to) <= 0);
- toConcat.push(lines.slice(from, to));
- }
-
- replacements.sort(function(a, b) {
- return comparePos(a.start, b.start);
- }).forEach(function(rep) {
- if (comparePos(sliceFrom, rep.start) > 0) {
- // Ignore nested replacement ranges.
- } else {
- pushSlice(sliceFrom, rep.start);
- toConcat.push(rep.lines);
- sliceFrom = rep.end;
- }
- });
+ var sliceFrom = loc.start,
+ toConcat = [];
- pushSlice(sliceFrom, loc.end);
+ function pushSlice(from, to) {
+ assert.ok(comparePos(from, to) <= 0);
+ toConcat.push(lines.slice(from, to));
+ }
- return linesModule.concat(toConcat);
- };
+ replacements.sort(function(a, b) {
+ return comparePos(a.start, b.start);
+ }).forEach(function(rep) {
+ if (comparePos(sliceFrom, rep.start) > 0) {
+ // Ignore nested replacement ranges.
+ } else {
+ pushSlice(sliceFrom, rep.start);
+ toConcat.push(rep.lines);
+ sliceFrom = rep.end;
+ }
+ });
+
+ pushSlice(sliceFrom, loc.end);
+
+ return linesModule.concat(toConcat);
+ };
}
exports.Patcher = Patcher;
var Pp = Patcher.prototype;
Pp.tryToReprintComments = function(newNode, oldNode, print) {
- var patcher = this;
-
- if (!newNode.comments &&
- !oldNode.comments) {
- // We were (vacuously) able to reprint all the comments!
- return true;
- }
+ var patcher = this;
- var newPath = FastPath.from(newNode);
- var oldPath = FastPath.from(oldNode);
-
- newPath.stack.push("comments", getSurroundingComments(newNode));
- oldPath.stack.push("comments", getSurroundingComments(oldNode));
-
- var reprints = [];
- var ableToReprintComments =
- findArrayReprints(newPath, oldPath, reprints);
-
- // No need to pop anything from newPath.stack or oldPath.stack, since
- // newPath and oldPath are fresh local variables.
-
- if (ableToReprintComments && reprints.length > 0) {
- reprints.forEach(function(reprint) {
- var oldComment = reprint.oldPath.getValue();
- assert.ok(oldComment.leading || oldComment.trailing);
- patcher.replace(
- oldComment.loc,
- // Comments can't have .comments, so it doesn't matter
- // whether we print with comments or without.
- print(reprint.newPath).indentTail(oldComment.loc.indent)
- );
- });
- }
+ if (!newNode.comments &&
+ !oldNode.comments) {
+ // We were (vacuously) able to reprint all the comments!
+ return true;
+ }
+
+ var newPath = FastPath.from(newNode);
+ var oldPath = FastPath.from(oldNode);
+
+ newPath.stack.push("comments", getSurroundingComments(newNode));
+ oldPath.stack.push("comments", getSurroundingComments(oldNode));
+
+ var reprints = [];
+ var ableToReprintComments =
+ findArrayReprints(newPath, oldPath, reprints);
+
+ // No need to pop anything from newPath.stack or oldPath.stack, since
+ // newPath and oldPath are fresh local variables.
+
+ if (ableToReprintComments && reprints.length > 0) {
+ reprints.forEach(function(reprint) {
+ var oldComment = reprint.oldPath.getValue();
+ assert.ok(oldComment.leading || oldComment.trailing);
+ patcher.replace(
+ oldComment.loc,
+ // Comments can't have .comments, so it doesn't matter whether we
+ // print with comments or without.
+ print(reprint.newPath).indentTail(oldComment.loc.indent)
+ );
+ });
+ }
- return ableToReprintComments;
+ return ableToReprintComments;
};
// Get all comments that are either leading or trailing, ignoring any
// comments that occur inside node.loc. Returns an empty array for nodes
// with no leading or trailing comments.
function getSurroundingComments(node) {
- var result = [];
- if (node.comments &&
- node.comments.length > 0) {
- node.comments.forEach(function(comment) {
- if (comment.leading || comment.trailing) {
- result.push(comment);
- }
- });
- }
- return result;
+ var result = [];
+ if (node.comments &&
+ node.comments.length > 0) {
+ node.comments.forEach(function(comment) {
+ if (comment.leading || comment.trailing) {
+ result.push(comment);
+ }
+ });
+ }
+ return result;
}
Pp.deleteComments = function(node) {
- if (!node.comments) {
- return;
+ if (!node.comments) {
+ return;
+ }
+
+ var patcher = this;
+
+ node.comments.forEach(function(comment) {
+ if (comment.leading) {
+ // Delete leading comments along with any trailing whitespace they
+ // might have.
+ patcher.replace({
+ start: comment.loc.start,
+ end: node.loc.lines.skipSpaces(
+ comment.loc.end, false, false)
+ }, "");
+
+ } else if (comment.trailing) {
+ // Delete trailing comments along with any leading whitespace they
+ // might have.
+ patcher.replace({
+ start: node.loc.lines.skipSpaces(
+ comment.loc.start, true, false),
+ end: comment.loc.end
+ }, "");
}
-
- var patcher = this;
-
- node.comments.forEach(function(comment) {
- if (comment.leading) {
- // Delete leading comments along with any trailing whitespace
- // they might have.
- patcher.replace({
- start: comment.loc.start,
- end: node.loc.lines.skipSpaces(
- comment.loc.end, false, false)
- }, "");
-
- } else if (comment.trailing) {
- // Delete trailing comments along with any leading whitespace
- // they might have.
- patcher.replace({
- start: node.loc.lines.skipSpaces(
- comment.loc.start, true, false),
- end: comment.loc.end
- }, "");
- }
- });
+ });
};
exports.getReprinter = function(path) {
- assert.ok(path instanceof FastPath);
-
- // Make sure that this path refers specifically to a Node, rather than
- // some non-Node subproperty of a Node.
- var node = path.getValue();
- if (!Printable.check(node))
- return;
-
- var orig = node.original;
- var origLoc = orig && orig.loc;
- var lines = origLoc && origLoc.lines;
- var reprints = [];
-
- if (!lines || !findReprints(path, reprints))
- return;
-
- return function(print) {
- var patcher = new Patcher(lines);
-
- reprints.forEach(function(reprint) {
- var newNode = reprint.newPath.getValue();
- var oldNode = reprint.oldPath.getValue();
-
- SourceLocation.assert(oldNode.loc, true);
-
- var needToPrintNewPathWithComments =
- !patcher.tryToReprintComments(newNode, oldNode, print)
-
- if (needToPrintNewPathWithComments) {
- // Since we were not able to preserve all leading/trailing
- // comments, we delete oldNode's comments, print newPath
- // with comments, and then patch the resulting lines where
- // oldNode used to be.
- patcher.deleteComments(oldNode);
- }
-
- var newLines = print(
- reprint.newPath,
- needToPrintNewPathWithComments
- ).indentTail(oldNode.loc.indent);
-
- var nls = needsLeadingSpace(lines, oldNode.loc, newLines);
- var nts = needsTrailingSpace(lines, oldNode.loc, newLines);
-
- // If we try to replace the argument of a ReturnStatement like
- // return"asdf" with e.g. a literal null expression, we run
- // the risk of ending up with returnnull, so we need to add an
- // extra leading space in situations where that might
- // happen. Likewise for "asdf"in obj. See #170.
- if (nls || nts) {
- var newParts = [];
- nls && newParts.push(" ");
- newParts.push(newLines);
- nts && newParts.push(" ");
- newLines = linesModule.concat(newParts);
- }
-
- patcher.replace(oldNode.loc, newLines);
- });
+ assert.ok(path instanceof FastPath);
+
+ // Make sure that this path refers specifically to a Node, rather than
+ // some non-Node subproperty of a Node.
+ var node = path.getValue();
+ if (!Printable.check(node))
+ return;
+
+ var orig = node.original;
+ var origLoc = orig && orig.loc;
+ var lines = origLoc && origLoc.lines;
+ var reprints = [];
+
+ if (!lines || !findReprints(path, reprints))
+ return;
+
+ return function(print) {
+ var patcher = new Patcher(lines);
+
+ reprints.forEach(function(reprint) {
+ var newNode = reprint.newPath.getValue();
+ var oldNode = reprint.oldPath.getValue();
+
+ SourceLocation.assert(oldNode.loc, true);
+
+ var needToPrintNewPathWithComments =
+ !patcher.tryToReprintComments(newNode, oldNode, print)
+
+ if (needToPrintNewPathWithComments) {
+ // Since we were not able to preserve all leading/trailing
+ // comments, we delete oldNode's comments, print newPath with
+ // comments, and then patch the resulting lines where oldNode used
+ // to be.
+ patcher.deleteComments(oldNode);
+ }
+
+ var newLines = print(
+ reprint.newPath,
+ needToPrintNewPathWithComments
+ ).indentTail(oldNode.loc.indent);
+
+ var nls = needsLeadingSpace(lines, oldNode.loc, newLines);
+ var nts = needsTrailingSpace(lines, oldNode.loc, newLines);
+
+ // If we try to replace the argument of a ReturnStatement like
+ // return"asdf" with e.g. a literal null expression, we run the risk
+ // of ending up with returnnull, so we need to add an extra leading
+ // space in situations where that might happen. Likewise for
+ // "asdf"in obj. See #170.
+ if (nls || nts) {
+ var newParts = [];
+ nls && newParts.push(" ");
+ newParts.push(newLines);
+ nts && newParts.push(" ");
+ newLines = linesModule.concat(newParts);
+ }
+
+ patcher.replace(oldNode.loc, newLines);
+ });
- // Recall that origLoc is the .loc of an ancestor node that is
- // guaranteed to contain all the reprinted nodes and comments.
- return patcher.get(origLoc).indentTail(-orig.loc.indent);
- };
+ // Recall that origLoc is the .loc of an ancestor node that is
+ // guaranteed to contain all the reprinted nodes and comments.
+ return patcher.get(origLoc).indentTail(-orig.loc.indent);
+ };
};
// If the last character before oldLoc and the first character of newLines
// are both identifier characters, they must be separated by a space,
// otherwise they will most likely get fused together into a single token.
function needsLeadingSpace(oldLines, oldLoc, newLines) {
- var posBeforeOldLoc = util.copyPos(oldLoc.start);
+ var posBeforeOldLoc = util.copyPos(oldLoc.start);
- // The character just before the location occupied by oldNode.
- var charBeforeOldLoc =
- oldLines.prevPos(posBeforeOldLoc) &&
- oldLines.charAt(posBeforeOldLoc);
+ // The character just before the location occupied by oldNode.
+ var charBeforeOldLoc =
+ oldLines.prevPos(posBeforeOldLoc) &&
+ oldLines.charAt(posBeforeOldLoc);
- // First character of the reprinted node.
- var newFirstChar = newLines.charAt(newLines.firstPos());
+ // First character of the reprinted node.
+ var newFirstChar = newLines.charAt(newLines.firstPos());
- return charBeforeOldLoc &&
- riskyAdjoiningCharExp.test(charBeforeOldLoc) &&
- newFirstChar &&
- riskyAdjoiningCharExp.test(newFirstChar);
+ return charBeforeOldLoc &&
+ riskyAdjoiningCharExp.test(charBeforeOldLoc) &&
+ newFirstChar &&
+ riskyAdjoiningCharExp.test(newFirstChar);
}
// If the last character of newLines and the first character after oldLoc
// are both identifier characters, they must be separated by a space,
// otherwise they will most likely get fused together into a single token.
function needsTrailingSpace(oldLines, oldLoc, newLines) {
- // The character just after the location occupied by oldNode.
- var charAfterOldLoc = oldLines.charAt(oldLoc.end);
+ // The character just after the location occupied by oldNode.
+ var charAfterOldLoc = oldLines.charAt(oldLoc.end);
- var newLastPos = newLines.lastPos();
+ var newLastPos = newLines.lastPos();
- // Last character of the reprinted node.
- var newLastChar = newLines.prevPos(newLastPos) &&
- newLines.charAt(newLastPos);
+ // Last character of the reprinted node.
+ var newLastChar = newLines.prevPos(newLastPos) &&
+ newLines.charAt(newLastPos);
- return newLastChar &&
- riskyAdjoiningCharExp.test(newLastChar) &&
- charAfterOldLoc &&
- riskyAdjoiningCharExp.test(charAfterOldLoc);
+ return newLastChar &&
+ riskyAdjoiningCharExp.test(newLastChar) &&
+ charAfterOldLoc &&
+ riskyAdjoiningCharExp.test(charAfterOldLoc);
}
function findReprints(newPath, reprints) {
- var newNode = newPath.getValue();
- Printable.assert(newNode);
+ var newNode = newPath.getValue();
+ Printable.assert(newNode);
- var oldNode = newNode.original;
- Printable.assert(oldNode);
+ var oldNode = newNode.original;
+ Printable.assert(oldNode);
- assert.deepEqual(reprints, []);
+ assert.deepEqual(reprints, []);
- if (newNode.type !== oldNode.type) {
- return false;
- }
+ if (newNode.type !== oldNode.type) {
+ return false;
+ }
- var oldPath = new FastPath(oldNode);
- var canReprint = findChildReprints(newPath, oldPath, reprints);
+ var oldPath = new FastPath(oldNode);
+ var canReprint = findChildReprints(newPath, oldPath, reprints);
- if (!canReprint) {
- // Make absolutely sure the calling code does not attempt to reprint
- // any nodes.
- reprints.length = 0;
- }
+ if (!canReprint) {
+ // Make absolutely sure the calling code does not attempt to reprint
+ // any nodes.
+ reprints.length = 0;
+ }
- return canReprint;
+ return canReprint;
}
function findAnyReprints(newPath, oldPath, reprints) {
- var newNode = newPath.getValue();
- var oldNode = oldPath.getValue();
+ var newNode = newPath.getValue();
+ var oldNode = oldPath.getValue();
- if (newNode === oldNode)
- return true;
+ if (newNode === oldNode)
+ return true;
- if (isArray.check(newNode))
- return findArrayReprints(newPath, oldPath, reprints);
+ if (isArray.check(newNode))
+ return findArrayReprints(newPath, oldPath, reprints);
- if (isObject.check(newNode))
- return findObjectReprints(newPath, oldPath, reprints);
+ if (isObject.check(newNode))
+ return findObjectReprints(newPath, oldPath, reprints);
- return false;
+ return false;
}
function findArrayReprints(newPath, oldPath, reprints) {
- var newNode = newPath.getValue();
- var oldNode = oldPath.getValue();
- isArray.assert(newNode);
- var len = newNode.length;
+ var newNode = newPath.getValue();
+ var oldNode = oldPath.getValue();
- if (!(isArray.check(oldNode) &&
- oldNode.length === len))
- return false;
+ if (newNode === oldNode ||
+ newPath.valueIsDuplicate() ||
+ oldPath.valueIsDuplicate()) {
+ return true;
+ }
- for (var i = 0; i < len; ++i) {
- newPath.stack.push(i, newNode[i]);
- oldPath.stack.push(i, oldNode[i]);
- var canReprint = findAnyReprints(newPath, oldPath, reprints);
- newPath.stack.length -= 2;
- oldPath.stack.length -= 2;
- if (!canReprint) {
- return false;
- }
+ isArray.assert(newNode);
+ var len = newNode.length;
+
+ if (!(isArray.check(oldNode) &&
+ oldNode.length === len))
+ return false;
+
+ for (var i = 0; i < len; ++i) {
+ newPath.stack.push(i, newNode[i]);
+ oldPath.stack.push(i, oldNode[i]);
+ var canReprint = findAnyReprints(newPath, oldPath, reprints);
+ newPath.stack.length -= 2;
+ oldPath.stack.length -= 2;
+ if (!canReprint) {
+ return false;
}
+ }
- return true;
+ return true;
}
function findObjectReprints(newPath, oldPath, reprints) {
- var newNode = newPath.getValue();
- isObject.assert(newNode);
+ var newNode = newPath.getValue();
+ isObject.assert(newNode);
- if (newNode.original === null) {
- // If newNode.original node was set to null, reprint the node.
- return false;
+ if (newNode.original === null) {
+ // If newNode.original node was set to null, reprint the node.
+ return false;
+ }
+
+ var oldNode = oldPath.getValue();
+ if (!isObject.check(oldNode))
+ return false;
+
+ if (newNode === oldNode ||
+ newPath.valueIsDuplicate() ||
+ oldPath.valueIsDuplicate()) {
+ return true;
+ }
+
+ if (Printable.check(newNode)) {
+ if (!Printable.check(oldNode)) {
+ return false;
}
- var oldNode = oldPath.getValue();
- if (!isObject.check(oldNode))
- return false;
+ // Here we need to decide whether the reprinted code for newNode is
+ // appropriate for patching into the location of oldNode.
+
+ if (newNode.type === oldNode.type) {
+ var childReprints = [];
- if (Printable.check(newNode)) {
- if (!Printable.check(oldNode)) {
- return false;
- }
-
- // Here we need to decide whether the reprinted code for newNode
- // is appropriate for patching into the location of oldNode.
-
- if (newNode.type === oldNode.type) {
- var childReprints = [];
-
- if (findChildReprints(newPath, oldPath, childReprints)) {
- reprints.push.apply(reprints, childReprints);
- } else if (oldNode.loc) {
- // If we have no .loc information for oldNode, then we
- // won't be able to reprint it.
- reprints.push({
- oldPath: oldPath.copy(),
- newPath: newPath.copy()
- });
- } else {
- return false;
- }
-
- return true;
- }
-
- if (Expression.check(newNode) &&
- Expression.check(oldNode) &&
- // If we have no .loc information for oldNode, then we won't
- // be able to reprint it.
- oldNode.loc) {
-
- // If both nodes are subtypes of Expression, then we should be
- // able to fill the location occupied by the old node with
- // code printed for the new node with no ill consequences.
- reprints.push({
- oldPath: oldPath.copy(),
- newPath: newPath.copy()
- });
-
- return true;
- }
-
- // The nodes have different types, and at least one of the types
- // is not a subtype of the Expression type, so we cannot safely
- // assume the nodes are syntactically interchangeable.
+ if (findChildReprints(newPath, oldPath, childReprints)) {
+ reprints.push.apply(reprints, childReprints);
+ } else if (oldNode.loc) {
+ // If we have no .loc information for oldNode, then we won't be
+ // able to reprint it.
+ reprints.push({
+ oldPath: oldPath.copy(),
+ newPath: newPath.copy()
+ });
+ } else {
return false;
+ }
+
+ return true;
}
- return findChildReprints(newPath, oldPath, reprints);
+ if (Expression.check(newNode) &&
+ Expression.check(oldNode) &&
+ // If we have no .loc information for oldNode, then we won't be
+ // able to reprint it.
+ oldNode.loc) {
+
+ // If both nodes are subtypes of Expression, then we should be able
+ // to fill the location occupied by the old node with code printed
+ // for the new node with no ill consequences.
+ reprints.push({
+ oldPath: oldPath.copy(),
+ newPath: newPath.copy()
+ });
+
+ return true;
+ }
+
+ // The nodes have different types, and at least one of the types is
+ // not a subtype of the Expression type, so we cannot safely assume
+ // the nodes are syntactically interchangeable.
+ return false;
+ }
+
+ return findChildReprints(newPath, oldPath, reprints);
}
// This object is reused in hasOpeningParen and hasClosingParen to avoid
@@ -395,141 +408,147 @@ var reusablePos = { line: 1, column: 0 };
var nonSpaceExp = /\S/;
function hasOpeningParen(oldPath) {
- var oldNode = oldPath.getValue();
- var loc = oldNode.loc;
- var lines = loc && loc.lines;
-
- if (lines) {
- var pos = reusablePos;
- pos.line = loc.start.line;
- pos.column = loc.start.column;
-
- while (lines.prevPos(pos)) {
- var ch = lines.charAt(pos);
-
- if (ch === "(") {
- // If we found an opening parenthesis but it occurred before
- // the start of the original subtree for this reprinting, then
- // we must not return true for hasOpeningParen(oldPath).
- return comparePos(oldPath.getRootValue().loc.start, pos) <= 0;
- }
-
- if (nonSpaceExp.test(ch)) {
- return false;
- }
- }
+ var oldNode = oldPath.getValue();
+ var loc = oldNode.loc;
+ var lines = loc && loc.lines;
+
+ if (lines) {
+ var pos = reusablePos;
+ pos.line = loc.start.line;
+ pos.column = loc.start.column;
+
+ while (lines.prevPos(pos)) {
+ var ch = lines.charAt(pos);
+
+ if (ch === "(") {
+ // If we found an opening parenthesis but it occurred before the
+ // start of the original subtree for this reprinting, then we must
+ // not return true for hasOpeningParen(oldPath).
+ return comparePos(oldPath.getRootValue().loc.start, pos) <= 0;
+ }
+
+ if (nonSpaceExp.test(ch)) {
+ return false;
+ }
}
+ }
- return false;
+ return false;
}
function hasClosingParen(oldPath) {
- var oldNode = oldPath.getValue();
- var loc = oldNode.loc;
- var lines = loc && loc.lines;
-
- if (lines) {
- var pos = reusablePos;
- pos.line = loc.end.line;
- pos.column = loc.end.column;
-
- do {
- var ch = lines.charAt(pos);
-
- if (ch === ")") {
- // If we found a closing parenthesis but it occurred after the
- // end of the original subtree for this reprinting, then we
- // must not return true for hasClosingParen(oldPath).
- return comparePos(pos, oldPath.getRootValue().loc.end) <= 0;
- }
-
- if (nonSpaceExp.test(ch)) {
- return false;
- }
-
- } while (lines.nextPos(pos));
- }
+ var oldNode = oldPath.getValue();
+ var loc = oldNode.loc;
+ var lines = loc && loc.lines;
+
+ if (lines) {
+ var pos = reusablePos;
+ pos.line = loc.end.line;
+ pos.column = loc.end.column;
+
+ do {
+ var ch = lines.charAt(pos);
+
+ if (ch === ")") {
+ // If we found a closing parenthesis but it occurred after the end
+ // of the original subtree for this reprinting, then we must not
+ // return true for hasClosingParen(oldPath).
+ return comparePos(pos, oldPath.getRootValue().loc.end) <= 0;
+ }
+
+ if (nonSpaceExp.test(ch)) {
+ return false;
+ }
- return false;
+ } while (lines.nextPos(pos));
+ }
+
+ return false;
}
function hasParens(oldPath) {
- // This logic can technically be fooled if the node has parentheses
- // but there are comments intervening between the parentheses and the
- // node. In such cases the node will be harmlessly wrapped in an
- // additional layer of parentheses.
- return hasOpeningParen(oldPath) && hasClosingParen(oldPath);
+ // This logic can technically be fooled if the node has parentheses but
+ // there are comments intervening between the parentheses and the
+ // node. In such cases the node will be harmlessly wrapped in an
+ // additional layer of parentheses.
+ return hasOpeningParen(oldPath) && hasClosingParen(oldPath);
}
function findChildReprints(newPath, oldPath, reprints) {
- var newNode = newPath.getValue();
- var oldNode = oldPath.getValue();
+ var newNode = newPath.getValue();
+ var oldNode = oldPath.getValue();
- isObject.assert(newNode);
- isObject.assert(oldNode);
+ isObject.assert(newNode);
+ isObject.assert(oldNode);
- if (newNode.original === null) {
- // If newNode.original node was set to null, reprint the node.
- return false;
- }
+ if (newNode.original === null) {
+ // If newNode.original node was set to null, reprint the node.
+ return false;
+ }
+
+ // If this type of node cannot come lexically first in its enclosing
+ // statement (e.g. a function expression or object literal), and it
+ // seems to be doing so, then the only way we can ignore this problem
+ // and save ourselves from falling back to the pretty printer is if an
+ // opening parenthesis happens to precede the node. For example,
+ // (function(){ ... }()); does not need to be reprinted, even though the
+ // FunctionExpression comes lexically first in the enclosing
+ // ExpressionStatement and fails the hasParens test, because the parent
+ // CallExpression passes the hasParens test. If we relied on the
+ // path.needsParens() && !hasParens(oldNode) check below, the absence of
+ // a closing parenthesis after the FunctionExpression would trigger
+ // pretty-printing unnecessarily.
+ if (!newPath.canBeFirstInStatement() &&
+ newPath.firstInStatement() &&
+ !hasOpeningParen(oldPath))
+ return false;
- // If this type of node cannot come lexically first in its enclosing
- // statement (e.g. a function expression or object literal), and it
- // seems to be doing so, then the only way we can ignore this problem
- // and save ourselves from falling back to the pretty printer is if an
- // opening parenthesis happens to precede the node. For example,
- // (function(){ ... }()); does not need to be reprinted, even though
- // the FunctionExpression comes lexically first in the enclosing
- // ExpressionStatement and fails the hasParens test, because the
- // parent CallExpression passes the hasParens test. If we relied on
- // the path.needsParens() && !hasParens(oldNode) check below, the
- // absence of a closing parenthesis after the FunctionExpression would
- // trigger pretty-printing unnecessarily.
- if (!newPath.canBeFirstInStatement() &&
- newPath.firstInStatement() &&
- !hasOpeningParen(oldPath))
- return false;
+ // If this node needs parentheses and will not be wrapped with
+ // parentheses when reprinted, then return false to skip reprinting and
+ // let it be printed generically.
+ if (newPath.needsParens(true) && !hasParens(oldPath)) {
+ return false;
+ }
- // If this node needs parentheses and will not be wrapped with
- // parentheses when reprinted, then return false to skip reprinting
- // and let it be printed generically.
- if (newPath.needsParens(true) && !hasParens(oldPath)) {
- return false;
- }
+ var keys = util.getUnionOfKeys(oldNode, newNode);
- var keys = util.getUnionOfKeys(oldNode, newNode);
+ if (oldNode.type === "File" ||
+ newNode.type === "File") {
+ // Don't bother traversing file.tokens, an often very large array
+ // returned by Babylon, and useless for our purposes.
+ delete keys.tokens;
+ }
- if (oldNode.type === "File" ||
- newNode.type === "File") {
- // Don't bother traversing file.tokens, an often very large array
- // returned by Babylon, and useless for our purposes.
- delete keys.tokens;
- }
+ // Don't bother traversing .loc objects looking for reprintable nodes.
+ delete keys.loc;
- // Don't bother traversing .loc objects looking for reprintable nodes.
- delete keys.loc;
+ var originalReprintCount = reprints.length;
- var originalReprintCount = reprints.length;
+ for (var k in keys) {
+ if (k.charAt(0) === "_") {
+ // Ignore "private" AST properties added by e.g. Babel plugins and
+ // parsers like Babylon.
+ continue;
+ }
- for (var k in keys) {
- newPath.stack.push(k, types.getFieldValue(newNode, k));
- oldPath.stack.push(k, types.getFieldValue(oldNode, k));
- var canReprint = findAnyReprints(newPath, oldPath, reprints);
- newPath.stack.length -= 2;
- oldPath.stack.length -= 2;
+ newPath.stack.push(k, types.getFieldValue(newNode, k));
+ oldPath.stack.push(k, types.getFieldValue(oldNode, k));
+ var canReprint = findAnyReprints(newPath, oldPath, reprints);
+ newPath.stack.length -= 2;
+ oldPath.stack.length -= 2;
- if (!canReprint) {
- return false;
- }
+ if (!canReprint) {
+ return false;
}
+ }
- // Return statements might end up running into ASI issues due to comments
- // inserted deep within the tree, so reprint them if anything changed
- // within them.
- if (ReturnStatement.check(newPath.getNode()) &&
- reprints.length > originalReprintCount) {
- return false;
- }
+ // Return statements might end up running into ASI issues due to
+ // comments inserted deep within the tree, so reprint them if anything
+ // changed within them.
+ if (ReturnStatement.check(newPath.getNode()) &&
+ reprints.length > originalReprintCount) {
+ return false;
+ }
- return true;
+ return true;
}
diff --git a/lib/printer.js b/lib/printer.js
index a89ad90..2040b1b 100644
--- a/lib/printer.js
+++ b/lib/printer.js
@@ -1083,6 +1083,9 @@ function genericPrintNoParens(path, options, print) {
case "JSXSpreadAttribute":
return concat(["{...", path.call(print, "argument"), "}"]);
+ case "JSXSpreadChild":
+ return concat(["{...", path.call(print, "expression"), "}"]);
+
case "JSXExpressionContainer":
return concat(["{", path.call(print, "expression"), "}"]);
@@ -1510,8 +1513,9 @@ function genericPrintNoParens(path, options, print) {
return fromString(nodeStr(n.value, options), options);
case "NumberLiteralTypeAnnotation":
+ case "NumericLiteralTypeAnnotation":
assert.strictEqual(typeof n.value, "number");
- return fromString("" + n.value, options);
+ return fromString(JSON.stringify(n.value), options);
case "StringTypeAnnotation":
return fromString("string", options);
diff --git a/lib/util.js b/lib/util.js
index 251cc84..81895de 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -163,10 +163,10 @@ util.fixFaultyLocations = function(node, lines) {
loc.end = lines.lastPos();
}
- if (node.type === "TemplateLiteral") {
- fixTemplateLiteral(node, lines);
+ fixForLoopHead(node, lines);
+ fixTemplateLiteral(node, lines);
- } else if (loc && node.decorators) {
+ if (loc && node.decorators) {
// Expand the .loc of the node responsible for printing the decorators
// (here, the decorated node) so that it includes node.decorators.
node.decorators.forEach(function (decorator) {
@@ -220,8 +220,37 @@ util.fixFaultyLocations = function(node, lines) {
}
};
+function fixForLoopHead(node, lines) {
+ if (node.type !== "ForStatement") {
+ return;
+ }
+
+ function fix(child) {
+ var loc = child && child.loc;
+ var start = loc && loc.start;
+ var end = loc && copyPos(loc.end);
+
+ while (start && end && comparePos(start, end) < 0) {
+ lines.prevPos(end);
+ if (lines.charAt(end) === ";") {
+ // Update child.loc.end to *exclude* the ';' character.
+ loc.end.line = end.line;
+ loc.end.column = end.column;
+ } else {
+ break;
+ }
+ }
+ }
+
+ fix(node.init);
+ fix(node.test);
+ fix(node.update);
+}
+
function fixTemplateLiteral(node, lines) {
- assert.strictEqual(node.type, "TemplateLiteral");
+ if (node.type !== "TemplateLiteral") {
+ return;
+ }
if (node.quasis.length === 0) {
// If there are no quasi elements, then there is nothing to fix.
diff --git a/package.json b/package.json
index 225a3ca..2645d5f 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"parsing",
"pretty-printing"
],
- "version": "0.11.22",
+ "version": "0.12.2",
"homepage": "http://github.com/benjamn/recast",
"repository": {
"type": "git",
@@ -28,15 +28,19 @@
"fs": false
},
"dependencies": {
- "ast-types": "0.9.5",
+ "ast-types": "0.9.10",
+ "core-js": "^2.4.1",
"esprima": "~3.1.0",
"private": "~0.1.5",
"source-map": "~0.5.0"
},
"devDependencies": {
- "babylon": "~6.15.0",
+ "babel-core": "^6.23.1",
+ "babel-preset-es2015": "^6.22.0",
+ "babylon": "~6.16.1",
"esprima-fb": "^15001.1001.0-dev-harmony-fb",
- "mocha": "~3.1.2"
+ "mocha": "~3.2.0",
+ "reify": "^0.5.3"
},
"engines": {
"node": ">= 0.8"
diff --git a/test/babylon.js b/test/babylon.js
index 2140773..74c75e2 100644
--- a/test/babylon.js
+++ b/test/babylon.js
@@ -5,20 +5,10 @@ var b = recast.types.builders;
var eol = require("os").EOL;
describe("decorators", function () {
- var babylon = require("babylon");
- var babylonOptions = {
- sourceType: 'module',
- allowImportExportEverywhere: false,
- allowReturnOutsideFunction: false,
- plugins: ["*", "jsx", "flow"]
- };
-
+ var babelTransform = require("babel-core").transform;
+ var babelPresetES2015 = require("babel-preset-es2015");
var parseOptions = {
- parser: {
- parse: function (source) {
- return babylon.parse(source, babylonOptions);
- }
- }
+ parser: require("reify/lib/parsers/babylon.js")
};
it("babel 6 printing", function () {
@@ -273,14 +263,6 @@ describe("decorators", function () {
'};',
].join('\n');
- var parseOptions = {
- parser: {
- parse: function (source) {
- return babylon.parse(source, {plugins: ['flow']});
- }
- },
- };
-
var ast = recast.parse(code, parseOptions)
var root = new recast.types.NodePath(ast);
@@ -363,4 +345,20 @@ describe("decorators", function () {
code
);
});
+
+ it("tolerates circular references", function () {
+ var code = "function foo(bar = true) {}";
+ var ast = recast.parse(code, {
+ parser: {
+ parse: function (source) {
+ return babelTransform(source, {
+ code: false,
+ ast: true,
+ sourceMap: false,
+ presets: [babelPresetES2015]
+ }).ast;
+ }
+ }
+ });
+ });
});
diff --git a/test/printer.js b/test/printer.js
index 2efbbc7..5071d67 100644
--- a/test/printer.js
+++ b/test/printer.js
@@ -8,1626 +8,1666 @@ var fromString = require("../lib/lines").fromString;
var eol = require("os").EOL;
describe("printer", function() {
- it("Printer", function testPrinter(done) {
- var code = testPrinter + "";
- var ast = parse(code);
- var printer = new Printer;
+ it("Printer", function testPrinter(done) {
+ var code = testPrinter + "";
+ var ast = parse(code);
+ var printer = new Printer;
- assert.strictEqual(typeof printer.print, "function");
- assert.strictEqual(printer.print(null).code, "");
+ assert.strictEqual(typeof printer.print, "function");
+ assert.strictEqual(printer.print(null).code, "");
- var string = printer.printGenerically(ast).code;
- assert.ok(string.indexOf("done();") > 0);
+ var string = printer.printGenerically(ast).code;
+ assert.ok(string.indexOf("done();") > 0);
- string = printer.print(ast).code;
+ string = printer.print(ast).code;
- // TODO
+ // TODO
- assert.ok(string.indexOf("// TODO") > 0);
+ assert.ok(string.indexOf("// TODO") > 0);
- done();
- });
+ done();
+ });
- var uselessSemicolons = [
- 'function a() {',
- ' return "a";',
- '};',
- '',
- 'function b() {',
- ' return "b";',
- '};'
- ].join(eol);
+ var uselessSemicolons = [
+ 'function a() {',
+ ' return "a";',
+ '};',
+ '',
+ 'function b() {',
+ ' return "b";',
+ '};'
+ ].join(eol);
- it("EmptyStatements", function() {
- var ast = parse(uselessSemicolons);
- var printer = new Printer({ tabWidth: 2 });
+ it("EmptyStatements", function() {
+ var ast = parse(uselessSemicolons);
+ var printer = new Printer({ tabWidth: 2 });
- var reprinted = printer.print(ast).code;
- assert.strictEqual(typeof reprinted, "string");
- assert.strictEqual(reprinted, uselessSemicolons);
+ var reprinted = printer.print(ast).code;
+ assert.strictEqual(typeof reprinted, "string");
+ assert.strictEqual(reprinted, uselessSemicolons);
- var generic = printer.printGenerically(ast).code;
- var withoutTrailingSemicolons = uselessSemicolons.replace(/\};/g, "}");
- assert.strictEqual(typeof generic, "string");
- assert.strictEqual(generic, withoutTrailingSemicolons);
+ var generic = printer.printGenerically(ast).code;
+ var withoutTrailingSemicolons = uselessSemicolons.replace(/\};/g, "}");
+ assert.strictEqual(typeof generic, "string");
+ assert.strictEqual(generic, withoutTrailingSemicolons);
+ });
+
+ var importantSemicolons = [
+ "var a = {};", // <--- this trailing semi-colon is very important
+ "(function() {})();"
+ ].join(eol);
+
+ it("IffeAfterVariableDeclarationEndingInObjectLiteral", function() {
+ var ast = parse(importantSemicolons);
+ var printer = new Printer({ tabWidth: 2 });
+
+ var reprinted = printer.printGenerically(ast).code;
+ assert.strictEqual(typeof reprinted, "string");
+ assert.strictEqual(reprinted, importantSemicolons);
+ });
+
+ var arrayExprWithTrailingComma = '[1, 2,];';
+ var arrayExprWithoutTrailingComma = '[1, 2];';
+
+ it("ArrayExpressionWithTrailingComma", function() {
+ var ast = parse(arrayExprWithTrailingComma);
+ var printer = new Printer({ tabWidth: 2 });
+
+ var body = ast.program.body;
+ var arrayExpr = body[0].expression;
+ n.ArrayExpression.assert(arrayExpr);
+
+ // This causes the array expression to be reprinted.
+ var arrayExprOrig = arrayExpr.original;
+ arrayExpr.original = null;
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ arrayExprWithoutTrailingComma
+ );
+
+ arrayExpr.original = arrayExprOrig;
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ arrayExprWithTrailingComma
+ );
+ });
+
+ var arrayExprWithHoles = '[,,];';
+
+ it("ArrayExpressionWithHoles", function() {
+ var ast = parse(arrayExprWithHoles);
+ var printer = new Printer({ tabWidth: 2 });
+
+ var body = ast.program.body;
+ var arrayExpr = body[0].expression;
+ n.ArrayExpression.assert(arrayExpr);
+
+ // This causes the array expression to be reprinted.
+ var arrayExprOrig = arrayExpr.original;
+ arrayExpr.original = null;
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ arrayExprWithHoles
+ );
+
+ arrayExpr.original = arrayExprOrig;
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ arrayExprWithHoles
+ );
+ });
+
+ var objectExprWithTrailingComma = '({x: 1, y: 2,});';
+ var objectExprWithoutTrailingComma = '({' + eol + ' x: 1,' + eol + ' y: 2' + eol + '});';
+
+ it("ArrayExpressionWithTrailingComma", function() {
+ var ast = parse(objectExprWithTrailingComma);
+ var printer = new Printer({ tabWidth: 2 });
+
+ var body = ast.program.body;
+ var objectExpr = body[0].expression;
+ n.ObjectExpression.assert(objectExpr);
+
+ // This causes the array expression to be reprinted.
+ var objectExprOrig = objectExpr.original;
+ objectExpr.original = null;
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ objectExprWithoutTrailingComma
+ );
+
+ objectExpr.original = objectExprOrig;
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ objectExprWithTrailingComma
+ );
+ });
+
+ var switchCase = [
+ "switch (test) {",
+ " default:",
+ " case a: break",
+ "",
+ " case b:",
+ " break;",
+ "}",
+ ].join(eol);
+
+ var switchCaseReprinted = [
+ "if (test) {",
+ " switch (test) {",
+ " default:",
+ " case a: break",
+ " case b:",
+ " break;",
+ " }",
+ "}"
+ ].join(eol);
+
+ var switchCaseGeneric = [
+ "if (test) {",
+ " switch (test) {",
+ " default:",
+ " case a:",
+ " break;",
+ " case b:",
+ " break;",
+ " }",
+ "}"
+ ].join(eol);
+
+ it("SwitchCase", function() {
+ var ast = parse(switchCase);
+ var printer = new Printer({ tabWidth: 2 });
+
+ var body = ast.program.body;
+ var switchStmt = body[0];
+ n.SwitchStatement.assert(switchStmt);
+
+ // This causes the switch statement to be reprinted.
+ switchStmt.original = null;
+
+ body[0] = b.ifStatement(
+ b.identifier("test"),
+ b.blockStatement([
+ switchStmt
+ ])
+ );
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ switchCaseReprinted
+ );
+
+ assert.strictEqual(
+ printer.printGenerically(ast).code,
+ switchCaseGeneric
+ );
+ });
+
+ var tryCatch = [
+ "try {",
+ " a();",
+ "} catch (e) {",
+ " b(e);",
+ "}"
+ ].join(eol);
+
+ it("IndentTryCatch", function() {
+ var ast = parse(tryCatch);
+ var printer = new Printer({ tabWidth: 2 });
+ var body = ast.program.body;
+ var tryStmt = body[0];
+ n.TryStatement.assert(tryStmt);
+
+ // Force reprinting.
+ assert.strictEqual(printer.printGenerically(ast).code, tryCatch);
+ });
+
+ var classBody = [
+ "class A {",
+ " foo(x) { return x }",
+ " bar(y) { this.foo(y); }",
+ " baz(x, y) {",
+ " this.foo(x);",
+ " this.bar(y);",
+ " }",
+ "}"
+ ];
+
+ var classBodyExpected = [
+ "class A {",
+ " foo(x) { return x }",
+ " bar(y) { this.foo(y); }",
+ " baz(x, y) {",
+ " this.foo(x);",
+ " this.bar(y);",
+ " }",
+ " foo(x) { return x }",
+ "}"
+ ];
+
+ it("MethodPrinting", function() {
+ var code = classBody.join(eol);
+ try {
+ var ast = parse(code);
+ } catch (e) {
+ // ES6 not supported, silently finish
+ return;
+ }
+ var printer = new Printer({ tabWidth: 2 });
+ var cb = ast.program.body[0].body;
+ n.ClassBody.assert(cb);
+
+ // Trigger reprinting of the class body.
+ cb.body.push(cb.body[0]);
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ classBodyExpected.join(eol)
+ );
+ });
+
+ var multiLineParams = [
+ "function f(/* first",
+ " xxx",
+ " param */ a,",
+ " // other params",
+ " b, c, // see?",
+ " d",
+ ") {",
+ " return a + b + c + d;",
+ "}"
+ ];
+
+ var multiLineParamsExpected = [
+ "function f(",
+ " /* first",
+ " xxx",
+ " param */ a,",
+ " // other params",
+ " b,",
+ " // see?",
+ " c,",
+ " d",
+ ") {",
+ " return a + b + c + d;",
+ "}"
+ ];
+
+ it("MultiLineParams", function() {
+ var code = multiLineParams.join(eol);
+ var ast = parse(code);
+ var printer = new Printer({ tabWidth: 2 });
+
+ recast.visit(ast, {
+ visitNode: function(path) {
+ path.value.original = null;
+ this.traverse(path);
+ }
});
- var importantSemicolons = [
- "var a = {};", // <--- this trailing semi-colon is very important
- "(function() {})();"
+ assert.strictEqual(
+ printer.print(ast).code,
+ multiLineParamsExpected.join(eol)
+ );
+ });
+
+ it("SimpleVarPrinting", function() {
+ var printer = new Printer({ tabWidth: 2 });
+ var varDecl = b.variableDeclaration("var", [
+ b.variableDeclarator(b.identifier("x"), null),
+ b.variableDeclarator(b.identifier("y"), null),
+ b.variableDeclarator(b.identifier("z"), null)
+ ]);
+
+ assert.strictEqual(
+ printer.print(b.program([varDecl])).code,
+ "var x, y, z;"
+ );
+
+ var z = varDecl.declarations.pop();
+ varDecl.declarations.pop();
+ varDecl.declarations.push(z);
+
+ assert.strictEqual(
+ printer.print(b.program([varDecl])).code,
+ "var x, z;"
+ );
+ });
+
+ it("MultiLineVarPrinting", function() {
+ var printer = new Printer({ tabWidth: 2 });
+ var varDecl = b.variableDeclaration("var", [
+ b.variableDeclarator(b.identifier("x"), null),
+ b.variableDeclarator(
+ b.identifier("y"),
+ b.objectExpression([
+ b.property("init", b.identifier("why"), b.literal("not"))
+ ])
+ ),
+ b.variableDeclarator(b.identifier("z"), null)
+ ]);
+
+ assert.strictEqual(printer.print(b.program([varDecl])).code, [
+ "var x,",
+ " y = {",
+ " why: \"not\"",
+ " },",
+ " z;"
+ ].join(eol));
+ });
+
+ it("ForLoopPrinting", function() {
+ var printer = new Printer({ tabWidth: 2 });
+ var loop = b.forStatement(
+ b.variableDeclaration("var", [
+ b.variableDeclarator(b.identifier("i"), b.literal(0))
+ ]),
+ b.binaryExpression("<", b.identifier("i"), b.literal(3)),
+ b.updateExpression("++", b.identifier("i"), /* prefix: */ false),
+ b.expressionStatement(
+ b.callExpression(b.identifier("log"), [b.identifier("i")])
+ )
+ );
+
+ assert.strictEqual(
+ printer.print(loop).code,
+ "for (var i = 0; i < 3; i++)" + eol +
+ " log(i);"
+ );
+ });
+
+ it("EmptyForLoopPrinting", function() {
+ var printer = new Printer({ tabWidth: 2 });
+ var loop = b.forStatement(
+ b.variableDeclaration("var", [
+ b.variableDeclarator(b.identifier("i"), b.literal(0))
+ ]),
+ b.binaryExpression("<", b.identifier("i"), b.literal(3)),
+ b.updateExpression("++", b.identifier("i"), /* prefix: */ false),
+ b.emptyStatement()
+ );
+
+ assert.strictEqual(
+ printer.print(loop).code,
+ "for (var i = 0; i < 3; i++)" + eol +
+ " ;"
+ );
+ });
+
+ it("ForInLoopPrinting", function() {
+ var printer = new Printer({ tabWidth: 2 });
+ var loop = b.forInStatement(
+ b.variableDeclaration("var", [
+ b.variableDeclarator(b.identifier("key"), null)
+ ]),
+ b.identifier("obj"),
+ b.expressionStatement(
+ b.callExpression(b.identifier("log"), [b.identifier("key")])
+ ),
+ /* each: */ false
+ );
+
+ assert.strictEqual(
+ printer.print(loop).code,
+ "for (var key in obj)" + eol +
+ " log(key);"
+ );
+ });
+
+ it("GuessTabWidth", function() {
+ var code = [
+ "function identity(x) {",
+ " return x;",
+ "}"
].join(eol);
- it("IffeAfterVariableDeclarationEndingInObjectLiteral", function() {
- var ast = parse(importantSemicolons);
- var printer = new Printer({ tabWidth: 2 });
-
- var reprinted = printer.printGenerically(ast).code;
- assert.strictEqual(typeof reprinted, "string");
- assert.strictEqual(reprinted, importantSemicolons);
- });
-
- var arrayExprWithTrailingComma = '[1, 2,];';
- var arrayExprWithoutTrailingComma = '[1, 2];';
-
- it("ArrayExpressionWithTrailingComma", function() {
- var ast = parse(arrayExprWithTrailingComma);
- var printer = new Printer({ tabWidth: 2 });
-
- var body = ast.program.body;
- var arrayExpr = body[0].expression;
- n.ArrayExpression.assert(arrayExpr);
-
- // This causes the array expression to be reprinted.
- var arrayExprOrig = arrayExpr.original;
- arrayExpr.original = null;
-
- assert.strictEqual(
- printer.print(ast).code,
- arrayExprWithoutTrailingComma
- );
-
- arrayExpr.original = arrayExprOrig;
-
- assert.strictEqual(
- printer.print(ast).code,
- arrayExprWithTrailingComma
- );
- });
-
- var arrayExprWithHoles = '[,,];';
-
- it("ArrayExpressionWithHoles", function() {
- var ast = parse(arrayExprWithHoles);
- var printer = new Printer({ tabWidth: 2 });
-
- var body = ast.program.body;
- var arrayExpr = body[0].expression;
- n.ArrayExpression.assert(arrayExpr);
-
- // This causes the array expression to be reprinted.
- var arrayExprOrig = arrayExpr.original;
- arrayExpr.original = null;
-
- assert.strictEqual(
- printer.print(ast).code,
- arrayExprWithHoles
- );
-
- arrayExpr.original = arrayExprOrig;
-
- assert.strictEqual(
- printer.print(ast).code,
- arrayExprWithHoles
- );
- });
-
- var objectExprWithTrailingComma = '({x: 1, y: 2,});';
- var objectExprWithoutTrailingComma = '({' + eol + ' x: 1,' + eol + ' y: 2' + eol + '});';
-
- it("ArrayExpressionWithTrailingComma", function() {
- var ast = parse(objectExprWithTrailingComma);
- var printer = new Printer({ tabWidth: 2 });
-
- var body = ast.program.body;
- var objectExpr = body[0].expression;
- n.ObjectExpression.assert(objectExpr);
-
- // This causes the array expression to be reprinted.
- var objectExprOrig = objectExpr.original;
- objectExpr.original = null;
-
- assert.strictEqual(
- printer.print(ast).code,
- objectExprWithoutTrailingComma
- );
-
- objectExpr.original = objectExprOrig;
-
- assert.strictEqual(
- printer.print(ast).code,
- objectExprWithTrailingComma
- );
- });
-
- var switchCase = [
- "switch (test) {",
- " default:",
- " case a: break",
- "",
- " case b:",
- " break;",
- "}",
+ var guessedTwo = [
+ "function identity(x) {",
+ " log(x);",
+ " return x;",
+ "}"
].join(eol);
- var switchCaseReprinted = [
- "if (test) {",
- " switch (test) {",
- " default:",
- " case a: break",
- " case b:",
- " break;",
- " }",
- "}"
+ var explicitFour = [
+ "function identity(x) {",
+ " log(x);",
+ " return x;",
+ "}"
].join(eol);
- var switchCaseGeneric = [
- "if (test) {",
- " switch (test) {",
- " default:",
- " case a:",
- " break;",
- " case b:",
- " break;",
- " }",
- "}"
+ var ast = parse(code);
+
+ var funDecl = ast.program.body[0];
+ n.FunctionDeclaration.assert(funDecl);
+
+ var funBody = funDecl.body.body;
+
+ funBody.unshift(
+ b.expressionStatement(
+ b.callExpression(
+ b.identifier("log"),
+ funDecl.params
+ )
+ )
+ );
+
+ assert.strictEqual(
+ new Printer().print(ast).code,
+ guessedTwo
+ );
+
+ assert.strictEqual(
+ new Printer({
+ tabWidth: 4
+ }).print(ast).code,
+ explicitFour
+ );
+ });
+
+ it("FunctionDefaultsAndRest", function() {
+ var printer = new Printer();
+ var funExpr = b.functionExpression(
+ b.identifier('a'),
+ [b.identifier('b'), b.identifier('c')],
+ b.blockStatement([]),
+ false,
+ false,
+ false,
+ undefined
+ );
+
+ funExpr.defaults = [undefined, b.literal(1)];
+ funExpr.rest = b.identifier('d');
+
+ assert.strictEqual(
+ printer.print(funExpr).code,
+ "function a(b, c = 1, ...d) {}"
+ );
+
+ var arrowFunExpr = b.arrowFunctionExpression(
+ [b.identifier('b'), b.identifier('c')],
+ b.blockStatement([]),
+ false,
+ false,
+ false,
+ undefined);
+
+ arrowFunExpr.defaults = [undefined, b.literal(1)];
+ arrowFunExpr.rest = b.identifier('d');
+
+ assert.strictEqual(
+ printer.print(arrowFunExpr).code,
+ "(b, c = 1, ...d) => {}"
+ );
+ });
+
+ it("generically prints parsed code and generated code the same way", function() {
+ var printer = new Printer();
+ var ast = b.program([
+ b.expressionStatement(b.literal(1)),
+ b.expressionStatement(b.literal(2))
+ ]);
+
+ assert.strictEqual(
+ printer.printGenerically(parse("1; 2;")).code,
+ printer.printGenerically(ast).code
+ );
+ });
+
+ it("ExportDeclaration semicolons", function() {
+ var printer = new Printer();
+ var code = "export var foo = 42;";
+ var ast = parse(code);
+
+ assert.strictEqual(printer.print(ast).code, code);
+ assert.strictEqual(printer.printGenerically(ast).code, code);
+
+ code = "export var foo = 42";
+ ast = parse(code);
+
+ assert.strictEqual(printer.print(ast).code, code);
+ assert.strictEqual(printer.printGenerically(ast).code, code + ";");
+
+ code = "export function foo() {}";
+ ast = parse(code);
+
+ assert.strictEqual(printer.print(ast).code, code);
+ assert.strictEqual(printer.printGenerically(ast).code, code);
+ });
+
+ var stmtListSpaces = [
+ "",
+ "var x = 1;",
+ "",
+ "",
+ "// y summation",
+ "var y = x + 1;",
+ "var z = x + y;",
+ "// after z",
+ "",
+ "console.log(x, y, z);",
+ "",
+ ""
+ ].join(eol);
+
+ var stmtListSpacesExpected = [
+ "",
+ "debugger;",
+ "var x = 1;",
+ "",
+ "",
+ "// y summation",
+ "var y = x + 1;",
+ "debugger;",
+ "var z = x + y;",
+ "// after z",
+ "",
+ "console.log(x, y, z);",
+ "",
+ "debugger;",
+ "",
+ ""
+ ].join(eol);
+
+ it("Statement list whitespace reuse", function() {
+ var ast = parse(stmtListSpaces);
+ var printer = new Printer({ tabWidth: 2 });
+ var debugStmt = b.expressionStatement(b.identifier("debugger"));
+
+ ast.program.body.splice(2, 0, debugStmt);
+ ast.program.body.unshift(debugStmt);
+ ast.program.body.push(debugStmt);
+
+ assert.strictEqual(
+ printer.print(ast).code,
+ stmtListSpacesExpected
+ );
+
+ var funDecl = b.functionDeclaration(
+ b.identifier("foo"),
+ [],
+ b.blockStatement(ast.program.body)
+ );
+
+ var linesModule = require("../lib/lines");
+
+ assert.strictEqual(
+ printer.print(funDecl).code,
+ linesModule.concat([
+ "function foo() {" + eol,
+ linesModule.fromString(
+ stmtListSpacesExpected.replace(/^\s+|\s+$/g, "")
+ ).indent(2),
+ eol + "}"
+ ]).toString()
+ );
+ });
+
+ it("should print static methods with the static keyword", function() {
+ var printer = new Printer({ tabWidth: 4 });
+ var ast = parse([
+ "class A {",
+ " static foo() {}",
+ "}"
+ ].join(eol));
+
+ var classBody = ast.program.body[0].body;
+ n.ClassBody.assert(classBody);
+
+ var foo = classBody.body[0];
+ n.MethodDefinition.assert(foo);
+
+ classBody.body.push(foo);
+
+ foo.key.name = "formerlyFoo";
+
+ assert.strictEqual(printer.print(ast).code, [
+ "class A {",
+ " static formerlyFoo() {}",
+ " static formerlyFoo() {}",
+ "}"
+ ].join(eol));
+ });
+
+ it("should print string literals with the specified delimiter", function() {
+ var ast = parse([
+ "var obj = {",
+ " \"foo's\": 'bar',",
+ " '\"bar\\'s\"': /regex/m",
+ "};"
+ ].join(eol));
+
+ var variableDeclaration = ast.program.body[0];
+ n.VariableDeclaration.assert(variableDeclaration);
+
+ var printer = new Printer({ quote: "single" });
+ assert.strictEqual(printer.printGenerically(ast).code, [
+ "var obj = {",
+ " 'foo\\'s': 'bar',",
+ " '\"bar\\'s\"': /regex/m",
+ "};"
+ ].join(eol));
+
+ var printer2 = new Printer({ quote: "double" });
+ assert.strictEqual(printer2.printGenerically(ast).code, [
+ "var obj = {",
+ " \"foo's\": \"bar\",",
+ ' "\\"bar\'s\\"": /regex/m',
+ "};"
+ ].join(eol));
+
+ var printer3 = new Printer({ quote: "auto" });
+ assert.strictEqual(printer3.printGenerically(ast).code, [
+ "var obj = {",
+ ' "foo\'s": "bar",',
+ ' \'"bar\\\'s"\': /regex/m',
+ "};"
+ ].join(eol));
+ });
+
+ it("should print block comments at head of class once", function() {
+ // Given.
+ var ast = parse([
+ "/**",
+ " * This class was in an IIFE and returned an instance of itself.",
+ " */",
+ "function SimpleClass() {",
+ "};"
+ ].join(eol));
+
+ var classIdentifier = b.identifier('SimpleClass');
+ var exportsExpression = b.memberExpression(b.identifier('module'), b.identifier('exports'), false);
+ var assignmentExpression = b.assignmentExpression('=', exportsExpression, classIdentifier);
+ var exportStatement = b.expressionStatement(assignmentExpression);
+
+ ast.program.body.push(exportStatement);
+
+ // When.
+ var printedClass = new Printer().print(ast).code;
+
+ // Then.
+ assert.strictEqual(printedClass, [
+ "/**",
+ " * This class was in an IIFE and returned an instance of itself.",
+ " */",
+ "function SimpleClass() {",
+ "}",
+ "module.exports = SimpleClass;"
+ ].join(eol));
+ });
+
+ it("should support computed properties", function() {
+ var code = [
+ 'class A {',
+ ' ["a"]() {}',
+ ' [ID("b")]() {}',
+ ' [0]() {}',
+ ' [ID(1)]() {}',
+ ' get ["a"]() {}',
+ ' get [ID("b")]() {}',
+ ' get [0]() {}',
+ ' get [ID(1)]() {}',
+ ' set ["a"](x) {}',
+ ' set [ID("b")](x) {}',
+ ' set [0](x) {}',
+ ' set [ID(1)](x) {}',
+ ' static ["a"]() {}',
+ ' static [ID("b")]() {}',
+ ' static [0]() {}',
+ ' static [ID(1)]() {}',
+ ' static get ["a"]() {}',
+ ' static get [ID("b")]() {}',
+ ' static get [0]() {}',
+ ' static get [ID(1)]() {}',
+ ' static set ["a"](x) {}',
+ ' static set [ID("b")](x) {}',
+ ' static set [0](x) {}',
+ ' static set [ID(1)](x) {}',
+ '}'
].join(eol);
- it("SwitchCase", function() {
- var ast = parse(switchCase);
- var printer = new Printer({ tabWidth: 2 });
-
- var body = ast.program.body;
- var switchStmt = body[0];
- n.SwitchStatement.assert(switchStmt);
-
- // This causes the switch statement to be reprinted.
- switchStmt.original = null;
-
- body[0] = b.ifStatement(
- b.identifier("test"),
- b.blockStatement([
- switchStmt
- ])
- );
-
- assert.strictEqual(
- printer.print(ast).code,
- switchCaseReprinted
- );
-
- assert.strictEqual(
- printer.printGenerically(ast).code,
- switchCaseGeneric
- );
+ var ast = parse(code);
+
+ var printer = new Printer({
+ tabWidth: 2
});
- var tryCatch = [
- "try {",
- " a();",
- "} catch (e) {",
- " b(e);",
- "}"
+ assert.strictEqual(
+ printer.printGenerically(ast).code,
+ code
+ );
+
+ var code = [
+ 'var obj = {',
+ ' ["a"]: 1,',
+ ' [ID("b")]: 2,',
+ ' [0]: 3,',
+ ' [ID(1)]: 4,',
+ ' ["a"]() {},',
+ ' [ID("b")]() {},',
+ ' [0]() {},',
+ ' [ID(1)]() {},',
+ ' get ["a"]() {},',
+ ' get [ID("b")]() {},',
+ ' get [0]() {},',
+ ' get [ID(1)]() {},',
+ ' set ["a"](x) {},',
+ ' set [ID("b")](x) {},',
+ ' set [0](x) {},',
+ ' set [ID(1)](x) {}',
+ '};'
].join(eol);
- it("IndentTryCatch", function() {
- var ast = parse(tryCatch);
- var printer = new Printer({ tabWidth: 2 });
- var body = ast.program.body;
- var tryStmt = body[0];
- n.TryStatement.assert(tryStmt);
+ ast = parse(code);
+
+ assert.strictEqual(
+ printer.printGenerically(ast).code,
+ code
+ );
+
+ ast = parse([
+ "var o = {",
+ " // This foo will become a computed method name.",
+ " foo() { return bar }",
+ "};"
+ ].join(eol));
+
+ var objExpr = ast.program.body[0].declarations[0].init;
+ n.ObjectExpression.assert(objExpr);
+
+ assert.strictEqual(objExpr.properties[0].computed, false);
+ objExpr.properties[0].computed = true;
+ objExpr.properties[0].kind = "get";
+
+ assert.strictEqual(recast.print(ast).code, [
+ "var o = {",
+ " // This foo will become a computed method name.",
+ " get [foo]() { return bar }",
+ "};"
+ ].join(eol));
+ });
+
+ it("prints trailing commas in object literals", function() {
+ var code = [
+ "({",
+ " foo: bar,",
+ " bar: foo,",
+ "});"
+ ].join(eol);
- // Force reprinting.
- assert.strictEqual(printer.printGenerically(ast).code, tryCatch);
+ var ast = parse(code, {
+ // Supports trailing commas whereas plain esprima does not.
+ parser: require("esprima-fb")
});
- var classBody = [
- "class A {",
- " foo(x) { return x }",
- " bar(y) { this.foo(y); }",
- " baz(x, y) {",
- " this.foo(x);",
- " this.bar(y);",
- " }",
- "}"
- ];
-
- var classBodyExpected = [
- "class A {",
- " foo(x) { return x }",
- " bar(y) { this.foo(y); }",
- " baz(x, y) {",
- " this.foo(x);",
- " this.bar(y);",
- " }",
- " foo(x) { return x }",
- "}"
- ];
-
- it("MethodPrinting", function() {
- var code = classBody.join(eol);
- try {
- var ast = parse(code);
- } catch (e) {
- // ES6 not supported, silently finish
- return;
- }
- var printer = new Printer({ tabWidth: 2 });
- var cb = ast.program.body[0].body;
- n.ClassBody.assert(cb);
-
- // Trigger reprinting of the class body.
- cb.body.push(cb.body[0]);
-
- assert.strictEqual(
- printer.print(ast).code,
- classBodyExpected.join(eol)
- );
+ var printer = new Printer({
+ tabWidth: 2,
+ trailingComma: true,
});
- var multiLineParams = [
- "function f(/* first",
- " xxx",
- " param */ a,",
- " // other params",
- " b, c, // see?",
- " d",
- ") {",
- " return a + b + c + d;",
- "}"
- ];
-
- var multiLineParamsExpected = [
- "function f(",
- " /* first",
- " xxx",
- " param */ a,",
- " // other params",
- " b,",
- " // see?",
- " c,",
- " d",
- ") {",
- " return a + b + c + d;",
- "}"
- ];
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
- it("MultiLineParams", function() {
- var code = multiLineParams.join(eol);
- var ast = parse(code);
- var printer = new Printer({ tabWidth: 2 });
+ // It should also work when using the `trailingComma` option as an object.
+ printer = new Printer({
+ tabWidth: 2,
+ trailingComma: { objects: true },
+ });
- recast.visit(ast, {
- visitNode: function(path) {
- path.value.original = null;
- this.traverse(path);
- }
- });
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- assert.strictEqual(
- printer.print(ast).code,
- multiLineParamsExpected.join(eol)
- );
- });
+ it("prints trailing commas in function calls", function() {
+ var code = [
+ "call(",
+ " 1,",
+ " 2,",
+ ");"
+ ].join(eol);
- it("SimpleVarPrinting", function() {
- var printer = new Printer({ tabWidth: 2 });
- var varDecl = b.variableDeclaration("var", [
- b.variableDeclarator(b.identifier("x"), null),
- b.variableDeclarator(b.identifier("y"), null),
- b.variableDeclarator(b.identifier("z"), null)
- ]);
-
- assert.strictEqual(
- printer.print(b.program([varDecl])).code,
- "var x, y, z;"
- );
-
- var z = varDecl.declarations.pop();
- varDecl.declarations.pop();
- varDecl.declarations.push(z);
-
- assert.strictEqual(
- printer.print(b.program([varDecl])).code,
- "var x, z;"
- );
+ var ast = parse(code, {
+ // Supports trailing commas whereas plain esprima does not.
+ parser: require("esprima-fb")
});
- it("MultiLineVarPrinting", function() {
- var printer = new Printer({ tabWidth: 2 });
- var varDecl = b.variableDeclaration("var", [
- b.variableDeclarator(b.identifier("x"), null),
- b.variableDeclarator(
- b.identifier("y"),
- b.objectExpression([
- b.property("init", b.identifier("why"), b.literal("not"))
- ])
- ),
- b.variableDeclarator(b.identifier("z"), null)
- ]);
-
- assert.strictEqual(printer.print(b.program([varDecl])).code, [
- "var x,",
- " y = {",
- " why: \"not\"",
- " },",
- " z;"
- ].join(eol));
+ var printer = new Printer({
+ tabWidth: 2,
+ wrapColumn: 1,
+ trailingComma: true,
});
- it("ForLoopPrinting", function() {
- var printer = new Printer({ tabWidth: 2 });
- var loop = b.forStatement(
- b.variableDeclaration("var", [
- b.variableDeclarator(b.identifier("i"), b.literal(0))
- ]),
- b.binaryExpression("<", b.identifier("i"), b.literal(3)),
- b.updateExpression("++", b.identifier("i"), /* prefix: */ false),
- b.expressionStatement(
- b.callExpression(b.identifier("log"), [b.identifier("i")])
- )
- );
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
- assert.strictEqual(
- printer.print(loop).code,
- "for (var i = 0; i < 3; i++)" + eol +
- " log(i);"
- );
+ // It should also work when using the `trailingComma` option as an object.
+ printer = new Printer({
+ tabWidth: 2,
+ wrapColumn: 1,
+ trailingComma: { parameters: true },
});
- it("EmptyForLoopPrinting", function() {
- var printer = new Printer({ tabWidth: 2 });
- var loop = b.forStatement(
- b.variableDeclaration("var", [
- b.variableDeclarator(b.identifier("i"), b.literal(0))
- ]),
- b.binaryExpression("<", b.identifier("i"), b.literal(3)),
- b.updateExpression("++", b.identifier("i"), /* prefix: */ false),
- b.emptyStatement()
- );
-
- assert.strictEqual(
- printer.print(loop).code,
- "for (var i = 0; i < 3; i++)" + eol +
- " ;"
- );
- });
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- it("ForInLoopPrinting", function() {
- var printer = new Printer({ tabWidth: 2 });
- var loop = b.forInStatement(
- b.variableDeclaration("var", [
- b.variableDeclarator(b.identifier("key"), null)
- ]),
- b.identifier("obj"),
- b.expressionStatement(
- b.callExpression(b.identifier("log"), [b.identifier("key")])
- ),
- /* each: */ false
- );
-
- assert.strictEqual(
- printer.print(loop).code,
- "for (var key in obj)" + eol +
- " log(key);"
- );
- });
-
- it("GuessTabWidth", function() {
- var code = [
- "function identity(x) {",
- " return x;",
- "}"
- ].join(eol);
-
- var guessedTwo = [
- "function identity(x) {",
- " log(x);",
- " return x;",
- "}"
- ].join(eol);
-
- var explicitFour = [
- "function identity(x) {",
- " log(x);",
- " return x;",
- "}"
- ].join(eol);
-
- var ast = parse(code);
-
- var funDecl = ast.program.body[0];
- n.FunctionDeclaration.assert(funDecl);
-
- var funBody = funDecl.body.body;
-
- funBody.unshift(
- b.expressionStatement(
- b.callExpression(
- b.identifier("log"),
- funDecl.params
- )
- )
- );
-
- assert.strictEqual(
- new Printer().print(ast).code,
- guessedTwo
- );
-
- assert.strictEqual(
- new Printer({
- tabWidth: 4
- }).print(ast).code,
- explicitFour
- );
- });
+ it("prints trailing commas in array expressions", function() {
+ var code = [
+ "[",
+ " 1,",
+ " 2,",
+ "];"
+ ].join(eol);
- it("FunctionDefaultsAndRest", function() {
- var printer = new Printer();
- var funExpr = b.functionExpression(
- b.identifier('a'),
- [b.identifier('b'), b.identifier('c')],
- b.blockStatement([]),
- false,
- false,
- false,
- undefined
- );
-
- funExpr.defaults = [undefined, b.literal(1)];
- funExpr.rest = b.identifier('d');
-
- assert.strictEqual(
- printer.print(funExpr).code,
- "function a(b, c = 1, ...d) {}"
- );
-
- var arrowFunExpr = b.arrowFunctionExpression(
- [b.identifier('b'), b.identifier('c')],
- b.blockStatement([]),
- false,
- false,
- false,
- undefined);
-
- arrowFunExpr.defaults = [undefined, b.literal(1)];
- arrowFunExpr.rest = b.identifier('d');
-
- assert.strictEqual(
- printer.print(arrowFunExpr).code,
- "(b, c = 1, ...d) => {}"
- );
+ var ast = parse(code, {
+ // Supports trailing commas whereas plain esprima does not.
+ parser: require("esprima-fb")
});
- it("generically prints parsed code and generated code the same way", function() {
- var printer = new Printer();
- var ast = b.program([
- b.expressionStatement(b.literal(1)),
- b.expressionStatement(b.literal(2))
- ]);
-
- assert.strictEqual(
- printer.printGenerically(parse("1; 2;")).code,
- printer.printGenerically(ast).code
- );
+ var printer = new Printer({
+ tabWidth: 2,
+ wrapColumn: 1,
+ trailingComma: true,
});
- it("ExportDeclaration semicolons", function() {
- var printer = new Printer();
- var code = "export var foo = 42;";
- var ast = parse(code);
-
- assert.strictEqual(printer.print(ast).code, code);
- assert.strictEqual(printer.printGenerically(ast).code, code);
-
- code = "export var foo = 42";
- ast = parse(code);
-
- assert.strictEqual(printer.print(ast).code, code);
- assert.strictEqual(printer.printGenerically(ast).code, code + ";");
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
- code = "export function foo() {}";
- ast = parse(code);
-
- assert.strictEqual(printer.print(ast).code, code);
- assert.strictEqual(printer.printGenerically(ast).code, code);
+ // It should also work when using the `trailingComma` option as an object.
+ printer = new Printer({
+ tabWidth: 2,
+ wrapColumn: 1,
+ trailingComma: { arrays: true },
});
- var stmtListSpaces = [
- "",
- "var x = 1;",
- "",
- "",
- "// y summation",
- "var y = x + 1;",
- "var z = x + y;",
- "// after z",
- "",
- "console.log(x, y, z);",
- "",
- ""
- ].join(eol);
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- var stmtListSpacesExpected = [
- "",
- "debugger;",
- "var x = 1;",
- "",
- "",
- "// y summation",
- "var y = x + 1;",
- "debugger;",
- "var z = x + y;",
- "// after z",
- "",
- "console.log(x, y, z);",
- "",
- "debugger;",
- "",
- ""
+ it("prints trailing commas in function definitions", function() {
+ var code = [
+ "function foo(",
+ " a,",
+ " b,",
+ ") {}"
].join(eol);
- it("Statement list whitespace reuse", function() {
- var ast = parse(stmtListSpaces);
- var printer = new Printer({ tabWidth: 2 });
- var debugStmt = b.expressionStatement(b.identifier("debugger"));
-
- ast.program.body.splice(2, 0, debugStmt);
- ast.program.body.unshift(debugStmt);
- ast.program.body.push(debugStmt);
-
- assert.strictEqual(
- printer.print(ast).code,
- stmtListSpacesExpected
- );
-
- var funDecl = b.functionDeclaration(
- b.identifier("foo"),
- [],
- b.blockStatement(ast.program.body)
- );
-
- var linesModule = require("../lib/lines");
-
- assert.strictEqual(
- printer.print(funDecl).code,
- linesModule.concat([
- "function foo() {" + eol,
- linesModule.fromString(
- stmtListSpacesExpected.replace(/^\s+|\s+$/g, "")
- ).indent(2),
- eol + "}"
- ]).toString()
- );
+ var ast = parse(code, {
+ // Supports trailing commas whereas plain esprima does not.
+ parser: require("esprima-fb")
});
- it("should print static methods with the static keyword", function() {
- var printer = new Printer({ tabWidth: 4 });
- var ast = parse([
- "class A {",
- " static foo() {}",
- "}"
- ].join(eol));
-
- var classBody = ast.program.body[0].body;
- n.ClassBody.assert(classBody);
-
- var foo = classBody.body[0];
- n.MethodDefinition.assert(foo);
-
- classBody.body.push(foo);
-
- foo.key.name = "formerlyFoo";
-
- assert.strictEqual(printer.print(ast).code, [
- "class A {",
- " static formerlyFoo() {}",
- " static formerlyFoo() {}",
- "}"
- ].join(eol));
+ var printer = new Printer({
+ tabWidth: 2,
+ wrapColumn: 1,
+ trailingComma: true,
});
- it("should print string literals with the specified delimiter", function() {
- var ast = parse([
- "var obj = {",
- " \"foo's\": 'bar',",
- " '\"bar\\'s\"': /regex/m",
- "};"
- ].join(eol));
-
- var variableDeclaration = ast.program.body[0];
- n.VariableDeclaration.assert(variableDeclaration);
-
- var printer = new Printer({ quote: "single" });
- assert.strictEqual(printer.printGenerically(ast).code, [
- "var obj = {",
- " 'foo\\'s': 'bar',",
- " '\"bar\\'s\"': /regex/m",
- "};"
- ].join(eol));
-
- var printer2 = new Printer({ quote: "double" });
- assert.strictEqual(printer2.printGenerically(ast).code, [
- "var obj = {",
- " \"foo's\": \"bar\",",
- ' "\\"bar\'s\\"": /regex/m',
- "};"
- ].join(eol));
-
- var printer3 = new Printer({ quote: "auto" });
- assert.strictEqual(printer3.printGenerically(ast).code, [
- "var obj = {",
- ' "foo\'s": "bar",',
- ' \'"bar\\\'s"\': /regex/m',
- "};"
- ].join(eol));
- });
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
- it("should print block comments at head of class once", function() {
- // Given.
- var ast = parse([
- "/**",
- " * This class was in an IIFE and returned an instance of itself.",
- " */",
- "function SimpleClass() {",
- "};"
- ].join(eol));
-
- var classIdentifier = b.identifier('SimpleClass');
- var exportsExpression = b.memberExpression(b.identifier('module'), b.identifier('exports'), false);
- var assignmentExpression = b.assignmentExpression('=', exportsExpression, classIdentifier);
- var exportStatement = b.expressionStatement(assignmentExpression);
-
- ast.program.body.push(exportStatement);
-
- // When.
- var printedClass = new Printer().print(ast).code;
-
- // Then.
- assert.strictEqual(printedClass, [
- "/**",
- " * This class was in an IIFE and returned an instance of itself.",
- " */",
- "function SimpleClass() {",
- "}",
- "module.exports = SimpleClass;"
- ].join(eol));
+ // It should also work when using the `trailingComma` option as an object.
+ printer = new Printer({
+ tabWidth: 2,
+ wrapColumn: 1,
+ trailingComma: { parameters: true },
});
- it("should support computed properties", function() {
- var code = [
- 'class A {',
- ' ["a"]() {}',
- ' [ID("b")]() {}',
- ' [0]() {}',
- ' [ID(1)]() {}',
- ' get ["a"]() {}',
- ' get [ID("b")]() {}',
- ' get [0]() {}',
- ' get [ID(1)]() {}',
- ' set ["a"](x) {}',
- ' set [ID("b")](x) {}',
- ' set [0](x) {}',
- ' set [ID(1)](x) {}',
- ' static ["a"]() {}',
- ' static [ID("b")]() {}',
- ' static [0]() {}',
- ' static [ID(1)]() {}',
- ' static get ["a"]() {}',
- ' static get [ID("b")]() {}',
- ' static get [0]() {}',
- ' static get [ID(1)]() {}',
- ' static set ["a"](x) {}',
- ' static set [ID("b")](x) {}',
- ' static set [0](x) {}',
- ' static set [ID(1)](x) {}',
- '}'
- ].join(eol);
-
- var ast = parse(code);
-
- var printer = new Printer({
- tabWidth: 2
- });
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
+
+ it("shouldn't print a trailing comma for a RestElement", function() {
+ var code = [
+ "function foo(",
+ " a,",
+ " b,",
+ " ...rest",
+ ") {}"
+ ].join(eol);
- assert.strictEqual(
- printer.printGenerically(ast).code,
- code
- );
-
- var code = [
- 'var obj = {',
- ' ["a"]: 1,',
- ' [ID("b")]: 2,',
- ' [0]: 3,',
- ' [ID(1)]: 4,',
- ' ["a"]() {},',
- ' [ID("b")]() {},',
- ' [0]() {},',
- ' [ID(1)]() {},',
- ' get ["a"]() {},',
- ' get [ID("b")]() {},',
- ' get [0]() {},',
- ' get [ID(1)]() {},',
- ' set ["a"](x) {},',
- ' set [ID("b")](x) {},',
- ' set [0](x) {},',
- ' set [ID(1)](x) {}',
- '};'
- ].join(eol);
-
- ast = parse(code);
-
- assert.strictEqual(
- printer.printGenerically(ast).code,
- code
- );
-
- ast = parse([
- "var o = {",
- " // This foo will become a computed method name.",
- " foo() { return bar }",
- "};"
- ].join(eol));
-
- var objExpr = ast.program.body[0].declarations[0].init;
- n.ObjectExpression.assert(objExpr);
-
- assert.strictEqual(objExpr.properties[0].computed, false);
- objExpr.properties[0].computed = true;
- objExpr.properties[0].kind = "get";
-
- assert.strictEqual(recast.print(ast).code, [
- "var o = {",
- " // This foo will become a computed method name.",
- " get [foo]() { return bar }",
- "};"
- ].join(eol));
+ var ast = parse(code, {
+ // The flow parser and Babylon recognize `...rest` as a `RestElement`
+ parser: require("babylon")
});
- it("prints trailing commas in object literals", function() {
- var code = [
- "({",
- " foo: bar,",
- " bar: foo,",
- "});"
- ].join(eol);
-
- var ast = parse(code, {
- // Supports trailing commas whereas plain esprima does not.
- parser: require("esprima-fb")
- });
-
- var printer = new Printer({
- tabWidth: 2,
- trailingComma: true,
- });
-
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
-
- // It should also work when using the `trailingComma` option as an object.
- printer = new Printer({
- tabWidth: 2,
- trailingComma: { objects: true },
- });
-
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var printer = new Printer({
+ tabWidth: 2,
+ wrapColumn: 1,
+ trailingComma: true,
});
- it("prints trailing commas in function calls", function() {
- var code = [
- "call(",
- " 1,",
- " 2,",
- ");"
- ].join(eol);
-
- var ast = parse(code, {
- // Supports trailing commas whereas plain esprima does not.
- parser: require("esprima-fb")
- });
-
- var printer = new Printer({
- tabWidth: 2,
- wrapColumn: 1,
- trailingComma: true,
- });
-
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- // It should also work when using the `trailingComma` option as an object.
- printer = new Printer({
- tabWidth: 2,
- wrapColumn: 1,
- trailingComma: { parameters: true },
- });
+ it("should support AssignmentPattern and RestElement", function() {
+ var code = [
+ "function foo(a, [b, c] = d(a), ...[e, f, ...rest]) {",
+ " return [a, b, c, e, f, rest];",
+ "}"
+ ].join(eol);
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var ast = parse(code, {
+ // Supports rest parameter destructuring whereas plain esprima
+ // does not.
+ parser: require("esprima-fb")
});
- it("prints trailing commas in array expressions", function() {
- var code = [
- "[",
- " 1,",
- " 2,",
- "];"
- ].join(eol);
-
- var ast = parse(code, {
- // Supports trailing commas whereas plain esprima does not.
- parser: require("esprima-fb")
- });
-
- var printer = new Printer({
- tabWidth: 2,
- wrapColumn: 1,
- trailingComma: true,
- });
-
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
-
- // It should also work when using the `trailingComma` option as an object.
- printer = new Printer({
- tabWidth: 2,
- wrapColumn: 1,
- trailingComma: { arrays: true },
- });
-
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var printer = new Printer({
+ tabWidth: 2
});
- it("prints trailing commas in function definitions", function() {
- var code = [
- "function foo(",
- " a,",
- " b,",
- ") {}"
- ].join(eol);
-
- var ast = parse(code, {
- // Supports trailing commas whereas plain esprima does not.
- parser: require("esprima-fb")
- });
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- var printer = new Printer({
- tabWidth: 2,
- wrapColumn: 1,
- trailingComma: true,
- });
-
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ it("adds parenthesis around spread patterns", function() {
+ var code = "(...rest) => rest;";
- // It should also work when using the `trailingComma` option as an object.
- printer = new Printer({
- tabWidth: 2,
- wrapColumn: 1,
- trailingComma: { parameters: true },
- });
+ var ast = b.program([
+ b.expressionStatement(b.arrowFunctionExpression(
+ [b.spreadElementPattern(b.identifier('rest'))],
+ b.identifier('rest'),
+ false
+ ))
+ ]);
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var printer = new Printer({
+ tabWidth: 2
});
- it("shouldn't print a trailing comma for a RestElement", function() {
- var code = [
- "function foo(",
- " a,",
- " b,",
- " ...rest",
- ") {}"
- ].join(eol);
-
- var ast = parse(code, {
- // The flow parser and Babylon recognize `...rest` as a `RestElement`
- parser: require("babylon")
- });
-
- var printer = new Printer({
- tabWidth: 2,
- wrapColumn: 1,
- trailingComma: true,
- });
-
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+
+ // Print RestElement the same way
+ ast = b.program([
+ b.expressionStatement(b.arrowFunctionExpression(
+ [b.restElement(b.identifier('rest'))],
+ b.identifier('rest'),
+ false
+ ))
+ ]);
+
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+
+ // Do the same for the `rest` field.
+ var arrowFunction = b.arrowFunctionExpression(
+ [],
+ b.identifier('rest'),
+ false
+ );
+ arrowFunction.rest = b.identifier('rest');
+ ast = b.program([
+ b.expressionStatement(arrowFunction)
+ ]);
+
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
+
+ it("adds parenthesis around single arrow function arg when options.arrowParensAlways is true", function() {
+ var code = "(a) => {};";
+
+ var fn = b.arrowFunctionExpression(
+ [b.identifier('a')],
+ b.blockStatement([]),
+ false
+ );
+
+ var ast = b.program([
+ b.expressionStatement(fn)
+ ]);
+
+ var printer = new Printer({
+ arrowParensAlways: true
});
-
- it("should support AssignmentPattern and RestElement", function() {
- var code = [
- "function foo(a, [b, c] = d(a), ...[e, f, ...rest]) {",
- " return [a, b, c, e, f, rest];",
- "}"
- ].join(eol);
-
- var ast = parse(code, {
- // Supports rest parameter destructuring whereas plain esprima
- // does not.
- parser: require("esprima-fb")
- });
-
- var printer = new Printer({
- tabWidth: 2
- });
-
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
+
+ it("adds parenthesis around arrow function when binding", function() {
+ var code = "var a = (x => y).bind(z);";
+
+ var fn = b.arrowFunctionExpression(
+ [b.identifier("x")],
+ b.identifier("y")
+ );
+
+ var declaration = b.variableDeclaration("var", [
+ b.variableDeclarator(
+ b.identifier("a"),
+ b.callExpression(
+ b.memberExpression(fn, b.identifier("bind"), false),
+ [b.identifier("z")]
+ )
+ )
+ ]);
+
+ var ast = b.program([declaration]);
+
+ var printer = new Printer();
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
+
+ it("adds parenthesis around async arrow functions with args", function() {
+ var code = "async () => {};";
+
+ var fn = b.arrowFunctionExpression(
+ [],
+ b.blockStatement([]),
+ false
+ );
+ fn.async = true;
+
+ var ast = b.program([
+ b.expressionStatement(fn)
+ ]);
+
+ var printer = new Printer({
+ tabWidth: 2
});
- it("adds parenthesis around spread patterns", function() {
- var code = "(...rest) => rest;";
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
- var ast = b.program([
- b.expressionStatement(b.arrowFunctionExpression(
- [b.spreadElementPattern(b.identifier('rest'))],
- b.identifier('rest'),
- false
- ))
- ]);
+ // No parenthesis for single params if they are identifiers
+ code = "async foo => {};";
+ fn.params = [b.identifier('foo')];
- var printer = new Printer({
- tabWidth: 2
- });
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ // Add parenthesis for destructuring
+ code = "async ([a, b]) => {};";
+ fn.params = [b.arrayPattern([b.identifier('a'), b.identifier('b')])];
- // Print RestElement the same way
- ast = b.program([
- b.expressionStatement(b.arrowFunctionExpression(
- [b.restElement(b.identifier('rest'))],
- b.identifier('rest'),
- false
- ))
- ]);
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ it("adds parenthesis around arrow functions with single arg and a type", function() {
+ var code = "(a: b) => {};";
- // Do the same for the `rest` field.
- var arrowFunction = b.arrowFunctionExpression(
- [],
- b.identifier('rest'),
- false
- );
- arrowFunction.rest = b.identifier('rest');
- ast = b.program([
- b.expressionStatement(arrowFunction)
- ]);
-
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
- });
+ var arg = b.identifier('a');
+ arg.typeAnnotation = b.typeAnnotation(
+ b.genericTypeAnnotation(b.identifier('b'), null)
+ );
- it("adds parenthesis around single arrow function arg when options.arrowParensAlways is true", function() {
- var code = "(a) => {};";
+ var fn = b.arrowFunctionExpression(
+ [arg],
+ b.blockStatement([]),
+ false
+ );
- var fn = b.arrowFunctionExpression(
- [b.identifier('a')],
- b.blockStatement([]),
- false
- );
+ var ast = b.program([
+ b.expressionStatement(fn)
+ ]);
- var ast = b.program([
- b.expressionStatement(fn)
- ]);
+ var printer = new Printer();
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- var printer = new Printer({
- arrowParensAlways: true
- });
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
- });
+ it("adds parenthesis around arrow functions with single arg and a return type", function() {
+ var code = "(a): void => {};";
- it("adds parenthesis around arrow function when binding", function() {
- var code = "var a = (x => y).bind(z);";
+ var arg = b.identifier('a');
- var fn = b.arrowFunctionExpression(
- [b.identifier("x")],
- b.identifier("y")
- );
+ var fn = b.arrowFunctionExpression(
+ [arg],
+ b.blockStatement([]),
+ false
+ );
- var declaration = b.variableDeclaration("var", [
- b.variableDeclarator(
- b.identifier("a"),
- b.callExpression(
- b.memberExpression(fn, b.identifier("bind"), false),
- [b.identifier("z")]
- )
- )
- ]);
+ fn.returnType = b.typeAnnotation(
+ b.voidTypeAnnotation()
+ );
- var ast = b.program([declaration]);
+ var ast = b.program([
+ b.expressionStatement(fn)
+ ]);
- var printer = new Printer();
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
- });
+ var printer = new Printer();
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- it("adds parenthesis around async arrow functions with args", function() {
- var code = "async () => {};";
+ it("prints class property initializers with type annotations correctly", function() {
+ var code = [
+ "class A {",
+ " foo = (a: b): void => {};",
+ "}",
+ ].join(eol);
- var fn = b.arrowFunctionExpression(
- [],
- b.blockStatement([]),
+ var arg = b.identifier('a');
+ arg.typeAnnotation = b.typeAnnotation(
+ b.genericTypeAnnotation(b.identifier('b'), null)
+ );
+
+ var fn = b.arrowFunctionExpression(
+ [arg],
+ b.blockStatement([]),
+ false
+ );
+ fn.returnType = b.typeAnnotation(
+ b.voidTypeAnnotation()
+ );
+
+ var ast = b.program([
+ b.classDeclaration(
+ b.identifier('A'),
+ b.classBody([
+ b.classProperty(
+ b.identifier('foo'),
+ fn,
+ null,
false
- );
- fn.async = true;
-
- var ast = b.program([
- b.expressionStatement(fn)
- ]);
-
- var printer = new Printer({
- tabWidth: 2
- });
-
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
-
- // No parenthesis for single params if they are identifiers
- code = "async foo => {};";
- fn.params = [b.identifier('foo')];
-
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
-
- // Add parenthesis for destructuring
- code = "async ([a, b]) => {};";
- fn.params = [b.arrayPattern([b.identifier('a'), b.identifier('b')])];
+ )
+ ])
+ )
+ ]);
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var printer = new Printer({
+ tabWidth: 2
});
- it("adds parenthesis around arrow functions with single arg and a type", function() {
- var code = "(a: b) => {};";
-
- var arg = b.identifier('a');
- arg.typeAnnotation = b.typeAnnotation(
- b.genericTypeAnnotation(b.identifier('b'), null)
- );
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- var fn = b.arrowFunctionExpression(
- [arg],
- b.blockStatement([]),
- false
- );
+ it("prints ClassProperty correctly", function() {
+ var code = [
+ "class A {",
+ " foo: Type = Bar;",
+ "}",
+ ].join(eol);
- var ast = b.program([
- b.expressionStatement(fn)
- ]);
+ var ast = b.program([
+ b.classDeclaration(
+ b.identifier('A'),
+ b.classBody([
+ b.classProperty(
+ b.identifier('foo'),
+ b.identifier('Bar'),
+ b.typeAnnotation(
+ b.genericTypeAnnotation(b.identifier('Type'), null)
+ )
+ )
+ ])
+ )
+ ]);
- var printer = new Printer();
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var printer = new Printer({
+ tabWidth: 2
});
- it("adds parenthesis around arrow functions with single arg and a return type", function() {
- var code = "(a): void => {};";
-
- var arg = b.identifier('a');
-
- var fn = b.arrowFunctionExpression(
- [arg],
- b.blockStatement([]),
- false
- );
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- fn.returnType = b.typeAnnotation(
- b.voidTypeAnnotation()
- );
+ it("prints static ClassProperty correctly", function() {
+ var code = [
+ "class A {",
+ " static foo = Bar;",
+ "}",
+ ].join(eol);
- var ast = b.program([
- b.expressionStatement(fn)
- ]);
+ var ast = b.program([
+ b.classDeclaration(
+ b.identifier('A'),
+ b.classBody([
+ b.classProperty(
+ b.identifier('foo'),
+ b.identifier('Bar'),
+ null,
+ true
+ )
+ ])
+ )
+ ]);
- var printer = new Printer();
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var printer = new Printer({
+ tabWidth: 2
});
- it("prints class property initializers with type annotations correctly", function() {
- var code = [
- "class A {",
- " foo = (a: b): void => {};",
- "}",
- ].join(eol);
-
- var arg = b.identifier('a');
- arg.typeAnnotation = b.typeAnnotation(
- b.genericTypeAnnotation(b.identifier('b'), null)
- );
-
- var fn = b.arrowFunctionExpression(
- [arg],
- b.blockStatement([]),
- false
- );
- fn.returnType = b.typeAnnotation(
- b.voidTypeAnnotation()
- );
-
- var ast = b.program([
- b.classDeclaration(
- b.identifier('A'),
- b.classBody([
- b.classProperty(
- b.identifier('foo'),
- fn,
- null,
- false
- )
- ])
- )
- ]);
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- var printer = new Printer({
- tabWidth: 2
- });
+ it("prints template expressions correctly", function() {
+ var code = [
+ "graphql`query`;",
+ ].join(eol);
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var ast = b.program([
+ b.taggedTemplateStatement(
+ b.identifier('graphql'),
+ b.templateLiteral(
+ [b.templateElement({cooked: 'query', raw: 'query'}, false)],
+ []
+ )
+ )
+ ]);
+
+ var printer = new Printer({
+ tabWidth: 2
});
- it("prints ClassProperty correctly", function() {
- var code = [
- "class A {",
- " foo: Type = Bar;",
- "}",
- ].join(eol);
-
- var ast = b.program([
- b.classDeclaration(
- b.identifier('A'),
- b.classBody([
- b.classProperty(
- b.identifier('foo'),
- b.identifier('Bar'),
- b.typeAnnotation(
- b.genericTypeAnnotation(b.identifier('Type'), null)
- )
- )
- ])
- )
- ]);
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
- var printer = new Printer({
- tabWidth: 2
- });
+ code = [
+ "graphql`query${foo.getQuery()}field${bar}`;",
+ ].join(eol);
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
- });
+ ast = b.program([
+ b.taggedTemplateStatement(
+ b.identifier('graphql'),
+ b.templateLiteral(
+ [
+ b.templateElement(
+ {cooked: 'query', raw: 'query'},
+ false
+ ),
+ b.templateElement(
+ {cooked: 'field', raw: 'field'},
+ false
+ ),
+ b.templateElement(
+ {cooked: '', raw: ''},
+ true
+ ),
+ ],
+ [
+ b.callExpression(
+ b.memberExpression(
+ b.identifier('foo'),
+ b.identifier('getQuery'),
+ false
+ ),
+ []
+ ),
+ b.identifier('bar')
+ ]
+ )
+ )
+ ]);
+
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+
+ code = [
+ "graphql`",
+ " query {",
+ " ${foo.getQuery()},",
+ " field,",
+ " ${bar},",
+ " }",
+ "`;",
+ ].join(eol);
- it("prints static ClassProperty correctly", function() {
- var code = [
- "class A {",
- " static foo = Bar;",
- "}",
- ].join(eol);
-
- var ast = b.program([
- b.classDeclaration(
- b.identifier('A'),
- b.classBody([
- b.classProperty(
- b.identifier('foo'),
- b.identifier('Bar'),
- null,
- true
- )
- ])
- )
- ]);
+ ast = parse(code);
+ pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
- var printer = new Printer({
- tabWidth: 2
- });
+ it("preserves newlines at the beginning/end of files", function() {
+ var code = [
+ "",
+ "f();",
+ ""
+ ].join(eol);
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var lines = fromString(code);
+ var ast = parse(code, {
+ esprima: {
+ parse: function(source, options) {
+ var program = require("esprima").parse(source, options);
+ n.Program.assert(program);
+ // Expand ast.program.loc to include any
+ // leading/trailing whitespace, to simulate the
+ // behavior of some parsers, e.g. babel-core.
+ lines.skipSpaces(program.loc.start, true, true);
+ lines.skipSpaces(program.loc.end, false, true);
+ return program;
+ }
+ }
});
- it("prints template expressions correctly", function() {
- var code = [
- "graphql`query`;",
- ].join(eol);
-
- var ast = b.program([
- b.taggedTemplateStatement(
- b.identifier('graphql'),
- b.templateLiteral(
- [b.templateElement({cooked: 'query', raw: 'query'}, false)],
- []
- )
- )
- ]);
+ ast.program.body.unshift(b.debuggerStatement());
- var printer = new Printer({
- tabWidth: 2
- });
-
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
-
- code = [
- "graphql`query${foo.getQuery()}field${bar}`;",
- ].join(eol);
-
- ast = b.program([
- b.taggedTemplateStatement(
- b.identifier('graphql'),
- b.templateLiteral(
- [
- b.templateElement(
- {cooked: 'query', raw: 'query'},
- false
- ),
- b.templateElement(
- {cooked: 'field', raw: 'field'},
- false
- ),
- b.templateElement(
- {cooked: '', raw: ''},
- true
- ),
- ],
- [
- b.callExpression(
- b.memberExpression(
- b.identifier('foo'),
- b.identifier('getQuery'),
- false
- ),
- []
- ),
- b.identifier('bar')
- ]
- )
- )
- ]);
-
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
-
- code = [
- "graphql`",
- " query {",
- " ${foo.getQuery()},",
- " field,",
- " ${bar},",
- " }",
- "`;",
- ].join(eol);
-
- ast = parse(code);
- pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ var printer = new Printer({
+ tabWidth: 2
});
- it("preserves newlines at the beginning/end of files", function() {
- var code = [
- "",
- "f();",
- ""
- ].join(eol);
-
- var lines = fromString(code);
- var ast = parse(code, {
- esprima: {
- parse: function(source, options) {
- var program = require("esprima").parse(source, options);
- n.Program.assert(program);
- // Expand ast.program.loc to include any
- // leading/trailing whitespace, to simulate the
- // behavior of some parsers, e.g. babel-core.
- lines.skipSpaces(program.loc.start, true, true);
- lines.skipSpaces(program.loc.end, false, true);
- return program;
- }
- }
- });
+ assert.strictEqual(printer.print(ast).code, [
+ "",
+ "debugger;",
+ "f();",
+ ""
+ ].join(eol));
+ });
+
+ it("respects options.lineTerminator", function() {
+ var lines = [
+ "var first = 1;",
+ "var second = 2;"
+ ];
+ var code = lines.join("\n");
+ var ast = parse(code);
+
+ assert.strictEqual(
+ new Printer({
+ lineTerminator: "\r\n"
+ }).print(ast).code,
+ lines.join("\r\n")
+ );
+ });
+
+ it("preserves indentation in unmodified template expressions", function() {
+ var printer = new Printer({
+ tabWidth: 2
+ });
- ast.program.body.unshift(b.debuggerStatement());
+ var code = [
+ "var x = {",
+ " y: () => Relay.QL`",
+ " query {",
+ " ${foo},",
+ " field,",
+ " }",
+ " `",
+ "};",
+ ].join(eol);
- var printer = new Printer({
- tabWidth: 2
- });
+ var ast = parse(code);
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
+
+ it("preserves indentation in modified template expressions", function() {
+ var code = [
+ "const fragments = {",
+ " viewer: Relay.QL`",
+ " fragment on Viewer { // 2 extraneous spaces.",
+ " actor { // 2 extraneous spaces.",
+ " id, // 2 extraneous spaces.",
+ " ${ foo}, // 3 extraneous spaces.",
+ " ${bar }, // Correct!",
+ " name, // Correct!",
+ " ${baz}, // Correct!",
+ " address { // 2 extraneous spaces.",
+ " id, // 2 extraneous spaces.",
+ " }, // 2 extraneous spaces.",
+ " } // 2 extraneous spaces.",
+ " } // 2 extraneous spaces.",
+ "<~ This line should not be indented.",
+ " `, // 2 extraneous spaces.",
+ "};"
+ ].join(eol);
- assert.strictEqual(printer.print(ast).code, [
- "",
- "debugger;",
- "f();",
- ""
- ].join(eol));
+ var ast = parse(code);
+ var printer = new Printer({
+ tabWidth: 2
});
- it("respects options.lineTerminator", function() {
- var lines = [
- "var first = 1;",
- "var second = 2;"
- ];
- var code = lines.join("\n");
- var ast = parse(code);
-
- assert.strictEqual(
- new Printer({
- lineTerminator: "\r\n"
- }).print(ast).code,
- lines.join("\r\n")
- );
- });
+ recast.visit(ast, {
+ visitTaggedTemplateExpression: function (path) {
+ function replaceIdWithNodeId(path) {
+ path.replace(path.value.replace(/\bid\b/g, "nodeID"));
+ }
- it("preserves indentation in unmodified template expressions", function() {
- var printer = new Printer({
- tabWidth: 2
+ path.get("quasi", "quasis").each(function (quasiPath) {
+ replaceIdWithNodeId(quasiPath.get("value", "cooked"));
+ replaceIdWithNodeId(quasiPath.get("value", "raw"));
});
- var code = [
- "var x = {",
- " y: () => Relay.QL`",
- " query {",
- " ${foo},",
- " field,",
- " }",
- " `",
- "};",
- ].join(eol);
-
- var ast = parse(code);
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
+ this.traverse(path);
+ }
});
- it("preserves indentation in modified template expressions", function() {
- var code = [
- "const fragments = {",
- " viewer: Relay.QL`",
- " fragment on Viewer { // 2 extraneous spaces.",
- " actor { // 2 extraneous spaces.",
- " id, // 2 extraneous spaces.",
- " ${ foo}, // 3 extraneous spaces.",
- " ${bar }, // Correct!",
- " name, // Correct!",
- " ${baz}, // Correct!",
- " address { // 2 extraneous spaces.",
- " id, // 2 extraneous spaces.",
- " }, // 2 extraneous spaces.",
- " } // 2 extraneous spaces.",
- " } // 2 extraneous spaces.",
- "<~ This line should not be indented.",
- " `, // 2 extraneous spaces.",
- "};"
- ].join(eol);
-
- var ast = parse(code);
- var printer = new Printer({
- tabWidth: 2
- });
-
- recast.visit(ast, {
- visitTaggedTemplateExpression: function (path) {
- function replaceIdWithNodeId(path) {
- path.replace(path.value.replace(/\bid\b/g, "nodeID"));
- }
+ var actual = printer.print(ast).code;
+ var expected = code.replace(/\bid\b/g, "nodeID");
- path.get("quasi", "quasis").each(function (quasiPath) {
- replaceIdWithNodeId(quasiPath.get("value", "cooked"));
- replaceIdWithNodeId(quasiPath.get("value", "raw"));
- });
+ assert.strictEqual(actual, expected);
+ });
- this.traverse(path);
- }
- });
+ it("prints commas for flow object types by default", function() {
+ var code = [
+ "type MyType = {",
+ " message: string,",
+ " isAwesome: boolean,",
+ "};"
+ ].join(eol);
- var actual = printer.print(ast).code;
- var expected = code.replace(/\bid\b/g, "nodeID");
+ var ast = b.typeAlias(
+ b.identifier("MyType"),
+ null,
+ b.objectTypeAnnotation([
+ b.objectTypeProperty(
+ b.identifier("message"),
+ b.stringTypeAnnotation(),
+ false
+ ),
+ b.objectTypeProperty(
+ b.identifier("isAwesome"),
+ b.booleanTypeAnnotation(),
+ false
+ )
+ ])
+ );
+
+ var printer = new Printer();
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
+
+ it("shouldn't print a trailing comma for single-line flow object types", function() {
+ var code1 = "type MyType = { message: string };";
+ var code2 = "type MyType = { [key: string]: string };";
+
+ var ast1 = b.typeAlias(
+ b.identifier("MyType"),
+ null,
+ b.objectTypeAnnotation([
+ b.objectTypeProperty(
+ b.identifier("message"),
+ b.stringTypeAnnotation(),
+ false
+ )
+ ])
+ );
+
+ var ast2 = b.typeAlias(
+ b.identifier("MyType"),
+ null,
+ b.objectTypeAnnotation([], [
+ b.objectTypeIndexer(
+ b.identifier('key'),
+ b.stringTypeAnnotation(),
+ b.stringTypeAnnotation(),
+ false
+ )
+ ])
+ );
+
+ var printer = new Printer({trailingComma: true});
+ var pretty1 = printer.printGenerically(ast1).code;
+ var pretty2 = printer.printGenerically(ast2).code;
+ assert.strictEqual(pretty1, code1);
+ assert.strictEqual(pretty2, code2);
+ });
+
+ it("prints semicolons for flow object types when options.flowObjectCommas is falsy", function() {
+ var code = [
+ "type MyType = {",
+ " message: string;",
+ " isAwesome: boolean;",
+ "};"
+ ].join(eol);
- assert.strictEqual(actual, expected);
- });
+ var ast = b.typeAlias(
+ b.identifier("MyType"),
+ null,
+ b.objectTypeAnnotation([
+ b.objectTypeProperty(
+ b.identifier("message"),
+ b.stringTypeAnnotation(),
+ false
+ ),
+ b.objectTypeProperty(
+ b.identifier("isAwesome"),
+ b.booleanTypeAnnotation(),
+ false
+ )
+ ])
+ );
+
+ var printer = new Printer({ flowObjectCommas: false });
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
+
+ it("prints parens for nullable union/intersection types", function() {
+ var code = "type MyType = ?(string | number);";
+
+ var ast = b.typeAlias(
+ b.identifier("MyType"),
+ null,
+ b.nullableTypeAnnotation(
+ b.unionTypeAnnotation(
+ [b.stringTypeAnnotation(), b.numberTypeAnnotation()]
+ )
+ )
+ );
+
+ var printer = new Printer({});
+ var pretty = printer.printGenerically(ast).code;
+ assert.strictEqual(pretty, code);
+ });
+
+ it("uses the `arrayBracketSpacing` and the `objectCurlySpacing` option", function() {
+ var babylon = require("babylon");
+ var parseOptions = {
+ parser: {
+ parse: function (source) {
+ return babylon.parse(source, {
+ sourceType: 'module',
+ plugins: ['flow'],
+ });
+ }
+ }
+ };
- it("prints commas for flow object types by default", function() {
- var code = [
- "type MyType = {",
- " message: string,",
- " isAwesome: boolean,",
- "};"
- ].join(eol);
+ var testCaseList = [{
+ printerConfig: {arrayBracketSpacing: false, objectCurlySpacing: false},
+ code: [
+ 'import {java, script} from "javascript";',
+ '',
+ 'function foo(a) {',
+ ' type MyType = {message: string};',
+ ' return [1, 2, 3];',
+ '}',
+ '',
+ 'export {foo};'
+ ].join(eol)
+ }, {
+ printerConfig: {arrayBracketSpacing: true, objectCurlySpacing: false},
+ code: [
+ 'import {java, script} from "javascript";',
+ '',
+ 'function foo(a) {',
+ ' type MyType = {message: string};',
+ ' return [ 1, 2, 3 ];',
+ '}',
+ '',
+ 'export {foo};'
+ ].join(eol)
+ }, {
+ printerConfig: {arrayBracketSpacing: false, objectCurlySpacing: true},
+ code: [
+ 'import { java, script } from "javascript";',
+ '',
+ 'function foo(a) {',
+ ' type MyType = { message: string };',
+ ' return [1, 2, 3];',
+ '}',
+ '',
+ 'export { foo };'
+ ].join(eol)
+ }, {
+ printerConfig: {arrayBracketSpacing: true, objectCurlySpacing: true},
+ code: [
+ 'import { java, script } from "javascript";',
+ '',
+ 'function foo(a) {',
+ ' type MyType = { message: string };',
+ ' return [ 1, 2, 3 ];',
+ '}',
+ '',
+ 'export { foo };'
+ ].join(eol)
+ }];
- var ast = b.typeAlias(
- b.identifier("MyType"),
- null,
- b.objectTypeAnnotation([
- b.objectTypeProperty(
- b.identifier("message"),
- b.stringTypeAnnotation(),
- false
- ),
- b.objectTypeProperty(
- b.identifier("isAwesome"),
- b.booleanTypeAnnotation(),
- false
- )
- ])
- );
-
- var printer = new Printer();
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
- });
+ testCaseList.forEach(function(testCase) {
+ var code = testCase.code;
+ var printer = new Printer(testCase.printerConfig);
- it("shouldn't print a trailing comma for single-line flow object types", function() {
- var code1 = "type MyType = { message: string };";
- var code2 = "type MyType = { [key: string]: string };";
+ var ast = parse(code, parseOptions);
+ var pretty = printer.printGenerically(ast).code;
- var ast1 = b.typeAlias(
- b.identifier("MyType"),
- null,
- b.objectTypeAnnotation([
- b.objectTypeProperty(
- b.identifier("message"),
- b.stringTypeAnnotation(),
- false
- )
- ])
- );
-
- var ast2 = b.typeAlias(
- b.identifier("MyType"),
- null,
- b.objectTypeAnnotation([], [
- b.objectTypeIndexer(
- b.identifier('key'),
- b.stringTypeAnnotation(),
- b.stringTypeAnnotation(),
- false
- )
- ])
- );
-
- var printer = new Printer({trailingComma: true});
- var pretty1 = printer.printGenerically(ast1).code;
- var pretty2 = printer.printGenerically(ast2).code;
- assert.strictEqual(pretty1, code1);
- assert.strictEqual(pretty2, code2);
+ assert.strictEqual(pretty, code);
});
+ });
- it("prints semicolons for flow object types when options.flowObjectCommas is falsy", function() {
- var code = [
- "type MyType = {",
- " message: string;",
- " isAwesome: boolean;",
- "};"
- ].join(eol);
+ it("prints no extra semicolons in for-loop heads (#377)", function () {
+ function check(head, parser) {
+ var source = "for (" + head + ") console.log(i);";
+ var ast = recast.parse(source, { parser: parser });
+ var loop = ast.program.body[0];
+ assert.strictEqual(loop.type, "ForStatement");
+ loop.body = b.blockStatement([]);
- var ast = b.typeAlias(
- b.identifier("MyType"),
- null,
- b.objectTypeAnnotation([
- b.objectTypeProperty(
- b.identifier("message"),
- b.stringTypeAnnotation(),
- false
- ),
- b.objectTypeProperty(
- b.identifier("isAwesome"),
- b.booleanTypeAnnotation(),
- false
- )
- ])
- );
-
- var printer = new Printer({ flowObjectCommas: false });
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
- });
+ var reprinted = recast.print(ast).code;
- it("prints parens for nullable union/intersection types", function() {
- var code = "type MyType = ?(string | number);";
+ var openParenIndex = reprinted.indexOf("(");
+ assert.notStrictEqual(openParenIndex, -1);
- var ast = b.typeAlias(
- b.identifier("MyType"),
- null,
- b.nullableTypeAnnotation(
- b.unionTypeAnnotation(
- [b.stringTypeAnnotation(), b.numberTypeAnnotation()]
- )
- )
- );
+ var closeParenIndex = reprinted.indexOf(")", openParenIndex);
+ assert.notStrictEqual(closeParenIndex, -1);
- var printer = new Printer({});
- var pretty = printer.printGenerically(ast).code;
- assert.strictEqual(pretty, code);
- });
+ var newHead = reprinted.slice(
+ openParenIndex + 1,
+ closeParenIndex
+ );
- it("uses the `arrayBracketSpacing` and the `objectCurlySpacing` option", function() {
- var babylon = require("babylon");
- var parseOptions = {
- parser: {
- parse: function (source) {
- return babylon.parse(source, {
- sourceType: 'module',
- plugins: ['flow'],
- });
- }
- }
- };
-
- var testCaseList = [{
- printerConfig: {arrayBracketSpacing: false, objectCurlySpacing: false},
- code: [
- 'import {java, script} from "javascript";',
- '',
- 'function foo(a) {',
- ' type MyType = {message: string};',
- ' return [1, 2, 3];',
- '}',
- '',
- 'export {foo};'
- ].join(eol)
- }, {
- printerConfig: {arrayBracketSpacing: true, objectCurlySpacing: false},
- code: [
- 'import {java, script} from "javascript";',
- '',
- 'function foo(a) {',
- ' type MyType = {message: string};',
- ' return [ 1, 2, 3 ];',
- '}',
- '',
- 'export {foo};'
- ].join(eol)
- }, {
- printerConfig: {arrayBracketSpacing: false, objectCurlySpacing: true},
- code: [
- 'import { java, script } from "javascript";',
- '',
- 'function foo(a) {',
- ' type MyType = { message: string };',
- ' return [1, 2, 3];',
- '}',
- '',
- 'export { foo };'
- ].join(eol)
- }, {
- printerConfig: {arrayBracketSpacing: true, objectCurlySpacing: true},
- code: [
- 'import { java, script } from "javascript";',
- '',
- 'function foo(a) {',
- ' type MyType = { message: string };',
- ' return [ 1, 2, 3 ];',
- '}',
- '',
- 'export { foo };'
- ].join(eol)
- }];
-
- testCaseList.forEach(function(testCase) {
- var code = testCase.code;
- var printer = new Printer(testCase.printerConfig);
-
- var ast = parse(code, parseOptions);
- var pretty = printer.printGenerically(ast).code;
-
- assert.strictEqual(pretty, code);
- });
- });
+ assert.strictEqual(newHead.split(";").length, 3);
+ }
+
+ function checkWith(parser) {
+ check("let i = 0; i < 1; i++", parser);
+ check("let i = 0 ; i < 1; i++", parser);
+ check("let i = 0; ; i++", parser);
+ check("let i = 0 ; ; i++", parser);
+ check("let i = 0; i < 1; ", parser);
+ check("let i = 0 ; i < 1; ", parser);
+ check("let i = 0; ; ", parser);
+ check("let i = 0 ; ; ", parser);
+ }
+
+ checkWith(require("esprima"));
+ checkWith(require("reify/lib/parsers/acorn.js"));
+ checkWith(require("reify/lib/parsers/babylon.js"));
+ });
});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-recast.git
More information about the Pkg-javascript-commits
mailing list