Releasing Prettier 1.0
This post was written by vjeux and edited by jlongster, and originally published here
We officially announced
prettier over two months ago as a way to
solve the problem of wasting time formatting your code. It started as an
experiment but it clearly resonated with a lot of people, amassing ~7000 GitHub
stars and over 100,000 monthly npm downloads
in just two months.
In case you don't know, prettier is a JavaScript formatter that works by compiling your code to an AST, and then pretty-printing the AST. Like browsers wrap text, prettier will also wrap code according to a given line length. The result is good-looking code that is completely consistent no matter who wrote it. This solves the problem of programmers spending a lot of time manually moving around code in their editor and arguing about styles (see gif).
It wasn't entirely clear from the beginning if we could make it always generate valid and good-looking code, but the good news is that this is no longer an open question. Many projects (React, Jest, immutable-js, Haul, and many more) and companies (Oculus, Cloudflare) adopted prettier to format their JavaScript codebase and realized the wins of automated formatting (see this tweet for more!). Some of them ran prettier on their entire codebase (tens of thousands of lines of code).
During the past three weeks we went through all the open issues and tried to make as many decisions regarding how code is being formatted as possible and fixed most of the bugs we knew about. It's not perfect and we'll keep iterating on it but it's a good time to release a 1.0!
This does not mean we won't change the format anymore, but changes will be minimal. For example, we are looking at adjusting ternaries but there will be very few changes of that nature from here on out, and when there are we will make a new major version.
What it means is that prettier is safe to use for production. We've fixed a ton of bugs and made sure the output is stable and semantically correct. Prettier is ready to be used from a few files in your side project to converting your entire codebase. It's up to you to figure out where you are in the journey of letting go of a particular way to formatting your code.
Let's take a look what 1.0 brings to prettier!
Thank you to all of the following contributors who have contributed to this release, and thank you to Ian Storm Taylor for the logo!
Adam Stankiewicz, Alex Rattray, Alex Stachowiak, Amjad Masad, Andrey Okonetchnikov, António Nuno Monteiro, Artem Sapegin, Benjamin Tan, Bill Mill, Brandon Mills, Brian Holt, Brian Ng, Charles Pick, ChristianHersevoort, Christopher Chedeau, Christopher Moeller, Damien Lebrun, Dan Harper, Dara Hak, David Ascher, David Hong, Davy Duperron, Eric Clemmons, Erick Romero, Esben Petersen, Filipe Fortes, Gregor Adams, Hampus,hlsson, Hawken Rives, Henry Zhu, Ilya Panasenko, James Henry, James Long, Jamie Webb, Jan Kassens, Jed Watson, Jeffrey Horn, Jeremy Morrell, Joe Fiorini, Jon LaBelle, Joseph Savona, Karl Horky, Karl O'Keeffe, Kent C. Dodds, Kevin Gibbons, Kim Joar Bekkelund, Lucas Bento, Lucas Duailibe, MarekMatejkaKCL, Mateusz Zatorski, Michael Ficarra, Michał Pierzchała, Mikael Brevik, Nathan Friedly, Patrick Camacho, Paul,arduner, Rafael Hengles, Raghuvir Kasturi, Rasmus Eneman, Riley Tomasek, Rogelio Guzman, Royce Townsend, Sean Grove, Shigeaki Okazaki, Simen,ekkhus, Simon Lydell, Sorin Muntean, Spencer Dixon, Thai Pangsakulyanont, Tom McKearney, Travis Jefferson, Umidbek Karimov, Vincent Voyer, Vu Tran, Walter Breakell, Yatharth Khatri, azu, daleroy, kalmanb, skratchdot
Options
Prettier is an opinionated code formatter. At the beginning of the project, we thought that it meant having no configuration like gofmt or refmt. But, as we moved forward, we realized that this was going to hurt the adoption of prettier and people that would have benefited from it wouldn't use it because it wasn't printing code the way they want.
Instead, our recent interpretation of being an opinionated code formatter is to provide options regarding basic aspects of the syntax that are of the nature of "if it doesn't do X, no matter how good it is, I'm never going to use it". For example, I (@vjeux) am never going to use standard because it doesn't use semi-colons. This is absolutely not a rational way to think about it, but that's the way many people behave and why we have "style wars".
We are still not going to introduce options for every type of syntax, but only for more impactful things. We've identified two major options that fall in that category: tabs vs spaces and semi vs no-semi. So we're adding them in prettier!
#1129)
--no-semi (Thanks to rattrayalex who did a lot of work to make this happen!
// Before
console.log();
[1, 2, 3].map(x => x + 1);
// After
console.log()
;[1, 2, 3].map(x => x + 1)
#1026)
--use-tabs (Thanks to rhengles for implementing this and explaining the reasons behind it!
// Before
if (1) {
··console.log(); // Two spaces
}
// After
if (1) {
» console.log(); // One Tab!
}
Formatting
The rest of this post document all of the smaller changes that we round up for 1.0. We won't usually post a changelog as a post, but we thought that it shows how stable prettier is getting and gives a good sense for the kinds of issues we are fixing these days.
#927)
Add newline for bracket-less arrow functions that return calls (Probably the most reported issue from the current version of prettier: we were not adding a newline after arrow functions inside of calls. Now, we do. On a side note, it's pretty exciting that the issues are now about small things like this and no longer "this looks completely broken" kind of issues.
// Before
const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter));
// After
const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter)
);
#871 & #870)
Add softline to assignment and parens around return for binary expressions (Big chains of logical expressions look weird when the first line is on the same line as the variable declaration or return. Instead, it's better to move it on the next line and add parenthesis in return to make it valid.
// Before
const computedDescriptionLines = (showConfirm &&
descriptionLinesConfirming) ||
(focused && !loading && descriptionLinesFocused) ||
descriptionLines;
// After
const computedDescriptionLines =
(showConfirm && descriptionLinesConfirming) ||
(focused && !loading && descriptionLinesFocused) ||
descriptionLines;
// Before
return !filePath.includes(coverageDirectory) &&
!filePath.endsWith(`.${SNAPSHOT_EXTENSION}`);
// After
return (
!filePath.includes(coverageDirectory) &&
!filePath.endsWith(`.${SNAPSHOT_EXTENSION}`)
);
#947)
Group first arg for inline functions (The first big custom pattern we've added in prettier is printing functions as
last arguments inline. A less common one is to do the same thing for the first
argument. It's very uncommon for libraries written nowadays to use that style
but some core JavaScript functions like setTimeout
are doing it so it makes
sense to support it in prettier.
// Before
setTimeout(
function() {
thing();
},
500
);
// After
setTimeout(function() {
thing();
}, 500);
#1144)
Improve jsx output for style components (Template literals are very tricky to format because whitespaces are significant. With prettier, we don't want to change the semantic of your program by reformatting it so we're leaving them as is. The current way we handle them is not optimal and we have ideas on how to fix them in a general way but in the meantime, we can do some targeted fixes like this one for JSX.
// Before
<style>
{
`
color: red;
`
}
</style>
// After
<style>{`
color: red;
`}</style>
#1029)
Add support for same line dot-notation in decorators (MobX 3 now uses member expressions that should be written inline, the previous heuristic was too restrictive. It is now fixed.
class X {
// Before
@action.bound
setPrice(price) {
this.price = price;
}
// After
@action.bound setPrice(price) {
this.price = price;
}
}
#1022)
Hug on single object destructuring function (React stateless functional components are all the rage right now and it makes sense to hug the destructuring of the first argument to take less space.
// Before
export default function StatelessFunctionalComponent(
{
searchFilters,
title,
items
}
) {
return <div />;
}
// After
export default function StatelessFunctionalComponent({
searchFilters,
title,
items,
}) {
return <div />
}
#1066)
Add support for currying (Redux is heavily promoting writing functions in a curried form where each argument is a nested function. Instead of indenting them, we're now putting them inline.
// Before
const mw = store =>
next =>
action => {
return next(action)
};
// After
const mw = store => next => action => {
return next(action)
};
#1056)
Respect escaping for JSX strings (For strings we've tried a bunch of different solutions regarding to escaping: escape nothing, escape everything, escape a whitelisted set... But we couldn't find a heuristic that would make all the use cases reasonable. So we decided to leave the strings escaped the way they were inputted. We're now doing this for JSX strings.
// Before
<a href="https://foo.bar?q1=foo&q2=bar" />
// After
<a href="https://foo.bar?q1=foo&q2=bar" />
Parenthesis
A blessing and curse of printing from the AST is that we have to reprint all the parenthesis of the program. Our stance used to be to print only the minimum number of parenthesis that were needed for the program to be valid and execute the same way. Now, we're willing to add parenthesis that are not strictly needed but help people understand the code.
#1153)
Add parenthesis for binary operators (// Before
var sizeIndex = index - 1 >>> level & MASK;
// After
var sizeIndex = ((index - 1) >>> level) & MASK;
#1182)
Add parenthesis for no-confusing-arrow rule (// Before
var x = a => 1 ? 2 : 3;
// After
var x = a => (1 ? 2 : 3);
#967)
Remove parens from chained assignments (// Before
this.size = (this._origin = (this._capacity = 0));
// After
this.size = this._origin = this._capacity = 0;
Flow
In the early days of prettier we've heavily focused on getting the core JavaScript language well supported. Now that it is in a good shape, we have more time to print Flow constructs in a polished way.
#1041)
Add ability for flow generics to break (// Before
type _ReactElement<DefaultProps, Props, Config: $Diff<Props, DefaultProps>, C: $React.Component<DefaultProps, Props, any>> = $React.Element<Config>;
// After
type _ReactElement<
DefaultProps,
Props,
Config: $Diff<Props, DefaultProps>,
C: $React.Component<DefaultProps, Props, any>
> = $React.Element<Config>;
#1018 & #1155)
Improve printing of flow intersections (// Before
type Props =
& {
focusedChildren?: React.Children,
onClick: () => void,
overlayChildren?: React.Children,
}
& FooterProps;
// After
type Props = {
focusedChildren?: React.Children,
onClick: () => void,
overlayChildren?: React.Children,
} & FooterProps;
#1003)
Add support for breaks in TupleTypeAnnotation (// Before
export type FileMetaData = [/* id */ string, /* mtime */ number, /* visited */
| 0
| 1, /* dependencies */ Array<string>];
// After
export type FileMetaData = [
/* id */ string,
/* mtime */ number,
/* visited */ 0|1,
/* dependencies */ Array<string>,
];
#972)
No parenthesis for Flow shorthand with one arg (// Before
type T = { method: (a) => void };
// After
type T = { method: a => void };
#1060)
Ability for interface to break (// Before
export interface Environment1 extends GenericEnvironment<SomeType, AnotherType, YetAnotherType> {
m(): void
}
// After
export interface Environment1
extends GenericEnvironment<SomeType, AnotherType, YetAnotherType> {
m(): void
}
#1132)
Fix printing of Flow type number literals (// Before
type Hex = {n: 1};
// After
type Hex = {n: 0x01};
Places to break
There are a few cases where prettier still outputs code that's > 80 columns where there's a possible way to write it without. The solution is to carefully find places where it's possible to break and make sure that it doesn't negatively impact common cases.
#1142 & #1188)
Allow to break after = for strings and member expressions (// Before
elements[0].innerHTML = '<div></div><div></div><div></div><div></div><div></div><div></div>';
var testExampleOrOrderOfGetterAndSetterReordered = obj.exampleOfOrderOfGetterAndSetterReordered;
// After
elements[0].innerHTML =
'<div></div><div></div><div></div><div></div><div></div><div></div>';
var testExampleOrOrderOfGetterAndSetterReordered =
obj.exampleOfOrderOfGetterAndSetterReordered;
#1036)
Add ability to break for top member expression (// Before
expect(
findDOMNode(component.instance()).getElementsByClassName(styles.inner)[0].style.paddingRight
).toBe("1000px");
// After
expect(
findDOMNode(component.instance()).getElementsByClassName(styles.inner)[0]
.style.paddingRight
).toBe("1000px");
Miscellaneous
#1023)
Inline class expressions for bracket-less arrow functions (Super small thing, we're now inlining classes returned from arrow functions.
// Before
jest.mock(
'../SearchSource',
() =>
class {
findMatchingTests(pattern) {
return {paths: []};
}
},
);
// After
jest.mock(
'../SearchSource',
() => class {
findMatchingTests(pattern) {
return {paths: []};
}
},
);
#981)
Do not respect newlines for object destructuring pattern (For objects, we have special handling where if there is a \n anywhere inside in the original source, we keep it expanded. It was only intended for objects but because the same code is shared with destructuring, it accidentally applied there too. This has been fixed.
// Before
const Component2 = ({
props
}) => <Text>Test</Text>;
// After
const Component1 = ({ props }) => <Text>Test</Text>;
Language Support
In order for you to be able to use prettier, it must be able to print the code you are writing. We are aiming to be able to print everything that our underlying parsers (babylon and flow) are able to parse.
#1073)
Add support for ForOfStatement with await flag (async function f() {
for await (const line of readLines(\\"/path/to/file\\")) {
(line: void); // error: string ~> void
}
}
#1064)
Add support for flow type spread (type TypeB = { ...TypeA };
Correctness
The absolute first requirement of prettier is to print valid code that has the same behavior as the original source. You shouldn't be afraid of running prettier on your entire codebase. In order to ensure this, we are doing a lot of things:
- Have an automated way to ensure that prettier is outputting valid code.
- Use it against large codebases such as Facebook, all of CDNjs and the many people in the community using it.
- Run extensive fuzzing using eslump.
- Making every report of such issue as high-pri and fixed right away.
If you are unlucky enough to see one, please report it so that we can fix it and
you can use // prettier-ignore
to get you unblocked.
#1170)
Properly handle \r\n in JSXText (// Before
<div>
{" "}
Text{" "}
</div>;
// After
<div>
Text
</div>;
#1169)
Fix parenthesis when call inside of new (// Before
new factory()();
// After
new (factory())();
#1152)
Add parens around arrow function return type (// Before
const f = (): string => string => {};
// After
const f = (): (string => string) => {};
#1114)
Fix printing of exact object flow type annotations (// Before
type Props = {};
// After
type Props = {||};
#1178)
Print return dangling comment (// Before
function f() {
return;
}
// After
function f() {
return /* a */;
}
#1131)
Print comment after object key (// Before
let a = {
"a": () => 1
};
// After
let a = {
"a" /* comment */: () => 1
};
#1133)
Fix leading comment inside returned SequenceExpression (// Before
function sequenceExpressionInside() {
return;
// Reason for a
a, b;
}
// After
function sequenceExpressionInside() {
return ( // Reason for a
a, b
);
}
#1002)
Fix single optional arrow param printing (// Before
a = b? => c;
// After
a = (b?) => c;
#998)
Fix default exports (// Before
export { foo, bar } from "./baz";
// After
export foo, {bar} from './baz';
#985)
Print line/mixed comments on new lines inside JSXEmptyExpression (// Before
<div>
{
// single line comment}
</div>;
// After
<div>
{
// single line comment
}
</div>;
Comments
prettier works by printing the AST, this makes handling comments tricky as they can be placed anywhere between tokens. The way prettier works is to attach a comment to an ast node, either before or after it. We have a generic heuristic to find the location that works most of the time but there's a large amount of edge cases that we need to handle manually.
prettier is never going to be able to properly print comments in all the crazy places where you can put them, but we expect to be able to do a reasonable job at them in most cases. If you see anything weird related to comments, please create issues and we can figure something out.
#674)
Add breakParent support for willBreak (// Before
runtimeAgent.getProperties(objectId, false, false, false, ( // ownProperties // accessorPropertiesOnly // generatePreview
error,
properties,
internalProperties
) => {
return 1;
});
// After
runtimeAgent.getProperties(
objectId,
false, // ownProperties
false, // accessorPropertiesOnly
false, // generatePreview
(error, properties, internalProperties) => {
return 1
},
);
#936)
Fix additional empty line switch case comment (// Before
switch (foo) {
case "bar":
doThing()
// no default
}
// After
switch (foo) {
case "bar":
doThing()
// no default
}
#1030)
Fix import declaration comments (// Before
import {
FN1,
FN2,
// FN3,
FN4
} from // FN4,
// FN5
"./module";
// After
import {
FN1,
FN2,
// FN3,
FN4,
// FN4,
// FN5
} from './module';
#1176 & #905)
Fix last argument comment for function (// Before
function f(
a: number
)// some comment here
: number {
return a + 1;
}
// After
function f(
a: number
// some comment here
): number {
return a + 1;
}
#1040)
Unrestrict flow union comments check (// Before
type UploadState<E, EM, D> =
// The upload hasn't begun yet
| A
| // The upload timed out
B
| // Failed somewhere on the line
C;
// After
type UploadState<E, EM, D> =
// The upload hasn't begun yet
| A
// The upload timed out
| B
// Failed somewhere on the line
| C;
#1045)
Fix comments inside of ObjectPattern (// Before
export default (
{
foo,
bar
// comment
// comment 2
}: {
foo?: Object,
bar?: Object
}
) => {};
// After
export default (
{
foo,
bar
}: {
// comment
foo?: Object,
// comment 2
bar?: Object,
},
) => {}
#1038)
Fix comment sorting location (// Before
let {
// comment
a = b
} = c;
// After
let {
a = b // comment
} = c;
#1043)
Fix comment location for binary expressions (// Before
a = Math.random() * (yRange * (1 - minVerticalFraction)) +
minVerticalFraction * yRange// Comment
-
offset;
// After
a =
// Comment
Math.random() * (yRange * (1 - minVerticalFraction)) +
minVerticalFraction * yRange -
offset;
#1074)
Fix class method comments (// Before
class x {
focus() // do nothing
{
// do nothing
}
}
// After
class x {
focus() {
// do nothing
// do nothing
}
}
#1125)
Support "// prettier-ignore" in comment blocks (// Before
module.exports = {
// Some comment
// prettier-ignore
m: matrix(1, 0, 0, 0, 1, 0, 0, 0, 1)
};
// After
module.exports = {
// Some comment
// prettier-ignore
m: matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
)
};
#1130)
Stabilize VariableDeclarator comments (// Before
let obj = [ // Comment
'val'
];
// After
let obj = [
// Comment
'val'
];
#1127)
Fix comment detection before comma (// Before
const foo = {
a: 'a' /* comment for this line */,
/* Section B */
b: 'b',
};
// After
const foo = {
a: 'a' /* comment for this line */,
/* Section B */
b: 'b',
};
#1042)
Fix last comment of an if test (// Before
if (isIdentifierStart(code) || code === 92) {
/* '\' */
}
// After
if (isIdentifierStart(code) || code === 92 /* '\' */) {}