Repository: prettier/prettier (JavaScript)

Issue:
handlebars attrbutes changed: {{@x.y.z}} becomes {{@x}}
**Prettier 3.3.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEACVAeAJgSwG7qFgA2AhgM7kC8AOiAjAE4CedhqAfDVOhgBYBGDsGDoYOGMTjsAvjIwB6QVx6ZcBdCQrU6AIwhZWIFexEABAB4A6ZlYBec7u2eERhfYZeFHUReo6YCv4gADQgEAAO4tDkyKCkjIwQAO4ACgkIsSikxMmkzLFhuoykYADWcDAAyqQAtnAAMjhQcMgAZjnkcEUl5ZVVEaXNAObITACu3SBdtThjjJNhcBYRcIw49bA5ACprUAk4cFkdxF1h5CNSAIrjEPDtnVMAVuQWVZdwN3etSCdnIABHW7wVJJCJZEAUAC0LTgWDhoRATFIOGIIwAwhBarVSMhIcRiIiLlBhlIAIIwJg4XTjEFrJotB6nKZ8GC1YgAdT4EiOgzAcCqmQk+AkzDxYEoiLwkwAklB4bAqmB1lEyfKqjBmFImf8IkkuhyShE8Xqjms8K0ws0uowYKDSMMcTqpoNGDa8aSNvVGIi9c0YBycFgYHxkAAOAAMYUYcCBOBj9sduN+jzCMFIukDwdDSAATGFxl1thnjqn6LVdHD4VgGqQSeMHXAAGIQRg4ykjPGkWkQEByIA)
<!-- prettier-ignore -->
```sh
--parser glimmer
```

**Input:**
<!-- prettier-ignore -->
```hbs
  <div     class="entry"    >
  <h1>{{  title    }}</h1>
  <div   class="body">
    {{@x.y.z}}
            {{   body         }}
</div> </div>
```

**Output:**
<!-- prettier-ignore -->
```hbs
<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{@x}}
    {{body}}
  </div>
</div>
```

**Expected behavior:**
Starting with 3.3.1 {{@x.y.z}} is output as {{@x}} , if I add `--debug-check` it will catch that the output is different


Hints:
Sorry for breaking your code, it's caused by #16346, print `node.tail` [here](https://github.com/prettier/prettier/blob/13163d9b7543021d1f24edadbc0e5a2b1598d478/src/language-handlebars/printer-glimmer.js#L796) should be able to fix it.

Do you want work on it?


Patch:
diff --git a/src/language-handlebars/clean.js b/src/language-handlebars/clean.js
--- a/src/language-handlebars/clean.js
+++ b/src/language-handlebars/clean.js
@@ -32,6 +32,10 @@ function clean(original, cloned /*, parent*/) {
   if (original.type === "AttrNode" && original.name.toLowerCase() === "class") {
     delete cloned.value;
   }
+
+  if (original.type === "PathExpression") {
+    cloned.head = original.head.original;
+  }
 }
 
 clean.ignoredProperties = new Set(["loc", "selfClosing"]);
diff --git a/src/language-handlebars/printer-glimmer.js b/src/language-handlebars/printer-glimmer.js
--- a/src/language-handlebars/printer-glimmer.js
+++ b/src/language-handlebars/printer-glimmer.js
@@ -366,10 +366,7 @@ function print(path, options, print) {
       ];
     }
     case "PathExpression":
-      return printPathExpression(node, print);
-
-    case "AtHead":
-      return node.name;
+      return printPathExpression(node);
 
     case "BooleanLiteral":
       return String(node.value);
@@ -389,6 +386,7 @@ function print(path, options, print) {
     case "NullLiteral":
       return "null";
 
+    case "AtHead": // Handled in `printPathExpression`
     case "VarHead": // Handled in `printPathExpression`
     case "ThisHead": // Handled in `printPathExpression`
     default:
@@ -784,21 +782,23 @@ const PATH_EXPRESSION_FORBIDDEN_IN_FIRST_PART = new Set([
   "null",
   "undefined",
 ]);
-const isPathExpressionPartNeedBrackets = (part, index) =>
-  (index !== 0 && PATH_EXPRESSION_FORBIDDEN_IN_FIRST_PART.has(part)) ||
-  /\s/.test(part) ||
-  /^\d/.test(part) ||
-  Array.prototype.some.call(part, (character) =>
-    PATH_EXPRESSION_FORBIDDEN_CHARACTERS.has(character),
-  );
-function printPathExpression(node, print) {
-  if (node.head.type === "AtHead") {
-    return print("head");
+const isPathExpressionPartNeedBrackets = (part, index) => {
+  if (index === 0 && part.startsWith("@")) {
+    return false;
   }
 
+  return (
+    (index !== 0 && PATH_EXPRESSION_FORBIDDEN_IN_FIRST_PART.has(part)) ||
+    /\s/.test(part) ||
+    /^\d/.test(part) ||
+    Array.prototype.some.call(part, (character) =>
+      PATH_EXPRESSION_FORBIDDEN_CHARACTERS.has(character),
+    )
+  );
+};
+function printPathExpression(node) {
+  // check if node is a legacy path expression and leave it alone
   if (node.tail.length === 0 && node.original.includes("/")) {
-    // check if node has data, or
-    // check if node is a legacy path expression (and leave it alone)
     return node.original;
   }
 
diff --git a/src/language-handlebars/visitor-keys.evaluate.js b/src/language-handlebars/visitor-keys.evaluate.js
--- a/src/language-handlebars/visitor-keys.evaluate.js
+++ b/src/language-handlebars/visitor-keys.evaluate.js
@@ -1,11 +1 @@
-import { visitorKeys as glimmerVisitorKeys } from "@glimmer/syntax";
-
-const visitorKeys = {
-  VarHead: [],
-  ThisHead: [],
-  AtHead: [],
-  ...glimmerVisitorKeys,
-  PathExpression: [...glimmerVisitorKeys.PathExpression, "head"],
-};
-
-export default visitorKeys;
+export { visitorKeys as default } from "@glimmer/syntax";


Test patch:
diff --git a/tests/format/handlebars/path-expressions/__snapshots__/format.test.js.snap b/tests/format/handlebars/path-expressions/__snapshots__/format.test.js.snap
--- a/tests/format/handlebars/path-expressions/__snapshots__/format.test.js.snap
+++ b/tests/format/handlebars/path-expressions/__snapshots__/format.test.js.snap
@@ -42,6 +42,10 @@ printWidth: 80
   {{array.2.[@#].[1]}}
   {{array.2.[a b]}}
   {{this.test}}
+  {{@x.y.z}}
+  {{@array.[true]}}
+  {{@array.2.[@#].[1]}}
+  {{@array.2.[a b]}}
   {{#if @foo}}
   {{/if}}
   <!-- legacy path format, which glimmer does not parse - we don't want to wrap with brackets -->
@@ -54,6 +58,10 @@ printWidth: 80
   {{array.[2].[@#].[1]}}
   {{array.[2].[a b]}}
   {{this.test}}
+  {{@x.y.z}}
+  {{@array.[true]}}
+  {{@array.[2].[@#].[1]}}
+  {{@array.[2].[a b]}}
   {{#if @foo}}{{/if}}
   <!-- legacy path format, which glimmer does not parse - we don't want to wrap with brackets -->
   {{foo/bar/baz ab=cd}}
diff --git a/tests/format/handlebars/path-expressions/literal-expressions.hbs b/tests/format/handlebars/path-expressions/literal-expressions.hbs
--- a/tests/format/handlebars/path-expressions/literal-expressions.hbs
+++ b/tests/format/handlebars/path-expressions/literal-expressions.hbs
@@ -3,6 +3,10 @@
   {{array.2.[@#].[1]}}
   {{array.2.[a b]}}
   {{this.test}}
+  {{@x.y.z}}
+  {{@array.[true]}}
+  {{@array.2.[@#].[1]}}
+  {{@array.2.[a b]}}
   {{#if @foo}}
   {{/if}}
   <!-- legacy path format, which glimmer does not parse - we don't want to wrap with brackets -->
diff --git a/tests/unit/__snapshots__/visitor-keys.js.snap b/tests/unit/__snapshots__/visitor-keys.js.snap
--- a/tests/unit/__snapshots__/visitor-keys.js.snap
+++ b/tests/unit/__snapshots__/visitor-keys.js.snap
@@ -1165,7 +1165,6 @@ exports[`visitor keys estree-json 1`] = `
 
 exports[`visitor keys glimmer 1`] = `
 {
-  "AtHead": [],
   "AttrNode": [
     "value",
   ],
@@ -1209,9 +1208,7 @@ exports[`visitor keys glimmer 1`] = `
   ],
   "NullLiteral": [],
   "NumberLiteral": [],
-  "PathExpression": [
-    "head",
-  ],
+  "PathExpression": [],
   "StringLiteral": [],
   "SubExpression": [
     "path",
@@ -1222,9 +1219,7 @@ exports[`visitor keys glimmer 1`] = `
     "body",
   ],
   "TextNode": [],
-  "ThisHead": [],
   "UndefinedLiteral": [],
-  "VarHead": [],
 }
 `;