Repository: sveltejs/svelte (JavaScript)
Date: 2020-01-10 17:37:01+00:00
Task type: Bug Fix

Issue:
White space escape characters are written into HTML when spread is used with SSR
**Describe the bug**
When new lines and tabs are used in attributes, then run through SSR, they end up written into the HTML, eg. `<div class="\none\ntwo\n"></div>`. This breaks the class name and can cause a FOUC with Sapper, and would completely break rendering of HTML-only static-generated pages.

**Logs**
N/A

**To Reproduce**

REPL: https://svelte.dev/repl/80b8863b778c4b608537a98fb811de43?version=3.16.7

If you download the REPL, change to `generate:'ssr'` and then `console.log(App.render())`, you get

```html
<div class="
	white
	space
">A</div>

<div class="\n\twhite\n\tspace\n">B</div>

<div class="white\nspace">C</div>
```

**Expected behavior**
I would expect the white space to be rendered into the HTML as white space characters, like the first `<div>` above.

**Stacktraces**
N/A

**Information about your Svelte project:**
- Your browser and the version: (e.x. Chrome 52.1, Firefox 48.0, IE 10)
N/A

- Your operating system: (e.x. OS X 10, Ubuntu Linux 19.10, Windows XP, etc)
Arch Linux

- Svelte version (Please check you can reproduce the issue with the latest release!)
3.16.7

- Whether your project uses Webpack or Rollup
Rollup

**Severity**
HTML-only web pages would be severely broken by this bug, and it may not be obvious during development. Sapper sites will have a FOUC.

**Additional context**
I came across this bug by running into a bug causing a FOUC when I was using Svelte Material UI:
[https://github.com/hperrin/svelte-material-ui/issues/66](https://github.com/hperrin/svelte-material-ui/issues/66)




Issue discussion:
I think I'll try fixing it tomorrow on my Twitch stream, at least by adding some failing test cases if not actually fixing it.

Solution patch:
diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts
--- a/src/runtime/internal/ssr.ts
+++ b/src/runtime/internal/ssr.ts
@@ -25,9 +25,7 @@ export function spread(args, classes_to_add) {
 		else if (boolean_attributes.has(name.toLowerCase())) {
 			if (value) str += " " + name;
 		} else if (value != null) {
-			str += " " + name + "=" + JSON.stringify(String(value)
-				.replace(/"/g, '&#34;')
-				.replace(/'/g, '&#39;'));
+			str += ` ${name}="${String(value).replace(/"/g, '&#34;').replace(/'/g, '&#39;')}"`;
 		}
 	});
 


Test patch:
diff --git a/test/server-side-rendering/samples/spread-attributes-white-space/_expected.html b/test/server-side-rendering/samples/spread-attributes-white-space/_expected.html
new file mode 100644
--- /dev/null
+++ b/test/server-side-rendering/samples/spread-attributes-white-space/_expected.html
@@ -0,0 +1,8 @@
+<input value="
+	bar
+">
+
+<input class="
+	white
+	space
+">
\ No newline at end of file
diff --git a/test/server-side-rendering/samples/spread-attributes-white-space/main.svelte b/test/server-side-rendering/samples/spread-attributes-white-space/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/server-side-rendering/samples/spread-attributes-white-space/main.svelte
@@ -0,0 +1,12 @@
+<script>
+	let props = {
+		value: '\n\tbar\n',
+	};
+</script>
+
+<input {...props} />
+
+<input class="
+	white
+	space
+" {...({})}>