[Pkg-javascript-commits] [node-recast] 01/05: Imported Upstream version 0.11.4

Julien Puydt julien.puydt at laposte.net
Sun May 29 05:49:56 UTC 2016


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 293d25846103f32f7bece6f185b7d6ad245e04cc
Author: Julien Puydt <julien.puydt at laposte.net>
Date:   Sun May 29 07:43:01 2016 +0200

    Imported Upstream version 0.11.4
---
 .travis.yml         |   4 +
 README.md           |   2 +-
 lib/comments.js     |   7 +-
 lib/fast-path.js    |   5 +
 lib/options.js      |   2 +-
 lib/parser.js       |   3 +
 lib/printer.js      | 335 +++++++++++++++++++++++++++-------------------------
 lib/util.js         | 115 ++++++++++++++----
 package.json        |  13 +-
 test/babylon.js     | 164 +++++++++++++++++++++++++
 test/comments.js    |  37 ++++++
 test/es6tests.js    |   6 +-
 test/jsx.js         |  89 +++++++-------
 test/parens.js      |   7 +-
 test/parser.js      |   4 +-
 test/printer.js     |  56 +++++++--
 test/type-syntax.js | 176 +++++++++++++--------------
 17 files changed, 689 insertions(+), 336 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 695586b..10219ea 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,5 +7,9 @@ node_js:
   - "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 d4d7c19..7cb75c1 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# recast, _v_. [![Build Status](https://travis-ci.org/benjamn/recast.png?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)
 
 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/comments.js b/lib/comments.js
index 4247b6a..7a32b56 100644
--- a/lib/comments.js
+++ b/lib/comments.js
@@ -216,7 +216,7 @@ function breakTies(tiesToBreak, lines) {
            (comment = tiesToBreak[indexOfFirstLeadingComment]) &&
            // If the comment is a //-style comment and indented more
            // deeply than the node itself, reconsider it as trailing.
-           comment.type === "Line" &&
+           (comment.type === "Line" || comment.type === "CommentLine") &&
            comment.loc.start.column > fn.loc.start.column) {
         ++indexOfFirstLeadingComment;
     }
@@ -337,10 +337,11 @@ exports.printComments = function(path, print) {
         var leading = types.getFieldValue(comment, "leading");
         var trailing = types.getFieldValue(comment, "trailing");
 
-        if (leading || (trailing && comment.type !== "Block")) {
+        if (leading || (trailing && !(n.Statement.check(value) ||
+                                      comment.type === "Block" ||
+                                      comment.type === "CommentBlock"))) {
             leadingParts.push(printLeadingComment(commentPath, print));
         } else if (trailing) {
-            assert.strictEqual(comment.type, "Block");
             trailingParts.push(printTrailingComment(commentPath, print));
         }
     }, "comments");
diff --git a/lib/fast-path.js b/lib/fast-path.js
index 1d97ab1..4f6ab77 100644
--- a/lib/fast-path.js
+++ b/lib/fast-path.js
@@ -319,6 +319,11 @@ FPp.needsParens = function(assumeExpressionContext) {
         }
 
     case "ArrowFunctionExpression":
+        if(parent.type === 'CallExpression' && 
+           name === 'callee') {
+            return true;
+        };
+
         return isBinary(parent);
 
     case "ObjectExpression":
diff --git a/lib/options.js b/lib/options.js
index 29ccccb..10f4520 100644
--- a/lib/options.js
+++ b/lib/options.js
@@ -2,7 +2,7 @@ var defaults = {
     // If you want to use a different branch of esprima, or any other
     // module that supports a .parse function, pass that module object to
     // recast.parse as options.parser (legacy synonym: options.esprima).
-    parser: require("esprima-fb"),
+    parser: require("esprima"),
 
     // Number of spaces the pretty-printer should use per tab for
     // indentation. If you do not pass this option explicitly, it will be
diff --git a/lib/parser.js b/lib/parser.js
index de589bd..fa2fef9 100644
--- a/lib/parser.js
+++ b/lib/parser.js
@@ -70,6 +70,8 @@ exports.parse = function parse(source, options) {
             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
@@ -124,6 +126,7 @@ TCp.copy = function(node) {
         // 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;
         }
diff --git a/lib/printer.js b/lib/printer.js
index 5adc810..e44ef0f 100644
--- a/lib/printer.js
+++ b/lib/printer.js
@@ -156,7 +156,53 @@ function maybeAddParens(path, lines) {
 
 function genericPrint(path, options, printPath) {
     assert.ok(path instanceof FastPath);
-    return maybeAddParens(path, genericPrintNoParens(path, options, printPath));
+
+    var node = path.getValue();
+    var parts = [];
+    var needsParens = false;
+    var linesWithoutParens =
+        genericPrintNoParens(path, options, printPath);
+
+    if (! node || linesWithoutParens.isEmpty()) {
+        return linesWithoutParens;
+    }
+
+    if (node.decorators &&
+        node.decorators.length > 0 &&
+        // If the parent node is an export declaration, it will be
+        // responsible for printing node.decorators.
+        ! util.getParentExportDeclaration(path)) {
+
+        path.each(function(decoratorPath) {
+            parts.push(printPath(decoratorPath), "\n");
+        }, "decorators");
+
+    } else if (util.isExportDeclaration(node) &&
+               node.declaration &&
+               node.declaration.decorators) {
+        // Export declarations are responsible for printing any decorators
+        // that logically apply to node.declaration.
+        path.each(function(decoratorPath) {
+            parts.push(printPath(decoratorPath), "\n");
+        }, "declaration", "decorators");
+
+    } else {
+        // Nodes with decorators can't have parentheses, so we can avoid
+        // computing path.needsParens() except in this case.
+        needsParens = path.needsParens();
+    }
+
+    if (needsParens) {
+        parts.unshift("(");
+    }
+
+    parts.push(linesWithoutParens);
+
+    if (needsParens) {
+        parts.push(")");
+    }
+
+    return concat(parts);
 }
 
 function genericPrintNoParens(path, options, print) {
@@ -172,6 +218,8 @@ function genericPrintNoParens(path, options, print) {
 
     namedTypes.Printable.assert(n);
 
+    var parts = [];
+
     switch (n.type) {
     case "File":
         return path.call(print, "program");
@@ -208,7 +256,7 @@ function genericPrintNoParens(path, options, print) {
         ]);
 
     case "MemberExpression":
-        var parts = [path.call(print, "object")];
+        parts.push(path.call(print, "object"));
 
         var property = path.call(print, "property");
         if (n.computed) {
@@ -227,8 +275,6 @@ function genericPrintNoParens(path, options, print) {
         ]);
 
     case "BindExpression":
-        var parts = [];
-
         if (n.object) {
             parts.push(path.call(print, "object"));
         }
@@ -255,8 +301,6 @@ function genericPrintNoParens(path, options, print) {
 
     case "FunctionDeclaration":
     case "FunctionExpression":
-        var parts = [];
-
         if (n.async)
             parts.push("async ");
 
@@ -285,15 +329,14 @@ function genericPrintNoParens(path, options, print) {
         return concat(parts);
 
     case "ArrowFunctionExpression":
-        var parts = [];
-
         if (n.async)
             parts.push("async ");
 
         if (
             n.params.length === 1 &&
             !n.rest &&
-            n.params[0].type === 'Identifier'
+            n.params[0].type === 'Identifier' &&
+            !n.params[0].typeAnnotation
         ) {
             parts.push(path.call(print, "params", 0));
         } else {
@@ -309,8 +352,6 @@ function genericPrintNoParens(path, options, print) {
         return concat(parts);
 
     case "MethodDefinition":
-        var parts = [];
-
         if (n.static) {
             parts.push("static ");
         }
@@ -320,7 +361,7 @@ function genericPrintNoParens(path, options, print) {
         return concat(parts);
 
     case "YieldExpression":
-        var parts = ["yield"];
+        parts.push("yield");
 
         if (n.delegate)
             parts.push("*");
@@ -331,7 +372,7 @@ function genericPrintNoParens(path, options, print) {
         return concat(parts);
 
     case "AwaitExpression":
-        var parts = ["await"];
+        parts.push("await");
 
         if (n.all)
             parts.push("*");
@@ -342,7 +383,7 @@ function genericPrintNoParens(path, options, print) {
         return concat(parts);
 
     case "ModuleDeclaration":
-        var parts = ["module", path.call(print, "id")];
+        parts.push("module", path.call(print, "id"));
 
         if (n.source) {
             assert.ok(!n.body);
@@ -354,8 +395,6 @@ function genericPrintNoParens(path, options, print) {
         return fromString(" ").join(parts);
 
     case "ImportSpecifier":
-        var parts = [];
-
         if (n.imported) {
             parts.push(path.call(print, "imported"));
             if (n.local &&
@@ -372,8 +411,6 @@ function genericPrintNoParens(path, options, print) {
         return concat(parts);
 
     case "ExportSpecifier":
-        var parts = [];
-
         if (n.local) {
             parts.push(path.call(print, "local"));
             if (n.exported &&
@@ -393,7 +430,7 @@ function genericPrintNoParens(path, options, print) {
         return fromString("*");
 
     case "ImportNamespaceSpecifier":
-        var parts = ["* as "];
+        parts.push("* as ");
         if (n.local) {
             parts.push(path.call(print, "local"));
         } else if (n.id) {
@@ -408,82 +445,23 @@ function genericPrintNoParens(path, options, print) {
         return path.call(print, "id");
 
     case "ExportDeclaration":
-        var parts = ["export"];
-
-        if (n["default"]) {
-            parts.push(" default");
-
-        } else if (n.specifiers &&
-                   n.specifiers.length > 0) {
-
-            if (n.specifiers.length === 1 &&
-                n.specifiers[0].type === "ExportBatchSpecifier") {
-                parts.push(" *");
-            } else {
-                parts.push(
-                    " { ",
-                    fromString(", ").join(path.map(print, "specifiers")),
-                    " }"
-                );
-            }
-
-            if (n.source)
-                parts.push(" from ", path.call(print, "source"));
-
-            parts.push(";");
-
-            return concat(parts);
-        }
-
-        if (n.declaration) {
-            var decLines = path.call(print, "declaration");
-            parts.push(" ", decLines);
-            if (lastNonSpaceCharacter(decLines) !== ";") {
-                parts.push(";");
-            }
-        }
-
-        return concat(parts);
-
     case "ExportDefaultDeclaration":
-        return concat([
-            "export default ",
-            path.call(print, "declaration")
-        ]);
-
     case "ExportNamedDeclaration":
-        var parts = ["export "];
-
-        if (n.declaration) {
-            parts.push(path.call(print, "declaration"));
-        }
-
-        if (n.specifiers &&
-            n.specifiers.length > 0) {
-            parts.push(
-                n.declaration ? ", {" : "{",
-                fromString(", ").join(path.map(print, "specifiers")),
-                "}"
-            );
-        }
-
-        if (n.source) {
-            parts.push(" from ", path.call(print, "source"));
-        }
-
-        return concat(parts);
+        return printExportDeclaration(path, options, print);
 
     case "ExportAllDeclaration":
-        var parts = ["export *"];
+        parts.push("export *");
 
         if (n.exported) {
             parts.push(" as ", path.call(print, "exported"));
         }
 
-        return concat([
+        parts.push(
             " from ",
             path.call(print, "source")
-        ]);
+        );
+
+        return concat(parts);
 
     case "ExportNamespaceSpecifier":
         return concat(["* as ", path.call(print, "exported")]);
@@ -492,7 +470,7 @@ function genericPrintNoParens(path, options, print) {
         return path.call(print, "exported");
 
     case "ImportDeclaration":
-        var parts = ["import "];
+        parts.push("import ");
 
         if (n.importKind && n.importKind !== "value") {
             parts.push(n.importKind + " ");
@@ -552,15 +530,13 @@ function genericPrintNoParens(path, options, print) {
         ]);
 
     case "ReturnStatement":
-        var parts = ["return"];
+        parts.push("return");
 
         if (n.argument) {
             var argLines = path.call(print, "argument");
             if (argLines.length > 1 &&
-                (namedTypes.XJSElement &&
-                 namedTypes.XJSElement.check(n.argument) ||
-                 namedTypes.JSXElement &&
-                 namedTypes.JSXElement.check(n.argument))) {
+                namedTypes.JSXElement &&
+                namedTypes.JSXElement.check(n.argument)) {
                 parts.push(
                     " (\n",
                     argLines.indent(options.tabWidth),
@@ -601,7 +577,7 @@ function genericPrintNoParens(path, options, print) {
         });
 
         var oneLine = (isTypeAnnotation && len === 1) || len === 0;
-        var parts = [oneLine ? "{" : "{\n"];
+        parts.push(oneLine ? "{" : "{\n");
 
         var i = 0;
         fields.forEach(function(field) {
@@ -650,14 +626,6 @@ function genericPrintNoParens(path, options, print) {
             return printMethod(path, options, print);
         }
 
-        var parts = [];
-
-        if (n.decorators) {
-            path.each(function(decoratorPath) {
-                parts.push(print(decoratorPath), "\n");
-            }, "decorators");
-        }
-
         var key = path.call(print, "key");
         if (n.computed) {
             parts.push("[", key, "]");
@@ -682,7 +650,7 @@ function genericPrintNoParens(path, options, print) {
         var printed = path.map(print, "elements");
         var joined = fromString(", ").join(printed);
         var oneLine = joined.getLineLength(1) <= options.wrapColumn;
-        var parts = [oneLine ? "[" : "[\n"];
+        parts.push(oneLine ? "[" : "[\n");
 
         path.each(function(elemPath) {
             var i = elemPath.getName();
@@ -741,14 +709,17 @@ function genericPrintNoParens(path, options, print) {
         return fromString(nodeStr(n.value, options), options);
 
     case "UnaryExpression":
-        var parts = [n.operator];
+        parts.push(n.operator);
         if (/[a-z]$/.test(n.operator))
             parts.push(" ");
         parts.push(path.call(print, "argument"));
         return concat(parts);
 
     case "UpdateExpression":
-        var parts = [path.call(print, "argument"), n.operator];
+        parts.push(
+            path.call(print, "argument"),
+            n.operator
+        );
 
         if (n.prefix)
             parts.reverse();
@@ -763,7 +734,7 @@ function genericPrintNoParens(path, options, print) {
         ]);
 
     case "NewExpression":
-        var parts = ["new ", path.call(print, "callee")];
+        parts.push("new ", path.call(print, "callee"));
         var args = n.arguments;
         if (args) {
             parts.push(printArgumentsList(path, options, print));
@@ -772,7 +743,7 @@ function genericPrintNoParens(path, options, print) {
         return concat(parts);
 
     case "VariableDeclaration":
-        var parts = [n.kind, " "];
+        parts.push(n.kind, " ");
         var maxLen = 0;
         var printed = path.map(function(childPath) {
             var lines = print(childPath);
@@ -907,14 +878,14 @@ function genericPrintNoParens(path, options, print) {
         ]);
 
     case "BreakStatement":
-        var parts = ["break"];
+        parts.push("break");
         if (n.label)
             parts.push(" ", path.call(print, "label"));
         parts.push(";");
         return concat(parts);
 
     case "ContinueStatement":
-        var parts = ["continue"];
+        parts.push("continue");
         if (n.label)
             parts.push(" ", path.call(print, "label"));
         parts.push(";");
@@ -928,10 +899,10 @@ function genericPrintNoParens(path, options, print) {
         ]);
 
     case "TryStatement":
-        var parts = [
+        parts.push(
             "try ",
             path.call(print, "block")
-        ];
+        );
 
         if (n.handler) {
             parts.push(" ", path.call(print, "handler"));
@@ -948,7 +919,7 @@ function genericPrintNoParens(path, options, print) {
         return concat(parts);
 
     case "CatchClause":
-        var parts = ["catch (", path.call(print, "param")];
+        parts.push("catch (", path.call(print, "param"));
 
         if (n.guard)
             // Note: esprima does not recognize conditional catch clauses.
@@ -973,8 +944,6 @@ function genericPrintNoParens(path, options, print) {
         // Note: ignoring n.lexical because it has no printing consequences.
 
     case "SwitchCase":
-        var parts = [];
-
         if (n.test)
             parts.push("case ", path.call(print, "test"), ":");
         else
@@ -993,40 +962,33 @@ function genericPrintNoParens(path, options, print) {
 
     // JSX extensions below.
 
-    case "XJSAttribute":
     case "JSXAttribute":
-        var parts = [path.call(print, "name")];
+        parts.push(path.call(print, "name"));
         if (n.value)
             parts.push("=", path.call(print, "value"));
         return concat(parts);
 
-    case "XJSIdentifier":
     case "JSXIdentifier":
         return fromString(n.name, options);
 
-    case "XJSNamespacedName":
     case "JSXNamespacedName":
         return fromString(":").join([
             path.call(print, "namespace"),
             path.call(print, "name")
         ]);
 
-    case "XJSMemberExpression":
     case "JSXMemberExpression":
         return fromString(".").join([
             path.call(print, "object"),
             path.call(print, "property")
         ]);
 
-    case "XJSSpreadAttribute":
     case "JSXSpreadAttribute":
         return concat(["{...", path.call(print, "argument"), "}"]);
 
-    case "XJSExpressionContainer":
     case "JSXExpressionContainer":
         return concat(["{", path.call(print, "expression"), "}"]);
 
-    case "XJSElement":
     case "JSXElement":
         var openingLines = path.call(print, "openingElement");
 
@@ -1041,11 +1003,7 @@ function genericPrintNoParens(path, options, print) {
 
                 if (namedTypes.Literal.check(child) &&
                     typeof child.value === "string") {
-                    if (/\S/.test(child.value)) {
-                        return child.value.replace(/^\s+|\s+$/g, "");
-                    } else if (/\n/.test(child.value)) {
-                        return "\n";
-                    }
+                    return child.value;
                 }
 
                 return print(childPath);
@@ -1060,9 +1018,8 @@ function genericPrintNoParens(path, options, print) {
             closingLines
         ]);
 
-    case "XJSOpeningElement":
     case "JSXOpeningElement":
-        var parts = ["<", path.call(print, "name")];
+        parts.push("<", path.call(print, "name"));
         var attrParts = [];
 
         path.each(function(attrPath) {
@@ -1091,15 +1048,12 @@ function genericPrintNoParens(path, options, print) {
 
         return concat(parts);
 
-    case "XJSClosingElement":
     case "JSXClosingElement":
         return concat(["</", path.call(print, "name"), ">"]);
 
-    case "XJSText":
     case "JSXText":
         return fromString(n.value, options);
 
-    case "XJSEmptyExpression":
     case "JSXEmptyExpression":
         return fromString("");
 
@@ -1124,13 +1078,12 @@ function genericPrintNoParens(path, options, print) {
         ]);
 
     case "ClassPropertyDefinition":
-        var parts = ["static ", path.call(print, "definition")];
+        parts.push("static ", path.call(print, "definition"));
         if (!namedTypes.MethodDefinition.check(n.definition))
             parts.push(";");
         return concat(parts);
 
     case "ClassProperty":
-        var parts = [];
         if (n.static)
             parts.push("static ");
 
@@ -1146,14 +1099,6 @@ function genericPrintNoParens(path, options, print) {
 
     case "ClassDeclaration":
     case "ClassExpression":
-        var parts = [];
-
-        if (n.decorators) {
-            path.each(function(decoratorPath) {
-                parts.push(print(decoratorPath), "\n");
-            }, "decorators");
-        }
-
         parts.push("class");
 
         if (n.id) {
@@ -1172,7 +1117,7 @@ function genericPrintNoParens(path, options, print) {
             );
         }
 
-        if (n["implements"]) {
+        if (n["implements"] && n['implements'].length > 0) {
             parts.push(
                 " implements ",
                 fromString(", ").join(path.map(print, "implements"))
@@ -1188,7 +1133,7 @@ function genericPrintNoParens(path, options, print) {
 
     case "TemplateLiteral":
         var expressions = path.map(print, "expressions");
-        var parts = ["`"];
+        parts.push("`");
 
         path.each(function(childPath) {
             var i = childPath.getName();
@@ -1238,8 +1183,6 @@ function genericPrintNoParens(path, options, print) {
     // Type Annotations for Facebook Flow, typically stripped out or
     // transformed away before printing.
     case "TypeAnnotation":
-        var parts = [];
-
         if (n.typeAnnotation) {
             if (n.typeAnnotation.type !== "FunctionTypeAnnotation") {
                 parts.push(": ");
@@ -1270,40 +1213,45 @@ function genericPrintNoParens(path, options, print) {
         return fromString("" + n.value, options);
 
     case "DeclareClass":
-        return concat([
-            fromString("declare class ", options),
+        return printFlowDeclaration(path, [
+            "class ",
             path.call(print, "id"),
             " ",
             path.call(print, "body"),
         ]);
 
     case "DeclareFunction":
-        return concat([
-            fromString("declare function ", options),
+        return printFlowDeclaration(path, [
+            "function ",
             path.call(print, "id"),
             ";"
         ]);
 
     case "DeclareModule":
-        return concat([
-            fromString("declare module ", options),
+        return printFlowDeclaration(path, [
+            "module ",
             path.call(print, "id"),
             " ",
             path.call(print, "body"),
         ]);
 
     case "DeclareVariable":
-        return concat([
-            fromString("declare var ", options),
+        return printFlowDeclaration(path, [
+            "var ",
             path.call(print, "id"),
             ";"
         ]);
 
+    case "DeclareExportDeclaration":
+        return concat([
+            "declare ",
+            printExportDeclaration(path, options, print)
+        ]);
+
     case "FunctionTypeAnnotation":
         // FunctionTypeAnnotation is ambiguous:
         // declare function(a: B): void; OR
         // var A: (a: B) => void;
-        var parts = [];
         var parent = path.getParentNode(0);
         var isArrowFunctionTypeAnnotation = !(
             namedTypes.ObjectTypeCallProperty.check(parent) ||
@@ -1338,6 +1286,7 @@ function genericPrintNoParens(path, options, print) {
     case "FunctionTypeParam":
         return concat([
             path.call(print, "name"),
+            n.optional ? '?' : '',
             ": ",
             path.call(print, "typeAnnotation"),
         ]);
@@ -1348,13 +1297,16 @@ function genericPrintNoParens(path, options, print) {
             path.call(print, "typeParameters")
         ]);
 
+    case "DeclareInterface":
+        parts.push("declare ");
+
     case "InterfaceDeclaration":
-        var parts = [
+        parts.push(
             fromString("interface ", options),
             path.call(print, "id"),
             path.call(print, "typeParameters"),
             " "
-        ];
+        );
 
         if (n["extends"]) {
             parts.push(
@@ -1383,6 +1335,12 @@ function genericPrintNoParens(path, options, print) {
             path.call(print, "typeAnnotation")
         ]);
 
+    case "NullLiteralTypeAnnotation":
+        return fromString("null", options);
+
+    case "ThisTypeAnnotation":
+        return fromString("this", options);
+
     case "NumberTypeAnnotation":
         return fromString("number", options);
 
@@ -1423,6 +1381,9 @@ function genericPrintNoParens(path, options, print) {
     case "StringTypeAnnotation":
         return fromString("string", options);
 
+    case "DeclareTypeAlias":
+        parts.push("declare ");
+
     case "TypeAlias":
         return concat([
             "type ",
@@ -1645,12 +1606,6 @@ function printMethod(path, options, print) {
 
     namedTypes.FunctionExpression.assert(node.value);
 
-    if (node.decorators) {
-        path.each(function(decoratorPath) {
-            parts.push(print(decoratorPath), "\n");
-        }, "decorators");
-    }
-
     if (node.value.async) {
         parts.push("async ");
     }
@@ -1734,6 +1689,66 @@ function printFunctionParams(path, options, print) {
     return joined;
 }
 
+function printExportDeclaration(path, options, print) {
+    var decl = path.getValue();
+    var parts = ["export "];
+
+    namedTypes.Declaration.assert(decl);
+
+    if (decl["default"] ||
+        decl.type === "ExportDefaultDeclaration") {
+        parts.push("default ");
+    }
+
+    if (decl.declaration) {
+        parts.push(path.call(print, "declaration"));
+
+    } else if (decl.specifiers &&
+               decl.specifiers.length > 0) {
+
+        if (decl.specifiers.length === 1 &&
+            decl.specifiers[0].type === "ExportBatchSpecifier") {
+            parts.push("*");
+        } else {
+            parts.push(
+                "{",
+                fromString(", ").join(path.map(print, "specifiers")),
+                "}"
+            );
+        }
+
+        if (decl.source) {
+            parts.push(" from ", path.call(print, "source"));
+        }
+    }
+
+    var lines = concat(parts);
+
+    if (lastNonSpaceCharacter(lines) !== ";") {
+        lines = concat([lines, ";"]);
+    }
+
+    return lines;
+}
+
+function printFlowDeclaration(path, parts) {
+    var parentExportDecl = util.getParentExportDeclaration(path);
+
+    if (parentExportDecl) {
+        assert.strictEqual(
+            parentExportDecl.type,
+            "DeclareExportDeclaration"
+        );
+    } else {
+        // If the parent node has type DeclareExportDeclaration, then it
+        // will be responsible for printing the "declare" token. Otherwise
+        // it needs to be printed with this non-exported declaration node.
+        parts.unshift("declare ");
+    }
+
+    return concat(parts);
+}
+
 function adjustClause(clause, options) {
     if (clause.length > 1)
         return concat([" ", clause]);
diff --git a/lib/util.js b/lib/util.js
index 5497f86..f78cdaa 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -6,6 +6,7 @@ var sourceMap = require("source-map");
 var SourceMapConsumer = sourceMap.SourceMapConsumer;
 var SourceMapGenerator = sourceMap.SourceMapGenerator;
 var hasOwn = Object.prototype.hasOwnProperty;
+var util = exports;
 
 function getUnionOfKeys() {
   var result = {};
@@ -19,12 +20,12 @@ function getUnionOfKeys() {
   }
   return result;
 }
-exports.getUnionOfKeys = getUnionOfKeys;
+util.getUnionOfKeys = getUnionOfKeys;
 
 function comparePos(pos1, pos2) {
   return (pos1.line - pos2.line) || (pos1.column - pos2.column);
 }
-exports.comparePos = comparePos;
+util.comparePos = comparePos;
 
 function copyPos(pos) {
   return {
@@ -32,9 +33,9 @@ function copyPos(pos) {
     column: pos.column
   };
 }
-exports.copyPos = copyPos;
+util.copyPos = copyPos;
 
-exports.composeSourceMaps = function(formerMap, latterMap) {
+util.composeSourceMaps = function(formerMap, latterMap) {
   if (formerMap) {
     if (!latterMap) {
       return formerMap;
@@ -83,7 +84,7 @@ exports.composeSourceMaps = function(formerMap, latterMap) {
   return smg.toJSON();
 };
 
-exports.getTrueLoc = function(node, lines) {
+util.getTrueLoc = function(node, lines) {
   // It's possible that node is newly-created (not parsed by Esprima),
   // in which case it probably won't have a .loc property (or an
   // .original property for that matter). That's fine; we'll just
@@ -92,42 +93,58 @@ exports.getTrueLoc = function(node, lines) {
     return null;
   }
 
-  var start = node.loc.start;
-  var end = node.loc.end;
+  var result = {
+    start: node.loc.start,
+    end: node.loc.end
+  };
+
+  function include(node) {
+    expandLoc(result, node.loc);
+  }
 
   // If the node has any comments, their locations might contribute to
   // the true start/end positions of the node.
   if (node.comments) {
-    node.comments.forEach(function(comment) {
-      if (comment.loc) {
-        if (comparePos(comment.loc.start, start) < 0) {
-          start = comment.loc.start;
-        }
-
-        if (comparePos(end, comment.loc.end) < 0) {
-          end = comment.loc.end;
-        }
-      }
-    });
+    node.comments.forEach(include);
+  }
+
+  // If the node is an export declaration and its .declaration has any
+  // decorators, their locations might contribute to the true start/end
+  // positions of the export declaration node.
+  if (util.isExportDeclaration(node) &&
+      node.declaration.decorators) {
+    node.declaration.decorators.forEach(include);
   }
 
-  if (comparePos(start, end) < 0) {
+  if (comparePos(result.start, result.end) < 0) {
     // Trim leading whitespace.
-    start = copyPos(start);
-    lines.skipSpaces(start, false, true);
+    result.start = copyPos(result.start);
+    lines.skipSpaces(result.start, false, true);
 
-    if (comparePos(start, end) < 0) {
+    if (comparePos(result.start, result.end) < 0) {
       // Trim trailing whitespace, if the end location is not already the
       // same as the start location.
-      end = copyPos(end);
-      lines.skipSpaces(end, true, true);
+      result.end = copyPos(result.end);
+      lines.skipSpaces(result.end, true, true);
     }
   }
 
-  return { start: start, end: end };
+  return result;
 };
 
-exports.fixFaultyLocations = function(node, lines) {
+function expandLoc(parentLoc, childLoc) {
+  if (parentLoc && childLoc) {
+    if (comparePos(childLoc.start, parentLoc.start) < 0) {
+      parentLoc.start = childLoc.start;
+    }
+
+    if (comparePos(parentLoc.end, childLoc.end) < 0) {
+      parentLoc.end = childLoc.end;
+    }
+  }
+}
+
+util.fixFaultyLocations = function(node, lines) {
   var loc = node.loc;
   if (loc) {
     if (loc.start.line < 1) {
@@ -141,6 +158,28 @@ exports.fixFaultyLocations = function(node, lines) {
 
   if (node.type === "TemplateLiteral") {
     fixTemplateLiteral(node, lines);
+
+  } else 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) {
+      expandLoc(loc, decorator.loc);
+    });
+
+  } else if (node.declaration && util.isExportDeclaration(node)) {
+    // Nullify .loc information for the child declaration so that we never
+    // try to reprint it without also reprinting the export declaration.
+    node.declaration.loc = null;
+
+    // Expand the .loc of the node responsible for printing the decorators
+    // (here, the export declaration) so that it includes node.decorators.
+    var decorators = node.declaration.decorators;
+    if (decorators) {
+      decorators.forEach(function (decorator) {
+        expandLoc(loc, decorator.loc);
+      });
+    }
+
   } else if ((n.MethodDefinition && n.MethodDefinition.check(node)) ||
              (n.Property.check(node) && (node.method || node.shorthand))) {
     // If the node is a MethodDefinition or a .method or .shorthand
@@ -218,3 +257,27 @@ function fixTemplateLiteral(node, lines) {
     }
   });
 }
+
+util.isExportDeclaration = function (node) {
+  if (node) switch (node.type) {
+  case "ExportDeclaration":
+  case "ExportDefaultDeclaration":
+  case "ExportDefaultSpecifier":
+  case "DeclareExportDeclaration":
+  case "ExportNamedDeclaration":
+  case "ExportAllDeclaration":
+    return true;
+  }
+
+  return false;
+};
+
+util.getParentExportDeclaration = function (path) {
+  var parentNode = path.getParentNode();
+  if (path.getName() === "declaration" &&
+      util.isExportDeclaration(parentNode)) {
+    return parentNode;
+  }
+
+  return null;
+};
diff --git a/package.json b/package.json
index 15c07ba..b87bb3b 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
     "parsing",
     "pretty-printing"
   ],
-  "version": "0.10.39",
+  "version": "0.11.4",
   "homepage": "http://github.com/benjamn/recast",
   "repository": {
     "type": "git",
@@ -24,13 +24,18 @@
     "test": "node ./node_modules/mocha/bin/mocha --reporter spec --full-trace",
     "debug": "node ./node_modules/mocha/bin/mocha --debug-brk --reporter spec"
   },
+  "browser": {
+    "fs": false
+  },
   "dependencies": {
-    "esprima-fb": "~15001.1001.0-dev-harmony-fb",
-    "source-map": "~0.5.0",
+    "ast-types": "0.8.16",
+    "esprima": "~2.7.1",
     "private": "~0.1.5",
-    "ast-types": "0.8.12"
+    "source-map": "~0.5.0"
   },
   "devDependencies": {
+    "babylon": "~6.4.2",
+    "esprima-fb": "^15001.1001.0-dev-harmony-fb",
     "mocha": "~2.2.5"
   },
   "engines": {
diff --git a/test/babylon.js b/test/babylon.js
new file mode 100644
index 0000000..319474e
--- /dev/null
+++ b/test/babylon.js
@@ -0,0 +1,164 @@
+var assert = require("assert");
+var recast = require("../main.js");
+var n = recast.types.namedTypes;
+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: [
+      'asyncFunctions',
+      'asyncGenerators',
+      'classConstructorCall',
+      'classProperties',
+      'decorators',
+      'doExpressions',
+      'exponentiationOperator',
+      'exportExtensions',
+      'flow',
+      'functionSent',
+      'functionBind',
+      'jsx',
+      'objectRestSpread',
+      'trailingFunctionCommas'
+    ]
+  };
+
+  var parseOptions = {
+    parser: {
+      parse: function (source) {
+        return babylon.parse(source, babylonOptions);
+      }
+    }
+  };
+
+  it("should not disappear when surrounding code changes", function () {
+    var code = [
+      'import foo from "foo";',
+      'import React from "react";',
+      '',
+      '@component',
+      '@callExpression({foo: "bar"})',
+      'class DebugPanel extends React.Component {',
+      '  render() {',
+      '    return (',
+      '      <div> test </div>',
+      '    );',
+      '  }',
+      '}',
+      '',
+      'export default DebugPanel;',
+    ].join(eol);
+
+    var ast = recast.parse(code, parseOptions);
+
+    assert.strictEqual(recast.print(ast).code, code);
+
+    var root = new recast.types.NodePath(ast);
+    var reactImportPath = root.get("program", "body", 1);
+    n.ImportDeclaration.assert(reactImportPath.value);
+
+    // Remove the second import statement.
+    reactImportPath.prune();
+
+    var reprinted = recast.print(ast).code;
+
+    assert.ok(reprinted.match(/@component/));
+    assert.ok(reprinted.match(/@callExpression/));
+
+    assert.strictEqual(
+      reprinted,
+      code.split(eol).filter(function (line) {
+        return ! line.match(/^import React from/);
+      }).join(eol)
+    );
+  });
+
+  it("should not disappear when an import is added and `export` is used inline", function () {
+    var code = [
+      'import foo from "foo";',
+      'import React from "react";',
+      '',
+      '@component',
+      '@callExpression({foo: "bar"})',
+      '@callExpressionMultiLine({',
+      '  foo: "bar",',
+      '})',
+      'export class DebugPanel extends React.Component {',
+      '  render() {',
+      '    return (',
+      '      <div> test </div>',
+      '    );',
+      '  }',
+      '}',
+    ].join(eol);
+
+    var ast = recast.parse(code, parseOptions);
+
+    assert.strictEqual(recast.print(ast).code, code);
+
+    var root = new recast.types.NodePath(ast);
+    var body = root.get("program", "body");
+
+    // add a new import statement
+    body.unshift(b.importDeclaration([
+      b.importDefaultSpecifier(b.identifier('x')),
+    ], b.literal('x')));
+
+    var reprinted = recast.print(ast).code;
+
+    assert.ok(reprinted.match(/@component/));
+    assert.ok(reprinted.match(/@callExpression/));
+
+    assert.strictEqual(
+      reprinted,
+      ['import x from "x";'].concat(code.split(eol)).join(eol)
+    );
+  });
+
+  it("should not disappear when an import is added and `export default` is used inline", function () {
+    var code = [
+      'import foo from "foo";',
+      'import React from "react";',
+      '',
+      '@component',
+      '@callExpression({foo: "bar"})',
+      '@callExpressionMultiLine({',
+      '  foo: "bar",',
+      '})',
+      'export default class DebugPanel extends React.Component {',
+      '  render() {',
+      '    return (',
+      '      <div> test </div>',
+      '    );',
+      '  }',
+      '}',
+    ].join(eol);
+
+    var ast = recast.parse(code, parseOptions);
+
+    assert.strictEqual(recast.print(ast).code, code);
+
+    var root = new recast.types.NodePath(ast);
+    var body = root.get("program", "body");
+
+    // add a new import statement
+    body.unshift(b.importDeclaration([
+      b.importDefaultSpecifier(b.identifier('x')),
+    ], b.literal('x')));
+
+    var reprinted = recast.print(ast).code;
+
+    assert.ok(reprinted.match(/@component/));
+    assert.ok(reprinted.match(/@callExpression/));
+
+    assert.strictEqual(
+      reprinted,
+      ['import x from "x";'].concat(code.split(eol)).join(eol)
+    );
+  });
+});
diff --git a/test/comments.js b/test/comments.js
index cd4051c..d7abfe3 100644
--- a/test/comments.js
+++ b/test/comments.js
@@ -220,6 +220,43 @@ describe("comments", function() {
         assert.strictEqual(actual, expected);
     });
 
+    var statementTrailing = [
+        "if (true) {",
+        "  f();",
+        "  // trailing 1",
+        "  /* trailing 2 */",
+        "  // trailing 3",
+        "  /* trailing 4 */",
+        "}"
+    ];
+
+    var statementTrailingExpected = [
+        "if (true) {",
+        "  e();",
+        "  f();",
+        "  // trailing 1",
+        "  /* trailing 2 */",
+        "  // trailing 3",
+        "  /* trailing 4 */",
+        "}"
+    ];
+
+    it("StatementTrailingComments", function() {
+        var code = statementTrailing.join(eol);
+        var ast = recast.parse(code);
+
+        var block = ast.program.body[0].consequent;
+        n.BlockStatement.assert(block);
+
+        block.body.unshift(b.expressionStatement(
+            b.callExpression(b.identifier("e"), [])));
+
+        var actual = recast.print(ast, { tabWidth: 2 }).code;
+        var expected = statementTrailingExpected.join(eol);
+
+        assert.strictEqual(actual, expected);
+    });
+
     var protoAssign = [
         "A.prototype.foo = function() {",
         "  return this.bar();",
diff --git a/test/es6tests.js b/test/es6tests.js
index 4203154..87af3a2 100644
--- a/test/es6tests.js
+++ b/test/es6tests.js
@@ -152,7 +152,7 @@ describe("import/export syntax", function() {
         // const variables must have an initializer
         checkInvalid(
             "export const bar;",
-            "Const must be initialized"
+            "Unexpected token ;"
         );
 
         // Unexpected token identifier, invalid named export syntax
@@ -188,7 +188,7 @@ describe("import/export syntax", function() {
         // Invalid module specifier
         checkInvalid(
             "import foo from bar;",
-            "Invalid module specifier"
+            "Unexpected token"
         );
 
         // Unexpected token default
@@ -224,7 +224,7 @@ describe("import/export syntax", function() {
         // Missing as after import *
         checkInvalid(
             "import * from 'foo';",
-            "Missing as after import *"
+            "Unexpected token"
         );
 
         // Unexpected token =
diff --git a/test/jsx.js b/test/jsx.js
index 7205b9e..5569b48 100644
--- a/test/jsx.js
+++ b/test/jsx.js
@@ -3,45 +3,52 @@ var Printer = require("../lib/printer").Printer;
 var types = require("../lib/types");
 
 describe("JSX Compatability", function() {
-    var printer = new Printer({ tabWidth: 2 });
-
-    function check(source) {
-        var ast1 = parse(source);
-        var ast2 = parse(printer.printGenerically(ast1).code);
-        types.astNodesAreEquivalent.assert(ast1, ast2);
-    }
-
-    it("should parse and print attribute comments", function() {
-        check("<b /* comment */ />");
-        check("<b /* multi\nline\ncomment */ />");
-    });
-
-    it("should parse and print child comments", function() {
-        check("<b>{/* comment */}</b>");
-        check("<b>{/* multi\nline\ncomment */}</b>");
-    });
-
-    it("should parse and print literal attributes", function() {
-        check("<b className=\"hello\" />");
-    });
-
-    it("should parse and print expression attributes", function() {
-        check("<b className={classes} />");
-    });
-
-    it("should parse and print chidren", function() {
-        check("<label><input /></label>");
-    });
-
-    it("should parse and print literal chidren", function() {
-        check("<b>hello world</b>");
-    });
-
-    it("should parse and print expression children", function() {
-        check("<b>{this.props.user.name}</b>");
-    });
-
-    it("should parse and print namespaced elements", function() {
-        check("<Foo.Bar />");
-    });
+  var printer = new Printer({ tabWidth: 2 });
+  var parseOptions = {
+    parser: require("esprima-fb")
+  };
+
+  function check(source) {
+    var ast1 = parse(source, parseOptions);
+    var ast2 = parse(printer.printGenerically(ast1).code, parseOptions);
+    types.astNodesAreEquivalent.assert(ast1, ast2);
+  }
+
+  it("should parse and print attribute comments", function() {
+    check("<b /* comment */ />");
+    check("<b /* multi\nline\ncomment */ />");
+  });
+
+  it("should parse and print child comments", function() {
+    check("<b>{/* comment */}</b>");
+    check("<b>{/* multi\nline\ncomment */}</b>");
+  });
+
+  it("should parse and print literal attributes", function() {
+    check("<b className=\"hello\" />");
+  });
+
+  it("should parse and print expression attributes", function() {
+    check("<b className={classes} />");
+  });
+
+  it("should parse and print chidren", function() {
+    check("<label><input /></label>");
+  });
+
+  it("should parse and print literal chidren", function() {
+    check("<b>hello world</b>");
+  });
+
+  it("should parse and print expression children", function() {
+    check("<b>{this.props.user.name}</b>");
+  });
+
+  it("should parse and print namespaced elements", function() {
+    check("<Foo.Bar />");
+  });
+
+  it('should parse and print string literal children with leading and trailing whitespace', function () {
+    check("<div>Hello, {name} and {name2}.</div>");
+  });
 });
diff --git a/test/parens.js b/test/parens.js
index d9764d3..5fd5211 100644
--- a/test/parens.js
+++ b/test/parens.js
@@ -1,5 +1,5 @@
 var assert = require("assert");
-var esprima = require("esprima-fb");
+var esprima = require("esprima");
 var parse = require("../lib/parser").parse;
 var Printer = require("../lib/printer").Printer;
 var NodePath = require("ast-types").NodePath;
@@ -271,4 +271,9 @@ describe("parens", function() {
             code
         );
     });
+
+    it("should be added to callees that are function expressions", function() {
+        check("(()=>{})()");
+        check("(function(){})()");
+    });
 });
diff --git a/test/parser.js b/test/parser.js
index d729bbf..5839460 100644
--- a/test/parser.js
+++ b/test/parser.js
@@ -27,8 +27,8 @@ describe("parser", function() {
     check("/* block comment */");
     check("// line comment");
     check("\t\t\t");
-    check("\n");
-    check("\n\n");
+    check(eol);
+    check(eol + eol);
     check("    ");
   });
 
diff --git a/test/printer.js b/test/printer.js
index 01d0710..90528fc 100644
--- a/test/printer.js
+++ b/test/printer.js
@@ -807,7 +807,10 @@ describe("printer", function() {
             "});"
         ].join(eol);
 
-        var ast = parse(code);
+        var ast = parse(code, {
+            // Supports trailing commas whereas plain esprima does not.
+            parser: require("esprima-fb")
+        });
 
         var printer = new Printer({
             tabWidth: 2,
@@ -826,7 +829,10 @@ describe("printer", function() {
             ");"
         ].join(eol);
 
-        var ast = parse(code);
+        var ast = parse(code, {
+            // Supports trailing commas whereas plain esprima does not.
+            parser: require("esprima-fb")
+        });
 
         var printer = new Printer({
             tabWidth: 2,
@@ -846,7 +852,10 @@ describe("printer", function() {
             "];"
         ].join(eol);
 
-        var ast = parse(code);
+        var ast = parse(code, {
+            // Supports trailing commas whereas plain esprima does not.
+            parser: require("esprima-fb")
+        });
 
         var printer = new Printer({
             tabWidth: 2,
@@ -866,7 +875,10 @@ describe("printer", function() {
             ") {}"
         ].join(eol);
 
-        var ast = parse(code);
+        var ast = parse(code, {
+            // Supports trailing commas whereas plain esprima does not.
+            parser: require("esprima-fb")
+        });
 
         var printer = new Printer({
             tabWidth: 2,
@@ -885,7 +897,12 @@ describe("printer", function() {
             "}"
         ].join(eol);
 
-        var ast = parse(code);
+        var ast = parse(code, {
+            // Supports rest parameter destructuring whereas plain esprima
+            // does not.
+            parser: require("esprima-fb")
+        });
+
         var printer = new Printer({
             tabWidth: 2
         });
@@ -975,6 +992,29 @@ describe("printer", function() {
         assert.strictEqual(pretty, code);
     });
 
+    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 fn = b.arrowFunctionExpression(
+            [arg],
+            b.blockStatement([]),
+            false
+        );
+
+        var ast = b.program([
+            b.expressionStatement(fn)
+        ]);
+
+        var printer = new Printer();
+        var pretty = printer.printGenerically(ast).code;
+        assert.strictEqual(pretty, code);
+    });
+
     it("prints ClassProperty correctly", function() {
         var code = [
             "class A {",
@@ -1122,7 +1162,7 @@ describe("printer", function() {
         var ast = parse(code, {
             esprima: {
                 parse: function(source, options) {
-                    var program = require("esprima-fb").parse(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
@@ -1178,7 +1218,7 @@ describe("printer", function() {
             "    }",
             "  `",
             "};",
-        ].join("\n");
+        ].join(eol);
 
         var ast = parse(code);
         var pretty = printer.printGenerically(ast).code;
@@ -1204,7 +1244,7 @@ describe("printer", function() {
             "<~ This line should not be indented.",
             "  `,                       // 2 extraneous spaces.",
             "};"
-        ].join("\n");
+        ].join(eol);
 
         var ast = parse(code);
         var printer = new Printer({
diff --git a/test/type-syntax.js b/test/type-syntax.js
index f84391a..87e9fdd 100644
--- a/test/type-syntax.js
+++ b/test/type-syntax.js
@@ -7,90 +7,94 @@ var b = types.builders;
 var eol = require("os").EOL;
 
 describe("type syntax", function() {
-    var printer = new Printer({ tabWidth: 2, quote: 'single' });
-
-    function check(source) {
-        var ast1 = parse(source);
-        var code = printer.printGenerically(ast1).code;
-        var ast2 = parse(code);
-        types.astNodesAreEquivalent.assert(ast1, ast2);
-        assert.strictEqual(source, code);
-    }
-
-    it("should parse and print type annotations correctly", function() {
-        // Import type annotations
-        check("import type foo from 'foo';");
-        check("import typeof foo from 'foo';");
-
-        // Scalar type annotations
-        check("var a: number;");
-        check("var a: number = 5;");
-
-        check("var a: any;");
-        check("var a: boolean;");
-        check("var a: string;");
-        check("var a: 'foo';");
-        check("var a: void;");
-
-        // Nullable
-        check("var a: ?number;");
-
-        // Unions & Intersections
-        check("var a: number | string | boolean = 26;");
-        check("var a: number & string & boolean = 26;");
-
-        // Types
-        check("var a: A = 5;");
-        // TODO!?
-        check("var a: typeof A;");
-
-        // Type aliases
-        check("type A = B;");
-        check("type A = B.C;");
-
-        // Generic
-        check("var a: Array<Foo>;");
-        check("var a: number[];");
-
-        // Return types
-        check("function a(): number {}");
-        check("var a: () => X = fn;");
-
-        // Object
-        check("var a: {" + eol + "  b: number;" + eol + "  x: {y: A};" + eol + "};");
-        check("var b: {[key: string]: number};")
-        check("var c: {(): number};")
-        check("var d: {" + eol + "  [key: string]: A;" + eol + "  [key: number]: B;" + eol + "  (): C;" + eol + "  a: D;" + eol + "};")
-
-        // Casts
-        check("(1 + 1: number);");
-
-        // Declare
-        check("declare var A: string;");
-
-        check("declare function foo(c: C): void;");
-        check("declare function foo(c: C, b: B): void;");
-        check("declare function foo(c: (e: Event) => void, b: B): void;");
-        check("declare class C {x: string}");
-        check("declare module M {" + eol + "  declare function foo(c: C): void;" + eol + "}");
-
-        // Classes
-        check("class A {" + eol + "  a: number;" + eol + "}");
-        check("class A {" + eol + "  foo(a: number): string {}" + eol + "}");
-        check("class A {" + eol + "  static foo(a: number): string {}" + eol + "}");
-
-        // Type parameters
-        check("class A<T> {}");
-        check("class A<X, Y> {}");
-        check("class A<X> extends B<Y> {}");
-        check("function a<T>(y: Y<T>): T {}");
-        check("class A {" + eol + "  foo<T>(a: number): string {}" + eol + "}");
-
-        // Interfaces
-        check("interface A<X> extends B<A>, C {a: number}");
-        check("class A extends B implements C<T>, Y {}");
-
-        // Bounded polymorphism
-        check("class A<T: number> {}");
-    });
+  var printer = new Printer({ tabWidth: 2, quote: 'single' });
+  var parseOptions = {
+    parser: require("esprima-fb")
+  };
+
+  function check(source) {
+    var ast1 = parse(source, parseOptions);
+    var code = printer.printGenerically(ast1).code;
+    var ast2 = parse(code, parseOptions);
+    types.astNodesAreEquivalent.assert(ast1, ast2);
+    assert.strictEqual(source, code);
+  }
+
+  it("should parse and print type annotations correctly", function() {
+    // Import type annotations
+    check("import type foo from 'foo';");
+    check("import typeof foo from 'foo';");
+
+    // Scalar type annotations
+    check("var a: number;");
+    check("var a: number = 5;");
+
+    check("var a: any;");
+    check("var a: boolean;");
+    check("var a: string;");
+    check("var a: 'foo';");
+    check("var a: void;");
+
+    // Nullable
+    check("var a: ?number;");
+
+    // Unions & Intersections
+    check("var a: number | string | boolean = 26;");
+    check("var a: number & string & boolean = 26;");
+
+    // Types
+    check("var a: A = 5;");
+    // TODO!?
+    check("var a: typeof A;");
+
+    // Type aliases
+    check("type A = B;");
+    check("type A = B.C;");
+
+    // Generic
+    check("var a: Array<Foo>;");
+    check("var a: number[];");
+
+    // Return types
+    check("function a(): number {}");
+    check("var a: () => X = fn;");
+
+    // Object
+    check("var a: {" + eol + "  b: number;" + eol + "  x: {y: A};" + eol + "};");
+    check("var b: {[key: string]: number};")
+    check("var c: {(): number};")
+    check("var d: {" + eol + "  [key: string]: A;" + eol + "  [key: number]: B;" + eol + "  (): C;" + eol + "  a: D;" + eol + "};")
+
+    // Casts
+    check("(1 + 1: number);");
+
+    // Declare
+    check("declare var A: string;");
+
+    check("declare function foo(c: C): void;");
+    check("declare function foo(c: C, b: B): void;");
+    check("declare function foo(c: (e: Event) => void, b: B): void;");
+    check("declare function foo(c: C, d?: Array<D>): void;");
+    check("declare class C {x: string}");
+    check("declare module M {" + eol + "  declare function foo(c: C): void;" + eol + "}");
+
+    // Classes
+    check("class A {" + eol + "  a: number;" + eol + "}");
+    check("class A {" + eol + "  foo(a: number): string {}" + eol + "}");
+    check("class A {" + eol + "  static foo(a: number): string {}" + eol + "}");
+
+    // Type parameters
+    check("class A<T> {}");
+    check("class A<X, Y> {}");
+    check("class A<X> extends B<Y> {}");
+    check("function a<T>(y: Y<T>): T {}");
+    check("class A {" + eol + "  foo<T>(a: number): string {}" + eol + "}");
+
+    // Interfaces
+    check("interface A<X> extends B<A>, C {a: number}");
+    check("class A extends B implements C<T>, Y {}");
+
+    // Bounded polymorphism
+    check("class A<T: number> {}");
+  });
 });

-- 
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