diff --git a/changelog_unreleased/markdown/18519.md b/changelog_unreleased/markdown/18519.md
new file mode 100644
index 000000000..a8fbbfb4e
--- /dev/null
+++ b/changelog_unreleased/markdown/18519.md
@@ -0,0 +1,37 @@
+#### Support formatting Angular syntax in code block (#18519 by @fisker)
+
+
+````md
+
+```angular-ts
+@Component({
+ selector: 'app-root',
+ template: `
Welcome to {{
+ Angular}}!
`,
+})
+export class App {}
+```
+
+```angular-html
+Welcome to {{
+ Angular}}!
+```
+
+
+Same as input
+
+
+```angular-ts
+@Component({
+ selector: "app-root",
+ template: `Welcome to {{ Angular }}!
`,
+})
+export class App {}
+```
+
+```angular-html
+Welcome to {{ Angular }}!
+```
+````
diff --git a/src/language-markdown/embed.js b/src/language-markdown/embed.js
index ab0967330..19cbe6f19 100644
--- a/src/language-markdown/embed.js
+++ b/src/language-markdown/embed.js
@@ -13,7 +13,16 @@ function embed(path, options) {
return;
}
- const parser = inferParser(options, { language });
+ let parser;
+ // https://shiki.style/references/engine-js-compat#supported-languages
+ if (language === "angular-ts") {
+ parser = inferParser(options, { language: "typescript" });
+ } else if (language === "angular-html") {
+ parser = "angular";
+ } else {
+ parser = inferParser(options, { language });
+ }
+
if (!parser) {
return;
}
diff --git a/tests/format/markdown/code/angular/__snapshots__/format.test.js.snap b/tests/format/markdown/code/angular/__snapshots__/format.test.js.snap
new file mode 100644
index 000000000..229c7cae0
--- /dev/null
+++ b/tests/format/markdown/code/angular/__snapshots__/format.test.js.snap
@@ -0,0 +1,294 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`angular-html.md - {"embeddedLanguageFormatting":"off"} format 1`] = `
+====================================options=====================================
+embeddedLanguageFormatting: "off"
+parsers: ["markdown"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+\`\`\`angular-html
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+\`\`\`
+
+=====================================output=====================================
+\`\`\`angular-html
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+\`\`\`
+
+================================================================================
+`;
+
+exports[`angular-html.md format 1`] = `
+====================================options=====================================
+parsers: ["markdown"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+\`\`\`angular-html
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+\`\`\`
+
+=====================================output=====================================
+\`\`\`angular-html
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+
+}
+\`\`\`
+
+================================================================================
+`;
+
+exports[`angular-html-invalid.md - {"embeddedLanguageFormatting":"off"} format 1`] = `
+====================================options=====================================
+embeddedLanguageFormatting: "off"
+parsers: ["markdown"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+\`\`\`angular-html
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+{{ reason.message }}
+ }
+ }
+\`\`\`
+
+=====================================output=====================================
+\`\`\`angular-html
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+{{ reason.message }}
+ }
+ }
+\`\`\`
+
+================================================================================
+`;
+
+exports[`angular-html-invalid.md format 1`] = `
+====================================options=====================================
+parsers: ["markdown"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+\`\`\`angular-html
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+{{ reason.message }}
+ }
+ }
+\`\`\`
+
+=====================================output=====================================
+\`\`\`angular-html
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+{{ reason.message }}
+ }
+ }
+\`\`\`
+
+================================================================================
+`;
+
+exports[`angular-ts.md - {"embeddedLanguageFormatting":"off"} format 1`] = `
+====================================options=====================================
+embeddedLanguageFormatting: "off"
+parsers: ["markdown"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+\`\`\`angular-ts
+type A ={
+ Ugly:string
+}
+
+@Component({template: \`
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ \`
+})
+export class Order {
+}
+\`\`\`
+
+=====================================output=====================================
+\`\`\`angular-ts
+type A ={
+ Ugly:string
+}
+
+@Component({template: \`
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ \`
+})
+export class Order {
+}
+\`\`\`
+
+================================================================================
+`;
+
+exports[`angular-ts.md format 1`] = `
+====================================options=====================================
+parsers: ["markdown"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+\`\`\`angular-ts
+type A ={
+ Ugly:string
+}
+
+@Component({template: \`
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ \`
+})
+export class Order {
+}
+\`\`\`
+
+=====================================output=====================================
+\`\`\`angular-ts
+type A = {
+ Ugly: string;
+};
+
+@Component({
+ template: \`
+ @if (orderForm.couponCode().disabled()) {
+
+ @for (
+ reason of orderForm.couponCode().disabledReasons();
+ track reason
+ ) {
+
{{ reason.message }}
+ }
+
+ }
+ \`,
+})
+export class Order {}
+\`\`\`
+
+================================================================================
+`;
+
+exports[`angular-ts-invalid.md - {"embeddedLanguageFormatting":"off"} format 1`] = `
+====================================options=====================================
+embeddedLanguageFormatting: "off"
+parsers: ["markdown"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+\`\`\`angular-ts
+ Ugly:string
+}
+
+@Component({template: \`
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ \`
+})
+export class Order {
+}
+\`\`\`
+
+=====================================output=====================================
+\`\`\`angular-ts
+ Ugly:string
+}
+
+@Component({template: \`
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ \`
+})
+export class Order {
+}
+\`\`\`
+
+================================================================================
+`;
+
+exports[`angular-ts-invalid.md format 1`] = `
+====================================options=====================================
+parsers: ["markdown"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+\`\`\`angular-ts
+ Ugly:string
+}
+
+@Component({template: \`
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ \`
+})
+export class Order {
+}
+\`\`\`
+
+=====================================output=====================================
+\`\`\`angular-ts
+ Ugly:string
+}
+
+@Component({template: \`
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ \`
+})
+export class Order {
+}
+\`\`\`
+
+================================================================================
+`;
diff --git a/tests/format/markdown/code/angular/angular-html-invalid.md b/tests/format/markdown/code/angular/angular-html-invalid.md
new file mode 100644
index 000000000..422a1a5a8
--- /dev/null
+++ b/tests/format/markdown/code/angular/angular-html-invalid.md
@@ -0,0 +1,6 @@
+```angular-html
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+{{ reason.message }}
+ }
+ }
+```
diff --git a/tests/format/markdown/code/angular/angular-html.md b/tests/format/markdown/code/angular/angular-html.md
new file mode 100644
index 000000000..bbaec0813
--- /dev/null
+++ b/tests/format/markdown/code/angular/angular-html.md
@@ -0,0 +1,8 @@
+```angular-html
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+```
diff --git a/tests/format/markdown/code/angular/angular-ts-invalid.md b/tests/format/markdown/code/angular/angular-ts-invalid.md
new file mode 100644
index 000000000..514e328d4
--- /dev/null
+++ b/tests/format/markdown/code/angular/angular-ts-invalid.md
@@ -0,0 +1,16 @@
+```angular-ts
+ Ugly:string
+}
+
+@Component({template: `
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ `
+})
+export class Order {
+}
+```
diff --git a/tests/format/markdown/code/angular/angular-ts.md b/tests/format/markdown/code/angular/angular-ts.md
new file mode 100644
index 000000000..420ba08c9
--- /dev/null
+++ b/tests/format/markdown/code/angular/angular-ts.md
@@ -0,0 +1,17 @@
+```angular-ts
+type A ={
+ Ugly:string
+}
+
+@Component({template: `
+@if (orderForm.couponCode().disabled()) {
+
+ @for (reason of orderForm.couponCode().disabledReasons(); track reason) {
+
{{ reason.message }}
+ }
+ }
+ `
+})
+export class Order {
+}
+```
diff --git a/tests/format/markdown/code/angular/format.test.js b/tests/format/markdown/code/angular/format.test.js
new file mode 100644
index 000000000..cbca7e1b9
--- /dev/null
+++ b/tests/format/markdown/code/angular/format.test.js
@@ -0,0 +1,2 @@
+runFormatTest(import.meta, ["markdown"]);
+runFormatTest(import.meta, ["markdown"], { embeddedLanguageFormatting: "off" });