Prettier 3.0: Hello, ECMAScript Modules!
We are excited to announce the release of the new version of Prettier!
We have made the migration to using ECMAScript Modules for all our source code. This change has significantly improved the development experience for the Prettier team. Please rest assured that when using Prettier as a library, you can still use it as CommonJS as well.
This update comes with several breaking changes. One notable example is the alteration in markdown formatting - spaces are no longer inserted between Latin characters and Chinese or Japanese characters. We'd like to extend our gratitude to Tatsunori Uchino, who has made significant contributions to Prettier over the past year, particularly with this feature. Additionally, the default value of trailingComma
has been changed to "all"
.
Another important change in this release is the significant overhaul of the plugin interface. Prettier now supports plugins written using ECMAScript Modules and async parsers. If you're a plugin developer, please exercise caution while updating. You can find the migration guide here. As always, we welcome bug reports and feedback!
This release also includes numerous formatting improvements and bug fixes.
If you appreciate Prettier and would like to support our work, please consider sponsoring us directly via our OpenCollective or by sponsoring the projects we depend on, such as typescript-eslint, remark, and Babel. Thank you for your continued support!
Highlights
Markdown
#11597 by @tats-u)
Improve handling of whitespace for Chinese, Japanese, and Korean (Stop inserting spaces between Chinese or Japanese and Western characters
Previously, Prettier would insert spaces between Chinese or Japanese and Western characters (letters and digits). While some people prefer this style, it isn’t standard, and is in fact contrary to official guidelines. Please see here for more details. We decided it’s not Prettier’s job to enforce a particular style in this case, so spaces aren’t inserted anymore, while existing ones are preserved. If you need a tool for enforcing spacing style, consider textlint-ja or lint-md (rules space-round-alphabet
and space-round-number
).
The tricky part of this change were ambiguous line breaks between Chinese or Japanese and Western characters. When Prettier unwraps text, it needs to decide whether such a line break should be simply removed or replaced with a space. For that Prettier examines the surrounding text and infers the preferred style.
<!-- Input -->
漢字
Alphabetsひらがな12345カタカナ67890
漢字 Alphabets ひらがな 12345 カタカナ 67890
<!-- Prettier 2.8 -->
漢字 Alphabets ひらがな 12345 カタカナ 67890
漢字 Alphabets ひらがな 12345 カタカナ 67890
<!-- Prettier 3.0 -->
漢字Alphabetsひらがな12345カタカナ67890
漢字 Alphabets ひらがな 12345 カタカナ 67890
Comply to line breaking rules in Chinese and Japanese
There are rules that prohibit certain characters from appearing at the beginning or the end of a line in Chinese and Japanese. E.g., full stop characters 。
, .
, and .
shouldn’t start a line whereas (
shouldn’t end a line. Prettier now follows these rules when it wraps text, that is when proseWrap
is set to always
.
<!-- Input -->
HTCPCPのエラー418は、ティーポットにコーヒーを淹(い)れさせようとしたときに返されるステータスコードだ。
<!-- Prettier 2.8 with --prose-wrap always --print-width 8 -->
HTCPCP の
エラー
418 は、
ティーポ
ットにコ
ーヒーを
淹(い)
れさせよ
うとした
ときに返
されるス
テータス
コードだ
。
<!-- Prettier 3.0 with the same options -->
HTCPCPの
エラー
418は、
ティー
ポットに
コーヒー
を淹
(い)れ
させよう
としたと
きに返さ
れるス
テータス
コード
だ。
Do not break lines inside Korean words
Korean uses spaces to divide words, and an inappropriate division may change the meaning of a sentence:
노래를 못해요.
: I’m not good at singing.노래를 못 해요.
: I can’t sing (for some reason).
Previously, when proseWrap
was set to always
, successive Hangul characters could get split by a line break, which could later be converted to a space when the document is edited and reformatted. This doesn’t happen anymore. Korean text is now wrapped like English.
<!-- Input -->
노래를 못해요.
<!-- Prettier 2.8 with --prose-wrap always --print-width 9 -->
노래를 못
해요.
<!-- Prettier 2.8, subsequent reformat with --prose-wrap always --print-width 80 -->
노래를 못 해요.
<!-- Prettier 3.0 with --prose-wrap always --print-width 9 -->
노래를
못해요.
<!-- Prettier 3.0, subsequent reformat with --prose-wrap always --print-width 80 -->
노래를 못해요.
A line break between Hangul and non-Hangul letters and digits is converted to a space when Prettier unwraps the text. Consider this example:
3분 기다려 주지.
In this sentence, if you break the line between “3” and “분”, a space will be inserted there when the text gets unwrapped.
API
#12748 by @fisker, #13211 by @thorn0 and @fisker)
Support plugins with async parsers (parse
function in a plugin can return a Promise now.
In order to support async parsers for embedded languages, we had to introduce a breaking change to the plugin API. Namely, the embed
method of a printer has now to match a completely new signature, incompatible with previous versions. If you're a plugin author and your plugins don't define embed
, you have nothing to worry about, otherwise see the docs for details.
Also, the preprocess
method of a printer can return a promise now.
#13130 by @fisker)
Support config files in ESM (Config files in ESM are supported, supported config file names:
prettier.config.js
(in place with{"type": "module"}
inpackage.json
).prettierrc.js
(same as above)prettier.config.mjs
.prettierrc.mjs
.
export default {
trailingComma: "es5",
tabWidth: 4,
semi: false,
singleQuote: true,
};
Shareable config package can also be a pure ESM package.
Breaking Changes
JavaScript
trailingComma
to all
(#11479 by @fisker, #13143 by @sosukesuzuki)
Change the default value for Since version 2.0. we've changed the default value for trailingComma
to es5
.
Internet Explorer, the last browser to not allow trailing commas in function calls, has been unsupported on June 15, 2022. Accordingly, change the default value for trailingComma
to all
.
If the old behavior is still preferred, please configure Prettier with { "trailingComma": "es5" }
.
babel
parser (#14314 by @fisker, @thorn0)
Remove Flow syntax support from For historical reasons, Prettier used to recognize Flow syntax in JS files when the parser
option was set to babel
even if the file didn't include the @flow
pragma. This support was limited and bad for performance, so it has been removed in Prettier 3.0. Prettier with the babel
parser still automatically switches to the Flow syntax if it finds the @flow
pragma or the file has the .js.flow
extension.
Flow
#13687, #13703 by @thorn0)
Remove support for Flow comments (Being a kind of preprocessor, Flow comments AKA comment types are processed on the token level and can't be represented in an AST in the general case. Flow builds the AST as if these special comment tokens didn't exist. Example:
/*:: if */ (x) + y;
This is parsed as if (x) +y;
by Flow and as x + y;
by JS parsers that don't support Flow.
Previously, for some special cases, Prettier tried to detect that this syntax was used and to preserve it. As an attempt to solve an unsolvable problem, this limited support was fragile and riddled with bugs, so it has been removed. Now if the parser
option is set to flow
or babel-flow
, Flow comments will be parsed and reprinted like normal code. If a parser that doesn't support Flow is used, they will be treated like usual comments.
// Input
let a /*: foo */ = b;
// Prettier 2.8
let a /*: foo */ = b;
// Prettier 3.0 with --parser flow
let a: foo = b;
// Prettier 3.0 with --parser babel
let a /*: foo */ = b;
--trailing-comma=es5
(#14086, #14085 by @fisker)
Print trailing comma in type parameters and tuple types when // Input
type Foo = [
{
from: string,
to: string,
}, // <- 1
];
type Foo = Promise<
| { ok: true, bar: string, baz: SomeOtherLongType }
| { ok: false, bar: SomeOtherLongType }, // <- 2
>;
// Prettier 2.8
type Foo = [
{
from: string,
to: string,
} // <- 1
];
type Foo = Promise<
| { ok: true, bar: string, baz: SomeOtherLongType }
| { ok: false, bar: SomeOtherLongType } // <- 2
>;
// Prettier 3.0
type Foo = [
{
from: string,
to: string,
}, // <- 1
];
type Foo = Promise<
| { ok: true, bar: string, baz: SomeOtherLongType }
| { ok: false, bar: SomeOtherLongType }, // <- 2
>;
CSS
css
parser (#7933, #9092, #9093 by @fisker)
Add the pure Previously, when --parser=css
was passed, Prettier tried to parse the content using postcss-scss
and postcss-less
. This caused confusion, and made syntax errors difficult to spot. Now --parser=css
works only with the vanilla CSS syntax.
If you use parser="css"
for your .less
/.scss
files, update it to the correct parser or remove the parser
option to let Prettier auto-detect the parser by the file extension.
/* Input */
/* Less Syntax with `--parser=css` */
a {.bordered();}
/* Prettier 2.8 */
/* Less Syntax with `--parser=css` */
a {
.bordered();
}
/* Prettier 3.0 */
SyntaxError: (postcss) CssSyntaxError Unknown word (2:4)
1 | /* Less Syntax with `--parser=css` */
> 2 | a {.bordered();}
/* Input */
/* Scss Syntax with `--parser=css` */
::before {content: #{$foo}}
/* Prettier 2.8 */
/* Scss Syntax with `--parser=css` */
::before {
content: #{$foo};
}
/* Prettier 3.0 */
SyntaxError: (postcss) CssSyntaxError Unknown word (2:22)
1 | /* Scss Syntax with `--parser=css` */
> 2 | ::before {content: #{$foo}}
GraphQL
#12835 by @fisker)
Drop support for "comma separated interfaces" syntax (# Input
type Type1 implements A, B {a: a}
# Prettier 2.8
type Type1 implements A, B {
a: a
}
# Prettier 3.0
SyntaxError: Syntax Error: Unexpected Name "B". (1:26)
> 1 | type Type1 implements A, B {a: a}
API
#11830 by @fisker, #13118 by @sosukesuzuki)
Drop support for Node.js 10 and 12 (The minimal required Node.js version is v14
#12574, #12788, #12790, #13265 by @fisker)
Change public APIs to asynchronous (prettier.format()
returnsPromise<string>
prettier.formatWithCursor()
returnsPromise<{formatted: string, cursorOffset: number}>
prettier.formatAST()
returnsPromise<string>
prettier.check()
returnsPromise<boolean>
prettier.getSupportInfo()
returnsPromise
prettier.clearConfigCache()
returnsPromise<void>
prettier.resolveConfig.sync
is removedprettier.resolveConfigFile.sync
is removedprettier.getFileInfo.sync
is removed
If you still need sync APIs, you can try @prettier/sync
#12740 by @fisker, #13530 by @fisker, #14570 by @fisker)
Npm package file structures changed (File structures changes:
bin-prettier.js
->bin/prettier.cjs
esm/standalone.mjs
->standalone.mjs
esm/parser-angular.mjs
->plugins/angular.mjs
parser-angular.js
->plugins/angular.js
esm/parser-babel.mjs
->plugins/babel.mjs
parser-babel.js
->plugins/babel.js
esm/parser-espree.mjs
->plugins/acorn-and-espree.mjs
parser-espree.js
->plugins/acorn.js
global object renamedprettierPlugins.espree
->prettierPlugins.acorn
esm/parser-flow.mjs
->plugins/flow.mjs
parser-flow.js
->plugins/flow.js
esm/parser-glimmer.mjs
->plugins/glimmer.mjs
parser-glimmer.js
->plugins/glimmer.js
esm/parser-graphql.mjs
->plugins/graphql.mjs
parser-graphql.js
->plugins/graphql.js
esm/parser-html.mjs
->plugins/html.mjs
parser-html.js
->plugins/html.js
esm/parser-markdown.mjs
->plugins/markdown.mjs
parser-markdown.js
->plugins/markdown.js
esm/parser-meriyah.mjs
->plugins/meriyah.mjs
parser-meriyah.js
->plugins/meriyah.js
esm/parser-postcss.mjs
->plugins/postcss.mjs
parser-postcss.js
->plugins/postcss.js
esm/parser-typescript.mjs
->plugins/typescript.mjs
parser-typescript.js
->plugins/typescript.js
esm/parser-yaml.mjs
->plugins/yaml.mjs
parser-yaml.js
->plugins/yaml.js
Check full list on https://unpkg.com/browse/prettier@3.0.0/.
A new plugin has been added:
plugins/estree.mjs
(ESM version)plugins/estree.js
(UMD version)
If you use standalone version, this plugin should be loaded when printing JavaScript, TypeScript, Flow, or JSON.
import { format } from "prettier/standalone";
- import prettierPluginBabel from "prettier/parser-babel";
+ import * as prettierPluginBabel from "prettier/plugins/babel";
+ import * as prettierPluginEstree from "prettier/plugins/estree";
console.log(
- format(code, {
+ await format(code, {
parser: "babel",
- plugins: [prettierPluginBabel],
+ plugins: [prettierPluginBabel, prettierPluginEstree],
})
);
- node ./node_modules/prettier/bin-prettier.js . --write
+ node ./node_modules/prettier/bin/prettier.cjs . --write
#13201 by @fisker)
Support plugins in ESM (Since v3.0.0, we load plugins via import()
instead of require()
, plugins can be ESM modules now.
If you use --plugin
by directory path, or file path without extensions, the plugin may not able to load.
- prettier . --plugin=path/to/my-plugin-directory
+ prettier . --plugin=path/to/my-plugin-directory/index.js
- prettier . --plugin=path/to/my-plugin-file
+ prettier . --plugin=path/to/my-plugin-file.js
prettier.doc
(#13203, #14456 by @fisker)
Update prettier.doc.builders.concat
was deprecated in v2.3.0, now it's removed.
The following apis are never documented, they mean to only use internally, now they are removed.
prettier.doc.utils.getDocParts
prettier.doc.utils.propagateBreaks
prettier.doc.utils.cleanDoc
prettier.doc.utils.getDocType
prettier.doc.debug.printDocToDebug
textToDoc
trims trailing hard lines (#13220 by @fisker)
Previously, in all core languages, after embedded code printed to Doc
, we call prettier.doc.utils.stripTrailingHardline()
to remove the trailing hard lines.
We believe make textToDoc
return docs without trailing hard lines makes the plugins easier to do embed
print.
#13250 by @fisker and @thorn0)
Removed support for custom parser api (Before plugins were a thing, Prettier had a similar but more limited feature called custom parsers. It’s been removed in v3.0.0 as its functionality was a subset of what the Plugin API did. If you used it, please check how to migrate.
parsers
passed to parsers.parse
has been removed (#13268 by @fisker)
The second argument The plugin's print
function signature changed from
function parse(text: string, parsers: object, options: object): AST;
to
function parse(text: string, options: object): Promise<AST> | AST;
The second argument parsers
has been removed, if you still need other parser during parse process, you can:
Import the plugin yourself (recommended)
import * as prettierPluginBabel from "prettier/plugins/babel"; const myCustomPlugin = { parsers: { "my-custom-parser": { async parse(text) { const ast = await prettierPluginBabel.parsers.babel.parse(text); ast.program.body[0].expression.callee.name = "_"; return ast; }, astFormat: "estree", }, }, };
Get the parser from the
options
argumentfunction getParserFromOptions(options, parserName) { const parserOrParserInitFunction = options.plugins.find( (plugin) => plugin.parsers && Object.hasOwn(plugin.parsers, parserName), )?.parsers[parserName]; return typeof parserOrParserInitFunction === "function" ? parserOrParserInitFunction() : parserOrParserInitFunction; } const myCustomPlugin = { parsers: { "my-custom-parser": { async parse(text, options) { const babelParser = await getParserFromOptions(options, "babel"); const ast = await babelParser.parse(text); ast.program.body[0].expression.callee.name = "_"; return ast; }, astFormat: "estree", }, }, };
undefined
and null
are not passed to plugin's print
function (#13397 by @fisker)
If your plugin happened to use print
to print them, please check them in the parent node instead.
function print(path, print) {
- const value = path.getValue();
- if (!value?.type) {
- return String(value);
- }
- return path.map(print, "values");
+ return path.map(({node}) => (node?.type ? print() : String(node)), "values");
}
label
docs (#13532 by @thorn0)
Allow using arbitrary truthy values for The label
doc builder has been changed. See the documentation.
getFileInfo()
resolves config by default (#14108 by @fisker)
options.resolveConfig
default to true
now, see the documentation.
#14759 by @fisker)
Plugin search feature has been removed (The plugin auto search feature didn't work well when using pnpm, and cause slowness.
--plugin-search-dir
, --no-plugin-search
flags for CLI and pluginSearchDirs
in API options has been removed in Prettier 3.0.
--plugin
flag and plugins
option should be used instead, see documentation.
CLI
.gitignore
d files by default (#14731 by @fisker)
Ignore Prettier ignores files ignored by .gitignore
by default.
If you want the old behavior(only ignore files ignored by .prettierignore
), use
prettier . --write --ignore-path=.prettierignore
Other Changes
JavaScript
#10714 by @thorn0)
Support the "decorated function" pattern (In this case the developer is usually willing to sacrifice the readability of the arrow function's signature to get less indentation in its body. Prettier now recognizes this pattern and keeps the arrow function hugged even if the signature breaks.
// Prettier 2.8
const Counter = decorator("my-counter")(
(props: { initialCount?: number; label?: string }) => {
// ...
}
);
// Prettier 3.0
const Counter = decorator("my-counter")((props: {
initialCount?: number;
label?: string;
}) => {
// ...
});
#13340 by @fisker)
Fix cursor positioning for files containing emoji ($ cat test.js
const { formatWithCursor } = await import("prettier");
const code = "'😀😀😀😀'";
await formatWithCursor(code, {parser: "babel", cursorOffset: 9})
# Prettier 2.8
$ node test.js
{ formatted: '"😀😀😀😀";\n', cursorOffset: 5, comments: [] }
# Prettier 3.0
$ node test.js
{ formatted: '"😀😀😀😀";\n', cursorOffset: 9, comments: [] }
#13341 by @thorn0)
Fix edge cases of the first call argument expansion (// Input
export default whatever(function (a: {
aaaaaaaaa: string;
bbbbbbbbb: string;
ccccccccc: string;
}) {
return null;
}, "xyz");
call(
function() {
return 1;
},
$var ?? $var ?? $var ?? $var ?? $var ?? $var ?? $var ?? $var ?? $var ?? 'test'
);
// Prettier 2.8
export default whatever(function (a: {
aaaaaaaaa: string;
bbbbbbbbb: string;
ccccccccc: string;
}) {
return null;
},
"xyz");
call(function () {
return 1;
}, $var ??
$var ??
$var ??
$var ??
$var ??
$var ??
$var ??
$var ??
$var ??
"test");
// Prettier 3.0
export default whatever(function (a: {
aaaaaaaaa: string,
bbbbbbbbb: string,
ccccccccc: string,
}) {
return null;
}, "xyz");
call(
function () {
return 1;
},
$var ??
$var ??
$var ??
$var ??
$var ??
$var ??
$var ??
$var ??
$var ??
"test",
);
#13391 by @thorn0)
Fix indentation of arrow function chains in call arguments and binary expressions (The motivation behind the chosen formatting is to make it clear how many arguments the call has. However, there was a bug with the indentation of the first signature in the chain if that signature didn't fit on one line.
// Prettier 2.8
askTrovenaBeenaDependsRowans(
glimseGlyphsHazardNoopsTieTie,
(
averredBathersBoxroomBuggyNurl,
anodyneCondosMalateOverateRetinol = "default"
) =>
(annularCooeedSplicesWalksWayWay) =>
(kochabCooieGameOnOboleUnweave) =>
abugidicRomanocastorProvider,
weaponizedStellatedOctahedron
);
// Prettier 3.0
askTrovenaBeenaDependsRowans(
glimseGlyphsHazardNoopsTieTie,
(
averredBathersBoxroomBuggyNurl,
anodyneCondosMalateOverateRetinol = "default",
) =>
(annularCooeedSplicesWalksWayWay) =>
(kochabCooieGameOnOboleUnweave) =>
abugidicRomanocastorProvider,
weaponizedStellatedOctahedron,
);
#13410 by @thorn0)
Don't break signature of hugged function expression if parameters are identifiers without types (// Prettier 2.8
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
props,
ref
) {
return <ThemeUILink ref={ref} variant="default" {...props} />;
});
// Prettier 3.0
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
function Link(props, ref) {
return <ThemeUILink ref={ref} variant="default" {...props} />;
},
);
#13438 by @thorn0)
Fix interleaved comments (// Input
function x() {
} // first
; // second
// Prettier 2.8
function x() {} // first // second
// Prettier 3.0
function x() {} // first
// second
#13445 by @thorn0)
Support nestled JSDoc comments (This kind of comments is used to document overloaded functions (see https://github.com/jsdoc/jsdoc/issues/1017).
// Input
/**
* @template T
* @param {Type} type
* @param {T} value
* @return {Value}
*//**
* @param {Type} type
* @return {Value}
*/
function value(type, value) {
if (arguments.length === 2) {
return new ConcreteValue(type, value);
} else {
return new Value(type);
}
}
// Prettier 2.8
/**
* @template T
* @param {Type} type
* @param {T} value
* @return {Value}
*/ /**
* @param {Type} type
* @return {Value}
*/
function value(type, value) {
if (arguments.length === 2) {
return new ConcreteValue(type, value);
} else {
return new Value(type);
}
}
// Prettier 3.0
/**
* @template T
* @param {Type} type
* @param {T} value
* @return {Value}
*//**
* @param {Type} type
* @return {Value}
*/
function value(type, value) {
if (arguments.length === 2) {
return new ConcreteValue(type, value);
} else {
return new Value(type);
}
}
#13532 by @thorn0)
Fix unstable template literals with embedded languages (If a template literal with embedded syntax is the only argument of a call or the body of an arrow function and has leading and trailing whitespace, it won't be printed on a new line.
// Input
foo(/* HTML */ ` <!-- bar1 --> bar <!-- bar2 --> `);
// Prettier 2.8 (first output)
foo(
/* HTML */ `
<!-- bar1 -->
bar
<!-- bar2 -->
`
);
// Prettier 2.8 (second output)
foo(/* HTML */ `
<!-- bar1 -->
bar
<!-- bar2 -->
`);
// Prettier 3.0 (first output)
foo(/* HTML */ `
<!-- bar1 -->
bar
<!-- bar2 -->
`);
#13621 by @fisker)
Fix indention of expressions in template literals (// Input
`
1. Go to ${chalk.green.underline(FOO_LINK)}
2. Click "${chalk.green(
"Run workflow"
)}" button, type "${chalk.yellow.underline(
version
)}", hit the "${chalk.bgGreen("Run workflow")}" button.
`
// Prettier 2.8
`
1. Go to ${chalk.green.underline(FOO_LINK)}
2. Click "${chalk.green(
"Run workflow"
)}" button, type "${chalk.yellow.underline(
version
)}", hit the "${chalk.bgGreen("Run workflow")}" button.
`;
// Prettier 3.0
`
1. Go to ${chalk.green.underline(FOO_LINK)}
2. Click "${chalk.green(
"Run workflow",
)}" button, type "${chalk.yellow.underline(
version,
)}", hit the "${chalk.bgGreen("Run workflow")}" button.
`;
#13752 by @fisker, #14862 by @sosukesuzuki)
Add support for "Explicit Resource Management" proposal (The Stage 2 proposal "Explicit Resource Management" is now supported via Babel 7.20.0 and 7.22.0.
Also keep in mind our policy on non-standardized syntax before using this proposed syntax feature with Prettier.
// Examples
{
using obj = g(); // block-scoped declaration
const r = obj.next();
} // calls finally blocks in `g`
{
await using obj = g(); // block-scoped declaration
const r = obj.next();
} // calls finally blocks in `g`
#13771 by @fisker)
Add support for "Import Reflection" proposal (The Stage 2 proposal "Import Reflection" is now supported via Babel 7.20.0. Also keep in mind our policy on non-standardized syntax before using this proposed syntax feature with Prettier.
// Examples
import module x from "<specifier>";
#14065 by @fisker)
Fix inconsistent between array/tuple and object/record (// Input
foo.a().b().c([n, o])
foo.a().b().c(#[n, o])
foo.a().b().c({n, o})
foo.a().b().c(#{n, o})
// Prettier 2.8
foo.a().b().c([n, o]);
foo
.a()
.b()
.c(#[n, o]);
foo.a().b().c({ n, o });
foo
.a()
.b()
.c(#{ n, o });
// Prettier 3.0
foo.a().b().c([n, o]);
foo.a().b().c(#[n, o]);
foo.a().b().c({ n, o });
foo.a().b().c(#{ n, o });
#14163 by @fisker)
Fix cursor tracking inside JSX Text (// Prettier 2.8
formatWithCursor(
["<>a", " <div>hi</div>", "</>"].join("\n"),
{ cursorOffset: 3, parser: "babel" }
).cursorOffset;
// -> 2
// Prettier 3.0
(await formatWithCursor(
["<>a", " <div>hi</div>", "</>"].join("\n"),
{ cursorOffset: 3, parser: "babel" }
)).cursorOffset;
// -> 6
await
expressions (#14192 by @thorn0)
Avoid unnecessarily indenting nested A refinement of this change in v2.3. Sometimes there is no need to force indentation of nested await
expressions.
// Prettier 2.8
await Promise.all(
(
await readdir("src")
).map((path) => {
import(`./${path}`);
})
);
// Prettier 3.0
await Promise.all(
(await readdir("src")).map((path) => {
import(`./${path}`);
}),
);
#14391 by @fisker)
Support regexp modifiers proposal (See Regular Expression Pattern Modifiers for ECMAScript.
prettier-ignore
d nodes (#14406 by @fisker)
Fix missing parentheses and semicolons around // Input
async function request(url) {
return (
// prettier-ignore
await fetch(url)
).json()
}
// Prettier 2.8
async function request(url) {
return (
// prettier-ignore
await fetch(url).json()
);
}
// Prettier 3.0
async function request(url) {
return (
// prettier-ignore
(await fetch(url)).json()
);
}
// Input
foo();
// prettier-ignore
[bar, baz].forEach(console.log)
// Prettier 2.8 (--no-semi)
foo()
// prettier-ignore
[bar, baz].forEach(console.log)
// Prettier 3.0
foo()
// prettier-ignore
;[bar, baz].forEach(console.log)
#14409 by @fisker)
Remove unnecessary parentheses around class expression (// Input
call(
@dec class {}
);
// Prettier 2.8
call(
(
@dec
class {}
)
);
// Prettier 3.0
call(
@dec
class {},
);
ExpressionStatement
instead of the whole statement (#14599 by @fisker)
Add parentheses to head of // Input
const isArray = (object) => ({}).toString.call(foo) === "[object Array]";
// Prettier 2.8
const isArray = (object) => ({}.toString.call(foo) === "[object Array]");
// Prettier 3.0
const isArray = (object) => ({}).toString.call(foo) === "[object Array]";
#14633 by @seiyab, @fisker)
Improve consistency between curried and non-curried arrow function (// Input
Y(() => a ? b : c);
Y(() => () => a ? b : c);
// Prettier 2.8
Y(() => (a ? b : c));
Y(() => () => a ? b : c);
// Prettier 3.0
Y(() => (a ? b : c));
Y(() => () => (a ? b : c));
#14736 by @solarized-fox)
Fix empty line check between array elements (// Input
[
(a = b),
c // comment
]
// Prettier 2.8
[
(a = b),
c, // comment
];
// Prettier 3.0
[
(a = b),
c, // comment
];
#14835 by @pieterv)
Support trailing comments in function parameters for all param types (Support function parameter trailing comments for RestElement
, ArrayPattern
and ObjectPattern
parameter node types.
// Input
function Foo(
...bar
// Trailing comment
) {}
// Prettier 2.8
function Foo(...bar) // Trailing comment
{}
// Prettier 3.0
function Foo(
...bar
// Trailing comment
) {}
#14861, #14863 by @sosukesuzuki)
Support Import Attributes (Support Import Attributes proposal.
import json from "./foo.json" with { type: "json" };
import("./foo.json", { with: { type: "json" } });
TypeScript
readonly
(#13427 by @thorn0, @sosukesuzuki)
Fix leading comments in mapped types with // Input
type Type = {
// comment
readonly [key in Foo];
};
// Prettier 2.8
type Type = {
readonly // comment
[key in Foo];
};
// Prettier 3.0
type Type = {
// comment
readonly [key in Foo];
};
#13608 by @sosukesuzuki)
Consistent dangling comments formatting for tuple types and arrays (// Input
type Foo = [
// comment
];
const bar = [
// comment
];
// Prettier 2.8
type Foo = [// comment];
const bar = [
// comment
];
// Prettier 3.0
type Foo = [
// comment
];
const bar = [
// comment
];
#13860 by @PerfectPan)
Fix union type should be printed in the multi-line variant when there are comments (// Input
type FooBar =
| Number // this documents the first option
| void // this documents the second option
;
// Prettier 2.8
type FooBar = Number | void; // this documents the first option // this documents the second option
// Prettier 3.0
type FooBar =
| Number // this documents the first option
| void; // this documents the second option
#14171 by @fisker)
Improve comment print and cursor tracking around type annotation (// Input
let foo /* comment */ : number;
// Prettier 2.8
let foo: /* comment */ number;
// Prettier 3.0
<Same as input>
// Prettier 2.8
prettier.formatWithCursor("let foo: number", {
cursorOffset: 7,
parser: "babel",
}).cursorOffset;
// -> 9
// Prettier 3.0
(
await prettier.formatWithCursor("let foo: number", {
cursorOffset: 7,
parser: "babel",
})
).cursorOffset;
// -> 7
#14402 by @seiyab)
Break on TypeScript parameter properties (// Input
class MyClass {
constructor(
protected x: number,
private y: string
) {}
}
// Prettier 2.8
class MyClass {
constructor(protected x: number, private y: string) {}
}
// Prettier 3.0
class MyClass {
constructor(
protected x: number,
private y: string,
) {}
}
#14654 by @fisker and @auvred)
Fix formatting of union type with single type (// Input
type T =
| (
| {
value: number
}
| {
value: string
}
)
// Prettier 2.8
type T =
|
| {
value: number;
}
| {
value: string;
};
// Prettier 3.0
type T =
| {
value: number;
}
| {
value: string;
};
#14659 by @fisker)
Improve new line detection in mapped type (// Input
type A1 = { [A in B]:
T}
type A2 = {
[A in B]:T}
// Prettier 2.8
type A1 = {
[A in B]: T;
};
type A2 = {
[A in B]: T;
};
// Prettier 3.0
type A1 = { [A in B]: T };
type A2 = {
[A in B]: T;
};
extends
in type parameters (#14672, #14858 by @sosukesuzuki)
Line breaking after // Input
export type OuterType2<
LongerLongerLongerLongerInnerType extends LongerLongerLongerLongerLongerLongerLongerLongerOtherType
> = { a: 1 };
// Prettier 2.8
export type OuterType2<
LongerLongerLongerLongerInnerType extends LongerLongerLongerLongerLongerLongerLongerLongerOtherType
> = { a: 1 };
// Prettier 3.0
export type OuterType2<
LongerLongerLongerLongerInnerType extends
LongerLongerLongerLongerLongerLongerLongerLongerOtherType,
> = { a: 1 };
#14688 by @fisker, @sosukesuzuki)
Fix missing required comma in type parameters (Previously, we only print trailing comma when file extension is .tsx
, turns out .mts
, .cts
files requires it to parse too.
// Input
export const unsafeCoerce = <T,>(u: unknown): T => u as T
// Prettier 2.8
export const unsafeCoerce = <T>(u: unknown): T => u as T;
// Prettier 3.0
export const unsafeCoerce = <T,>(u: unknown): T => u as T;
TSInstantiationExpression
followed by a property access (#14701 by @morsko1)
Keep parentheses around // Input
(Array<string>).a;
(Array<string>)?.a;
(Array<string>)[a];
(Array<string>)?.[a];
// Prettier 2.8
Array<string>.a;
Array<string>?.a;
Array<string>[a];
Array<string>?.[a];
// Prettier 3.0
(Array<string>).a;
(Array<string>)?.a;
(Array<string>)[a];
(Array<string>)?.[a];
// prettier-ignore
on a call signature line (#14830 by @ot07)
Fix issue with double semicolon caused by // Input
type Foo = {
(): void; // prettier-ignore
second: string;
};
// Prettier 2.8
type Foo = {
(): void;; // prettier-ignore
second: string;
};
// Prettier 3.0
type Foo = {
(): void; // prettier-ignore
second: string;
};
Flow
declare function
signature now breaks before the return type (#13396 by @thorn0)
An object type in a This behavior has been unified with how TypeScript is formatted.
// Input
declare function bla (props: { a: boolean, b: string, c: number }): Promise<Array<foo>>
// Prettier 2.8
declare function bla(props: { a: boolean, b: string, c: number }): Promise<
Array<foo>
>;
// Prettier 3.0
declare function bla(props: {
a: boolean;
b: string;
c: number;
}): Promise<Array<foo>>;
#14573 by @SamChou19815)
Support conditional type and infer type (// Input
type TestReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
// Prettier 2.8
// Does not parse
// Prettier 3.0
type TestReturnType<T extends (...args: any[]) => any> = T extends (
...args: any[]
) => infer R
? R
: any;
#14619 by @jbrown215)
Support Mapped Types and keyof (// Input
type Mapped = {[key in keyof O]:number};
// Prettier 2.8
// Does not parse
// Prettier 3.0
type Mapped = { [key in keyof O]: number };
#14767 by @panagosg7)
Support type guards (// Input
function isString (x: mixed): x is string { return typeof x === "string"; }
// Prettier 2.8
// Does not parse
// Prettier 3.0
function isString(x: mixed): x is string {
return typeof x === 'string';
}
CSS
#9209 by @fisker)
Improve custom properties format (Thanks to PostCSS 8.0
, we can handle these edge cases on custom properties.
/* Input */
:root {
--empty: ;
--JSON: [1, "2", {"three": {"a":1}}, [4]];
--javascript: function(rule) { console.log(rule) };
}
@supports (--element(".minwidth", { "minWidth": 300 })) {
[--self] {
background: greenyellow;
}
}
/* Prettier 2.8 */
SyntaxError: (postcss) CssSyntaxError Missed semicolon (3:20)
1 | :root {
2 | --empty: ;
> 3 | --JSON: [1, "2", {"three": {"a":1}}, [4]];
| ^
4 | --javascript: function(rule) { console.log(rule) };
5 | }
6 |
/* Prettier 3.0 */
:root {
--empty: ;
--JSON: [1, "2", {"three": {"a": 1}}, [4]];
--javascript: function(rule) {console.log(rule)};
}
@supports (--element(".minwidth", {"minWidth": 300})) {
[--self] {
background: greenyellow;
}
}
var
function (#13402 by @sosukesuzuki)
Keep trailing-comma for /* Input */
.foo {
--bar: var(--baz,);
}
/* Prettier 2.8 */
.foo {
--bar: var(--baz);
}
/* Prettier 3.0 */
.foo {
--bar: var(--baz,);
}
#14208 by @mvorisek)
Fix line break in CSS declaration with comma (// Input
.myclass {
box-shadow:
inset 0 0 10px #555,
0 0 20px black;
}
// Prettier 2.8
.myclass {
box-shadow: inset 0 0 10px #555, 0 0 20px black;
}
// Prettier 3.0
.myclass {
box-shadow:
inset 0 0 10px #555,
0 0 20px black;
}
#14476 by @seiyab)
Fix url contains comma (/* Input */
@font-face {
src: url(RobotoFlex-VariableFont_GRAD,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght.ttf);
}
/* Prettier 2.8 */
@font-face {
src: url(RobotoFlex-VariableFont_GRADXTRAYOPQYTASYTDEYTFIYTLCYTUCopszslntwdthwght.ttf);
}
/* Prettier 3.0 */
@font-face {
src: url(RobotoFlex-VariableFont_GRAD,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght.ttf);
}
SCSS
\
(#13487 by @sosukesuzuki)
Fix formatting string value that includes escape /* Input */
$description: "Lorem ipsum dolor sit \"amet\", consectetur adipiscing elit, " +
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
/* Prettier 2.8 */
$description: 'Lorem ipsum dolor sit "amet", consectetur adipiscing elit, '+ "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
/* Prettier 3.0 */
$description: 'Lorem ipsum dolor sit "amet", consectetur adipiscing elit, ' +
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
Less
#11343 by @fisker)
Fix interpolation parse error (// Input
@{selector}-title{ @{prop}-size: @{color} }
// Prettier 2.8
SyntaxError: CssSyntaxError: Unknown word (1:20)
> 1 | @{selector}-title{ @{prop}-size: @{color} }
// Prettier 3.0
@{selector}-title {
@{prop}-size: @{color};
}
#14109 by @fisker)
Keep inline JavaScript code as it is (// Input
.calcPxMixin() {
@functions: ~`(function() {
const designWidth = 3840
const actualWidth = 5760
this.calcPx = function(_) {
return _ * actualWidth / designWidth + 'px'
}
})()`;
}
// Prettier 2.8
.calcPxMixin() {
@functions: ~`(
function() {const designWidth = 3840 const actualWidth = 5760 this.calcPx =
function(_) {return _ * actualWidth / designWidth + "px"}}
)
() `;
}
// Prettier 3.0
<Same as input>
HTML
doctype
in lowercase (#7391 by @fisker)
Print HTML5 <!-- Input -->
<!DocType html>
<html><head></head><body></body></html>
<!-- Prettier 2.8 -->
<!DOCTYPE html>
<html>
<head></head>
<body></body>
</html>
<!-- Prettier 3.0 -->
<!doctype html>
<html>
<head></head>
<body></body>
</html>
#13578 by @thorn0)
Update angular-html-parser (Prettier's fork of Angular's HTML parser was synced with the upstream.
<script>
inside SVG (#14400 by @fisker)
Format <!-- Input -->
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<script>
document.addEventListener(
'DOMContentLoaded', () => {
const element = document.getElementById('foo')
if (element) {
element.fillStyle = 'currentColor'
}
});
</script>
</svg>
<!-- Prettier 2.8 -->
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<script>
document.addEventListener( 'DOMContentLoaded', () => { const element =
document.getElementById('foo') if (element) { element.fillStyle =
'currentColor' } });
</script>
</svg>
<!-- Prettier 3.0 -->
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<script>
document.addEventListener("DOMContentLoaded", () => {
const element = document.getElementById("foo");
if (element) {
element.fillStyle = "currentColor";
}
});
</script>
</svg>
<search>
element (#14615 by @fisker)
Recognize HTML spec added <search>
element.
<!-- Input -->
<SEARCH title="Website">
...
</SEARCH>
<!-- Prettier 2.8 -->
<SEARCH title="Website"> ... </SEARCH>
<!-- Prettier 3.0 -->
<search title="Website">...</search>
Vue
htmlWhitespaceSensitivity
when formatting Vue SFC root blocks (#14401 by @fisker)
Ignore <!-- Input -->
<docs lang=unknown></docs><docs lang=unknown></docs><!-- display: inline --><docs lang=unknown></docs><docs lang=unknown style="display: inline"></docs>
<!-- Prettier 2.8 (--html-whitespace-sensitivity=strict) -->
<docs lang="unknown"></docs>><docs lang="unknown"></docs
><!-- display: inline --><docs lang="unknown"></docs
>><docs lang="unknown" style="display: inline"></docs>
<!-- Prettier 3.0 -->
<docs lang="unknown"></docs>
<docs lang="unknown"></docs>
<!-- display: inline -->
<docs lang="unknown"></docs>
<docs lang="unknown" style="display: inline"></docs>
#14506 by @seiyab)
Format TypeScript expression in attribute bindings (<!-- Input -->
<script lang="ts"></script>
<template>
<comp :foo=" (a:string)=>1"/>
</template>
<!-- Prettier 2.8 -->
<script lang="ts"></script>
<template>
<comp :foo=" (a:string)=>1" />
</template>
<!-- Prettier 3.0 -->
<script lang="ts"></script>
<template>
<comp :foo="(a: string) => 1" />
</template>
#14542 by @fisker)
Fix Vue filter detection (<!-- Input -->
<template>
<div>
{{
fn(
bitwise | or | operator | a_long_long_long_long_long_long_long_long_long_long_variable
)
| filter1
| filter2
| filter3
| filter4
}}
</div>
</template>
<!-- Prettier 2.8 -->
<template>
<div>
{{
fn(
bitwise
| or
| operator
| a_long_long_long_long_long_long_long_long_long_long_variable
)
| filter1
| filter2
| filter3
| filter4
}}
</div>
</template>
<!-- Prettier 3.0 -->
<template>
<div>
{{
fn(
bitwise |
or |
operator |
a_long_long_long_long_long_long_long_long_long_long_variable,
)
| filter1
| filter2
| filter3
| filter4
}}
</div>
</template>
#14557 by @fisker)
Avoid unnecessary leading semicolon (<!-- Input -->
<template>
<div @click="[foo, bar].forEach(fn => void fn())"></div>
</template>
<!-- Prettier 2.8 (With `--no-semi` option) -->
<template>
<div @click=";[foo, bar].forEach((fn) => void fn())"></div>
</template>
<!-- Prettier 3.0 -->
<template>
<div @click="[foo, bar].forEach((fn) => void fn())"></div>
</template>
lang="ts"
(#14587 by @seiyab)
Format TS expressions when any script tag has <!-- Input -->
<script></script>
<script setup lang="ts"></script>
<template>
{{ (x as number).toFixed(2) }}
</template>
<!-- Prettier 2.8 -->
<script></script>
<script setup lang="ts"></script>
<template>
{{ (x as number).toFixed(2) }}
</template>
<!-- Prettier 3.0 -->
<script></script>
<script setup lang="ts"></script>
<template>
{{ (x as number).toFixed(2) }}
</template>
Angular
@angular/compiler
to v14 (#13609 by @fisker)
Update - Support shorthand object
- Drop support for quote expressions
<!-- Input -->
<div [input]="{a, b : 2 }"></div>
<!-- Prettier 2.8 -->
Error: Cannot find front char /:/ from index 0 in "{a, b : 2 }"
<!-- Prettier 3.0 -->
<div [input]="{ a, b: 2 }"></div>
<!-- Input -->
<a [href]="http://google.com">Click me</a>
<!-- Prettier 2.8 -->
<a [href]="http: //google.com">Click me</a>
<!-- Prettier 3.0 -->
<a [href]="http://google.com">Click me</a>
#14216 by @thron0)
Fix parentheses with nullish coalescing operator (<!-- Input -->
<img [src]="(x && y) ?? z" />
<!-- Prettier 2.8 -->
<img [src]="x && y ?? z" />
<!-- Prettier 3.0 -->
<img [src]="(x && y) ?? z" />
#14658 by @fisker)
Support computed optional chaining (<!-- Input -->
<img [src]=" a?.[0]" />
<!-- Prettier 2.8 -->
<img [src]=" a?.[0]" />
<!-- Prettier 3.0 -->
<img [src]="a?.[0]" />
#14961 by @waterplea)
Remove space after pipe name (We introduced a new format for pipe in Prettier 2.8, but this was not accepted by the community.
Therefore, we are introducing a new format that reflects community input.
For more information on the discussion, please see https://github.com/prettier/prettier/issues/13887.
<!-- Input -->
<my-component
[value]="value | transform: arg1 : arg2 | format: arg3 : arg4"
></my-component>
<!-- Prettier 2.8 -->
<my-component
[value]="value | transform : arg1 : arg2 | format : arg3 : arg4"
></my-component>
<!-- Prettier 3.0 -->
<my-component
[value]="value | transform: arg1 : arg2 | format: arg3 : arg4"
></my-component>
Markdown
#13590 by @kachkaev and @thorn0)
Preserve multiple spaces in inline code (Previously, multiple whitespace characters in inline code were collapsed into a single space. This is no longer happening to match CommonMark spec.
<!-- Input -->
` foo bar baz `
<!-- Prettier 2.8 -->
` foo bar baz `
<!-- Prettier 3.0 -->
` foo bar baz `
API
.d.ts
files (#14212 by @sosukesuzuki, @fisker)
Add Add type definition files required to use Prettier's JavaScript API from TypeScript. This eliminates the need for users to install @types/prettier
.
prettier.util
(#14317, #14320 by @fisker)
Update Added
prettier.util.getNextNonSpaceNonCommentCharacter
Changed
prettier.util.getNextNonSpaceNonCommentCharacter
Signature changed from
function getNextNonSpaceNonCommentCharacterIndex<N>( text: string, node: N, locEnd: (node: N) => number, ): number | false;
to
function getNextNonSpaceNonCommentCharacterIndex( text: string, startIndex: number, ): number | false;
Changed
prettier.util.isPreviousLineEmpty
Signature changed from
function isPreviousLineEmpty<N>( text: string, node: N, locStart: (node: N) => number, ): boolean;
to
function isPreviousLineEmpty(text: string, startIndex: number): boolean;
Changed
prettier.util.isNextLineEmpty
Signature changed from
function isNextLineEmpty<N>( text: string, node: N, locEnd: (node: N) => number, ): boolean;
to
function isNextLineEmpty(text: string, startIndex: number): boolean;
Deprecated
prettier.util.isNextLineEmptyAfterIndex
Use
prettier.util.isNextLineEmpty
instead.
See the documentation for details.
#14576 by @fisker)
Fix plugin loading cache (Plugin instances are incorrectly memoized, check this issue for details.
babel
parser (#14718 by @fisker)
Stop formatting unknown code with await prettier.format("foo")
// Prettier 2.8
No parser and no filepath given, using 'babel' the parser now but this will throw an error in the future. Please specify a parser or a filepath so one can be inferred.
'foo;\n'
// Prettier 3.0
UndefinedParserError: No parser and no file path given, couldn't infer a parser.
CLI
#11369 by @webark)
Updated failure message to be more informative (Updated the "Forgot to run Prettier?" to "Run Prettier with --write to fix."
This keeps the same spirit of the message, but is less likely to be misinterpreted as it's a more formal message rather than being somewhat familial.
--loglevel
to --log-level
(#13204 by @sosukesuzuki)
Change # Prettier 2.8
prettier test.js --loglevel=debug
# Prettier 3.0
prettier test.js --log-level=debug
--ignore-path
(#14332 by @fisker)
Accept multiple You can now pass multiple --ignore-path
.
prettier . --ignore-path=.prettier-ignore --ignore-path=.eslintignore
#14333 by @fisker)
Display posix style paths on Windows (Align with other tools like ESLint and Stylelint.
// Prettier 2.8
Checking formatting...
[warn] src\utils\create-get-visitor-keys.js
[warn] src\utils\unexpected-node-error.js
[warn] Code style issues found in 2 files. Forgot to run Prettier?
// Prettier 3.0
Checking formatting...
[warn] src/utils/create-get-visitor-keys.js
[warn] src/utils/unexpected-node-error.js
[warn] Code style issues found in 2 files. Forgot to run Prettier?
#14627 by @andersk)
Don’t expand globs via symbolic links (Prettier no longer follows symbolic links while expanding command line arguments. This avoids problems in many scenarios such as symlinks outside the source tree, symlinks to ignored files, and cycles of symlinks.
#14788 by @sosukesuzuki)
Print line breaking after file path with errors (Previously, only the --write
option printed a newline before the error, but other options and no options print a newline as well.
# Input
prettier ./test.js
# Prettier 2.8
test.js[error] test.js: SyntaxError: Unexpected token: ')' (1:6)
[error] > 1 | 1 (+-) hoge
[error] | ^
# Prettier 3.0
test.js
[error] test.js: SyntaxError: Unexpected token: ')' (1:6)
[error] > 1 | 1 (+-) hoge
[error] |
#14794 by @fisker)
Clear filename before print ignored file code to screen (# Input
echo test.js > .prettierignore
echo code > test.js
prettier ./test.js
# Prettier 2.8
test.jscode
# Prettier 3.0
code