mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(formatter_core): keep front matter verbatim when body starts with indented delimiter (#27946)
``` --- --- # <- THIS should be kept, otherwise closes FM! --- ```
This commit is contained in:
1 parent
c22a9b2742
commit
529b057811
6 files changed
+159
-5
No files matched your search
@@ -275,3 +275,32 @@ Prettier keeps `json` / `json5` blocks as written: their parser rejects a blank
|
||||
`prettier-plugin-astro` formats it with bundled `sass-formatter`, which we do not bundle (patched out).
|
||||
Rare in Astro projects (GitHub code search, 2026-10: about 350 files in 72 repos, vs 29k for `lang="scss"`).
|
||||
Not tested but `oxc-css-parser` already parses it, so we can add Sass support for `oxc_formatter_css` in the future.
|
||||
|
||||
## front-matter-indented-delimiter
|
||||
|
||||
- Why: semantics
|
||||
- Pin: `conformance/fixtures/edge-cases/xxx-in-md/front-matter-indented-delimiter.md`
|
||||
|
||||
```markdown
|
||||
<!-- input -->
|
||||
---
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
<!-- ours -->
|
||||
---
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
<!-- prettier -->
|
||||
---
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
```
|
||||
|
||||
A front matter block whose body starts with an indented delimiter (` ---`, ` ...`, ` +++`) stays verbatim in CSS and Markdown (`write_front_matter` in `oxc_formatter_core`).
|
||||
The indented delimiter does not close the block, but the body is trimmed before formatting, so it prints at the start of a line.
|
||||
On the next parse (Prettier's and ours), the block closes there and the rest of the body moves into the document.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
---
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
# Heading
|
||||
@@ -254,12 +254,14 @@
|
||||
|
||||
## markdown
|
||||
|
||||
### Option 1: 273/282 (96.81%)
|
||||
### Option 1: 273/283 (96.47%)
|
||||
|
||||
```json
|
||||
{"printWidth":80}
|
||||
```
|
||||
|
||||
- [edge-cases/xxx-in-md/front-matter-indented-delimiter.md](diffs/markdown/edge-cases__xxx-in-md__front-matter-indented-delimiter.md.md)
|
||||
- apps/oxfmt/DIVERGENCES.md#front-matter-indented-delimiter
|
||||
- [edge-cases/xxx-in-md/line-ranged-code-block.md](diffs/markdown/edge-cases__xxx-in-md__line-ranged-code-block.md.md)
|
||||
- apps/oxfmt/DIVERGENCES.md#line-ranged-code-block
|
||||
- [edge-cases/xxx-in-md/lwc-code-block.md](diffs/markdown/edge-cases__xxx-in-md__lwc-code-block.md.md)
|
||||
@@ -279,12 +281,14 @@
|
||||
- [externals/prettier/markdown/thematicBreak/simple.md](diffs/markdown/externals__prettier__markdown__thematicBreak__simple.md.md)
|
||||
- crates/oxc_formatter_markdown/DIVERGENCES.md#leading-thematic-break
|
||||
|
||||
### Option 2: 269/282 (95.39%)
|
||||
### Option 2: 269/283 (95.05%)
|
||||
|
||||
```json
|
||||
{"printWidth":100,"proseWrap":"always"}
|
||||
```
|
||||
|
||||
- [edge-cases/xxx-in-md/front-matter-indented-delimiter.md](diffs/markdown/edge-cases__xxx-in-md__front-matter-indented-delimiter.md.md)
|
||||
- apps/oxfmt/DIVERGENCES.md#front-matter-indented-delimiter
|
||||
- [edge-cases/xxx-in-md/line-ranged-code-block.md](diffs/markdown/edge-cases__xxx-in-md__line-ranged-code-block.md.md)
|
||||
- apps/oxfmt/DIVERGENCES.md#line-ranged-code-block
|
||||
- [edge-cases/xxx-in-md/lwc-code-block.md](diffs/markdown/edge-cases__xxx-in-md__lwc-code-block.md.md)
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# edge-cases/xxx-in-md/front-matter-indented-delimiter.md
|
||||
|
||||
## Option 1
|
||||
|
||||
`````json
|
||||
{"printWidth":80}
|
||||
`````
|
||||
|
||||
### Diff
|
||||
|
||||
`````diff
|
||||
===================================================================
|
||||
--- prettier
|
||||
+++ oxfmt
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
+ ---
|
||||
+title: Home
|
||||
---
|
||||
-title: Home
|
||||
----
|
||||
|
||||
# Heading
|
||||
|
||||
`````
|
||||
|
||||
### Actual (oxfmt)
|
||||
|
||||
`````md
|
||||
---
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
# Heading
|
||||
|
||||
`````
|
||||
|
||||
### Expected (prettier)
|
||||
|
||||
`````md
|
||||
---
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
# Heading
|
||||
|
||||
`````
|
||||
|
||||
## Option 2
|
||||
|
||||
`````json
|
||||
{"printWidth":100,"proseWrap":"always"}
|
||||
`````
|
||||
|
||||
### Diff
|
||||
|
||||
`````diff
|
||||
===================================================================
|
||||
--- prettier
|
||||
+++ oxfmt
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
+ ---
|
||||
+title: Home
|
||||
---
|
||||
-title: Home
|
||||
----
|
||||
|
||||
# Heading
|
||||
|
||||
`````
|
||||
|
||||
### Actual (oxfmt)
|
||||
|
||||
`````md
|
||||
---
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
# Heading
|
||||
|
||||
`````
|
||||
|
||||
### Expected (prettier)
|
||||
|
||||
`````md
|
||||
---
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
# Heading
|
||||
|
||||
`````
|
||||
@@ -63,6 +63,16 @@ describe("CSS front matter", () => {
|
||||
expect(result.code).toBe("---\nkey: [unclosed\n---\n\nb {\n}\n");
|
||||
});
|
||||
|
||||
it("keeps the block verbatim when its body starts with an indented delimiter", async () => {
|
||||
// DIVERGENCES.md#front-matter-indented-delimiter
|
||||
for (const block of ["---\n ---\na: b\n---", "---\n ---\n---", "---\n ...\na: b\n..."]) {
|
||||
// oxlint-disable-next-line no-await-in-loop
|
||||
const result = await format("a.css", `${block}\nb {}\n`);
|
||||
expect(result.errors).toStrictEqual([]);
|
||||
expect(result.code).toBe(`${block}\n\nb {\n}\n`);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps the block verbatim under embeddedLanguageFormatting: off", async () => {
|
||||
const result = await format("a.css", "---\ntitle: Home\n---\nb {}\n", {
|
||||
embeddedLanguageFormatting: "off",
|
||||
|
||||
@@ -23,7 +23,8 @@ use crate::{
|
||||
/// Membership means "gets the frame treatment", not "gets formatted":
|
||||
/// a member language without a serving formatter keeps its body verbatim (`PreserveOriginal`),
|
||||
/// but its EMPTY block still normalizes through the frame when a dispatcher is installed.
|
||||
/// Never fails; any refusal (`PreserveOriginal`, operational error, no dispatcher, non-embeddable language)
|
||||
/// Never fails; any refusal (`PreserveOriginal`, operational error, no dispatcher, non-embeddable language,
|
||||
/// a body starting with a delimiter)
|
||||
/// keeps the block's bytes as-is while the host document still formats.
|
||||
///
|
||||
/// The composed shape (Prettier `embed.js` + its css/markdown printers):
|
||||
@@ -42,14 +43,21 @@ pub fn write_front_matter<'a, C: FormatContext>(
|
||||
write_frame(fm, None, f);
|
||||
return;
|
||||
}
|
||||
// An indented delimiter on the first line (` ---`) never closes the block,
|
||||
// but trimmed and formatted it starts a line and closes it early on the next parse.
|
||||
// Keep the block verbatim, see DIVERGENCES.md#front-matter-indented-delimiter.
|
||||
//
|
||||
// The body is a Fragment:
|
||||
// front matter inside front matter must never acquire envelope semantics
|
||||
// (generic FM recursion).
|
||||
if let Some(ir) = dispatch_fragment_ir(f, language, body, None) {
|
||||
if !body.starts_with(fm.start_delimiter)
|
||||
&& !body.starts_with(fm.end_delimiter)
|
||||
&& let Some(ir) = dispatch_fragment_ir(f, language, body, None)
|
||||
{
|
||||
write_frame(fm, Some(ir), f);
|
||||
return;
|
||||
}
|
||||
// `PreserveOriginal` or an operational error:
|
||||
// A body starting with a delimiter, `PreserveOriginal` or an operational error:
|
||||
// fall through to the verbatim block below.
|
||||
}
|
||||
write!(f, text(fm.raw));
|
||||
|
||||
Reference in new issue
Block a user