Repository: jlongster/prettier
Pull request #170 (2017-01-13 18:10:35)

Add test for shebang and move to index.js
When looking into adding a test, I realized that the logic was inside of bin/prettier.js and therefore only applying to the cli. Moving it to index.js and adding a test so that it's more robust :)


Patch:
diff --git a/bin/prettier.js b/bin/prettier.js
index f034c7e3c..a5b6229f0 100755
--- a/bin/prettier.js
+++ b/bin/prettier.js
@@ -33,13 +33,6 @@ if (!filenames.length && !stdin) {
   process.exit(1);
 }
 
-function formatWithShebang(input) {
-  const index = input.indexOf("\n");
-  const shebang = input.slice(0, index + 1);
-  const programInput = input.slice(index + 1);
-  return shebang + format(programInput);
-}
-
 function format(input) {
   return jscodefmt.format(input, {
     printWidth: argv["print-width"],
@@ -77,7 +70,7 @@ if (stdin) {
 
       let output;
       try {
-        output = input.startsWith("#!") ? formatWithShebang(input) : format(input);
+        output = format(input);
       } catch (e) {
         process.exitCode = 2;
         console.error(e);
diff --git a/index.js b/index.js
index 082babfb6..e37fcdf40 100644
--- a/index.js
+++ b/index.js
@@ -23,34 +23,49 @@ var babylonOptions = {
   ]
 };
 
-module.exports = {
-  format: function(text, opts) {
-    opts = opts || {};
-    let ast;
-
-    if(opts.useFlowParser) {
-      ast = flowParser.parse(text);
-      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);
+function format(text, opts) {
+  opts = opts || {};
+  let ast;
+
+  if(opts.useFlowParser) {
+    ast = flowParser.parse(text);
+    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);
     }
-    else {
-      ast = babylon.parse(text, babylonOptions);
-    }
+  }
+  else {
+    ast = babylon.parse(text, babylonOptions);
+  }
 
-    // Interleave comment nodes
-    if(ast.comments) {
-      comments.attach(ast.comments, ast, text);
-      ast.comments = [];
-    }
-    ast.tokens = [];
-    opts.originalText = text;
+  // Interleave comment nodes
+  if(ast.comments) {
+    comments.attach(ast.comments, ast, text);
+    ast.comments = [];
+  }
+  ast.tokens = [];
+  opts.originalText = text;
+
+  const printer = new Printer(opts);
+  return printer.printGenerically(ast).code;
+}
+
+function formatWithShebang(text, opts) {
+  if (!text.startsWith("#!")) {
+    return format(text, opts);
+  }
 
-    const printer = new Printer(opts);
-    return printer.printGenerically(ast).code;
+  const index = text.indexOf("\n");
+  const shebang = text.slice(0, index + 1);
+  const programText = text.slice(index + 1);
+  return shebang + format(programText, opts);
+}
+
+module.exports = {
+  format: function(text, opts) {
+    return formatWithShebang(text, opts);
   }
 };


Test patch:
diff --git a/tests/bracketSpacing/__snapshots__/jsfmt.spec.js.snap b/tests/bracketSpacing/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..69d639e88
--- /dev/null
+++ b/tests/bracketSpacing/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,35 @@
+exports[`test array.js 1`] = `
+"const arr1 = [1,2,3,4];
+const arr2 = [1, 2, 3, 4];
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const arr1 = [ 1, 2, 3, 4 ];
+const arr2 = [ 1, 2, 3, 4 ];
+"
+`;
+
+exports[`test array.js 2`] = `
+"const arr1 = [1,2,3,4];
+const arr2 = [1, 2, 3, 4];
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const arr1 = [1, 2, 3, 4];
+const arr2 = [1, 2, 3, 4];
+"
+`;
+
+exports[`test object.js 1`] = `
+"const obj1 = {a:1, b:2, c:3}
+const obj2 = { a:1, b:2, c:3 };
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const obj1 = { a: 1, b: 2, c: 3 };
+const obj2 = { a: 1, b: 2, c: 3 };
+"
+`;
+
+exports[`test object.js 2`] = `
+"const obj1 = {a:1, b:2, c:3}
+const obj2 = { a:1, b:2, c:3 };
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const obj1 = {a: 1, b: 2, c: 3};
+const obj2 = {a: 1, b: 2, c: 3};
+"
+`;
diff --git a/tests/bracketSpacing/array.js b/tests/bracketSpacing/array.js
new file mode 100644
index 000000000..967d82c21
--- /dev/null
+++ b/tests/bracketSpacing/array.js
@@ -0,0 +1,2 @@
+const arr1 = [1,2,3,4];
+const arr2 = [1, 2, 3, 4];
diff --git a/tests/bracketSpacing/jsfmt.spec.js b/tests/bracketSpacing/jsfmt.spec.js
new file mode 100644
index 000000000..7d17b3723
--- /dev/null
+++ b/tests/bracketSpacing/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname);
+
+run_spec(__dirname, { bracketSpacing: false });
diff --git a/tests/bracketSpacing/object.js b/tests/bracketSpacing/object.js
new file mode 100644
index 000000000..693616c04
--- /dev/null
+++ b/tests/bracketSpacing/object.js
@@ -0,0 +1,2 @@
+const obj1 = {a:1, b:2, c:3}
+const obj2 = { a:1, b:2, c:3 };
diff --git a/tests/quotes/__snapshots__/jsfmt.spec.js.snap b/tests/quotes/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..cbc3e7bd5
--- /dev/null
+++ b/tests/quotes/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,123 @@
+exports[`test functions.js 1`] = `
+"const a = () => \"Foo bar\";
+
+function b(object, key) {
+  return object[\'key\'];
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = () => \"Foo bar\";
+
+function b(object, key) {
+  return object[\"key\"];
+}
+"
+`;
+
+exports[`test functions.js 2`] = `
+"const a = () => \"Foo bar\";
+
+function b(object, key) {
+  return object[\'key\'];
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = () => \'Foo bar\';
+
+function b(object, key) {
+  return object[\'key\'];
+}
+"
+`;
+
+exports[`test strings.js 1`] = `
+"\"abc\"
+\'abc\'
+
+\'\\\'\'
+
+\'\"\'
+\'\\\"\'
+\'\\\\\"\'
+
+\"\'\"
+\"\\\'\"
+\"\\\\\'\"
+
+\"\'\\\"\"
+\'\\\'\"\'
+
+\'\\\\\'
+\"\\\\\"
+
+\'\\0\'
+\'🐶\'
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\"abc\";
+\"abc\";
+
+\"\'\";
+
+\'\"\';
+\'\"\';
+\'\\\\\"\';
+
+\"\'\";
+\"\'\";
+\"\\\\\'\";
+
+\"\'\\\"\";
+\"\'\\\"\";
+
+\"\\\\\";
+\"\\\\\";
+
+\"\\u0000\";
+\"🐶\";
+"
+`;
+
+exports[`test strings.js 2`] = `
+"\"abc\"
+\'abc\'
+
+\'\\\'\'
+
+\'\"\'
+\'\\\"\'
+\'\\\\\"\'
+
+\"\'\"
+\"\\\'\"
+\"\\\\\'\"
+
+\"\'\\\"\"
+\'\\\'\"\'
+
+\'\\\\\'
+\"\\\\\"
+
+\'\\0\'
+\'🐶\'
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\'abc\';
+\'abc\';
+
+\"\'\";
+
+\'\"\';
+\'\"\';
+\'\\\\\"\';
+
+\"\'\";
+\"\'\";
+\"\\\\\'\";
+
+\'\\\'\"\';
+\'\\\'\"\';
+
+\'\\\\\';
+\'\\\\\';
+
+\'\\u0000\';
+\'🐶\';
+"
+`;
diff --git a/tests/quotes/functions.js b/tests/quotes/functions.js
new file mode 100644
index 000000000..56fdd5bce
--- /dev/null
+++ b/tests/quotes/functions.js
@@ -0,0 +1,5 @@
+const a = () => "Foo bar";
+
+function b(object, key) {
+  return object['key'];
+}
diff --git a/tests/quotes/jsfmt.spec.js b/tests/quotes/jsfmt.spec.js
new file mode 100644
index 000000000..821d6e5ee
--- /dev/null
+++ b/tests/quotes/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname);
+
+run_spec(__dirname, { singleQuote: true });
diff --git a/tests/quotes/strings.js b/tests/quotes/strings.js
new file mode 100644
index 000000000..b296b144d
--- /dev/null
+++ b/tests/quotes/strings.js
@@ -0,0 +1,21 @@
+"abc"
+'abc'
+
+'\''
+
+'"'
+'\"'
+'\\"'
+
+"'"
+"\'"
+"\\'"
+
+"'\""
+'\'"'
+
+'\\'
+"\\"
+
+'\0'
+'🐶'
diff --git a/tests/shebang/__snapshots__/jsfmt.spec.js.snap b/tests/shebang/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..ab63719bc
--- /dev/null
+++ b/tests/shebang/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,8 @@
+exports[`test shebang.js 1`] = `
+"#!/usr/bin/env node
+function a() {}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#!/usr/bin/env node
+function a() {}
+"
+`;
diff --git a/tests/shebang/jsfmt.spec.js b/tests/shebang/jsfmt.spec.js
new file mode 100644
index 000000000..989047bcc
--- /dev/null
+++ b/tests/shebang/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname);
diff --git a/tests/shebang/shebang.js b/tests/shebang/shebang.js
new file mode 100644
index 000000000..552d6b4c7
--- /dev/null
+++ b/tests/shebang/shebang.js
@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+function a() {}
diff --git a/tests/tabWith/__snapshots__/jsfmt.spec.js.snap b/tests/tabWith/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..e583af41a
--- /dev/null
+++ b/tests/tabWith/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,105 @@
+exports[`test class.js 1`] = `
+"class A {
+  method() {
+    var x = 1;
+    while(typeof x == \"number\" || typeof x == \"string\") {
+        x = x + 1;
+        if (true) x = \"\";
+    }
+    var z:number = x;
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class A {
+  method() {
+    var x = 1;
+    while (typeof x == \"number\" || typeof x == \"string\") {
+      x = x + 1;
+      if (true)
+        x = \"\";
+    }
+    var z: number = x;
+  }
+}
+"
+`;
+
+exports[`test class.js 2`] = `
+"class A {
+  method() {
+    var x = 1;
+    while(typeof x == \"number\" || typeof x == \"string\") {
+        x = x + 1;
+        if (true) x = \"\";
+    }
+    var z:number = x;
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class A {
+    method() {
+        var x = 1;
+        while (typeof x == \"number\" || typeof x == \"string\") {
+            x = x + 1;
+            if (true)
+                x = \"\";
+        }
+        var z: number = x;
+    }
+}
+"
+`;
+
+exports[`test nested-functions.spec.js 1`] = `
+"const c = () => {};
+
+function a() {
+  return function b() {
+    return () => {
+      return function() {
+        return c;
+      }
+    }
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const c = () => {};
+
+function a() {
+  return function b() {
+    return () => {
+      return function() {
+        return c;
+      };
+    };
+  };
+}
+"
+`;
+
+exports[`test nested-functions.spec.js 2`] = `
+"const c = () => {};
+
+function a() {
+  return function b() {
+    return () => {
+      return function() {
+        return c;
+      }
+    }
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const c = () => {};
+
+function a() {
+    return function b() {
+        return () => {
+            return function() {
+                return c;
+            };
+        };
+    };
+}
+"
+`;
diff --git a/tests/tabWith/class.js b/tests/tabWith/class.js
new file mode 100644
index 000000000..3e07e843a
--- /dev/null
+++ b/tests/tabWith/class.js
@@ -0,0 +1,10 @@
+class A {
+  method() {
+    var x = 1;
+    while(typeof x == "number" || typeof x == "string") {
+        x = x + 1;
+        if (true) x = "";
+    }
+    var z:number = x;
+  }
+}
diff --git a/tests/tabWith/jsfmt.spec.js b/tests/tabWith/jsfmt.spec.js
new file mode 100644
index 000000000..862b4296f
--- /dev/null
+++ b/tests/tabWith/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname);
+
+run_spec(__dirname, { tabWidth: 4 });
diff --git a/tests/tabWith/nested-functions.spec.js b/tests/tabWith/nested-functions.spec.js
new file mode 100644
index 000000000..8e121b753
--- /dev/null
+++ b/tests/tabWith/nested-functions.spec.js
@@ -0,0 +1,11 @@
+const c = () => {};
+
+function a() {
+  return function b() {
+    return () => {
+      return function() {
+        return c;
+      }
+    }
+  }
+}
diff --git a/tests/trailingComma/__snapshots__/jsfmt.spec.js.snap b/tests/trailingComma/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..d6a325b0a
--- /dev/null
+++ b/tests/trailingComma/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,123 @@
+exports[`test function-calls.js 1`] = `
+"const a = (param1, param2, param3) => {}
+
+a(\'value\', \'value2\', \'value3\');
+
+a(
+  \'a-long-value\',
+  \'a-really-really-long-value\',
+  \'a-really-really-really-long-value\',
+);
+
+a(\'value\', \'value2\', a(\'long-nested-value\', \'long-nested-value2\', \'long-nested-value3\'));
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = (param1, param2, param3) => {};
+
+a(\"value\", \"value2\", \"value3\");
+
+a(
+  \"a-long-value\",
+  \"a-really-really-long-value\",
+  \"a-really-really-really-long-value\"
+);
+
+a(
+  \"value\",
+  \"value2\",
+  a(\"long-nested-value\", \"long-nested-value2\", \"long-nested-value3\")
+);
+"
+`;
+
+exports[`test function-calls.js 2`] = `
+"const a = (param1, param2, param3) => {}
+
+a(\'value\', \'value2\', \'value3\');
+
+a(
+  \'a-long-value\',
+  \'a-really-really-long-value\',
+  \'a-really-really-really-long-value\',
+);
+
+a(\'value\', \'value2\', a(\'long-nested-value\', \'long-nested-value2\', \'long-nested-value3\'));
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = (param1, param2, param3) => {};
+
+a(\"value\", \"value2\", \"value3\");
+
+a(
+  \"a-long-value\",
+  \"a-really-really-long-value\",
+  \"a-really-really-really-long-value\",
+);
+
+a(
+  \"value\",
+  \"value2\",
+  a(\"long-nested-value\", \"long-nested-value2\", \"long-nested-value3\"),
+);
+"
+`;
+
+exports[`test object.js 1`] = `
+"const a = {
+  b: true,
+  c: {
+    c1: \'hello\'
+  },
+  d: false
+};
+
+const aLong = {
+  bHasALongName: \'a-long-value\',
+  cHasALongName: {
+    c1: \'a-really-long-value\',
+    c2: \'a-really-really-long-value\',
+  },
+  dHasALongName: \'a-long-value-too\'
+};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = { b: true, c: { c1: \"hello\" }, d: false };
+
+const aLong = {
+  bHasALongName: \"a-long-value\",
+  cHasALongName: {
+    c1: \"a-really-long-value\",
+    c2: \"a-really-really-long-value\"
+  },
+  dHasALongName: \"a-long-value-too\"
+};
+"
+`;
+
+exports[`test object.js 2`] = `
+"const a = {
+  b: true,
+  c: {
+    c1: \'hello\'
+  },
+  d: false
+};
+
+const aLong = {
+  bHasALongName: \'a-long-value\',
+  cHasALongName: {
+    c1: \'a-really-long-value\',
+    c2: \'a-really-really-long-value\',
+  },
+  dHasALongName: \'a-long-value-too\'
+};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = { b: true, c: { c1: \"hello\" }, d: false };
+
+const aLong = {
+  bHasALongName: \"a-long-value\",
+  cHasALongName: {
+    c1: \"a-really-long-value\",
+    c2: \"a-really-really-long-value\",
+  },
+  dHasALongName: \"a-long-value-too\",
+};
+"
+`;
diff --git a/tests/trailingComma/function-calls.js b/tests/trailingComma/function-calls.js
new file mode 100644
index 000000000..86e205476
--- /dev/null
+++ b/tests/trailingComma/function-calls.js
@@ -0,0 +1,11 @@
+const a = (param1, param2, param3) => {}
+
+a('value', 'value2', 'value3');
+
+a(
+  'a-long-value',
+  'a-really-really-long-value',
+  'a-really-really-really-long-value',
+);
+
+a('value', 'value2', a('long-nested-value', 'long-nested-value2', 'long-nested-value3'));
diff --git a/tests/trailingComma/jsfmt.spec.js b/tests/trailingComma/jsfmt.spec.js
new file mode 100644
index 000000000..61e3318df
--- /dev/null
+++ b/tests/trailingComma/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname);
+
+run_spec(__dirname, { trailingComma: true });
diff --git a/tests/trailingComma/object.js b/tests/trailingComma/object.js
new file mode 100644
index 000000000..9bdc3b1f2
--- /dev/null
+++ b/tests/trailingComma/object.js
@@ -0,0 +1,16 @@
+const a = {
+  b: true,
+  c: {
+    c1: 'hello'
+  },
+  d: false
+};
+
+const aLong = {
+  bHasALongName: 'a-long-value',
+  cHasALongName: {
+    c1: 'a-really-long-value',
+    c2: 'a-really-really-long-value',
+  },
+  dHasALongName: 'a-long-value-too'
+};
diff --git a/tests_config/run_spec.js b/tests_config/run_spec.js
index 851303276..9be8940fe 100644
--- a/tests_config/run_spec.js
+++ b/tests_config/run_spec.js
@@ -17,14 +17,14 @@ function removeEmptyStatements(ast) {
   });
 }
 
-function run_spec(dirname) {
+function run_spec(dirname, options) {
   fs.readdirSync(dirname).forEach(filename => {
     if (filename.endsWith('.js') && filename !== 'jsfmt.spec.js') {
       const path = dirname + '/' + filename;
 
       if (!RUN_AST_TESTS) {
         const source = read(path).replace(/\r\n/g, '\n');
-        const output = prettyprint(source, path);
+        const output = prettyprint(source, path, options || {});
         test(filename, () => {
           expect(source + '~'.repeat(80) + '\n' + output).toMatchSnapshot();
         });
@@ -79,8 +79,12 @@ function parse(string) {
   return stripLocation(flowParser.parse(string));
 }
 
-function prettyprint(src, filename) {
-  return prettier.format(src, { filename, useFlowParser: true, printWidth: 80 });
+function prettyprint(src, filename, options) {
+  return prettier.format(src, Object.assign({
+    filename,
+    useFlowParser: true,
+    printWidth: 80,
+  }, options));
 }
 
 function read(filename) {