diff --git a/changelog_unreleased/blog-post-intro.md b/changelog_unreleased/blog-post-intro.md new file mode 100644 index 000000000..6591cf8dc --- /dev/null +++ b/changelog_unreleased/blog-post-intro.md @@ -0,0 +1,18 @@ +--- +authors: "fisker" +title: "Prettier 3.9: New Parser Foundations and Formatting Improvements" +--- + +We are excited to announce Prettier 3.9! + +This release brings major parser upgrades for Markdown, YAML, Flow, GraphQL, and Angular, along with formatting improvements for JavaScript and TypeScript (particularly in `--no-semi` mode). + +If you find Prettier valuable, please consider [sponsoring us on OpenCollective](https://opencollective.com/prettier) or supporting the upstream projects we rely on. Your contributions help us keep improving the tool for everyone. + +Thank you for your continued support! ❤️ + +A reminder, when Prettier is installed or updated, it’s [strongly recommended](https://prettier.io/docs/install#summary) to specify the exact version in `package.json`: `"prettier": "3.9.0"`, not `"prettier": "^3.9.0"`. + +If you use [`@prettier/plugin-oxc`](https://www.npmjs.com/package/@prettier/plugin-oxc) or [`@prettier/plugin-hermes`](https://www.npmjs.com/package/@prettier/plugin-hermes), don't forget upgrade them to ensure the new formatting rules are applied. + + diff --git a/changelog_unreleased/flow/19398-2.md b/changelog_unreleased/flow/19398-2.md new file mode 100644 index 000000000..e736fe6ab --- /dev/null +++ b/changelog_unreleased/flow/19398-2.md @@ -0,0 +1,15 @@ +#### Preserve Flow comment syntax (#19398 by @SamChou19815) + +Preserves Flow comment syntax annotations instead of printing them as regular Flow syntax. + + +```flow +// Input +function foo(bar /*: T[] */, baz /*: T */) /*: S */ {} + +// Prettier stable +function foo(bar: T[], baz: T): S {} + +// Prettier main +function foo(bar /*: T[] */, baz /*: T */) /*: S */ {} +``` diff --git a/changelog_unreleased/flow/19398.md b/changelog_unreleased/flow/19398.md index 2567fa67d..18add539a 100644 --- a/changelog_unreleased/flow/19398.md +++ b/changelog_unreleased/flow/19398.md @@ -1,15 +1,5 @@ -#### Preserve Flow comment syntax and improve parser performance (#19398 by @SamChou19815) +#### [HIGHLIGHT] New Rust-based Flow parser (#19398 by @SamChou19815) -Prettier now uses Flow's Rust-backed parser. This preserves Flow comment syntax annotations instead of printing them as regular Flow syntax. In local parser-only benchmarks, `flow-parser/oxidized` parsed Prettier's valid Flow fixtures in 266.4ms median time, compared with 422.6ms for the old parser, and parsed `flow_parser.js` in 1298.0ms, compared with 2269.6ms. +Prettier now uses the new Rust-based Flow parser (oxidized) released by the Flow team, which improves performance for Flow-typed code. - -```flow -// Input -function foo(bar /*: T[] */, baz /*: T */) /*: S */ {} - -// Prettier stable -function foo(bar: T[], baz: T): S {} - -// Prettier main -function foo(bar /*: T[] */, baz /*: T */) /*: S */ {} -``` +In local parser-only benchmarks, `flow-parser/oxidized` parsed Prettier's valid Flow fixtures in 266.4ms median time, compared with 422.6ms for the old parser, and parsed `flow_parser.js` in 1298.0ms, compared with 2269.6ms. diff --git a/changelog_unreleased/graphql/19171.md b/changelog_unreleased/graphql/19171.md deleted file mode 100644 index 0e63aefb5..000000000 --- a/changelog_unreleased/graphql/19171.md +++ /dev/null @@ -1,17 +0,0 @@ -#### Support "directives on directive definitions" (#19171 by @YBJ0000) - -[Directives on directive definitions](https://github.com/graphql/graphql-js/pull/4521) (and `extend directive` extensions) are now supported. - - -```gql -# Input -directive @a @b on QUERY -extend directive @a @b - -# Prettier stable -Error: Syntax Error: Expected "on", found "@". - -# Prettier main -directive @a @b on QUERY -extend directive @a @b -``` diff --git a/changelog_unreleased/graphql/19297.md b/changelog_unreleased/graphql/19297.md index d77428016..bb547e579 100644 --- a/changelog_unreleased/graphql/19297.md +++ b/changelog_unreleased/graphql/19297.md @@ -1,6 +1,8 @@ -#### Support "Fragment arguments" (#19297 by @fisker) +#### [HIGHLIGHT] Support GraphQL.js v17 (#19171 by @YBJ0000, #19297 by @fisker) -[Fragment arguments](https://github.com/graphql/graphql-js/pull/4015) are now supported. +Prettier now fully supports newer syntax features from GraphQL.js v17, including directives on directive definitions, fragment arguments, and other enhancements. + +[Fragment arguments](https://github.com/graphql/graphql-js/pull/4015) ```gql @@ -11,13 +13,25 @@ fragment variableProfilePic on User { # Prettier stable SyntaxError: Syntax Error: Expected Name, found "(". (2:23) - 1 | fragment variableProfilePic on User { -> 2 | ...dynamicProfilePic(size: $size) - | ^ - 3 | } # Prettier main fragment variableProfilePic on User { ...dynamicProfilePic(size: $size) } ``` + +[Directives on directive definitions](https://github.com/graphql/graphql-js/pull/4521) (and `extend directive` extensions) + + +```gql +# Input +directive @a @b on QUERY +extend directive @a @b + +# Prettier stable +Error: Syntax Error: Expected "on", found "@". + +# Prettier main +directive @a @b on QUERY +extend directive @a @b +``` diff --git a/changelog_unreleased/markdown/18277.md b/changelog_unreleased/markdown/18277.md index 6abef72af..b624cdb0e 100644 --- a/changelog_unreleased/markdown/18277.md +++ b/changelog_unreleased/markdown/18277.md @@ -1,5 +1,7 @@ #### [HIGHLIGHT] Upgrade parser to latest micromark (#18277 by @seiyab, @j-f1, @fisker) -We've upgraded Prettier's Markdown parser from the outdated remark-parse v8 to the modern micromark v4. This upgrade significantly enhances CommonMark and GFM compliance, resolves numerous long-standing parsing bugs, and lays a stronger foundation for future enhancements. +We've upgraded Prettier's Markdown parser from the outdated `remark-parse` v8 to the modern `micromark` v4. This upgrade significantly enhances CommonMark and GFM compliance, resolves numerous long-standing parsing bugs, and lays a stronger foundation for future enhancements. -A huge thank you to @seiyab for their tremendous effort in driving this major upgrade! +Huge thanks to [@seiyab](https://github.com/seiyab), [@j-f1](https://github.com/j-f1), and everyone else who contributed to this long-awaited improvement for their tremendous effort in driving this major upgrade! + +**Note**: While the core Markdown parser has been upgraded, [the MDX parser upgrade](https://github.com/prettier/prettier/pull/18533) is not yet complete. If you're familiar with the unified ecosystem, micromark, or MDX, we'd love your help to finish the migration — contributions are very welcome! diff --git a/changelog_unreleased/misc/19014.md b/changelog_unreleased/misc/19014.md index 7dec60da1..30428710a 100644 --- a/changelog_unreleased/misc/19014.md +++ b/changelog_unreleased/misc/19014.md @@ -1,4 +1,4 @@ -#### updating `Printer` interface (#19014 by @Janther) +#### Update `Printer` interface (#19014 by @Janther) ```diff export interface Printer { diff --git a/changelog_unreleased/yaml/18419.md b/changelog_unreleased/yaml/18419.md new file mode 100644 index 000000000..c5fefa604 --- /dev/null +++ b/changelog_unreleased/yaml/18419.md @@ -0,0 +1,5 @@ +#### [HIGHLIGHT] Update dependency `yaml` to v2 (#18419 by @ota-meshi, @fisker) + +The YAML parser has been upgraded to use `yaml` v2, which fixes many long standing parse issues. + +Thanks to excellent work on the [`yaml-unist-parser`](https://github.com/prettier/yaml-unist-parser/pull/301) side by [@ota-meshi](https://github.com/ota-meshi). diff --git a/scripts/draft-blog-post.js b/scripts/draft-blog-post.js index 899ff7f05..5a485b855 100644 --- a/scripts/draft-blog-post.js +++ b/scripts/draft-blog-post.js @@ -47,7 +47,7 @@ for (const entry of fs.readdirSync(changelogUnreleasedDirectory, { continue; } - const dirPath = path.join(entry.path, entry.name); + const dirPath = path.join(entry.parentPath, entry.name); const category = categoriesByDir.get(entry.name); if (!category) { diff --git a/website/blog/2026-06-27-3.9.0.md b/website/blog/2026-06-27-3.9.0.md new file mode 100644 index 000000000..377b80663 --- /dev/null +++ b/website/blog/2026-06-27-3.9.0.md @@ -0,0 +1,2613 @@ +--- +authors: "fisker" +title: "Prettier 3.9: New Parser Foundations and Formatting Improvements" +--- + +We are excited to announce Prettier 3.9! + +This release brings major parser upgrades for Markdown, YAML, Flow, GraphQL, and Angular, along with formatting improvements for JavaScript and TypeScript (particularly in `--no-semi` mode). + +If you find Prettier valuable, please consider [sponsoring us on OpenCollective](https://opencollective.com/prettier) or supporting the upstream projects we rely on. Your contributions help us keep improving the tool for everyone. + +Thank you for your continued support! ❤️ + +A reminder, when Prettier is installed or updated, it’s [strongly recommended](https://prettier.io/docs/install#summary) to specify the exact version in `package.json`: `"prettier": "3.9.0"`, not `"prettier": "^3.9.0"`. + +If you use [`@prettier/plugin-oxc`](https://www.npmjs.com/package/@prettier/plugin-oxc) or [`@prettier/plugin-hermes`](https://www.npmjs.com/package/@prettier/plugin-hermes), don't forget upgrade them to ensure the new formatting rules are applied. + + + +## Highlights + +### Markdown + +#### Upgrade parser to latest micromark ([#18277](https://github.com/prettier/prettier/pull/18277) by [@seiyab](https://github.com/seiyab), [@j-f1](https://github.com/j-f1), [@fisker](https://github.com/fisker)) {#change-18277} + +We've upgraded Prettier's Markdown parser from the outdated `remark-parse` v8 to the modern `micromark` v4. This upgrade significantly enhances CommonMark and GFM compliance, resolves numerous long-standing parsing bugs, and lays a stronger foundation for future enhancements. + +Huge thanks to [@seiyab](https://github.com/seiyab), [@j-f1](https://github.com/j-f1), and everyone else who contributed to this long-awaited improvement for their tremendous effort in driving this major upgrade! + +**Note**: While the core Markdown parser has been upgraded, [the MDX parser upgrade](https://github.com/prettier/prettier/pull/18533) is not yet complete. If you're familiar with the unified ecosystem, micromark, or MDX, we'd love your help to finish the migration — contributions are very welcome! + +### YAML + +#### Update dependency `yaml` to v2 ([#18419](https://github.com/prettier/prettier/pull/18419) by [@ota-meshi](https://github.com/ota-meshi), [@fisker](https://github.com/fisker)) {#change-18419} + +The YAML parser has been upgraded to use `yaml` v2, which fixes many long standing parse issues. + +Thanks to excellent work on the [`yaml-unist-parser`](https://github.com/prettier/yaml-unist-parser/pull/301) side by [@ota-meshi](https://github.com/ota-meshi). + +### GraphQL + +#### Support GraphQL.js v17 ([#19171](https://github.com/prettier/prettier/pull/19171) by [@YBJ0000](https://github.com/YBJ0000), [#19297](https://github.com/prettier/prettier/pull/19297) by [@fisker](https://github.com/fisker)) {#change-19297} + +Prettier now fully supports newer syntax features from GraphQL.js v17, including directives on directive definitions, fragment arguments, and other enhancements. + +[Fragment arguments](https://github.com/graphql/graphql-js/pull/4015) + + +```gql +# Input +fragment variableProfilePic on User { + ...dynamicProfilePic(size: $size) +} + +# Prettier 3.8 +SyntaxError: Syntax Error: Expected Name, found "(". (2:23) + +# Prettier 3.9 +fragment variableProfilePic on User { + ...dynamicProfilePic(size: $size) +} +``` + +[Directives on directive definitions](https://github.com/graphql/graphql-js/pull/4521) (and `extend directive` extensions) + + +```gql +# Input +directive @a @b on QUERY +extend directive @a @b + +# Prettier 3.8 +Error: Syntax Error: Expected "on", found "@". + +# Prettier 3.9 +directive @a @b on QUERY +extend directive @a @b +``` + +### Flow + +#### New Rust-based Flow parser ([#19398](https://github.com/prettier/prettier/pull/19398) by [@SamChou19815](https://github.com/SamChou19815)) {#change-19398} + +Prettier now uses the new Rust-based Flow parser (oxidized) released by the Flow team, which improves performance for Flow-typed code. + +In local parser-only benchmarks, the new parser parsed Prettier's valid Flow fixtures in 266.4ms median time, compared with 422.6ms for the old parser, and parsed `flow_parser.js` in 1298.0ms, compared with 2269.6ms. + +## Other Changes + +### JavaScript + +#### Stabilize comment around `break` and `continue` in no-semi mode ([#7161](https://github.com/prettier/prettier/pull/7161) by [@thorn0](https://github.com/thorn0), [@fisker](https://github.com/fisker)) {#change-7161} + + +```jsx +// Input +for (;;) { + if (condition) { + break; // breaking comment + + (possibleArray || []).sort() + } +} + +// Prettier 3.8 (--no-semi, first format) +for (;;) { + if (condition) { + break // breaking comment + + ;(possibleArray || []).sort() + } +} + +// Prettier 3.8 (--no-semi, second format) +for (;;) { + if (condition) { + break // breaking comment + ;(possibleArray || []).sort() + } +} + +// Prettier 3.9 (--no-semi, both first and second format) +for (;;) { + if (condition) { + break; // breaking comment + + (possibleArray || []).sort(); + } +} +``` + +#### Remove redundant parentheses in "return statement" ([#18142](https://github.com/prettier/prettier/pull/18142) by [@fisker](https://github.com/fisker)) {#change-18142} + + +```jsx +// Input +function sequenceExpressionInside() { + return ( // Reason for a + a, b + ); +} + +// Prettier 3.8 +function sequenceExpressionInside() { + return ( + // Reason for a + (a, b) + ); +} + +// Prettier 3.9 +function sequenceExpressionInside() { + return ( + // Reason for a + a, b + ); +} +``` + +#### Fix alignment in embedded template interpolations ([#18380](https://github.com/prettier/prettier/pull/18380) by [@fisker](https://github.com/fisker)) {#change-18380-2} + + +```jsx +// Input +string = ` + .class { + flex-direction: column${ + long_cond && long_cond && long_cond + ? "-reverse" : + "" + }; + } +`; +css = css` + .class { + flex-direction: column${ + long_cond && long_cond && long_cond + ? "-reverse" : + "" + }; + } +`; + +// Prettier 3.8 +string = ` + .class { + flex-direction: column${ + long_cond && long_cond && long_cond ? "-reverse" : "" + }; + } +`; +css = css` + .class { + flex-direction: column${long_cond && long_cond && long_cond + ? "-reverse" + : ""}; + } +`; + +// Prettier 3.9 +string = ` + .class { + flex-direction: column${ + long_cond && long_cond && long_cond ? "-reverse" : "" + }; + } +`; +css = css` + .class { + flex-direction: column${ + long_cond && long_cond && long_cond ? "-reverse" : "" + }; + } +`; +``` + +#### Avoid linebreaks in embedded template interpolations ([#18380](https://github.com/prettier/prettier/pull/18380) by [@fisker](https://github.com/fisker)) {#change-18380} + + +```jsx +// Input +string = /* Comment */ ` +
${long_cond && long_cond && long_cond && long_cond && long_cond ? "content" : ""}
+`; +html = /* HTML */ ` +
${long_cond && long_cond && long_cond && long_cond && long_cond ? "content" : ""}
+`; + +// Prettier 3.8 +string = /* Comment */ ` +
${long_cond && long_cond && long_cond && long_cond && long_cond ? "content" : ""}
+`; +html = /* HTML */ ` +
+ ${long_cond && long_cond && long_cond && long_cond && long_cond + ? "content" + : ""} +
+`; + +// Prettier 3.9 +string = /* Comment */ ` +
${long_cond && long_cond && long_cond && long_cond && long_cond ? "content" : ""}
+`; +html = /* HTML */ ` +
+ ${long_cond && long_cond && long_cond && long_cond && long_cond ? "content" : ""} +
+`; +``` + +#### Improve logical not expression print ([#18397](https://github.com/prettier/prettier/pull/18397), [#18401](https://github.com/prettier/prettier/pull/18401) by [@fisker](https://github.com/fisker)) {#change-18397} + +- Fixed inner logical expression can be double parenthesized in some case. +- Inline the expression in `if`/`while`/`do..while` condition, to reduce diff when changing condition to negated value. + + +```jsx +// Input +if (!( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +)) { +} else if ( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +) { +} + +// Prettier 3.8 +if ( + !( + // `import("foo")` + ( + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" + ) + ) +) { +} else if ( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +) { +} + +// Prettier 3.9 +if (!( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +)) { +} else if ( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +) { +} +``` + +_This code example above is [a real world case](https://github.com/prettier/prettier/blob/edb53db4b1ec326d3f0f4fb683f56c83beefeba4/src/language-js/print/call-expression.js#L135-L155) right here in the Prettier codebase_ + +#### Remove unexpected space inserted before expression in CSS selectors ([#18460](https://github.com/prettier/prettier/pull/18460) by [@kovsu](https://github.com/kovsu)) {#change-18460} + + +```js +// Input +css`foo:${bar} {}` + +// Prettier 3.8 +css` + foo: ${bar} { + + } +`; + +// Prettier 3.9 +css` + foo:${bar} { + } +`; +``` + +#### Keep iife function comments in parentheses ([#18538](https://github.com/prettier/prettier/pull/18538) by [@fisker](https://github.com/fisker)) {#change-18538} + + +```jsx +// Input +const a = ( + /** + * @param {number} foo + * @return {number} + */ + function (foo) { + return foo + 1; + } +)(1); + +// Prettier 3.8 +const a = /** + * @param {number} foo + * @return {number} + */ +(function (foo) { + return foo + 1; +})(1); + +// Prettier 3.9 +const a = ( + /** + * @param {number} foo + * @return {number} + */ + function (foo) { + return foo + 1; + } +)(1); +``` + +#### Fix comment print for parenthesized callee ([#18540](https://github.com/prettier/prettier/pull/18540) by [@fisker](https://github.com/fisker)) {#change-18540} + + +```jsx +// Input +const a = ( + function(){} + /* comment for callee */ + )(), + b = ( + function(){} + )(/* comment for call */) + +// Prettier 3.8 +const a = (function () {})(), + /* comment for callee */ + b = (function () {})(/* comment for call */); + +// Prettier 3.9 +const a = ( + function () {} + /* comment for callee */ + )(), + b = (function () {})(/* comment for call */); +``` + +#### Preserve trailing double spaces in JSDoc ([#18594](https://github.com/prettier/prettier/pull/18594) by [@seiyab](https://github.com/seiyab)) {#change-18594} + +Some tools treat these spaces as meaningful. + +```js +await prettier.format("/**\n * With 2 spaces \n */", { parser: "babel" }); + +/* Prettier 3.8 */ +// -> '/**\n * With 2 spaces\n */\n' + +/* Prettier 3.9 */ +// -> '/**\n * With 2 spaces \n */\n' +// ^^ space preserved +``` + +#### Drop support for the old "import assertions" syntax ([#18611](https://github.com/prettier/prettier/pull/18611) by [@fisker](https://github.com/fisker)) {#change-18611} + +`import assertions` (using the `assert` keyword) is an old version of the current [import attributes proposal](https://github.com/tc39/proposal-import-attributes). + +```js +// Old (deprecated) +import foo from "./foo.json" assert { type: "json" }; + +// Current standard +import foo from "./foo.json" with { type: "json" }; +``` + +Babel 8 completely removed support for the legacy `assert` syntax (the old parser plugin is gone). +Without Babel parser support, Prettier can no longer reliably parse or format code using `import ... assert { ... }`. + +Please migrate to the `with` syntax. + +```diff +- import foo from "./foo.json" assert { type: "json" }; ++ import foo from "./foo.json" with { type: "json" }; +``` + +See also Prettier's [disclaimer about non-standard syntax](https://prettier.io/docs/rationale.html#disclaimer-about-non-standard-syntax). + +#### Improve comment handling around empty call argument list ([#18615](https://github.com/prettier/prettier/pull/18615) by [@fisker](https://github.com/fisker)) {#change-18615-2} + + +```jsx +// Input +call +// line comment +(); +call(// line comment +); +call( + // line comment +); + +call +/* block comment */ +(); +call(/* block comment */ +); +call( + /* block comment */ +); + +// Prettier 3.8 +call(); +// line comment +call(); // line comment +call(); +// line comment + +call(); +/* block comment */ +call /* block comment */(); +call(); +/* block comment */ + +// Prettier 3.9 +call +// line comment +(); +call( + // line comment +); +call( + // line comment +); + +call +/* block comment */ +(); +call(/* block comment */); +call(/* block comment */); +``` + +#### Fix unstable comment in call expression ([#18615](https://github.com/prettier/prettier/pull/18615) by [@fisker](https://github.com/fisker)) {#change-18615-3} + + +```jsx +// Input +foo = + call( + /* call argument */); + +// Prettier 3.8 (First format) +foo = + call(); + /* call argument */ + +// Prettier 3.8 (Second format) +foo = call(); +/* call argument */ + +// Prettier 3.9 +foo = call(/* call argument */); +``` + +#### Allow empty call expression argument list with long comments to break ([#18615](https://github.com/prettier/prettier/pull/18615) by [@fisker](https://github.com/fisker)) {#change-18615} + + +```jsx +// Input +call(/* a long long long long long long long long long long long long comment */); +array = [/* a long long long long long long long long long long long long comment */]; +object = {/* a long long long long long long long long long long long long comment */}; + +// Prettier 3.8 +call(/* a long long long long long long long long long long long long comment */); +array = [ + /* a long long long long long long long long long long long long comment */ +]; +object = { + /* a long long long long long long long long long long long long comment */ +}; + +// Prettier 3.9 +call( + /* a long long long long long long long long long long long long comment */ +); +array = [ + /* a long long long long long long long long long long long long comment */ +]; +object = { + /* a long long long long long long long long long long long long comment */ +}; +``` + +#### Don't force empty array and object with short comments to break ([#18617](https://github.com/prettier/prettier/pull/18617) by [@fisker](https://github.com/fisker)) {#change-18617} + + +```jsx +// Input +const array = [/* comment */] +const object = {/* comment */} + +// Prettier 3.8 +const array = [ + /* comment */ +]; +const object = { + /* comment */ +}; + +// Prettier 3.9 +const array = [/* comment */]; +const object = {/* comment */}; +``` + +#### Fix dangling comment print in function parameters ([#18623](https://github.com/prettier/prettier/pull/18623) by [@fisker](https://github.com/fisker)) {#change-18623} + + +```jsx +// Input +fn = function( + // comment +) {} +arrow = ( + // comment +) => {} + +// Prettier 3.8 +fn = function () // comment +{}; +arrow = () => + // comment + {}; + +// Prettier 3.9 +fn = function ( + // comment +) {}; +arrow = ( + // comment +) => {}; +``` + +#### Fix inconsistent comment attach between `NewExpression` and `CallExpression` ([#18669](https://github.com/prettier/prettier/pull/18669) by [@fisker](https://github.com/fisker)) {#change-18669} + + +```jsx +// Input +foo( // comment + bar +); +new Foo( // comment + bar +); + +// Prettier 3.8 +foo( + // comment + bar, +); +new Foo(bar); // comment + +// Prettier 3.9 +foo( + // comment + bar, +); +new Foo( + // comment + bar, +); +``` + +#### Fix missing parentheses around optional chaining ([#18720](https://github.com/prettier/prettier/pull/18720) by [@fisker](https://github.com/fisker)) {#change-18720} + + +```jsx +// Input +(a?.b()).c(); + +// Prettier 3.8 +a?.b().c(); + +// Prettier 3.9 +(a?.b()).c(); +``` + +#### Fix range format on variable declaration ([#18734](https://github.com/prettier/prettier/pull/18734), [#18740](https://github.com/prettier/prettier/pull/18740) by [@fisker](https://github.com/fisker)) {#change-18734} + + +```jsx +// Input +let i ="format me!" ; +// ^^^^^^^^^^^^^^ Range + +// Prettier 3.8 +let i = "format me!"; ; + +// Prettier 3.9 +let i = "format me!"; +``` + +#### Improve blank line detection between statements ([#18736](https://github.com/prettier/prettier/pull/18736) by [@fisker](https://github.com/fisker)) {#change-18736-2} + + +```jsx +// Input +const exports = text + .matchAll(/(?<=\n)exports\.(?\w+) = \k;/g) + .map((match) => match.groups.specifier) + .toArray() +; + +foo(); + +// Prettier 3.8 (--no-semi) +const exports = text + .matchAll(/(?<=\n)exports\.(?\w+) = \k;/g) + .map((match) => match.groups.specifier) + .toArray(); +foo(); + +// Prettier 3.9 +const exports = text + .matchAll(/(?<=\n)exports\.(?\w+) = \k;/g) + .map((match) => match.groups.specifier) + .toArray(); + +foo(); +``` + +#### Preserve empty line before `;` in `no-semi` mode ([#18736](https://github.com/prettier/prettier/pull/18736), [#18737](https://github.com/prettier/prettier/pull/18737) by [@fisker](https://github.com/fisker)) {#change-18736} + + +```jsx +// Input +a + +;[].b() + +// Prettier 3.8 (--no-semi) +a +;[].b() + +// Prettier 3.9 +a + +;[].b() +``` + +#### Print leading semicolon before type cast comments ([#18751](https://github.com/prettier/prettier/pull/18751) by [@fisker](https://github.com/fisker)) {#change-18751} + + +```jsx +// Input +;/** @type {string[]} */ (['foo', 'bar']).forEach(doStuff) + +// Prettier 3.8 (--no-semi, first format) +/** @type {string[]} */ ;(["foo", "bar"]).forEach(doStuff) + +// Prettier 3.8 (--no-semi, second format) +/** @type {string[]} */ ;["foo", "bar"].forEach(doStuff) + +// Prettier 3.9 (--no-semi) +;/** @type {string[]} */ (["foo", "bar"]).forEach(doStuff) +``` + +#### Prevent moving comments after arrow into parameters ([#18775](https://github.com/prettier/prettier/pull/18775) by [@fisker](https://github.com/fisker)) {#change-18775} + + +```jsx +// Input +KEYPAD_NUMBERS.map(num => ( // Buttons 0-9 +
+)); + +const createIdFilter = + (foo) => /** @param {string} id */ + (id) => id; + +// Prettier 3.8 +KEYPAD_NUMBERS.map( + ( + num // Buttons 0-9 + ) =>
+); + +const createIdFilter = (foo /** @param {string} id */) => (id) => id; + +// Prettier 3.9 +KEYPAD_NUMBERS.map((num) => ( + // Buttons 0-9 +
+)); + +const createIdFilter = (foo) => /** @param {string} id */ (id) => id; +``` + +#### Fix comments above `else` ([#18813](https://github.com/prettier/prettier/pull/18813) by [@fisker](https://github.com/fisker)) {#change-18813} + + +```jsx +// Input +/* Case 1 */ +if (1) { +} + +/* Case 2 */ +else if (2) { +} + +// Prettier 3.8 +/* Case 1 */ +if (1) { +} else if (2) { + +/* Case 2 */ +} + +// Prettier 3.9 +/* Case 1 */ +if (1) { +} + +/* Case 2 */ +else if (2) { +} +``` + +#### Fix "comment not printed" error in debugger statement ([#18840](https://github.com/prettier/prettier/pull/18840) by [@fisker](https://github.com/fisker)) {#change-18840} + + +```jsx +// Input +debugger // Comment +; + +// Prettier 3.8 +Error: Comment "comment" was not printed. Please report this error! + +// Prettier 3.9 +debugger; // Comment +``` + +#### Improve `do..while` print in `no-semi` mode ([#18851](https://github.com/prettier/prettier/pull/18851) by [@fisker](https://github.com/fisker)) {#change-18851} + + +```jsx +// Input +do { + doStuff() +} while (1) + +// comment +;[foo, bar].forEach(doStuff) + +// Prettier 3.8 +do { + doStuff() +} while ( + 1 + + // comment +) +;[foo, bar].forEach(doStuff) + +// Prettier 3.9 +do { + doStuff() +} while (1) + +// comment +;[foo, bar].forEach(doStuff) +``` + +#### Fix duplicated comments in empty branches with `experimentalTernaries` ([#18963](https://github.com/prettier/prettier/pull/18963) by [@kovsu](https://github.com/kovsu)) {#change-18963} + + +```jsx +// Input +condition ? ifTrue +: [ + // Hello, world! + ]; + +// Prettier 3.8 (--experimental-ternaries) +condition ? ifTrue + // Hello, world! +: ( + [ + // Hello, world! + ] +); + +// Prettier 3.9 (--experimental-ternaries) +condition ? ifTrue : ( + [ + // Hello, world! + ] +); +``` + +#### Preserve blank lines between JSX attributes ([#19161](https://github.com/prettier/prettier/pull/19161) by [@Poliklot](https://github.com/Poliklot)) {#change-19161} + +Prettier now keeps one existing blank line between JSX attributes, consistent with how it preserves blank lines in many other places. Multiple blank lines are still collapsed to one. + + +```jsx +// Input +