---
authors: "sosukesuzuki"
title: "Prettier 3.4: A lot of bug fixes"
---

This release includes numerous bug fixes and other improvements.

If you appreciate Prettier and would like to support our work, please consider sponsoring us directly via [our OpenCollective](https://opencollective.com/prettier) or by sponsoring the projects we depend on, such as [typescript-eslint](https://opencollective.com/typescript-eslint), [remark](https://opencollective.com/unified), and [Babel](https://opencollective.com/babel). Thank you for your continued support!

<!-- truncate -->

## Other Changes

### JavaScript

#### Fix template literal print with array ([#13315](https://github.com/prettier/prettier/pull/13315) by [@fisker](https://github.com/fisker), [@syi0808](https://github.com/syi0808))

<!-- prettier-ignore -->
```jsx
// Input
const string = `${[[1, 2], [3, 4]]}`

// Prettier 3.3
const string = `${[
  [2],
  [4],
]}`;

// Prettier 3.4
const string = `${[
  [1, 2],
  [3, 4],
]}`;
```

#### Add missing parentheses in tagged template literals ([#16500](https://github.com/prettier/prettier/pull/16500) by [@syi0808](https://github.com/syi0808))

<!-- prettier-ignore -->
```jsx
// Input
(String?.raw)``;
(getTag?.())``;

// Prettier 3.3
String?.raw``;
getTag?.()``;

// Prettier 3.4
(String?.raw)``;
(getTag?.())``;
```

#### Don't remove useless `\` in string literals ([#16563](https://github.com/prettier/prettier/pull/16563) by [@sosukesuzuki](https://github.com/sosukesuzuki), [#16763](https://github.com/prettier/prettier/pull/16763) by [@fisker](https://github.com/fisker))

Previously, Prettier would remove useless escape characters (`\`) from string literals. However, this behavior was inconsistent as it did not apply to template literals, which was reported in [issue #16542](https://github.com/prettier/prettier/issues/16542).

This issue is a feature request to extend this behavior to template literals as well.

After discussing this internally, the Prettier team concluded that removing useless escape characters, whether in string literals or template literals, is the responsibility of a linter, not a formatter.

This change disables the removal of useless escape characters in string literals across all languages supported by Prettier. To keep the previous behavior, we recommend using ESLint rules like [`no-useless-escape`](https://eslint.org/docs/latest/rules/no-useless-escape).

Escaped quotes (e.g. `"\"\'"`) may get unescaped when changing between different quotes. This is explained on the [Rationale](https://prettier.io/docs/rationale#strings) page of the official documentation.

<!-- prettier-ignore -->
```jsx
// Input
const str = "\a";

// Prettier 3.3
const str = "a";

// Prettier 3.4
const str = "\a";
```

#### Improve comment formatting for logical expression in unary expression ([#16593](https://github.com/prettier/prettier/pull/16593) by [@sosukesuzuki](https://github.com/sosukesuzuki))

<!-- prettier-ignore -->
```jsx
// Input
!(
  cond1 || // foo
  cond2 || // bar
  cond3 // baz
);

// Prettier 3.3
!(
  (
    cond1 || // foo
    cond2 || // bar
    cond3
  ) // baz
);

// Prettier 3.4
!(
  cond1 || // foo
  cond2 || // bar
  cond3 // baz
);
```

#### Removed support for experimental syntax ([#16643](https://github.com/prettier/prettier/pull/16643), [#16705](https://github.com/prettier/prettier/pull/16705) by [@fisker](https://github.com/fisker))

- `DecimalLiteral` - The [Decimal proposal](https://github.com/tc39/proposal-decimal) decided not to introduce new syntax. The `decimal` plugin will be removed from Babel 8.
- `importReflection` - The "Import Reflection" proposal has been renamed to ["Source Phase Imports"](https://github.com/tc39/proposal-source-phase-imports)

- [Disclaimer about non-standard syntax](https://prettier.io/docs/rationale#disclaimer-about-non-standard-syntax)

### TypeScript

#### Add missing parentheses in tagged template literals ([#16500](https://github.com/prettier/prettier/pull/16500) by [@syi0808](https://github.com/syi0808))

<!-- prettier-ignore -->
```jsx
// Input
(String?.raw!)``;
(String?.raw)!``;

// Prettier 3.3
String?.raw!``;
String?.raw!``;

// Prettier 3.4
(String?.raw)!``;
(String?.raw)!``;
```

#### Preserve a comment on between decorator and modified parameter property ([#16574](https://github.com/prettier/prettier/pull/16574) by [@sosukesuzuki](https://github.com/sosukesuzuki))

The current version of Prettier unexpectedly moves a line comment between a parameter property modified by `readonly`, `private`, `public`, etc and a decorator. This output results in invalid TypeScript code.

This change ensures that the original format is preserved.

<!-- prettier-ignore -->
```ts
// Input
class Foo {
  constructor(
    @decorator
    // comment
    readonly foo,
  ) {}
}

// Prettier 3.3
class Foo {
  constructor(
    @decorator
    readonly // comment
    foo,
  ) {}
}

// Prettier 3.4
class Foo {
  constructor(
    @decorator
    // comment
    readonly foo,
  ) {}
}
```

#### Preserve a comment between modifier and the decorated property name ([#16578](https://github.com/prettier/prettier/pull/16578) by [@sosukesuzuki](https://github.com/sosukesuzuki))

There was a bug where block comments between the modifier and the name of a decorated property were being treated as trailing comments of the decorator. This behavior was not only unexpected but also lacked idempotency.

With this change, the position of the block comment between the modifier and the property name is preserved.

<!-- prettier-ignore -->
```ts
// Input
class Foo {
  @decorator
  readonly /* comment */ propertyName;
}

// Prettier 3.3
class Foo {
  @decorator /* comment */
  readonly propertyName;
}

// Prettier 3.3 (second output)
class Foo {
  @decorator /* comment */ readonly propertyName;
}

// Prettier 3.4
class Foo {
  @decorator
  readonly /* comment */ propertyName;
}
```

#### Don't print an extra line break for arrow function with type parameter in assignment ([#16586](https://github.com/prettier/prettier/pull/16586) by [@sosukesuzuki](https://github.com/sosukesuzuki))

There was a bug where an extra line was inserted when assigning a chained arrow function with type parameters to a variable if there was a line comment above it.

This change ensures that the extra line is no longer inserted.

<!-- prettier-ignore -->
```ts
// Input
const foo1 =
  // comment
  <T,>() => () => 1;

// Prettier 3.3
const foo1 =
  // comment

    <T,>() =>
    () =>
      1;

// Prettier 3.4
const foo1 =
  // comment
  <T,>() =>
    () =>
      1;
```

#### Support "Top-level await statements" ([#16729](https://github.com/prettier/prettier/pull/16729) by [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```ts
// Input
(await (await fetch()).json()).foo

// Prettier 3.3
await(await fetch()).json().foo;

// Prettier 3.4
(await (await fetch()).json()).foo;
```

#### Fix class heritage breaks even though it remains inside the line width ([#16730](https://github.com/prettier/prettier/pull/16730) by [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```ts
// Input
export class JiraCreatePixFraudDetectionGateway
  implements Pick<IssuePixFraudDetectionGateway, "createPixFraudDetectionIssue">
{
  constructor(private readonly logger: Logger) {
    this.logger = logger.child({
      context: JiraCreatePixFraudDetectionGateway.name,
    });
  }
}

// Prettier 3.3
export class JiraCreatePixFraudDetectionGateway
  implements
    Pick<IssuePixFraudDetectionGateway, "createPixFraudDetectionIssue">
{
  constructor(private readonly logger: Logger) {
    this.logger = logger.child({
      context: JiraCreatePixFraudDetectionGateway.name,
    });
  }
}

// Prettier 3.4
export class JiraCreatePixFraudDetectionGateway
  implements Pick<IssuePixFraudDetectionGateway, "createPixFraudDetectionIssue">
{
  constructor(private readonly logger: Logger) {
    this.logger = logger.child({
      context: JiraCreatePixFraudDetectionGateway.name,
    });
  }
}
```

#### Print `declare` before accessibility in class properties ([#16731](https://github.com/prettier/prettier/pull/16731) by [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```ts
// Input
class A {
  declare private readonly name: string;
}

// Prettier 3.3
class A {
  private declare readonly name: string;
}

// Prettier 3.4
class A {
  declare private readonly name: string;
}
```

### CSS

#### Resolve some types of overrun in CSS ([#16570](https://github.com/prettier/prettier/pull/16570) by [@seiyab](https://github.com/seiyab))

<!-- prettier-ignore -->
```css
/* Input */
@media (prefers-reduced-data: no-preference) {
  @font-face {
    unicode-range: U+0000-00FF, U+0131,
U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
}

/* Prettier 3.3 */
@media (prefers-reduced-data: no-preference) {
  @font-face {
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
      U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212,
      U+2215, U+FEFF, U+FFFD;
  }
}

/* Prettier 3.4 */
@media (prefers-reduced-data: no-preference) {
  @font-face {
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
      U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
      U+2212, U+2215, U+FEFF, U+FFFD;
  }
}
```

#### Remove extra indentation in pseudo-class function ([#16572](https://github.com/prettier/prettier/pull/16572) by [@sosukesuzuki](https://github.com/sosukesuzuki))

This change fixes a bug where extra indentation was added when line breaks were included in the argument list of pseudo-class functions like [`:where()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:where), [`:is()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:is), and [`:not`](https://developer.mozilla.org/en-US/docs/Web/CSS/:not).

<!-- prettier-ignore -->
```css
/* Input */
:where(input:not([type="button"], [type="reset"], [type="submit"]), textarea, select) {
    /* CSS here */
}

/* Prettier 3.3 */
:where(
    input:not([type="button"], [type="reset"], [type="submit"]),
    textarea,
    select
  ) {
  /* CSS here */
}

/* Prettier 3.4 */
:where(
  input:not([type="button"], [type="reset"], [type="submit"]),
  textarea,
  select
) {
  /* CSS here */
}

```

#### Fix formatting of incomplete CSS value comments ([#16583](https://github.com/prettier/prettier/pull/16583) by [@sosukesuzuki](https://github.com/sosukesuzuki), [@fisker](https://github.com/fisker))

When formatting CSS value comments, the trailing `/` may be lost, resulting in an invalid comment.

This change ensures that value comments are not truncated.

<!-- prettier-ignore -->
```css
/* Input */
h1 {
  --OFF: /* OFF */;
}

/* Prettier 3.3 */
h1 {
  --OFF:  /* OFF *;
}

/* Prettier 3.4 */
h1 {
  --OFF: /* OFF */;
}
```

### SCSS

#### Fix error thrown when formatting SCSS file ([#16607](https://github.com/prettier/prettier/pull/16607) by [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```scss
// Input
@if true {
  $newKey: ($key: ( $theme-name: $value ))
}

// Prettier 3.3
Error

// Prettier 3.4
@if true {
  $newKey: (
    $key:
      (
        $theme-name: $value,
      ),
  );
}
```

#### Fix wrong trailing comma position after comment in SCSS ([#16617](https://github.com/prettier/prettier/pull/16617) by [@Ma-hawaj](https://github.com/Ma-hawaj), [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```scss
/* Input */
$z-indexes: (
    header: 1035,
    overlay: 1202 // TODO: change to 1050 after bootstrap modals will be removed
);

/* Prettier 3.3 */
$z-indexes: (
    header: 1035,
    overlay: 1202 // TODO: change to 1050 after bootstrap modals will be removed,
);

/* Prettier 3.4 */
$z-indexes: (
    header: 1035,
    overlay: 1202, // TODO: change to 1050 after bootstrap modals will be removed
);
```

### HTML

#### Keep doctype in non-html files unchanged ([#16765](https://github.com/prettier/prettier/pull/16765) by [@fisker](https://github.com/fisker))

In Prettier v3, [we print HTML5 doctype in lowercase](https://prettier.io/blog/2023/07/05/3.0.0#print-html5-doctype-in-lowercase-7391httpsgithubcomprettierprettierpull7391-by-fiskerhttpsgithubcomfisker), it's safe for HTML files, however users may use the `html` parser to format other files eg: XHTML files, lowercase the `doctype` will break XHTML documents.

Starting with Prettier 3.4, we'll only lowercase [HTML5 doctype (`<!doctype html>`)](https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Writing_style_guide/Code_style_guide/HTML#doctype) when the file extension is `.html` or `.htm`, otherwise they will be untouched.

<!-- prettier-ignore -->
```html
<!-- Input -->
<!-- foo.xhtml -->
<!DOCTYPE html>

<!-- Prettier 3.3 -->
<!doctype html>

<!-- Prettier 3.4 -->
<!DOCTYPE html>
```

### Vue

#### Fix extra semicolon inserted in Vue event binding with non-ascii characters ([#16733](https://github.com/prettier/prettier/pull/16733) by [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```vue
<!-- Input -->
<template>
  <button @click="点击事件">点击!</button>
  <button @click="onClick">Click!</button>
</template>

<!-- Prettier 3.3 -->
<template>
  <button @click="点击事件;">点击!</button>
  <button @click="onClick">Click!</button>
</template>

<!-- Prettier 3.4 -->
<template>
  <button @click="点击事件">点击!</button>
  <button @click="onClick">Click!</button>
</template>
```

### Angular

#### Support Angular 19 ([#16862](https://github.com/prettier/prettier/pull/16862) by [@fisker](https://github.com/fisker))

Angular 19 added [support for `typeof` keyword in template expressions](https://github.com/angular/angular/pull/58183).

<!-- prettier-ignore -->
```html
<!-- Input -->
<div>{{ typeof
      x ===
    'object' ? 'Y' : 'N'}}</div>

<!-- Prettier 3.3 -->
<div>
  {{ typeof
      x ===
    'object' ? 'Y' : 'N'}}
</div>

// Prettier 3.4
<div>{{ typeof x === "object" ? "Y" : "N" }}</div>
```

### Markdown

#### Remove excessive spaces after line prefixes for unordered lists in Markdown ([#15526](https://github.com/prettier/prettier/pull/15526) by [@TomasLudvik](https://github.com/TomasLudvik))

<!-- prettier-ignore -->
```md
<!-- Input -->
- first line
    - second line indented
- third line
    - fourth line
        - fifth line

<!-- Prettier 3.3 -->
-   first line
    -   second line indented
-   third line
    -   fourth line
        -   fifth line

<!-- Prettier 3.4 -->
- first line
    - second line indented
- third line
    - fourth line
        - fifth line
```

#### Fix incorrect wrap in sentence with linkReference ([#16546](https://github.com/prettier/prettier/pull/16546) by [@seiyab](https://github.com/seiyab))

<!-- prettier-ignore -->
```md
<!-- Input (--prose-wrap=always) -->
This folder has [VHS] tape files to create gifs for the [Widget Showcase]. To run them, install VHS from main (the theme and screenshot commands are not yet released).

<!-- Prettier 3.3 -->
This folder has [VHS] tape files to create gifs for the [Widget Showcase]. To run
them, install VHS from main (the theme and screenshot commands are not yet released).

<!-- Prettier 3.4 -->
This folder has [VHS] tape files to create gifs for the [Widget Showcase]. To
run them, install VHS from main (the theme and screenshot commands are not yet
released).
```

#### Preserve non-ASCII whitespaces at the end of the line and beginning of the next line ([#16619](https://github.com/prettier/prettier/pull/16619) by [@tats-u](https://github.com/tats-u))

Prettier removes non-ASCII spaces at the end of the line and beginning of the next line. However, this behavior is not consistent with the CommonMark spec.

https://spec.commonmark.org/0.31.2/#soft-line-breaks

> Spaces at the end of the line and beginning of the next line are removed:

https://spec.commonmark.org/0.31.2/#unicode-whitespace-character

> A Unicode whitespace character is a character in the Unicode Zs general category, or a tab (U+0009), line feed (U+000A), form feed (U+000C), or carriage return (U+000D).

> Unicode whitespace is a sequence of one or more Unicode whitespace characters.

> A space is U+0020.

The CommonMark spec doesn't mention non-ASCII spaces here, so removing them changes the content of the Markdown document.

<!-- prettier-ignore -->
```md
<!-- Input -->
 EM Space (U+2003) EM Space

　全角スペース (U+3000) 全形空白

<!-- Prettier 3.3 -->
EM Space (U+2003) EM Space

全角スペース (U+3000) 全形空白

<!-- Prettier 3.4 -->
 EM Space (U+2003) EM Space

　全角スペース (U+3000) 全形空白
```

#### Don't break a line a between Chinese or Japanese and others ([#16691](https://github.com/prettier/prettier/pull/16691) by [@tats-u](https://github.com/tats-u))

Markdown documents are mainly converted to HTML or components of JavaScript-based frameworks. This means that paragraphs in Markdown are eventually processed by the browser according to [CSS rules](https://drafts.csswg.org/css-text-4/). This is because many Markdown converter preserve line breaks in paragraphs in input Markdown and HTML itself does not specify how browsers should handle line breaks in text in HTML.

According to [CSS rules (CSS Text Module Level 3 or later)](https://drafts.csswg.org/css-text-4/#line-break-transform), browsers should remove line breaks between Chinese/Japanese characters instead of replacing them with spaces. However, this rule has been ignored by WebKit-based or Webkit-derived browsers (Chrome, Safari, and so on) for long time.

For example, the following HTML paragraph:

<!-- prettier-ignore -->
```html
<p>
日本語
汉语
漢語
<p>
```

generated from the following Markdown:

```md
日本語
汉语
漢語
```

should be rendered as follows according to CSS rules and actually is rendered such by Firefox:

```md
日本語汉语漢語
```

However, Chrome and Safari render it as follows:

```md
日本語 汉语 漢語
```

This is why we should stop Prettier from line breaking between Chinese/Japanese characters. We decided to stop Prettier from forcing users to use a plugin for a Markdown converter that concatenates lines that start or end with Chinese/Japanese characters ([remark-join-cjk-lines](https://www.npmjs.com/package/remark-join-cjk-lines), for example).

Also, [a line break between Chinese/Japanese and others are equivalent to a space](https://github.com/w3c/csswg-drafts/blob/076914a198bf3ac642001619e08acefdf607d61d/css-text/Overview.bs#L712) according to before [the commit suspending a concrete rule in CSS Text Module Level 3 by commenting it out](https://github.com/w3c/csswg-drafts/commit/b3bb0ed18b3168981c54c437bcfb5881ef49975b) fixing [an issue on the CSS Working Group Editor Drafts](https://github.com/w3c/csswg-drafts/issues/5086). Firefox follows this rule. Therefore, all browsers render the following paragraph:

````html
<!-- prettier-ignore -->
```html
<p>日本語 English 汉语 한국어 漢語</p>

<p></p>
````

as follows:

```md
日本語 English 汉语 한국어 漢語
```

However, Prettier has broken a line between Chinese/Japanese characters in Markdown for a long time, and between Chinese/Japanese and latin characters in some cases since 3.0.0. For example, the following Markdown paragraph:

```md
日本語English汉语
English
漢語
```

is formatted as follows if `--prose-wrap` is set to the other value than `preserve` in Prettier 3.x:

```md
日本語English汉语English漢語
```

However, the following HTML, which is generated by a Markdown-to-HTML converter based on the above Markdown:

<!-- prettier-ignore -->
```html
<p>
日本語English汉语
English
漢語
</p>
```

is rendered as follows by all browsers:

```md
日本語English汉语 English 漢語
```

This is why we should stop Prettier from line breaking al　so around Chinese/Japanese characters in Markdown. We are going to conform Prettier's behavior to this rule in a future version. After that, line breaks between Chinese/Japanese and others will be allowed again under certain rules.

One of the few exceptions is spaces and line breaks between Chinese/Japanese and Korean letters. The following Markdown paragraphs are equivalent even in the current Prettier version:

```md
현재 韓國의 大統領은 尹錫悅이다.
```

```md
현재
韓國의
大統領은
尹錫悅이다.
```

You get the former if you format the latter with `--prose-wrap=always` and a sufficiently long `--print-width` value or with `--prose-wrap=never`, and you get the latter if you format the former with `--prose-wrap=always` with a extremely short `--print-width` value. Therefore, we do not have to touch such spaces and line breaks.

Another exception is those between a Chinese/Japanese character and a meaningful symbol in Markdown like `*`, `` ` ``, `[`, and `]`. For example, the following Markdown paragraph is equivalent even in the current Prettier version:

```md
**Yarn** のCLI経由でフォーマットするには `yarn prettier -w ` を実行してください。
```

```md
**Yarn**
のCLI経由でフォーマットするには
`yarn prettier -w .`
を実行してください。
```

<!-- prettier-ignore -->
```md
<!-- Input (--prose-wrap=always --print-width=20) -->
日本語 汉语 漢語 English 한국어 日本語 汉语 漢語 English 한국어 日本語 汉语 漢語 English 한국어 日本語 汉语 漢語 English 한국어

日本語汉语漢語English한국어日本語汉语漢語English한국어日本語汉语漢語English한국어日本語汉语漢語English한국어

<!-- Prettier 3.3 -->
日本語 汉语 漢語
English 한국어 日本
語 汉语 漢語 English
한국어 日本語 汉语
漢語 English 한국어
日本語 汉语 漢語
English 한국어

日本語汉语漢語
English한국어日本語
汉语漢語
English한국어日本語
汉语漢語
English한국어日本語
汉语漢語
English한국어

<!-- Prettier 3.4 -->
日本語 汉语 漢語 English
한국어
日本語 汉语 漢語 English
한국어
日本語 汉语 漢語 English
한국어
日本語 汉语 漢語 English
한국어

日本語汉语漢語English한국어日本語汉语漢語English한국어日本語汉语漢語English한국어日本語汉语漢語English한국어
```

#### Tell regexp-util to generate regex compatible with u flag ([#16816](https://github.com/prettier/prettier/pull/16816) by [@tats-u](https://github.com/tats-u))

CJK characters outside of BMP and Ideographic Variation Sequences (IVS; variation selectors dedicated for han/kanji), which consume 2 characters in JavaScript string, have not been treated as CJK. This is due to the fact that Prettier has not passed the appropriate flag `"u"` to the `regexp-util` package.

In the following example, “𠮷” (U+20BB7) is out of BMP, and “葛󠄀” is a combination of a han “葛” (U+845B) in BMP and an IVS U+E0100. The latter requires a font with the support of Adobe Japan-1 (e.g. Yu Gothic UI and Source Han Sans) to be rendered as a form different from that of the character without IVS (葛).

<!-- prettier-ignore -->
```md
<!-- Input (--prose-wrap=never) -->
a 字 a 字 a 字
𠮷
𠮷
葛󠄀
葛󠄀
終

<!-- Prettier 3.3 -->
a 字 a 字 a 字 𠮷 𠮷 葛󠄀 葛󠄀 終

<!-- Prettier 3.4 -->
a 字 a 字 a 字𠮷𠮷葛󠄀葛󠄀終

```

### YAML

#### Fix yaml comment in non-singleline items missing space before `#` ([#16489](https://github.com/prettier/prettier/pull/16489) by [@fyc09](https://github.com/fyc09))

<!-- prettier-ignore -->
```yaml
# Input
123: # hello
  # comment

# Prettier 3.3
123:# hello
  # comment

# Prettier 3.4
123: # hello
  # comment
```

### API

#### Stop doc mutation in `prettier.doc.printDocToString` ([#13315](https://github.com/prettier/prettier/pull/13315) by [@fisker](https://github.com/fisker))

For performance reason, `prettier.doc.printDocToString` used to mutate `.parts` of the [`fill`](https://github.com/prettier/prettier/blob/main/commands.md#fill) command during print. It was converted to a [pure function](https://en.wikipedia.org/wiki/Pure_function) to ensure output correctness.

#### Make `getPreferredQuote` public ([#16567](https://github.com/prettier/prettier/pull/16567) by [@sosukesuzuki](https://github.com/sosukesuzuki))

This change makes the internal `getPreferredQuote` function a part of the public API.

In languages like JavaScript, both single quotes and double quotes can be used for string literals. Prettier determines the quote to enclose a string literal based on the number of quotes within the string and the value of the [`singleQuote`](https://prettier.io/docs/options#quotes) option. For more details, please refer to the [Rationale](https://prettier.io/docs/rationale#strings) page.

The `getPreferredQuote` function determines the appropriate quote to enclose a string literal and has the following interface:

```ts
type Quote = '"' | "'";
function getPreferredQuote(
  text: string,
  preferredQuoteOrPreferSingleQuote: Quote | boolean,
): Quote;
```

Here are some examples of how to use it:

<!-- prettier-ignore -->
```jsx
import * as prettier from "prettier";

const SINGLE_QUOTE = `'`;
const DOUBLE_QUOTE = `"`;

console.log(prettier.util.getPreferredQuote(`Hello World Test`, SINGLE_QUOTE)); // '
console.log(prettier.util.getPreferredQuote(`Hello World Test`, DOUBLE_QUOTE)); // "
console.log(prettier.util.getPreferredQuote(`'Hello' "World" 'Test'`, SINGLE_QUOTE)); // "
console.log(prettier.util.getPreferredQuote(`"Hello" 'World' "Test"`, DOUBLE_QUOTE)); // '
console.log(prettier.util.getPreferredQuote(`"Hello" "World" "Test"`, SINGLE_QUOTE)); // '
console.log(prettier.util.getPreferredQuote(`'Hello' 'World' 'Test'`, DOUBLE_QUOTE)); // "
```

Making this function public will benefit plugin developers. Since the function is relatively short, you can find more details in the [implementation](https://github.com/prettier/prettier/blob/509a0cc10f12cbfa5304737965f99284449aca88/src/utils/get-preferred-quote.js).

#### Fix loading ESM-style shared config file in Node.js 23 ([#16857](https://github.com/prettier/prettier/pull/16857) by [@sosukesuzuki](https://github.com/sosukesuzuki))

In Prettier 3.3, attempting to load an ESM-style [shared config file](https://prettier.io/docs/sharing-configurations) in Node.js 23 resulted in the following warnings, preventing the options from being loaded:

```
[warn] Ignored unknown option { __esModule: true }.
[warn] Ignored unknown option { default: { trailingComma: "es5", tabWidth: 4, singleQuote: true } }.
```

This issue was caused by a new module feature in Node.js 23, known as [`require(ESM)`](https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require). Prettier 3.4 resolves this problem, allowing the options to load correctly.

For more details, please see https://github.com/prettier/prettier/issues/16812.

### CLI

#### Ignore files in the Jujutsu directory ([#16684](https://github.com/prettier/prettier/pull/16684) by [@marcusirgens](https://github.com/marcusirgens))

The Jujutsu VCS uses the `.jj` directory, similarly to how Git uses `.git`.

This change adds `.jj` to the list of directories which are silently ignored by prettier.

### Miscellaneous

#### Fix `cursorOffset` feature sometimes being catastrophically slow ([#15709](https://github.com/prettier/prettier/pull/15709) by [@ExplodingCabbage](https://github.com/ExplodingCabbage))

Previously, Prettier's `cursorOffset` feature would be spectacularly slow in certain unfortunate circumstances (namely when the user's cursor was not contained within a leaf node of the AST, and the non-leaf node containing it was very large and being significantly reformatted by Prettier). As a consequence, if you used Prettier via an editor integration that used `cursorOffset` under the hood, your editor would sometimes inexplicably hang when you tried to format a file.

All examples of this phenomenon that we are aware of should now be fixed, but bug reports of any further pathological examples would be welcome.
