Hacker News new | past | comments | ask | show | jobs | submit login
A Prettier JavaScript Formatter (jlongster.com)
498 points by danabramov on Jan 10, 2017 | hide | past | favorite | 153 comments



I really have to back up the recommendation for Wadler's "prettier printer" paper. I'm working on a Haskell parser and pretty-printer for Rust and I had pretty much given up on ever finding a lightweight pretty printer that would be able to choose whether to put something on one line or to split it up into multiple lines all by itself. For anyone needing this in Haskell, I strongly urge you to consider the modern rewrite of the initial library [1].

[1] https://hackage.haskell.org/package/wl-pprint-annotated


Yeah, I can second that recommendation on reading the paper [0]. It's one of the nicest I've read. There is also a Rust crate for it[1] - though it's not as... 'pretty'... as the Haskell EDSL, obviously.

[0]: http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettie...

[1]: https://crates.io/crates/pretty


I think there's beauty in the best you can do with a very simple implementation.

Even if you can improve "prettiness" with heroic technical efforts, I think the end result would be uglier. There's something special about simple rules coming together to address just the most important needs.

In the tech world, we tend to put more finesse into things... because we can. But as a result, most software is over-finessed and therefore not finessed at all. A great example is how tortured we let our CSS rules get, rather than allowing a design to relax a little into the constraints of the layout engine.

A hand-set newspaper isn't beautiful because it overcame every constraint, it's beautiful because it accepted its constraints, and made hard tradeoffs in service of a goal.


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

Bob Nystrom (munificent) disagrees[0] after writing one himself:

> The search space we have to cover is exponentially large, and even ranking different solutions is a subtle problem.

[0]: http://journal.stuffwithstuff.com/2015/09/08/the-hardest-pro...


I will note that one could design a pretty printer that reliably had better performance if the formatting style they chose was simpler and more amenable to it.

My (mostly self-imposed) task was more difficult because I was trying to follow the existing style that humans were hand-applying to their Dart code, and that had a lot of tricky non-local cases that look nice but are hard to automate.


I have to say Dartfmt is one of my favorite things about using Dart. It works amazingly well and the fact that it's not configurable is a great decision [0]. I'm never afraid to peek into unfamiliar code because of that. So thank you.

[0] https://github.com/dart-lang/dart_style/wiki/FAQ#why-cant-i-...


You're welcome! That means a lot to me. I tried really really hard to make it produce output I and others would like.

> I'm never afraid to peek into unfamiliar code because of that.

This is exactly why dartfmt exists. It's not about making your code more readable to you, it's about making strangers' code more readable, because that lowers the bar to contribution between people in the ecosystem.


This is a great read! From having dipped my toes into this, I'd say a large part of the problem (from the library designer's point of view) is deciding what interface to expose to the library user and making that as language agnostic as possible. The question becomes: what are the fundamental things that usually characterize pretty-printing? Obviously (or maybe not), you'll want indentation, hard-breaks, soft-breaks, ... but where does it end? Less obviously, you may want to have alignment features. Figuring out the right set of building blocks is what I think Wadler did well.

Also, describing how these building blocks interact using laws is really incredibly useful! For that, credit goes (I think) to Hughes [1].

[1] http://www.cse.chalmers.se/~rjmh/Papers/pretty.ps


That's a great article, it really digs into some of the complex issues


This looks really nice. One of my favourite things about Golang is the use of gofmt and the resulting consistency of all Go programs. The idea of parsing the code to AST representation and then printing it back as source is brilliant, all though it can mess stuff up like the chaining of `.then` as mentioned in the post.


I completely agree. What is, for most programming languages / code, a huge waste of time and huge amount of annoyance becomes something you no longer have to deal with.

Having choices can be nice, but it introduces a huge amount of cognitive load. And, really, if we all just use the same format, we'll all (eventually) get used to it.

We really do have better things to deal with than formatting style.


It also helps if you want to write code conversion tools, like gofix. I once stopped writing such a tool for a Python project because it was a pain trying to change only the affected lines, while keeping the idiosyncratic syntax of the rest of the files unaffected.

(By the way, for people trying to do the same, I recommend the redbaron library, which makes it much easier to handle: https://github.com/PyCQA/redbaron)


gofmt doesn't solve the hard problem that this blog post (and Wadler's paper) is about. From the article: "There's an extremely important piece missing from existing styling tools: the maximum line length."

gofmt's decision to not bother to require a line length may work somewhat for Go (which tends to encourage short lines by virtue of lacking expressiveness), but it doesn't work for lots of other languages, including JavaScript.


The fact that gofmt doesn't enforce some arbitrary line length is a blessing. I have a modern widescreen monitor and I use a maximized window text editor. I have no problem with 200 char length lines. I'm more easily annoyed by 80 (or 60, like in this case) char limits, as if I was viewing this code on punch cards.


It can be configurable. The rust community for example sticks to 100 or 120 lines (and rustfmt lets you choose).

I have big screens but side by side diffs on unformatted code on GitHub are always wrapped. Plus I often split my screen. Not everyone uses maximized windows all the time, and in a shared codebase sometimes you want to try and cater to everyone.


> The fact that gofmt doesn't enforce some arbitrary line length is a blessing.

You don't have an infinitely long screen. You have some line length limit.


You don't usually code for one screen since you're not generally coding for yourself alone. IMHO unlimited line length + editor soft-wrap is the best way to deal with the problem since it automatically adjust the size to the reader's screen.

Same for tabs for indentation[1], that allows the reader to set its own indentation width depending on its own preferences.

I don't really understand why people keep using spaces + fixed line length. Old habits die hard I think.

[1] tabs for indentation, space for aligning


It's infinitely scrollable though. Scrolling horizontally sometimes isn't a bad fallback.


Scrolling horizontally interrupts flow like nothing else!

99% of all software projects have a de facto or de jure line length limit for a reason.


I like to divide my screen into multiple windows each 80 or 90 columns wide, so that I can see several source files at the same time, or code and documentation and shell windows.

Line length limits help me to make really effective use of big screens.


That's exactly how I feel about it. Line length is something I couldn't care less about. If it makes sense to be on one line, then it should be. Otherwise, create the new line where it does make sense.


Why does the formatter need to solve this and not the editor? The editor has more information available to it, like the present screen space.


> Why does the formatter need to solve this and not the editor? The editor has more information available to it, like the present screen space.

Because making the line wrapping nice requires deep syntax-aware editor integration. Most languages try to be at least somewhat plain-text-editor-friendly.


> Because making the line wrapping nice requires deep syntax-aware editor integration.

Why does it need to be syntax aware?


Think what happens when you break a line comment (or a string, in languages where it can't span several lines). And that's an easy example at that.


What happens? Nothing as far as I'm aware. The code is still one line.


> Because making the line wrapping nice requires deep syntax-aware editor integration.

'nice' is the key word here


Theoretically, an editor could. I have yet to see one that does this well. Most do dumb wrapping, with no language sensitivity.

I think it's just a matter of programmer habit now -- folks expect their editor to mirror the code without formatting tweaks.

It gets confusing when you realize that programmers may insert their own indentation and wrapping, and sometimes you want to make this part of the actual file, and sometimes you want to remove it before serializing.


I've been pretty happy with vim and "set breakindent" where the broken line is indented to the parent one. I pretty frequently work with small views of a file.


I wish more people would realise this.


Yeah that's a good point. I was referring more the general idea of enforcing style by parsing the code and reconstructing the source from the AST though. This is a better method than the linter approach that you get with eslint for example.


My team recently ganged up on me to tell me my line spacing was f*cked. Upon reflection, it seems that as I'm writing code I group lines into 'working well - one line, 'not sure - two lines', 'probably will change - lots of space', 'hey look at me! - off by itself'. Apparently, this unconcious invisible system drives others crazy. Developers are so picky! ;-)

We've actually got a bunch of automatic eslint rules, but it doesn't correct for this. Looking at the various beautifiers, I couldn't find anything that really cleaned up line spacing very well, and ended up running a bunch of sed commands to remove lines and then spacing out things manually in a "sane" way (so picky!!). Having a formatter that actually parsed the AST and rewrote the code like gofmt will be very handy if it works - I'll have to try it out.


There's a eslint rule for multiple blank lines


That one actually crashes in my atom installation. I get a warning that it cannot show me where the multiple empty lines are.


After using Elm and the elm-format package in Emacs for the last few weeks I can say that not having to format the source and just letting your editor do it for you on save or via a command is so very nice.

This is a trend that I feel is going to catch on across any language that can support it. It makes trivial decisions and arguments about formatting a thing of the past.


> This is a trend that I feel is going to catch on

This has been pretty standard practice in the boring ol' enterprise for at least 10 or 15 years.

Java has Checkstyle, with format-on-save support for IntelliJ, Eclipse, and NetBeans at the very least. Visual Studio supports this for all of the .NET languages. Obviously, Golang has `go fmt`. Etc.

Not to be snarky, but whenever a bold new trend seems to be really "catching on"... it's almost always a rehash of something that the enterprise was doing back in the 90's, or academic researchers were writing papers about back in the 70's. The only things that ever really change in this industry are: (1) solutions that were once impractical on old hardware become practical on newer hardware, and (2) solutions that were over-engineered in their original form come back in more user-friendly simplified forms.


Have you used the Visual Studio tools?

Could you point me in a direction to set this up on my system?


It's almost crazy to think this already isn't just a thing. Why is this just coming to a head now, after all these years of carrying on over code formatting?


Many communities have been using their own solutions for it for a while (e.g pylint / pep8 if you've used Python, clang-format for C++, js-beautify for javascript, etc) and many companies use "linter as style enforcer" but you still need to watch out for some things that linters don't catch in most languages.

FWIW if you use vim, this plugin is great: https://github.com/google/vim-codefmt


I'm not convinced it is just coming to a head now. I've seen automatic/forced formatting on projects at least as far back as 2000, when I entered the workforce.


The ones I've used for java have been consistently bad though. Autoformat as a commit hook leads to ugly code. Granted, I mean "bad" as in "gets ya 75% of the way".


Yes, forced/automated based on that company standards. I'm referring more to universal.


I bet you heard of "go fmt".


Right, but that's a single relatively new language.


I wonder why this is suddenly accepted. Having very rigid coding standards used to be a no-go for projects you were either not paid for or in an industry where "creative expression" mattered more (i.e. not J2EE).

There were coding styles, but actually enforcing them wasn't something regularly done (linting is okay), for fear of "bondage & discipline" complaints.

Now the former "rock star" Ruby developers are embracing static typing and Ein Code, Ein Style formatting. Weird.


Gofmt was the catalyst for me. I do it with StandardJS in Visual Studio Code on save automatically, I won't waste my time manually formatting code - or caring about it - again.

Personal code formatting preferences is a problem. Devs should just let go of it, once it applies automatically your preferences quickly change.

You can even wire up GitHub etc repos to automatically reject pull requests that add pointless formatting preferences someone will pointedly defend.


Can you point me to more information regarding Github and rejecting pull requests that are strictly formatting changes? Sounds interesting...


The formatters have command line programs that return error codes if a file requires fixing so you can require not getting an error from that in your testing or building process.


i hate the output of elm-format, though :( it's very wasteful of vertical space.


Tangential question:

Why do Haskell and Elm format lists like this?

    type alias Circle =
        { x : Float
        , y : Float
        , radius : Float
        }
...and not like this:

    type alias Circle = {
      x : Float,
      y : Float,
      radius : Float
    }
...? The first makes me shudder with revulsion every time I see it --- that's not where commas go, dammit --- and there must be a reason, which I've never been able to figure out.

The Elm style guide mentions in passing that trailing commas require a diff that adds a field to modify two lines but one, but that applies to leading commas too if you add the field at the beginning of the list.


I get why this bothers a lot of people, but OTOH I get why some people like it. I think the reason for the former in the mind of the user is that the comma clearly signifies that you are adding another value after it, and you save valuable key strokes not having to add a comma to the end of the preceding line just to add another k:v on the line you're concerned about.


- It allows you to easily delete a line without needing to also modify the line before. - It also makes your diffs cleaner. With the latter, if you add a line, you'll also have to inspect the `radius: Float` line when doing a code review, even though it only added a comma.


Putting he comma at the start of the line allows for deleting any line without having to edit another (unless it's the first in which case the missing brace is more obvious IMHO.

The trailing comma is more easily missed I'd say.


this diff, right here, is what i'm complaining about. i decided to bite the bullet and start running elm-format since it was part of the 0.17->0.18 automigration tool, but it has yet to grow on me.

https://github.com/martindemello/crosspad.elm/commit/c67deb4...


And this is why we have yet to converge. We are people, we have opinions! And some of us lead teams and make decisions.


https://github.com/facebook/pfff is a tool which support pretty printing a bunch of different languages (currently c, c++, java, js, php, python, rust, and a bunch more).

The idea is to have a common AST (with parsers for each language) which keeps track of whitespace/comments/etc and then share a ton of logic by writing writing code at the AST layer.

You can use pfff to pretty print according to your style needs. Also supports a bunch of other neat things such as search/replace.


I've looked at pfff and couldn't really figure out what's supposed to work and what isn't. Would love to pretty print python with it. (I've been looking at the many different pretty printers in the python world and haven't found anything that I feel works well enough). Did you use it for pretty printing python?


This has been the single biggest missing tool in the JS ecosystem. I never even realized until I experienced the magic of gofmt. It should honestly be a standard tool for new languages going forward. Kudos to the creator. I'll try it out later today - hopefully it works well for us.


How is it different from jsbeautifier[1] other than enforcing its own style rules? [1] https://github.com/beautify-web/js-beautify


Looking at both github repos, jsbeautifier looks more complicated (as a project), and I don't think it uses the AST or at least I did not see anything that looked like it would create one. Looking at some of the code I saw `string.replace(...)` so it seems to be string-manipulation. It also is for HTML and CSS too it seems, while the project discussed here is just JS, so more targeted, and the approach to go through the AST seems intriguing to me.

PS: This seems to be the code for the JS beautifier in jsbeautifier: https://github.com/beautify-web/js-beautify/blob/master/js/l...


If I personally prefer the second Promise chain:

    myPromise.then(() => {
      // ...
    }).then(() => {
      // ...
    }).catch(() => {
      // ..
    });
and if I also prefer the Lisp-like approach and hate putting:

    );
on its own line, am I part of the problem? What if my whole team uses that style? For example, we would write:

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

    foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(),
            isThereSeriouslyAnotherOne());
(100 characters wide, double indent on the continuation line. This is pretty standard for Java formatting.)

Stated differently, is this meant to be very opinionated in hopes that all JS would follow a uniform style? I think that can work for Go since it was like that from Day 1. For JS, though, it has been out for so long that many teams have developed their own preferred style. I suspect that most of them would avoid an opinionated formatter that differs in a few small ways from their in-house format, if only because it seems silly to break diffs and git blame for a sweeping formatting change.


I think that this is definitely not a tool for everybody, and I think that's alright.

Dan Abramov said it best, when he opened an issue urging to resist the urge to add configuration: https://github.com/jlongster/prettier/issues/40

Configuration has a cost, and the product will be better and more reliable if it's limited. As it stands right now, the configuration isn't ideal for my preferences, but I'm fine with that; either I'll

* use the tool and adopt new patterns (ones which, frankly, have very little impact on anything), * keep doing this stuff manually (it's gotten me this far!) * fork the project, and bear the cost of maintaining the updated configuration myself.


I find if I hate a formatting pattern that I'm forced to use, after a while I prefer it and hate all others.


I avoid opinionated lint rules because it's annoying to retrain yourself, but if my editor does all the work and it's readable enough I'd love to adopt this or something like it.

The version control issues might be a blocker though. It would be neat if git had a way to do diffs at the syntax tree level.


Would using the git diff with whitespace ignored not help?


Prettier inserts missing semicolons, but otherwise maybe.


I appreciate the emacs bindings [0]!

Between this and eslint+flycheck I will feel less envious of Visual Studio Code's editor environment (if only there were a way to leverage some of its fantastic IntelliSense from emacs...)

[0] https://github.com/jlongster/prettier/tree/master/editors/em...


If you're using typescript and emacs, https://github.com/ananthakumaran/tide is a very good integration with the built-in language server for semantic auto-completion and "jump-to" support. I use it every day on large projects.

I think that vscode uses the typescript language server behind-the-scenes for plain javascript analysis.


Tide also works with normal javascript projects. All you need is a jsconfig.json at the root of your project. This gets you the same intellisense as VSC.


Flow is cool, but TypeScript has way more traction. Would jump on this in an instant if it had TypeScript support and a VS Code plugin.


Typescript is another language. It kind of goes without saying that people using a different language be more likely to use something if it were in the language they use, doesn't it?


I mean, by any reasonable definitely of a language, Flow is another language too.


Typescript aims to be a superset of ES6+, with all valid Javascript also being valid Typescript (at least syntax wise).

So while it is another language, it is very much related.



Awesome that this exists already. Now we just need TypeScript support... :)


clang-format supports TypeScript and has a VS Code plugin. (See my other comment in this thread for some discussion of the differences between the tools.)


I may need to check that out. Thanks for the link!


Eventually we should just store the AST in source control. Prevents inconsistencies when style changes and/or 50k lines diffs to change the indentation level.


I certainly think that language definitions should include a standard parsed format in addition to their human-friendly syntax. This can just be an s-expression representation of the normal, concrete syntax, without any transformations applied. This way, we don't have to build language-specific rules for precedence, offside/significant-whitespace, fixity, etc. into every tool for that language.

Implementations (compilers and interpreters) should include tools/modes for converting between these two representations, and should support running programs written in the parsed format in addition to those written in the human-friendly syntax. This isn't asking much: the difficult part is parsing the human-friendly syntax, which implementations must already do.

The benefit is that we don't end up with a mismatch between what the compilers/interpreters accept, and what the other tooling accepts (linters, formatters, doc generators, static analysers, search engines, syntax highlighters, use-finders, go-to-definition, refactoring tools, etc.). This would, incidentally, allow people to read and write code as s-expressions, but that's not the point; it's just about representing concrete syntax as closely as possible (plus arbitrary annotations, e.g. for line numbers, etc.), whilst exposing the structure in a machine-friendly way.

This would also make it much easier to make new tools, extend existing ones, and improve practices, e.g. like syntax-aware diffing, standard code formatting (i.e. tools like this one), version-control-friendly representations, tree-based editors, more powerful navigation in editors and IDEs, etc.


This becomes very difficult when languages change their AST. Most languages are still evolving and the AST changes.


Nice to see that comments are preserved. Not all languages keep comments in their AST.


I made a VS Code plugin for Prettier

https://github.com/esbenp/prettier-vscode

It is my first VS code extension so have no idea if it is correct way. Feedback is welcome.


Cool! Don't worry about it being "correct" or not. :)


Much better than "Standard JS" and its choice of no semicolons. I know about "Semi Standard JS", but I refuse to accept such a little difference that could be a configuration has to live in a entirely new project.


I hate that they had the audacity to call it standard.


But the linked formatter doesn't seem to have an option for no semicolons so I wouldn't call it better in that regard.

EDIT: Looks like they may be open to it though. https://github.com/jlongster/prettier/issues/12


Semicolons are not optional in JavaScript: ASI (http://www.ecma-international.org/ecma-262/6.0/#sec-automati...) is an error correction scheme for novice programmers. The spec's parsing rules calls out the statements following where a semicolon should be "offending tokens". There is no leeway here for style or preference.


The thing is, they made the "error correction scheme" work so well that they are no longer required. Give it a go sometime; you might like it.


Note that Standard is a linter, this is a reformatter. Standard gives you errors but doesn't fix things for you.


https://github.com/feross/standard#is-there-an-automatic-for...

“You can use standard --fix to automatically fix most issues automatically.

standard --fix is built into standard (since v8.0.0) for maximum convenience. Lots of problems are fixable, but some errors, like forgetting to handle the error in node-style callbacks, must be fixed manually.”


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

I'd disagree. See https://www.youtube.com/watch?v=wf-BqAjZb8M


How does this handle tabs vs spaces? I looked the post, the repo and the issues page and I didn't find anything. I'm specially concerned about not seeing this in the options of the API, since there are strong opinions on both sides.


It always prints spaces and you can configure the number of spaces with `--tab-width`.

For tabs/spaces and semicolons I'm considering if we should support those options. We need to figure out the goal of the project: is it to converge on generally a single format, or is it to provide formatting options for a few large groups of people that have different opinions. There are issues on the project discussing this right now.


Whilst it's easy to flame about tabs vs spaces and other bike-sheddy issues online, I wonder how much people really care about it? After all, many people just get on with life when it comes to non-optional syntax, e.g. whilst many people flame about Python's use of significant whitespace, I doubt that's been a major reason for many people to actually use a different language, or some semicolon-and-braces-to-indentation preprocessor.

Perhaps stick to just one, as seems to be the case right now (whichever it is), and mention in the documentation that those wanting the other option are free to e.g. write a patch/fork on GitHub/postprocessor/etc., with the understanding that such support will eventually get merged in iff it's actively maintained for some amount of time, its author/maintainer is active in general development and maintenance for the project, and there's significant community adoption of such a patch.

Whilst not perfect, this sort of approach might better determine who actually cares enough about this to offset the community-splitting effects of allowing both; compared to a general accumulation of online grumbling.


Fantastic. One thing I've learned that's incredibly important is code style has to be uniform across all engineers. Seems like an unimportant point to devs that haven't worked with teams that enforce it, but it really is.

As was once told to me, my code should look like your code and yours like mine.


> One thing I've learned that's incredibly important is code style has to be uniform across all engineers.

Honestly, why does this matter at all?


So people can easily read each other's code.


I have used ESFormatter (https://github.com/millermedeiros/esformatter) with decent amount of success. Does anybody know how Prettier compares to ESFormatter?


esformatter suffers from the same problems as eslint as described in the post. It doesn't take maximum line width into account, which is a critical piece for laying out code.


How does this compare to clang-format, which also formats JavaScript and also (to use one of the features highlighted in the article) obeys line length?


I attempted to use clang-format on a mixed typescript/tsx codebase and found that it mangled jsx expressions. This wasn't surprising but I suspect that typescript users probably overlap heavily with react users so it's not a great solution as is.


Not sure, didn't know it formatted JavaScript! This is written in JS at least so if anything it's more approachable to the community.


Yes, that's a good point. If you ever get a chance to compare to clang-format I'd be interested to read your results. My vague understanding is that clang-format has some sophisticated-ish algorithms for intelligently wrapping complex expressions. However, I also think its output is inferior on code like your .then() chain example.


Nice attempt, but currently wrong. For example, it pretty-formats this code:

  function makeComponent() {
    return /*test*/ { a: 1
    };
  }
into

  function makeComponent() {
    return /*test*/
    { a: 1 };
  }
From my own experience writing a formatter, comments are very difficult (if one wants to preserve them). I guess the author should reparse the generated code to ensure the AST hasn't changed.


We do. The entire Flow test suite passes when pretty-printing and re-parsing and comparing ASTs.

There are a very few subtle bugs like the one you mentioned above. As noted in the readme, this is a beta. It's certainly more than an attempt.


Don't let HN negativity get you down. This is an awesome project and I'll contribute bug fixes/test cases if we end up adopting it on our team (very likely based on your blog post).


the parent gave an ill example and suggest a solution, how's that a negativity?


I believe it was the "Nice attempt, but currently wrong." at the start of the comment. "Nice attempt" could imply that this should be thrown out in favor of another attempt to solve the problem.


The entire comment was patronizing. "Nice attempt" minimizes the effort involved and implies the author is a novice. "but currently wrong" categorizes the entire project as a failure because of a single bug. The followup "From my own experience writing a formatter" really drives the whole thing home.


Classic HN :-)


Wasn't meant that way.


"Nice attempt" used to actually mean the words it said. You know, like "nice job".


Also, “currently wrong” is a really sweeping statement to make based on a single problem written in a fairly uncommon style. Yes, there are things which need to be fixed but that doesn't mean we have to be so dismissive of someone else's work which they've given away for free to the community.


Like dangoor said, it was the way it was delivered.


I'm actually very excited by your "attempt". Looking forward to where it goes!


I think what the GP is saying is to do the re-parsing before/after every time you reformat (and then presumably error exit if the ASTs don't match). When/if you no longer have any known bugs like that you might want to remove it, but it does seem like a good idea until you get there.


Running existing codebases through it and comparing the ASTs is a nice idea. Seems like it should be pretty easy to automated it for the top N (100?) JavaScript projects.


While understanding the state of a project is important for potential adopters, might I suggest filing a GitHub issue in lieu of this comment as something that might actually have a positive outcome?


I've found another issue where it just removes comments entirely inside an otherwise empty block.

    function doSomething() : int {
      return myPromise
      .then(() => {
        // ...do thing
        /* what */
      })
      .catch(() => {
        // ..do other thing
        /* the heck */
      });
    }
becomes

    function doSomething(): int {
      return myPromise
        .then(() => {})
        .catch(() => {});
    }
Still a very nice pretty printer, though.


I used Recast on a codebase at work to migrate to a new code style, and I ran into a long tail of issues with preserving comments that looked like they would require some significant work to fix. I know the author of Recast (Hi Ben) and corresponded with him a little bit about it at the time.


It also is not preserving JSX spacing correctly, and the example the site provides even shows that:

    JSX is<strong>supported</strong>
Note the lack of space after the word "is".


I know this isn't really practical, but as a rule you shouldn't mix comments and code. Comments should be above any function definitions for this and many other obvious reasons. Removing inline comments seems like a reasonable default for a project like this, since those adopting it have already ceded some amount of control over the way their code is structured.


I don't do much serious JS. What's the problem there?


JavaScript has automatic semicolon insertion (ASI).

This will add semicolons all over your code depending on some rules, for example, if you write a return, and write the value you want to return in the next line, it adds a semicolon after the return AND after the value, effectively ignoring the value in the end.

    return
      myValue;
becomes

    return;
      myValue;
It's a bit meh, because you have to remember these rules even if you use semicolons.


The first one returns an object, the second one returns undefined


It seems a pretty format can be subjective depending on personal preference and code complexity. I'm surprised that there aren't and formatted that are customizable in that you break down the format of a function into a few sections and give the user options on how they would best prefer the format to look. (At least there's not a formatter like that which I'm aware of)


This is awesome - this reminds me of Google's clang-format which they use to format Angular automatically, but sounds like it's less sucky. The idea behind it is that developers tend to be opinionated about code style, so having a tool do it for you eliminates those arguments, which can be a hold up for PRs getting merged into a codebase.


This is awesome, but one request: can you please extend it into a real (ie, AST based) diffing tool? Current diffing tools all compare the text (which is basically an input format) rather than the AST, and hence end up focusing on formatting rather than actual changes.


You could hack a diffing tool that reformats both the before and after and shows the diff of the generated text.

This would not catch changes like

    --val  x =  [1]
    ++var x = [
    ++  1,
    ++];
as expected, while it would give you a slightly more verbose diff if the change was

    --val x = [short(), short()];
    ++val x = [
    ++  a_really_long_name_that_pushes_the_line_length(),
    ++  a_really_long_name_that_pushes_the_line_length(),
    ++];


If you auto-format as a commit hook then all of the changes you see will be AST-based, and you can ignore the style.


Oh man, this really needs a vscode plugin, or sublime!


Is there a way to format code before committing it to git, without affecting the working tree?


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

huh? I've been using emacs with WebMode + tide + FlyCheck and it supports jsx just fine. Moreover, tide[1] provides great support for plain javascript (I've used it extensively on a ES6 codebase with great results).

[1]: https://github.com/ananthakumaran/tide


For emacs, recently released rjsx-mode solves the JSX problems.

See https://github.com/felipeochoa/rjsx-mode


I enjoy something similar for Elm code called Elm Format. https://github.com/avh4/elm-format


I was actually just looking for something like this the other day because I'm forced to use actual JS like a json config, so it can use Require to import XML data and sprite sheet location right into a config my application loads. Problem with all the other formatters is they made everything on new lines, which would take up a giant amount of space.

Would be cool to have this as a little website to try out, too.


I'm already using and it works really nice, also submitted a pull request too. But one thing I've always looked for in formatters is still missing here, it's the ability to format the JSX in React.

It would be nice if the JSX in React's render could be formatted nicely because a lot of times the code get's so long that formatting and indenting becomes inaccurate.


Awesome! Can't wait for a sublime plugin :)


Would it be reasonable to have an editor that ran this every time you resize your editor window?

I like to have multiple windows on screen at once, and will often adjust them to make best use of screen real estate. I tend let it soft-wrap the text, but I'd love it if it would do soft-wrapping... prettier.


That sounds like an awesome idea (given that display is totally separated from the format-on-save anyways).

I got a quick feel for it with this in a terminal:

    watch -n 0.1 'prettier --print-width $COLUMNS index.js'
... and unfortunately didn't really like it. Maybe in a pinch, but it still ends up feeling really cramped trying to read column in low-width terminals. At the other extreme (having the terminal be the only window on a 21:9 ultrawide), some of the lines get so long that you'd want a max anyways.


I'm playing with this in CodeMirror (browser based code editor) which talks to a local node.js server, and I think it looks really good, as long as you have lower and upper limits as you suggest. Biggest concern is stuff like the undo history. I think it just needs a "formatting" button or even background process when you leave it idle for a bit. Might be better to run on the browser but haven't bothered trying yet.


In the section of 'respecting patterns', I was really expecting that it'd maintain my style of curly braces if they're already there, which has same indentation level (and on new line) for a function's opening and closing braces.

e.g. :

function something()

{

}


I was hoping to see alignment of = and : when declaring variables and objects.


I'm personally not a fan on alignment because of its impact on the project's commit history and diffs. Not saying it's not nice to look at, I just prefer not changing many unrelated lines because a new variable name is one character longer than the names that surround it.


i guess i'm relatively new to javascript (can count years on one hand), but like many devs, i'm OCD about formatting and i'm a huge fan of eslint. i have found that curating a list of rules can require cycles (not to mention differences in opinion). i recently came across this project which had some allure, and i made note to try when i had a moment: https://github.com/sindresorhus/xo, how does "prettier" compare to this?


am I the only one who dislikes spaces after braces?

i.e foo({ num: 3 },


That's actually one of the few config options. Pass `--bracket-spacing=false` to turn off that spacing (or `bracketSpacing: false` in the API)


This tool looks amazing! Would you consider adding a flag for whether to terminate statements with a semicolon, or consider accepting a PR that added such an option? (My team is all-in on `standard`, so I'd love to be able to execute this in a way that would be an easy fixer for our linting rules.)


Check out this issue, we are discussing it: https://github.com/jlongster/prettier/issues/12

Honestly, we may never do it. But we're still figuring out if the ultimate goal is to keep to a single style or not.


I used to like the style of space padded block statements but not object literals:

    foo(function() { return {num: 3}; });
I used that a lot when writing code that used D3. But since arrow functions, I hardly ever use a single line block statements, so this style is obsolete.


gofmt — the official formatter of the Go language — made a very wise choice for its formatting strategy. I think it's something other tools can learn from.

gofmt will fix indentation, spacing, and so on, but it will generally preserve structure. For example, this:

  a:=Foo{value:42}
becomes, of course:

  a := Foo{value: 42}
But! This:

  a:=Foo{
  value:42,
  }
becomes:

  a := Foo{
    value: 42,
  }
That's because gofmt can't really pretend to know that it knows better than the developer here. Sometimes code does need to be loose (like in a DSL or a big declaration, or a test (which must be readable), or similar). Sometimes it should be compact.

This means you never have to fight gofmt. Never once have I disagreed with its decisions.

gofmt works this way because its ruleset isn't exhaustive; it says that, yes, an indent must happen after a hanging "{", but the ruleset doesn't say that a line break must happen. If there's a line break, let's stick with it.

Prettier seems to have a strict normalization approach: AST goes in, canonical form goes out. For example, I tried this fictional piece of code:

    performAction({
      type: "thing",
      value: 42,
      owner: user, 
      bucket: root,
      path: p
    });
It's turned into a much less readable blob because it happens to fit on a single line:

    performAction({type: "thing", value: 42, owner: user, bucket: root, path: p});
Unfortunately, in every instance where I indent the code in this manner, it's for a specific reason. I wanted it on separate lines; the fact that it happens to fit on a single line doesn't matter at all. Prettier overruled my carefully indented code.

I often format code in a specific way for regularity: Every chunk in a block should have the same format, because each chunk is an instance of the same pattern. For example, I might have something like:

  const RULES = [
    {
      type: "boost",
      fn: node => increaseImportance(node),
      weight: 0.5,
      dependencies: ["priority", "rotation"]
    },
    {
      type: "eliminate",
      fn: node => nil
    },
    // ... lots of rules ...
  ]
Prettier's output generates inconsistency:

    const RULES = [
      {
        type: "boost",
        fn: node => increaseImportance(node),
        weight: 0.5,
        dependencies: ["priority", "rotation"]
      },
      {type: "eliminate", fn: node => nil}
    ];
To quote the immortal Trump: No way!

Maybe one could use some advanced heuristics to find an optimal balance between width vs. indentation vs. compactness; for example, in the above array, a clever formatter could see that it's an array of object literals, which means that it should prioritize regularity over compactness. If it's an array of something simple (like numbers, but not numbers with trailing comments), it can go compact. Maybe.

I don't use Prettier at the moment, but I know the strictness would drive me nuts. I predict that Prettier is going to cause a lot of frustration and heated discussion as a result of the one-size-fits-all approach. I don't think a canonical form for everything even makes sense; people need regularity (and no surprises), but not at the cost of readability.


> I often format code in a specific way for regularity:

I am trying to write my own formatter at the moment, and this is exactly the property I want, also. I'm working in Haskell, and one example is case statements. There are two ways I'd like to format them:

case foo of A -> ... B -> ...

or

case foo of A -> ...

  B ->
    ...
The first is when every case fits on one line, but the second is when at least one case needs to span multiple lines. I don't want this:

case foo of A -> ...

  B -> ...
I haven't yet come up with a good way to do that heuristically and efficiently, but maybe just looking at how the original code is formatted would be enough! Thanks for providing some food for thought :)


It breaks the moment you type in with()




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: