Skip to main content

Prettier 3.10: New Flow syntax, new JavaScript syntax, and new plugin powered by Yuku

· 31 min read

We're excited to announce Prettier 3.10!

This release adds support for new Flow syntax and the latest JavaScript proposals, and introduces a new official plugin — @prettier/plugin-yuku — powered by the high-performance Yuku toolchain, with dedicated JavaScript and TypeScript parsers.

It also includes a wide range of fixes and improvements across JavaScript, TypeScript, Markdown, HTML, and more.

If you find Prettier valuable, please consider sponsoring us on OpenCollective or supporting the upstream projects we rely on. Your contributions help us keep improving the tool for everyone.

Thank you for your continued support! ❤️

Highlights​

JavaScript​

Added a new official plugin @prettier/plugin-yuku (#19628, #19629 by @fisker)​

@prettier/plugin-yuku is powered by Yuku (A high-performance JavaScript/TypeScript compiler toolchain written in Zig).

This plugin includes two new parsers: yuku (JavaScript syntax) and yuku-ts (TypeScript syntax).

To use this plugin:

  1. Install the plugin:

    yarn add --dev prettier @prettier/plugin-yuku
  2. Add it to your .prettierrc:

    plugins:
    - "@prettier/plugin-yuku"

Due to package size limitations, this plugin is not bundled with the main prettier package and must be installed separately.

For more information, check the package homepage.

Big thanks to @arshad-yaseen for his excellent work.

Flow​

Support new Flow syntax (#20136 by @gkz)​

Prettier now supports Flow syntax added in flow-parser 0.332.0, including template literal and constructor types, mapped-type modifiers and key remapping, abstract and override class members, inline import types, CommonJS import/export bindings, and unique symbol types.

// Input
abstract class Example { abstract value:string; }
type Constructor = new <T>(value:T)=>Container<T>;
type Getters<T> = {-readonly [K in keyof T as `get${K}`]-?: T[K]};
type Imported = import("package-name").Exported;
type Unique = unique symbol;
import Value=require("value-module");
export = Value;

// Prettier 3.9
// Not supported

// Prettier 3.10
abstract class Example {
abstract value: string;
}
type Constructor = new <T>(value: T) => Container<T>;
type Getters<T> = { -readonly [K in keyof T as `get${K}`]-?: T[K] };
type Imported = import("package-name").Exported;
type Unique = unique symbol;
import Value = require("value-module");
export = Value;

Support parameter properties, in out variance, non-null assertions, export as namespace, unlabeled optional tuple members, declared class private fields, and declared class mixin extends in Flow (#20407 by @gkz)​

The flow parser can now format constructor parameter properties, in out variance annotations, non-null assertions, export as namespace declarations, unlabeled optional tuple members, private fields in declared classes, and declared classes that extend a mixin call. Previously, Prettier threw an error on all of these.

// Input
class A { constructor(public x: string, private readonly y: number = 1) {} }
type Invariant<in out T> = {get: () => T, set: (value: T) => void};
const value = maybeNull! .value;
export as namespace Library;
type T = [number ?, (string | boolean) ?];
declare class C { #x; }
declare class D extends mixin( Base ) {}

// Prettier 3.9
Error: Missing visitor keys for 'ParameterProperty', etc.

// Prettier 3.10
class A {
constructor(
public x: string,
private readonly y: number = 1,
) {}
}
type Invariant<in out T> = { get: () => T, set: (value: T) => void };
const value = maybeNull!.value;
export as namespace Library;
type T = [number?, (string | boolean)?];
declare class C {
#x;
}
declare class D extends mixin(Base) {}

Other Changes​

JavaScript​

Fix unstable comment between a tagged template's tag and quasi (#19345 by @sarathfrancis90)​

// Input
foo /* comment */
`x`;

// Prettier 3.9 (First format)
foo /* comment */`x`;

// Prettier 3.9 (Second format)
foo /* comment */ `x`;

// Prettier 3.10
foo /* comment */
`x`;

Fix misplaced type cast comments (#19652 by @fisker)​

// Input
foo = a ? b : (/** @type {Foo} */ (foo) ?? bar);

// Prettier 3.9
foo = a ? b : /** @type {Foo} */ ((foo) ?? bar);

// Prettier 3.10
foo = a ? b : (/** @type {Foo} */ (foo) ?? bar);

Fix extra indentation for inline array/object in logical chains (#19792 by @santhiprakash)​

// Input
const x = $abilities ?? ["foo", "bar", "bar"] ?? ["foo", "bar", "bar", "veryVeryVeryVeryVeryVeryVeryVeryVeryLongKey"];

// Prettier 3.9
const x = $abilities ?? ["foo", "bar", "bar"] ?? [
"foo",
"bar",
"bar",
"veryVeryVeryVeryVeryVeryVeryVeryVeryLongKey",
];

// Prettier 3.10
const x = $abilities ?? ["foo", "bar", "bar"] ?? [
"foo",
"bar",
"bar",
"veryVeryVeryVeryVeryVeryVeryVeryVeryLongKey",
];

Fix range formatting expanding to a non-source-element node (#19880 by @Kjubikstronk)​

// Input (range covers the two indented lines)
beforeEach(() => {
// c
a();
});

// Prettier 3.9
beforeEach(() => {
// c
a();
};);

// Prettier 3.10
beforeEach(() => {
// c
a();
});

Fix unstable trailing comment on a nested parenthesized assignment (#19893 by @Kjubikstronk)​

// Input
assigned = (a = c /* comment */);

// Prettier 3.9 (First format)
assigned = a = c /* comment */;

// Prettier 3.9 (Second format)
assigned = a = c; /* comment */

// Prettier 3.10
assigned = a = c; /* comment */

Fix unstable comment before the first element of a sequence expression (#19894 by @Kjubikstronk)​

// Input
((/* c */ a), b);

// Prettier 3.9 (First format)
(/* c */ a, b);

// Prettier 3.9 (Second format)
/* c */ (a, b);

// Prettier 3.10
/* c */ (a, b);

Keep necessary parentheses in yield expression (#19913 by @giaBaoJS)​

// Input
function* f() {
yield (/*
*/ a);
}

// Prettier 3.9
function* f() {
yield /*
*/ a;
}

// Prettier 3.10
function* f() {
yield (
/*
*/ a
);
}

Fix unstable trailing comment on a parenthesized arrow chain (#19930 by @Kjubikstronk)​

The parentheses around the inner arrow are redundant, so they are dropped. That leaves the comment next to the statement's semicolon, where the next format attaches it to the statement instead of to the arrow it was written against.

// Input
x2 = (a) => ((b) => {
c();
} /* ! */ // K
)

// Prettier 3.9 (First format)
x2 = (a) => (b) => {
c();
} /* ! */; // K

// Prettier 3.9 (Second format)
x2 = (a) => (b) => {
c();
}; /* ! */ // K

// Prettier 3.10
x2 = (a) => (b) => {
c();
}; /* ! */ // K

Avoid unnecessary semicolons before private members named #in or #instanceof (#20019 by @cellison-figma)​

// Input
class Graph {
#nodes = {};
#in = {};
}

// Prettier 3.9 (--parser acorn --no-semi)
class Graph {
#nodes = {};
#in = {}
}

// Prettier 3.10 (--parser acorn --no-semi)
class Graph {
#nodes = {}
#in = {}
}

Non-Babel parsers (flow, acorn, etc.) are affected.

Keep necessary parentheses around throw expression (#20028 by @lazerg)​

// Input
(throw error);

// Prettier 3.9
throw error;

// Prettier 3.10
(throw error);

Preserve comment order in test calls (#20043 by @lllleolin-max)​

// Input
test("x", () => {
run();
}, // first
// second
// third
60000);

// Prettier 3.9
test("x", () => {
run();
}, // second // first
// third
60000);

// Prettier 3.10
test(
"x",
() => {
run();
}, // first
// second
// third
60000,
);

Fix unstable JSX single-character text before a self-closing element (#20104 by @kovsu)​

// Input
<div>
<b>x</b>
: <Foo attribute="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" />
</div>;

// Prettier 3.9 (First format)
<div>
<b>x</b>
:{" "}
<Foo attribute="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" />
</div>;

// Prettier 3.9 (Second format)
<div>
<b>x</b>:{" "}
<Foo attribute="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" />
</div>;

// Prettier 3.10
<div>
<b>x</b>:{" "}
<Foo attribute="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" />
</div>;

Prevent comment after head of member chain move around (#20125 by @fisker)​

// Input
x // comment
.foo()

// Prettier 3.9
x.foo(); // comment

// Prettier 3.10
x // comment
.foo();

Fix range formatting inside a for statement head (#20163 by @ump45nose)​

// Input
for (const f of filters) {}

// Prettier 3.9 (--range-start 5 --range-end 12)
for (const f; of filters) {}

// Prettier 3.10
for (const f of filters) {}

Keep comments before exported decorators stable (#20180 by @charan-rathore)​

// Input
export /* comment */ @decorator class A {}

// Prettier 3.9 (First format)
export /* comment */
@decorator
class A {}

// Prettier 3.9 (Second format)
export /* comment */

@decorator
class A {}

// Prettier 3.10
export /* comment */ @decorator class A {}

Keep multiple comments before => on the same line (#20198 by @costajohnt)​

// Input
const f = (a) /* b */ /* c */ => a;

// Prettier 3.9
const f = (a) /* b */
/* c */ => a;

// Prettier 3.10
const f = (a) /* b */ /* c */ => a;

Fix prettier-ignored for initializer (#20204 by @fisker)​

// Input
for (/* prettier-ignore */ let i = 0; i < 1; i++) {}

// Prettier 3.9
for (/* prettier-ignore */ let i = 0;; i < 1; i++) {}

// Prettier 3.10
for (/* prettier-ignore */ let i = 0; i < 1; i++) {}

Fix duplicated comments on JSX super classes (#20211 by @fisker)​

// Input
class A extends (/* c */ <div />) {}
class B extends (<div /> /* c */) {}

// Prettier 3.9
class A extends /* c */ (/* c */ <div />) {}
class B extends (<div /> /* c */) /* c */ {}

// Prettier 3.10
class A extends (/* c */ <div />) {}
class B extends (<div /> /* c */) {}

Break after < when JSX opening tag name has a leading comment (#20265 by @doishy-doish)​

// Input
const a = < // comment
div />;

// Prettier 3.9
const a = (
<// comment
div
/>
);

// Prettier 3.10
const a = (
<
// comment
div
/>
);

Fix prettier-ignored node dropping ASI-protective semicolons (#20280 by @intertermux-code, @fisker)​

// Input
log()
// prettier-ignore
;(new Widget).run()

// Prettier 3.9 (--no-semi)
log()
// prettier-ignore
(new Widget).run()

// Prettier 3.10
log()
// prettier-ignore
;(new Widget).run()

Improve performance on files with many comments (#20322 by @JoviDeCroock)​

Formatting JavaScript, TypeScript, and Flow files with many comments now avoids repeatedly copying the full source while masking comments. Formatting output is unchanged.

Align BinaryishExpression in YieldExpression with ReturnStatement (#20353 by @fisker)​

// Input
async function* f() {
return (
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ??
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
);

yield (
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ??
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
);
}

// Prettier 3.9
async function* f() {
return (
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ??
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
);

yield aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ??
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
}

// Prettier 3.10
async function* f() {
return (
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ??
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
);

yield (
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ??
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
);
}

Avoid stack overflows when formatting long binary expressions (#20409 by @splincode)​

Prettier now formats long chains of binary operators without overflowing the JavaScript call stack. Previously, formatting thousands of operands could fail with a RangeError.

// Prettier 3.9
await prettier.format("a + ".repeat(2000) + "a", { parser: "babel" });
// RangeError: Maximum call stack size exceeded

// Prettier 3.10
await prettier.format("a + ".repeat(2000) + "a", { parser: "babel" });
// 'a +\n' +
// ' a +\n' +
// ' a +\n'... 2003 more characters

Add support for "Structs", "Import Sync", and "Deferred re-exports" proposal (#20427, #20430, #20430, #20438 by @fisker)​

Prettier 3.10 added support for new ES proposals.

  • Structs proposal (Only non-shared structs)

    struct Point {
    x = 0;
    y = 0;

    get length() {
    return Math.hypot(this.x, this.y);
    }
    }
  • Import Sync proposal

    const { readFile } = import.sync("node:fs");
  • Deferred re-exports proposal

    export defer { parse } from "./parser.js";
    import { parse, format } as myLib from "my-library";
    export { parse } as parser from "my-library";

Please keep in mind our policy on non-standardized syntax before using these proposed syntax features with Prettier.

Fix sequence expression indentation in yield expression (#20428 by @kovsu)​

// Input
function* f() {
yield (aLongIdentifierName, aLongIdentifierName, aLongIdentifierName, aLongIdentifierName);
}

// Prettier 3.9
function* f() {
yield (aLongIdentifierName,
aLongIdentifierName,
aLongIdentifierName,
aLongIdentifierName);
}

// Prettier 3.10
function* f() {
yield (
aLongIdentifierName,
aLongIdentifierName,
aLongIdentifierName,
aLongIdentifierName
);
}

Fix extra semicolon after prettier-ignored class property (#20429 by @kovsu)​

// Input
class C {
// prettier-ignore
property = 1;
[computed] = 2;
}

// Prettier 3.9 (--no-semi)
class C {
// prettier-ignore
property = 1;;
[computed] = 2
}

// Prettier 3.10 (--no-semi)
class C {
// prettier-ignore
property = 1;
[computed] = 2
}

Fix unstable comments before of and in in for heads (#20434 by @lazerg)​

// Input
for (const foo //comment
of bar) {
console.log(foo)
}

// Prettier 3.9 (First format)
for (const foo of bar) { //comment
console.log(foo);
}

// Prettier 3.9 (Second format)
for (const foo of bar) {
//comment
console.log(foo);
}

// Prettier 3.10
for (const foo //comment
of bar) {
console.log(foo);
}

Preserve blank lines between dangling comments (#20473 by @adityajai119)​

// Input
function foo() {
// comment 1

// comment 2
}

// Prettier 3.9
function foo() {
// comment 1
// comment 2
}

// Prettier 3.10
function foo() {
// comment 1

// comment 2
}

The same fix applies to TypeScript, Flow and GraphQL.

TypeScript​

Keep comment on the same line in property signature (#19688 by @kovsu)​

// Input
interface A { property: /** Comment */
B }

// Prettier 3.9
interface A {
property: /** Comment */
B;
}

// Prettier 3.10
interface A {
property: /** Comment */ B;
}

Fix comment before the first heritage clause (#19897 by @fisker)​

// Input
class Foo
extends Bar
implements
// Comment for Ifoo
IFoo,
IBar {
// ...
}

// Prettier 3.9
class Foo
extends Bar
// Comment for Ifoo
implements IFoo, IBar {
// ...
}

// Prettier 3.10
class Foo
extends Bar
implements
// Comment for Ifoo
IFoo,
IBar {
// ...
}

Fix unstable comment before the first heritage clause (#19897 by @fisker)​

// Input
class MyClass extends MySuperClass implements /* foo */ ISomething, /* bar */ IOtherThing, /* baz */ IThirdThing {
}

// Prettier 3.9 (First format)
class MyClass
extends MySuperClass
implements
/* foo */ ISomething,
/* bar */ IOtherThing,
/* baz */ IThirdThing {}

// Prettier 3.9 (Second format)
class MyClass
extends MySuperClass
/* foo */
implements ISomething, /* bar */ IOtherThing, /* baz */ IThirdThing {}

// Prettier 3.10
class MyClass
extends MySuperClass
implements
/* foo */ ISomething,
/* bar */ IOtherThing,
/* baz */ IThirdThing {}

Keep comment after destructuring pattern out of the pattern (#19905 by @lazerg)​

// Input
const {
foo
} /* comment */
: Foo = bar;

// Prettier 3.9
const { foo /* comment */ }: Foo = bar;

// Prettier 3.10
const { foo } /* comment */ : Foo = bar;

Fix misaligned jsdoc comment before a satisfies or as type (#19939 by @Kjubikstronk)​

// Input
const foo = {
bar: 1,
} as
/**
* comment line 1
* comment line 2
*/
Foo;

// Prettier 3.9
const foo = {
bar: 1,
} as /**
* comment line 1
* comment line 2
*/
Foo;

// Prettier 3.10
const foo = {
bar: 1,
} as
/**
* comment line 1
* comment line 2
*/
Foo;

Keep an own-line comment before a satisfies or as type on its own line (#19939 by @Kjubikstronk)​

// Input
const foo = {
bar: 1,
} as
// Comment
Record<string, number>;

// Prettier 3.9 (First format)
const foo = {
bar: 1,
} as // Comment
Record<string, number>;

// Prettier 3.9 (Second format)
const foo = {
bar: 1,
} as Record<string, number>; // Comment

// Prettier 3.10
const foo = {
bar: 1,
} as
// Comment
Record<string, number>;

Add missing parentheses for TSInstantiationExpression (#20027 by @fisker)​

// Input
new (a?.b<h>)();
(a?.b<c>)`foo`;

// Prettier 3.9
new a?.b<h>();
a?.b<c>`foo`;

// Prettier 3.10
new (a?.b<h>)();
(a?.b<c>)`foo`;

Fix missing parentheses around TSInstantiationExpression before ! and in export default (#20177 by @nwang783)​

// Input
const a = (make<T>)!;
export default (class {})<string>;

// Prettier 3.9
const a = make<T>!;
export default class {}<string>;

// Prettier 3.10
const a = (make<T>)!;
export default (class {}<string>);

Fix range formatting inside type parameter lists (#20309 by @sansynx)​

// Input
type Box<T> = { value:
T};

// Prettier 3.9 (--range-start 9 --range-end 10)
// SyntaxError: Expression expected. (1:4)
// > 1 | <T>
// | ^

// Prettier 3.10
type Box<T> = { value: T };

Fix unstable comment before enum or namespace body (#20318 by @fisker)​

// Input
enum E // c
{ A }
namespace N // c
{ a; }
class C // c
{ a; }

// Prettier 3.9 (First format)
enum E { // c
A,
}
namespace N { // c
a;
}
class C {
// c
a;
}

// Prettier 3.9 (Second format)
enum E {
// c
A,
}
namespace N {
// c
a;
}
class C {
// c
a;
}

// Prettier 3.10
enum E {
// c
A,
}
namespace N {
// c
a;
}
class C {
// c
a;
}

Fix missing parentheses around looser operands of TSInstantiationExpression (#20345 by @vidigoat)​

// Input
x = (c ? a : b)<T>;
x = (a as B)<T>;
x = (y++)<T>;

// Prettier 3.9
x = c ? a : b<T>;
x = a as B<T>;
x = y++<T>;

// Prettier 3.10
x = (c ? a : b)<T>;
x = (a as B)<T>;
x = (y++)<T>;

Add parentheses to TSInstantiationExpression in binary expressions (#20351 by @vidigoat, #20455 by @fisker)​

// Input
x = (make<T>) + y;
x = (make<T>) >> y;

// Prettier 3.9
x = make<T> + y;
x = make<T> >> y;

// Prettier 3.10
x = (make<T>) + y;
x = (make<T>) >> y;

Add parentheses around unary expressions and type assertions that end with a TSInstantiationExpression (#20454 by @vidigoat)​

// Input
x = (typeof make<T>) + y;
x = (<any>make<T>) - y;

// Prettier 3.9
x = typeof make<T> + y;
x = <any>make<T> - y;

// Prettier 3.10
x = (typeof make<T>) + y;
x = (<any>make<T>) - y;

Keep parentheses around < comparison on the left side of > (#20459 by @lazerg)​

// Input
(a < b) > (c ? c : c);
(a < b) > `${c}`;

// Prettier 3.9
a < b > (c ? c : c);
a < b > `${c}`;

// Prettier 3.10
(a < b) > (c ? c : c);
(a < b) > `${c}`;

This also applies to JavaScript and Flow.

Flow​

Fix comment in destructuring patterns (#19902 by @fisker)​

// Input
const {
foo,
// bar
// baz
}: Foo = expr;

// Prettier 3.9 (First format)
const {
foo1,
} // bar
// baz
: Foo = expr;

// Prettier 3.9 (Second format)
const {
foo1, // bar
} // baz
: Foo = expr;

// Prettier 3.10
const {
foo1,
// bar
// baz
}: Foo = expr;

Preserve Flow declaration signatures and property syntax (#20385 by @gkz)​

Prettier now preserves anonymous ambient default-export function signatures, declared constructors without return annotations, literal initializers on readonly declared-class fields, and optional Flow indexers when using the flow parser. Quoted methods named "new" retain their quotes so formatting does not turn them into construct signatures. Construct signatures also keep their unquoted spelling with quoteProps: "consistent".

The anonymous default-export function example is from a .js.flow declaration file.

// Input (example.js.flow)
export default function (): void;

// Prettier 3.9
export default function ;

// Prettier 3.10
export default function (): void;
// Input
declare class C { constructor(); readonly x = 42; static readonly y = "value"; }

// Prettier 3.9
declare class C {
constructor(): ;
readonly x: ;
static readonly y: ;
}

// Prettier 3.10
declare class C {
constructor();
readonly x = 42;
static readonly y = "value";
}
// Input
type T = { [key: string]?: number };

// Prettier 3.9
type T = { [key: string]: number };

// Prettier 3.10
type T = { [key: string]?: number };
// Input
type Method = { 'new'(): string };
type Constructor = { new(): string };

// Prettier 3.9
type Method = { new(): string };
type Constructor = { new(): string };

// Prettier 3.10
type Method = { "new"(): string };
type Constructor = { new(): string };

Fix duplicated comments in record declarations (#20416 by @kovsu)​

// Input
record R // comment
{
a: string,
}

// Prettier 3.9 (First format)
record R // comment // comment
{
a: string,
}

// Prettier 3.9 (Second format)
record R // comment // comment // comment // comment
{
a: string,
}

// Prettier 3.10
record R {
// comment
a: string,
}

Fix unstable comments before component and hook bodies (#20417 by @kovsu)​

// Input
component Foo() // comment
{
return 1;
}

// Prettier 3.9 (First format)
component Foo() { // comment
return 1;
}

// Prettier 3.9 (Second format)
component Foo() {
// comment
return 1;
}

// Prettier 3.10
component Foo() {
// comment
return 1;
}

Fix unstable comments in Flow hook parameters (#20464 by @hoanle0126)​

// Input
declare hook useFoo(
baz: $ReadOnly<{k: string, reallllllllllllllllllllllllllllllllllllllyLong: string}>
// Trailing comment
): void;

// Prettier 3.9 (First format)
declare hook useFoo(
baz: $ReadOnly<{
k: string,
reallllllllllllllllllllllllllllllllllllllyLong: string,
}>,
): // Trailing comment
void;

// Prettier 3.9 (Second format)
declare hook useFoo(
baz: $ReadOnly<{
k: string,
reallllllllllllllllllllllllllllllllllllllyLong: string,
}>, // Trailing comment
): void;

// Prettier 3.10
declare hook useFoo(
baz: $ReadOnly<{
k: string,
reallllllllllllllllllllllllllllllllllllllyLong: string,
}>,
// Trailing comment
): void;

CSS​

Keep leading + glued in An+B notation (#19738 by @lazerg)​

The An+B grammar doesn't allow whitespace between a leading + and the n, so the extra space produced invalid output.

/* Input */
a:nth-child(+3n+2) {
}

/* Prettier 3.9 */
a:nth-child(+ 3n + 2) {
}

/* Prettier 3.10 */
a:nth-child(+3n + 2) {
}

Preserve empty lines after function calls in parenthesized values (#19960 by @kovsu)​

/* Input */
a {
background: linear-gradient(
to right,
rgba(0, 0, 0, 0.5) calc(50% - 1px),

transparent calc(50% + 1px)
);
}

/* Prettier 3.9 */
a {
background: linear-gradient(
to right,
rgba(0, 0, 0, 0.5) calc(50% - 1px),
transparent calc(50% + 1px)
);
}

/* Prettier 3.10 */
a {
background: linear-gradient(
to right,
rgba(0, 0, 0, 0.5) calc(50% - 1px),

transparent calc(50% + 1px)
);
}

Fix whitespace-only custom property with !important (#20055 by @scs0209)​

/* Input */
a {
--x: !important;
}

/* Prettier 3.9 */
a {
--x: !important;
}

/* Prettier 3.10 */
a {
--x: !important;
}

SCSS​

Keep compound comparisons together (#19687 by @aland-omed)​

// Input
@if $something == $something-else and $another-thing == $another-thing-with-long-name {}

// Prettier 3.9
@if $something ==
$something-else and
$another-thing ==
$another-thing-with-long-name
{}

// Prettier 3.10
@if $something == $something-else and
$another-thing == $another-thing-with-long-name
{}

Preserve trailing commas in single-item lists (#19928 by @kovsu)​

// Input
$list: ("a",);

// Prettier 3.9
$list: ("a");

// Prettier 3.10
$list: ("a",);

Fix // comment being swallowed in selector lists with empty attribute values (#20305 by @fixfon)​

// Input
a[href=""], // comment
foo a[href=""] {
color: red;
}

// Prettier 3.9
a[href=""],
// comment foo a[href=""] {
color: red;
}

// Prettier 3.10
a[href=""], // comment
foo a[href=""] {
color: red;
}

The same fix applies to Less.

Format a map entry after a comment like any other entry (#20424 by @vibbix)​

// Input
$map: (
"a": (1, 2),
// comment
"b": (3, 4),
);

// Prettier 3.9
$map: (
"a": (
1,
2,
),
// comment
"b": (
3,
4,
),
);

// Prettier 3.10
$map: (
"a": (
1,
2,
),
// comment
"b": (
3,
4,
),
);

Less​

Remove extra spaces after [$ in indirect property lookups (#19782 by @kovsu)​

// Input
.foo {
value: #namespace[$@prop-name];
}

// Prettier 3.9
.foo {
value: #namespace[$ @prop-name];
}

// Prettier 3.10
.foo {
value: #namespace[$@prop-name];
}

Remove extra spaces after [$ in indirect property lookups on variables (#20279 by @0xvikram)​

// Input
.foo {
value: @config[$@prop-name];
}

// Prettier 3.9
.foo {
value: @config[$ @prop-name];
}

// Prettier 3.10
.foo {
value: @config[$@prop-name];
}

Fix parsing of interpolation in at-rule params (#20390 by @dshovchko, @Manavarya09, @fisker)​

Less now deprecates bare variables in at-rule preludes (@media @mq) in favor of interpolation (@media @{mq}), which Prettier previously failed to parse.

// Input
@media @{mq-xs} { .a { color: red } }

// Prettier 3.9
SyntaxError: CssSyntaxError: Unknown word mq-xs (1:10)

// Prettier 3.10
@media @{mq-xs} {
.a {
color: red;
}
}

HTML​

Fix attribute name normalization (#19709 by @fisker)​

<!-- Input -->
<span CLASS="foo"></span>
<div CLASS="foo"></div>

<!-- Prettier 3.9 -->
<span CLASS="foo"></span>
<div class="foo"></div>

<!-- Prettier 3.10 -->
<span class="foo"></span>
<div class="foo"></div>

Fix duplicated > before <pre> containing elements (#20215 by @nazarli-shabnam)​

<!-- Input -->
<p></p><pre><code></code></pre>

<!-- Prettier 3.9 (--html-whitespace-sensitivity strict) -->
<p></p>><pre><code></code></pre>

<!-- Prettier 3.10 -->
<p></p><pre><code></code></pre>

Preserve CDATA content in HTML and Angular templates (#20256 by @splincode)​

<!-- Input -->
<![CDATA[<app-alert>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</app-alert>]]>

<!-- Prettier 3.9 -->
<![CDATA[<app-alert> Lorem ipsum dolor sit amet consectetur adipisicing elit.
</app-alert>]]>

<!-- Prettier 3.10 -->
<![CDATA[<app-alert>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</app-alert>]]>

Vue​

Avoid adding blank lines to interpolations (#20001 by @vzer200)​

<!-- Input -->
<template>
<div>
({{ a
}})
</div>
</template>

<!-- Prettier 3.9 (--embedded-language-formatting=off) -->
<template>
<div>
({{ a

}})
</div>
</template>

<!-- Prettier 3.10 (--embedded-language-formatting=off) -->
<template>
<div>
({{ a
}})
</div>
</template>

Preserve double-brace placeholders inside string literals (#20117 by @splincode)​

<!-- Input -->
<template>
<div>{{"{{first}} / {{second}} files selected"}}</div>
</template>

<!-- Prettier 3.9 -->
<template>
<div>{{"{{first}} / {{ second }} files selected"}}</div>
</template>

<!-- Prettier 3.10 -->
<template>
<div>{{ "{{first}} / {{second}} files selected" }}</div>
</template>

Angular​

Add ng-model-options to recognized AngularJS attributes (#19777 by @xemlock)​

<!-- Input -->
<input ng-model="$ctrl.name" ng-model-options="{ debounce : 1000 , 'updateOn' : 'blur' }" />

<!-- Prettier 3.9 -->
<input
ng-model="$ctrl.name"
ng-model-options="{ debounce : 1000 , 'updateOn' : 'blur' }"
/>

<!-- Prettier 3.10 -->
<input
ng-model="$ctrl.name"
ng-model-options="{ debounce: 1000, updateOn: 'blur' }"
/>

Avoid adding blank lines to interpolations (#20001 by @vzer200)​

<!-- Input -->
<div>
({{ a
}})
</div>

<!-- Prettier 3.9 (--embedded-language-formatting=off) -->
<div>
({{ a

}})
</div>

<!-- Prettier 3.10 (--embedded-language-formatting=off) -->
<div>
({{ a
}})
</div>

Preserve interpolations inside elements with ngNonBindable directive (#20044 by @splincode, #20115 by @fisker)​

<!-- Input -->
<span ngNonBindable>{{foo+bar}}</span>

<!-- Prettier 3.9 -->
<span ngNonBindable>{{ foo + bar }}</span>

<!-- Prettier 3.10 -->
<span ngNonBindable>{{foo+bar}}</span>

Preserve Angular ngIf microsyntax keywords after let declarations (#20051 by @splincode)​

<!-- Input -->
<div *ngIf="item$ | async; let item; else loadingTemplate"></div>

<!-- Prettier 3.9 -->
<div *ngIf="item$ | async; let item; else: loadingTemplate"></div>

<!-- Prettier 3.10 -->
<div *ngIf="item$ | async; let item; else loadingTemplate"></div>

Preserve Angular syntax inside elements with ngNonBindable directive (#20066 by @splincode)​

<!-- Input -->
<span ngNonBindable>@if(foo){bar}</span>
<span ngNonBindable>@let foo=bar+baz;</span>

<!-- Prettier 3.9 -->
<span ngNonBindable>
@if (foo) {
bar
}
</span>
<span ngNonBindable>@let foo = bar + baz;</span>

<!-- Prettier 3.10 -->
<span ngNonBindable>@if(foo){bar}</span>
<span ngNonBindable>@let foo=bar+baz;</span>

Preserve double-brace placeholders inside string literals (#20117 by @splincode)​

<!-- Input -->
{{ "{{num_of_selected_files}} / {{num_of_total_files}} files selected" | translate: {num_of_selected_files: 20, num_of_total_files: 30} }}

<!-- Prettier 3.9 -->
{{ "{{num_of_selected_files}} / {{ num_of_total_files }} files selected" |
translate: {num_of_selected_files: 20, num_of_total_files: 30} }}

<!-- Prettier 3.10 -->
{{
"{{num_of_selected_files}} / {{num_of_total_files}} files selected"
| translate: { num_of_selected_files: 20, num_of_total_files: 30 }
}}

Avoid breaking inline element closing tags after interpolations (#20134 by @splincode)​

<!-- Input -->
<div class="text">
Sorry, we couldn’t find an account for <b>{{ phoneInput }}</b>, let's create one:
</div>

<!-- Prettier 3.9 -->
<div class="text">
Sorry, we couldn’t find an account for <b>{{ phoneInput }}</b
>, let's create one:
</div>

<!-- Prettier 3.10 -->
<div class="text">
Sorry, we couldn’t find an account for <b>{{ phoneInput }}</b>, let's create
one:
</div>

Fix unstable indentation of nested logical expressions (#20405 by @MIbtehajNasar)​

<!-- Input -->
@if (wrapper.input.source !== "datepicker" && (wrapper.input.inputElementType !== "textarea" && wrapper.input.inputElementType !== "autocomplete")) {}

<!-- Prettier 3.9 -->
@if (
wrapper.input.source !== "datepicker" &&
wrapper.input.inputElementType !== "textarea" &&
wrapper.input.inputElementType !== "autocomplete"
) {}

<!-- Prettier 3.10 -->
@if (
wrapper.input.source !== "datepicker" &&
wrapper.input.inputElementType !== "textarea" &&
wrapper.input.inputElementType !== "autocomplete"
) {}

Ember / Handlebars​

Support embedded <script> (#20012 by @kovsu)​

{{! Input }}
<script>
const a = 1;
console.log( a );
</script>

{{! Prettier 3.9 }}
<script>
const a = 1; console.log( a );
</script>

{{! Prettier 3.10 }}
<script>
const a = 1;
console.log(a);
</script>

Fix invalid output for <style> containing mustaches (#20030 by @kovsu)​

{{! Input }}
<style>
a {
color: {{foo}}
}
</style>

{{! Prettier 3.9 }}
<style>
a {
color:{{foo}}}
</style>

{{! Prettier 3.10 }}
<style>
a { color:
{{foo}}
}
</style>

Don't format <style lang=""> as CSS (#20031 by @kovsu)​

{{! Input }}
<style lang="">
.a { color: red;
}
</style>

{{! Prettier 3.9 }}
<style lang="">
.a {
color: red;
}
</style>

{{! Prettier 3.10 }}
<style lang="">
.a { color: red;
}
</style>

GraphQL​

Preserve carriage returns in GraphQL strings (#20420 by @Sskift)​

Carriage returns in ordinary quoted strings are now kept escaped. Previously, formatting emitted a literal carriage return inside the string, so parsing or formatting the output again failed with an unterminated string error.

# Input
{ field(value: "first\rsecond") }

# Prettier 3.9: the output contains a literal carriage return and cannot be parsed again.

# Prettier 3.10
{
field(value: "first\rsecond")
}

Allow empty selection sets (#20469 by @fisker)​

Empty selections are now allowed by spec.

# Input
{
a
deep {}
...Frag
}
fragment Frag on Type {}

# Prettier 3.9
SyntaxError: Syntax Error: Expected Name, found "}". (3:9)
1 | {
2 | a
> 3 | deep {}
| ^
4 | ...Frag
5 | }
6 | fragment Frag on Type {}

# Prettier 3.10
{
a
deep {}
...Frag
}
fragment Frag on Type {}

Markdown​

Avoid corrupting URL with <, (, \ (#19482 by @andersk)​

Avoid corrupting a link, image, or link reference definition with certain characters like <, (, \, by using <...> wrapping and \ escaping as appropriate.

<!-- Input -->
[link](<\<(\\>)

<!-- Prettier 3.9 -->
[link](<(\)

<!-- Prettier 3.10 -->
[link](<\<(\\>)

Fix escaped character in links (#19849 by @Kjubikstronk)​

<!-- Input -->
[test](/hello?foo&quot\;bar)

<!-- Prettier 3.9 (First format) -->
[test](/hello?foo&quot;bar)

<!-- Prettier 3.9 (Second format) -->
[test](/hello?foo"bar)

<!-- Prettier 3.10 -->
[test](/hello?foo\&quot;bar)

Fix fenced code block in JS templates (#19908 by @Kjubikstronk)​

// Input
const content = markdown`
\`\`\`js
markdown\`
\\\`\\\`\\\`js
console.log('hi');
\\\`\\\`\\\`
\`
\`\`\`
`;

// Prettier 3.9
const content = markdown`
~~~js
markdown\`
~~~js
console.log("hi");
~~~
\`;
~~~
`;

// Prettier 3.10
const content = markdown`
~~~~js
markdown\`
~~~js
console.log("hi");
~~~
\`;
~~~~
`;

Preserve newlines around Chinese/Japanese characters not around Korean letters in all proseWrap settings (#20143 by @tats-u)​

<!-- Input -->
English
English
日本語
日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語
日本語
한국어
한국어
中文
中文!
한국어
(中文)

<!-- Prettier 3.9 (--prose-wrap=never) -->
English English日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語 한국어 한국어 中文中文!한국어(中文)

<!-- Prettier 3.10 -->
English English
日本語
日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語
日本語 한국어 한국어 中文
中文!
한국어
(中文)

Preserve the blank line between an HTML block and a nested list in a list item (#20243 by @devanshranjan10)​

<!-- Input -->
- Some text

<details>
<summary>Summary</summary>

- Lorem
- ipsum
</details>

<!-- Prettier 3.9 -->
- Some text

<details>
<summary>Summary</summary>
- Lorem
- ipsum

</details>

<!-- Prettier 3.10 -->
- Some text

<details>
<summary>Summary</summary>

- Lorem
- ipsum

</details>

YAML​

Preserve order of anchor and tag (#19599 by @fisker)​

# Input
foo: &anchor1 !!str Value1
bar: !!str &anchor2 Value2

# Prettier 3.9
foo: !!str &anchor1 Value1
bar: !!str &anchor2 Value2

# Prettier 3.10
foo: &anchor1 !!str Value1
bar: !!str &anchor2 Value2

Preserve trailing whitespace in YAML block scalars (#19764, #20475 by @splincode)​

Prettier now preserves trailing spaces, tabs, and whitespace-only lines when formatting literal and folded YAML block scalars.

await prettier.format("foo: |-\n value \n", { parser: "yaml" });

/* Prettier 3.9 */
// -> "foo: |-\n value\n"

/* Prettier 3.10 */
// -> "foo: |-\n value \n"
// ^^ spaces preserved

Fix extra blank line before end comments after a trailing comment (#20371 by @lazerg)​

# Input
a: 1
b: 2 # comment

# end comment

# Prettier 3.9
a: 1
b: 2 # comment


# end comment

# Prettier 3.10
a: 1
b: 2 # comment

# end comment

API​

Support configuration files with UTF-8 BOM (#20127 by @fisker)​

# Input
$ cat .prettierrc.json
{"semi":false}
$ prettier example.js --check

# Prettier 3.9
Invalid configuration for file "example.js":
JSON Error in .prettierrc.json:
Unexpected token "" (\u{feff})

# Prettier 3.10
Checking formatting...
[warn] example.js
[warn] Code style issues found in the above file. Run Prettier with --write to fix.

Return true in comment attach helpers (#20329 by @fisker)​

prettier.util.add{Trailing,Leading,Dangling}Comment() now returns true instead of void, for easier use in comment handle functions.

import * as prettier from "prettier";

function ownLine(comment, text, options, ast, isLastComment) {
const { precedingNode } = comment;
if (precedingNode?.type === "punctuation") {
- prettier.util.addTrailingComment(precedingNode, comment);
- return true;
+ return prettier.util.addTrailingComment(precedingNode, comment);
}
return false;
}

CLI​

Update experimental cli (#20452 by @fisker)​

Full changelog:

Miscellaneous​

Handle Unicode zero-width marks in string width (#19538 by @TheAlexLichter)​

Prettier now treats Unicode nonspacing and enclosing marks as zero-width when measuring lines.

// Input
moment.updateLocale("bo", { monthsShort: "ཟླ་༡_ཟླ་༢_ཟླ་༣_ཟླ་༤_ཟླ་༥_ཟླ་༦_ཟླ་༧_ཟླ་༨_ཟླ་༩_ཟླ་༡༠_ཟླ་༡༡_ཟླ་༡༢".split("_") });

// Prettier 3.9
moment.updateLocale("bo", {
monthsShort:
"ཟླ་༡_ཟླ་༢_ཟླ་༣_ཟླ་༤_ཟླ་༥_ཟླ་༦_ཟླ་༧_ཟླ་༨_ཟླ་༩_ཟླ་༡༠_ཟླ་༡༡_ཟླ་༡༢".split("_"),
});

// Prettier 3.10
moment.updateLocale("bo", {
monthsShort: "ཟླ་༡_ཟླ་༢_ཟླ་༣_ཟླ་༤_ཟླ་༥_ཟླ་༦_ཟླ་༧_ཟླ་༨_ཟླ་༩_ཟླ་༡༠_ཟླ་༡༡_ཟླ་༡༢".split("_"),
});

Share .editorconfig reads across resolveConfig() calls (#20462 by @lishaduck)​

Prettier now caches each parsed .editorconfig file and shares it with every other file under it. Previously, each call would probe from the file's directory up to the project root for every potential .editorconfig.

In a TypeScript monorepo using a custom formatting script, config resolution for all 786 of their files now reads the .editorconfig 31 times instead of 3,969, and is approximately 3× faster.