diff --git a/changelog_unreleased/angular/18593.md b/changelog_unreleased/angular/18593.md
new file mode 100644
index 000000000..7d1ae4039
--- /dev/null
+++ b/changelog_unreleased/angular/18593.md
@@ -0,0 +1,25 @@
+#### Add support for multiple switch cases (#18593 by @fisker)
+
+This feature added in Angular v21.1.
+
+
+```html
+
+@switch (foo) {
+ @case (0)
+ @case (1) {
+
case 0 or 1
+ }
+}
+
+
+SyntaxError: Incomplete block "case". If you meant to write the @ character, you should use the "@" HTML entity instead. (2:3)
+
+
+@switch (a) {
+ @case (0)
+ @case (1) {
+ case 0 or 1
+ }
+}
+```
diff --git a/package.json b/package.json
index 092a9b626..a61dbfbd1 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
"acorn": "8.15.0",
"acorn-jsx": "5.3.2",
"angular-estree-parser": "15.2.0",
- "angular-html-parser": "10.1.1",
+ "angular-html-parser": "10.3.0",
"camelcase": "9.0.0",
"ci-info": "4.3.1",
"cjk-regex": "3.4.0",
diff --git a/src/language-html/print/angular-control-flow-block.js b/src/language-html/print/angular-control-flow-block.js
index e1d0919e0..5f9690403 100644
--- a/src/language-html/print/angular-control-flow-block.js
+++ b/src/language-html/print/angular-control-flow-block.js
@@ -24,18 +24,20 @@ function printAngularControlFlowBlock(path, options, print) {
docs.push(" (", group(print("parameters")), ")");
}
- docs.push(" {");
+ if (!isSwitchFallthroughCase(node)) {
+ docs.push(" {");
- const shouldPrintCloseBracket = shouldCloseBlock(node);
- if (node.children.length > 0) {
- node.firstChild.hasLeadingSpaces = true;
- node.lastChild.hasTrailingSpaces = true;
- docs.push(indent([hardline, printChildren(path, options, print)]));
- if (shouldPrintCloseBracket) {
- docs.push(hardline, "}");
+ const shouldPrintCloseBracket = shouldCloseBlock(node);
+ if (node.children.length > 0) {
+ node.firstChild.hasLeadingSpaces = true;
+ node.lastChild.hasTrailingSpaces = true;
+ docs.push(indent([hardline, printChildren(path, options, print)]));
+ if (shouldPrintCloseBracket) {
+ docs.push(hardline, "}");
+ }
+ } else if (shouldPrintCloseBracket) {
+ docs.push("}");
}
- } else if (shouldPrintCloseBracket) {
- docs.push("}");
}
return group(docs, { shouldBreak: true });
@@ -48,6 +50,19 @@ function shouldCloseBlock(node) {
);
}
+const isSwitchCaseBlock = (node) =>
+ node?.kind === "angularControlFlowBlock" &&
+ (node.name === "case" || node.name === "default");
+// https://github.com/angular/angular/commit/0ad3adc7c6d4094f1e3432a3f2e3bdc9862cb4fa#diff-77a6f285c6ea13d6644ef635e7495e71134d1141af2650cb9ae5631ff1f38bf2R263
+// https://github.com/prettier/prettier/issues/18563#issuecomment-3728354491
+function isSwitchFallthroughCase(node) {
+ return (
+ isSwitchCaseBlock(node) &&
+ node.endSourceSpan &&
+ node.endSourceSpan.start.offset === node.endSourceSpan.end.offset
+ );
+}
+
function isPreviousBlockUnClosed(path) {
const { previous } = path;
return (
diff --git a/tests/format/angular/control-flow/__snapshots__/format.test.js.snap b/tests/format/angular/control-flow/__snapshots__/format.test.js.snap
index 8e14f252c..57d672535 100644
--- a/tests/format/angular/control-flow/__snapshots__/format.test.js.snap
+++ b/tests/format/angular/control-flow/__snapshots__/format.test.js.snap
@@ -526,6 +526,120 @@ printWidth: 80
================================================================================
`;
+exports[`multiple-switch-cases.html format 1`] = `
+====================================options=====================================
+parsers: ["angular"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+
+
+@switch (expr) {
+ @case ( 'one')
+ @case ( 'two') {
+ {{ oneOrTwo()}}
+ }
+ @case ( 'three') {
+ {{ three()}}
+ }
+}
+
+
+@switch (expr) {
+ @case (1)
+ @case (2) {
+ {{oneOrTwo() }}
+ }
+ @case (3 )
+ @default {
+ {{default() }}
+ }
+}
+
+
+ {{message}}
+ @switch (value()) {
+ @case ( 0) @case(1 ) {
+ case 01
+ }
+ @case (2) {
+ case 2
+ }
+ @default {
+ default
+ }
+ }
+
+
+@switch(a){@default @case(2){default before case
}}
+@switch(a){@case(1) @case(2){} @case(3) {3
}}
+
+@switch(a){@case(1) @case(2) {}}
+
+=====================================output=====================================
+
+
+@switch (expr) {
+ @case ("one")
+ @case ("two") {
+ {{ oneOrTwo() }}
+ }
+ @case ("three") {
+ {{ three() }}
+ }
+}
+
+@switch (expr) {
+ @case (1)
+ @case (2) {
+ {{ oneOrTwo() }}
+ }
+ @case (3)
+ @default {
+ {{ default() }}
+ }
+}
+
+
+ {{ message }}
+ @switch (value()) {
+ @case (0)
+ @case (1) {
+ case 01
+ }
+ @case (2) {
+ case 2
+ }
+ @default {
+ default
+ }
+ }
+
+
+@switch (a) {
+ @default
+ @case (2) {
+ default before case
+ }
+}
+@switch (a) {
+ @case (1)
+ @case (2) {}
+ @case (3) {
+ 3
+ }
+}
+
+@switch (a) {
+ @case (1)
+ @case (2) {
+
+ }
+}
+
+================================================================================
+`;
+
exports[`switch.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
diff --git a/tests/format/angular/control-flow/multiple-switch-cases.html b/tests/format/angular/control-flow/multiple-switch-cases.html
new file mode 100644
index 000000000..a56e8795d
--- /dev/null
+++ b/tests/format/angular/control-flow/multiple-switch-cases.html
@@ -0,0 +1,43 @@
+
+
+@switch (expr) {
+ @case ( 'one')
+ @case ( 'two') {
+ {{ oneOrTwo()}}
+ }
+ @case ( 'three') {
+ {{ three()}}
+ }
+}
+
+
+@switch (expr) {
+ @case (1)
+ @case (2) {
+ {{oneOrTwo() }}
+ }
+ @case (3 )
+ @default {
+ {{default() }}
+ }
+}
+
+
+ {{message}}
+ @switch (value()) {
+ @case ( 0) @case(1 ) {
+ case 01
+ }
+ @case (2) {
+ case 2
+ }
+ @default {
+ default
+ }
+ }
+
+
+@switch(a){@default @case(2){default before case
}}
+@switch(a){@case(1) @case(2){} @case(3) {3
}}
+
+@switch(a){@case(1) @case(2) {}}
diff --git a/yarn.lock b/yarn.lock
index d95915db5..173adef45 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2861,10 +2861,10 @@ __metadata:
languageName: node
linkType: hard
-"angular-html-parser@npm:10.1.1":
- version: 10.1.1
- resolution: "angular-html-parser@npm:10.1.1"
- checksum: 10/ad2995b43fb5c6465e7d1de5f6909f79e08486724997bb3417934c7bc0541e5604f563147a68e18b71eebba57dc61e184cf5d47451ed8b19a4b34d79cecb5f60
+"angular-html-parser@npm:10.3.0":
+ version: 10.3.0
+ resolution: "angular-html-parser@npm:10.3.0"
+ checksum: 10/97078879db8f53fc9645d2cd8c748c9eb3770790ec96f249bd96e0ab9293f65506b7facd201c17f2f18884e82550f3c362972e7b3a5448b0e1fb2a6938e9ef4d
languageName: node
linkType: hard
@@ -7518,7 +7518,7 @@ __metadata:
acorn: "npm:8.15.0"
acorn-jsx: "npm:5.3.2"
angular-estree-parser: "npm:15.2.0"
- angular-html-parser: "npm:10.1.1"
+ angular-html-parser: "npm:10.3.0"
base64-arraybuffer-es6: "npm:3.1.0"
browserslist: "npm:4.28.0"
browserslist-to-esbuild: "npm:2.1.1"