diff --git a/changelog_unreleased/angular/15926.md b/changelog_unreleased/angular/15926.md
new file mode 100644
index 000000000..15a7d1bba
--- /dev/null
+++ b/changelog_unreleased/angular/15926.md
@@ -0,0 +1,42 @@
+#### Respect `trailingComma` in Angular templates (#15926 by @fisker)
+
+If you are using an old version of Angular doesn't support trailing comma, make sure add
+
+```yaml
+overrides:
+ - files: ""
+ options:
+ trailingComma: none
+```
+
+to your `.prettierrc` file.
+
+
+```jsx
+// Input
+
+
+// Prettier stable
+
+
+// Prettier main
+
+```
diff --git a/src/language-html/embed.js b/src/language-html/embed.js
index 3a5924a92..8a339f39a 100644
--- a/src/language-html/embed.js
+++ b/src/language-html/embed.js
@@ -114,7 +114,6 @@ function embed(path, options) {
};
if (options.parser === "angular") {
textToDocOptions.parser = "__ng_interpolation";
- textToDocOptions.trailingComma = "none";
} else if (options.parser === "vue") {
textToDocOptions.parser = isVueSfcWithTypescriptScript(
path,
diff --git a/src/language-html/embed/angular-attributes.js b/src/language-html/embed/angular-attributes.js
index 8a21a1d2f..431c66ff9 100644
--- a/src/language-html/embed/angular-attributes.js
+++ b/src/language-html/embed/angular-attributes.js
@@ -18,11 +18,7 @@ function createAngularPrinter({ parser }) {
formatAttributeValue(
getUnescapedAttributeValue(path.node),
textToDoc,
- {
- parser,
- // angular does not allow trailing comma
- trailingComma: "none",
- },
+ { parser },
shouldHugJsExpression,
);
}
diff --git a/src/language-html/embed/angular-control-flow-block-parameters.js b/src/language-html/embed/angular-control-flow-block-parameters.js
index 46750cd5d..c12b49964 100644
--- a/src/language-html/embed/angular-control-flow-block-parameters.js
+++ b/src/language-html/embed/angular-control-flow-block-parameters.js
@@ -24,7 +24,6 @@ function printAngularControlFlowBlockParameters(
{
parser: "__ng_directive",
__isInHtmlAttribute: false,
- trailingComma: "none",
},
shouldHugJsExpression,
);
diff --git a/src/language-html/embed/angular-interpolation.js b/src/language-html/embed/angular-interpolation.js
index 53caceeb8..2fc953739 100644
--- a/src/language-html/embed/angular-interpolation.js
+++ b/src/language-html/embed/angular-interpolation.js
@@ -19,7 +19,6 @@ async function printAngularInterpolation(text, textToDoc) {
await formatAttributeValue(part, textToDoc, {
parser: "__ng_interpolation",
__isInHtmlInterpolation: true, // to avoid unexpected `}}`
- trailingComma: "none",
}),
]),
line,
diff --git a/src/language-js/print/call-arguments.js b/src/language-js/print/call-arguments.js
index ca6b65c0f..affc3320c 100644
--- a/src/language-js/print/call-arguments.js
+++ b/src/language-js/print/call-arguments.js
@@ -82,7 +82,12 @@ function printCallArguments(path, options, print) {
const isDynamicImport =
node.type === "ImportExpression" || node.callee.type === "Import";
const maybeTrailingComma =
- !isDynamicImport && shouldPrintComma(options, "all") ? "," : "";
+ // Angular does not allow trailing comma
+ !options.parser.startsWith("__ng_") &&
+ !isDynamicImport &&
+ shouldPrintComma(options, "all")
+ ? ","
+ : "";
function allArgsBrokenOut() {
return group(
diff --git a/tests/format/angular/angular/__snapshots__/format.test.js.snap b/tests/format/angular/angular/__snapshots__/format.test.js.snap
index f6aa3e031..f257aeded 100644
--- a/tests/format/angular/angular/__snapshots__/format.test.js.snap
+++ b/tests/format/angular/angular/__snapshots__/format.test.js.snap
@@ -451,10 +451,10 @@ printWidth: 80
[target]="{a: 1}"
[target]="{a: 1}"
[target]="{
- trailingComma: 'notAllowed'
+ trailingComma: 'notAllowed',
}"
[target]="[
- longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong
+ longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong,
]"
[target]="true"
[target]="undefined"
@@ -540,14 +540,14 @@ printWidth: 80
listRow.NextScheduledSendStatus == 3
"
[target]="{
- longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong: true
+ longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong: true,
}"
[error]="'We couldn\\\\\\'t find anything with that name.'"
*ngIf="form.controls.details?.controls.amount?.errors.min"
[ngClass]="{
'btn-success': (dialog$ | async).level === dialogLevelEnum.SUCCESS,
'btn-warning': (dialog$ | async).level === dialogLevelEnum.WARNING,
- 'btn-svg': (dialog$ | async).level === dialogLevelEnum.DANGER
+ 'btn-svg': (dialog$ | async).level === dialogLevelEnum.DANGER,
}"
[stickout]="+toolAssembly.stickoutMm"
test="{{ 'test' | translate }}"
@@ -730,10 +730,10 @@ printWidth: 80
[target]="{ a: 1 }"
[target]="{ a: 1 }"
[target]="{
- trailingComma: 'notAllowed'
+ trailingComma: 'notAllowed',
}"
[target]="[
- longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong
+ longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong,
]"
[target]="true"
[target]="undefined"
@@ -819,14 +819,14 @@ printWidth: 80
listRow.NextScheduledSendStatus == 3
"
[target]="{
- longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong: true
+ longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong: true,
}"
[error]="'We couldn\\\\\\'t find anything with that name.'"
*ngIf="form.controls.details?.controls.amount?.errors.min"
[ngClass]="{
'btn-success': (dialog$ | async).level === dialogLevelEnum.SUCCESS,
'btn-warning': (dialog$ | async).level === dialogLevelEnum.WARNING,
- 'btn-svg': (dialog$ | async).level === dialogLevelEnum.DANGER
+ 'btn-svg': (dialog$ | async).level === dialogLevelEnum.DANGER,
}"
[stickout]="+toolAssembly.stickoutMm"
test="{{ 'test' | translate }}"
@@ -1053,20 +1053,20 @@ printWidth: 1
]
"
[target]="[
- 1
+ 1,
]"
[target]="{
- a: 1
+ a: 1,
}"
[target]="{
- a: 1
+ a: 1,
}"
[target]="{
trailingComma:
- 'notAllowed'
+ 'notAllowed',
}"
[target]="[
- longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong
+ longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong,
]"
[target]="
true
@@ -1210,7 +1210,7 @@ printWidth: 1
1,
2,
3,
- 666666666666666666666666666666666666
+ 666666666666666666666666666666666666,
];
let i = index
"
@@ -1320,7 +1320,7 @@ printWidth: 1
3
"
[target]="{
- longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong: true
+ longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong: true,
}"
[error]="
'We couldn\\\\\\'t find anything with that name.'
@@ -1355,7 +1355,7 @@ printWidth: 1
| async
)
.level ===
- dialogLevelEnum.DANGER
+ dialogLevelEnum.DANGER,
}"
[stickout]="
+toolAssembly.stickoutMm
@@ -1636,10 +1636,10 @@ trailingComma: "es5"
[target]="{ a: 1 }"
[target]="{ a: 1 }"
[target]="{
- trailingComma: 'notAllowed'
+ trailingComma: 'notAllowed',
}"
[target]="[
- longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong
+ longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong,
]"
[target]="true"
[target]="undefined"
@@ -1725,14 +1725,14 @@ trailingComma: "es5"
listRow.NextScheduledSendStatus == 3
"
[target]="{
- longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong: true
+ longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong: true,
}"
[error]="'We couldn\\\\\\'t find anything with that name.'"
*ngIf="form.controls.details?.controls.amount?.errors.min"
[ngClass]="{
'btn-success': (dialog$ | async).level === dialogLevelEnum.SUCCESS,
'btn-warning': (dialog$ | async).level === dialogLevelEnum.WARNING,
- 'btn-svg': (dialog$ | async).level === dialogLevelEnum.DANGER
+ 'btn-svg': (dialog$ | async).level === dialogLevelEnum.DANGER,
}"
[stickout]="+toolAssembly.stickoutMm"
test="{{ 'test' | translate }}"
@@ -3395,21 +3395,21 @@ printWidth: 1
{{
[
- 1
+ 1,
]
}}
{{
{
- a: 1
+ a: 1,
}
}}
{{
{
- a: 1
+ a: 1,
}
}}
@@ -3599,8 +3599,8 @@ printWidth: 1
| translate
: {
count:
- array.length
- })
+ array.length,
+ }),
}
}}
@@ -3609,7 +3609,7 @@ printWidth: 1
{
a:
1 +
- {}
+ {},
}
}}
@@ -3618,14 +3618,14 @@ printWidth: 1
{
a:
a ===
- {}
+ {},
}
}}
{{
{
- a: !{}
+ a: !{},
}
}}
@@ -3634,7 +3634,7 @@ printWidth: 1
{
a: a
? b
- : {}
+ : {},
}
}}
@@ -8391,7 +8391,7 @@ can be found in the LICENSE file at http://angular.io/license
@@ -9371,7 +9371,7 @@ bindon-ngModel
[ngClass]="{
bad: false,
curly: true,
- special: true
+ special: true,
}"
>
Curly
diff --git a/tests/format/angular/trailing-comma/__snapshots__/format.test.js.snap b/tests/format/angular/trailing-comma/__snapshots__/format.test.js.snap
new file mode 100644
index 000000000..4f734d6a6
--- /dev/null
+++ b/tests/format/angular/trailing-comma/__snapshots__/format.test.js.snap
@@ -0,0 +1,622 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`trailing-comma.html - {"trailingComma":"all"} format 1`] = `
+====================================options=====================================
+parsers: ["angular"]
+printWidth: 80
+trailingComma: "all"
+ | printWidth
+=====================================input======================================
+
+
+=====================================output=====================================
+
+
+================================================================================
+`;
+
+exports[`trailing-comma.html - {"trailingComma":"es5"} format 1`] = `
+====================================options=====================================
+parsers: ["angular"]
+printWidth: 80
+trailingComma: "es5"
+ | printWidth
+=====================================input======================================
+
+
+=====================================output=====================================
+
+
+================================================================================
+`;
+
+exports[`trailing-comma.html - {"trailingComma":"none"} format 1`] = `
+====================================options=====================================
+parsers: ["angular"]
+printWidth: 80
+trailingComma: "none"
+ | printWidth
+=====================================input======================================
+
+
+=====================================output=====================================
+
+
+================================================================================
+`;
diff --git a/tests/format/angular/trailing-comma/format.test.js b/tests/format/angular/trailing-comma/format.test.js
new file mode 100644
index 000000000..08fb6de70
--- /dev/null
+++ b/tests/format/angular/trailing-comma/format.test.js
@@ -0,0 +1,3 @@
+runFormatTest(import.meta, ["angular"], { trailingComma: "none" });
+runFormatTest(import.meta, ["angular"], { trailingComma: "es5" });
+runFormatTest(import.meta, ["angular"], { trailingComma: "all" });
diff --git a/tests/format/angular/trailing-comma/trailing-comma.html b/tests/format/angular/trailing-comma/trailing-comma.html
new file mode 100644
index 000000000..7e40dc499
--- /dev/null
+++ b/tests/format/angular/trailing-comma/trailing-comma.html
@@ -0,0 +1,56 @@
+