Repository: jlongster/prettier
Pull request #338 (2017-01-19 21:19:11)

Extract parser.js
Before, we would parse things inline in both index.js and run_spec.js. Extracting it should make it easier to manage and understand what is going on.

I ran `AST_COMPARE=1 npm test` and it still works.


Patch:
diff --git a/index.js b/index.js
index bf7b69989..3e47bc5f8 100644
--- a/index.js
+++ b/index.js
@@ -1,30 +1,11 @@
 "use strict";
-const babylon = require("babylon");
-const flowParser = require("flow-parser");
+
 const comments = require("./src/comments");
 const version = require("./package.json").version;
 const printAstToDoc = require("./src/printer").printAstToDoc;
 const printDocToString = require("./src/doc-printer").printDocToString;
 const normalizeOptions = require("./src/options").normalize;
-
-var babylonOptions = {
-  sourceType: "module",
-  allowImportExportEverywhere: false,
-  allowReturnOutsideFunction: false,
-  plugins: [
-    "jsx",
-    "flow",
-    "doExpressions",
-    "objectRestSpread",
-    "decorators",
-    "classProperties",
-    "exportExtensions",
-    "asyncGenerators",
-    "functionBind",
-    "functionSent",
-    "dynamicImport"
-  ]
-};
+const parser = require("./src/parser");
 
 function format(text, opts) {
   let ast;
@@ -32,22 +13,9 @@ function format(text, opts) {
   opts = normalizeOptions(opts);
 
   if (opts.useFlowParser) {
-    ast = flowParser.parse(text, {
-      esproposal_class_instance_fields: true,
-      esproposal_class_static_fields: true,
-      esproposal_export_star_as: true
-    });
-    if (ast.errors.length > 0) {
-      let msg = ast.errors[0].message +
-        " on line " +
-        ast.errors[0].loc.start.line;
-      if (opts.filename) {
-        msg += " in file " + opts.filename;
-      }
-      throw new Error(msg);
-    }
+    ast = parser.parseWithFlow(text, opts.filename);
   } else {
-    ast = babylon.parse(text, babylonOptions);
+    ast = parser.parseWithBabylon(text);
   }
 
   // Interleave comment nodes
diff --git a/src/doc-builders.js b/src/doc-builders.js
index 2dcb5f668..ae4abccc6 100644
--- a/src/doc-builders.js
+++ b/src/doc-builders.js
@@ -11,10 +11,6 @@ function assertDoc(val) {
   );
 }
 
-function fromString(text) {
-  return "" + text;
-}
-
 function concat(parts) {
   parts.forEach(assertDoc);
 
@@ -85,7 +81,6 @@ function join(sep, arr) {
 }
 
 module.exports = {
-  fromString,
   concat,
   join,
   line,
diff --git a/src/fast-path.js b/src/fast-path.js
index 6e873c952..666a65b7d 100644
--- a/src/fast-path.js
+++ b/src/fast-path.js
@@ -82,19 +82,6 @@ FPp.getParentNode = function getParentNode(count) {
   return getNodeHelper(this, ~~count + 1);
 };
 
-// The length of the stack can be either even or odd, depending on whether
-// or not we have a name for the root value. The difference between the
-// index of the root value and the index of the final value is always
-// 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];
-};
-
 // Temporarily push properties named by string arguments given after the
 // callback function onto this.stack, then call the callback with a
 // reference to this (modified) FastPath object. Note that the stack will
@@ -422,14 +409,6 @@ function isBinary(node) {
   return n.BinaryExpression.check(node) || n.LogicalExpression.check(node);
 }
 
-function isUnaryLike(node) {
-  // I considered making SpreadElement and SpreadProperty subtypes
-  // of UnaryExpression, but they're not really Expression nodes.
-  return n.UnaryExpression.check(node) ||
-    n.SpreadElement && n.SpreadElement.check(node) ||
-    n.SpreadProperty && n.SpreadProperty.check(node);
-}
-
 function containsCallExpression(node) {
   if (n.CallExpression.check(node)) {
     return true;
diff --git a/src/parser.js b/src/parser.js
new file mode 100644
index 000000000..bbecc1546
--- /dev/null
+++ b/src/parser.js
@@ -0,0 +1,53 @@
+"use strict";
+
+function parseWithFlow(text, filename) {
+  // Inline the require to avoid loading all the JS if we don't use it
+  const flowParser = require("flow-parser");
+
+  const ast = flowParser.parse(text, {
+    esproposal_class_instance_fields: true,
+    esproposal_class_static_fields: true,
+    esproposal_export_star_as: true
+  });
+
+  if (ast.errors.length > 0) {
+    let msg = ast.errors[0].message +
+      " on line " +
+      ast.errors[0].loc.start.line;
+    if (filename) {
+      msg += " in file " + filename;
+    }
+    throw new Error(msg);
+  }
+
+  return ast;
+}
+
+function parseWithBabylon(text) {
+  // Inline the require to avoid loading all the JS if we don't use it
+  const babylon = require("babylon");
+
+  return babylon.parse(text, {
+    sourceType: "module",
+    allowImportExportEverywhere: false,
+    allowReturnOutsideFunction: false,
+    plugins: [
+      "jsx",
+      "flow",
+      "doExpressions",
+      "objectRestSpread",
+      "decorators",
+      "classProperties",
+      "exportExtensions",
+      "asyncGenerators",
+      "functionBind",
+      "functionSent",
+      "dynamicImport"
+    ]
+  });
+}
+
+module.exports = {
+  parseWithFlow,
+  parseWithBabylon
+};
diff --git a/src/printer.js b/src/printer.js
index 72b8a5893..82635eecd 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -7,7 +7,6 @@ var isIdentifierName = require("esutils").keyword.isIdentifierNameES6;
 var jsesc = require("jsesc");
 
 var docBuilders = require("./doc-builders");
-var fromString = docBuilders.fromString;
 var concat = docBuilders.concat;
 var join = docBuilders.join;
 var line = docBuilders.line;
@@ -96,11 +95,11 @@ function genericPrintNoParens(path, options, print) {
   var n = path.getValue();
 
   if (!n) {
-    return fromString("");
+    return "";
   }
 
   if (typeof n === "string") {
-    return fromString(n, options);
+    return n;
   }
 
   // TODO: For some reason NumericLiteralTypeAnnotation is not
@@ -137,7 +136,7 @@ function genericPrintNoParens(path, options, print) {
     // Babel extension.
     case "Noop":
     case "EmptyStatement":
-      return fromString("");
+      return "";
     case "ExpressionStatement":
       return concat([ path.call(print, "expression"), ";" ]);
     case // Babel extension.
@@ -355,7 +354,7 @@ function genericPrintNoParens(path, options, print) {
 
       return concat(parts);
     case "ExportBatchSpecifier":
-      return fromString("*");
+      return "*"
     case "ImportNamespaceSpecifier":
       parts.push("* as ");
 
@@ -656,15 +655,15 @@ function genericPrintNoParens(path, options, print) {
     case "SequenceExpression":
       return join(", ", path.map(print, "expressions"));
     case "ThisExpression":
-      return fromString("this");
+      return "this"
     case "Super":
-      return fromString("super");
+      return "super"
     case // Babel 6 Literal split
     "NullLiteral":
-      return fromString("null");
+      return "null"
     case // Babel 6 Literal split
     "RegExpLiteral":
-      return fromString(n.extra.raw);
+      return n.extra.raw;
     // Babel 6 Literal split
     case "NumericLiteral":
       return n.extra.raw;
@@ -674,7 +673,7 @@ function genericPrintNoParens(path, options, print) {
     case "StringLiteral":
     case "Literal":
       if (typeof n.value === "number") return n.raw;
-      if (typeof n.value !== "string") return fromString(n.value, options);
+      if (typeof n.value !== "string") return "" + n.value;
 
       return nodeStr(n, options);
     case // Babel 6
@@ -682,7 +681,7 @@ function genericPrintNoParens(path, options, print) {
       return path.call(print, "value");
     case // Babel 6
     "DirectiveLiteral":
-      return fromString(nodeStr(n, options));
+      return nodeStr(n, options);
     case "ModuleSpecifier":
       if (n.local) {
         throw new Error("The ESTree ModuleSpecifier type should be abstract");
@@ -690,7 +689,7 @@ function genericPrintNoParens(path, options, print) {
 
       // The Esprima ModuleSpecifier type is just a string-valued
       // Literal identifying the imported-from module.
-      return fromString(nodeStr(n, options), options);
+      return nodeStr(n, options);
     case "UnaryExpression":
       parts.push(n.operator);
 
@@ -990,7 +989,7 @@ function genericPrintNoParens(path, options, print) {
       return concat(parts);
     // JSX extensions below.
     case "DebuggerStatement":
-      return fromString("debugger;");
+      return "debugger;"
     case "JSXAttribute":
       parts.push(path.call(print, "name"));
 
@@ -1009,7 +1008,7 @@ function genericPrintNoParens(path, options, print) {
 
       return concat(parts);
     case "JSXIdentifier":
-      return fromString(n.name, options);
+      return "" + n.name;
     case "JSXNamespacedName":
       return join(":", [
         path.call(print, "namespace"),
@@ -1082,7 +1081,7 @@ function genericPrintNoParens(path, options, print) {
       ]);
     case "ClassBody":
       if (n.body.length === 0) {
-        return fromString("{}");
+        return "{}"
       }
 
       return concat([
@@ -1209,17 +1208,17 @@ function genericPrintNoParens(path, options, print) {
       return concat([ "[", join(", ", path.map(print, "types")), "]" ]);
     case "ExistentialTypeParam":
     case "ExistsTypeAnnotation":
-      return fromString("*", options);
+      return "*"
     case "EmptyTypeAnnotation":
-      return fromString("empty", options);
+      return "empty"
     case "AnyTypeAnnotation":
-      return fromString("any", options);
+      return "any"
     case "MixedTypeAnnotation":
-      return fromString("mixed", options);
+      return "mixed"
     case "ArrayTypeAnnotation":
       return concat([ path.call(print, "elementType"), "[]" ]);
     case "BooleanTypeAnnotation":
-      return fromString("boolean", options);
+      return "boolean"
     case "NumericLiteralTypeAnnotation":
     case "BooleanLiteralTypeAnnotation":
       return "" + n.value;
@@ -1311,7 +1310,7 @@ function genericPrintNoParens(path, options, print) {
       }
 
       parts.push(
-        fromString("interface ", options),
+        "interface ",
         path.call(print, "id"),
         path.call(print, "typeParameters"),
         " "
@@ -1366,11 +1365,11 @@ function genericPrintNoParens(path, options, print) {
     case "NullableTypeAnnotation":
       return concat([ "?", path.call(print, "typeAnnotation") ]);
     case "NullLiteralTypeAnnotation":
-      return fromString("null", options);
+      return "null"
     case "ThisTypeAnnotation":
-      return fromString("this", options);
+      return "this"
     case "NumberTypeAnnotation":
-      return fromString("number", options);
+      return "number"
     case "ObjectTypeCallProperty":
       if (n.static) {
         parts.push("static ");
@@ -1416,13 +1415,13 @@ function genericPrintNoParens(path, options, print) {
         path.call(print, "id")
       ]);
     case "StringLiteralTypeAnnotation":
-      return fromString(nodeStr(n, options), options);
+      return nodeStr(n, options);
     case "NumberLiteralTypeAnnotation":
       assert.strictEqual(typeof n.value, "number");
 
-      return fromString("" + n.value, options);
+      return "" + n.value;
     case "StringTypeAnnotation":
-      return fromString("string", options);
+      return "string"
     case "DeclareTypeAlias":
     case "TypeAlias": {
       const parent = path.getParentNode(1);
@@ -1477,7 +1476,7 @@ function genericPrintNoParens(path, options, print) {
       return concat(parts);
     case "TypeofTypeAnnotation":
       return concat([
-        fromString("typeof ", options),
+        "typeof ",
         path.call(print, "argument")
       ]);
     case "VoidTypeAnnotation":


Test patch:
diff --git a/tests_config/run_spec.js b/tests_config/run_spec.js
index 78ed9ca07..43e3cdd41 100644
--- a/tests_config/run_spec.js
+++ b/tests_config/run_spec.js
@@ -2,6 +2,7 @@
 const fs = require('fs');
 const prettier = require("../");
 const types = require("ast-types");
+const parser = require('../src/parser');
 
 const RUN_AST_TESTS = process.env["AST_COMPARE"];
 
@@ -75,12 +76,7 @@ function stripLocation(ast) {
 }
 
 function parse(string) {
-  const flowParser = require('flow-parser');
-  return stripLocation(flowParser.parse(string, {
-    esproposal_class_instance_fields: true,
-    esproposal_class_static_fields: true,
-    esproposal_export_star_as: true,
-  }));
+  return stripLocation(parser.parseWithFlow(string));
 }
 
 function prettyprint(src, filename, options) {