Prettier 2.3. In which assignments are consistent, short keys non-breaking, and Handlebars official
This release focuses on fixing long-standing issues in the JavaScript printer. Be warned that, unfortunately, reformatting a project with the new version might result in quite a big diff. If you don’t use ignoreRevsFile
to hide such wholesale changes from git blame
, it might be about time.
A remarkable milestone is the long-awaited release of the Ember / Handlebars formatter. It’s supposed to be the last formatter included directly in the core library. In the future, for sustainability, languages should be added only by plugins.
We are grateful to our financial contributors: Salesforce, Indeed, Frontend Masters, Airbnb, Shogun Labs, Skyscanner, Konstantin Pschera, and many others who help us keep going. If you enjoy Prettier and would like to support our work, head to our OpenCollective. Please consider also supporting the projects Prettier depends on, such as typescript-eslint, remark, and Babel.
Most of the changes in this release are thanks to the hard work of Fisker Cheung, Georgii Dolzhykov, and Sosuke Suzuki, along with many other contributors.
And just a reminder, when Prettier is installed or updated, it’s strongly recommended to specify the exact version in package.json
: "2.3.0"
, not "^2.3.0"
.
Highlights
JavaScript
#10222, #10643, #10672 by @thorn0; #10158 by @sosukesuzuki)
Format assignments more consistently (Previously, Prettier had a lot of trouble with figuring out how to break lines in assignments. E.g., long right-hand sides often stayed unbroken. Not anymore.
// Prettier 2.2
aParticularlyLongAndObnoxiousNameForIllustrativePurposes = anotherVeryLongNameForIllustrativePurposes;
aParticularlyLongAndObnoxiousNameForIllustrativePurposes = "a very long string for illustrative purposes"
.length;
someReallyLongThingStoredInAMapWithAReallyBigName[
pageletID
] = _someVariableThatWeAreCheckingForFalsiness
? Date.now() - _someVariableThatWeAreCheckingForFalsiness
: 0;
class x {
private readonly rawConfigFromFile$: BehaviorSubject<any> = new BehaviorSubject(
notRead
);
}
// Prettier 2.3
aParticularlyLongAndObnoxiousNameForIllustrativePurposes =
anotherVeryLongNameForIllustrativePurposes;
aParticularlyLongAndObnoxiousNameForIllustrativePurposes =
"a very long string for illustrative purposes".length;
someReallyLongThingStoredInAMapWithAReallyBigName[pageletID] =
_someVariableThatWeAreCheckingForFalsiness
? Date.now() - _someVariableThatWeAreCheckingForFalsiness
: 0;
class x {
private readonly rawConfigFromFile$: BehaviorSubject<any> =
new BehaviorSubject(notRead);
}
#10335 by @thorn0)
Prevent wrapping object properties with short keys (Line breaks after short property names in object literals often look unnatural. Even when such a line break yields a line length benefit of 1 or 2 characters, it rarely looks justified. Prettier 2.3 avoids line breaks after property names shorter than tabWidth + 3
– for example, 5 characters in the default configuration, or 7 characters with tabWidth: 4
. This heuristic may be revised in future versions.
// Prettier 2.2
const importantLink = {
url:
"https://prettier.io/docs/en/rationale.html#what-prettier-is-concerned-about",
gitHubUrl:
"https://github.com/prettier/prettier/blob/main/docs/rationale.md#what-prettier-is-concerned-about",
};
// Prettier 2.3
const importantLink = {
url: "https://prettier.io/docs/en/rationale.html#what-prettier-is-concerned-about",
gitHubUrl:
"https://github.com/prettier/prettier/blob/main/docs/rationale.md#what-prettier-is-concerned-about",
};
Ember / Handlebars
#10290 by @dcyriller & @thorn0)
Move Handlebars support out of alpha to release (This started in 2017. Handlebars support has been in Prettier for a while, but it wasn’t released officially as it wasn’t really ready. Its status went from “alpha” to “experimental” to “beta” and then, if you checked older release notes, you can see that after “beta” somehow it was “alpha” again…
Well, anyway, it finally is happening: Prettier can now officially format HTML templates with Handlebars! 🎉
It uses Glimmer, Ember’s Handlebars parser, so it should be compliant with the HTML spec thanks to the Ember team.
The --html-whitespace-sensitivity
option is supported and defaults to strict
, which means that Prettier will always respect the presence or absence of whitespace around tags and consider it unsafe to add whitespace where there were none and vice versa as this can affect how the document is rendered in the browser. The css
value is not yet supported (treated as strict
for now).
The feature is called “Ember / Handlebars” and not just “Handlebars” because Glimmer doesn’t support some syntax and use cases of Handlebars. This is mostly due to the fact that Handlebars, being a template engine (a preprocessor), doesn’t care about the underlying syntax of the content it processes whereas Glimmer parses two syntaxes – HTML and Handlebars – at the same time and combines the result into a single tree, which Prettier can print. This means Prettier won’t format Handlebars files that can’t be parsed into such a tree, either because the underlying syntax isn’t HTML or because template directives and tags overlap in a way that can’t be represented in a tree (e.g., {{#if foo}}<div>{{/if}
). Even with these restrictions, the formatter still seems to be useful enough to non-Ember Handlebars users. As for the syntax unsupported by Ember, there is a good chance to see support for it in future versions of Prettier as Glimmer uses a full-fledged Handlebars parser under the hood.
Files with the extensions .hbs
and .handlebars
are recognized as Handlebars by default. For other extensions, the --parser
option with the value glimmer
has to be specified – for example, using command line or, better yet, configuration overrides.
See the formatter in action on the playground!
Formatting Improvements
JavaScript
#9992, #10543 by @sosukesuzuki & @thorn0)
Refine formatting of curried arrow functions (// Prettier 2.2
const currying = (argument1) => (argument2) => (argument3) => (argument4) => (
argument5
) => (argument6) => foo;
// Prettier 2.3
const currying =
(argument1) =>
(argument2) =>
(argument3) =>
(argument4) =>
(argument5) =>
(argument6) =>
foo;
#10238 by @sosukesuzuki)
Improve formatting for React Hooks calls (// Prettier 2.2
const { firstName, lastName } = useMemo(() => parseFullName(fullName), [
fullName,
]);
// Prettier 2.3
const { firstName, lastName } = useMemo(
() => parseFullName(fullName),
[fullName]
);
#10085 by @sosukesuzuki)
Improve visual separation between header and body in classes with multiline headers (// Prettier 2.2
class loooooooooooooooooooong
extends looooooooooooooooooong
implements loooooooooooooooooooong {
property: string;
}
// Prettier 2.3
class loooooooooooooooooooong
extends looooooooooooooooooong
implements loooooooooooooooooooong
{
property: string;
}
#10106, #10160 by @thorn0)
Concise formatting of number-only arrays (While in general, Prettier avoids this kind of formatting because it's not diff-friendly, in this special case we decided that the benefits outweigh the risks.
If at least one element has a trailing single-line (// ...
) comment on the same line, the concise formatting isn't applied. On the other hand, single-line comments placed on separate lines don't have such an effect and – as well as empty lines – can be used for logical grouping.
// Input
const lazyCatererNumbers = [1, 2, 4, 7, 11, 16, 22, 29, 37, 46,
// n > 10
56, 67, 79, 92, 106, 121, 137, 154, 172, 191, 211, 232, 254, 277, 301, 326, 352, 379, 407, 436, 466,
497, 529, 562, 596, 631, 667, 704, 742, 781,
// n > 40
821, 862, 904, 947, 991, 1036, 1082, 1129, 1177, 1226, 1276, 1327, 1379];
// Prettier 2.2
const lazyCatererNumbers = [
1,
2,
4,
7,
11,
16,
22,
29,
37,
// ... ✂ 46 lines ✂ ...
1379,
];
// Prettier 2.3
const lazyCatererNumbers = [
1, 2, 4, 7, 11, 16, 22, 29, 37, 46,
// n > 10
56, 67, 79, 92, 106, 121, 137, 154, 172, 191, 211, 232, 254, 277, 301, 326,
352, 379, 407, 436, 466, 497, 529, 562, 596, 631, 667, 704, 742, 781,
// n > 40
821, 862, 904, 947, 991, 1036, 1082, 1129, 1177, 1226, 1276, 1327, 1379,
];
#10342 by @thorn0)
Improve formatting for nested await expressions in heads of member and call expressions (Even though Prettier tries to be helpful here, please don't write code like this. Have mercy upon your teammates and use intermediate variables.
// Input
const getAccountCount = async () =>
(await
(await (
await focusOnSection(BOOKMARKED_PROJECTS_SECTION_NAME)
).findItem("My bookmarks")).getChildren()
).length
// Prettier 2.2
const getAccountCount = async () =>
(
await (
await (await focusOnSection(BOOKMARKED_PROJECTS_SECTION_NAME)).findItem(
"My bookmarks"
)
).getChildren()
).length;
// Prettier 2.3
const getAccountCount = async () =>
(
await (
await (
await focusOnSection(BOOKMARKED_PROJECTS_SECTION_NAME)
).findItem("My bookmarks")
).getChildren()
).length;
do
expressions in function calls (#10693 by @sosukesuzuki)
Improve formatting for “do
expressions” are a stage 1 ECMAScript proposal.
// Prettier 2.2
expect(
do {
var bar = "foo";
bar;
}
).toBe("foo");
// Prettier 2.3
expect(do {
var bar = "foo";
bar;
}).toBe("foo");
#10187, #10266 by @sosukesuzuki)
Consistent indentation for conditional operators (// Prettier 2.2
const dotNotationMemberExpression = (callNode.parent?.type ===
AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
).TSESTree.BinaryExpression;
const computedMemberExpression = (callNode.parent?.type ===
AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent)[TSESTree.BinaryExpression];
const callExpressionCallee = (callNode.parent?.type ===
AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent)(TSESTree.BinaryExpression);
const typeScriptAsExpression = (callNode.parent?.type ===
AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent) as TSESTree.BinaryExpression;
// Prettier 2.3
const dotNotationMemberExpression = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
).TSESTree.BinaryExpression;
const computedMemberExpression = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
)[TSESTree.BinaryExpression];
const callExpressionCallee = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
)(TSESTree.BinaryExpression);
const typeScriptAsExpression = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
) as TSESTree.BinaryExpression;
HTML
class
attribute (#7865 by @thorn0)
Prefix-based multiline formatting for the Formatting of HTML class names will now keep classes on one line until the line length limit is reached; at that point, consecutive classes with the same prefix will be grouped together on each line. For layout frameworks such as Bootstrap and Tailwind CSS, which add many classes to an element, this is important for readability and maintainability vs. the previous behavior (keeping all classes on one line) or e.g. putting each class on its own line.
<!-- Prettier 2.2 -->
<div
class="SomeComponent__heading-row d-flex flex-column flex-lg-row justify-content-start justify-content-lg-between align-items-start align-items-lg-center"
></div>
<!-- Prettier 2.3 -->
<div
class="
SomeComponent__heading-row
d-flex
flex-column flex-lg-row
justify-content-start justify-content-lg-between
align-items-start align-items-lg-center
"
></div>
Other Changes
JavaScript
#9672 by @fisker)
Fix unstable multiple comments on the same line (// Input
a;
/*1*//*2*/
/*3*/
b;
// Prettier 2.2
a; /*2*/
/*1*/ /*3*/
b;
// Prettier 2.2 (second format)
a; /*2*/ /*3*/
/*1*/ b;
// Prettier 2.3
a;
/*1*/ /*2*/
/*3*/
b;
rangeStart
(#9704 by @fisker)
Don’t format nodes that end just before Previously, when range formatting was performed, such nodes were considered part of the range, now they're excluded. This affects other languages that the range formatting feature supports, not only JavaScript.
// Input
foo = 1.0000;bar = 1.0000;baz=1.0000;
^^^^^^^^^^^^^ Range
// Prettier 2.2
foo = 1.0;
bar = 1.0;baz=1.0000;
// Prettier 2.3
foo = 1.0000;bar = 1.0;baz=1.0000;
#9711 by @fisker)
Fix comments inside JSX end tag (// Input
<a><// comment
/a>;
// Prettier 2.2
<a></// comment
a>;
// Prettier 2.3
<a></
// comment
a
>;
#9743 by @fisker)
Fix inconsistent language comment detection (An /* HTML */
comment should directly precede a template literal for the latter to be recognized as HTML-in-JS. Previously, the comment was erroneously recognized is some other locations.
// Input
foo /* HTML */ = `<DIV>
</DIV>`;
// Prettier 2.2 (--parser=babel)
foo /* HTML */ = `<div></div>`;
// Prettier 2.2 (--parser=meriyah)
foo /* HTML */ = `<DIV>
</DIV>`;
// Prettier 2.3 (All JavaScript parsers)
foo /* HTML */ = `<DIV>
</DIV>`;
#9850 by @fisker)
Fix extra semicolon added to ignored directives (// Input
// prettier-ignore
'use strict';
function foo() {
// prettier-ignore
"use strict";;
}
// Prettier 2.2
// prettier-ignore
'use strict';;
function foo() {
// prettier-ignore
"use strict";;
}
// Prettier 2.3
// prettier-ignore
'use strict';
function foo() {
// prettier-ignore
"use strict";
}
#9866 by @fisker)
Fix unstable JSX formatting with U+3000 (// Input
<p>
<span /> {this.props.data.title} <span />
//----- ^ U+3000 --------------- ^ U+3000
</p>
// Prettier 2.2
<p>
<span />
{this.props.data.title} <span />
//----- ^ U+3000 --------------- ^ U+3000
</p>;
// Prettier 2.2 (second format)
<p>
<span /> {this.props.data.title} <span />
//----- ^ U+3000 --------------- ^ U+3000
</p>;
// Prettier 2.3
<p>
<span /> {this.props.data.title} <span />
//----- ^ U+3000 --------------- ^ U+3000
</p>;
a(b => c => function (){})
(#10278 by @thorn0)
Fix error thrown on expressions like Regression from v2.2.0.
// Input
a(b => c => function (){})
// Prettier 2.2
TypeError: Cannot read property 'length' of undefined
// Prettier 2.3
a((b) => (c) => function () {});
#10296 by @fisker)
Improve formatting for inline decorators (// Input
class Foo {
@decorator([]) bar() {}
@decorator(
[]
) baz() {}
}
// Prettier 2.2
class Foo {
@decorator([]) bar() {}
@decorator([])
baz() {}
}
// Prettier 2.3
class Foo {
@decorator([]) bar() {}
@decorator([]) baz() {}
}
#10334 by @thorn0)
Fix ASI protection for private fields (// Input
class C {
#field = 'value';
["method"]() {}
}
// Prettier 2.2 (with --no-semi)
class C {
#field = "value"
["method"]() {}
}
// Prettier 2.3 (with --no-semi)
class C {
#field = "value";
["method"]() {}
}
#10417 by @sosukesuzuki, @thorn0)
Support Module Blocks proposal (Support formatting for Module Blocks Stage 2 proposal.
// Input
module { export let foo = "foo"; };
// Prettier 2.2
SyntaxError: Unexpected token, expected ";"
// Prettier 2.3
module {
export let foo = "foo";
};
yield
in a pipeline (#10446 by @fisker)
Fix missing parentheses for // Input
function* f() {
return x |> (yield #);
}
// Prettier 2.2
function* f() {
return x |> yield #;
}
// Prettier 2.3
function* f() {
return x |> (yield #);
}
#10495 by @fisker, #9787 by @sosukesuzuki, #10065, #10322 by @thorn0)
Make Babel’s error recovery more selective (Previously, because of the error recovery, the Babel parser was too permissive, which lead to all kinds of AST shapes that Prettier couldn’t print. Prettier 2.3 lets Babel recover only from a few harmless types of errors – for example, multiple const
declarations with the same name. Anything else is reported as syntax errors.
// Input
foo("a", , "b");
// Prettier 2.2
TypeError: Cannot read property 'type' of null
// Prettier 2.3
[error] stdin: SyntaxError: Argument expression expected. (1:10)
[error] > 1 | foo("a", , "b");
[error] | ^
// Input
const \u{20} = 1
// Prettier 2.2
const = 1;
// Prettier 2.3
[error] stdin: SyntaxError: Invalid Unicode escape (1:7)
[error] > 1 | const \u{20} = 1
[error] | ^ ^
#10517 by @thorn0)
Avoid last-argument hugging for number-only arrays (Another special case for number-only arrays.
// Input
instantiate(game, [
transform([-0.7, 0.5, 0]),
render_colored_diffuse(game.MaterialDiffuse, game.Meshes["monkey_flat"], [1, 1, 0.3, 1]),
]);
// Prettier 2.2
instantiate(game, [
transform([-0.7, 0.5, 0]),
render_colored_diffuse(game.MaterialDiffuse, game.Meshes["monkey_flat"], [
1,
1,
0.3,
1,
]),
]);
// Prettier 2.3
instantiate(game, [
transform([-0.7, 0.5, 0]),
render_colored_diffuse(
game.MaterialDiffuse,
game.Meshes["monkey_flat"],
[1, 1, 0.3, 1]
),
]);
define
(#10528 by @thorn0)
Improve detection of AMD Prettier special-cases AMD define
calls to avoid unexpected line breaks in them. We now only format define
calls if they are at the top level of a function or program and are passed arguments in the way AMD expects.
// Prettier 2.2
const someVariable = define("some string literal", anotherVariable, yetAnotherVariable);
// Prettier 2.3
const someVariable = define(
"some string literal",
anotherVariable,
yetAnotherVariable
);
prettier-ignore
comments (#10666 by @fisker)
Fix duplicated // Input
foo = a.
// prettier-ignore
b;
// Prettier 2.2
foo =
// prettier-ignore
a.
// prettier-ignore
b;
// Prettier 2.3
foo = a.
// prettier-ignore
b;
mapDoc
(#10695 by @thorn0)
Process conditional groups in In particular, this fixes broken substitutions in HTML-in-JS.
// Input
export default function include_photoswipe(
gallery_selector = ".my-gallery"
): string {
return /* HTML */ `
<script>
window.addEventListener("load", () =>
initPhotoSwipeFromDOM("${gallery_selector}")
);
</script>`;
}
// Prettier 2.2
export default function include_photoswipe(
gallery_selector = ".my-gallery"
): string {
return /* HTML */ ` <script>
window.addEventListener("load", () =>
initPhotoSwipeFromDOM("PRETTIER_HTML_PLACEHOLDER_0_13_IN_JS")
);
</script>`;
}
// Prettier 2.3
export default function include_photoswipe(
gallery_selector = ".my-gallery"
): string {
return /* HTML */ ` <script>
window.addEventListener("load", () =>
initPhotoSwipeFromDOM("${gallery_selector}")
);
</script>`;
}
#10712 by @thorn0)
Avoid arg expansion that leads to broken code (// Input
glimseGlyphsHazardNoopsTieTie(({ a, b = () => {
console.log();
}}) => averredBathersBoxroomBuggyNurl());
// Prettier 2.2
glimseGlyphsHazardNoopsTieTie(({ a, b = () => {
console.log();
} }) => averredBathersBoxroomBuggyNurl());
// Prettier 2.3
glimseGlyphsHazardNoopsTieTie(
({
a,
b = () => {
console.log();
},
}) => averredBathersBoxroomBuggyNurl()
);
async
in for-of
(#10781 by @fisker)
Fix missing parentheses around See https://github.com/tc39/ecma262/issues/2034
// Input
for ((async) of []);
// Prettier 2.2
for (async of []);
// Prettier 2.2 (second format)
SyntaxError: Unexpected token, expected "=>" (1:15)
> 1 | for (async of []);
// Prettier 2.3
for ((async) of []);
async do
expressions proposal (#10813 by @sosukesuzuki)
Support See https://github.com/tc39/proposal-async-do-expressions
// Input
const x = async do {
await requestAPI().json();
};
// Prettier 2.2
SyntaxError: Unexpected token, expected ";" (1:17)
// Prettier 2.3
const x = async do {
await requestAPI().json();
};
TypeScript
MethodDefinition
(#9872 by @fisker)
Fix missing comments in typescript
parser only, babel-ts
doesn't have this issue.
// Input
class Foo {
bar() /* bat */;
}
// Prettier 2.2
Error: Comment "bat" was not printed. Please report this error!
// Prettier 2.3
class Foo {
bar /* bat */();
}
#10024 by @sosukesuzuki, #10357 by @thorn0)
Fix unnecessary line breaks in method type declaration parameters (// Input
type Foo = {
method(foo: "foo"): `
`
};
// Prettier 2.2
type Foo = {
method(
foo: "foo"
): `
`;
};
// Prettier 2.3
type Foo = {
method(foo: "foo"): `
`;
};
#10109 by @sosukesuzuki, #10353 by @thorn0)
Print trailing commas in type parameters (TypeScript has been supporting trailing commas in type parameters since TypeScript 2.7 released in January 2018. Prettier 2.3 prints them if the trailingComma
option is set to all
. Keep this option at the more conservative default value es5
if compatibility with TypeScript 2.7 or older is needed. Note that TypeScript still doesn't support trailing commas in type arguments (type parameter instantiations).
// Input
export class BaseSingleLevelProfileTargeting<
T extends ValidSingleLevelProfileNode,
> {
// ...
}
// Prettier 2.2
export class BaseSingleLevelProfileTargeting<
T extends ValidSingleLevelProfileNode
> {
// ...
}
// Prettier 2.3 with --trailling-comma=all
export class BaseSingleLevelProfileTargeting<
T extends ValidSingleLevelProfileNode,
> {
// ...
}
#10316 by @thorn0)
Allow hugging arguments that are non-concise arrow functions with return type annotations (// Prettier 2.2
users.map(
(user: User): User => {
return user;
}
);
// Prettier 2.3
users.map((user: User): User => {
return user;
})
#10337 by @thorn0)
Correct parentheses for non-null assertions (Necessary parentheses sometimes weren't printed in expressions containing non-null assertions. This has been fixed.
// Input
const myFunction2 = (key: string): number =>
({
a: 42,
b: 42,
}[key]!)
// Prettier 2.2 (invalid syntax)
const myFunction2 = (key: string): number =>
{
a: 42,
b: 42,
}[key]!;
// Prettier 2.3
const myFunction2 = (key: string): number =>
({
a: 42,
b: 42,
}[key]!);
#10341 by @thorn0)
Indent type assertions in heads of member and call expressions (// Input
const accountCount = (findItemInSection(BOOKMARKED_PROJECTS_SECTION_NAME,
"My bookmarks") as TreeItem).getChildren().length;
// Prettier 2.2
const accountCount = (findItemInSection(
BOOKMARKED_PROJECTS_SECTION_NAME,
"My bookmarks"
) as TreeItem).getChildren().length;
// Prettier 2.3
const accountCount = (
findItemInSection(
BOOKMARKED_PROJECTS_SECTION_NAME,
"My bookmarks"
) as TreeItem
).getChildren().length;
#10390 by @sosukesuzuki)
Support intrinsic keyword (// Input
type Uppercase<S extends string> = intrinsic;
// Prettier 2.2
Error: unknown type: "TSIntrinsicKeyword"
// Prettier 2.3
type Uppercase<S extends string> = intrinsic;
#10418, #10466, #10546, #10589 by @sosukesuzuki)
Support TypeScript 4.2 (abstract
Construct Signatures
// Input
type T = abstract new () => void;
// Prettier 2.2
SyntaxError: Unexpected token, expected ";" (1:19)
// Prettier 2.3
type T = abstract new () => void;
Type imports in import require declaration
// Input
import type A = require("A");
// Prettier 2.2
SyntaxError: Only ECMAScript imports may use 'import type'.
// Prettier 2.3
import type A = require("A");
#10457 by @thorn0)
Fix misplaced comments in unions and intersections (// Input
type Foo = "1" | "2" /* two */ | "3";
// Prettier 2.2
type Foo = "1" | "2" | /* two */ "3";
// Prettier 2.3
type Foo = "1" | "2" /* two */ | "3";
#10702 by @thorn0)
Don’t print parens around nested type assertions (// Input
foo as unknown as Bar
// Prettier 2.2
(foo as unknown) as Bar;
// Prettier 2.3
foo as unknown as Bar;
babel-ts
(#10811 by @sosukesuzuki)
Support TypeScript 4.3 via override
modifiers in class elements
class Foo extends {
override method() {}
}
static index signatures ([key: KeyType]: ValueType
) in classes
class Foo {
static [key: string]: Bar;
}
get
/ set
in type declarations
interface Foo {
set foo(value);
get foo(): string;
}
Flow
declare export * from …
(#9767 by @fisker)
Fix missing semicolon in // Input
declare export * from "ES6_ExportAllFrom_Source2";
// Prettier 2.2
declare export * from "ES6_ExportAllFrom_Source2"
// Prettier 2.3
declare export * from "ES6_ExportAllFrom_Source2";
this
type annotation in functions via babel-flow
(#10397 by @sosukesuzuki)
Support this
type annotation is supported since Babel 7.13.
// Input
var foo: (this: boolean) => void;
// Prettier 2.2
SyntaxError: Unexpected token, expected ")" (1:15)
// Prettier 2.3
var foo: (this: boolean) => void;
#10505 by @thorn0)
Fix range formatting issues (Prettier had trouble formatting some ranges in function declarations. SyntaxError
was thrown. Prettier 2.3 formats these cases without errors. Examples of problematic ranges are shown below:
declare export function graphql<Props, Variables, Component: React$ComponentType<Props>>
// ^^^^^ range 1
(query: GQLDocument, config?: Config<Props, QueryConfigOptions<Variables>>):
(Component: Component) => React$ComponentType<$Diff<React$ElementConfig<Component>, {
data: Object|void,
// ^^^^ range 2
mutate: Function|void
}>>
#10594 by @gkz)
Support for Flow's indexed access type (// Input
const x: Obj['foo'] = 1;
// Prettier 2.2
// Error: unsupported node type "IndexedAccessType"
// Prettier 2.3
const x: Obj["foo"] = 1;
#10788 by @gkz)
Support for Flow's optional indexed access type (// Input
type T = Obj?.['foo'];
// Prettier 2.2
// Error: unsupported node type "OptionalIndexedAccessType"
// Prettier 2.3
type T = Obj?.['foo'];
JSON
--quote-props=preserve
(#10323 by @thorn0)
Don't use smart quotes for JSON5 with With the quoteProps
option set to preserve
and singleQuotes
to false
(default), double quotes are always used for printing strings, including situations like "bla\"bla"
. This effectively allows using --parser json5
for "JSON with comments and trailing commas".
// Input
{
"char": "\"",
}
// Prettier 2.2
{
"char": '"',
}
// Prettier 2.3
{
"char": "\"",
}
#10346, #10443, #10456, #10434 by @fisker)
Stricter JSON parser (Prettier internally uses a JavaScript expression parser to parse JSON. That's why the json
and json5
parsers used to be very forgiving and allowed all kinds of JavaScript expressions. Now they are much stricter, although some simple non-standard syntax is still allowed (e.g., JSON6 is supported, except for multiple minus signs: ----123
).
// Input
[1, 2, 1 + 2]
// Prettier 2.2
[1, 2, 1 + 2]
// Prettier 2.3
SyntaxError: BinaryExpression is not allowed in JSON. (1:8)
> 1 | [1, 2, 1 + 2]
| ^
#10433 by @fisker)
Improve error message (// Input
{key: "foo" + "bar"}
// Prettier 2.2 (--parser=json-stringify)
SyntaxError: BinaryExpression is not allowed in JSON. (1:7)
> 1 | {key: "foo" + "bar"}
| ^
// Prettier 2.3
SyntaxError: BinaryExpression is not allowed in JSON. (1:7)
> 1 | {key: "foo" + "bar"}
| ^^^^^^^^^^^^^
#10497 by @fisker)
Fix syntax error on JSON range formatting (// Input
[{ a: 1.0000}, {"b": 2.0000 }]
// ^^^^^^^^^^^ range
// Prettier 2.2
SyntaxError: Unexpected token (1:4)
> 1 | "b": 2.0000
| ^
// Prettier 2.3
[{ a: 1.0000}, { "b": 2.0 }]
CSS
-custom-url()
calls (#9966 by @vjeux)
Fix absolute path in custom CSS The CSS parser is parsing this as ["division", "absolute/path"]
instead of a single "/absolute/path"
token unless you are in a url()
call. Because we put space after division, it results in an incorrect path. The fix was to avoid printing a space if a division is the first token of a call, which hopefully should be safe.
/* Input */
-custom-url(/absolute/path)
/* Prettier 2.2 */
-custom-url(/ absolute/path)
/* Prettier 2.3 */
-custom-url(/absolute/path)
@keyframes
params from being parsed as a Less variable (#10773 by @tmazeika)
Exclude /* Input */
@keyframes :global(spin) {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* Prettier 2.2 */
@keyframes: global(spin){
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
};
/* Prettier 2.3 */
@keyframes :global(spin) {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
SCSS
#9710 by @fisker)
Fix broken comment inside parens (// Input
.simplification {
foo: (
calc() // not a comment anymore
);
}
// Prettier 2.2
.simplification {
foo: (calc() // not a comment anymore);
}
// Prettier 2.3
.simplification {
foo: (
calc() // not a comment anymore
);
}
#10005 by @fisker)
Fix maps with keys that are lists or maps (// Input
$map: (
('my list'): 'hello world',
);
// Prettier 2.2
TypeError: Cannot read property 'length' of undefined
// Prettier 2.3
$map: (
("my list"): "hello world",
);
Ember / Handlebars
#9082 by @kangax, @fisker)
Preserve tilde comments ({{!-- Input --}}
{{~! Comment }}
{{! Comment ~}}
{{~! Comment ~}}
{{!-- Prettier 2.2 --}}
{{! Comment }}
{{! Comment }}
{{! Comment }}
{{!-- Prettier 2.3 --}}
{{~! Comment }}
{{! Comment ~}}
{{~! Comment ~}}
#9885 by @dcyriller)
Add a whitespace sensitive mode (--html-whitespace-sensitivity strict
{{!-- Input --}}
<span>123 {{mustache}}</span>
<span>
123 {{mustache}}</span>
<span>123 {{mustache}}
</span>
<span>
123 {{mustache}}
</span>
{{!-- Prettier 2.2 --}}
<span>
123 {{mustache}}
</span>
<span>
123 {{mustache}}
</span>
<span>
123 {{mustache}}
</span>
<span>
123 {{mustache}}
</span>
{{!-- Prettier 2.3 --}}
<span>123 {{mustache}}</span>
<span>
123
{{mustache}}</span>
<span>123
{{mustache}}
</span>
<span>
123
{{mustache}}
</span>
blockParam
on its own line (#9978 by @dcyriller)
Print final {{!-- Input --}}
<MyComponent @prop={{true}} @prop2={{true}} @prop3={{true}} @prop4={{true}} as |thing|></MyComponent>
{{#block param hashKey=hashValue hashKey=hashValue hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{!-- Prettier 2.2 --}}
<MyComponent
@prop={{true}}
@prop2={{true}}
@prop3={{true}}
@prop4={{true}} as |thing|
/>
{{#block
param
hashKey=hashValue
hashKey=hashValue
hashKey=hashValue as |blockParam|
}}
Hello
{{/block}}
{{!-- Prettier 2.3 --}}
<MyComponent
@prop={{true}}
@prop2={{true}}
@prop3={{true}}
@prop4={{true}}
as |thing|
/>
{{#block
param
hashKey=hashValue
hashKey=hashValue
hashKey=hashValue
as |blockParam|
}}
Hello
{{/block}}
#10145 by @thorn0)
Fix formatting of attributes (- fix escaping of
{{
in attributes and text - fix the choice between
'
and"
for attributes with interpolations - fix the bug with
[object Object]
printed in theclass
attribute - implement simple formatting for the
class
attribute, like Prettier formatted it in HTML before v2.3.0
{{!-- Input --}}
<div class="
foo"></div>
<div bar='"{{expr}}"'></div>
<div baz="\{{ non-expression }}"></div>
{{!-- Prettier 2.2 --}}
<div class="[object Object],foo"></div>
<div bar=""{{expr}}""></div>
<div baz="{{ non-expression }}"></div>
{{!-- Prettier 2.3 --}}
<div class="foo"></div>
<div bar='"{{expr}}"'></div>
<div baz="\{{ non-expression }}"></div>
#10179 by @dcyriller)
Split text content on multiple lines ({{!-- Input --}}
<div>
A long enough string to trigger a line break that would prevent wrapping more and more.
</div>
{{!-- Prettier 2.2 --}}
<div>
A long enough string to trigger a line break that would prevent wrapping more and more.
</div>
{{!-- Prettier 2.3 --}}
<div>
A long enough string to trigger a line break that would prevent wrapping more
and more.
</div>
#10550 by @rwjblue and @thorn0)
Preserve numeric character references ({{! Input }}
<span class="stampFont" style="font-family: 'stampfont'"></span>
{{! Prettier 2.2 }}
<span class="stampFont" style="font-family: 'stampfont'"></span>
{{! Prettier 2.3 }}
<span class="stampFont" style="font-family: 'stampfont'"></span>
#10586 by @dcyriller)
Do not break opening mustache and path ({{!-- Input --}}
<GlimmerComponent
@errors={{or this.aVeryLongProperty (and this.aProperty (v-get bike "number" "message"))}}
data-test-beneficiary-account-number
/>
<GlimmerComponent
@progress={{aPropertyEngdingAfterEightiethColumnToHighlightAWeirdClosingParenIssue}}
/>
{{!-- Prettier 2.2 --}}
<GlimmerComponent
@errors={{
or
this.aVeryLongProperty
(and this.aProperty (v-get bike "number" "message"))
}}
data-test-beneficiary-account-number
/>
<GlimmerComponent
@progress={{
aPropertyEngdingAfterEightiethColumnToHighlightAWeirdClosingParenIssue
}}
/>
{{!-- Prettier 2.3 --}}
<GlimmerComponent
@errors={{or
this.aLongProperty
(and this.aProperty (v-get bike "number" "message"))
}}
data-test-beneficiary-account-number
/>
<GlimmerComponent
@progress={{aPropertyEngdingAfterEightiethColumnToHighlightAWeirdClosingParenIssue}}
/>
GraphQL
#10689 by @patriscus)
Fix missing space after keyword in anonymous operations (# Input
query ($unnamed: String) {
id
}
# Prettier 2.2
query($unnamed: String) {
id
}
# Prettier 2.3
query ($unnamed: String) {
id
}
Markdown
#9736 by @fisker)
Fix extra newline at the end of JavaScript fenced code block with string literal (<!-- Input -->
Markdown
```js
"· "
```
<!-- Prettier 2.2 -->
Markdown
```js
"· ";
```
<!-- Prettier 2.3 -->
Markdown
```js
"· ";
```
#9791 by @fisker)
Fix empty front matter formatting (<!-- Input -->
---
---
# Title
a|b|c|
|:--|:-:|--:|
|d|e|f|
---
text
<!-- Prettier 2.2 -->
---
---
# Title
a|b|c|
|:--|:-:|--:|
|d|e|f|
---
text
<!-- Prettier 2.3 -->
---
---
# Title
| a | b | c |
| :-- | :-: | --: |
| d | e | f |
---
text
#9878 by @michaelbeaumont)
Support YAML document end marker in front matter (Add the ability to delimit the end of front matter with ...
.
<!-- Input -->
---
title: Hello
slug: home
...
Markdown
<!-- Prettier 2.2 -->
---
title: Hello
slug: home
...
Markdown
<!-- Prettier 2.3 -->
---
title: Hello
slug: home
...
Markdown
YAML
#10516 by @eemeli & @thorn0)
Fix SyntaxError incorrectly thrown on anchors followed by empty lines (Prettier couldn't parse this valid YAML. Thanks to Eemeli Aro for fixing this bug in the underlying parser.
# Input
key1: &default
subkey1: value1
key2:
<<: *default
# Prettier 2.2
SyntaxError: Nested mappings are not allowed in compact mappings (1:7)
# Prettier 2.3
key1: &default
subkey1: value1
key2:
<<: *default
API
.prettierrc
as YAML when formatting it (#8105 by @fisker)
Treat The .prettierrc
file can be written in either JSON or YAML. Previously, when Prettier formatted it, the parser was inferred to be json
, which lead to a SyntaxError
thrown if the content was YAML. Now it’s treated as a YAML file. However, if it's JSON, it will be formatted as JSON (not as JSON-like YAML).
concat
(#9733 by @fisker, @thorn0)
Use arrays instead of To simplify the code of AST printers, the data structure for the concatenation command has been changed from { type: 'concat', parts: Doc[] }
to Doc[]
. The old format is deprecated, but for compatibility, the doc printer still supports it, and doc.builders.concat
(as well as some other builder functions) will keep using it until the next major version of Prettier.
If you're a plugin author, this change should only concern you if your plugin introspects or modifies composed docs. If it happens to be the case, please make your plugin compatible with future versions of Prettier by tweaking the introspecting code to support the new format. There also is an off-chance where this change can break things, namely if a plugin calls another plugin to print an embedded language and then introspects the returned doc. There seems to be no reason for plugins to do that though.
To replace concat(…)
calls in your plugins, you can try auto-fix by this ESLint rule prettier-doc/no-concat
.
// Prettier 2.2
myDoc = group(concat(["foo", line, "bar"]));
// Prettier 2.3
myDoc = group(["foo", line, "bar"]);
lineSuffixBoundary
IR command (#10122 by @thorn0)
Fix There was a bug in the implementation of the lineSuffixBoundary
command that significantly limited its usefulness: the printer algorithm didn't correctly consider it a potential line break. Now that the bug has been fixed, we urge plugin authors to give this command another try and see if it can help them simplify printing of trailing comments.
// Input
group([
"let foo = [",
indent([
softline,
[lineSuffixBoundary, "item1,"],
line,
[lineSuffixBoundary, "item2,", lineSuffix(" // comment")],
line,
[lineSuffixBoundary, "item3"],
]),
softline,
"];",
])
// Prettier 2.2
let foo = [item1, item2, // comment
item3];
// Prettier 2.3
let foo = [
item1,
item2, // comment
item3
];
indentIfBreak
IR command (#10221 by @thorn)
Add indentIfBreak(doc, { groupId })
is an optimized version of ifBreak(indent(doc), doc, { groupId })
.
print
callback (#10557 by @fisker)
Simplified The third argument of the print
method of plugin printers (the print
callback) has been updated. Its first argument can be a string or an array of strings now.
To print the current node, call print
without arguments:
function print(path, options, print) {
const parts = [];
path.each((childPath) => {
- parts.push(print(childPath));
+ parts.push(print());
}, "children");
return parts;
}
To print a property of the current node, use "property"
or ["property"]
:
function print(path, options, print) {
- return path.call(print, "property");
+ return print("property");
}
To print a sub-property of the current node, use ["property1", "property2"]
:
function print(path, options, print) {
// print `node.child.child`
- return path.call(print, "child", "child");
+ return print(["child", "child"]);
}
See also an example in the docs.
CLI
#10058 by @daronmcintosh)
Add CLI option to prevent errors on unmatched pattern (The Prettier CLI will no longer display an error when no files match the glob pattern passed as input.
# Prettier 2.2
$ npx prettier --check "prettier/docs/*.yaml"
Checking formatting...
[error] No files matching the pattern were found: "prettier/docs/*.yaml".
All matched files use Prettier code style!
# Prettier 2.3
$ npx prettier --check --no-error-on-unmatched-pattern "prettier/docs/*.yaml"
Checking formatting...
All matched files use Prettier code style!
#10124 by @thorn0)
Add CLI flag for debugging comment attachment issues (A new --debug-print-comments
CLI flag and corresponding functionality for the Playground.
--debug-print-doc
(#10169, #10177 by @thorn0)
Round-trippable The idea is to make the output of --debug-print-doc
closer to actual code for generating docs (Prettier's intermediate representation). Ideally, it should be possible for it to work without modification after copy-pasting into a JS file. That ideal hasn't probably been reached by this PR, but it's pretty close. This is going to make --debug-print-doc
and the corresponding part of the Playground a bit more useful.
--find-config-path
can't find config file (#10208 by @fisker)
Print an error message when # Prettier 2.2
$ prettier --find-config-path /prettier.js
# Silently failed
# Prettier 2.3
$ prettier --find-config-path /prettier.js
[error] Can not find configure file for "/prettier.js"
#10217 by @fisker)
Clear printed long filenames of formatting file (# Prettier 2.2
$ prettier tests/flow-repo/config_module_system_node_resolve_dirname --check
Checking formatting...
tests\flow-repo\config_module_system_node_resolve_dirname\custom_resolve_dir\tes
tests\flow-repo\config_module_system_node_resolve_dirname\custom_resolve_dir\tes
tests\flow-repo\config_module_system_node_resolve_dirname\subdir\custom_resolve_
All matched files use Prettier code style!
# Prettier 2.3
$ prettier tests/flow-repo/config_module_system_node_resolve_dirname --check
Checking formatting...
All matched files use Prettier code style!
constructor
(#10256 by @ashlkv)
Don't skip file paths containing special words like Directories whose names happened to coincide with the properties of Object.prototype
were ignored by Prettier CLI because of a classic bug (introduced in Prettier 2.0.0) with object properties not being checked for being own.
# Prettier 2.2
$ prettier "js/constructor/*.js" --write
[error] No matching files. Patterns: js/constructor/*.js
# Prettier 2.3
$ prettier "js/constructor/*.js" --write
js/constructor/test.js 42ms