Repository: prettier/prettier (JavaScript)
Date: 2023-01-13 02:01:08+00:00
Task type: Feature

Issue:
Angular templates self closing tags
<!--

BEFORE SUBMITTING AN ISSUE:

1.  Search for your issue on GitHub: https://github.com/prettier/prettier/issues
    A large number of opened issues are duplicates of existing issues.
    If someone has already opened an issue for what you are experiencing,
    you do not need to open a new issue — please add a 👍 reaction to the
    existing issue instead.

2.  We get a lot of requests for adding options, but Prettier is
    built on the principle of being opinionated about code formatting.
    This means we add options only in the case of strict technical necessity.
    Find out more: https://prettier.io/docs/en/option-philosophy.html

Don't fill the form below manually! Let a program create a report for you:

1. Go to https://prettier.io/playground
2. Paste your code and set options
3. Press the "Report issue" button in the lower right

-->

**Prettier 2.8.2**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAeAhgB0wWngZxgHoA+AHShABoQJMYBLafZUdAJ3YgHcAFDhCxToANt3QBPFjQBG7dGADWcGAGV0AWzgAZBlDjIAZqPxxZ8pStWYFegObIY7AK5mQcDTLgATbz+3oUHbO6HZwAGIQ7BroMIxByCDozjAQ1CAAFjAaIgDqGQwENmBwqoKFDABuhRKJYPjSIHqm7DC88nYxRiZuAFb4AB6q9iJwAIrOEPDdIqY0NuwtiYHBIhzpmOx6MLkM3jAZyAAcAAzzXKa58piJm3AtlQY0AI6T8O10Qkn4OPo+Pul2HBXgwge1Ql0kMZZm5TBoGI4XLCRuM3gYoT0aDB0DJdvtDkgAExY+QMET2ADCEA0kPc+AArOlnKYACo4oTQuYgSquACSUD8sFUYC29AAggLVDAJKMZqYAL7yoA)

**Input:**

```jsx
<app-test/>
```

**Output:**

```jsx
SyntaxError: Only void and foreign elements can be self closed "app-test" (1:1)
> 1 | <app-test/>
    | ^^^^^^^^^
  2 |
```

**Expected behavior:**
In [Angular 15.1.0](https://github.com/angular/angular/releases/tag/15.1.0) developers are allowed to use self closed tags for custom elements https://github.com/angular/angular/pull/48535
Prettier should support this feature



Issue discussion:

Solution patch:
diff --git a/changelog_unreleased/angular/14170.md b/changelog_unreleased/angular/14170.md
new file mode 100644
index 000000000000..471169c7dbfe
--- /dev/null
+++ b/changelog_unreleased/angular/14170.md
@@ -0,0 +1,18 @@
+#### Allow self-closing tags on custom elements (#14170 by @fisker)
+
+See [Angular v15.1.0 release note](https://github.com/angular/angular/releases/tag/15.1.0) for details.
+
+<!-- prettier-ignore -->
+```html
+// Input
+<app-test/>
+
+// Prettier stable
+SyntaxError: Only void and foreign elements can be self closed "app-test" (1:1)
+> 1 | <app-test/>
+    | ^^^^^^^^^
+  2 |
+
+// Prettier main
+<app-test />
+```
diff --git a/src/language-html/parser-html.js b/src/language-html/parser-html.js
index 5763fe7c72b9..a959d6e11cd2 100644
--- a/src/language-html/parser-html.js
+++ b/src/language-html/parser-html.js
@@ -24,7 +24,7 @@ const { locStart, locEnd } = require("./loc.js");
  * @typedef {import('angular-html-parser/lib/compiler/src/ml_parser/parser').ParseTreeResult} ParserTreeResult
  * @typedef {Omit<import('angular-html-parser').ParseOptions, 'canSelfClose'> & {
  *   name?: 'html' | 'angular' | 'vue' | 'lwc';
- *   recognizeSelfClosing?: boolean;
+ *   canSelfClose?: boolean;
  *   normalizeTagName?: boolean;
  *   normalizeAttributeName?: boolean;
  * }} ParserOptions
@@ -42,7 +42,7 @@ const { locStart, locEnd } = require("./loc.js");
 function ngHtmlParser(
   input,
   {
-    recognizeSelfClosing,
+    canSelfClose,
     normalizeTagName,
     normalizeAttributeName,
     allowHtmComponentClosingTags,
@@ -64,7 +64,7 @@ function ngHtmlParser(
   } = require("angular-html-parser/lib/compiler/src/ml_parser/html_tags");
 
   let { rootNodes, errors } = parser.parse(input, {
-    canSelfClose: recognizeSelfClosing,
+    canSelfClose,
     allowHtmComponentClosingTags,
     isTagNameCaseSensitive,
     getTagContentType,
@@ -97,7 +97,7 @@ function ngHtmlParser(
         let secondParseResult;
         const doSecondParse = () =>
           parser.parse(input, {
-            canSelfClose: recognizeSelfClosing,
+            canSelfClose,
             allowHtmComponentClosingTags,
             isTagNameCaseSensitive,
           });
@@ -135,13 +135,13 @@ function ngHtmlParser(
       }
     } else {
       // If not Vue SFC, treat as html
-      recognizeSelfClosing = true;
+      canSelfClose = true;
       normalizeTagName = true;
       normalizeAttributeName = true;
       allowHtmComponentClosingTags = true;
       isTagNameCaseSensitive = false;
       const htmlParseResult = parser.parse(input, {
-        canSelfClose: recognizeSelfClosing,
+        canSelfClose,
         allowHtmComponentClosingTags,
         isTagNameCaseSensitive,
       });
@@ -373,7 +373,7 @@ function _parse(text, options, parserOptions, shouldParseFrontMatter = true) {
  */
 function createParser({
   name,
-  recognizeSelfClosing = false,
+  canSelfClose = false,
   normalizeTagName = false,
   normalizeAttributeName = false,
   allowHtmComponentClosingTags = false,
@@ -386,7 +386,7 @@ function createParser({
         text,
         { parser: name, ...options },
         {
-          recognizeSelfClosing,
+          canSelfClose,
           normalizeTagName,
           normalizeAttributeName,
           allowHtmComponentClosingTags,
@@ -405,15 +405,15 @@ module.exports = {
   parsers: {
     html: createParser({
       name: "html",
-      recognizeSelfClosing: true,
+      canSelfClose: true,
       normalizeTagName: true,
       normalizeAttributeName: true,
       allowHtmComponentClosingTags: true,
     }),
-    angular: createParser({ name: "angular" }),
+    angular: createParser({ name: "angular", canSelfClose: true }),
     vue: createParser({
       name: "vue",
-      recognizeSelfClosing: true,
+      canSelfClose: true,
       isTagNameCaseSensitive: true,
       getTagContentType: (tagName, prefix, hasParent, attrs) => {
         if (


Test patch:
diff --git a/tests/format/angular/self-closing/__snapshots__/jsfmt.spec.js.snap b/tests/format/angular/self-closing/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..f4a624bed741
--- /dev/null
+++ b/tests/format/angular/self-closing/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,36 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`self-closing.component.html format 1`] = `
+====================================options=====================================
+parsers: ["angular"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<app-test/>
+<app-test />
+<app-test
+/>
+<img>
+<img/>
+<img />
+<img
+/>
+<div/>
+<div />
+<div
+/>
+
+=====================================output=====================================
+<app-test />
+<app-test />
+<app-test />
+<img />
+<img />
+<img />
+<img />
+<div />
+<div />
+<div />
+
+================================================================================
+`;
diff --git a/tests/format/angular/self-closing/jsfmt.spec.js b/tests/format/angular/self-closing/jsfmt.spec.js
new file mode 100644
index 000000000000..b802ad8670c5
--- /dev/null
+++ b/tests/format/angular/self-closing/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname, ["angular"]);
diff --git a/tests/format/angular/self-closing/self-closing.component.html b/tests/format/angular/self-closing/self-closing.component.html
new file mode 100644
index 000000000000..06e1d53f7657
--- /dev/null
+++ b/tests/format/angular/self-closing/self-closing.component.html
@@ -0,0 +1,13 @@
+<app-test/>
+<app-test />
+<app-test
+/>
+<img>
+<img/>
+<img />
+<img
+/>
+<div/>
+<div />
+<div
+/>