diff --git a/.github/ISSUE_TEMPLATE/formatting.md b/.github/ISSUE_TEMPLATE/formatting.md
index 5d6d3a27fd91..d4691d8e9beb 100644
--- a/.github/ISSUE_TEMPLATE/formatting.md
+++ b/.github/ISSUE_TEMPLATE/formatting.md
@@ -26,7 +26,7 @@ Don't fill the form below manually! Let a program create a report for you:
-->
-**Prettier 3.9.1**
+**Prettier 3.9.2**
[Playground link](https://prettier.io/playground/#.....)
```sh
diff --git a/.github/ISSUE_TEMPLATE/integration.md b/.github/ISSUE_TEMPLATE/integration.md
index 0cdaaa6ec174..2e5ff9ecb057 100644
--- a/.github/ISSUE_TEMPLATE/integration.md
+++ b/.github/ISSUE_TEMPLATE/integration.md
@@ -20,7 +20,7 @@ BEFORE SUBMITTING AN ISSUE:
**Environments:**
-- Prettier Version: 3.9.1
+- Prettier Version: 3.9.2
- Running Prettier via:
- Runtime:
- Operating System:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 889a1f80d016..6da1c2bdce20 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,25 @@
+# 3.9.2
+
+[diff](https://github.com/prettier/prettier/compare/3.9.1...3.9.2)
+
+#### Markdown: Fix unexpected removal of characters in liquid syntax ([#19489](https://github.com/prettier/prettier/pull/19489) by [@seiyab](https://github.com/seiyab))
+
+
+```md
+// Input
+
+{{ page.title
+}} text
+
+
+{{ page.title
+ text
+
+
+{{ page.title
+}} text
+```
+
# 3.9.1
[diff](https://github.com/prettier/prettier/compare/3.9.0...3.9.1)
diff --git a/changelog_unreleased/cli/19483.md b/changelog_unreleased/cli/19483.md
deleted file mode 100644
index 55eae66d8580..000000000000
--- a/changelog_unreleased/cli/19483.md
+++ /dev/null
@@ -1,3 +0,0 @@
-#### Fix ignored file has been cached incorrectly (#19483 by @kovsu)
-
-Bug details https://github.com/prettier/prettier/issues/18016
diff --git a/changelog_unreleased/markdown/19489.md b/changelog_unreleased/markdown/19489.md
new file mode 100644
index 000000000000..51a1848214ed
--- /dev/null
+++ b/changelog_unreleased/markdown/19489.md
@@ -0,0 +1,17 @@
+#### Fix unexpected removal of characters in liquid syntax (#19489 by @seiyab)
+
+
+```md
+// Input
+
+{{ page.title
+}} text
+
+
+{{ page.title
+ text
+
+
+{{ page.title
+}} text
+```
diff --git a/package.json b/package.json
index 8924dc8979e5..e0d7b6bf9c34 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "prettier",
- "version": "3.9.1",
+ "version": "3.9.2",
"description": "Prettier is an opinionated code formatter",
"bin": "./bin/prettier.cjs",
"repository": "prettier/prettier",
@@ -179,7 +179,7 @@
"node-style-text": "2.1.2",
"npm-run-all2": "9.0.2",
"open-editor": "6.0.0",
- "prettier": "3.9.0",
+ "prettier": "3.9.1",
"pretty-bytes": "7.1.0",
"pretty-ms": "9.3.0",
"puppeteer": "25.2.1",
diff --git a/src/language-markdown/parse/micromark/micromark-extension-liquid.js b/src/language-markdown/parse/micromark/micromark-extension-liquid.js
index 808879086415..cac4a27a4d0b 100644
--- a/src/language-markdown/parse/micromark/micromark-extension-liquid.js
+++ b/src/language-markdown/parse/micromark/micromark-extension-liquid.js
@@ -33,10 +33,10 @@ function liquidFromMarkdown() {
/** @type {Handle} */
function exit(token) {
- const d = this.resume();
+ this.resume();
/** @type {any} */
const node = this.stack.at(-1);
- node.value = d;
+ node.value = this.sliceSerialize(token);
this.exit(token);
}
}
@@ -55,6 +55,9 @@ function liquidSyntax() {
};
function tokenize(effects, ok, nok) {
+ /** @type {typeof codes.rightCurlyBrace | typeof codes.percentSign} */
+ let closingCode;
+
return start;
/** @type {State} */
@@ -66,6 +69,10 @@ function liquidSyntax() {
switch (code) {
case codes.percentSign:
case codes.leftCurlyBrace:
+ closingCode =
+ code === codes.percentSign
+ ? codes.percentSign
+ : codes.rightCurlyBrace;
effects.consume(code);
return inside;
default:
@@ -77,9 +84,9 @@ function liquidSyntax() {
/** @type {State} */
function inside(code) {
switch (code) {
- case codes.percentSign:
- case codes.rightCurlyBrace:
- return effects.check({ tokenize: tokenizeClose }, close, data)(code);
+ case closingCode:
+ effects.consume(code);
+ return mayClose;
case codes.eof:
return nok(code);
default:
@@ -91,53 +98,21 @@ function liquidSyntax() {
effects.enter(types.data);
return inside;
}
- return data(code);
+ effects.consume(code);
+ return inside;
}
}
/** @type {State} */
- function data(code) {
- effects.consume(code);
- return inside;
- }
-
- function tokenizeClose(effects, ok, nok) {
- return startClose;
-
- /** @type {State} */
- function startClose(code) {
- switch (code) {
- case codes.percentSign:
- case codes.rightCurlyBrace:
- effects.consume(code);
- return endClose;
- default:
- return nok(code);
- }
- }
-
- /** @type {State} */
- function endClose(code) {
- if (code !== codes.rightCurlyBrace) {
- return nok(code);
- }
+ function mayClose(code) {
+ if (code === codes.rightCurlyBrace) {
effects.consume(code);
+ effects.exit(types.data);
+ effects.exit(nodeType);
return ok;
}
- }
-
- /** @type {State} */
- function close(code) {
- effects.consume(code);
- return closeEnd;
- }
- /** @type {State} */
- function closeEnd(code) {
- effects.consume(code);
- effects.exit(types.data);
- effects.exit(nodeType);
- return ok;
+ return inside;
}
}
}
diff --git a/tests/format/markdown/liquid/__snapshots__/format.test.js.snap b/tests/format/markdown/liquid/__snapshots__/format.test.js.snap
index d79547f8fd50..f7220d61af0c 100644
--- a/tests/format/markdown/liquid/__snapshots__/format.test.js.snap
+++ b/tests/format/markdown/liquid/__snapshots__/format.test.js.snap
@@ -1,5 +1,63 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+exports[`character-after-closing-tokens.md - {"proseWrap":"always"} format 1`] = `
+====================================options=====================================
+parsers: ["markdown"]
+proseWrap: "always"
+ printWidth: 80 (default) |
+=====================================input======================================
+{{ page.title
+}} a
+
+---
+
+{%
+ if user
+%} Hello, {{ user }}!
+{% endif %}
+
+=====================================output=====================================
+{{ page.title
+}} a
+
+---
+
+{%
+ if user
+%} Hello, {{ user }}! {% endif %}
+
+================================================================================
+`;
+
+exports[`character-after-closing-tokens.md format 1`] = `
+====================================options=====================================
+parsers: ["markdown"]
+ printWidth: 80 (default) |
+=====================================input======================================
+{{ page.title
+}} a
+
+---
+
+{%
+ if user
+%} Hello, {{ user }}!
+{% endif %}
+
+=====================================output=====================================
+{{ page.title
+}} a
+
+---
+
+{%
+ if user
+%} Hello, {{ user }}!
+{% endif %}
+
+================================================================================
+`;
+
exports[`example-1.md - {"proseWrap":"always"} format 1`] = `
====================================options=====================================
parsers: ["markdown"]
@@ -216,6 +274,138 @@ parsers: ["markdown"]
================================================================================
`;
+exports[`issue-19488.md - {"proseWrap":"always"} format 1`] = `
+====================================options=====================================
+parsers: ["markdown"]
+proseWrap: "always"
+ printWidth: 80 (default) |
+=====================================input======================================
+{{% blocks/cover
+title="Welcome"
+%}}
+
+This input uses Hugo shortcode. Prettier doesn't support this explicitly but it shouldn't break it either.
+
+{{% /blocks/cover %}}
+
+---
+
+{{% blocks/cover
+%}}
+
+{{% blocks/cover
+%%}}
+
+{{% blocks/cover
+}}}
+
+{{{% blocks/cover
+%}}
+
+{% blocks/cover
+%}}
+
+{{ blocks/cover
+%}}
+
+=====================================output=====================================
+{{% blocks/cover
+title="Welcome"
+%}}
+
+This input uses Hugo shortcode. Prettier doesn't support this explicitly but it
+shouldn't break it either.
+
+{{% /blocks/cover %}}
+
+---
+
+{{% blocks/cover
+%}}
+
+{{% blocks/cover
+%%}}
+
+{{% blocks/cover
+}}}
+
+{{{% blocks/cover
+%}}
+
+{% blocks/cover
+%}}
+
+{{ blocks/cover
+%}}
+
+================================================================================
+`;
+
+exports[`issue-19488.md format 1`] = `
+====================================options=====================================
+parsers: ["markdown"]
+ printWidth: 80 (default) |
+=====================================input======================================
+{{% blocks/cover
+title="Welcome"
+%}}
+
+This input uses Hugo shortcode. Prettier doesn't support this explicitly but it shouldn't break it either.
+
+{{% /blocks/cover %}}
+
+---
+
+{{% blocks/cover
+%}}
+
+{{% blocks/cover
+%%}}
+
+{{% blocks/cover
+}}}
+
+{{{% blocks/cover
+%}}
+
+{% blocks/cover
+%}}
+
+{{ blocks/cover
+%}}
+
+=====================================output=====================================
+{{% blocks/cover
+title="Welcome"
+%}}
+
+This input uses Hugo shortcode. Prettier doesn't support this explicitly but it shouldn't break it either.
+
+{{% /blocks/cover %}}
+
+---
+
+{{% blocks/cover
+%}}
+
+{{% blocks/cover
+%%}}
+
+{{% blocks/cover
+}}}
+
+{{{% blocks/cover
+%}}
+
+{% blocks/cover
+%}}
+
+{{ blocks/cover
+%}}
+
+================================================================================
+`;
+
exports[`unbalanced-mismatched-braces.md - {"proseWrap":"always"} format 1`] = `
====================================options=====================================
parsers: ["markdown"]
@@ -226,8 +416,8 @@ proseWrap: "always"
Braces {% doesn't match so these words should not be considered as *liquid node* }}
=====================================output=====================================
-Braces
-{% doesn't match so these words should not be considered as *liquid node* }}
+Braces {% doesn't match so these words should not be considered as _liquid node_
+}}
================================================================================
`;
@@ -241,7 +431,7 @@ parsers: ["markdown"]
Braces {% doesn't match so these words should not be considered as *liquid node* }}
=====================================output=====================================
-Braces {% doesn't match so these words should not be considered as *liquid node* }}
+Braces {% doesn't match so these words should not be considered as _liquid node_ }}
================================================================================
`;
diff --git a/tests/format/markdown/liquid/character-after-closing-tokens.md b/tests/format/markdown/liquid/character-after-closing-tokens.md
new file mode 100644
index 000000000000..b3c601f9b856
--- /dev/null
+++ b/tests/format/markdown/liquid/character-after-closing-tokens.md
@@ -0,0 +1,9 @@
+{{ page.title
+}} a
+
+---
+
+{%
+ if user
+%} Hello, {{ user }}!
+{% endif %}
diff --git a/tests/format/markdown/liquid/issue-19488.md b/tests/format/markdown/liquid/issue-19488.md
new file mode 100644
index 000000000000..71d14d01b0e9
--- /dev/null
+++ b/tests/format/markdown/liquid/issue-19488.md
@@ -0,0 +1,27 @@
+{{% blocks/cover
+title="Welcome"
+%}}
+
+This input uses Hugo shortcode. Prettier doesn't support this explicitly but it shouldn't break it either.
+
+{{% /blocks/cover %}}
+
+---
+
+{{% blocks/cover
+%}}
+
+{{% blocks/cover
+%%}}
+
+{{% blocks/cover
+}}}
+
+{{{% blocks/cover
+%}}
+
+{% blocks/cover
+%}}
+
+{{ blocks/cover
+%}}
diff --git a/website/versioned_docs/version-stable/browser.md b/website/versioned_docs/version-stable/browser.md
index aeb13c1833c5..6aa6f27950a3 100644
--- a/website/versioned_docs/version-stable/browser.md
+++ b/website/versioned_docs/version-stable/browser.md
@@ -21,7 +21,7 @@ Required options:
- **[`parser`](options.md#parser) (or [`filepath`](options.md#file-path))**: One of these options has to be specified for Prettier to know which parser to use.
-- **`plugins`**: Unlike the `format` function from the [Node.js-based API](api.md#prettierformatsource-options), this function doesn’t load plugins automatically. The `plugins` option is required because all the parsers included in the Prettier package come as plugins (for reasons of file size). These plugins are files in [https://unpkg.com/browse/prettier@3.9.1/plugins](https://unpkg.com/browse/prettier@3.9.1/plugins). Note that `estree` plugin should be loaded when printing JavaScript, TypeScript, Flow, or JSON.
+- **`plugins`**: Unlike the `format` function from the [Node.js-based API](api.md#prettierformatsource-options), this function doesn’t load plugins automatically. The `plugins` option is required because all the parsers included in the Prettier package come as plugins (for reasons of file size). These plugins are files in [https://unpkg.com/browse/prettier@3.9.2/plugins](https://unpkg.com/browse/prettier@3.9.2/plugins). Note that `estree` plugin should be loaded when printing JavaScript, TypeScript, Flow, or JSON.
You need to load the ones that you’re going to use and pass them to `prettier.format` using the `plugins` option.
@@ -32,8 +32,8 @@ See below for examples.
### Global
```html
-
-
+
+