From 3201a21a36685752117aadb9f2d658d0c865aa59 Mon Sep 17 00:00:00 2001
From: Rich-Harris <richard.a.harris@gmail.com>
Date: Wed, 3 May 2017 17:48:19 -0400
Subject: [PATCH 1/8] build _set method in one place

---
 src/generators/dom/index.js | 38 +++++++++++++++++--------------------
 1 file changed, 17 insertions(+), 21 deletions(-)

diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js
index 260cce52bec5..95d47e06fa3a 100644
--- a/src/generators/dom/index.js
+++ b/src/generators/dom/index.js
@@ -61,17 +61,6 @@ export default function dom ( parsed, source, options ) {
 		_set: new CodeBuilder()
 	};
 
-	if ( options.dev ) {
-		builders._set.addBlock( deindent`
-			if ( typeof newState !== 'object' ) {
-				throw new Error( 'Component .set was called without an object of data key-values to update.' );
-			}
-		`);
-	}
-
-	builders._set.addLine( 'var oldState = this._state;' );
-	builders._set.addLine( `this._state = ${generator.helper( 'assign' )}( {}, oldState, newState );` );
-
 	if ( computations.length ) {
 		const builder = new CodeBuilder();
 		const differs = generator.helper( 'differs' );
@@ -98,18 +87,28 @@ export default function dom ( parsed, source, options ) {
 	}
 
 	if ( options.dev ) {
+		builders._set.addBlock( deindent`
+			if ( typeof newState !== 'object' ) {
+				throw new Error( 'Component .set was called without an object of data key-values to update.' );
+			}
+		`);
+
 		Array.from( generator.readonly ).forEach( prop => {
 			builders._set.addLine( `if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );` );
 		});
 	}
 
-	if ( computations.length ) {
-		builders._set.addLine( `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )` );
-	}
-
-	builders._set.addLine( `${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );` );
-	if ( block.hasUpdateMethod ) builders._set.addLine( `if ( this._fragment ) this._fragment.update( newState, this._state );` ); // TODO is the condition necessary?
-	builders._set.addLine( `${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );` );
+	// TODO is the `if ( this._fragment )` condition necessary?
+	builders._set.addBlock( deindent`
+		var oldState = this._state;
+		this._state = ${generator.helper( 'assign' )}( {}, oldState, newState );
+		${computations.length && `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )`}
+		${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );
+		${block.hasUpdateMethod && `if ( this._fragment ) this._fragment.update( newState, this._state );`}
+		${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );
+		${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`}
+		${( generator.hasComponents || generator.hasIntroTransitions ) && `this._flush();`}
+	` );
 
 	if ( hasJs ) {
 		builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` );
@@ -147,8 +146,6 @@ export default function dom ( parsed, source, options ) {
 			if ( options.target ) this._fragment.mount( options.target, null );
 			while ( this._bindings.length ) this._bindings.pop()();
 		` );
-
-		builders._set.addLine( `while ( this._bindings.length ) this._bindings.pop()();` );
 	} else {
 		builders.init.addBlock( deindent`
 			this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
@@ -160,7 +157,6 @@ export default function dom ( parsed, source, options ) {
 		const statement = `this._flush();`;
 
 		builders.init.addBlock( statement );
-		builders._set.addBlock( statement );
 	}
 
 	if ( templateProperties.oncreate ) {

From 4bff8d049b55e6e7bbbea2dce9237a3f393476e4 Mon Sep 17 00:00:00 2001
From: Rich-Harris <richard.a.harris@gmail.com>
Date: Wed, 3 May 2017 19:10:13 -0400
Subject: [PATCH 2/8] more codebuilder consolidation

---
 src/generators/dom/index.js | 54 ++++++++++++-------------------------
 1 file changed, 17 insertions(+), 37 deletions(-)

diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js
index 95d47e06fa3a..cb53883f6601 100644
--- a/src/generators/dom/index.js
+++ b/src/generators/dom/index.js
@@ -129,35 +129,17 @@ export default function dom ( parsed, source, options ) {
 		builders.main.addBlock( block.render() );
 	});
 
-	builders.init.addLine( `this._torndown = false;` );
-
-	if ( parsed.css && options.css !== false ) {
-		builders.init.addLine( `if ( !document.getElementById( ${JSON.stringify( generator.cssId + '-style' )} ) ) ${generator.alias( 'add_css' )}();` );
-	}
-
-	if ( generator.hasComponents || generator.hasIntroTransitions ) {
-		builders.init.addLine( `this._renderHooks = [];` );
-	}
-
-	if ( generator.hasComplexBindings ) {
-		builders.init.addBlock( deindent`
-			this._bindings = [];
-			this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
-			if ( options.target ) this._fragment.mount( options.target, null );
-			while ( this._bindings.length ) this._bindings.pop()();
-		` );
-	} else {
-		builders.init.addBlock( deindent`
-			this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
-			if ( options.target ) this._fragment.mount( options.target, null );
-		` );
-	}
-
-	if ( generator.hasComponents || generator.hasIntroTransitions ) {
-		const statement = `this._flush();`;
-
-		builders.init.addBlock( statement );
-	}
+	builders.init.addBlock( deindent`
+		this._torndown = false;
+		${parsed.css && options.css !== false && `if ( !document.getElementById( ${JSON.stringify( generator.cssId + '-style' )} ) ) ${generator.alias( 'add_css' )}();`}
+		${( generator.hasComponents || generator.hasIntroTransitions ) && `this._renderHooks = [];`}
+		${generator.hasComplexBindings && `this._bindings = [];`}
+
+		this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
+		if ( options.target ) this._fragment.mount( options.target, null );
+		${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`}
+		${( generator.hasComponents || generator.hasIntroTransitions ) && `this._flush();`}
+	` );
 
 	if ( templateProperties.oncreate ) {
 		builders.init.addBlock( deindent`
@@ -218,12 +200,6 @@ export default function dom ( parsed, source, options ) {
 		${builders.init}
 	` );
 
-	builders.main.addBlock( deindent`
-		function ${name} ( options ) {
-			${constructorBlock}
-		}
-	` );
-
 	const sharedPath = options.shared === true ? 'svelte/shared.js' : options.shared;
 
 	const prototypeBase = `${name}.prototype` + ( templateProperties.methods ? `, ${generator.alias( 'template' )}.methods` : '' );
@@ -236,10 +212,14 @@ export default function dom ( parsed, source, options ) {
 			}
 		}`;
 
-	builders.main.addBlock( `${generator.helper( 'assign' )}( ${prototypeBase}, ${proto});` );
-
 	// TODO deprecate component.teardown()
 	builders.main.addBlock( deindent`
+		function ${name} ( options ) {
+			${constructorBlock}
+		}
+
+		${generator.helper( 'assign' )}( ${prototypeBase}, ${proto});
+
 		${name}.prototype._set = function _set ( newState ) {
 			${builders._set}
 		};

From a5f7fe79ea70c2c56e6bb02136f36d945108c4f2 Mon Sep 17 00:00:00 2001
From: Rich-Harris <richard.a.harris@gmail.com>
Date: Wed, 3 May 2017 19:22:16 -0400
Subject: [PATCH 3/8] collapse constructor block into builders.main

---
 src/generators/dom/index.js | 70 +++++++++++--------------------------
 src/utils/deindent.js       |  6 +++-
 2 files changed, 25 insertions(+), 51 deletions(-)

diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js
index cb53883f6601..b7c671910b78 100644
--- a/src/generators/dom/index.js
+++ b/src/generators/dom/index.js
@@ -151,55 +151,6 @@ export default function dom ( parsed, source, options ) {
 		` );
 	}
 
-	const constructorBlock = new CodeBuilder();
-
-	constructorBlock.addLine( `options = options || {};` );
-	if ( generator.usesRefs ) constructorBlock.addLine( `this.refs = {};` );
-
-	constructorBlock.addLine(
-		`this._state = ${templateProperties.data ? `${generator.helper( 'assign' )}( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};`
-	);
-
-	if ( !generator.builders.metaBindings.isEmpty() ) {
-		constructorBlock.addBlock( generator.builders.metaBindings );
-	}
-
-	if ( computations.length ) {
-		constructorBlock.addLine(
-			`${generator.alias( 'recompute' )}( this._state, this._state, {}, true );`
-		);
-	}
-
-	if ( options.dev ) {
-		generator.expectedProperties.forEach( prop => {
-			constructorBlock.addLine(
-				`if ( !( '${prop}' in this._state ) ) console.warn( "Component was created without expected data property '${prop}'" );`
-			);
-		});
-
-		constructorBlock.addBlock(
-			`if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`
-		);
-	}
-
-	if ( generator.bindingGroups.length ) {
-		constructorBlock.addLine( `this._bindingGroups = [ ${Array( generator.bindingGroups.length ).fill( '[]' ).join( ', ' )} ];` );
-	}
-
-	constructorBlock.addBlock( deindent`
-		this._observers = {
-			pre: Object.create( null ),
-			post: Object.create( null )
-		};
-
-		this._handlers = Object.create( null );
-
-		this._root = options._root || this;
-		this._yield = options._yield;
-
-		${builders.init}
-	` );
-
 	const sharedPath = options.shared === true ? 'svelte/shared.js' : options.shared;
 
 	const prototypeBase = `${name}.prototype` + ( templateProperties.methods ? `, ${generator.alias( 'template' )}.methods` : '' );
@@ -215,7 +166,26 @@ export default function dom ( parsed, source, options ) {
 	// TODO deprecate component.teardown()
 	builders.main.addBlock( deindent`
 		function ${name} ( options ) {
-			${constructorBlock}
+			options = options || {};
+			${options.dev && `if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`}
+			${generator.usesRefs && `this.refs = {};`}
+			this._state = ${templateProperties.data ? `${generator.helper( 'assign' )}( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};
+			${generator.builders.metaBindings}
+			${computations.length && `${generator.alias( 'recompute' )}( this._state, this._state, {}, true );`}
+			${options.dev && Array.from( generator.expectedProperties ).map( prop => `if ( !( '${prop}' in this._state ) ) console.warn( "Component was created without expected data property '${prop}'" );`)}
+			${generator.bindingGroups.length && `this._bindingGroups = [ ${Array( generator.bindingGroups.length ).fill( '[]' ).join( ', ' )} ];`}
+
+			this._observers = {
+				pre: Object.create( null ),
+				post: Object.create( null )
+			};
+
+			this._handlers = Object.create( null );
+
+			this._root = options._root || this;
+			this._yield = options._yield;
+
+			${builders.init}
 		}
 
 		${generator.helper( 'assign' )}( ${prototypeBase}, ${proto});
diff --git a/src/utils/deindent.js b/src/utils/deindent.js
index f618dd5277f5..be20e42ef47f 100644
--- a/src/utils/deindent.js
+++ b/src/utils/deindent.js
@@ -9,9 +9,13 @@ export default function deindent ( strings, ...values ) {
 	let trailingIndentation = getTrailingIndentation( result );
 
 	for ( let i = 1; i < strings.length; i += 1 ) {
-		const expression = values[ i - 1 ];
+		let expression = values[ i - 1 ];
 		const string = strings[i].replace( pattern, '' );
 
+		if ( Array.isArray( expression ) ) {
+			expression = expression.length ? expression.join( '\n' ) : null;
+		}
+
 		if ( expression || expression === '' ) {
 			const value = String( expression ).replace( /\n/g, `\n${trailingIndentation}` );
 			result += value + string;

From 3efb5ab9930b49f7de2724fbbdeb2901e6863fc7 Mon Sep 17 00:00:00 2001
From: Rich-Harris <richard.a.harris@gmail.com>
Date: Wed, 3 May 2017 19:27:50 -0400
Subject: [PATCH 4/8] fix test

---
 test/js/samples/computed-collapsed-if/expected.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/js/samples/computed-collapsed-if/expected.js b/test/js/samples/computed-collapsed-if/expected.js
index df9295d28be2..1a6b8b82a61d 100644
--- a/test/js/samples/computed-collapsed-if/expected.js
+++ b/test/js/samples/computed-collapsed-if/expected.js
@@ -29,6 +29,7 @@ function create_main_fragment ( state, component ) {
 function SvelteComponent ( options ) {
 	options = options || {};
 	this._state = options.data || {};
+
 	recompute( this._state, this._state, {}, true );
 
 	this._observers = {

From 43091431d12406b5b0686ecfc0f2b9abfe713541 Mon Sep 17 00:00:00 2001
From: Rich-Harris <richard.a.harris@gmail.com>
Date: Wed, 3 May 2017 21:02:17 -0400
Subject: [PATCH 5/8] simplify SSR codegen

---
 src/generators/dom/index.js                   |  16 +-
 .../dom/visitors/Element/meta/Window.js       |   4 +-
 src/generators/server-side-rendering/index.js | 147 +++++++-----------
 .../samples/computed-collapsed-if/expected.js |   1 -
 .../samples/comment/_actual.html              |   2 +-
 .../component-data-dynamic/_actual.html       |   6 +-
 .../component-data-static/_actual.html        |   2 +-
 .../samples/computed/_actual.html             |   2 +-
 .../empty-elements-closed/_actual.html        |   2 +-
 .../samples/import-non-component/_actual.html |   2 +-
 .../samples/styles-nested/_actual.html        |   8 +-
 11 files changed, 79 insertions(+), 113 deletions(-)

diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js
index b7c671910b78..478806b5eba4 100644
--- a/src/generators/dom/index.js
+++ b/src/generators/dom/index.js
@@ -19,9 +19,7 @@ class DomGenerator extends Generator {
 		this.readonly = new Set();
 
 		// initial values for e.g. window.innerWidth, if there's a <:Window> meta tag
-		this.builders = {
-			metaBindings: new CodeBuilder()
-		};
+		this.metaBindings = [];
 	}
 
 	helper ( name ) {
@@ -91,11 +89,13 @@ export default function dom ( parsed, source, options ) {
 			if ( typeof newState !== 'object' ) {
 				throw new Error( 'Component .set was called without an object of data key-values to update.' );
 			}
-		`);
 
-		Array.from( generator.readonly ).forEach( prop => {
-			builders._set.addLine( `if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );` );
-		});
+			${
+				Array.from( generator.readonly ).map( prop =>
+					`if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );`
+				)
+			}
+		`);
 	}
 
 	// TODO is the `if ( this._fragment )` condition necessary?
@@ -170,7 +170,7 @@ export default function dom ( parsed, source, options ) {
 			${options.dev && `if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`}
 			${generator.usesRefs && `this.refs = {};`}
 			this._state = ${templateProperties.data ? `${generator.helper( 'assign' )}( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};
-			${generator.builders.metaBindings}
+			${generator.metaBindings}
 			${computations.length && `${generator.alias( 'recompute' )}( this._state, this._state, {}, true );`}
 			${options.dev && Array.from( generator.expectedProperties ).map( prop => `if ( !( '${prop}' in this._state ) ) console.warn( "Component was created without expected data property '${prop}'" );`)}
 			${generator.bindingGroups.length && `this._bindingGroups = [ ${Array( generator.bindingGroups.length ).fill( '[]' ).join( ', ' )} ];`}
diff --git a/src/generators/dom/visitors/Element/meta/Window.js b/src/generators/dom/visitors/Element/meta/Window.js
index c89f11c47c2d..01f1bbf4ca7e 100644
--- a/src/generators/dom/visitors/Element/meta/Window.js
+++ b/src/generators/dom/visitors/Element/meta/Window.js
@@ -84,7 +84,7 @@ export default function visitWindow ( generator, block, node ) {
 			events[ associatedEvent ].push( `${attribute.value.name}: this.${attribute.name}` );
 
 			// add initial value
-			generator.builders.metaBindings.addLine(
+			generator.metaBindings.push(
 				`this._state.${attribute.value.name} = window.${attribute.name};`
 			);
 		}
@@ -166,7 +166,7 @@ export default function visitWindow ( generator, block, node ) {
 		` );
 
 		// add initial value
-		generator.builders.metaBindings.addLine(
+		generator.metaBindings.push(
 			`this._state.${bindings.online} = navigator.onLine;`
 		);
 
diff --git a/src/generators/server-side-rendering/index.js b/src/generators/server-side-rendering/index.js
index 02e4e39bf28f..2b3be31bbd18 100644
--- a/src/generators/server-side-rendering/index.js
+++ b/src/generators/server-side-rendering/index.js
@@ -1,5 +1,4 @@
 import deindent from '../../utils/deindent.js';
-import CodeBuilder from '../../utils/CodeBuilder.js';
 import Generator from '../Generator.js';
 import Block from './Block.js';
 import visit from './visit.js';
@@ -24,13 +23,6 @@ export default function ssr ( parsed, source, options ) {
 
 	const { computations, hasJs, templateProperties } = generator.parseJs( true );
 
-	const builders = {
-		main: new CodeBuilder(),
-		bindings: new CodeBuilder(),
-		render: new CodeBuilder(),
-		renderCss: new CodeBuilder()
-	};
-
 	// create main render() function
 	const mainBlock = new Block({
 		generator,
@@ -43,84 +35,9 @@ export default function ssr ( parsed, source, options ) {
 		visit( generator, mainBlock, node );
 	});
 
-	builders.render.addLine(
-		templateProperties.data ? `state = Object.assign( ${generator.alias( 'template' )}.data(), state || {} );` : `state = state || {};`
-	);
-
-	computations.forEach( ({ key, deps }) => {
-		builders.render.addLine(
-			`state.${key} = ${generator.alias( 'template' )}.computed.${key}( ${deps.map( dep => `state.${dep}` ).join( ', ' )} );`
-		);
-	});
-
-	if ( generator.bindings.length ) {
-		const bindings = generator.bindings.join( '\n\n' );
-
-		builders.render.addBlock( deindent`
-			var settled = false;
-			var tmp;
-
-			while ( !settled ) {
-				settled = true;
-
-				${bindings}
-			}
-		` );
-	}
-
-	builders.render.addBlock(
-		`return \`${generator.renderCode}\`;`
-	);
-
-	// create renderCss() function
-	builders.renderCss.addBlock(
-		`var components = [];`
-	);
-
-	if ( generator.css ) {
-		builders.renderCss.addBlock( deindent`
-			components.push({
-				filename: ${name}.filename,
-				css: ${JSON.stringify( generator.css )},
-				map: null // TODO
-			});
-		` );
-	}
-
-	if ( templateProperties.components ) {
-		builders.renderCss.addBlock( deindent`
-			var seen = {};
-
-			function addComponent ( component ) {
-				var result = component.renderCss();
-				result.components.forEach( x => {
-					if ( seen[ x.filename ] ) return;
-					seen[ x.filename ] = true;
-					components.push( x );
-				});
-			}
-		` );
-
-		templateProperties.components.value.properties.forEach( prop => {
-			const { name } = prop.key;
-			const expression = generator.importedComponents.get( name ) || `${generator.alias( 'template' )}.components.${name}`;
-			builders.renderCss.addLine( `addComponent( ${expression} );` );
-		});
-	}
-
-	builders.renderCss.addBlock( deindent`
-		return {
-			css: components.map( x => x.css ).join( '\\n' ),
-			map: null,
-			components
-		};
-	` );
+	const result = deindent`
+		${hasJs && `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]`}
 
-	if ( hasJs ) {
-		builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` );
-	}
-
-	builders.main.addBlock( deindent`
 		var ${name} = {};
 
 		${name}.filename = ${JSON.stringify( options.filename )};
@@ -130,11 +47,63 @@ export default function ssr ( parsed, source, options ) {
 		};
 
 		${name}.render = function ( state, options ) {
-			${builders.render}
+			${templateProperties.data ? `state = Object.assign( ${generator.alias( 'template' )}.data(), state || {} );` : `state = state || {};`}
+
+			${computations.map( ({ key, deps }) =>
+				`state.${key} = ${generator.alias( 'template' )}.computed.${key}( ${deps.map( dep => `state.${dep}` ).join( ', ' )} );`
+			)}
+
+			${generator.bindings.length && deindent`
+				var settled = false;
+				var tmp;
+
+				while ( !settled ) {
+					settled = true;
+
+					${generator.bindings.join( '\n\n' )}
+				}
+			`}
+
+			return \`${generator.renderCode}\`;
 		};
 
 		${name}.renderCss = function () {
-			${builders.renderCss}
+			var components = [];
+
+			${generator.css && deindent`
+				components.push({
+					filename: ${name}.filename,
+					css: ${JSON.stringify( generator.css )},
+					map: null // TODO
+				});
+			`}
+
+			${templateProperties.components && deindent`
+				var seen = {};
+
+				function addComponent ( component ) {
+					var result = component.renderCss();
+					result.components.forEach( x => {
+						if ( seen[ x.filename ] ) return;
+						seen[ x.filename ] = true;
+						components.push( x );
+					});
+				}
+
+				${
+					templateProperties.components.value.properties.map( prop => {
+						const { name } = prop.key;
+						const expression = generator.importedComponents.get( name ) || `${generator.alias( 'template' )}.components.${name}`;
+						return `addComponent( ${expression} );`;
+					})
+				}
+			`}
+
+			return {
+				css: components.map( x => x.css ).join( '\\n' ),
+				map: null,
+				components
+			};
 		};
 
 		var escaped = {
@@ -148,9 +117,7 @@ export default function ssr ( parsed, source, options ) {
 		function __escape ( html ) {
 			return String( html ).replace( /["'&<>]/g, match => escaped[ match ] );
 		}
-	` );
-
-	const result = builders.main.toString();
+	`;
 
 	return generator.generate( result, options, { name, format } );
 }
diff --git a/test/js/samples/computed-collapsed-if/expected.js b/test/js/samples/computed-collapsed-if/expected.js
index 1a6b8b82a61d..df9295d28be2 100644
--- a/test/js/samples/computed-collapsed-if/expected.js
+++ b/test/js/samples/computed-collapsed-if/expected.js
@@ -29,7 +29,6 @@ function create_main_fragment ( state, component ) {
 function SvelteComponent ( options ) {
 	options = options || {};
 	this._state = options.data || {};
-
 	recompute( this._state, this._state, {}, true );
 
 	this._observers = {
diff --git a/test/server-side-rendering/samples/comment/_actual.html b/test/server-side-rendering/samples/comment/_actual.html
index e5c61a4b0ec2..0dfdacc02ba3 100644
--- a/test/server-side-rendering/samples/comment/_actual.html
+++ b/test/server-side-rendering/samples/comment/_actual.html
@@ -1,3 +1,3 @@
 <p>before</p>
 
-	<p>after</p>
\ No newline at end of file
+<p>after</p>
\ No newline at end of file
diff --git a/test/server-side-rendering/samples/component-data-dynamic/_actual.html b/test/server-side-rendering/samples/component-data-dynamic/_actual.html
index 6aedef46ee3c..d27b63e16290 100644
--- a/test/server-side-rendering/samples/component-data-dynamic/_actual.html
+++ b/test/server-side-rendering/samples/component-data-dynamic/_actual.html
@@ -1,4 +1,4 @@
 <div><p>foo: lol</p>
-	<p>baz: 42 (number)</p>
-	<p>qux: this is a piece of string</p>
-	<p>quux: core</p></div>
\ No newline at end of file
+<p>baz: 42 (number)</p>
+<p>qux: this is a piece of string</p>
+<p>quux: core</p></div>
\ No newline at end of file
diff --git a/test/server-side-rendering/samples/component-data-static/_actual.html b/test/server-side-rendering/samples/component-data-static/_actual.html
index 15a2100d4ad9..442c858e4dee 100644
--- a/test/server-side-rendering/samples/component-data-static/_actual.html
+++ b/test/server-side-rendering/samples/component-data-static/_actual.html
@@ -1,2 +1,2 @@
 <div><p>foo: bar</p>
-	<p>baz: 42 (number)</p></div>
\ No newline at end of file
+<p>baz: 42 (number)</p></div>
\ No newline at end of file
diff --git a/test/server-side-rendering/samples/computed/_actual.html b/test/server-side-rendering/samples/computed/_actual.html
index 099eebe604f8..6386593ee9f2 100644
--- a/test/server-side-rendering/samples/computed/_actual.html
+++ b/test/server-side-rendering/samples/computed/_actual.html
@@ -1,2 +1,2 @@
 <p>1 + 2 = 3</p>
-	<p>3 * 3 = 9</p>
\ No newline at end of file
+<p>3 * 3 = 9</p>
\ No newline at end of file
diff --git a/test/server-side-rendering/samples/empty-elements-closed/_actual.html b/test/server-side-rendering/samples/empty-elements-closed/_actual.html
index 643f5b6b4945..cb9b2cdcad8e 100644
--- a/test/server-side-rendering/samples/empty-elements-closed/_actual.html
+++ b/test/server-side-rendering/samples/empty-elements-closed/_actual.html
@@ -1,2 +1,2 @@
 <a></a>
-	<p></p>
\ No newline at end of file
+<p></p>
\ No newline at end of file
diff --git a/test/server-side-rendering/samples/import-non-component/_actual.html b/test/server-side-rendering/samples/import-non-component/_actual.html
index 893a7b890bbe..25e562b3dfb6 100644
--- a/test/server-side-rendering/samples/import-non-component/_actual.html
+++ b/test/server-side-rendering/samples/import-non-component/_actual.html
@@ -1,2 +1,2 @@
 <div>i got 99 problems</div>
-	<div>the answer is 42</div>
\ No newline at end of file
+<div>the answer is 42</div>
\ No newline at end of file
diff --git a/test/server-side-rendering/samples/styles-nested/_actual.html b/test/server-side-rendering/samples/styles-nested/_actual.html
index 1962e544544a..4b5e1430c85a 100644
--- a/test/server-side-rendering/samples/styles-nested/_actual.html
+++ b/test/server-side-rendering/samples/styles-nested/_actual.html
@@ -1,5 +1,5 @@
 <div svelte-4188175681>red</div>
-	<div svelte-146600313>green: foo</div>
-	<div svelte-1506185237>blue: foo</div>
-	<div svelte-146600313>green: bar</div>
-	<div svelte-1506185237>blue: bar</div>
\ No newline at end of file
+<div svelte-146600313>green: foo</div>
+<div svelte-1506185237>blue: foo</div>
+<div svelte-146600313>green: bar</div>
+<div svelte-1506185237>blue: bar</div>
\ No newline at end of file

From 0d67026904b91b75cd6fa090ff385076ed8822f3 Mon Sep 17 00:00:00 2001
From: Rich-Harris <richard.a.harris@gmail.com>
Date: Wed, 3 May 2017 21:19:48 -0400
Subject: [PATCH 6/8] more simplification

---
 src/generators/dom/index.js                   | 59 ++++++++-----------
 .../dom/visitors/Element/EventHandler.js      | 18 ++----
 .../dom/visitors/Element/meta/Window.js       | 23 ++++----
 src/utils/CodeBuilder.js                      |  9 +--
 4 files changed, 44 insertions(+), 65 deletions(-)

diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js
index 478806b5eba4..04c50b42e04f 100644
--- a/src/generators/dom/index.js
+++ b/src/generators/dom/index.js
@@ -55,7 +55,6 @@ export default function dom ( parsed, source, options ) {
 
 	const builders = {
 		main: new CodeBuilder(),
-		init: new CodeBuilder(),
 		_set: new CodeBuilder()
 	};
 
@@ -84,22 +83,18 @@ export default function dom ( parsed, source, options ) {
 		` );
 	}
 
-	if ( options.dev ) {
-		builders._set.addBlock( deindent`
+	// TODO is the `if ( this._fragment )` condition necessary?
+	builders._set.addBlock( deindent`
+		${options.dev && deindent`
 			if ( typeof newState !== 'object' ) {
 				throw new Error( 'Component .set was called without an object of data key-values to update.' );
 			}
 
-			${
-				Array.from( generator.readonly ).map( prop =>
-					`if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );`
-				)
-			}
-		`);
-	}
+			${Array.from( generator.readonly ).map( prop =>
+				`if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );`
+			)}
+		`}
 
-	// TODO is the `if ( this._fragment )` condition necessary?
-	builders._set.addBlock( deindent`
 		var oldState = this._state;
 		this._state = ${generator.helper( 'assign' )}( {}, oldState, newState );
 		${computations.length && `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )`}
@@ -129,28 +124,6 @@ export default function dom ( parsed, source, options ) {
 		builders.main.addBlock( block.render() );
 	});
 
-	builders.init.addBlock( deindent`
-		this._torndown = false;
-		${parsed.css && options.css !== false && `if ( !document.getElementById( ${JSON.stringify( generator.cssId + '-style' )} ) ) ${generator.alias( 'add_css' )}();`}
-		${( generator.hasComponents || generator.hasIntroTransitions ) && `this._renderHooks = [];`}
-		${generator.hasComplexBindings && `this._bindings = [];`}
-
-		this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
-		if ( options.target ) this._fragment.mount( options.target, null );
-		${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`}
-		${( generator.hasComponents || generator.hasIntroTransitions ) && `this._flush();`}
-	` );
-
-	if ( templateProperties.oncreate ) {
-		builders.init.addBlock( deindent`
-			if ( options._root ) {
-				options._root._renderHooks.push( ${generator.alias( 'template' )}.oncreate.bind( this ) );
-			} else {
-				${generator.alias( 'template' )}.oncreate.call( this );
-			}
-		` );
-	}
-
 	const sharedPath = options.shared === true ? 'svelte/shared.js' : options.shared;
 
 	const prototypeBase = `${name}.prototype` + ( templateProperties.methods ? `, ${generator.alias( 'template' )}.methods` : '' );
@@ -185,7 +158,23 @@ export default function dom ( parsed, source, options ) {
 			this._root = options._root || this;
 			this._yield = options._yield;
 
-			${builders.init}
+			this._torndown = false;
+			${parsed.css && options.css !== false && `if ( !document.getElementById( ${JSON.stringify( generator.cssId + '-style' )} ) ) ${generator.alias( 'add_css' )}();`}
+			${( generator.hasComponents || generator.hasIntroTransitions ) && `this._renderHooks = [];`}
+			${generator.hasComplexBindings && `this._bindings = [];`}
+
+			this._fragment = ${generator.alias( 'create_main_fragment' )}( this._state, this );
+			if ( options.target ) this._fragment.mount( options.target, null );
+			${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`}
+			${( generator.hasComponents || generator.hasIntroTransitions ) && `this._flush();`}
+
+			${templateProperties.oncreate && deindent`
+				if ( options._root ) {
+					options._root._renderHooks.push( ${generator.alias( 'template' )}.oncreate.bind( this ) );
+				} else {
+					${generator.alias( 'template' )}.oncreate.call( this );
+				}
+			`}
 		}
 
 		${generator.helper( 'assign' )}( ${prototypeBase}, ${proto});
diff --git a/src/generators/dom/visitors/Element/EventHandler.js b/src/generators/dom/visitors/Element/EventHandler.js
index 5439bceee2f6..2979d5d2f993 100644
--- a/src/generators/dom/visitors/Element/EventHandler.js
+++ b/src/generators/dom/visitors/Element/EventHandler.js
@@ -1,5 +1,4 @@
 import deindent from '../../../../utils/deindent.js';
-import CodeBuilder from '../../../../utils/CodeBuilder.js';
 import flattenReference from '../../../../utils/flattenReference.js';
 
 export default function visitEventHandler ( generator, block, state, node, attribute ) {
@@ -49,18 +48,11 @@ export default function visitEventHandler ( generator, block, state, node, attri
 		block.getUniqueName( `${name}_handler` );
 
 	// create the handler body
-	const handlerBody = new CodeBuilder();
-
-	if ( state.usesComponent ) {
-		// TODO the element needs to know to create `thing._svelte = { component: component }`
-		handlerBody.addLine( `var ${block.component} = this._svelte.component;` );
-	}
-
-	declarations.forEach( declaration => {
-		handlerBody.addLine( declaration );
-	});
-
-	handlerBody.addLine( `[✂${attribute.expression.start}-${attribute.expression.end}✂];` );
+	const handlerBody = deindent`
+		${state.usesComponent && `var ${block.component} = this._svelte.component;`}
+		${declarations}
+		[✂${attribute.expression.start}-${attribute.expression.end}✂];
+	`;
 
 	const handler = isCustomEvent ?
 		deindent`
diff --git a/src/generators/dom/visitors/Element/meta/Window.js b/src/generators/dom/visitors/Element/meta/Window.js
index 01f1bbf4ca7e..4af598481127 100644
--- a/src/generators/dom/visitors/Element/meta/Window.js
+++ b/src/generators/dom/visitors/Element/meta/Window.js
@@ -1,6 +1,5 @@
 import flattenReference from '../../../../../utils/flattenReference.js';
 import deindent from '../../../../../utils/deindent.js';
-import CodeBuilder from '../../../../../utils/CodeBuilder.js';
 
 const associatedEvents = {
 	innerWidth: 'resize',
@@ -43,8 +42,10 @@ export default function visitWindow ( generator, block, node ) {
 			}
 
 			const handlerName = block.getUniqueName( `onwindow${attribute.name}` );
-			const handlerBody = ( usesState ? `var state = ${block.component}.get();\n` : '' ) +
-				`[✂${attribute.expression.start}-${attribute.expression.end}✂];`;
+			const handlerBody = deindent`
+				${usesState && `var state = ${block.component}.get();`}
+				[✂${attribute.expression.start}-${attribute.expression.end}✂];
+			`;
 
 			block.builders.create.addBlock( deindent`
 				function ${handlerName} ( event ) {
@@ -96,25 +97,21 @@ export default function visitWindow ( generator, block, node ) {
 		const handlerName = block.getUniqueName( `onwindow${event}` );
 		const props = events[ event ].join( ',\n' );
 
-		const handlerBody = new CodeBuilder();
 		if ( event === 'scroll' ) { // TODO other bidirectional bindings...
 			block.addVariable( lock, 'false' );
-			handlerBody.addLine( `${lock} = true;` );
 		}
 
-		if ( generator.options.dev ) handlerBody.addLine( `component._updatingReadonlyProperty = true;` );
+		const handlerBody = deindent`
+			${event === 'scroll' && `${lock} = true;`}
+			${generator.options.dev && `component._updatingReadonlyProperty = true;`}
 
-		handlerBody.addBlock( deindent`
 			${block.component}.set({
 				${props}
 			});
-		` );
-
-		if ( generator.options.dev ) handlerBody.addLine( `component._updatingReadonlyProperty = false;` );
 
-		if ( event === 'scroll' ) {
-			handlerBody.addLine( `${lock} = false;` );
-		}
+			${generator.options.dev && `component._updatingReadonlyProperty = false;`}
+			${event === 'scroll' && `${lock} = false;`}
+		`;
 
 		block.builders.create.addBlock( deindent`
 			function ${handlerName} ( event ) {
diff --git a/src/utils/CodeBuilder.js b/src/utils/CodeBuilder.js
index eb2e215c40d6..ff898776a0cc 100644
--- a/src/utils/CodeBuilder.js
+++ b/src/utils/CodeBuilder.js
@@ -2,11 +2,12 @@ const LINE = {};
 const BLOCK = {};
 
 export default class CodeBuilder {
-	constructor () {
-		this.result = '';
+	constructor ( str = '' ) {
+		this.result = str;
 
-		this.first = null;
-		this.last = null;
+		const initial = str ? ( /\n/.test( str ) ? BLOCK : LINE ) : null;
+		this.first = initial;
+		this.last = initial;
 
 		this.lastCondition = null;
 	}

From fa80261ae2125bfcd4ed3c8f62b6a8bb75ae3240 Mon Sep 17 00:00:00 2001
From: Rich-Harris <richard.a.harris@gmail.com>
Date: Wed, 3 May 2017 21:28:33 -0400
Subject: [PATCH 7/8] more simplification

---
 src/generators/dom/visitors/EachBlock.js | 37 +++++-------------------
 1 file changed, 7 insertions(+), 30 deletions(-)

diff --git a/src/generators/dom/visitors/EachBlock.js b/src/generators/dom/visitors/EachBlock.js
index 5a427c94c699..14c4a4297e50 100644
--- a/src/generators/dom/visitors/EachBlock.js
+++ b/src/generators/dom/visitors/EachBlock.js
@@ -1,4 +1,3 @@
-import CodeBuilder from '../../../utils/CodeBuilder.js';
 import deindent from '../../../utils/deindent.js';
 import visit from '../visit.js';
 
@@ -119,24 +118,13 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
 	const iteration = block.getUniqueName( `${each_block}_iteration` );
 	const _iterations = block.getUniqueName( `_${each_block}_iterations` );
 
-	block.builders.create.addLine( `var ${lookup} = Object.create( null );` );
-
-	const create = new CodeBuilder();
-
-	create.addBlock( deindent`
-		var ${key} = ${each_block_value}[${i}].${node.key};
-		${iterations}[${i}] = ${lookup}[ ${key} ] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}, ${key} );
-	` );
-
-	if ( state.parentNode ) {
-		create.addLine(
-			`${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`
-		);
-	}
-
 	block.builders.create.addBlock( deindent`
+		var ${lookup} = Object.create( null );
+
 		for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) {
-			${create}
+			var ${key} = ${each_block_value}[${i}].${node.key};
+			${iterations}[${i}] = ${lookup}[ ${key} ] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}, ${key} );
+			${state.parentNode && `${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`}
 		}
 	` );
 
@@ -206,21 +194,10 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
 }
 
 function unkeyed ( generator, block, state, node, snippet, { create_each_block, each_block_value, iterations, i, params, anchor, mountOrIntro } ) {
-	const create = new CodeBuilder();
-
-	create.addLine(
-		`${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );`
-	);
-
-	if ( state.parentNode ) {
-		create.addLine(
-			`${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`
-		);
-	}
-
 	block.builders.create.addBlock( deindent`
 		for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) {
-			${create}
+			${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );
+			${state.parentNode && `${iterations}[${i}].${mountOrIntro}( ${state.parentNode}, null );`}
 		}
 	` );
 

From d8364f6e0a89002594e0f2523a1901c56ea35348 Mon Sep 17 00:00:00 2001
From: Rich-Harris <richard.a.harris@gmail.com>
Date: Wed, 3 May 2017 21:35:15 -0400
Subject: [PATCH 8/8] remove unnecessary if statement

---
 src/generators/dom/index.js                                | 3 +--
 test/js/samples/collapses-text-around-comments/expected.js | 4 ++--
 test/js/samples/each-block-changed-check/expected.js       | 4 ++--
 test/js/samples/if-block-no-update/expected.js             | 2 +-
 test/js/samples/if-block-simple/expected.js                | 4 ++--
 test/js/samples/use-elements-as-anchors/expected.js        | 4 ++--
 6 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js
index 04c50b42e04f..60aee7b5adfd 100644
--- a/src/generators/dom/index.js
+++ b/src/generators/dom/index.js
@@ -83,7 +83,6 @@ export default function dom ( parsed, source, options ) {
 		` );
 	}
 
-	// TODO is the `if ( this._fragment )` condition necessary?
 	builders._set.addBlock( deindent`
 		${options.dev && deindent`
 			if ( typeof newState !== 'object' ) {
@@ -99,7 +98,7 @@ export default function dom ( parsed, source, options ) {
 		this._state = ${generator.helper( 'assign' )}( {}, oldState, newState );
 		${computations.length && `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )`}
 		${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );
-		${block.hasUpdateMethod && `if ( this._fragment ) this._fragment.update( newState, this._state );`}
+		${block.hasUpdateMethod && `this._fragment.update( newState, this._state );`}
 		${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );
 		${generator.hasComplexBindings && `while ( this._bindings.length ) this._bindings.pop()();`}
 		${( generator.hasComponents || generator.hasIntroTransitions ) && `this._flush();`}
diff --git a/test/js/samples/collapses-text-around-comments/expected.js b/test/js/samples/collapses-text-around-comments/expected.js
index faa7e9d36ab2..8145330e3e08 100644
--- a/test/js/samples/collapses-text-around-comments/expected.js
+++ b/test/js/samples/collapses-text-around-comments/expected.js
@@ -69,7 +69,7 @@ SvelteComponent.prototype._set = function _set ( newState ) {
 	var oldState = this._state;
 	this._state = assign( {}, oldState, newState );
 	dispatchObservers( this, this._observers.pre, newState, oldState );
-	if ( this._fragment ) this._fragment.update( newState, this._state );
+	this._fragment.update( newState, this._state );
 	dispatchObservers( this, this._observers.post, newState, oldState );
 };
 
@@ -83,4 +83,4 @@ SvelteComponent.prototype.teardown = SvelteComponent.prototype.destroy = functio
 	this._torndown = true;
 };
 
-export default SvelteComponent;
+export default SvelteComponent;
\ No newline at end of file
diff --git a/test/js/samples/each-block-changed-check/expected.js b/test/js/samples/each-block-changed-check/expected.js
index c0a809272713..b31be6e9fa11 100644
--- a/test/js/samples/each-block-changed-check/expected.js
+++ b/test/js/samples/each-block-changed-check/expected.js
@@ -145,7 +145,7 @@ SvelteComponent.prototype._set = function _set ( newState ) {
 	var oldState = this._state;
 	this._state = assign( {}, oldState, newState );
 	dispatchObservers( this, this._observers.pre, newState, oldState );
-	if ( this._fragment ) this._fragment.update( newState, this._state );
+	this._fragment.update( newState, this._state );
 	dispatchObservers( this, this._observers.post, newState, oldState );
 };
 
@@ -159,4 +159,4 @@ SvelteComponent.prototype.teardown = SvelteComponent.prototype.destroy = functio
 	this._torndown = true;
 };
 
-export default SvelteComponent;
+export default SvelteComponent;
\ No newline at end of file
diff --git a/test/js/samples/if-block-no-update/expected.js b/test/js/samples/if-block-no-update/expected.js
index 2d7d9662e5ed..e86244e8e5ea 100644
--- a/test/js/samples/if-block-no-update/expected.js
+++ b/test/js/samples/if-block-no-update/expected.js
@@ -95,7 +95,7 @@ SvelteComponent.prototype._set = function _set ( newState ) {
 	var oldState = this._state;
 	this._state = assign( {}, oldState, newState );
 	dispatchObservers( this, this._observers.pre, newState, oldState );
-	if ( this._fragment ) this._fragment.update( newState, this._state );
+	this._fragment.update( newState, this._state );
 	dispatchObservers( this, this._observers.post, newState, oldState );
 };
 
diff --git a/test/js/samples/if-block-simple/expected.js b/test/js/samples/if-block-simple/expected.js
index 6ed64aa06b59..ea623351fd2a 100644
--- a/test/js/samples/if-block-simple/expected.js
+++ b/test/js/samples/if-block-simple/expected.js
@@ -76,7 +76,7 @@ SvelteComponent.prototype._set = function _set ( newState ) {
 	var oldState = this._state;
 	this._state = assign( {}, oldState, newState );
 	dispatchObservers( this, this._observers.pre, newState, oldState );
-	if ( this._fragment ) this._fragment.update( newState, this._state );
+	this._fragment.update( newState, this._state );
 	dispatchObservers( this, this._observers.post, newState, oldState );
 };
 
@@ -90,4 +90,4 @@ SvelteComponent.prototype.teardown = SvelteComponent.prototype.destroy = functio
 	this._torndown = true;
 };
 
-export default SvelteComponent;
+export default SvelteComponent;
\ No newline at end of file
diff --git a/test/js/samples/use-elements-as-anchors/expected.js b/test/js/samples/use-elements-as-anchors/expected.js
index 6d4f9b68aa90..3ddbc2ed1dd0 100644
--- a/test/js/samples/use-elements-as-anchors/expected.js
+++ b/test/js/samples/use-elements-as-anchors/expected.js
@@ -226,7 +226,7 @@ SvelteComponent.prototype._set = function _set ( newState ) {
 	var oldState = this._state;
 	this._state = assign( {}, oldState, newState );
 	dispatchObservers( this, this._observers.pre, newState, oldState );
-	if ( this._fragment ) this._fragment.update( newState, this._state );
+	this._fragment.update( newState, this._state );
 	dispatchObservers( this, this._observers.post, newState, oldState );
 };
 
@@ -240,4 +240,4 @@ SvelteComponent.prototype.teardown = SvelteComponent.prototype.destroy = functio
 	this._torndown = true;
 };
 
-export default SvelteComponent;
+export default SvelteComponent;
\ No newline at end of file