Repository: prettier/prettier (JavaScript)
Date: 2023-02-22 12:55:33+00:00
Task type: Bug Fix

Issue:
Script in SVG is incorrectly formatted when using HTML parser
<!--

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.7.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAeAzgNwOYAIAeAtgDZToC8AOiABYwwAOSA9MwO4cB0bAzJxACdszAEwAGCcyzZquNgEsAJjBpUQIgCxjZNOPOx01m7SFyZ5cNgCEI+NWNwPNuTdQB8lKJRiow8gWDEcLhgdtQAjCKyYACeajxRpgJqAGyyAGbyxMRqYACuAgIIMADCEMSC7p7e3qgAhlDyhHXwuC0wAvIARnnwAHJ1hHC5MbKYdcR5cBTUCQDckbKKBfacKegZWTnU6UVwAF5wssweXj7MfgFBp7V4mdlqUNBHpugdEADWw9T5hcVlFQEsjeAk+cAAtMR5FA4GA6gw1KC8lBFMD3l9wQplKpqIlcCCweDFHV0DQ6oU6nFcWjQRjiaSIOl0ug4DAjFUzrUGC0aLhFGoALIOEw3GA1HwNJotYLtTo9fqDb4gVGmcaTaaCyK4cIATkw4QFuu1IhoBq1AFZMOCDebjTRrXNDSIXOF9QKROE7WbnTwrQaeHbrbIunBsNDVgB2DamZbJahiTgiaO4e7bEC7OAHF64E7VMX5+qNZqtWXdXpwAZDNQEukkmiM5mssYTKYzdRzEy4ENhqCR5Ox1ZJzYPHZ7Q7HUW1ZjclSTnwz3n86hCxwc8W1SXFmX0OXlytKlVmFsa5fhCPaiPEBO26+G8+X6+4cG3+-EZ+cW3v80C89nt+P69HXCAAOD9tVA80rzA28QLA80Pygz9b3g20UP-MCvwFFDwIQr9HA-YNQ3DeNOB1fsVhIodTFTNQMyzCc83FQspRLHcywVKtqBrCF6XrJkWTFVVjzbEQO0IntVjIpYKJABMqJTLZaLHbMlDUYgSRgABldFs1zTlzgXOdmK3AAVAQGnQdJBEINo2PlCtFTUDoLKsgRCFkGAYgYJVQRgaVm3VNsHC1SI5h4FJgudRZTG7YiQHUt5tNpOBOAQQ8B1mMRkyKbyWjKZE2WoaFFDgTJGngBizlQZhsFFDAwE6BgYDnRQIHyIZYE4OpFEUABRTBigAGXkN4EDgAQAAoAHIABEAHkBTKWAhogbq4EUKaABpcAmgBKXByDcXBgEY-NIDIGBcCeEqDr5Nq8g6mBOGwVleqCR6rBiABJRRpoSrSdKm3a5lO7x5HSHbrrgfaTv08Ugku6RbqhzhuSKWA+ggErUfJYpMextG8axuAQbh-NwZ2qbuTyFkAEEixaeRoHQKbcGhfEcBh0H828BHcHrAaBFu9IJhZUn1x5mA+YqNbFGF0WSe58UUe6vqBtgYbRphSaprS-rii2nb9sO46lclmWSrl8hcA6KZxcliWKYmgBCAXxq5smJe8aQcZpuB6ZYpmyD2+2vfzABfM3vHD4Go5gH3Vf1jWRvgbXpsICA-eKcbDb2g6jthsPxTdoXrdtxXPcln3kWpumGZgIP0BDuOY9Dh345wLqeqTmBNdT8b08zlkgjqAbc+Ngu4+8Ev5eIMWp4biGJot9aPaLqvO9r-368b5vK-FSP95gVuzcPiWz4j2OxSqqQGvkJrTmq6RThATaQAgJrG+QUByVBNgAAVcZkGQCACYbBKToFfiALo5kwBfC0oqYaMJkAiznnAN+MC6hwNZJpbkfgoDYGQOXN+cBCAhh6utQaDRsB5DqC9AAYtZdo0JCEoDqL0CAUC6AkAAOo0HkPAdAeC4CaQQOgAR8hzCeRAWAdAkC37QhZAIGA-9zLYGaCghWb8ABW6B8CaRYUEAAinkCA8BNFoLfmjJRIDuHECgQwTosAeFKBUMgYCYgrGghZDw8yDAQGOOmONAaUCACOpj4CqI-pAth6BwQwnWutKBRRwn+DgKouhGikCoJZG-FkhB5BEIEFMPJhi4AmLMXACxuSQB+S6C47EyARBv2clkFhZRCBZJANMc0UC-YmTqF0GJOT0EgEwFML6KJiiaTvk1emihtIxCCNUuA4dw5AA)

```sh
# Options (if any):
--parser html
```

**Input:**

```html
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" viewBox="0 0 24 24">
	<script>
		document.addEventListener('DOMContentLoaded', () => {
			const node = document.getElementById('lastStroke');
			if (node) {
				let svg = node.parentNode.parentNode.parentNode;
				if ('pauseAnimations' in svg) {
					let hover = false;
					let loaded = false;
					node.addEventListener('endEvent', () => {
						loaded = true;
						if (!hover) {
							svg.pauseAnimations();
						}
					});
					svg.addEventListener('mouseenter', () => {
						hover = true;
						svg.unpauseAnimations();
					});
					svg.addEventListener('mouseleave', () => {
						hover = false;
						if (loaded) {
							svg.pauseAnimations();
						}
					});
				}
			}
		});	
	</script>
</svg>
```

**Output:**

```svg
<svg
  xmlns="http://www.w3.org/2000/svg"
  width="240"
  height="240"
  viewBox="0 0 24 24"
>
  <script>
    document.addEventListener('DOMContentLoaded', () => { const node =
    document.getElementById('lastStroke'); if (node) { let svg =
    node.parentNode.parentNode.parentNode; if ('pauseAnimations' in svg) { let
    hover = false; let loaded = false; node.addEventListener('endEvent', () => {
    loaded = true; if (!hover) { svg.pauseAnimations(); } });
    svg.addEventListener('mouseenter', () => { hover = true;
    svg.unpauseAnimations(); }); svg.addEventListener('mouseleave', () => {
    hover = false; if (loaded) { svg.pauseAnimations(); } }); } } });
  </script>
</svg>
```

**Expected behavior:**

Expected output to be readable.

There is no special parser for SVG, so using HTML parser to format it. However, something weird happens when there is a script inside SVG and Prettier turns it into unreadable mess.


Issue discussion:

Solution patch:
diff --git a/changelog_unreleased/html/14400.md b/changelog_unreleased/html/14400.md
new file mode 100644
index 000000000000..7d9accfd49ed
--- /dev/null
+++ b/changelog_unreleased/html/14400.md
@@ -0,0 +1,38 @@
+#### Format `<script>` inside SVG (#14400 by @fisker)
+
+<!-- prettier-ignore -->
+```html
+<!-- Input -->
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+<script>
+document.addEventListener(
+'DOMContentLoaded', () => {
+  const element = document.getElementById('foo')
+   if (element) {
+element.fillStyle = 'currentColor'
+}
+});
+</script>
+</svg>
+
+<!-- Prettier stable -->
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+  <script>
+    document.addEventListener( 'DOMContentLoaded', () => { const element =
+    document.getElementById('foo') if (element) { element.fillStyle =
+    'currentColor' } });
+  </script>
+</svg>
+
+<!-- Prettier main -->
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+  <script>
+    document.addEventListener("DOMContentLoaded", () => {
+      const element = document.getElementById("foo");
+      if (element) {
+        element.fillStyle = "currentColor";
+      }
+    });
+  </script>
+</svg>
+```
diff --git a/src/language-html/utils/index.js b/src/language-html/utils/index.js
index 3d57ceb8aadf..e292de525b3d 100644
--- a/src/language-html/utils/index.js
+++ b/src/language-html/utils/index.js
@@ -109,6 +109,7 @@ function isScriptLikeTag(node) {
     (node.fullName === "script" ||
       node.fullName === "style" ||
       node.fullName === "svg:style" ||
+      node.fullName === "svg:script" ||
       (isUnknownNamespace(node) &&
         (node.name === "script" || node.name === "style")))
   );


Test patch:
diff --git a/tests/config/utils/check-parsers.js b/tests/config/utils/check-parsers.js
index 166b37619f6f..a8def844d115 100644
--- a/tests/config/utils/check-parsers.js
+++ b/tests/config/utils/check-parsers.js
@@ -46,7 +46,10 @@ const categoryParsers = new Map([
     "handlebars",
     { parsers: ["glimmer"], verifyParsers: [], extensions: [".hbs"] },
   ],
-  ["html", { parsers: ["html"], verifyParsers: [], extensions: [".html"] }],
+  [
+    "html",
+    { parsers: ["html"], verifyParsers: [], extensions: [".html", ".svg"] },
+  ],
   ["mjml", { parsers: ["html"], verifyParsers: [], extensions: [".mjml"] }],
   [
     "js",
diff --git a/tests/format/html/svg/__snapshots__/jsfmt.spec.js.snap b/tests/format/html/svg/__snapshots__/jsfmt.spec.js.snap
index ba70e2d935ac..4bbcb552cdc8 100644
--- a/tests/format/html/svg/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/html/svg/__snapshots__/jsfmt.spec.js.snap
@@ -19,16 +19,7 @@ printWidth: 80
 </html>
 
 <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
-<defs /> <style >
-    polygon { fill: black }
-
-    div {
-  color: white;
-      font:18px serif;
-      height: 100%;
-      overflow: auto;
-    }
-  </style>
+<defs /> 
 
  <g>
   <g><polygon points="5,5 195,10 185,185 10,195" />
@@ -73,18 +64,6 @@ printWidth: 80
 
 <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
   <defs />
-  <style>
-    polygon {
-      fill: black;
-    }
-
-    div {
-      color: white;
-      font: 18px serif;
-      height: 100%;
-      overflow: auto;
-    }
-  </style>
 
   <g>
     <g>
diff --git a/tests/format/html/svg/embeded/__snapshots__/jsfmt.spec.js.snap b/tests/format/html/svg/embeded/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..f8796610d2ca
--- /dev/null
+++ b/tests/format/html/svg/embeded/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,253 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`svg.html - {"embeddedLanguageFormatting":"off"} format 1`] = `
+====================================options=====================================
+embeddedLanguageFormatting: "off"
+parsers: ["html"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>SVG</title>
+  </head>
+  <body>
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+<script>
+document.addEventListener(
+'DOMContentLoaded', () => {
+  const element = document.getElementById('foo')
+   if (element) {
+element.fillStyle = 'currentColor'
+}
+});
+</script>
+<style >
+    polygon { fill: black }
+
+    div {
+  color: white;
+      font:18px serif;
+      height: 100%;
+      overflow: auto;
+    }
+  </style>
+</svg>
+  </body>
+</html>
+
+=====================================output=====================================
+<!doctype html>
+<html>
+  <head>
+    <title>SVG</title>
+  </head>
+  <body>
+    <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+      <script>
+        document.addEventListener(
+        'DOMContentLoaded', () => {
+          const element = document.getElementById('foo')
+           if (element) {
+        element.fillStyle = 'currentColor'
+        }
+        });
+      </script>
+      <style>
+          polygon { fill: black }
+
+          div {
+        color: white;
+            font:18px serif;
+            height: 100%;
+            overflow: auto;
+          }
+      </style>
+    </svg>
+  </body>
+</html>
+
+================================================================================
+`;
+
+exports[`svg.html format 1`] = `
+====================================options=====================================
+parsers: ["html"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>SVG</title>
+  </head>
+  <body>
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+<script>
+document.addEventListener(
+'DOMContentLoaded', () => {
+  const element = document.getElementById('foo')
+   if (element) {
+element.fillStyle = 'currentColor'
+}
+});
+</script>
+<style >
+    polygon { fill: black }
+
+    div {
+  color: white;
+      font:18px serif;
+      height: 100%;
+      overflow: auto;
+    }
+  </style>
+</svg>
+  </body>
+</html>
+
+=====================================output=====================================
+<!doctype html>
+<html>
+  <head>
+    <title>SVG</title>
+  </head>
+  <body>
+    <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+      <script>
+        document.addEventListener("DOMContentLoaded", () => {
+          const element = document.getElementById("foo");
+          if (element) {
+            element.fillStyle = "currentColor";
+          }
+        });
+      </script>
+      <style>
+        polygon {
+          fill: black;
+        }
+
+        div {
+          color: white;
+          font: 18px serif;
+          height: 100%;
+          overflow: auto;
+        }
+      </style>
+    </svg>
+  </body>
+</html>
+
+================================================================================
+`;
+
+exports[`svg.svg - {"embeddedLanguageFormatting":"off"} format 1`] = `
+====================================options=====================================
+embeddedLanguageFormatting: "off"
+parsers: ["html"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+<script>
+document.addEventListener(
+'DOMContentLoaded', () => {
+  const element = document.getElementById('foo')
+   if (element) {
+element.fillStyle = 'currentColor'
+}
+});
+</script>
+<style >
+    polygon { fill: black }
+
+    div {
+  color: white;
+      font:18px serif;
+      height: 100%;
+      overflow: auto;
+    }
+  </style>
+</svg>
+
+=====================================output=====================================
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+  <script>
+    document.addEventListener(
+    'DOMContentLoaded', () => {
+      const element = document.getElementById('foo')
+       if (element) {
+    element.fillStyle = 'currentColor'
+    }
+    });
+  </script>
+  <style>
+      polygon { fill: black }
+
+      div {
+    color: white;
+        font:18px serif;
+        height: 100%;
+        overflow: auto;
+      }
+  </style>
+</svg>
+
+================================================================================
+`;
+
+exports[`svg.svg format 1`] = `
+====================================options=====================================
+parsers: ["html"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+<script>
+document.addEventListener(
+'DOMContentLoaded', () => {
+  const element = document.getElementById('foo')
+   if (element) {
+element.fillStyle = 'currentColor'
+}
+});
+</script>
+<style >
+    polygon { fill: black }
+
+    div {
+  color: white;
+      font:18px serif;
+      height: 100%;
+      overflow: auto;
+    }
+  </style>
+</svg>
+
+=====================================output=====================================
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+  <script>
+    document.addEventListener("DOMContentLoaded", () => {
+      const element = document.getElementById("foo");
+      if (element) {
+        element.fillStyle = "currentColor";
+      }
+    });
+  </script>
+  <style>
+    polygon {
+      fill: black;
+    }
+
+    div {
+      color: white;
+      font: 18px serif;
+      height: 100%;
+      overflow: auto;
+    }
+  </style>
+</svg>
+
+================================================================================
+`;
diff --git a/tests/format/html/svg/embeded/jsfmt.spec.js b/tests/format/html/svg/embeded/jsfmt.spec.js
new file mode 100644
index 000000000000..75e3c2373e26
--- /dev/null
+++ b/tests/format/html/svg/embeded/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(import.meta, ["html"]);
+run_spec(import.meta, ["html"], { embeddedLanguageFormatting: "off" });
diff --git a/tests/format/html/svg/embeded/svg.html b/tests/format/html/svg/embeded/svg.html
new file mode 100644
index 000000000000..6934c58a7604
--- /dev/null
+++ b/tests/format/html/svg/embeded/svg.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>SVG</title>
+  </head>
+  <body>
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+<script>
+document.addEventListener(
+'DOMContentLoaded', () => {
+  const element = document.getElementById('foo')
+   if (element) {
+element.fillStyle = 'currentColor'
+}
+});
+</script>
+<style >
+    polygon { fill: black }
+
+    div {
+  color: white;
+      font:18px serif;
+      height: 100%;
+      overflow: auto;
+    }
+  </style>
+</svg>
+  </body>
+</html>
diff --git a/tests/format/html/svg/embeded/svg.svg b/tests/format/html/svg/embeded/svg.svg
new file mode 100644
index 000000000000..2a86602fb068
--- /dev/null
+++ b/tests/format/html/svg/embeded/svg.svg
@@ -0,0 +1,21 @@
+<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
+<script>
+document.addEventListener(
+'DOMContentLoaded', () => {
+  const element = document.getElementById('foo')
+   if (element) {
+element.fillStyle = 'currentColor'
+}
+});
+</script>
+<style >
+    polygon { fill: black }
+
+    div {
+  color: white;
+      font:18px serif;
+      height: 100%;
+      overflow: auto;
+    }
+  </style>
+</svg>
diff --git a/tests/format/html/svg/svg.html b/tests/format/html/svg/svg.html
index 7009b55de539..21f7256f3f80 100644
--- a/tests/format/html/svg/svg.html
+++ b/tests/format/html/svg/svg.html
@@ -11,16 +11,7 @@
 </html>
 
 <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
-<defs /> <style >
-    polygon { fill: black }
-
-    div {
-  color: white;
-      font:18px serif;
-      height: 100%;
-      overflow: auto;
-    }
-  </style>
+<defs /> 
 
  <g>
   <g><polygon points="5,5 195,10 185,185 10,195" />