website/blog/2020-08-24-2.1.0.md
This release adds a new --embedded-language-formatting option, supports new JavaScript/TypeScript features, and includes many bug fixes and improvements!
When Prettier identifies cases where it looks like you've placed some code it knows how to format within a string in another file, like in a tagged template in JavaScript with a tag named html or in code blocks in Markdown, it will try to format that code by default.
Sometimes this behavior is undesirable, since it can change the behavior of your code. This option allows you to switch between the default behavior (auto) and disabling this feature entirely (off). It applies to all languages where Prettier recognizes embedded code, not just JavaScript.
// Input
html`
<p>
I am expecting this to come out exactly like it went in.
`;
// using --embedded-language-formatting=auto (or omitting this option)
html`
<p>
I am expecting this to come out exactly like it went in.
</p>
`;
// using --embedded-language-formatting=off
html`
<p>
I am expecting this to come out exactly like it went in.
`;
// Input
type Range = [start: number, end: number];
// Prettier 2.0
SyntaxError: Unexpected token, expected "," (1:20)
> 1 | type Range = [start: number, end: number];
// Prettier 2.1
type Range = [start: number, end: number];
// Input
a ||= b;
// Prettier 2.0
SyntaxError: Expression expected. (1:5)
> 1 | a ||= b;
// Prettier 2.1
a ||= b;
// Input
try {} catch (e: any) {}
// Prettier 2.0
try {
} catch (e) {}
// Prettier 2.1
try {
} catch (e: any) {}
|>) Proposal?F#-style Pipeline:
<!-- prettier-ignore -->// Input
promises |> await;
// Output (Prettier 2.0)
SyntaxError: Unexpected token (1:18)
> 1 | promises |> await;
| ^
// Output (Prettier 2.1)
promises |> await;
Smart Pipeline:
<!-- prettier-ignore -->// Input
5 |> # * 2
// Output (Prettier 2.0)
SyntaxError: Unexpected character '#' (1:6)
> 1 | 5 |> # * 2
| ^
// Output (Prettier 2.1)
5 |> # * 2
If a comments line had trailing whitespaces, comments were not detected as end-of-line.
<!-- prettier-ignore -->// Input
var a = { /* extra whitespace --> */
b };
var a = { /* no whitespace --> */
b };
// Prettier 2.0
var a = {
/* extra whitespace --> */
b,
};
var a = {
/* no whitespace --> */ b,
};
// Prettier 2.1
var a = {
/* extra whitespace --> */ b,
};
var a = {
/* no whitespace --> */ b,
};
// Input
const SingleConcat = styled.div`
${something()}
& > ${Child}:not(:first-child) {
margin-left:5px;
}
`
const MultiConcats = styled.div`
${something()}
& > ${Child}${Child2}:not(:first-child) {
margin-left:5px;
}
`
const SeparatedConcats = styled.div`
font-family: "${a}", "${b}";
`
// Prettier 2.0 -- same as input
// Prettier 2.1
const SingleConcat = styled.div`
${something()}
& > ${Child}:not(:first-child) {
margin-left: 5px;
}
`;
const MultiConcats = styled.div`
${something()}
& > ${Child}${Child2}:not(:first-child) {
margin-left: 5px;
}
`;
const SeparatedConcats = styled.div`
font-family: "${a}", "${b}";
`;
// Input
const foo = {
// prettier-ignore
bar: "baz"
}
// Prettier 2.0
const foo = {
// prettier-ignore
bar: "baz"
};
// Prettier 2.1
const foo = {
// prettier-ignore
bar: "baz",
};
// Input
const a = {
"\u2139": 'why "\\u2139" converted to "i"?',
};
// Prettier 2.0
const a = {
ℹ: 'why "\\u2139" converted to "i"?',
};
// Prettier 2.1
const a = {
"\u2139": 'why "\\u2139" is converted to "i"?',
};
// Input
css`
color: var(--global--color--${props.color});
`
// Prettier 2.0
css`
color: var(--global--color-- ${props.color});
`;
// Prettier 2.1
css`
color: var(--global--color--${props.color});
`;
extends part of class declarations/expressions (#8312 by @thorn0)// Input
class a extends a // comment
{
constructor() {}
}
// Prettier 2.0
class a extends a // comment {
constructor() {}
}
// Prettier 2.1
class a extends a { // comment
constructor() {}
}
test.each template strings (#8354 by @yogmel)// Input
test.each`
a | b | c
${1} | ${[{ start: 1, end: 3 },{ start: 15, end: 20 },]} | ${[]}
`("example test", ({a, b, c}) => {})
// Prettier 2.0
test.each`
a | b | c
${1} | ${[
{ start: 1, end: 3 },
{ start: 15, end: 20 }
]} | ${[]}
`("example test", ({ a, b, c }) => {});
// Prettier 2.1
test.each`
a | b | c
${1} | ${[{ start: 1, end: 3 }, { start: 15, end: 20 }]} | ${[]}
`("example test", ({ a, b, c }) => {});
insertPragma and requirePragma in files with shebang (#8376 by @fisker)// `--insert-pragma`
// Input
#!/usr/bin/env node
hello
.world();
// Prettier 2.0
SyntaxError: Unexpected token (3:1)
1 | /** @format */
2 |
> 3 | #!/usr/bin/env node
| ^
4 | hello
5 | .world();
// Prettier 2.1
#!/usr/bin/env node
/** @format */
hello.world();
// `--require-pragma`
// Input
#!/usr/bin/env node
/**
* @format
*/
hello
.world();
// Prettier 2.0
#!/usr/bin/env node
/**
* @format
*/
hello
.world();
// Prettier 2.1
#!/usr/bin/env node
/**
* @format
*/
hello.world();
> echo -e "export default class Foo{\n/**/\n}" | prettier --range-start 16 --range-end 31 --parser babel
// Prettier 2.0
export default class Foo {
/**/
}
// Prettier 2.1
export default class Foo {
/**/
}
Not all statement types were detected (read here how range formatting works in Prettier).
<!-- prettier-ignore -->// Input
for (const element of list) { /* ... */ }
// ^^^^^^^^^^^^^^^^^^^^^^ ← range
// Prettier 2.0
for (const element of list) { /* ... */ }
// Prettier 2.1
for (const element of list) {
/* ... */
}
in (#8431 by @sosukesuzuki)Support Stage-2 proposal Private Fields in in.
// Input
#prop in obj;
// Prettier 2.0
SyntaxError: Unexpected token (1:1)
> 1 | #prop in obj;
| ^
2 |
// Prettier 2.1
#prop in obj;
Support Stage-2 proposal ES Module Attributes and JSON modules.
<!-- prettier-ignore -->// Input
import foo from "foo.json" with type: "json";
// Prettier 2.0
SyntaxError: Unexpected token, expected ";" (1:28)
> 1 | import foo from "foo.json" with type: "json";
| ^
// Prettier 2.1
import foo from "foo.json" with type: "json";
Support Stage-2 proposal JavaScript Records & Tuples Proposal.
Only support #[]/#{} syntax, not {| |} / [| |].
Tuples
<!-- prettier-ignore -->// Input
#[1, 2, 3]
// Prettier 2.0
SyntaxError: Unexpected token (1:1)
> 1 | #[1, 2, 3]
| ^
// Prettier 2.1
#[1, 2, 3];
Records
<!-- prettier-ignore -->// Input
#{
a: 1,
b: 2,
c: 3,
}
// Prettier 2.0
SyntaxError: Unexpected token (1:1)
> 1 | #{
| ^
2 | a: 1,
3 | b: 2,
4 | c: 3,
// Prettier 2.1
#{
a: 1,
b: 2,
c: 3,
};
// Input
(<div/>) < 5;
// Prettier 2.0
<div/> < 5;
// Prettier 2.0 second output
SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>? (1:9)
> 1 | <div /> < 5;
| ^
2 |
// Prettier 2.1
(<div />) < 5;
// Input
a +
a + // comment
a;
// Prettier 2.0
a +
a + // comment
a;
// Prettier 2.1
a +
a + // comment
a;
// Input
Math.min(
(
/* foo */
document.body.scrollHeight -
(window.scrollY + window.innerHeight)
) - devsite_footer_height,
0,
)
// Prettier 2.0 (first output)
Math.min(
/* foo */
document.body.scrollHeight -
(window.scrollY + window.innerHeight) -
devsite_footer_height,
0
);
// Prettier 2.0 (second output)
Math.min(
/* foo */
document.body.scrollHeight -
(window.scrollY + window.innerHeight) -
devsite_footer_height,
0
);
// Prettier 2.1 (first and second outputs)
Math.min(
/* foo */
document.body.scrollHeight -
(window.scrollY + window.innerHeight) -
devsite_footer_height,
0
);
// Input
const topOfDescriptionBox =
Layout.window.width + // Images are 1:1 aspect ratio, full screen width
Layout.headerHeight;
// Prettier 2.0 (first output)
const topOfDescriptionBox =
Layout.window.width + Layout.headerHeight; // Images are 1:1 aspect ratio, full screen width
// Prettier 2.0 (second output)
const topOfDescriptionBox = Layout.window.width + Layout.headerHeight; // Images are 1:1 aspect ratio, full screen width
// Prettier 2.1 (first and second outputs)
const topOfDescriptionBox =
Layout.window.width + // Images are 1:1 aspect ratio, full screen width
Layout.headerHeight;
Prettier removes quotes from object keys if they are identifiers. Now, Prettier also removes quotes from object keys that are numbers.
If you use quoteProps: "consistent", Prettier can also add quotes to number keys so that all properties end up with quotes.
// Input
x = {
"a": null,
"1": null,
};
// Prettier 2.0
x = {
a: null,
"1": null,
};
// Prettier 2.1
x = {
a: null,
1: null,
};
Prettier only touches “simple” numbers such as 1 and 123.5. It won’t make the following transformations, as they feel unexpected:
1e2 -> "100"
0b10 -> "10"
1_000 -> "1000"
1.0 -> "1"
0.99999999999999999 -> "1"
999999999999999999999 -> "1e+21"
2n -> "2"
"1e+100" -> 1e100
(Please don’t use confusing numbers as object keys!)
Note that Prettier only unquotes numbers using the "babel" parser. It’s not completely safe to do so in TypeScript.
// Input
type A = B extends T
? // comment
foo
: bar;
// Prettier 2.0
type A = B extends T // comment
? foo
: bar;
// Prettier 2.1
type A = B extends T
? // comment
foo
: bar;
// Input
test
? /* comment
comment
*/
foo
: bar;
// Prettier 2.0
test ? /* comment
comment
*/ foo : bar;
// Prettier 2.1
test
? /* comment
comment
*/
foo
: bar;
// Input
class C {
ma() {} /* D */ /* E */
mb() {}
}
// Prettier 2.0
class C {
ma() {} /* E */ /* D */
mb() {}
}
// Prettier 2.0 (Second format)
class C {
ma() {} /* D */ /* E */
mb() {}
}
// Prettier 2.1
class C {
ma() {} /* D */ /* E */
mb() {}
}
prettier-ignore comments (#8742 by @fisker)// Input
a = <div {.../* prettier-ignore */{}}/>
a = <div {...{}/* prettier-ignore */}/>
// Prettier 2.0
a = <div {/* prettier-ignore */ .../* prettier-ignore */ {}} />;
a = <div {...{} /* prettier-ignore */ /* prettier-ignore */} />;
// Prettier 2.1
a = <div {.../* prettier-ignore */ {}} />;
a = <div {...{} /* prettier-ignore */} />;
Support Stage-1 proposal Decimal Proposal.
<!-- prettier-ignore -->// Input
0.3m;
// Prettier 2.0
SyntaxError: Identifier directly after number (1:4)
> 1 | 0.3m;
// Prettier 2.1
0.3m;
In v2.0.0 we removed the parens when yielding jsx, this works for most parsers, but ESLint throws when parsing it, related issue.
// Input
function* f() {
yield <div>generator</div>
}
// Prettier 2.0
function* f() {
yield <div>generator</div>
}
// Prettier 2.1
function* f() {
yield (<div>generator</div>);
}
babel-ts parser so that it emits a proper syntax error for (a:b) (#8046 by @thorn0)Previously, such code was parsed without errors, but an error was thrown in the printer, and this error was printed in an unfriendly way, with a stack trace. Now a proper syntax error is thrown by the parser.
<!-- prettier-ignore -->// Input
(a:b)
// Prettier 2.0
[error] test.ts: Error: unknown type: "TSTypeCastExpression"
[error] ... [a long stack trace here] ...
// Prettier 2.1
[error] test.ts: SyntaxError: Did not expect a type annotation here. (1:2)
[error] > 1 | (a:b)
[error] | ^
[error] 2 |
Prettier wraps any string literal which is in statement position in parentheses because otherwise such strings would get interpreted as directives if they occurred at the top of a function or program, which can change the behavior of the program. It is not technically necessary to do this for strings which are not on the first line of a function or program. But doing so anyway is more consistent and can highlight bugs. See for example this thread on twitter.
Previously this didn't consistently work for the typescript parser. Only strings already wrapped in parentheses retained them.
// Input
f();
'use foo';
('use bar');
// Prettier 2.0
f();
"use foo";
("use bar");
// Prettier 2.1
f();
("use foo");
("use bar");
See https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#breaking-changes
<!-- prettier-ignore -->// Input
(a?.b)!.c;
// Prettier 2.0
a?.b!.c;
// Prettier 2.1
(a?.b)!.c;
// Input
let f: <A>(
((?A) => B),
) => B;
// Prettier 2.0
let f: <A>((?A) => B) => B;
// Prettier 2.0 (Second format)
SyntaxError: Unexpected token (1:12)
> 1 | let f: <A>((?A) => B) => B;
// Prettier 2.1
let f: <A>(((?A) => B)) => B;
Do not add a trailing semicolon if default exporting a Flow Enum.
<!-- prettier-ignore -->// Input
export default enum B {}
// Prettier 2.0
export default enum B {};
// Prettier 2.1
export default enum B {}
/* Input */
@at-root .foo
// .bar
{
}
/* Prettier 2.0 */
@at-root .foo
// .bar {
}
/* Prettier 2.1 */
@at-root .foo
// .bar
{
}
Improve the handling of unquoted URL content in CSS/SCSS/Less. This doesn't address the underlying parsing issues, but at least ensures Prettier doesn't modify URLs.
<!-- prettier-ignore -->/* Input */
@import url(https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900&display=swap);
@import url(//fonts.googleapis.com/css?family=#{ get-font-family('Roboto') }:100,300,500,700,900&display=swap);
.validUnquotedUrls{
background: url(data/+0ThisShouldNotBeLowerCased);
background: url(https://foo/A*3I8oSY6AKRMAAAAAAAAAAABkARQnAQ);
background: url(https://example.com/some/quite,long,url,with,commas.jpg);
}
/* Prettier 2.0 */
@import url(
https://fonts.googleapis.com/css?family=Roboto:100,
300,
400,
500,
700,
900&display=swap
);
@import url(
//fonts.googleapis.com/css?family=#{get-font-family("Roboto")}:100,
300,
500,
700,
900&display=swap
);
.validUnquotedUrls {
background: url(data/+0thisshouldnotbelowercased);
background: url(https://foo/A*3i8osy6akrmaaaaaaaaaaabkarqnaq);
background: url(https://example.com/some/quite, long, url, with, commas.jpg);
}
/* Prettier 2.1 */
@import url(https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900&display=swap);
@import url(//fonts.googleapis.com/css?family=#{ get-font-family('Roboto') }:100,300,500,700,900&display=swap);
.validUnquotedUrls{
background: url(data/+0ThisShouldNotBeLowerCased);
background: url(https://foo/A*3I8oSY6AKRMAAAAAAAAAAABkARQnAQ);
background: url(https://example.com/some/quite,long,url,with,commas.jpg);
}
/* Input */
.grid {
grid-template-rows:
[row-1-00\:00] auto;
}
/* Prettier 2.0 */
.grid {
grid-template-rows: [row-1-00\: 00] auto;
}
/* Prettier 2.1 */
.grid {
grid-template-rows: [row-1-00\:00] auto;
}
/* Input */
@supports selector(:focus-visible) {
button:focus {
outline: none;
}
button:focus-visible {
outline: 2px solid orange;
}
}
/* Prettier 2.0 */
@supports selector(: focus-visible) {
button:focus {
outline: none;
}
button:focus-visible {
outline: 2px solid orange;
}
}
/* Prettier 2.1 */
@supports selector(:focus-visible) {
button:focus {
outline: none;
}
button:focus-visible {
outline: 2px solid orange;
}
}
Prettier no longer unexpectedly add extra space after function when passing arbitrary arguments. Prettier no longer breaks code when inline number lists are used as arbitrary arguments.
<!-- prettier-ignore -->/* Input */
body {
test: function($list...);
foo: bar(returns-list($list)...);
background-color: rgba(50 50 50 50...);
}
/* Prettier 2.0 */
body {
test: function($list...);
foo: bar(returns-list($list) ...);
background-color: rgba(50 50 50 50..);
}
/* Prettier 2.1 */
body {
test: function($list...);
foo: bar(returns-list($list)...);
background-color: rgba(50 50 50 50...);
}
Previously, Prettier would place extra indentation of lines following comments within SCSS maps Prettier now no longer places these indentations
<!-- prettier-ignore -->/* Input */
$my-map: (
'foo': 1, // Foo
'bar': 2, // Bar
);
/* Prettier 2.0 */
$my-map: (
"foo": 1,
// Foo
"bar": 2,
// Bar
);
/* Prettier 2.1 */
$my-map: (
"foo": 1,
// Foo
"bar": 2,
// Bar
);
// Input
a {
color: // comment
red;
}
// Prettier 2.0
a {
color: // comment red;
}
// Prettier 2.1
a {
color: // comment
red;
}
// Input
@mixin foo() {
a {
color: #f99;
}
}
@include foo() /* comment*/
// Prettier 2.0
@mixin foo() {
a {
color: #f99;
}
}
@include foo();
// Prettier 2.1
@mixin foo() {
a {
color: #f99;
}
}
@include foo(); /* comment*/
:extend pseudo-class (#8178 by @fisker)The selector is parsed as value before, now it's recognized as selector.
<!-- prettier-ignore -->// Input
.hello {
&:extend(.input[type="checkbox"]:checked ~ label)}
// Prettier 2.0
.hello {
&:extend(.input[type= "checkbox" ]: checked ~label);
}
// Prettier 2.1
.hello {
&:extend(.input[type="checkbox"]:checked ~ label);
}
/* (#8360 by @fisker)When inline comments contains /*, some other comment not correctly printed.
// Input
@import "a";
// '/*' <-- this breaks formatting
@import 'b';
/* block */
/*no-space block*/
// Prettier 2.0
@import "a";
// '/*' <-- this breaks formatting
@import "b";
@import 'b
@import 'b';
/* bl
// Prettier 2.1
@import "a";
// '/*' <-- this breaks formatting
@import "b";
/* block */
/*no-space block*/
<!-- Input -->
<div style="margin: 0; padding: 20px"></div>
<div style="margin: 0; padding: 20px; position: relative; display: inline-block; color: blue"></div>
<!-- Prettier 2.0 -->
<div style="margin: 0; padding: 20px;"></div>
<div
style="
margin: 0;
padding: 20px;
position: relative;
display: inline-block;
color: blue;
"
></div>
<!-- Prettier 2.1 -->
<div style="margin: 0; padding: 20px"></div>
<div
style="
margin: 0;
padding: 20px;
position: relative;
display: inline-block;
color: blue;
"
></div>
Non-ASCII whitespace characters like U+00A0 U+2005 etc. are not considered whitespace in html, they should not be removed.
// Prettier 2.0
[...require("prettier").format("<i> \u2005 </i>", { parser: "html" })]
.slice(3, -5)
.map((c) => c.charCodeAt(0).toString(16));
// -> [ '20' ]
// `U+2005` is removed
// Prettier 2.1
[...require("prettier").format("<i> \u2005 </i>", { parser: "html" })]
.slice(3, -5)
.map((c) => c.charCodeAt(0).toString(16));
// -> [ '20', '2005', '20' ]
Previously we parse html <script> blocks as "module"(ECMAScript Module grammar), this is why we can't parse comments starts with <!--(aka HTML-like comments), now we parse <script> blocks as "script", unless this <script>
type="module"type="text/babel" and data-type="module", introduced in [email protected]<!-- Input -->
<SCRIPT>
<!--
alert("hello" + ' world!')
//--></SCRIPT>
<!-- Prettier 2.0 -->
SyntaxError: Unexpected token (2:1)
1 |
> 2 | <!--
| ^
3 | alert("hello" + ' world!')
4 | //-->
<!-- Prettier 2.1 -->
<script>
<!--
alert("hello" + " world!");
//-->
</script>
<select> as inline-block and <optgroup>/<option> as block (#8275 by @thorn0, #8620 by @fisker)Now Prettier knows that it's safe to add whitespace inside select, option and optgroup tags.
<!-- Input -->
<select><option>Blue</option><option>Green</option><optgroup label="Darker"><option>Dark Blue</option><option>Dark Green</option></optgroup></select>
<!-- Prettier 2.0 -->
<select
><option>Blue</option
><option>Green</option
><optgroup label="Darker"
><option>Dark Blue</option><option>Dark Green</option></optgroup
></select
>
<!-- Prettier 2.1 -->
<select>
<option>Blue</option>
<option>Green</option>
<optgroup label="Darker">
<option>Dark Blue</option>
<option>Dark Green</option>
</optgroup>
</select>
<!-- Input -->
<!-- Prettier 2.0 -->
<!-- Prettier 2.1 -->
<script type="text/html> (#8371 by @sosukesuzuki)<!-- Input -->
<script type="text/html">
<div>
<p>foo</p>
</div>
</script>
<!-- Prettier 2.0 -->
<script type="text/html">
<div>
<p>foo</p>
</div>
</script>
<!-- Prettier 2.1 -->
<script type="text/html">
<div>
<p>foo</p>
</div>
</script>
Support dynamic language detection in front matter, also available for css, less, scss, and markdown parser.
<!-- Input -->
---my-awsome-language
title: Title
description: Description
---
<h1>
prettier</h1>
<!-- Prettier 2.0 -->
---my-awsome-language title: Title description: Description ---
<h1>
prettier
</h1>
<!-- Prettier 2.1 -->
---my-awsome-language
title: Title
description: Description
---
<h1>
prettier
</h1>
Previously, Prettier always preserved line breaks around inline nodes (i.e. inline elements, text, interpolations). In general, Prettier, as much as possible, tries to avoid relying on original formatting, but there are at least two cases when collapsing inline nodes into a single line is undesirable, namely list-like content and conditional constructions (e.g. v-if/v-else in Vue). A good way to detect those cases couldn't be found, so such a trade-off was made. It turned out, however, for text-only content, this relaxation of rules was unnecessary and only led to confusingly inconsistent formatting.
<!-- Input -->
<div>
Hello, world!
</div>
<div>
Hello, {{ username }}!
</div>
<!-- Prettier 2.0 -->
<div>
Hello, world!
</div>
<div>Hello, {{ username }}!</div>
<!-- Prettier 2.1 -->
<div>Hello, world!</div>
<div>Hello, {{ username }}!</div>
<!-- Input -->
<div>before<details><summary>summary long long long long </summary>details</details>after</div>
<div>before<dialog open>dialog long long long long long long long long </dialog>after</div>
<div>before<object data="horse.wav"><param name="autoplay" value="true"/><param name="autoplay" value="true"/></object>after</div>
<!-- Prettier 2.0 -->
<div>
before<details><summary>summary long long long long </summary>details</details
>after
</div>
<div>
before<dialog open>dialog long long long long long long long long </dialog
>after
</div>
<div>
before<object data="horse.wav"
><param name="autoplay" value="true" /><param
name="autoplay"
value="true" /></object
>after
</div>
<!-- Prettier 2.1 -->
<div>
before
<details>
<summary>summary long long long long</summary>
details
</details>
after
</div>
<div>
before
<dialog open>dialog long long long long long long long long</dialog>
after
</div>
<div>
before<object data="horse.wav">
<param name="autoplay" value="true" />
<param name="autoplay" value="true" /></object
>after
</div>
<!-- Input -->
<video controls width="250">
<source src="/media/examples/flower.webm"
type="video/webm">
<source src="/media/examples/flower.mp4"
type="video/mp4"
></video>text after
<!-- Prettier 2.0 -->
<video controls width="250">
<source src="/media/examples/flower.webm" type="video/webm" />
<source src="/media/examples/flower.mp4" type="video/mp4" /></video
>text after
<!-- Prettier 2.1 -->
<video controls width="250">
<source src="/media/examples/flower.webm" type="video/webm" />
<source src="/media/examples/flower.mp4" type="video/mp4" /></video
>text after
Support formatting all language blocks(including custom blocks with lang attribute) with builtin parsers and plugins.
<!-- Input -->
<template lang="pug">
div.text( color = "primary", disabled ="true" )
</template>
<i18n lang="json">
{
"hello": 'prettier',}
</i18n>
<!-- Prettier 2.0 -->
<template lang="pug">
div.text( color = "primary", disabled ="true" )
</template>
<i18n lang="json">
{
"hello": 'prettier',}
</i18n>
<!-- Prettier 2.1 -->
<template lang="pug">
.text(color="primary", disabled="true")
</template>
<i18n lang="json">
{
"hello": "prettier"
}
</i18n>
@prettier/plugin-pug is required for this example.
<!-- Input -->
<custom lang="javascript">
const foo = "</";
</custom>
<!-- Prettier 2.0 -->
SyntaxError: Unexpected character """ (2:19)
[error] 1 | <custom lang="javascript">
[error] > 2 | const foo = "</";
[error] | ^
[error] 3 | </custom>
<!-- Prettier 2.1 -->
<custom lang="javascript">
const foo = "</";
</custom>
<!-- Input -->
<!doctype html><HTML></HTML>
<!-- Prettier 2.0 -->
<!DOCTYPE html>><HTML></HTML>
<!-- Prettier 2.1 -->
<!DOCTYPE html><HTML></HTML>
template in Vue SFC (#8325 by @sosukesuzuki)<!-- Input -->
<template><p>foo</p><div>foo</div></template>
<!-- Prettier 2.0 -->
<template
><p>foo</p>
<div>foo</div></template
>
<!-- Prettier 2.1 -->
<template>
<p>foo</p>
<div>foo</div>
</template>
When use vue parse for HTML, parse template as HTML.
<!-- prettier-ignore --><!-- Input -->
<!DOCTYPE html>
<html>
<body STYLE="color: #333">
<DIV id="app">
<DIV>First Line</DIV><DIV>Second Line</DIV>
</DIV>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data() {
return {}
},
});
</script>
</body>
</html>
<!-- Prettier 2.0 -->
<!DOCTYPE html>
<html>
<body STYLE="color: #333">
<DIV id="app"> <DIV>First Line</DIV><DIV>Second Line</DIV> </DIV>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data() {
return {};
},
});
</script>
</body>
</html>
<!-- Prettier 2.1 -->
<!DOCTYPE html>
<html>
<body style="color: #333">
<div id="app">
<div>First Line</div>
<div>Second Line</div>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data() {
return {};
},
});
</script>
</body>
</html>
<!-- Input -->
<!DOCTYPE html><HTML>
<body>
<div v-if="foo === 'foo'">
</div>
<script>
new Vue({el: '#app'})
</script>
</body>
</HTML>
<!-- Prettier 2.0 -->
<!DOCTYPE html>
<HTML>
<body>
<div v-if="foo === 'foo'">
</div>
<script>
new Vue({el: '#app'})
</script>
</body>
</HTML>
<!-- Prettier 2.1 -->
<!DOCTYPE html>
<HTML>
<body>
<div v-if="foo === 'foo'"></div>
<script>
new Vue({ el: "#app" });
</script>
</body>
</HTML>
<!-- Input -->
<template>
<span>{{(a|| b)}} {{z&&(a&&b)}}</span>
</template>
<!-- Prettier 2.0 -->
<template>
<span>{{(a|| b)}} {{z&&(a&&b)}}</span>
</template>
<!-- Prettier 2.1 -->
<template>
<span>{{ a || b }} {{ z && a && b }}</span>
</template>
<!-- Input -->
<template>
<div #default="{foo:{bar:{baz}}}"></div>
</template>
<!-- Prettier 2.0 -->
<template>
<div #default="{foo:{bar:{baz}}}"></div>
</template>
<!-- Prettier 2.1 -->
<template>
<div #default="{ foo: { bar: { baz } } }"></div>
</template>
this (#8253 by @thorn0, #7942 by @fisker, fixes in angular-estree-parser by @ikatyang)<!-- Input -->
{{ a?.b[c] }}
{{ a ( this )}}
<!-- Prettier 2.0 -->
{{ (a?.b)[c] }}
{{ a ( this )}}
<!-- Prettier 2.1 -->
{{ a?.b[c] }}
{{ a(this) }}
<!-- Input -->
<div ng-style="{ 'color': ('#222' | darken)}"></div>
<!-- Prettier 2.0 -->
<div ng-style="{ color: '#222' | darken }"></div>
<!-- Prettier 2.1 -->
<div ng-style="{ 'color': ('#222' | darken)}"></div>
{{!-- Input --}}
<a href='/{{url}}'></a>
<a href="/{{url}}"></a>
<a href='url'></a>
<a href="url"></a>
{{!-- Prettier 2.0 --}}
<a href="/{{url}}"></a>
<a href="/{{url}}"></a>
<a href='url'></a>
<a href='url'></a>
{{!-- Prettier 2.1 --}}
<a href='/{{url}}'></a>
<a href='/{{url}}'></a>
<a href='url'></a>
<a href='url'></a>
{{!-- Input --}}
<div>
{{classic-component-with-many-properties
class="hello"
param=this.someValue
secondParam=this.someValue
thirdParam=this.someValue
}}
</div>
{{!-- Prettier 2.0 --}}
<div>
{{
classic-component-with-many-properties
class="hello"
param=this.someValue
secondParam=this.someValue
thirdParam=this.someValue
}}
</div>
{{!-- Prettier 2.1 --}}
<div>
{{classic-component-with-many-properties
class="hello"
param=this.someValue
secondParam=this.someValue
thirdParam=this.someValue
}}
</div>
{{!-- Input --}}
\{{mustache}}
\\{{mustache}}
\\\{{mustache}}
{{!-- Prettier 2.0 --}}
{{mustache}}
\{{mustache}}
\\{{mustache}}
{{!-- Prettier 2.1 --}}
\{{mustache}}
\\{{mustache}}
\\\{{mustache}}
{{!-- Input --}}
<div class=' class '></div>
<div title=' other attribute '></div>
{{!-- Prettier 2.0 --}}
<div class="class"></div>
<div title="other attribute"></div>
{{!-- Prettier 2.1 --}}
<div class="class"></div>
<div title=" other attribute "></div>
# Input
fragment TodoList_list on TodoList @argumentDefinitions(
count: {type: "Int", defaultValue: 10},
) {
title
}
# Prettier 2.0
fragment TodoList_list on TodoList
@argumentDefinitions(count: { type: "Int", defaultValue: 10 }) {
title
}
# Prettier 2.1
fragment TodoList_list on TodoList
@argumentDefinitions(count: { type: "Int", defaultValue: 10 }) {
title
}
# Input
type Type1 implements
A &
# comment 1
B
# comment 2
& C {a: a}
# Prettier 2.0
type Type1 implements A & # comment 1
B & # comment 2
C {
a: a
}
# Prettier 2.0 (Second format)
type Type1 implements A & B & C { # comment 1 # comment 2
a: a
}
# Prettier 2.1
type Type1 implements A &
# comment 1
B &
# comment 2
C {
a: a
}
See "RFC: Allow interfaces to implement other interfaces"
<!-- prettier-ignore --># Input
interface Resource implements Node {
id: ID!
url: String
}
# Prettier 2.0
interface Resource {
id: ID!
url: String
}
# Prettier 2.1
interface Resource implements Node {
id: ID!
url: String
}
remark-parse to v8 (#8140 by @saramarcondes, @fisker, @thorn0)remark, the Markdown parser that Prettier uses, got a long-overdue update (5.0.0 → 8.0.2, see remark's changelog). This fixed tons of old bugs, in particular related to parsing list items indented with tabs.
Please note that the new version is stricter in parsing footnotes, a syntax extension not defined in any specification. Previously, Prettier would parse (and output, depending on the --tab-width option) multiline footnotes indented with any number of spaces. The new version recognizes only multiline footnotes indented with 4 spaces. This change isn't considered breaking as the syntax is non-standard, but if you happen to use it, before updating Prettier, you might want to use its older version with --tab-width=4 to make footnotes in your files compatible with the new version.
<!-- Input -->
麻󠄁羽󠄀‼️
<!-- Prettier 2.0 -->
麻 󠄁 羽 󠄀 ‼️
<!-- Prettier 2.1 -->
麻󠄁羽󠄀‼️
prettier-ignore (#8355 by @fisker)# Input
# prettier-ignore
---
prettier: true
...
hello: world
# Prettier 2.0
# prettier-ignore
---
prettier: true
---
hello: world
# Prettier 2.0 (Second format)
# prettier-ignore
---
prettier: true
---
hello: world
# Prettier 2.1
# prettier-ignore
---
prettier: true
---
hello: world
# Input
a:
- a: a
# - b: b
# - c: c
- d: d
b:
- a: a
# - b: b
# - c: c
# Prettier 2.0
a:
- a: a
# - b: b
# - c: c
- d: d
b:
- a: a
# - b: b
# - c: c
# Prettier 2.1
a:
- a: a
# - b: b
# - c: c
- d: d
b:
- a: a
# - b: b
# - c: c
yaml and yaml-unist-parser (#8386 by @fisker, fixes in yaml-unist-parser by @ikatyang)yaml: Add explicit error for block scalars with more-indented leading empty linesyaml-unist-parser: fix: no false positive for document head end marker position# Input
# --- comments ---
# Prettier 2.0
--- # --- comments ---
# Prettier 2.1
# --- comments ---
# Input
empty block scalar: >
# comment
# Prettier 2.0
empty block scalar: >
# comment
# Prettier 2.1
SyntaxError: Block scalars with more-indented leading empty lines must use an explicit indentation indicator (1:21)
> 1 | empty block scalar: >
| ^
> 2 |
| ^^
> 3 | # comment
| ^^^^^^^^^^^
yaml-unist-parser by @ikatyang)# Input
foo:
<<: &anchor
K1: "One"
K2: "Two"
bar:
<<: *anchor
K3: "Three"
# Prettier 2.0
SyntaxError: Merge nodes can only have Alias nodes as values (2:2)
1 | foo:
> 2 | <<: &anchor
| ^^^^^^^^^^^
> 3 | K1: "One"
| ^^^^^^^^^^^^
> 4 | K2: "Two"
| ^^^^^^^^^^^^
> 5 |
| ^
6 | bar:
7 | <<: *anchor
8 | K3: "Three"
# Prettier 2.1
foo:
<<: &anchor
K1: "One"
K2: "Two"
bar:
<<: *anchor
K3: "Three"
parser option (#8390 by @thorn0)When a plugin defines a language, the parsers specified for this language are now automatically added to the list of valid values of the parser option.
This might be useful for editor integrations or other applications that need a list of available parsers.
npm install --save-dev --save-exact prettier @prettier/plugin-php
const hasPhpParser = prettier
.getSupportInfo()
.options.find((option) => option.name === "parser")
.choices.map((choice) => choice.value)
.includes("php"); // false in Prettier 2.0, true in Prettier 2.1
prettier.getFileInfo() (#8548, #8551, #8585 by @fisker){resolveConfig: true}, the inferredParser should be the parser resolved from config file, previous version might returns wrong result for files supported by builtin parsers.{resolveConfig: true} and {ignorePath: "a/file/in/different/dir"}, the inferredParser result might incorrect.filePath is ignored, the inferredParser is always null now.$ echo {"parser":"flow"}>.prettierrc
$ node -p "require('prettier').getFileInfo.sync('./foo.js', {resolveConfig: true})"
# Prettier 2.0
# { ignored: false, inferredParser: 'babel' }
# Prettier 2.1
# { ignored: false, inferredParser: 'flow' }
$ echo ignored.js>>.prettierignore
$ node -p "require('prettier').getFileInfo.sync('./ignored.js')"
# Prettier 2.0
# { ignored: true, inferredParser: 'babel' }
# Prettier 2.1
# { ignored: true, inferredParser: null }
Previous version can't find .editorconfig for files in deep directory (depth great than 9 to project root, see #5705).
.cjs and .json5 configuration files (#8890, #8957 by @fisker)Added new format of configuration files:
.prettierrc.json5.prettierrc.cjsprettier.config.cjsWhen files have BOM, in previous version, the real range is wrongly calculated.
<!-- prettier-ignore -->const text = "\uFEFF" + "foo = 1.0000;bar = 1.0000;";
// ^^^^^^^^^^^^^ Range
const result = require("prettier")
.format(text, {
parser: "babel",
rangeStart: 1,
rangeEnd: 13,
})
// Visualize BOM
.replace("\uFEFF", "<<BOM>>")
// Visualize EOL
.replace("\n", "<<EOL>>");
console.log(result);
// Prettier 2.0
// -> <<BOM>>foo = 1.0;<<EOL>>bar = 1.0;
// ^^^^^^^^^ This part should not be formatted
// Prettier 2.1
// -> <<BOM>>foo = 1.0;bar = 1.0000;
// Prettier 2.0
$ echo "dir" > .prettierignore
$ prettier **/*.js -l
dir/😁.js
dir/中文.js
not-ignored.js
// Prettier 2.1
$ echo "dir" > .prettierignore
$ prettier **/*.js -l
not-ignored.js
--file-info respect the .prettierrc and --no-config (#8586, #8830 by @fisker)$ echo {"parser":"ninja"}>.prettierrc
# Prettier 2.0
$ prettier --file-info file.js
# { "ignored": false, "inferredParser": "babel" }
$ prettier --file-info file.js --no-config
# { "ignored": false, "inferredParser": "babel" }
# Prettier 2.1
$ prettier --file-info file.js
# { "ignored": false, "inferredParser": "ninja" }
$ prettier --file-info file.js --no-config
# { "ignored": false, "inferredParser": "babel" }
--ignore-unknown(alias -u) flag (#8829 by @fisker)# Prettier 2.0
npx prettier * --check
Checking formatting...
foo.unknown[error] No parser could be inferred for file: foo.unknown
All matched files use Prettier code style!
# Prettier 2.1
npx prettier * --check --ignore-unknown
Checking formatting...
All matched files use Prettier code style!
-w alias for --write option (#8833 by @fisker)# Prettier 2.0
$ prettier index.js -w
[warn] Ignored unknown option -w. Did you mean -_?
"use strict";
module.exports = require("./src/index");
# Prettier 2.1
$ prettier index.js -w
index.js 30ms
-_ for unknown options (#8934 by @fisker)# Prettier 2.0
$ prettier foo.js -a
[warn] Ignored unknown option -a. Did you mean -_?
# Prettier 2.1
$ prettier foo.js -a
[warn] Ignored unknown option -a. Did you mean -c?