Repository: jlongster/prettier
Pull request #441 (2017-01-23 21:41:19)

Fix typo in config.useFlowParser variable name
Fixes a typo

btw, this warning looks like this:

<img width="471" alt="screen shot 2017-01-23 at 22 41 51" src="https://cloud.githubusercontent.com/assets/5106466/22223971/3466af42-e1bd-11e6-960f-bcb1cba3d4b2.png">



Patch:
diff --git a/bin/prettier.js b/bin/prettier.js
index 076269a9d..e151edc28 100755
--- a/bin/prettier.js
+++ b/bin/prettier.js
@@ -125,7 +125,12 @@ if (stdin) {
         output = format(input);
       } catch (e) {
         process.exitCode = 2;
-        console.error(filename + ": " + e);
+        if(e.loc) {
+          console.error(filename + ": " + e);
+        }
+        else {
+          console.error(filename + ":", e);
+        }
         return;
       }
 
diff --git a/src/comments.js b/src/comments.js
index 0269f64f5..7493cc7db 100644
--- a/src/comments.js
+++ b/src/comments.js
@@ -7,6 +7,7 @@ var docBuilders = require("./doc-builders");
 var fromString = docBuilders.fromString;
 var concat = docBuilders.concat;
 var hardline = docBuilders.hardline;
+var breakParent = docBuilders.breakParent;
 var util = require("./util");
 var comparePos = util.comparePos;
 var childNodesCacheKey = Symbol("child-nodes");
diff --git a/src/deprecated.js b/src/deprecated.js
index 49c123e7e..5b5a5be43 100644
--- a/src/deprecated.js
+++ b/src/deprecated.js
@@ -6,7 +6,7 @@ const deprecated = {
 
   Prettier now treats your configuration as:
   {
-    ${"\"parser\""}: ${config.seFlowParser ? "\"flow\"" : "\"babylon\""}
+    ${"\"parser\""}: ${config.useFlowParser ? "\"flow\"" : "\"babylon\""}
   }`
 };
 
diff --git a/src/doc-builders.js b/src/doc-builders.js
index 8d185fc4d..7a0c1010f 100644
--- a/src/doc-builders.js
+++ b/src/doc-builders.js
@@ -1,7 +1,7 @@
 "use strict";
 const assert = require("assert");
 const utils = require("./doc-utils");
-const hasHardLine = utils.hasHardLine;
+const willBreak = utils.willBreak;
 
 function assertDoc(val) {
   assert(
@@ -38,7 +38,7 @@ function group(contents, opts) {
 function multilineGroup(contents, opts) {
   return group(
     contents,
-    Object.assign(opts || {}, { shouldBreak: hasHardLine(contents) })
+    Object.assign(opts || {}, { shouldBreak: willBreak(contents) })
   );
 }
 
@@ -60,9 +60,10 @@ function ifBreak(breakContents, flatContents) {
   return { type: "if-break", breakContents, flatContents };
 }
 
+const breakParent =  { type: "break-parent" };
 const line = { type: "line" };
 const softline = { type: "line", soft: true };
-const hardline = { type: "line", hard: true };
+const hardline = concat([{ type: "line", hard: true }, breakParent ]);
 const literalline = { type: "line", hard: true, literal: true };
 
 function join(sep, arr) {
@@ -89,6 +90,7 @@ module.exports = {
   group,
   multilineGroup,
   conditionalGroup,
+  breakParent,
   ifBreak,
   indent
 };
diff --git a/src/doc-debug.js b/src/doc-debug.js
index 36c744dc3..2ae5b3f2f 100644
--- a/src/doc-debug.js
+++ b/src/doc-debug.js
@@ -1,9 +1,5 @@
 "use strict";
 function flattenDoc(doc) {
-  if (!doc || typeof doc === "string" || doc.type === "line") {
-    return doc;
-  }
-
   if (doc.type === "concat") {
     var res = [];
 
@@ -20,26 +16,25 @@ function flattenDoc(doc) {
     }
 
     return Object.assign({}, doc, { parts: res });
-  }
 
-  if (doc.type === "indent") {
-    return Object.assign({}, doc, { contents: flattenDoc(doc.contents) });
-  }
-
-  if (doc.type === "if-break") {
+  } else if (doc.type === "if-break") {
     return Object.assign({}, doc, {
-      breakContents: flattenDoc(doc.breakContents),
-      flatContents: flattenDoc(doc.flatContents)
+      breakContents: doc.breakContents ? flattenDoc(doc.breakContents) : null,
+      flatContents: doc.flatContents ? flattenDoc(doc.flatContents) : null
     });
-  }
 
-  if (doc.type === "group") {
+  } else if (doc.type === "group") {
     return Object.assign({}, doc, {
       contents: flattenDoc(doc.contents),
       expandedStates: doc.expandedStates
         ? doc.expandedStates.map(flattenDoc)
         : doc.expandedStates
     });
+
+  } else if (doc.contents) {
+    return Object.assign({}, doc, { contents: flattenDoc(doc.contents) });
+  } else {
+    return doc;
   }
 }
 
@@ -61,6 +56,10 @@ function printDoc(doc) {
     return "line";
   }
 
+  if (doc.type === "break-parent") {
+    return "breakParent";
+  }
+
   if (doc.type === "concat") {
     return "[" + doc.parts.map(printDoc).join(", ") + "]";
   }
@@ -77,14 +76,14 @@ function printDoc(doc) {
   }
 
   if (doc.type === "group") {
-    return (doc.break
-      ? "multilineGroup"
-      : doc.expandedStates ? "conditionalGroup" : "group") +
+    if(doc.expandedStates) {
+      return "conditionalGroup(" +
+        "[" + doc.expandedStates.map(printDoc).join(",") + "])";
+    }
+
+    return (doc.break ? "wrappedGroup" : "group") +
       "(" +
       printDoc(doc.contents) +
-      (doc.expandedStates
-        ? ", [" + doc.expandedStates.map(printDoc).join(",") + "]"
-        : "") +
       ")";
   }
 }
diff --git a/src/doc-utils.js b/src/doc-utils.js
index 400608eef..4d5149c40 100644
--- a/src/doc-utils.js
+++ b/src/doc-utils.js
@@ -1,38 +1,26 @@
 "use strict";
-function iterDoc(topDoc, func) {
-  const docs = [ topDoc ];
-  while (docs.length !== 0) {
-    const doc = docs.pop();
-    let res = undefined;
-
-    if (typeof doc === "string") {
-      const res = func("string", doc);
-
-      if (res) {
-        return res;
-      }
-    } else {
-      const res = func(doc.type, doc);
-
-      if (res) {
-        return res;
-      }
+function traverseDoc(doc, onEnter, onExit) {
+  if(onEnter) {
+    onEnter(doc);
+  }
 
-      if (doc.type === "concat") {
-        for (var i = doc.parts.length - 1; i >= 0; i--) {
-          docs.push(doc.parts[i]);
-        }
-      } else if (doc.type === "if-break") {
-        if (doc.breakContents) {
-          docs.push(doc.breakContents);
-        }
-        if (doc.flatContents) {
-          docs.push(doc.flatContents);
-        }
-      } else if (doc.type !== "line") {
-        docs.push(doc.contents);
-      }
+  if (doc.type === "concat") {
+    for (var i = 0; i < doc.parts.length; i++) {
+      traverseDoc(doc.parts[i], onEnter, onExit);
+    }
+  } else if (doc.type === "if-break") {
+    if (doc.breakContents) {
+      traverseDoc(doc.breakContents, onEnter, onExit);
+    }
+    if (doc.flatContents) {
+      traverseDoc(doc.flatContents, onEnter, onExit);
     }
+  } else if (doc.contents) {
+    traverseDoc(doc.contents, onEnter, onExit);
+  }
+
+  if(onExit) {
+    onExit(doc);
   }
 }
 
@@ -41,26 +29,91 @@ function isEmpty(n) {
 }
 
 function getFirstString(doc) {
-  return iterDoc(doc, (type, doc) => {
-    if (type === "string" && doc.trim().length !== 0) {
-      return doc;
+  let firstString = null;
+  traverseDoc(doc, doc => {
+    if (
+      typeof doc === "string" && doc.trim().length !== 0 && firstString === null
+    ) {
+      firstString = doc;
+    }
+  });
+  return firstString;
+}
+
+function isLineNext(doc) {
+  let flag = null;
+  traverseDoc(doc, doc => {
+    if(flag === null) {
+      if (typeof doc === "string") {
+        flag = false;
+      }
+      if (doc.type === "line") {
+        flag = true;
+      }
     }
   });
+  return !!flag;
 }
 
-function hasHardLine(doc) {
-  // TODO: If we hit a group, check if it's already marked as a
-  // multiline group because they should be marked bottom-up.
-  return !!iterDoc(doc, (type, doc) => {
-    switch (type) {
+function willBreak(doc) {
+  let willBreak = false;
+  traverseDoc(doc, doc => {
+    switch (doc.type) {
+      case "group":
+        if(doc.break) {
+          willBreak = true;
+        }
       case "line":
         if (doc.hard) {
-          return true;
+          willBreak = true;
         }
 
         break;
     }
   });
+  return willBreak;
+}
+
+function breakParentGroup(groupStack) {
+  if(groupStack.length > 0) {
+    const parentGroup = groupStack[groupStack.length - 1];
+    // Breaks are not propagated through conditional groups because
+    // the user is expected to manually handle what breaks.
+    if(!parentGroup.expandedStates) {
+      parentGroup.break = true;
+    }
+  }
+  return null;
+}
+
+function propagateBreaks(doc) {
+  const groupStack = [];
+  traverseDoc(
+    doc,
+    doc => {
+      if (doc.type === "break-parent") {
+        breakParentGroup(groupStack);
+      }
+      if (doc.type === "group") {
+        groupStack.push(doc);
+      }
+    },
+    doc => {
+      if (doc.type === "group") {
+        const group = groupStack.pop();
+        if(group.break) {
+          breakParentGroup(groupStack);
+        }
+      }
+    }
+  );
 }
 
-module.exports = { isEmpty, getFirstString, hasHardLine };
+module.exports = {
+  isEmpty,
+  getFirstString,
+  willBreak,
+  isLineNext,
+  traverseDoc,
+  propagateBreaks
+};
diff --git a/src/printer.js b/src/printer.js
index 5add5c58e..720e9607d 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -19,7 +19,8 @@ var conditionalGroup = docBuilders.conditionalGroup;
 var ifBreak = docBuilders.ifBreak;
 
 var docUtils = require("./doc-utils");
-var hasHardLine = docUtils.hasHardLine;
+var willBreak = docUtils.willBreak;
+var isLineNext = docUtils.isLineNext;
 var getFirstString = docUtils.getFirstString;
 var isEmpty = docUtils.isEmpty;
 
@@ -226,7 +227,7 @@ function genericPrintNoParens(path, options, print) {
 
       parts.push(
         path.call(print, "typeParameters"),
-        multilineGroup(
+        group(
           concat([
             printFunctionParams(path, print, options),
             printReturnType(path, print)
@@ -255,7 +256,7 @@ function genericPrintNoParens(path, options, print) {
         parts.push(path.call(print, "params", 0));
       } else {
         parts.push(
-          multilineGroup(
+          group(
             concat([
               printFunctionParams(path, print, options),
               printReturnType(path, print)
@@ -808,7 +809,10 @@ function genericPrintNoParens(path, options, print) {
         if (hasBraces && !isEmpty) {
           parts.push(" else");
         } else {
-          parts.push(concat([ hardline, "else" ]));
+          // We use `conditionalGroup` to suppress break propagation.
+          // This allows us to provide a hardline without forcing the
+          // entire `if` clause to break up.
+          parts.push(conditionalGroup([concat([ hardline, "else" ])]));
         }
 
         parts.push(
@@ -1053,7 +1057,7 @@ function genericPrintNoParens(path, options, print) {
         return concat([ "{", path.call(print, "expression"), "}" ]);
       }
 
-      return multilineGroup(
+      return group(
         concat([
           "{",
           indent(
@@ -1684,7 +1688,7 @@ function printArgumentsList(path, options, print) {
     lastArg.type === "NewExpression";
 
   if (groupLastArg) {
-    const shouldBreak = printed.slice(0, -1).some(hasHardLine);
+    const shouldBreak = printed.slice(0, -1).some(willBreak);
     return conditionalGroup(
       [
         concat([ "(", join(concat([ ", " ]), printed), ")" ]),
@@ -2151,13 +2155,13 @@ function printJSXChildren(path, options, print) {
 function printJSXElement(path, options, print) {
   const n = path.getValue();
 
-  // turn <div></div> into <div />
+  // Turn <div></div> into <div />
   if (isEmptyJSXElement(n)) {
     n.openingElement.selfClosing = true;
     delete n.closingElement;
   }
 
-  // if no children, just print the opening element
+  // If no children, just print the opening element
   const openingLines = path.call(print, "openingElement");
   if (n.openingElement.selfClosing) {
     assert.ok(!n.closingElement);
@@ -2165,49 +2169,64 @@ function printJSXElement(path, options, print) {
   }
 
   const children = printJSXChildren(path, options, print);
-  let hasAnyHardLine = false;
+  let forcedBreak = false;
 
-  // trim trailing whitespace, recording if there was a hardline
-  while (children.length && util.getLast(children).type === "line") {
-    if (hasHardLine(util.getLast(children))) hasAnyHardLine = true;
+  // Trim trailing whitespace, recording if there was a hardline
+  while (children.length && isLineNext(util.getLast(children))) {
+    if (willBreak(util.getLast(children))) {
+      forcedBreak = true;
+    }
     children.pop();
   }
 
-  // trim leading whitespace, recording if there was a hardline
-  while (children.length && children[0].type === "line") {
-    if (hasHardLine(children[0])) hasAnyHardLine = true;
+  // Trim leading whitespace, recording if there was a hardline
+  while (children.length && isLineNext(children[0])) {
+    if (willBreak(children[0])) {
+      forcedBreak = true;
+    }
     children.shift();
   }
 
-  // group by line, recording if there was a hardline.
+  // Group by line, recording if there was a hardline.
   let childrenGroupedByLine;
   if (children.length === 1) {
-    if (!hasAnyHardLine && hasHardLine(children[0])) hasAnyHardLine = true;
+    if (!forcedBreak && willBreak(children[0])) {
+      forcedBreak = true;
+    }
 
-    // no need for groups, and a lone jsx whitespace doesn't work otherwise
-    childrenGroupedByLine = [ hardline, children[0] ];
+    // Don't wrap a single child in a group as there is no need, and a
+    // lone JSX whitespace doesn't work otherwise because the group
+    // will be printed in flat mode, and we need to print `{' '}` in
+    // break mode.
+    childrenGroupedByLine = [concat([ hardline, children[0] ])];
   } else {
-    //  prefill leading newline, and initialize the first line's group
-    childrenGroupedByLine = [ hardline, group(concat([])) ];
+    // Prefill leading newline, and initialize the first line's group
+    let groups = [[]];
     children.forEach((child, i) => {
       let prev = children[i - 1];
-      if (prev && hasHardLine(prev)) {
-        hasAnyHardLine = true;
+      if (prev && willBreak(prev)) {
+        forcedBreak = true;
 
-        // on a new line, so create a new group and put this element in it
-        const newLineGroup = group(concat([ child ]));
-        childrenGroupedByLine.push(newLineGroup);
+        // On a new line, so create a new group and put this element
+        // in it.
+        groups.push([ child ]);
       } else {
-        // not on a newline, so add this element to the current group
-        const currentLineGroup = util.getLast(childrenGroupedByLine);
-        currentLineGroup.contents.parts.push(child);
+        // Not on a newline, so add this element to the current group.
+        util.getLast(groups).push(child);
       }
 
-      // ensure we record hardline of last element
-      if (!hasAnyHardLine && i === children.length - 1) {
-        if (hasHardLine(child)) hasAnyHardLine = true;
+      // Ensure we record hardline of last element.
+      if (!forcedBreak && i === children.length - 1) {
+        if (willBreak(child)) forcedBreak = true;
       }
     });
+
+    childrenGroupedByLine = [
+      hardline,
+      // Conditional groups suppress break propagation; we want output
+      // hard lines without breaking up the entire jsx element.
+      concat(groups.map(contents => conditionalGroup([concat(contents)])))
+    ];
   }
 
   const closingLines = path.call(print, "closingElement");
@@ -2224,17 +2243,14 @@ function printJSXElement(path, options, print) {
     ])
   );
 
-  let elem;
-  if (hasAnyHardLine) {
-    elem = multiLineElem;
-  } else {
-    elem = conditionalGroup([
-      group(concat([ openingLines, concat(children), closingLines ])),
-      multiLineElem
-    ]);
+  if (forcedBreak) {
+    return multiLineElem;
   }
 
-  return elem;
+  return conditionalGroup([
+    group(concat([ openingLines, concat(children), closingLines ])),
+    multiLineElem
+  ]);
 }
 
 function maybeWrapJSXElementInParens(path, elem, options) {
@@ -2435,7 +2451,9 @@ function printAstToDoc(ast, options) {
     );
   }
 
-  return printGenerically(FastPath.from(ast));
+  const doc = printGenerically(FastPath.from(ast));
+  docUtils.propagateBreaks(doc);
+  return doc;
 }
 
 module.exports = { printAstToDoc };


Test patch:
diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap
index bb706efb6..4f9e9b811 100644
--- a/tests/comments/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap
@@ -149,11 +149,16 @@ export type AsyncExecuteOptions = child_process$execFileOpts & {
 // optional trailing comma gets moved all the way to the beginning
 const regex = new RegExp(
   // optional trailing comma
-  \"^\\\\s*\" + // beginning of the line
-    \"name\\\\s*=\\\\s*\" + // name =
-    \"[\'\\\"]\" + // opening quotation mark
-    escapeStringRegExp(target.name) + // target name
-    \"[\'\\\"]\" + // closing quotation mark
+  \"^\\\\s*\" +
+    // beginning of the line
+    \"name\\\\s*=\\\\s*\" +
+    // name =
+    \"[\'\\\"]\" +
+    // opening quotation mark
+    escapeStringRegExp(target.name) +
+    // target name
+    \"[\'\\\"]\" +
+    // closing quotation mark
     \",?$\"
 );
 
@@ -345,11 +350,16 @@ export type AsyncExecuteOptions = child_process$execFileOpts & {
 // optional trailing comma gets moved all the way to the beginning
 const regex = new RegExp(
   // optional trailing comma
-  \"^\\\\s*\" + // beginning of the line
-    \"name\\\\s*=\\\\s*\" + // name =
-    \"[\'\\\"]\" + // opening quotation mark
-    escapeStringRegExp(target.name) + // target name
-    \"[\'\\\"]\" + // closing quotation mark
+  \"^\\\\s*\" +
+    // beginning of the line
+    \"name\\\\s*=\\\\s*\" +
+    // name =
+    \"[\'\\\"]\" +
+    // opening quotation mark
+    escapeStringRegExp(target.name) +
+    // target name
+    \"[\'\\\"]\" +
+    // closing quotation mark
     \",?$\"
 );
 
diff --git a/tests/function/__snapshots__/jsfmt.spec.js.snap b/tests/function/__snapshots__/jsfmt.spec.js.snap
index 36eca4ff1..a45974f40 100644
--- a/tests/function/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/function/__snapshots__/jsfmt.spec.js.snap
@@ -388,7 +388,8 @@ new (function() {
 (function() {
 });
 a = function f() {
-} || b;
+} ||
+  b;
 "
 `;
 
diff --git a/tests/refinements/__snapshots__/jsfmt.spec.js.snap b/tests/refinements/__snapshots__/jsfmt.spec.js.snap
index 47713d4d6..2bdaaa8ba 100644
--- a/tests/refinements/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/refinements/__snapshots__/jsfmt.spec.js.snap
@@ -2490,7 +2490,8 @@ type Breakfast = Apple | Orange | Broccoli | Carrot;
 function bar(x: Breakfast) {
   if (x.kind === \"Fruit\") {
     (x.taste: \"Good\");
-  } // error, Apple.taste = Bad else (x.raw: \"No\"); // error, Carrot.raw = Maybe
+  } // error, Apple.taste = Bad else
+    (x.raw: \"No\"); // error, Carrot.raw = Maybe
 }
 
 function qux(x: Breakfast) {
diff --git a/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap b/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap
index 2848c5c28..537666ef9 100644
--- a/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap
@@ -17,7 +17,8 @@ function foo(x: ?string): $NonMaybeType<?string> {
 function foo(x: ?string): $NonMaybeType<?string> {
   if (x != null) {
     return x;
-  } else return 0; // this should be an error
+  } else
+    return 0; // this should be an error
 }
 
 //(foo(): string); // should not be necessary to expose the error above