A Prettier JavaScript Formatter

January 10, 2017

Today I am announcing prettier, a JavaScript formatter inspired by refmt with advanced support for language features from ES2017, JSX, and Flow. Prettier gets rid of all original styling and guarantees consistency by parsing JavaScript into an AST and pretty-printing the AST. Unlike eslint, there aren't a million configuration options and rules. But more importantly: everything is fixable. I'm excited to have time for my own open-source work now that I've left Mozilla, so this is my way of kicking off 2017.

Here's a live demo. Note the JSX and Flow support. You can type anything into the editor below and it will format it automatically. The maximum line length here is 60. The top editor is the raw source and the bottom is the formatted version.

(The above demo is running with prettier version )

Many of you know that I usually don't use JSX when writing React code. Over a month ago I wanted to try it out, and I realized one of the things holding me back was poor JSX support in Emacs. Emacs has great support for automatically indenting code; I never manually indent anything. But this doesn't work with JSX, and when I looked around at other editors, I found similar problems (other editors are generally worse at forcing correct indentation).

Around the same time I had been using Reason which provides a refmt tool which automatically formats code. I was hooked. It removes all the distractions of writing code; you can write it however you like and instantly format it correctly. I realized this would not only solve my JSX problem, but provide a tool for enforcing consistent styles across teams no matter what editor is used.

If computers are good at anything, they are good at parsing code and analyzing it. So I set out to make this work, and prettier was born. I didn't want to start from scratch, so it's a fork of recast's printer with the internals rewritten to use Wadler's algorithm from "A prettier printer".

Why did I choose this algorithm? First lets look at why none of the existing style tools really work.

There's an extremely important piece missing from existing styling tools: the maximum line length. Sure, you can tell eslint to warn you when you have a line that's too long, but that's an after-thought (eslint never knows how to fix it). The maximum line length is a critical piece the formatter needs for laying out and wrapping code.

For example, take the following code:

foo(arg1, arg2, arg3);

That looks like the right way to format it. However, we've all run into this situation:

foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());

Suddenly our previous format for calling function breaks down because this is too long. What you would probably do is this instead:

foo(
  reallyLongArg(),
  omgSoManyParameters(),
  IShouldRefactorThis(),
  isThereSeriouslyAnotherOne()
);

This clearly shows that the maximum line length has a direct impact on the style of code we desire. The fact that current style tools ignore this means they can't really help with the situations that are actually the most troublesome. Individuals on teams will all format these differently according to their own rules and we lose the consistency we sought after.

Wadler's algorithm described in the paper is a simple constraint-based layout system for code. It "measures" code and will break it across lines if it cross the maximum line width.

Even if we disregard line widths, it's too easy to sneak in various styles of code in all other linters. The most strict linter I know happily lets all these styles happen:

foo({ num: 3 },
  1, 2)

foo(
  { num: 3 },
  1, 2)

foo(
  { num: 3 },
  1,
  2
)

Prettier bans all custom styling by parsing it away and re-printing the parsed AST with its own rules that take the maximum line width into account, wrapping code when necessary.

Respecting Patterns

A lot of work has been put into making prettier actually viable. The output is already very good, and I'm sure there are more tweaks we can make to make everyone happy.

We make sure to tailor code to specific patterns. For example, this is a popular style in JavaScript:

myPromise
  .then(() => {
    // ...
  })
  .then(() => {
    // ...
  })
  .catch(() => {
    // ..
  });

A naive printer would collapse it into something like this:

myPromise.then(() => {
  // ...
}).then(() => {
  // ...
}).catch(() => {
  // ..
});

However, we detect this "chaining" pattern and specifically output the original code where each .then is on its own line.

If you are using a pattern that prettier does not format well, please open an issue and we can talk about ways to detect this and specialize it for your case.

Frictionless Teams

When working in a team, reducing friction is important. This is especially true on large teams. While it's impossible to avoid friction entirely, the more we can leverage tools to make it easier to work together the better.

You might think configuring eslint doesn't take much time, or that teams won't spend much time arguing about syntax. In my experience, that's not true. Even if you've configured eslint out the wazoo, it doesn't actually catch a whole range of style differences. Teams still struggle to enforce a consistent style and it's a big distraction.

The minutiae of syntax does not matter. Let it go. Let a tool like prettier just do it's job and focus on the real problems.

Freedom

It turns out that a tool like prettier actually makes it easier to write code however you want, because you can instantly format it correctly afterwards!

Don't care about writing semicolons? Sure! Go ahead and write this:

function foo() {
  var x = 5
  var y = 6
  var z = 7
  return x + y + z
}

Paste that into the demo at the top and you'll see prettier happily inserts the semicolons for you.

Working on a really complex problem and just want to focus on writing some dirty code? Sure! Put it all on one line. Indulge yourself in dirty syntax. Formatting it correctly is just one keystroke away.

Check out prettier!

Thanks to:

  • Christopher Chedeau for pushing me to make this actually viable and setting up the Jest test suite
  • Pieter Vanderwerff for discussing solutions as he works on a similar project
  • Jordan Walke for writing refmt in the first place and inspiring this